1 // SPDX-License-Identifier: GPL-2.0-only
3 * IMG SPDIF 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_SPDIF_OUT_TX_FIFO 0x0
28 #define IMG_SPDIF_OUT_CTL 0x4
29 #define IMG_SPDIF_OUT_CTL_FS_MASK BIT(4)
30 #define IMG_SPDIF_OUT_CTL_CLK_MASK BIT(2)
31 #define IMG_SPDIF_OUT_CTL_SRT_MASK BIT(0)
33 #define IMG_SPDIF_OUT_CSL 0x14
35 #define IMG_SPDIF_OUT_CSH_UV 0x18
36 #define IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT 0
37 #define IMG_SPDIF_OUT_CSH_UV_CSH_MASK 0xff
39 struct img_spdif_out
{
44 struct snd_dmaengine_dai_dma_data dma_data
;
46 struct reset_control
*rst
;
52 static int img_spdif_out_runtime_suspend(struct device
*dev
)
54 struct img_spdif_out
*spdif
= dev_get_drvdata(dev
);
56 clk_disable_unprepare(spdif
->clk_ref
);
57 clk_disable_unprepare(spdif
->clk_sys
);
62 static int img_spdif_out_runtime_resume(struct device
*dev
)
64 struct img_spdif_out
*spdif
= dev_get_drvdata(dev
);
67 ret
= clk_prepare_enable(spdif
->clk_sys
);
69 dev_err(dev
, "clk_enable failed: %d\n", ret
);
73 ret
= clk_prepare_enable(spdif
->clk_ref
);
75 dev_err(dev
, "clk_enable failed: %d\n", ret
);
76 clk_disable_unprepare(spdif
->clk_sys
);
83 static inline void img_spdif_out_writel(struct img_spdif_out
*spdif
, u32 val
,
86 writel(val
, spdif
->base
+ reg
);
89 static inline u32
img_spdif_out_readl(struct img_spdif_out
*spdif
, u32 reg
)
91 return readl(spdif
->base
+ reg
);
94 static void img_spdif_out_reset(struct img_spdif_out
*spdif
)
96 u32 ctl
, status_low
, status_high
;
98 ctl
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CTL
) &
99 ~IMG_SPDIF_OUT_CTL_SRT_MASK
;
100 status_low
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSL
);
101 status_high
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSH_UV
);
103 reset_control_assert(spdif
->rst
);
104 reset_control_deassert(spdif
->rst
);
106 img_spdif_out_writel(spdif
, ctl
, IMG_SPDIF_OUT_CTL
);
107 img_spdif_out_writel(spdif
, status_low
, IMG_SPDIF_OUT_CSL
);
108 img_spdif_out_writel(spdif
, status_high
, IMG_SPDIF_OUT_CSH_UV
);
111 static int img_spdif_out_info(struct snd_kcontrol
*kcontrol
,
112 struct snd_ctl_elem_info
*uinfo
)
114 uinfo
->type
= SNDRV_CTL_ELEM_TYPE_IEC958
;
120 static int img_spdif_out_get_status_mask(struct snd_kcontrol
*kcontrol
,
121 struct snd_ctl_elem_value
*ucontrol
)
123 ucontrol
->value
.iec958
.status
[0] = 0xff;
124 ucontrol
->value
.iec958
.status
[1] = 0xff;
125 ucontrol
->value
.iec958
.status
[2] = 0xff;
126 ucontrol
->value
.iec958
.status
[3] = 0xff;
127 ucontrol
->value
.iec958
.status
[4] = 0xff;
132 static int img_spdif_out_get_status(struct snd_kcontrol
*kcontrol
,
133 struct snd_ctl_elem_value
*ucontrol
)
135 struct snd_soc_dai
*cpu_dai
= snd_kcontrol_chip(kcontrol
);
136 struct img_spdif_out
*spdif
= snd_soc_dai_get_drvdata(cpu_dai
);
140 spin_lock_irqsave(&spdif
->lock
, flags
);
142 reg
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSL
);
143 ucontrol
->value
.iec958
.status
[0] = reg
& 0xff;
144 ucontrol
->value
.iec958
.status
[1] = (reg
>> 8) & 0xff;
145 ucontrol
->value
.iec958
.status
[2] = (reg
>> 16) & 0xff;
146 ucontrol
->value
.iec958
.status
[3] = (reg
>> 24) & 0xff;
148 reg
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSH_UV
);
149 ucontrol
->value
.iec958
.status
[4] =
150 (reg
& IMG_SPDIF_OUT_CSH_UV_CSH_MASK
) >>
151 IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT
;
153 spin_unlock_irqrestore(&spdif
->lock
, flags
);
158 static int img_spdif_out_set_status(struct snd_kcontrol
*kcontrol
,
159 struct snd_ctl_elem_value
*ucontrol
)
161 struct snd_soc_dai
*cpu_dai
= snd_kcontrol_chip(kcontrol
);
162 struct img_spdif_out
*spdif
= snd_soc_dai_get_drvdata(cpu_dai
);
166 reg
= ((u32
)ucontrol
->value
.iec958
.status
[3] << 24);
167 reg
|= ((u32
)ucontrol
->value
.iec958
.status
[2] << 16);
168 reg
|= ((u32
)ucontrol
->value
.iec958
.status
[1] << 8);
169 reg
|= (u32
)ucontrol
->value
.iec958
.status
[0];
171 spin_lock_irqsave(&spdif
->lock
, flags
);
173 img_spdif_out_writel(spdif
, reg
, IMG_SPDIF_OUT_CSL
);
175 reg
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSH_UV
);
176 reg
&= ~IMG_SPDIF_OUT_CSH_UV_CSH_MASK
;
177 reg
|= (u32
)ucontrol
->value
.iec958
.status
[4] <<
178 IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT
;
179 img_spdif_out_writel(spdif
, reg
, IMG_SPDIF_OUT_CSH_UV
);
181 spin_unlock_irqrestore(&spdif
->lock
, flags
);
186 static struct snd_kcontrol_new img_spdif_out_controls
[] = {
188 .access
= SNDRV_CTL_ELEM_ACCESS_READ
,
189 .iface
= SNDRV_CTL_ELEM_IFACE_PCM
,
190 .name
= SNDRV_CTL_NAME_IEC958("", PLAYBACK
, MASK
),
191 .info
= img_spdif_out_info
,
192 .get
= img_spdif_out_get_status_mask
195 .iface
= SNDRV_CTL_ELEM_IFACE_PCM
,
196 .name
= SNDRV_CTL_NAME_IEC958("", PLAYBACK
, DEFAULT
),
197 .info
= img_spdif_out_info
,
198 .get
= img_spdif_out_get_status
,
199 .put
= img_spdif_out_set_status
203 static int img_spdif_out_trigger(struct snd_pcm_substream
*substream
, int cmd
,
204 struct snd_soc_dai
*dai
)
206 struct img_spdif_out
*spdif
= snd_soc_dai_get_drvdata(dai
);
211 case SNDRV_PCM_TRIGGER_START
:
212 case SNDRV_PCM_TRIGGER_RESUME
:
213 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
214 reg
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CTL
);
215 reg
|= IMG_SPDIF_OUT_CTL_SRT_MASK
;
216 img_spdif_out_writel(spdif
, reg
, IMG_SPDIF_OUT_CTL
);
218 case SNDRV_PCM_TRIGGER_STOP
:
219 case SNDRV_PCM_TRIGGER_SUSPEND
:
220 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
221 spin_lock_irqsave(&spdif
->lock
, flags
);
222 img_spdif_out_reset(spdif
);
223 spin_unlock_irqrestore(&spdif
->lock
, flags
);
232 static int img_spdif_out_hw_params(struct snd_pcm_substream
*substream
,
233 struct snd_pcm_hw_params
*params
, struct snd_soc_dai
*dai
)
235 struct img_spdif_out
*spdif
= snd_soc_dai_get_drvdata(dai
);
236 unsigned int channels
;
237 long pre_div_a
, pre_div_b
, diff_a
, diff_b
, rate
, clk_rate
;
239 snd_pcm_format_t format
;
241 rate
= params_rate(params
);
242 format
= params_format(params
);
243 channels
= params_channels(params
);
245 dev_dbg(spdif
->dev
, "hw_params rate %ld channels %u format %u\n",
246 rate
, channels
, format
);
248 if (format
!= SNDRV_PCM_FORMAT_S32_LE
)
254 pre_div_a
= clk_round_rate(spdif
->clk_ref
, rate
* 256);
257 pre_div_b
= clk_round_rate(spdif
->clk_ref
, rate
* 384);
261 diff_a
= abs((pre_div_a
/ 256) - rate
);
262 diff_b
= abs((pre_div_b
/ 384) - rate
);
264 /* If diffs are equal, use lower clock rate */
266 clk_set_rate(spdif
->clk_ref
, pre_div_b
);
268 clk_set_rate(spdif
->clk_ref
, pre_div_a
);
271 * Another driver (eg machine driver) may have rejected the above
272 * change. Get the current rate and set the register bit according to
275 clk_rate
= clk_get_rate(spdif
->clk_ref
);
277 diff_a
= abs((clk_rate
/ 256) - rate
);
278 diff_b
= abs((clk_rate
/ 384) - rate
);
280 reg
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CTL
);
281 if (diff_a
<= diff_b
)
282 reg
&= ~IMG_SPDIF_OUT_CTL_CLK_MASK
;
284 reg
|= IMG_SPDIF_OUT_CTL_CLK_MASK
;
285 img_spdif_out_writel(spdif
, reg
, IMG_SPDIF_OUT_CTL
);
290 static const struct snd_soc_dai_ops img_spdif_out_dai_ops
= {
291 .trigger
= img_spdif_out_trigger
,
292 .hw_params
= img_spdif_out_hw_params
295 static int img_spdif_out_dai_probe(struct snd_soc_dai
*dai
)
297 struct img_spdif_out
*spdif
= snd_soc_dai_get_drvdata(dai
);
299 snd_soc_dai_init_dma_data(dai
, &spdif
->dma_data
, NULL
);
301 snd_soc_add_dai_controls(dai
, img_spdif_out_controls
,
302 ARRAY_SIZE(img_spdif_out_controls
));
307 static struct snd_soc_dai_driver img_spdif_out_dai
= {
308 .probe
= img_spdif_out_dai_probe
,
312 .rates
= SNDRV_PCM_RATE_8000_192000
,
313 .formats
= SNDRV_PCM_FMTBIT_S32_LE
315 .ops
= &img_spdif_out_dai_ops
318 static const struct snd_soc_component_driver img_spdif_out_component
= {
319 .name
= "img-spdif-out"
322 static int img_spdif_out_probe(struct platform_device
*pdev
)
324 struct img_spdif_out
*spdif
;
325 struct resource
*res
;
328 struct device
*dev
= &pdev
->dev
;
330 spdif
= devm_kzalloc(&pdev
->dev
, sizeof(*spdif
), GFP_KERNEL
);
334 platform_set_drvdata(pdev
, spdif
);
336 spdif
->dev
= &pdev
->dev
;
338 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
339 base
= devm_ioremap_resource(&pdev
->dev
, res
);
341 return PTR_ERR(base
);
345 spdif
->rst
= devm_reset_control_get_exclusive(&pdev
->dev
, "rst");
346 if (IS_ERR(spdif
->rst
)) {
347 if (PTR_ERR(spdif
->rst
) != -EPROBE_DEFER
)
348 dev_err(&pdev
->dev
, "No top level reset found\n");
349 return PTR_ERR(spdif
->rst
);
352 spdif
->clk_sys
= devm_clk_get(&pdev
->dev
, "sys");
353 if (IS_ERR(spdif
->clk_sys
)) {
354 if (PTR_ERR(spdif
->clk_sys
) != -EPROBE_DEFER
)
355 dev_err(dev
, "Failed to acquire clock 'sys'\n");
356 return PTR_ERR(spdif
->clk_sys
);
359 spdif
->clk_ref
= devm_clk_get(&pdev
->dev
, "ref");
360 if (IS_ERR(spdif
->clk_ref
)) {
361 if (PTR_ERR(spdif
->clk_ref
) != -EPROBE_DEFER
)
362 dev_err(dev
, "Failed to acquire clock 'ref'\n");
363 return PTR_ERR(spdif
->clk_ref
);
366 pm_runtime_enable(&pdev
->dev
);
367 if (!pm_runtime_enabled(&pdev
->dev
)) {
368 ret
= img_spdif_out_runtime_resume(&pdev
->dev
);
372 ret
= pm_runtime_get_sync(&pdev
->dev
);
376 img_spdif_out_writel(spdif
, IMG_SPDIF_OUT_CTL_FS_MASK
,
379 img_spdif_out_reset(spdif
);
380 pm_runtime_put(&pdev
->dev
);
382 spin_lock_init(&spdif
->lock
);
384 spdif
->dma_data
.addr
= res
->start
+ IMG_SPDIF_OUT_TX_FIFO
;
385 spdif
->dma_data
.addr_width
= 4;
386 spdif
->dma_data
.maxburst
= 4;
388 ret
= devm_snd_soc_register_component(&pdev
->dev
,
389 &img_spdif_out_component
,
390 &img_spdif_out_dai
, 1);
394 ret
= devm_snd_dmaengine_pcm_register(&pdev
->dev
, NULL
, 0);
398 dev_dbg(&pdev
->dev
, "Probe successful\n");
403 if (!pm_runtime_status_suspended(&pdev
->dev
))
404 img_spdif_out_runtime_suspend(&pdev
->dev
);
406 pm_runtime_disable(&pdev
->dev
);
411 static int img_spdif_out_dev_remove(struct platform_device
*pdev
)
413 pm_runtime_disable(&pdev
->dev
);
414 if (!pm_runtime_status_suspended(&pdev
->dev
))
415 img_spdif_out_runtime_suspend(&pdev
->dev
);
420 #ifdef CONFIG_PM_SLEEP
421 static int img_spdif_out_suspend(struct device
*dev
)
423 struct img_spdif_out
*spdif
= dev_get_drvdata(dev
);
426 if (pm_runtime_status_suspended(dev
)) {
427 ret
= img_spdif_out_runtime_resume(dev
);
432 spdif
->suspend_ctl
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CTL
);
433 spdif
->suspend_csl
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSL
);
434 spdif
->suspend_csh
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSH_UV
);
436 img_spdif_out_runtime_suspend(dev
);
441 static int img_spdif_out_resume(struct device
*dev
)
443 struct img_spdif_out
*spdif
= dev_get_drvdata(dev
);
446 ret
= img_spdif_out_runtime_resume(dev
);
450 img_spdif_out_writel(spdif
, spdif
->suspend_ctl
, IMG_SPDIF_OUT_CTL
);
451 img_spdif_out_writel(spdif
, spdif
->suspend_csl
, IMG_SPDIF_OUT_CSL
);
452 img_spdif_out_writel(spdif
, spdif
->suspend_csh
, IMG_SPDIF_OUT_CSH_UV
);
454 if (pm_runtime_status_suspended(dev
))
455 img_spdif_out_runtime_suspend(dev
);
460 static const struct of_device_id img_spdif_out_of_match
[] = {
461 { .compatible
= "img,spdif-out" },
464 MODULE_DEVICE_TABLE(of
, img_spdif_out_of_match
);
466 static const struct dev_pm_ops img_spdif_out_pm_ops
= {
467 SET_RUNTIME_PM_OPS(img_spdif_out_runtime_suspend
,
468 img_spdif_out_runtime_resume
, NULL
)
469 SET_SYSTEM_SLEEP_PM_OPS(img_spdif_out_suspend
, img_spdif_out_resume
)
472 static struct platform_driver img_spdif_out_driver
= {
474 .name
= "img-spdif-out",
475 .of_match_table
= img_spdif_out_of_match
,
476 .pm
= &img_spdif_out_pm_ops
478 .probe
= img_spdif_out_probe
,
479 .remove
= img_spdif_out_dev_remove
481 module_platform_driver(img_spdif_out_driver
);
483 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
484 MODULE_DESCRIPTION("IMG SPDIF Output driver");
485 MODULE_LICENSE("GPL v2");