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 int img_spdif_out_dai_probe(struct snd_soc_dai
*dai
)
292 struct img_spdif_out
*spdif
= snd_soc_dai_get_drvdata(dai
);
294 snd_soc_dai_init_dma_data(dai
, &spdif
->dma_data
, NULL
);
296 snd_soc_add_dai_controls(dai
, img_spdif_out_controls
,
297 ARRAY_SIZE(img_spdif_out_controls
));
302 static const struct snd_soc_dai_ops img_spdif_out_dai_ops
= {
303 .probe
= img_spdif_out_dai_probe
,
304 .trigger
= img_spdif_out_trigger
,
305 .hw_params
= img_spdif_out_hw_params
308 static struct snd_soc_dai_driver img_spdif_out_dai
= {
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",
320 .legacy_dai_naming
= 1,
323 static int img_spdif_out_probe(struct platform_device
*pdev
)
325 struct img_spdif_out
*spdif
;
326 struct resource
*res
;
329 struct device
*dev
= &pdev
->dev
;
331 spdif
= devm_kzalloc(&pdev
->dev
, sizeof(*spdif
), GFP_KERNEL
);
335 platform_set_drvdata(pdev
, spdif
);
337 spdif
->dev
= &pdev
->dev
;
339 base
= devm_platform_get_and_ioremap_resource(pdev
, 0, &res
);
341 return PTR_ERR(base
);
345 spdif
->rst
= devm_reset_control_get_exclusive(&pdev
->dev
, "rst");
346 if (IS_ERR(spdif
->rst
))
347 return dev_err_probe(&pdev
->dev
, PTR_ERR(spdif
->rst
),
348 "No top level reset found\n");
350 spdif
->clk_sys
= devm_clk_get(&pdev
->dev
, "sys");
351 if (IS_ERR(spdif
->clk_sys
))
352 return dev_err_probe(dev
, PTR_ERR(spdif
->clk_sys
),
353 "Failed to acquire clock 'sys'\n");
355 spdif
->clk_ref
= devm_clk_get(&pdev
->dev
, "ref");
356 if (IS_ERR(spdif
->clk_ref
))
357 return dev_err_probe(dev
, PTR_ERR(spdif
->clk_ref
),
358 "Failed to acquire clock 'ref'\n");
360 pm_runtime_enable(&pdev
->dev
);
361 if (!pm_runtime_enabled(&pdev
->dev
)) {
362 ret
= img_spdif_out_runtime_resume(&pdev
->dev
);
366 ret
= pm_runtime_resume_and_get(&pdev
->dev
);
370 img_spdif_out_writel(spdif
, IMG_SPDIF_OUT_CTL_FS_MASK
,
373 img_spdif_out_reset(spdif
);
374 pm_runtime_put(&pdev
->dev
);
376 spin_lock_init(&spdif
->lock
);
378 spdif
->dma_data
.addr
= res
->start
+ IMG_SPDIF_OUT_TX_FIFO
;
379 spdif
->dma_data
.addr_width
= 4;
380 spdif
->dma_data
.maxburst
= 4;
382 ret
= devm_snd_soc_register_component(&pdev
->dev
,
383 &img_spdif_out_component
,
384 &img_spdif_out_dai
, 1);
388 ret
= devm_snd_dmaengine_pcm_register(&pdev
->dev
, NULL
, 0);
392 dev_dbg(&pdev
->dev
, "Probe successful\n");
397 if (!pm_runtime_status_suspended(&pdev
->dev
))
398 img_spdif_out_runtime_suspend(&pdev
->dev
);
400 pm_runtime_disable(&pdev
->dev
);
405 static void img_spdif_out_dev_remove(struct platform_device
*pdev
)
407 pm_runtime_disable(&pdev
->dev
);
408 if (!pm_runtime_status_suspended(&pdev
->dev
))
409 img_spdif_out_runtime_suspend(&pdev
->dev
);
412 #ifdef CONFIG_PM_SLEEP
413 static int img_spdif_out_suspend(struct device
*dev
)
415 struct img_spdif_out
*spdif
= dev_get_drvdata(dev
);
418 if (pm_runtime_status_suspended(dev
)) {
419 ret
= img_spdif_out_runtime_resume(dev
);
424 spdif
->suspend_ctl
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CTL
);
425 spdif
->suspend_csl
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSL
);
426 spdif
->suspend_csh
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSH_UV
);
428 img_spdif_out_runtime_suspend(dev
);
433 static int img_spdif_out_resume(struct device
*dev
)
435 struct img_spdif_out
*spdif
= dev_get_drvdata(dev
);
438 ret
= img_spdif_out_runtime_resume(dev
);
442 img_spdif_out_writel(spdif
, spdif
->suspend_ctl
, IMG_SPDIF_OUT_CTL
);
443 img_spdif_out_writel(spdif
, spdif
->suspend_csl
, IMG_SPDIF_OUT_CSL
);
444 img_spdif_out_writel(spdif
, spdif
->suspend_csh
, IMG_SPDIF_OUT_CSH_UV
);
446 if (pm_runtime_status_suspended(dev
))
447 img_spdif_out_runtime_suspend(dev
);
452 static const struct of_device_id img_spdif_out_of_match
[] = {
453 { .compatible
= "img,spdif-out" },
456 MODULE_DEVICE_TABLE(of
, img_spdif_out_of_match
);
458 static const struct dev_pm_ops img_spdif_out_pm_ops
= {
459 SET_RUNTIME_PM_OPS(img_spdif_out_runtime_suspend
,
460 img_spdif_out_runtime_resume
, NULL
)
461 SET_SYSTEM_SLEEP_PM_OPS(img_spdif_out_suspend
, img_spdif_out_resume
)
464 static struct platform_driver img_spdif_out_driver
= {
466 .name
= "img-spdif-out",
467 .of_match_table
= img_spdif_out_of_match
,
468 .pm
= &img_spdif_out_pm_ops
470 .probe
= img_spdif_out_probe
,
471 .remove
= img_spdif_out_dev_remove
473 module_platform_driver(img_spdif_out_driver
);
475 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
476 MODULE_DESCRIPTION("IMG SPDIF Output driver");
477 MODULE_LICENSE("GPL v2");