2 * IMG I2S output controller driver
4 * Copyright (C) 2015 Imagination Technologies Ltd.
6 * Author: Damien Horsley <Damien.Horsley@imgtec.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
13 #include <linux/clk.h>
14 #include <linux/init.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
18 #include <linux/platform_device.h>
19 #include <linux/pm_runtime.h>
20 #include <linux/reset.h>
22 #include <sound/core.h>
23 #include <sound/dmaengine_pcm.h>
24 #include <sound/initval.h>
25 #include <sound/pcm.h>
26 #include <sound/pcm_params.h>
27 #include <sound/soc.h>
29 #define IMG_I2S_OUT_TX_FIFO 0x0
31 #define IMG_I2S_OUT_CTL 0x4
32 #define IMG_I2S_OUT_CTL_DATA_EN_MASK BIT(24)
33 #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK 0xffe000
34 #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT 13
35 #define IMG_I2S_OUT_CTL_FRM_SIZE_MASK BIT(8)
36 #define IMG_I2S_OUT_CTL_MASTER_MASK BIT(6)
37 #define IMG_I2S_OUT_CTL_CLK_MASK BIT(5)
38 #define IMG_I2S_OUT_CTL_CLK_EN_MASK BIT(4)
39 #define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK BIT(3)
40 #define IMG_I2S_OUT_CTL_BCLK_POL_MASK BIT(2)
41 #define IMG_I2S_OUT_CTL_ME_MASK BIT(0)
43 #define IMG_I2S_OUT_CH_CTL 0x4
44 #define IMG_I2S_OUT_CHAN_CTL_CH_MASK BIT(11)
45 #define IMG_I2S_OUT_CHAN_CTL_LT_MASK BIT(10)
46 #define IMG_I2S_OUT_CHAN_CTL_FMT_MASK 0xf0
47 #define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT 4
48 #define IMG_I2S_OUT_CHAN_CTL_JUST_MASK BIT(3)
49 #define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK BIT(1)
50 #define IMG_I2S_OUT_CHAN_CTL_ME_MASK BIT(0)
52 #define IMG_I2S_OUT_CH_STRIDE 0x20
58 struct snd_dmaengine_dai_dma_data dma_data
;
60 unsigned int max_i2s_chan
;
61 void __iomem
*channel_base
;
62 bool force_clk_active
;
63 unsigned int active_channels
;
64 struct reset_control
*rst
;
65 struct snd_soc_dai_driver dai_driver
;
68 static int img_i2s_out_suspend(struct device
*dev
)
70 struct img_i2s_out
*i2s
= dev_get_drvdata(dev
);
72 if (!i2s
->force_clk_active
)
73 clk_disable_unprepare(i2s
->clk_ref
);
78 static int img_i2s_out_resume(struct device
*dev
)
80 struct img_i2s_out
*i2s
= dev_get_drvdata(dev
);
83 if (!i2s
->force_clk_active
) {
84 ret
= clk_prepare_enable(i2s
->clk_ref
);
86 dev_err(dev
, "clk_enable failed: %d\n", ret
);
94 static inline void img_i2s_out_writel(struct img_i2s_out
*i2s
, u32 val
,
97 writel(val
, i2s
->base
+ reg
);
100 static inline u32
img_i2s_out_readl(struct img_i2s_out
*i2s
, u32 reg
)
102 return readl(i2s
->base
+ reg
);
105 static inline void img_i2s_out_ch_writel(struct img_i2s_out
*i2s
,
106 u32 chan
, u32 val
, u32 reg
)
108 writel(val
, i2s
->channel_base
+ (chan
* IMG_I2S_OUT_CH_STRIDE
) + reg
);
111 static inline u32
img_i2s_out_ch_readl(struct img_i2s_out
*i2s
, u32 chan
,
114 return readl(i2s
->channel_base
+ (chan
* IMG_I2S_OUT_CH_STRIDE
) + reg
);
117 static inline void img_i2s_out_ch_disable(struct img_i2s_out
*i2s
, u32 chan
)
121 reg
= img_i2s_out_ch_readl(i2s
, chan
, IMG_I2S_OUT_CH_CTL
);
122 reg
&= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK
;
123 img_i2s_out_ch_writel(i2s
, chan
, reg
, IMG_I2S_OUT_CH_CTL
);
126 static inline void img_i2s_out_ch_enable(struct img_i2s_out
*i2s
, u32 chan
)
130 reg
= img_i2s_out_ch_readl(i2s
, chan
, IMG_I2S_OUT_CH_CTL
);
131 reg
|= IMG_I2S_OUT_CHAN_CTL_ME_MASK
;
132 img_i2s_out_ch_writel(i2s
, chan
, reg
, IMG_I2S_OUT_CH_CTL
);
135 static inline void img_i2s_out_disable(struct img_i2s_out
*i2s
)
139 reg
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
);
140 reg
&= ~IMG_I2S_OUT_CTL_ME_MASK
;
141 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
144 static inline void img_i2s_out_enable(struct img_i2s_out
*i2s
)
148 reg
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
);
149 reg
|= IMG_I2S_OUT_CTL_ME_MASK
;
150 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
153 static void img_i2s_out_reset(struct img_i2s_out
*i2s
)
156 u32 core_ctl
, chan_ctl
;
158 core_ctl
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
) &
159 ~IMG_I2S_OUT_CTL_ME_MASK
&
160 ~IMG_I2S_OUT_CTL_DATA_EN_MASK
;
162 if (!i2s
->force_clk_active
)
163 core_ctl
&= ~IMG_I2S_OUT_CTL_CLK_EN_MASK
;
165 chan_ctl
= img_i2s_out_ch_readl(i2s
, 0, IMG_I2S_OUT_CH_CTL
) &
166 ~IMG_I2S_OUT_CHAN_CTL_ME_MASK
;
168 reset_control_assert(i2s
->rst
);
169 reset_control_deassert(i2s
->rst
);
171 for (i
= 0; i
< i2s
->max_i2s_chan
; i
++)
172 img_i2s_out_ch_writel(i2s
, i
, chan_ctl
, IMG_I2S_OUT_CH_CTL
);
174 for (i
= 0; i
< i2s
->active_channels
; i
++)
175 img_i2s_out_ch_enable(i2s
, i
);
177 img_i2s_out_writel(i2s
, core_ctl
, IMG_I2S_OUT_CTL
);
178 img_i2s_out_enable(i2s
);
181 static int img_i2s_out_trigger(struct snd_pcm_substream
*substream
, int cmd
,
182 struct snd_soc_dai
*dai
)
184 struct img_i2s_out
*i2s
= snd_soc_dai_get_drvdata(dai
);
188 case SNDRV_PCM_TRIGGER_START
:
189 case SNDRV_PCM_TRIGGER_RESUME
:
190 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
191 reg
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
);
192 if (!i2s
->force_clk_active
)
193 reg
|= IMG_I2S_OUT_CTL_CLK_EN_MASK
;
194 reg
|= IMG_I2S_OUT_CTL_DATA_EN_MASK
;
195 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
197 case SNDRV_PCM_TRIGGER_STOP
:
198 case SNDRV_PCM_TRIGGER_SUSPEND
:
199 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
200 img_i2s_out_reset(i2s
);
209 static int img_i2s_out_hw_params(struct snd_pcm_substream
*substream
,
210 struct snd_pcm_hw_params
*params
, struct snd_soc_dai
*dai
)
212 struct img_i2s_out
*i2s
= snd_soc_dai_get_drvdata(dai
);
213 unsigned int channels
, i2s_channels
;
214 long pre_div_a
, pre_div_b
, diff_a
, diff_b
, rate
, clk_rate
;
216 u32 reg
, control_mask
, control_set
= 0;
217 snd_pcm_format_t format
;
219 rate
= params_rate(params
);
220 format
= params_format(params
);
221 channels
= params_channels(params
);
222 i2s_channels
= channels
/ 2;
224 if (format
!= SNDRV_PCM_FORMAT_S32_LE
)
227 if ((channels
< 2) ||
228 (channels
> (i2s
->max_i2s_chan
* 2)) ||
232 pre_div_a
= clk_round_rate(i2s
->clk_ref
, rate
* 256);
235 pre_div_b
= clk_round_rate(i2s
->clk_ref
, rate
* 384);
239 diff_a
= abs((pre_div_a
/ 256) - rate
);
240 diff_b
= abs((pre_div_b
/ 384) - rate
);
242 /* If diffs are equal, use lower clock rate */
244 clk_set_rate(i2s
->clk_ref
, pre_div_b
);
246 clk_set_rate(i2s
->clk_ref
, pre_div_a
);
249 * Another driver (eg alsa machine driver) may have rejected the above
250 * change. Get the current rate and set the register bit according to
251 * the new minimum diff
253 clk_rate
= clk_get_rate(i2s
->clk_ref
);
255 diff_a
= abs((clk_rate
/ 256) - rate
);
256 diff_b
= abs((clk_rate
/ 384) - rate
);
259 control_set
|= IMG_I2S_OUT_CTL_CLK_MASK
;
261 control_set
|= ((i2s_channels
- 1) <<
262 IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT
) &
263 IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK
;
265 control_mask
= IMG_I2S_OUT_CTL_CLK_MASK
|
266 IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK
;
268 img_i2s_out_disable(i2s
);
270 reg
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
);
271 reg
= (reg
& ~control_mask
) | control_set
;
272 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
274 for (i
= 0; i
< i2s_channels
; i
++)
275 img_i2s_out_ch_enable(i2s
, i
);
277 for (; i
< i2s
->max_i2s_chan
; i
++)
278 img_i2s_out_ch_disable(i2s
, i
);
280 img_i2s_out_enable(i2s
);
282 i2s
->active_channels
= i2s_channels
;
287 static int img_i2s_out_set_fmt(struct snd_soc_dai
*dai
, unsigned int fmt
)
289 struct img_i2s_out
*i2s
= snd_soc_dai_get_drvdata(dai
);
291 bool force_clk_active
;
292 u32 chan_control_mask
, control_mask
, chan_control_set
= 0;
293 u32 reg
, control_set
= 0;
295 force_clk_active
= ((fmt
& SND_SOC_DAIFMT_CLOCK_MASK
) ==
296 SND_SOC_DAIFMT_CONT
);
298 if (force_clk_active
)
299 control_set
|= IMG_I2S_OUT_CTL_CLK_EN_MASK
;
301 switch (fmt
& SND_SOC_DAIFMT_MASTER_MASK
) {
302 case SND_SOC_DAIFMT_CBM_CFM
:
304 case SND_SOC_DAIFMT_CBS_CFS
:
305 control_set
|= IMG_I2S_OUT_CTL_MASTER_MASK
;
311 switch (fmt
& SND_SOC_DAIFMT_INV_MASK
) {
312 case SND_SOC_DAIFMT_NB_NF
:
313 control_set
|= IMG_I2S_OUT_CTL_BCLK_POL_MASK
;
315 case SND_SOC_DAIFMT_NB_IF
:
316 control_set
|= IMG_I2S_OUT_CTL_BCLK_POL_MASK
;
317 control_set
|= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK
;
319 case SND_SOC_DAIFMT_IB_NF
:
321 case SND_SOC_DAIFMT_IB_IF
:
322 control_set
|= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK
;
328 switch (fmt
& SND_SOC_DAIFMT_FORMAT_MASK
) {
329 case SND_SOC_DAIFMT_I2S
:
330 chan_control_set
|= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK
;
332 case SND_SOC_DAIFMT_LEFT_J
:
338 control_mask
= IMG_I2S_OUT_CTL_CLK_EN_MASK
|
339 IMG_I2S_OUT_CTL_MASTER_MASK
|
340 IMG_I2S_OUT_CTL_BCLK_POL_MASK
|
341 IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK
;
343 chan_control_mask
= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK
;
345 img_i2s_out_disable(i2s
);
347 reg
= img_i2s_out_readl(i2s
, IMG_I2S_OUT_CTL
);
348 reg
= (reg
& ~control_mask
) | control_set
;
349 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
351 for (i
= 0; i
< i2s
->active_channels
; i
++)
352 img_i2s_out_ch_disable(i2s
, i
);
354 for (i
= 0; i
< i2s
->max_i2s_chan
; i
++) {
355 reg
= img_i2s_out_ch_readl(i2s
, i
, IMG_I2S_OUT_CH_CTL
);
356 reg
= (reg
& ~chan_control_mask
) | chan_control_set
;
357 img_i2s_out_ch_writel(i2s
, i
, reg
, IMG_I2S_OUT_CH_CTL
);
360 for (i
= 0; i
< i2s
->active_channels
; i
++)
361 img_i2s_out_ch_enable(i2s
, i
);
363 img_i2s_out_enable(i2s
);
365 i2s
->force_clk_active
= force_clk_active
;
370 static const struct snd_soc_dai_ops img_i2s_out_dai_ops
= {
371 .trigger
= img_i2s_out_trigger
,
372 .hw_params
= img_i2s_out_hw_params
,
373 .set_fmt
= img_i2s_out_set_fmt
376 static int img_i2s_out_dai_probe(struct snd_soc_dai
*dai
)
378 struct img_i2s_out
*i2s
= snd_soc_dai_get_drvdata(dai
);
380 snd_soc_dai_init_dma_data(dai
, &i2s
->dma_data
, NULL
);
385 static const struct snd_soc_component_driver img_i2s_out_component
= {
386 .name
= "img-i2s-out"
389 static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream
*st
,
390 struct snd_pcm_hw_params
*params
, struct dma_slave_config
*sc
)
392 unsigned int i2s_channels
= params_channels(params
) / 2;
393 struct snd_soc_pcm_runtime
*rtd
= st
->private_data
;
394 struct snd_dmaengine_dai_dma_data
*dma_data
;
397 dma_data
= snd_soc_dai_get_dma_data(rtd
->cpu_dai
, st
);
399 ret
= snd_hwparams_to_dma_slave_config(st
, params
, sc
);
403 sc
->dst_addr
= dma_data
->addr
;
404 sc
->dst_addr_width
= dma_data
->addr_width
;
405 sc
->dst_maxburst
= 4 * i2s_channels
;
410 static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config
= {
411 .prepare_slave_config
= img_i2s_out_dma_prepare_slave_config
414 static int img_i2s_out_probe(struct platform_device
*pdev
)
416 struct img_i2s_out
*i2s
;
417 struct resource
*res
;
420 unsigned int max_i2s_chan_pow_2
;
422 struct device
*dev
= &pdev
->dev
;
424 i2s
= devm_kzalloc(&pdev
->dev
, sizeof(*i2s
), GFP_KERNEL
);
428 platform_set_drvdata(pdev
, i2s
);
430 i2s
->dev
= &pdev
->dev
;
432 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
433 base
= devm_ioremap_resource(&pdev
->dev
, res
);
435 return PTR_ERR(base
);
439 if (of_property_read_u32(pdev
->dev
.of_node
, "img,i2s-channels",
440 &i2s
->max_i2s_chan
)) {
441 dev_err(&pdev
->dev
, "No img,i2s-channels property\n");
445 max_i2s_chan_pow_2
= 1 << get_count_order(i2s
->max_i2s_chan
);
447 i2s
->channel_base
= base
+ (max_i2s_chan_pow_2
* 0x20);
449 i2s
->rst
= devm_reset_control_get(&pdev
->dev
, "rst");
450 if (IS_ERR(i2s
->rst
)) {
451 if (PTR_ERR(i2s
->rst
) != -EPROBE_DEFER
)
452 dev_err(&pdev
->dev
, "No top level reset found\n");
453 return PTR_ERR(i2s
->rst
);
456 i2s
->clk_sys
= devm_clk_get(&pdev
->dev
, "sys");
457 if (IS_ERR(i2s
->clk_sys
)) {
458 if (PTR_ERR(i2s
->clk_sys
) != -EPROBE_DEFER
)
459 dev_err(dev
, "Failed to acquire clock 'sys'\n");
460 return PTR_ERR(i2s
->clk_sys
);
463 i2s
->clk_ref
= devm_clk_get(&pdev
->dev
, "ref");
464 if (IS_ERR(i2s
->clk_ref
)) {
465 if (PTR_ERR(i2s
->clk_ref
) != -EPROBE_DEFER
)
466 dev_err(dev
, "Failed to acquire clock 'ref'\n");
467 return PTR_ERR(i2s
->clk_ref
);
470 ret
= clk_prepare_enable(i2s
->clk_sys
);
474 reg
= IMG_I2S_OUT_CTL_FRM_SIZE_MASK
;
475 img_i2s_out_writel(i2s
, reg
, IMG_I2S_OUT_CTL
);
477 reg
= IMG_I2S_OUT_CHAN_CTL_JUST_MASK
|
478 IMG_I2S_OUT_CHAN_CTL_LT_MASK
|
479 IMG_I2S_OUT_CHAN_CTL_CH_MASK
|
480 (8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT
);
482 for (i
= 0; i
< i2s
->max_i2s_chan
; i
++)
483 img_i2s_out_ch_writel(i2s
, i
, reg
, IMG_I2S_OUT_CH_CTL
);
485 img_i2s_out_reset(i2s
);
487 pm_runtime_enable(&pdev
->dev
);
488 if (!pm_runtime_enabled(&pdev
->dev
)) {
489 ret
= img_i2s_out_resume(&pdev
->dev
);
494 i2s
->active_channels
= 1;
495 i2s
->dma_data
.addr
= res
->start
+ IMG_I2S_OUT_TX_FIFO
;
496 i2s
->dma_data
.addr_width
= 4;
497 i2s
->dma_data
.maxburst
= 4;
499 i2s
->dai_driver
.probe
= img_i2s_out_dai_probe
;
500 i2s
->dai_driver
.playback
.channels_min
= 2;
501 i2s
->dai_driver
.playback
.channels_max
= i2s
->max_i2s_chan
* 2;
502 i2s
->dai_driver
.playback
.rates
= SNDRV_PCM_RATE_8000_192000
;
503 i2s
->dai_driver
.playback
.formats
= SNDRV_PCM_FMTBIT_S32_LE
;
504 i2s
->dai_driver
.ops
= &img_i2s_out_dai_ops
;
506 ret
= devm_snd_soc_register_component(&pdev
->dev
,
507 &img_i2s_out_component
, &i2s
->dai_driver
, 1);
511 ret
= devm_snd_dmaengine_pcm_register(&pdev
->dev
,
512 &img_i2s_out_dma_config
, 0);
519 if (!pm_runtime_status_suspended(&pdev
->dev
))
520 img_i2s_out_suspend(&pdev
->dev
);
522 pm_runtime_disable(&pdev
->dev
);
523 clk_disable_unprepare(i2s
->clk_sys
);
528 static int img_i2s_out_dev_remove(struct platform_device
*pdev
)
530 struct img_i2s_out
*i2s
= platform_get_drvdata(pdev
);
532 pm_runtime_disable(&pdev
->dev
);
533 if (!pm_runtime_status_suspended(&pdev
->dev
))
534 img_i2s_out_suspend(&pdev
->dev
);
536 clk_disable_unprepare(i2s
->clk_sys
);
541 static const struct of_device_id img_i2s_out_of_match
[] = {
542 { .compatible
= "img,i2s-out" },
545 MODULE_DEVICE_TABLE(of
, img_i2s_out_of_match
);
547 static const struct dev_pm_ops img_i2s_out_pm_ops
= {
548 SET_RUNTIME_PM_OPS(img_i2s_out_suspend
,
549 img_i2s_out_resume
, NULL
)
552 static struct platform_driver img_i2s_out_driver
= {
554 .name
= "img-i2s-out",
555 .of_match_table
= img_i2s_out_of_match
,
556 .pm
= &img_i2s_out_pm_ops
558 .probe
= img_i2s_out_probe
,
559 .remove
= img_i2s_out_dev_remove
561 module_platform_driver(img_i2s_out_driver
);
563 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
564 MODULE_DESCRIPTION("IMG I2S Output Driver");
565 MODULE_LICENSE("GPL v2");