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 snd_soc_dai_set_dma_data(cpu_dai
, substream
, (void *)&host
->dma_params
);
67 ret
= clk_enable(host
->clk
);
72 spdif_out_configure(host
);
77 static void spdif_out_shutdown(struct snd_pcm_substream
*substream
,
78 struct snd_soc_dai
*dai
)
80 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
82 if (substream
->stream
!= SNDRV_PCM_STREAM_PLAYBACK
)
85 clk_disable(host
->clk
);
86 host
->running
= false;
87 snd_soc_dai_set_dma_data(dai
, substream
, NULL
);
90 static void spdif_out_clock(struct spdif_out_dev
*host
, u32 core_freq
,
95 clk_set_rate(host
->clk
, core_freq
);
96 divider
= DIV_ROUND_CLOSEST(clk_get_rate(host
->clk
), (rate
* 128));
98 ctrl
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
99 ctrl
&= ~SPDIF_DIVIDER_MASK
;
100 ctrl
|= (divider
<< SPDIF_DIVIDER_SHIFT
) & SPDIF_DIVIDER_MASK
;
101 writel(ctrl
, host
->io_base
+ SPDIF_OUT_CTRL
);
104 static int spdif_out_hw_params(struct snd_pcm_substream
*substream
,
105 struct snd_pcm_hw_params
*params
,
106 struct snd_soc_dai
*dai
)
108 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
111 if (substream
->stream
!= SNDRV_PCM_STREAM_PLAYBACK
)
114 rate
= params_rate(params
);
122 * The clock is multiplied by 10 to bring it to feasible range
123 * of frequencies for sscg
125 core_freq
= 64000 * 128 * 10; /* 81.92 MHz */
133 core_freq
= 176400 * 128; /* 22.5792 MHz */
139 core_freq
= 192000 * 128; /* 24.576 MHz */
143 spdif_out_clock(host
, core_freq
, rate
);
144 host
->saved_params
.core_freq
= core_freq
;
145 host
->saved_params
.rate
= rate
;
150 static int spdif_out_trigger(struct snd_pcm_substream
*substream
, int cmd
,
151 struct snd_soc_dai
*dai
)
153 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
157 if (substream
->stream
!= SNDRV_PCM_STREAM_PLAYBACK
)
161 case SNDRV_PCM_TRIGGER_START
:
162 case SNDRV_PCM_TRIGGER_RESUME
:
163 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
164 ctrl
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
165 ctrl
&= ~SPDIF_OPMODE_MASK
;
166 if (!host
->saved_params
.mute
)
167 ctrl
|= SPDIF_OPMODE_AUD_DATA
|
170 ctrl
|= SPDIF_OPMODE_MUTE_PCM
;
171 writel(ctrl
, host
->io_base
+ SPDIF_OUT_CTRL
);
174 case SNDRV_PCM_TRIGGER_STOP
:
175 case SNDRV_PCM_TRIGGER_SUSPEND
:
176 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
177 ctrl
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
178 ctrl
&= ~SPDIF_OPMODE_MASK
;
179 ctrl
|= SPDIF_OPMODE_OFF
;
180 writel(ctrl
, host
->io_base
+ SPDIF_OUT_CTRL
);
190 static int spdif_digital_mute(struct snd_soc_dai
*dai
, int mute
)
192 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
195 host
->saved_params
.mute
= mute
;
196 val
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
197 val
&= ~SPDIF_OPMODE_MASK
;
200 val
|= SPDIF_OPMODE_MUTE_PCM
;
203 val
|= SPDIF_OPMODE_AUD_DATA
| SPDIF_STATE_NORMAL
;
205 val
|= SPDIF_OPMODE_OFF
;
208 writel(val
, host
->io_base
+ SPDIF_OUT_CTRL
);
212 static int spdif_mute_get(struct snd_kcontrol
*kcontrol
,
213 struct snd_ctl_elem_value
*ucontrol
)
215 struct snd_soc_codec
*codec
= snd_kcontrol_chip(kcontrol
);
216 struct snd_soc_card
*card
= codec
->card
;
217 struct snd_soc_pcm_runtime
*rtd
= card
->rtd
;
218 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
219 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(cpu_dai
);
221 ucontrol
->value
.integer
.value
[0] = host
->saved_params
.mute
;
225 static int spdif_mute_put(struct snd_kcontrol
*kcontrol
,
226 struct snd_ctl_elem_value
*ucontrol
)
228 struct snd_soc_codec
*codec
= snd_kcontrol_chip(kcontrol
);
229 struct snd_soc_card
*card
= codec
->card
;
230 struct snd_soc_pcm_runtime
*rtd
= card
->rtd
;
231 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
232 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(cpu_dai
);
234 if (host
->saved_params
.mute
== ucontrol
->value
.integer
.value
[0])
237 spdif_digital_mute(cpu_dai
, ucontrol
->value
.integer
.value
[0]);
241 static const struct snd_kcontrol_new spdif_out_controls
[] = {
242 SOC_SINGLE_BOOL_EXT("IEC958 Playback Switch", 0,
243 spdif_mute_get
, spdif_mute_put
),
246 int spdif_soc_dai_probe(struct snd_soc_dai
*dai
)
248 return snd_soc_add_dai_controls(dai
, spdif_out_controls
,
249 ARRAY_SIZE(spdif_out_controls
));
252 static const struct snd_soc_dai_ops spdif_out_dai_ops
= {
253 .digital_mute
= spdif_digital_mute
,
254 .startup
= spdif_out_startup
,
255 .shutdown
= spdif_out_shutdown
,
256 .trigger
= spdif_out_trigger
,
257 .hw_params
= spdif_out_hw_params
,
260 static struct snd_soc_dai_driver spdif_out_dai
= {
264 .rates
= (SNDRV_PCM_RATE_32000
| SNDRV_PCM_RATE_44100
| \
265 SNDRV_PCM_RATE_48000
| SNDRV_PCM_RATE_96000
| \
266 SNDRV_PCM_RATE_192000
),
267 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
269 .probe
= spdif_soc_dai_probe
,
270 .ops
= &spdif_out_dai_ops
,
273 static int spdif_out_probe(struct platform_device
*pdev
)
275 struct spdif_out_dev
*host
;
276 struct spear_spdif_platform_data
*pdata
;
277 struct resource
*res
;
280 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
284 if (!devm_request_mem_region(&pdev
->dev
, res
->start
,
285 resource_size(res
), pdev
->name
)) {
286 dev_warn(&pdev
->dev
, "Failed to get memory resourse\n");
290 host
= devm_kzalloc(&pdev
->dev
, sizeof(*host
), GFP_KERNEL
);
292 dev_warn(&pdev
->dev
, "kzalloc fail\n");
296 host
->io_base
= devm_ioremap(&pdev
->dev
, res
->start
,
298 if (!host
->io_base
) {
299 dev_warn(&pdev
->dev
, "ioremap failed\n");
303 host
->clk
= clk_get(&pdev
->dev
, NULL
);
304 if (IS_ERR(host
->clk
))
305 return PTR_ERR(host
->clk
);
307 pdata
= dev_get_platdata(&pdev
->dev
);
309 host
->dma_params
.data
= pdata
->dma_params
;
310 host
->dma_params
.addr
= res
->start
+ SPDIF_OUT_FIFO_DATA
;
311 host
->dma_params
.max_burst
= 16;
312 host
->dma_params
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
313 host
->dma_params
.filter
= pdata
->filter
;
315 dev_set_drvdata(&pdev
->dev
, host
);
317 ret
= snd_soc_register_dai(&pdev
->dev
, &spdif_out_dai
);
326 static int spdif_out_remove(struct platform_device
*pdev
)
328 struct spdif_out_dev
*host
= dev_get_drvdata(&pdev
->dev
);
330 snd_soc_unregister_dai(&pdev
->dev
);
331 dev_set_drvdata(&pdev
->dev
, NULL
);
339 static int spdif_out_suspend(struct device
*dev
)
341 struct platform_device
*pdev
= to_platform_device(dev
);
342 struct spdif_out_dev
*host
= dev_get_drvdata(&pdev
->dev
);
345 clk_disable(host
->clk
);
350 static int spdif_out_resume(struct device
*dev
)
352 struct platform_device
*pdev
= to_platform_device(dev
);
353 struct spdif_out_dev
*host
= dev_get_drvdata(&pdev
->dev
);
356 clk_enable(host
->clk
);
357 spdif_out_configure(host
);
358 spdif_out_clock(host
, host
->saved_params
.core_freq
,
359 host
->saved_params
.rate
);
364 static SIMPLE_DEV_PM_OPS(spdif_out_dev_pm_ops
, spdif_out_suspend
, \
367 #define SPDIF_OUT_DEV_PM_OPS (&spdif_out_dev_pm_ops)
370 #define SPDIF_OUT_DEV_PM_OPS NULL
374 static struct platform_driver spdif_out_driver
= {
375 .probe
= spdif_out_probe
,
376 .remove
= spdif_out_remove
,
379 .owner
= THIS_MODULE
,
380 .pm
= SPDIF_OUT_DEV_PM_OPS
,
384 module_platform_driver(spdif_out_driver
);
386 MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");
387 MODULE_DESCRIPTION("SPEAr SPDIF OUT SoC Interface");
388 MODULE_LICENSE("GPL");
389 MODULE_ALIAS("platform:spdif_out");