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/dmaengine_pcm.h>
22 #include <sound/soc.h>
23 #include <sound/spear_dma.h>
24 #include <sound/spear_spdif.h>
25 #include "spdif_out_regs.h"
26 #include "spear_pcm.h"
28 struct spdif_out_params
{
34 struct spdif_out_dev
{
36 struct spear_dma_data dma_params
;
37 struct spdif_out_params saved_params
;
39 void __iomem
*io_base
;
40 struct snd_dmaengine_dai_dma_data dma_params_tx
;
41 struct snd_dmaengine_pcm_config config
;
44 static void spdif_out_configure(struct spdif_out_dev
*host
)
46 writel(SPDIF_OUT_RESET
, host
->io_base
+ SPDIF_OUT_SOFT_RST
);
48 writel(readl(host
->io_base
+ SPDIF_OUT_SOFT_RST
) & ~SPDIF_OUT_RESET
,
49 host
->io_base
+ SPDIF_OUT_SOFT_RST
);
51 writel(SPDIF_OUT_FDMA_TRIG_16
| SPDIF_OUT_MEMFMT_16_16
|
52 SPDIF_OUT_VALID_HW
| SPDIF_OUT_USER_HW
|
53 SPDIF_OUT_CHNLSTA_HW
| SPDIF_OUT_PARITY_HW
,
54 host
->io_base
+ SPDIF_OUT_CFG
);
56 writel(0x7F, host
->io_base
+ SPDIF_OUT_INT_STA_CLR
);
57 writel(0x7F, host
->io_base
+ SPDIF_OUT_INT_EN_CLR
);
60 static int spdif_out_startup(struct snd_pcm_substream
*substream
,
61 struct snd_soc_dai
*cpu_dai
)
63 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(cpu_dai
);
66 if (substream
->stream
!= SNDRV_PCM_STREAM_PLAYBACK
)
69 ret
= clk_enable(host
->clk
);
74 spdif_out_configure(host
);
79 static void spdif_out_shutdown(struct snd_pcm_substream
*substream
,
80 struct snd_soc_dai
*dai
)
82 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
84 if (substream
->stream
!= SNDRV_PCM_STREAM_PLAYBACK
)
87 clk_disable(host
->clk
);
88 host
->running
= false;
91 static void spdif_out_clock(struct spdif_out_dev
*host
, u32 core_freq
,
96 clk_set_rate(host
->clk
, core_freq
);
97 divider
= DIV_ROUND_CLOSEST(clk_get_rate(host
->clk
), (rate
* 128));
99 ctrl
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
100 ctrl
&= ~SPDIF_DIVIDER_MASK
;
101 ctrl
|= (divider
<< SPDIF_DIVIDER_SHIFT
) & SPDIF_DIVIDER_MASK
;
102 writel(ctrl
, host
->io_base
+ SPDIF_OUT_CTRL
);
105 static int spdif_out_hw_params(struct snd_pcm_substream
*substream
,
106 struct snd_pcm_hw_params
*params
,
107 struct snd_soc_dai
*dai
)
109 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
112 if (substream
->stream
!= SNDRV_PCM_STREAM_PLAYBACK
)
115 rate
= params_rate(params
);
123 * The clock is multiplied by 10 to bring it to feasible range
124 * of frequencies for sscg
126 core_freq
= 64000 * 128 * 10; /* 81.92 MHz */
134 core_freq
= 176400 * 128; /* 22.5792 MHz */
140 core_freq
= 192000 * 128; /* 24.576 MHz */
144 spdif_out_clock(host
, core_freq
, rate
);
145 host
->saved_params
.core_freq
= core_freq
;
146 host
->saved_params
.rate
= rate
;
151 static int spdif_out_trigger(struct snd_pcm_substream
*substream
, int cmd
,
152 struct snd_soc_dai
*dai
)
154 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
158 if (substream
->stream
!= SNDRV_PCM_STREAM_PLAYBACK
)
162 case SNDRV_PCM_TRIGGER_START
:
163 case SNDRV_PCM_TRIGGER_RESUME
:
164 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
165 ctrl
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
166 ctrl
&= ~SPDIF_OPMODE_MASK
;
167 if (!host
->saved_params
.mute
)
168 ctrl
|= SPDIF_OPMODE_AUD_DATA
|
171 ctrl
|= SPDIF_OPMODE_MUTE_PCM
;
172 writel(ctrl
, host
->io_base
+ SPDIF_OUT_CTRL
);
175 case SNDRV_PCM_TRIGGER_STOP
:
176 case SNDRV_PCM_TRIGGER_SUSPEND
:
177 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
178 ctrl
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
179 ctrl
&= ~SPDIF_OPMODE_MASK
;
180 ctrl
|= SPDIF_OPMODE_OFF
;
181 writel(ctrl
, host
->io_base
+ SPDIF_OUT_CTRL
);
191 static int spdif_digital_mute(struct snd_soc_dai
*dai
, int mute
)
193 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
196 host
->saved_params
.mute
= mute
;
197 val
= readl(host
->io_base
+ SPDIF_OUT_CTRL
);
198 val
&= ~SPDIF_OPMODE_MASK
;
201 val
|= SPDIF_OPMODE_MUTE_PCM
;
204 val
|= SPDIF_OPMODE_AUD_DATA
| SPDIF_STATE_NORMAL
;
206 val
|= SPDIF_OPMODE_OFF
;
209 writel(val
, host
->io_base
+ SPDIF_OUT_CTRL
);
213 static int spdif_mute_get(struct snd_kcontrol
*kcontrol
,
214 struct snd_ctl_elem_value
*ucontrol
)
216 struct snd_soc_dai
*cpu_dai
= snd_kcontrol_chip(kcontrol
);
217 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(cpu_dai
);
219 ucontrol
->value
.integer
.value
[0] = host
->saved_params
.mute
;
223 static int spdif_mute_put(struct snd_kcontrol
*kcontrol
,
224 struct snd_ctl_elem_value
*ucontrol
)
226 struct snd_soc_dai
*cpu_dai
= snd_kcontrol_chip(kcontrol
);
227 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(cpu_dai
);
229 if (host
->saved_params
.mute
== ucontrol
->value
.integer
.value
[0])
232 spdif_digital_mute(cpu_dai
, ucontrol
->value
.integer
.value
[0]);
236 static const struct snd_kcontrol_new spdif_out_controls
[] = {
237 SOC_SINGLE_BOOL_EXT("IEC958 Playback Switch", 0,
238 spdif_mute_get
, spdif_mute_put
),
241 static int spdif_soc_dai_probe(struct snd_soc_dai
*dai
)
243 struct spdif_out_dev
*host
= snd_soc_dai_get_drvdata(dai
);
245 host
->dma_params_tx
.filter_data
= &host
->dma_params
;
246 dai
->playback_dma_data
= &host
->dma_params_tx
;
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 const struct snd_soc_component_driver spdif_out_component
= {
277 static int spdif_out_probe(struct platform_device
*pdev
)
279 struct spdif_out_dev
*host
;
280 struct spear_spdif_platform_data
*pdata
;
281 struct resource
*res
;
284 host
= devm_kzalloc(&pdev
->dev
, sizeof(*host
), GFP_KERNEL
);
288 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
289 host
->io_base
= devm_ioremap_resource(&pdev
->dev
, res
);
290 if (IS_ERR(host
->io_base
))
291 return PTR_ERR(host
->io_base
);
293 host
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
294 if (IS_ERR(host
->clk
))
295 return PTR_ERR(host
->clk
);
297 pdata
= dev_get_platdata(&pdev
->dev
);
299 host
->dma_params
.data
= pdata
->dma_params
;
300 host
->dma_params
.addr
= res
->start
+ SPDIF_OUT_FIFO_DATA
;
301 host
->dma_params
.max_burst
= 16;
302 host
->dma_params
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
304 dev_set_drvdata(&pdev
->dev
, host
);
306 ret
= devm_snd_soc_register_component(&pdev
->dev
, &spdif_out_component
,
311 return devm_spear_pcm_platform_register(&pdev
->dev
, &host
->config
,
316 static int spdif_out_suspend(struct device
*dev
)
318 struct platform_device
*pdev
= to_platform_device(dev
);
319 struct spdif_out_dev
*host
= dev_get_drvdata(&pdev
->dev
);
322 clk_disable(host
->clk
);
327 static int spdif_out_resume(struct device
*dev
)
329 struct platform_device
*pdev
= to_platform_device(dev
);
330 struct spdif_out_dev
*host
= dev_get_drvdata(&pdev
->dev
);
333 clk_enable(host
->clk
);
334 spdif_out_configure(host
);
335 spdif_out_clock(host
, host
->saved_params
.core_freq
,
336 host
->saved_params
.rate
);
341 static SIMPLE_DEV_PM_OPS(spdif_out_dev_pm_ops
, spdif_out_suspend
, \
344 #define SPDIF_OUT_DEV_PM_OPS (&spdif_out_dev_pm_ops)
347 #define SPDIF_OUT_DEV_PM_OPS NULL
351 static struct platform_driver spdif_out_driver
= {
352 .probe
= spdif_out_probe
,
355 .pm
= SPDIF_OUT_DEV_PM_OPS
,
359 module_platform_driver(spdif_out_driver
);
361 MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");
362 MODULE_DESCRIPTION("SPEAr SPDIF OUT SoC Interface");
363 MODULE_LICENSE("GPL");
364 MODULE_ALIAS("platform:spdif_out");