2 * ALSA SoC SPDIF Out Audio Layer for spear processors
4 * Copyright (C) 2012 ST Microelectronics
5 * Vipin Kumar <vipin.kumar@st.com>
7 * This file is licensed under the terms of the GNU General Public
8 * License version 2. This program is licensed "as is" without any
9 * warranty of any kind, whether express or implied.
12 #include <linux/clk.h>
13 #include <linux/delay.h>
14 #include <linux/device.h>
15 #include <linux/kernel.h>
16 #include <linux/init.h>
18 #include <linux/ioport.h>
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <sound/soc.h>
22 #include <sound/spear_dma.h>
23 #include <sound/spear_spdif.h>
24 #include "spdif_out_regs.h"
26 struct spdif_out_params
{
32 struct spdif_out_dev
{
34 struct spear_dma_data dma_params
;
35 struct spdif_out_params saved_params
;
37 void __iomem
*io_base
;
40 static void spdif_out_configure(struct spdif_out_dev
*host
)
42 writel(SPDIF_OUT_RESET
, host
->io_base
+ SPDIF_OUT_SOFT_RST
);
44 writel(readl(host
->io_base
+ SPDIF_OUT_SOFT_RST
) & ~SPDIF_OUT_RESET
,
45 host
->io_base
+ SPDIF_OUT_SOFT_RST
);
47 writel(SPDIF_OUT_FDMA_TRIG_16
| SPDIF_OUT_MEMFMT_16_16
|
48 SPDIF_OUT_VALID_HW
| SPDIF_OUT_USER_HW
|
49 SPDIF_OUT_CHNLSTA_HW
| SPDIF_OUT_PARITY_HW
,
50 host
->io_base
+ SPDIF_OUT_CFG
);
52 writel(0x7F, host
->io_base
+ SPDIF_OUT_INT_STA_CLR
);
53 writel(0x7F, host
->io_base
+ SPDIF_OUT_INT_EN_CLR
);
56 static int spdif_out_startup(struct snd_pcm_substream
*substream
,
57 struct snd_soc_dai
*cpu_dai
)
59 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(cpu_dai
);
62 if (substream
->stream
!= SNDRV_PCM_STREAM_PLAYBACK
)
65 ret
= clk_enable(host
->clk
);
70 spdif_out_configure(host
);
75 static void spdif_out_shutdown(struct snd_pcm_substream
*substream
,
76 struct snd_soc_dai
*dai
)
78 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
80 if (substream
->stream
!= SNDRV_PCM_STREAM_PLAYBACK
)
83 clk_disable(host
->clk
);
84 host
->running
= false;
87 static void spdif_out_clock(struct spdif_out_dev
*host
, u32 core_freq
,
92 clk_set_rate(host
->clk
, core_freq
);
93 divider
= DIV_ROUND_CLOSEST(clk_get_rate(host
->clk
), (rate
* 128));
95 ctrl
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
96 ctrl
&= ~SPDIF_DIVIDER_MASK
;
97 ctrl
|= (divider
<< SPDIF_DIVIDER_SHIFT
) & SPDIF_DIVIDER_MASK
;
98 writel(ctrl
, host
->io_base
+ SPDIF_OUT_CTRL
);
101 static int spdif_out_hw_params(struct snd_pcm_substream
*substream
,
102 struct snd_pcm_hw_params
*params
,
103 struct snd_soc_dai
*dai
)
105 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
108 if (substream
->stream
!= SNDRV_PCM_STREAM_PLAYBACK
)
111 rate
= params_rate(params
);
119 * The clock is multiplied by 10 to bring it to feasible range
120 * of frequencies for sscg
122 core_freq
= 64000 * 128 * 10; /* 81.92 MHz */
130 core_freq
= 176400 * 128; /* 22.5792 MHz */
136 core_freq
= 192000 * 128; /* 24.576 MHz */
140 spdif_out_clock(host
, core_freq
, rate
);
141 host
->saved_params
.core_freq
= core_freq
;
142 host
->saved_params
.rate
= rate
;
147 static int spdif_out_trigger(struct snd_pcm_substream
*substream
, int cmd
,
148 struct snd_soc_dai
*dai
)
150 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
154 if (substream
->stream
!= SNDRV_PCM_STREAM_PLAYBACK
)
158 case SNDRV_PCM_TRIGGER_START
:
159 case SNDRV_PCM_TRIGGER_RESUME
:
160 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
161 ctrl
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
162 ctrl
&= ~SPDIF_OPMODE_MASK
;
163 if (!host
->saved_params
.mute
)
164 ctrl
|= SPDIF_OPMODE_AUD_DATA
|
167 ctrl
|= SPDIF_OPMODE_MUTE_PCM
;
168 writel(ctrl
, host
->io_base
+ SPDIF_OUT_CTRL
);
171 case SNDRV_PCM_TRIGGER_STOP
:
172 case SNDRV_PCM_TRIGGER_SUSPEND
:
173 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
174 ctrl
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
175 ctrl
&= ~SPDIF_OPMODE_MASK
;
176 ctrl
|= SPDIF_OPMODE_OFF
;
177 writel(ctrl
, host
->io_base
+ SPDIF_OUT_CTRL
);
187 static int spdif_digital_mute(struct snd_soc_dai
*dai
, int mute
)
189 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
192 host
->saved_params
.mute
= mute
;
193 val
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
194 val
&= ~SPDIF_OPMODE_MASK
;
197 val
|= SPDIF_OPMODE_MUTE_PCM
;
200 val
|= SPDIF_OPMODE_AUD_DATA
| SPDIF_STATE_NORMAL
;
202 val
|= SPDIF_OPMODE_OFF
;
205 writel(val
, host
->io_base
+ SPDIF_OUT_CTRL
);
209 static int spdif_mute_get(struct snd_kcontrol
*kcontrol
,
210 struct snd_ctl_elem_value
*ucontrol
)
212 struct snd_soc_codec
*codec
= snd_kcontrol_chip(kcontrol
);
213 struct snd_soc_card
*card
= codec
->card
;
214 struct snd_soc_pcm_runtime
*rtd
= card
->rtd
;
215 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
216 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(cpu_dai
);
218 ucontrol
->value
.integer
.value
[0] = host
->saved_params
.mute
;
222 static int spdif_mute_put(struct snd_kcontrol
*kcontrol
,
223 struct snd_ctl_elem_value
*ucontrol
)
225 struct snd_soc_codec
*codec
= snd_kcontrol_chip(kcontrol
);
226 struct snd_soc_card
*card
= codec
->card
;
227 struct snd_soc_pcm_runtime
*rtd
= card
->rtd
;
228 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
229 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(cpu_dai
);
231 if (host
->saved_params
.mute
== ucontrol
->value
.integer
.value
[0])
234 spdif_digital_mute(cpu_dai
, ucontrol
->value
.integer
.value
[0]);
238 static const struct snd_kcontrol_new spdif_out_controls
[] = {
239 SOC_SINGLE_BOOL_EXT("IEC958 Playback Switch", 0,
240 spdif_mute_get
, spdif_mute_put
),
243 static int spdif_soc_dai_probe(struct snd_soc_dai
*dai
)
245 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
247 dai
->playback_dma_data
= &host
->dma_params
;
249 return snd_soc_add_dai_controls(dai
, spdif_out_controls
,
250 ARRAY_SIZE(spdif_out_controls
));
253 static const struct snd_soc_dai_ops spdif_out_dai_ops
= {
254 .digital_mute
= spdif_digital_mute
,
255 .startup
= spdif_out_startup
,
256 .shutdown
= spdif_out_shutdown
,
257 .trigger
= spdif_out_trigger
,
258 .hw_params
= spdif_out_hw_params
,
261 static struct snd_soc_dai_driver spdif_out_dai
= {
265 .rates
= (SNDRV_PCM_RATE_32000
| SNDRV_PCM_RATE_44100
| \
266 SNDRV_PCM_RATE_48000
| SNDRV_PCM_RATE_96000
| \
267 SNDRV_PCM_RATE_192000
),
268 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
270 .probe
= spdif_soc_dai_probe
,
271 .ops
= &spdif_out_dai_ops
,
274 static const struct snd_soc_component_driver spdif_out_component
= {
278 static int spdif_out_probe(struct platform_device
*pdev
)
280 struct spdif_out_dev
*host
;
281 struct spear_spdif_platform_data
*pdata
;
282 struct resource
*res
;
285 host
= devm_kzalloc(&pdev
->dev
, sizeof(*host
), GFP_KERNEL
);
287 dev_warn(&pdev
->dev
, "kzalloc fail\n");
291 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
292 host
->io_base
= devm_ioremap_resource(&pdev
->dev
, res
);
293 if (IS_ERR(host
->io_base
))
294 return PTR_ERR(host
->io_base
);
296 host
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
297 if (IS_ERR(host
->clk
))
298 return PTR_ERR(host
->clk
);
300 pdata
= dev_get_platdata(&pdev
->dev
);
302 host
->dma_params
.data
= pdata
->dma_params
;
303 host
->dma_params
.addr
= res
->start
+ SPDIF_OUT_FIFO_DATA
;
304 host
->dma_params
.max_burst
= 16;
305 host
->dma_params
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
306 host
->dma_params
.filter
= pdata
->filter
;
308 dev_set_drvdata(&pdev
->dev
, host
);
310 ret
= snd_soc_register_component(&pdev
->dev
, &spdif_out_component
,
315 static int spdif_out_remove(struct platform_device
*pdev
)
317 snd_soc_unregister_component(&pdev
->dev
);
323 static int spdif_out_suspend(struct device
*dev
)
325 struct platform_device
*pdev
= to_platform_device(dev
);
326 struct spdif_out_dev
*host
= dev_get_drvdata(&pdev
->dev
);
329 clk_disable(host
->clk
);
334 static int spdif_out_resume(struct device
*dev
)
336 struct platform_device
*pdev
= to_platform_device(dev
);
337 struct spdif_out_dev
*host
= dev_get_drvdata(&pdev
->dev
);
340 clk_enable(host
->clk
);
341 spdif_out_configure(host
);
342 spdif_out_clock(host
, host
->saved_params
.core_freq
,
343 host
->saved_params
.rate
);
348 static SIMPLE_DEV_PM_OPS(spdif_out_dev_pm_ops
, spdif_out_suspend
, \
351 #define SPDIF_OUT_DEV_PM_OPS (&spdif_out_dev_pm_ops)
354 #define SPDIF_OUT_DEV_PM_OPS NULL
358 static struct platform_driver spdif_out_driver
= {
359 .probe
= spdif_out_probe
,
360 .remove
= spdif_out_remove
,
363 .owner
= THIS_MODULE
,
364 .pm
= SPDIF_OUT_DEV_PM_OPS
,
368 module_platform_driver(spdif_out_driver
);
370 MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");
371 MODULE_DESCRIPTION("SPEAr SPDIF OUT SoC Interface");
372 MODULE_LICENSE("GPL");
373 MODULE_ALIAS("platform:spdif_out");