2 * IMG SPDIF 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_SPDIF_OUT_TX_FIFO 0x0
31 #define IMG_SPDIF_OUT_CTL 0x4
32 #define IMG_SPDIF_OUT_CTL_FS_MASK BIT(4)
33 #define IMG_SPDIF_OUT_CTL_CLK_MASK BIT(2)
34 #define IMG_SPDIF_OUT_CTL_SRT_MASK BIT(0)
36 #define IMG_SPDIF_OUT_CSL 0x14
38 #define IMG_SPDIF_OUT_CSH_UV 0x18
39 #define IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT 0
40 #define IMG_SPDIF_OUT_CSH_UV_CSH_MASK 0xff
42 struct img_spdif_out
{
47 struct snd_dmaengine_dai_dma_data dma_data
;
49 struct reset_control
*rst
;
52 static int img_spdif_out_suspend(struct device
*dev
)
54 struct img_spdif_out
*spdif
= dev_get_drvdata(dev
);
56 clk_disable_unprepare(spdif
->clk_ref
);
61 static int img_spdif_out_resume(struct device
*dev
)
63 struct img_spdif_out
*spdif
= dev_get_drvdata(dev
);
66 ret
= clk_prepare_enable(spdif
->clk_ref
);
68 dev_err(dev
, "clk_enable failed: %d\n", ret
);
75 static inline void img_spdif_out_writel(struct img_spdif_out
*spdif
, u32 val
,
78 writel(val
, spdif
->base
+ reg
);
81 static inline u32
img_spdif_out_readl(struct img_spdif_out
*spdif
, u32 reg
)
83 return readl(spdif
->base
+ reg
);
86 static void img_spdif_out_reset(struct img_spdif_out
*spdif
)
88 u32 ctl
, status_low
, status_high
;
90 ctl
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CTL
) &
91 ~IMG_SPDIF_OUT_CTL_SRT_MASK
;
92 status_low
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSL
);
93 status_high
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSH_UV
);
95 reset_control_assert(spdif
->rst
);
96 reset_control_deassert(spdif
->rst
);
98 img_spdif_out_writel(spdif
, ctl
, IMG_SPDIF_OUT_CTL
);
99 img_spdif_out_writel(spdif
, status_low
, IMG_SPDIF_OUT_CSL
);
100 img_spdif_out_writel(spdif
, status_high
, IMG_SPDIF_OUT_CSH_UV
);
103 static int img_spdif_out_info(struct snd_kcontrol
*kcontrol
,
104 struct snd_ctl_elem_info
*uinfo
)
106 uinfo
->type
= SNDRV_CTL_ELEM_TYPE_IEC958
;
112 static int img_spdif_out_get_status_mask(struct snd_kcontrol
*kcontrol
,
113 struct snd_ctl_elem_value
*ucontrol
)
115 ucontrol
->value
.iec958
.status
[0] = 0xff;
116 ucontrol
->value
.iec958
.status
[1] = 0xff;
117 ucontrol
->value
.iec958
.status
[2] = 0xff;
118 ucontrol
->value
.iec958
.status
[3] = 0xff;
119 ucontrol
->value
.iec958
.status
[4] = 0xff;
124 static int img_spdif_out_get_status(struct snd_kcontrol
*kcontrol
,
125 struct snd_ctl_elem_value
*ucontrol
)
127 struct snd_soc_dai
*cpu_dai
= snd_kcontrol_chip(kcontrol
);
128 struct img_spdif_out
*spdif
= snd_soc_dai_get_drvdata(cpu_dai
);
132 spin_lock_irqsave(&spdif
->lock
, flags
);
134 reg
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSL
);
135 ucontrol
->value
.iec958
.status
[0] = reg
& 0xff;
136 ucontrol
->value
.iec958
.status
[1] = (reg
>> 8) & 0xff;
137 ucontrol
->value
.iec958
.status
[2] = (reg
>> 16) & 0xff;
138 ucontrol
->value
.iec958
.status
[3] = (reg
>> 24) & 0xff;
140 reg
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSH_UV
);
141 ucontrol
->value
.iec958
.status
[4] =
142 (reg
& IMG_SPDIF_OUT_CSH_UV_CSH_MASK
) >>
143 IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT
;
145 spin_unlock_irqrestore(&spdif
->lock
, flags
);
150 static int img_spdif_out_set_status(struct snd_kcontrol
*kcontrol
,
151 struct snd_ctl_elem_value
*ucontrol
)
153 struct snd_soc_dai
*cpu_dai
= snd_kcontrol_chip(kcontrol
);
154 struct img_spdif_out
*spdif
= snd_soc_dai_get_drvdata(cpu_dai
);
158 reg
= ((u32
)ucontrol
->value
.iec958
.status
[3] << 24);
159 reg
|= ((u32
)ucontrol
->value
.iec958
.status
[2] << 16);
160 reg
|= ((u32
)ucontrol
->value
.iec958
.status
[1] << 8);
161 reg
|= (u32
)ucontrol
->value
.iec958
.status
[0];
163 spin_lock_irqsave(&spdif
->lock
, flags
);
165 img_spdif_out_writel(spdif
, reg
, IMG_SPDIF_OUT_CSL
);
167 reg
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CSH_UV
);
168 reg
&= ~IMG_SPDIF_OUT_CSH_UV_CSH_MASK
;
169 reg
|= (u32
)ucontrol
->value
.iec958
.status
[4] <<
170 IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT
;
171 img_spdif_out_writel(spdif
, reg
, IMG_SPDIF_OUT_CSH_UV
);
173 spin_unlock_irqrestore(&spdif
->lock
, flags
);
178 static struct snd_kcontrol_new img_spdif_out_controls
[] = {
180 .access
= SNDRV_CTL_ELEM_ACCESS_READ
,
181 .iface
= SNDRV_CTL_ELEM_IFACE_PCM
,
182 .name
= SNDRV_CTL_NAME_IEC958("", PLAYBACK
, MASK
),
183 .info
= img_spdif_out_info
,
184 .get
= img_spdif_out_get_status_mask
187 .iface
= SNDRV_CTL_ELEM_IFACE_PCM
,
188 .name
= SNDRV_CTL_NAME_IEC958("", PLAYBACK
, DEFAULT
),
189 .info
= img_spdif_out_info
,
190 .get
= img_spdif_out_get_status
,
191 .put
= img_spdif_out_set_status
195 static int img_spdif_out_trigger(struct snd_pcm_substream
*substream
, int cmd
,
196 struct snd_soc_dai
*dai
)
198 struct img_spdif_out
*spdif
= snd_soc_dai_get_drvdata(dai
);
203 case SNDRV_PCM_TRIGGER_START
:
204 case SNDRV_PCM_TRIGGER_RESUME
:
205 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
206 reg
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CTL
);
207 reg
|= IMG_SPDIF_OUT_CTL_SRT_MASK
;
208 img_spdif_out_writel(spdif
, reg
, IMG_SPDIF_OUT_CTL
);
210 case SNDRV_PCM_TRIGGER_STOP
:
211 case SNDRV_PCM_TRIGGER_SUSPEND
:
212 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
213 spin_lock_irqsave(&spdif
->lock
, flags
);
214 img_spdif_out_reset(spdif
);
215 spin_unlock_irqrestore(&spdif
->lock
, flags
);
224 static int img_spdif_out_hw_params(struct snd_pcm_substream
*substream
,
225 struct snd_pcm_hw_params
*params
, struct snd_soc_dai
*dai
)
227 struct img_spdif_out
*spdif
= snd_soc_dai_get_drvdata(dai
);
228 unsigned int channels
;
229 long pre_div_a
, pre_div_b
, diff_a
, diff_b
, rate
, clk_rate
;
231 snd_pcm_format_t format
;
233 rate
= params_rate(params
);
234 format
= params_format(params
);
235 channels
= params_channels(params
);
237 dev_dbg(spdif
->dev
, "hw_params rate %ld channels %u format %u\n",
238 rate
, channels
, format
);
240 if (format
!= SNDRV_PCM_FORMAT_S32_LE
)
246 pre_div_a
= clk_round_rate(spdif
->clk_ref
, rate
* 256);
249 pre_div_b
= clk_round_rate(spdif
->clk_ref
, rate
* 384);
253 diff_a
= abs((pre_div_a
/ 256) - rate
);
254 diff_b
= abs((pre_div_b
/ 384) - rate
);
256 /* If diffs are equal, use lower clock rate */
258 clk_set_rate(spdif
->clk_ref
, pre_div_b
);
260 clk_set_rate(spdif
->clk_ref
, pre_div_a
);
263 * Another driver (eg machine driver) may have rejected the above
264 * change. Get the current rate and set the register bit according to
267 clk_rate
= clk_get_rate(spdif
->clk_ref
);
269 diff_a
= abs((clk_rate
/ 256) - rate
);
270 diff_b
= abs((clk_rate
/ 384) - rate
);
272 reg
= img_spdif_out_readl(spdif
, IMG_SPDIF_OUT_CTL
);
273 if (diff_a
<= diff_b
)
274 reg
&= ~IMG_SPDIF_OUT_CTL_CLK_MASK
;
276 reg
|= IMG_SPDIF_OUT_CTL_CLK_MASK
;
277 img_spdif_out_writel(spdif
, reg
, IMG_SPDIF_OUT_CTL
);
282 static const struct snd_soc_dai_ops img_spdif_out_dai_ops
= {
283 .trigger
= img_spdif_out_trigger
,
284 .hw_params
= img_spdif_out_hw_params
287 static int img_spdif_out_dai_probe(struct snd_soc_dai
*dai
)
289 struct img_spdif_out
*spdif
= snd_soc_dai_get_drvdata(dai
);
291 snd_soc_dai_init_dma_data(dai
, &spdif
->dma_data
, NULL
);
293 snd_soc_add_dai_controls(dai
, img_spdif_out_controls
,
294 ARRAY_SIZE(img_spdif_out_controls
));
299 static struct snd_soc_dai_driver img_spdif_out_dai
= {
300 .probe
= img_spdif_out_dai_probe
,
304 .rates
= SNDRV_PCM_RATE_8000_192000
,
305 .formats
= SNDRV_PCM_FMTBIT_S32_LE
307 .ops
= &img_spdif_out_dai_ops
310 static const struct snd_soc_component_driver img_spdif_out_component
= {
311 .name
= "img-spdif-out"
314 static int img_spdif_out_probe(struct platform_device
*pdev
)
316 struct img_spdif_out
*spdif
;
317 struct resource
*res
;
320 struct device
*dev
= &pdev
->dev
;
322 spdif
= devm_kzalloc(&pdev
->dev
, sizeof(*spdif
), GFP_KERNEL
);
326 platform_set_drvdata(pdev
, spdif
);
328 spdif
->dev
= &pdev
->dev
;
330 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
331 base
= devm_ioremap_resource(&pdev
->dev
, res
);
333 return PTR_ERR(base
);
337 spdif
->rst
= devm_reset_control_get(&pdev
->dev
, "rst");
338 if (IS_ERR(spdif
->rst
)) {
339 if (PTR_ERR(spdif
->rst
) != -EPROBE_DEFER
)
340 dev_err(&pdev
->dev
, "No top level reset found\n");
341 return PTR_ERR(spdif
->rst
);
344 spdif
->clk_sys
= devm_clk_get(&pdev
->dev
, "sys");
345 if (IS_ERR(spdif
->clk_sys
)) {
346 if (PTR_ERR(spdif
->clk_sys
) != -EPROBE_DEFER
)
347 dev_err(dev
, "Failed to acquire clock 'sys'\n");
348 return PTR_ERR(spdif
->clk_sys
);
351 spdif
->clk_ref
= devm_clk_get(&pdev
->dev
, "ref");
352 if (IS_ERR(spdif
->clk_ref
)) {
353 if (PTR_ERR(spdif
->clk_ref
) != -EPROBE_DEFER
)
354 dev_err(dev
, "Failed to acquire clock 'ref'\n");
355 return PTR_ERR(spdif
->clk_ref
);
358 ret
= clk_prepare_enable(spdif
->clk_sys
);
362 img_spdif_out_writel(spdif
, IMG_SPDIF_OUT_CTL_FS_MASK
,
365 img_spdif_out_reset(spdif
);
367 pm_runtime_enable(&pdev
->dev
);
368 if (!pm_runtime_enabled(&pdev
->dev
)) {
369 ret
= img_spdif_out_resume(&pdev
->dev
);
374 spin_lock_init(&spdif
->lock
);
376 spdif
->dma_data
.addr
= res
->start
+ IMG_SPDIF_OUT_TX_FIFO
;
377 spdif
->dma_data
.addr_width
= 4;
378 spdif
->dma_data
.maxburst
= 4;
380 ret
= devm_snd_soc_register_component(&pdev
->dev
,
381 &img_spdif_out_component
,
382 &img_spdif_out_dai
, 1);
386 ret
= devm_snd_dmaengine_pcm_register(&pdev
->dev
, NULL
, 0);
390 dev_dbg(&pdev
->dev
, "Probe successful\n");
395 if (!pm_runtime_status_suspended(&pdev
->dev
))
396 img_spdif_out_suspend(&pdev
->dev
);
398 pm_runtime_disable(&pdev
->dev
);
399 clk_disable_unprepare(spdif
->clk_sys
);
404 static int img_spdif_out_dev_remove(struct platform_device
*pdev
)
406 struct img_spdif_out
*spdif
= platform_get_drvdata(pdev
);
408 pm_runtime_disable(&pdev
->dev
);
409 if (!pm_runtime_status_suspended(&pdev
->dev
))
410 img_spdif_out_suspend(&pdev
->dev
);
412 clk_disable_unprepare(spdif
->clk_sys
);
417 static const struct of_device_id img_spdif_out_of_match
[] = {
418 { .compatible
= "img,spdif-out" },
421 MODULE_DEVICE_TABLE(of
, img_spdif_out_of_match
);
423 static const struct dev_pm_ops img_spdif_out_pm_ops
= {
424 SET_RUNTIME_PM_OPS(img_spdif_out_suspend
,
425 img_spdif_out_resume
, NULL
)
428 static struct platform_driver img_spdif_out_driver
= {
430 .name
= "img-spdif-out",
431 .of_match_table
= img_spdif_out_of_match
,
432 .pm
= &img_spdif_out_pm_ops
434 .probe
= img_spdif_out_probe
,
435 .remove
= img_spdif_out_dev_remove
437 module_platform_driver(img_spdif_out_driver
);
439 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
440 MODULE_DESCRIPTION("IMG SPDIF Output driver");
441 MODULE_LICENSE("GPL v2");