1 /* sound/soc/s3c24xx/spdif.c
3 * ALSA SoC Audio Layer - Samsung S/PDIF Controller driver
5 * Copyright (c) 2010 Samsung Electronics Co. Ltd
6 * http://www.samsung.com/
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
14 #include <linux/clk.h>
16 #include <sound/pcm.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
20 #include <plat/audio.h>
26 static struct s3c2410_dma_client spdif_dma_client_out = {
27 .name = "S/PDIF Stereo out",
30 static struct s3c_dma_params spdif_stereo_out;
31 static struct samsung_spdif_info spdif_info;
32 struct snd_soc_dai samsung_spdif_dai;
33 EXPORT_SYMBOL_GPL(samsung_spdif_dai);
35 static inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai)
37 return cpu_dai->private_data;
40 static void spdif_snd_txctrl(struct samsung_spdif_info *spdif, int on)
42 void __iomem *regs = spdif->regs;
45 dev_dbg(spdif->dev, "Entered %s\n", __func__);
47 clkcon = readl(regs + CLKCON);
50 writel(clkcon | CLKCTL_PWR_ON, regs + CLKCON);
52 writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
55 static int spdif_set_sysclk(struct snd_soc_dai *cpu_dai,
56 int clk_id, unsigned int freq, int dir)
58 struct samsung_spdif_info *spdif = to_info(cpu_dai);
60 dev_dbg(spdif->dev, "Entered %s\n", __func__);
62 if (dir == SND_SOC_CLOCK_IN)
63 spdif->use_int_clk = 0;
65 spdif->use_int_clk = 1;
67 spdif->clk_rate = freq;
72 static int spdif_trigger(struct snd_pcm_substream *substream, int cmd,
73 struct snd_soc_dai *dai)
75 struct snd_soc_pcm_runtime *rt = substream->private_data;
76 struct samsung_spdif_info *spdif = to_info(rt->dai->cpu_dai);
79 dev_dbg(spdif->dev, "Entered %s\n", __func__);
82 case SNDRV_PCM_TRIGGER_START:
83 case SNDRV_PCM_TRIGGER_RESUME:
84 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
85 spin_lock_irqsave(&spdif->lock, flags);
86 spdif_snd_txctrl(spdif, 1);
87 spin_unlock_irqrestore(&spdif->lock, flags);
89 case SNDRV_PCM_TRIGGER_STOP:
90 case SNDRV_PCM_TRIGGER_SUSPEND:
91 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
92 spin_lock_irqsave(&spdif->lock, flags);
93 spdif_snd_txctrl(spdif, 0);
94 spin_unlock_irqrestore(&spdif->lock, flags);
103 static int spdif_sysclk_ratios[] = {
107 static int spdif_hw_params(struct snd_pcm_substream *substream,
108 struct snd_pcm_hw_params *params,
109 struct snd_soc_dai *socdai)
111 struct snd_soc_pcm_runtime *rt = substream->private_data;
112 struct snd_soc_dai_link *dai = rt->dai;
113 struct samsung_spdif_info *spdif = to_info(dai->cpu_dai);
114 struct s3c_dma_params *dma_data;
115 void __iomem *regs = spdif->regs;
116 u32 con, cstas, clkcon;
120 dev_dbg(spdif->dev, "Entered %s\n", __func__);
122 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
123 dma_data = spdif->dma_playback;
125 printk(KERN_ERR "capture is not supported\n");
129 snd_soc_dai_set_dma_data(dai->cpu_dai, substream, dma_data);
131 spin_lock_irqsave(&spdif->lock, flags);
133 con = readl(regs + CON);
134 cstas = readl(regs + CSTAS);
135 clkcon = readl(regs + CLKCON);
137 con &= ~CON_FIFO_TH_MASK;
138 con |= (0x7 << CON_FIFO_TH_SHIFT);
139 con |= CON_USERDATA_23RDBIT;
142 con &= ~CON_PCM_MASK;
143 switch (params_format(params)) {
144 case SNDRV_PCM_FORMAT_S16_LE:
145 con |= CON_PCM_16BIT;
148 dev_err(spdif->dev, "Unsupported data size.\n");
152 if (spdif->use_int_clk)
153 clkcon &= ~CLKCTL_MCLK_EXT;
155 clkcon |= CLKCTL_MCLK_EXT;
157 ratio = spdif->clk_rate / params_rate(params);
158 for (i = 0; i < ARRAY_SIZE(spdif_sysclk_ratios); i++)
159 if (ratio == spdif_sysclk_ratios[i])
161 if (i == ARRAY_SIZE(spdif_sysclk_ratios)) {
162 dev_err(spdif->dev, "Invalid clock ratio %d/%d\n",
163 spdif->clk_rate, params_rate(params));
167 if (spdif->use_int_clk)
168 clk_set_rate(spdif->sclk, spdif->clk_rate);
170 con &= ~CON_MCLKDIV_MASK;
173 con |= CON_MCLKDIV_256FS;
176 con |= CON_MCLKDIV_384FS;
179 con |= CON_MCLKDIV_512FS;
183 cstas &= ~CSTAS_SAMP_FREQ_MASK;
184 switch (params_rate(params)) {
186 cstas |= CSTAS_SAMP_FREQ_44;
189 cstas |= CSTAS_SAMP_FREQ_48;
192 cstas |= CSTAS_SAMP_FREQ_32;
195 cstas |= CSTAS_SAMP_FREQ_96;
201 cstas &= ~CSTAS_CATEGORY_MASK;
202 cstas |= CSTAS_CATEGORY_CODE_CDP;
203 cstas |= CSTAS_NO_COPYRIGHT;
205 writel(con, regs + CON);
206 writel(cstas, regs + CSTAS);
207 writel(clkcon, regs + CLKCON);
209 spin_unlock_irqrestore(&spdif->lock, flags);
214 spin_unlock_irqrestore(&spdif->lock, flags);
219 static void spdif_shutdown(struct snd_pcm_substream *substream,
220 struct snd_soc_dai *dai)
222 struct snd_soc_pcm_runtime *rt = substream->private_data;
223 struct samsung_spdif_info *spdif = to_info(rt->dai->cpu_dai);
224 void __iomem *regs = spdif->regs;
227 dev_dbg(spdif->dev, "Entered %s\n", __func__);
229 con = readl(regs + CON);
230 clkcon = readl(regs + CLKCON);
232 writel(con | CON_SW_RESET, regs + CON);
235 writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
239 static int spdif_suspend(struct snd_soc_dai *cpu_dai)
241 struct samsung_spdif_info *spdif = to_info(cpu_dai);
242 u32 con = spdif->saved_con;
244 dev_dbg(spdif->dev, "Entered %s\n", __func__);
246 spdif->saved_clkcon = readl(spdif->regs + CLKCON);
247 spdif->saved_con = readl(spdif->regs + CON);
248 spdif->saved_cstas = readl(spdif->regs + CSTAS);
250 writel(CLKCTL_PWR_ON, spdif->regs + CLKCON);
251 writel(con | CON_SW_RESET, spdif->regs + CON);
254 writel(~CLKCTL_PWR_ON, spdif->regs + CLKCON);
259 static int spdif_resume(struct snd_soc_dai *cpu_dai)
261 struct samsung_spdif_info *spdif = to_info(cpu_dai);
263 dev_dbg(spdif->dev, "Entered %s\n", __func__);
265 writel(spdif->saved_clkcon, spdif->regs + CLKCON);
266 writel(spdif->saved_con, spdif->regs + CON);
267 writel(spdif->saved_cstas, spdif->regs + CSTAS);
272 #define spdif_suspend NULL
273 #define spdif_resume NULL
276 static struct snd_soc_dai_ops spdif_dai_ops = {
277 .set_sysclk = spdif_set_sysclk,
278 .trigger = spdif_trigger,
279 .hw_params = spdif_hw_params,
280 .shutdown = spdif_shutdown,
283 #define S5P_SPDIF_RATES (SNDRV_PCM_RATE_32000 | \
284 SNDRV_PCM_RATE_44100 | \
285 SNDRV_PCM_RATE_48000 | \
286 SNDRV_PCM_RATE_96000)
287 #define S5P_SPDIF_FORMATS SNDRV_PCM_FMTBIT_S16_LE
289 static __devinit int spdif_probe(struct platform_device *pdev)
292 struct samsung_spdif_info *spdif;
293 struct snd_soc_dai *dai;
294 struct resource *mem_res, *dma_res;
295 struct s3c_audio_pdata *spdif_pdata;
297 spdif_pdata = pdev->dev.platform_data;
299 dev_dbg(&pdev->dev, "Entered %s\n", __func__);
301 dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
303 dev_err(&pdev->dev, "Unable to get dma resource.\n");
307 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
309 dev_err(&pdev->dev, "Unable to get register resource.\n");
313 if (spdif_pdata && spdif_pdata->cfg_gpio
314 && spdif_pdata->cfg_gpio(pdev)) {
315 dev_err(&pdev->dev, "Unable to configure GPIO pins\n");
319 /* Fill-up basic DAI features */
320 samsung_spdif_dai.name = "samsung-spdif";
321 samsung_spdif_dai.ops = &spdif_dai_ops;
322 samsung_spdif_dai.suspend = spdif_suspend;
323 samsung_spdif_dai.resume = spdif_resume;
324 samsung_spdif_dai.playback.channels_min = 2;
325 samsung_spdif_dai.playback.channels_max = 2;
326 samsung_spdif_dai.playback.rates = S5P_SPDIF_RATES;
327 samsung_spdif_dai.playback.formats = S5P_SPDIF_FORMATS;
330 spdif->dev = &pdev->dev;
332 spin_lock_init(&spdif->lock);
334 spdif->pclk = clk_get(&pdev->dev, "spdif");
335 if (IS_ERR(spdif->pclk)) {
336 dev_err(&pdev->dev, "failed to get SPDIF PCLK\n");
340 clk_enable(spdif->pclk);
342 spdif->use_int_clk = 1;
345 /* Get source clock that can be set rate. Actually 'sclk_spdif'
346 * has no divider to set it's rate, so we should have to use
347 * parent clock of 'sclk_spdif' that has divider.
349 spdif->sclk = clk_get_parent(clk_get(&pdev->dev, "sclk_spdif"));
350 if (IS_ERR(spdif->sclk)) {
351 dev_err(&pdev->dev, "failed to get SPDIF source clock\n");
355 clk_enable(spdif->sclk);
357 /* Request SPDIF Register's memory region */
358 if (!request_mem_region(mem_res->start,
359 resource_size(mem_res), "samsung-spdif")) {
360 dev_err(&pdev->dev, "Unable to request register region\n");
365 spdif->regs = ioremap(mem_res->start, 0x100);
366 if (spdif->regs == NULL) {
367 dev_err(&pdev->dev, "Cannot ioremap registers\n");
372 /* Register cpu dai */
373 dai = &samsung_spdif_dai;
374 dai->dev = &pdev->dev;
375 dai->private_data = spdif;
377 ret = snd_soc_register_dai(dai);
379 dev_err(&pdev->dev, "Failed to register cpu dai\n");
383 spdif_stereo_out.dma_size = 2;
384 spdif_stereo_out.client = &spdif_dma_client_out;
385 spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF;
386 spdif_stereo_out.channel = dma_res->start;
388 spdif->dma_playback = &spdif_stereo_out;
392 iounmap(spdif->regs);
394 release_mem_region(mem_res->start, resource_size(mem_res));
396 clk_disable(spdif->sclk);
397 clk_put(spdif->sclk);
399 clk_disable(spdif->pclk);
400 clk_put(spdif->pclk);
405 static __devexit int spdif_remove(struct platform_device *pdev)
407 struct snd_soc_dai *dai = &samsung_spdif_dai;
408 struct samsung_spdif_info *spdif = &spdif_info;
409 struct resource *mem_res;
411 snd_soc_unregister_dai(dai);
413 iounmap(spdif->regs);
415 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
416 release_mem_region(mem_res->start, resource_size(mem_res));
418 clk_disable(spdif->pclk);
419 clk_disable(spdif->sclk);
420 clk_put(spdif->pclk);
421 clk_put(spdif->sclk);
426 static struct platform_driver samsung_spdif_driver = {
427 .probe = spdif_probe,
428 .remove = spdif_remove,
430 .name = "samsung-spdif",
431 .owner = THIS_MODULE,
435 static int __init spdif_init(void)
437 return platform_driver_register(&samsung_spdif_driver);
439 module_init(spdif_init);
441 static void __exit spdif_exit(void)
443 platform_driver_unregister(&samsung_spdif_driver);
445 module_exit(spdif_exit);
447 MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
448 MODULE_DESCRIPTION("Samsung S/PDIF Controller Driver");
449 MODULE_LICENSE("GPL");