1 // SPDX-License-Identifier: GPL-2.0-only
3 * IMG I2S output controller driver
5 * Copyright (C) 2015 Imagination Technologies Ltd.
7 * Author: Damien Horsley <Damien.Horsley@imgtec.com>
10 #include <linux/clk.h>
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/pm_runtime.h>
17 #include <linux/reset.h>
19 #include <sound/core.h>
20 #include <sound/dmaengine_pcm.h>
21 #include <sound/initval.h>
22 #include <sound/pcm.h>
23 #include <sound/pcm_params.h>
24 #include <sound/soc.h>
26 #define IMG_I2S_OUT_TX_FIFO 0x0
28 #define IMG_I2S_OUT_CTL 0x4
29 #define IMG_I2S_OUT_CTL_DATA_EN_MASK BIT(24)
30 #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK 0xffe000
31 #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT 13
32 #define IMG_I2S_OUT_CTL_FRM_SIZE_MASK BIT(8)
33 #define IMG_I2S_OUT_CTL_MASTER_MASK BIT(6)
34 #define IMG_I2S_OUT_CTL_CLK_MASK BIT(5)
35 #define IMG_I2S_OUT_CTL_CLK_EN_MASK BIT(4)
36 #define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK BIT(3)
37 #define IMG_I2S_OUT_CTL_BCLK_POL_MASK BIT(2)
38 #define IMG_I2S_OUT_CTL_ME_MASK BIT(0)
40 #define IMG_I2S_OUT_CH_CTL 0x4
41 #define IMG_I2S_OUT_CHAN_CTL_CH_MASK BIT(11)
42 #define IMG_I2S_OUT_CHAN_CTL_LT_MASK BIT(10)
43 #define IMG_I2S_OUT_CHAN_CTL_FMT_MASK 0xf0
44 #define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT 4
45 #define IMG_I2S_OUT_CHAN_CTL_JUST_MASK BIT(3)
46 #define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK BIT(1)
47 #define IMG_I2S_OUT_CHAN_CTL_ME_MASK BIT(0)
49 #define IMG_I2S_OUT_CH_STRIDE 0x20
55 struct snd_dmaengine_dai_dma_data dma_data
;
57 unsigned int max_i2s_chan
;
58 void __iomem
*channel_base
;
59 bool force_clk_active
;
60 unsigned int active_channels
;
61 struct reset_control
*rst
;
62 struct snd_soc_dai_driver dai_driver
;
67 static int img_i2s_out_runtime_suspend(struct device
*dev
)
69 struct img_i2s_out
*i2s
= dev_get_drvdata(dev
);
71 clk_disable_unprepare(i2s
->clk_ref
);
72 clk_disable_unprepare(i2s
->clk_sys
);
77 static int img_i2s_out_runtime_resume(struct device
*dev
)
79 struct img_i2s_out
*i2s
= dev_get_drvdata(dev
);
82 ret
= clk_prepare_enable(i2s
->clk_sys
);
84 dev_err(dev
, "clk_enable failed: %d\n", ret
);
88 ret
= clk_prepare_enable(i2s
->clk_ref
);
90 dev_err(dev
, "clk_enable failed: %d\n", ret
);
91 clk_disable_unprepare(i2s
->clk_sys
);
98 static inline void img_i2s_out_writel(struct img_i2s_out
*i2s
, u32 val
,
101 writel(val
, i2s
->base
+ reg
);
104 static inline u32
img_i2s_out_readl(struct img_i2s_out
*i2s
, u32 reg
)
106 return readl(i2s
->base
+ reg
);
109 static inline void img_i2s_out_ch_writel(struct img_i2s_out
*i2s
,
110 u32 chan
, u32 val
, u32 reg
)
112 writel(val
, i2s
->channel_base
+ (chan
* IMG_I2S_OUT_CH_STRIDE
) + reg
);
115 static inline u32
img_i2s_out_ch_readl(struct img_i2s_out
*i2s
, u32 chan
,
118 return readl(i2s
->channel_base
+ (chan
* IMG_I2S_OUT_CH_STRIDE
) + reg
);
121 static inline void img_i2s_out_ch_disable(struct img_i2s_out
*i2s
, u32 chan
)
125 reg
= img_i2s_out_ch_readl(i2s
, chan
, IMG_I2S_OUT_CH_CTL
);
126 reg
&= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK
;
127 img_i2s_out_ch_writel(i2s
, chan
, reg
, IMG_I2S_OUT_CH_CTL
);
130 static inline void img_i2s_out_ch_enable(struct img_i2s_out
*i2s
, u32 chan
)
134 reg
= img_i2s_out_ch_readl(i2s
, chan
, IMG_I2S_OUT_CH_CTL
);
135 reg
|= IMG_I2S_OUT_CHAN_CTL_ME_MASK
;
136 img_i2s_out_ch_writel(i2s
, chan
, reg
, IMG_I2S_OUT_CH_CTL
);
139 static inline void img_i2s_out_disable(struct img_i2s_out
*i2s
)
143 reg
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
);
144 reg
&= ~IMG_I2S_OUT_CTL_ME_MASK
;
145 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
148 static inline void img_i2s_out_enable(struct img_i2s_out
*i2s
)
152 reg
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
);
153 reg
|= IMG_I2S_OUT_CTL_ME_MASK
;
154 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
157 static void img_i2s_out_reset(struct img_i2s_out
*i2s
)
160 u32 core_ctl
, chan_ctl
;
162 core_ctl
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
) &
163 ~IMG_I2S_OUT_CTL_ME_MASK
&
164 ~IMG_I2S_OUT_CTL_DATA_EN_MASK
;
166 if (!i2s
->force_clk_active
)
167 core_ctl
&= ~IMG_I2S_OUT_CTL_CLK_EN_MASK
;
169 chan_ctl
= img_i2s_out_ch_readl(i2s
, 0, IMG_I2S_OUT_CH_CTL
) &
170 ~IMG_I2S_OUT_CHAN_CTL_ME_MASK
;
172 reset_control_assert(i2s
->rst
);
173 reset_control_deassert(i2s
->rst
);
175 for (i
= 0; i
< i2s
->max_i2s_chan
; i
++)
176 img_i2s_out_ch_writel(i2s
, i
, chan_ctl
, IMG_I2S_OUT_CH_CTL
);
178 for (i
= 0; i
< i2s
->active_channels
; i
++)
179 img_i2s_out_ch_enable(i2s
, i
);
181 img_i2s_out_writel(i2s
, core_ctl
, IMG_I2S_OUT_CTL
);
182 img_i2s_out_enable(i2s
);
185 static int img_i2s_out_trigger(struct snd_pcm_substream
*substream
, int cmd
,
186 struct snd_soc_dai
*dai
)
188 struct img_i2s_out
*i2s
= snd_soc_dai_get_drvdata(dai
);
192 case SNDRV_PCM_TRIGGER_START
:
193 case SNDRV_PCM_TRIGGER_RESUME
:
194 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
195 reg
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
);
196 if (!i2s
->force_clk_active
)
197 reg
|= IMG_I2S_OUT_CTL_CLK_EN_MASK
;
198 reg
|= IMG_I2S_OUT_CTL_DATA_EN_MASK
;
199 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
201 case SNDRV_PCM_TRIGGER_STOP
:
202 case SNDRV_PCM_TRIGGER_SUSPEND
:
203 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
204 img_i2s_out_reset(i2s
);
213 static int img_i2s_out_hw_params(struct snd_pcm_substream
*substream
,
214 struct snd_pcm_hw_params
*params
, struct snd_soc_dai
*dai
)
216 struct img_i2s_out
*i2s
= snd_soc_dai_get_drvdata(dai
);
217 unsigned int channels
, i2s_channels
;
218 long pre_div_a
, pre_div_b
, diff_a
, diff_b
, rate
, clk_rate
;
220 u32 reg
, control_mask
, control_set
= 0;
221 snd_pcm_format_t format
;
223 rate
= params_rate(params
);
224 format
= params_format(params
);
225 channels
= params_channels(params
);
226 i2s_channels
= channels
/ 2;
228 if (format
!= SNDRV_PCM_FORMAT_S32_LE
)
231 if ((channels
< 2) ||
232 (channels
> (i2s
->max_i2s_chan
* 2)) ||
236 pre_div_a
= clk_round_rate(i2s
->clk_ref
, rate
* 256);
239 pre_div_b
= clk_round_rate(i2s
->clk_ref
, rate
* 384);
243 diff_a
= abs((pre_div_a
/ 256) - rate
);
244 diff_b
= abs((pre_div_b
/ 384) - rate
);
246 /* If diffs are equal, use lower clock rate */
248 clk_set_rate(i2s
->clk_ref
, pre_div_b
);
250 clk_set_rate(i2s
->clk_ref
, pre_div_a
);
253 * Another driver (eg alsa machine driver) may have rejected the above
254 * change. Get the current rate and set the register bit according to
255 * the new minimum diff
257 clk_rate
= clk_get_rate(i2s
->clk_ref
);
259 diff_a
= abs((clk_rate
/ 256) - rate
);
260 diff_b
= abs((clk_rate
/ 384) - rate
);
263 control_set
|= IMG_I2S_OUT_CTL_CLK_MASK
;
265 control_set
|= ((i2s_channels
- 1) <<
266 IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT
) &
267 IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK
;
269 control_mask
= IMG_I2S_OUT_CTL_CLK_MASK
|
270 IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK
;
272 img_i2s_out_disable(i2s
);
274 reg
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
);
275 reg
= (reg
& ~control_mask
) | control_set
;
276 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
278 for (i
= 0; i
< i2s_channels
; i
++)
279 img_i2s_out_ch_enable(i2s
, i
);
281 for (; i
< i2s
->max_i2s_chan
; i
++)
282 img_i2s_out_ch_disable(i2s
, i
);
284 img_i2s_out_enable(i2s
);
286 i2s
->active_channels
= i2s_channels
;
291 static int img_i2s_out_set_fmt(struct snd_soc_dai
*dai
, unsigned int fmt
)
293 struct img_i2s_out
*i2s
= snd_soc_dai_get_drvdata(dai
);
295 bool force_clk_active
;
296 u32 chan_control_mask
, control_mask
, chan_control_set
= 0;
297 u32 reg
, control_set
= 0;
299 force_clk_active
= ((fmt
& SND_SOC_DAIFMT_CLOCK_MASK
) ==
300 SND_SOC_DAIFMT_CONT
);
302 if (force_clk_active
)
303 control_set
|= IMG_I2S_OUT_CTL_CLK_EN_MASK
;
305 switch (fmt
& SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK
) {
306 case SND_SOC_DAIFMT_BC_FC
:
308 case SND_SOC_DAIFMT_BP_FP
:
309 control_set
|= IMG_I2S_OUT_CTL_MASTER_MASK
;
315 switch (fmt
& SND_SOC_DAIFMT_INV_MASK
) {
316 case SND_SOC_DAIFMT_NB_NF
:
317 control_set
|= IMG_I2S_OUT_CTL_BCLK_POL_MASK
;
319 case SND_SOC_DAIFMT_NB_IF
:
320 control_set
|= IMG_I2S_OUT_CTL_BCLK_POL_MASK
;
321 control_set
|= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK
;
323 case SND_SOC_DAIFMT_IB_NF
:
325 case SND_SOC_DAIFMT_IB_IF
:
326 control_set
|= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK
;
332 switch (fmt
& SND_SOC_DAIFMT_FORMAT_MASK
) {
333 case SND_SOC_DAIFMT_I2S
:
334 chan_control_set
|= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK
;
336 case SND_SOC_DAIFMT_LEFT_J
:
342 control_mask
= IMG_I2S_OUT_CTL_CLK_EN_MASK
|
343 IMG_I2S_OUT_CTL_MASTER_MASK
|
344 IMG_I2S_OUT_CTL_BCLK_POL_MASK
|
345 IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK
;
347 chan_control_mask
= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK
;
349 ret
= pm_runtime_resume_and_get(i2s
->dev
);
353 img_i2s_out_disable(i2s
);
355 reg
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
);
356 reg
= (reg
& ~control_mask
) | control_set
;
357 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
359 for (i
= 0; i
< i2s
->active_channels
; i
++)
360 img_i2s_out_ch_disable(i2s
, i
);
362 for (i
= 0; i
< i2s
->max_i2s_chan
; i
++) {
363 reg
= img_i2s_out_ch_readl(i2s
, i
, IMG_I2S_OUT_CH_CTL
);
364 reg
= (reg
& ~chan_control_mask
) | chan_control_set
;
365 img_i2s_out_ch_writel(i2s
, i
, reg
, IMG_I2S_OUT_CH_CTL
);
368 for (i
= 0; i
< i2s
->active_channels
; i
++)
369 img_i2s_out_ch_enable(i2s
, i
);
371 img_i2s_out_enable(i2s
);
372 pm_runtime_put(i2s
->dev
);
374 i2s
->force_clk_active
= force_clk_active
;
379 static int img_i2s_out_dai_probe(struct snd_soc_dai
*dai
)
381 struct img_i2s_out
*i2s
= snd_soc_dai_get_drvdata(dai
);
383 snd_soc_dai_init_dma_data(dai
, &i2s
->dma_data
, NULL
);
388 static const struct snd_soc_dai_ops img_i2s_out_dai_ops
= {
389 .probe
= img_i2s_out_dai_probe
,
390 .trigger
= img_i2s_out_trigger
,
391 .hw_params
= img_i2s_out_hw_params
,
392 .set_fmt
= img_i2s_out_set_fmt
395 static const struct snd_soc_component_driver img_i2s_out_component
= {
396 .name
= "img-i2s-out",
397 .legacy_dai_naming
= 1,
400 static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream
*st
,
401 struct snd_pcm_hw_params
*params
, struct dma_slave_config
*sc
)
403 unsigned int i2s_channels
= params_channels(params
) / 2;
404 struct snd_soc_pcm_runtime
*rtd
= snd_soc_substream_to_rtd(st
);
405 struct snd_dmaengine_dai_dma_data
*dma_data
;
408 dma_data
= snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd
, 0), st
);
410 ret
= snd_hwparams_to_dma_slave_config(st
, params
, sc
);
414 sc
->dst_addr
= dma_data
->addr
;
415 sc
->dst_addr_width
= dma_data
->addr_width
;
416 sc
->dst_maxburst
= 4 * i2s_channels
;
421 static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config
= {
422 .prepare_slave_config
= img_i2s_out_dma_prepare_slave_config
425 static int img_i2s_out_probe(struct platform_device
*pdev
)
427 struct img_i2s_out
*i2s
;
428 struct resource
*res
;
431 unsigned int max_i2s_chan_pow_2
;
433 struct device
*dev
= &pdev
->dev
;
435 i2s
= devm_kzalloc(&pdev
->dev
, sizeof(*i2s
), GFP_KERNEL
);
439 platform_set_drvdata(pdev
, i2s
);
441 i2s
->dev
= &pdev
->dev
;
443 base
= devm_platform_get_and_ioremap_resource(pdev
, 0, &res
);
445 return PTR_ERR(base
);
449 if (of_property_read_u32(pdev
->dev
.of_node
, "img,i2s-channels",
450 &i2s
->max_i2s_chan
)) {
451 dev_err(&pdev
->dev
, "No img,i2s-channels property\n");
455 max_i2s_chan_pow_2
= 1 << get_count_order(i2s
->max_i2s_chan
);
457 i2s
->channel_base
= base
+ (max_i2s_chan_pow_2
* 0x20);
459 i2s
->rst
= devm_reset_control_get_exclusive(&pdev
->dev
, "rst");
460 if (IS_ERR(i2s
->rst
))
461 return dev_err_probe(&pdev
->dev
, PTR_ERR(i2s
->rst
),
462 "No top level reset found\n");
464 i2s
->clk_sys
= devm_clk_get(&pdev
->dev
, "sys");
465 if (IS_ERR(i2s
->clk_sys
))
466 return dev_err_probe(dev
, PTR_ERR(i2s
->clk_sys
),
467 "Failed to acquire clock 'sys'\n");
469 i2s
->clk_ref
= devm_clk_get(&pdev
->dev
, "ref");
470 if (IS_ERR(i2s
->clk_ref
))
471 return dev_err_probe(dev
, PTR_ERR(i2s
->clk_ref
),
472 "Failed to acquire clock 'ref'\n");
474 i2s
->suspend_ch_ctl
= devm_kcalloc(dev
,
475 i2s
->max_i2s_chan
, sizeof(*i2s
->suspend_ch_ctl
), GFP_KERNEL
);
476 if (!i2s
->suspend_ch_ctl
)
479 pm_runtime_enable(&pdev
->dev
);
480 if (!pm_runtime_enabled(&pdev
->dev
)) {
481 ret
= img_i2s_out_runtime_resume(&pdev
->dev
);
485 ret
= pm_runtime_resume_and_get(&pdev
->dev
);
489 reg
= IMG_I2S_OUT_CTL_FRM_SIZE_MASK
;
490 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
492 reg
= IMG_I2S_OUT_CHAN_CTL_JUST_MASK
|
493 IMG_I2S_OUT_CHAN_CTL_LT_MASK
|
494 IMG_I2S_OUT_CHAN_CTL_CH_MASK
|
495 (8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT
);
497 for (i
= 0; i
< i2s
->max_i2s_chan
; i
++)
498 img_i2s_out_ch_writel(i2s
, i
, reg
, IMG_I2S_OUT_CH_CTL
);
500 img_i2s_out_reset(i2s
);
501 pm_runtime_put(&pdev
->dev
);
503 i2s
->active_channels
= 1;
504 i2s
->dma_data
.addr
= res
->start
+ IMG_I2S_OUT_TX_FIFO
;
505 i2s
->dma_data
.addr_width
= 4;
506 i2s
->dma_data
.maxburst
= 4;
508 i2s
->dai_driver
.playback
.channels_min
= 2;
509 i2s
->dai_driver
.playback
.channels_max
= i2s
->max_i2s_chan
* 2;
510 i2s
->dai_driver
.playback
.rates
= SNDRV_PCM_RATE_8000_192000
;
511 i2s
->dai_driver
.playback
.formats
= SNDRV_PCM_FMTBIT_S32_LE
;
512 i2s
->dai_driver
.ops
= &img_i2s_out_dai_ops
;
514 ret
= devm_snd_soc_register_component(&pdev
->dev
,
515 &img_i2s_out_component
, &i2s
->dai_driver
, 1);
519 ret
= devm_snd_dmaengine_pcm_register(&pdev
->dev
,
520 &img_i2s_out_dma_config
, 0);
527 if (!pm_runtime_status_suspended(&pdev
->dev
))
528 img_i2s_out_runtime_suspend(&pdev
->dev
);
530 pm_runtime_disable(&pdev
->dev
);
535 static void img_i2s_out_dev_remove(struct platform_device
*pdev
)
537 pm_runtime_disable(&pdev
->dev
);
538 if (!pm_runtime_status_suspended(&pdev
->dev
))
539 img_i2s_out_runtime_suspend(&pdev
->dev
);
542 #ifdef CONFIG_PM_SLEEP
543 static int img_i2s_out_suspend(struct device
*dev
)
545 struct img_i2s_out
*i2s
= dev_get_drvdata(dev
);
549 if (pm_runtime_status_suspended(dev
)) {
550 ret
= img_i2s_out_runtime_resume(dev
);
555 for (i
= 0; i
< i2s
->max_i2s_chan
; i
++) {
556 reg
= img_i2s_out_ch_readl(i2s
, i
, IMG_I2S_OUT_CH_CTL
);
557 i2s
->suspend_ch_ctl
[i
] = reg
;
560 i2s
->suspend_ctl
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
);
562 img_i2s_out_runtime_suspend(dev
);
567 static int img_i2s_out_resume(struct device
*dev
)
569 struct img_i2s_out
*i2s
= dev_get_drvdata(dev
);
573 ret
= img_i2s_out_runtime_resume(dev
);
577 for (i
= 0; i
< i2s
->max_i2s_chan
; i
++) {
578 reg
= i2s
->suspend_ch_ctl
[i
];
579 img_i2s_out_ch_writel(i2s
, i
, reg
, IMG_I2S_OUT_CH_CTL
);
582 img_i2s_out_writel(i2s
, i2s
->suspend_ctl
, IMG_I2S_OUT_CTL
);
584 if (pm_runtime_status_suspended(dev
))
585 img_i2s_out_runtime_suspend(dev
);
591 static const struct of_device_id img_i2s_out_of_match
[] = {
592 { .compatible
= "img,i2s-out" },
595 MODULE_DEVICE_TABLE(of
, img_i2s_out_of_match
);
597 static const struct dev_pm_ops img_i2s_out_pm_ops
= {
598 SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend
,
599 img_i2s_out_runtime_resume
, NULL
)
600 SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend
, img_i2s_out_resume
)
603 static struct platform_driver img_i2s_out_driver
= {
605 .name
= "img-i2s-out",
606 .of_match_table
= img_i2s_out_of_match
,
607 .pm
= &img_i2s_out_pm_ops
609 .probe
= img_i2s_out_probe
,
610 .remove
= img_i2s_out_dev_remove
612 module_platform_driver(img_i2s_out_driver
);
614 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
615 MODULE_DESCRIPTION("IMG I2S Output Driver");
616 MODULE_LICENSE("GPL v2");