upload tizen1.0 source
[kernel/linux-2.6.36.git] / sound / soc / s3c24xx / spdif.c
1 /* sound/soc/s3c24xx/spdif.c
2  *
3  * ALSA SoC Audio Layer - Samsung S/PDIF Controller driver
4  *
5  * Copyright (c) 2010 Samsung Electronics Co. Ltd
6  *              http://www.samsung.com/
7  *
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.
11  */
12
13 #include <linux/io.h>
14 #include <linux/clk.h>
15
16 #include <sound/pcm.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
19
20 #include <plat/audio.h>
21 #include <mach/dma.h>
22
23 #include "s3c-dma.h"
24 #include "spdif.h"
25
26 static struct s3c2410_dma_client spdif_dma_client_out = {
27         .name           = "S/PDIF Stereo out",
28 };
29
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);
34
35 static inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai)
36 {
37         return cpu_dai->private_data;
38 }
39
40 static void spdif_snd_txctrl(struct samsung_spdif_info *spdif, int on)
41 {
42         void __iomem *regs = spdif->regs;
43         u32 clkcon;
44
45         dev_dbg(spdif->dev, "Entered %s\n", __func__);
46
47         clkcon = readl(regs + CLKCON);
48
49         if (on)
50                 writel(clkcon | CLKCTL_PWR_ON, regs + CLKCON);
51         else
52                 writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
53 }
54
55 static int spdif_set_sysclk(struct snd_soc_dai *cpu_dai,
56                                 int clk_id, unsigned int freq, int dir)
57 {
58         struct samsung_spdif_info *spdif = to_info(cpu_dai);
59
60         dev_dbg(spdif->dev, "Entered %s\n", __func__);
61
62         if (dir == SND_SOC_CLOCK_IN)
63                 spdif->use_int_clk = 0;
64         else
65                 spdif->use_int_clk = 1;
66
67         spdif->clk_rate = freq;
68
69         return 0;
70 }
71
72 static int spdif_trigger(struct snd_pcm_substream *substream, int cmd,
73                                 struct snd_soc_dai *dai)
74 {
75         struct snd_soc_pcm_runtime *rt = substream->private_data;
76         struct samsung_spdif_info *spdif = to_info(rt->dai->cpu_dai);
77         unsigned long flags;
78
79         dev_dbg(spdif->dev, "Entered %s\n", __func__);
80
81         switch (cmd) {
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);
88                 break;
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);
95                 break;
96         default:
97                 return -EINVAL;
98         }
99
100         return 0;
101 }
102
103 static int spdif_sysclk_ratios[] = {
104         512, 384, 256,
105 };
106
107 static int spdif_hw_params(struct snd_pcm_substream *substream,
108                                 struct snd_pcm_hw_params *params,
109                                 struct snd_soc_dai *socdai)
110 {
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;
117         unsigned long flags;
118         int i, ratio;
119
120         dev_dbg(spdif->dev, "Entered %s\n", __func__);
121
122         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
123                 dma_data = spdif->dma_playback;
124         else {
125                 printk(KERN_ERR "capture is not supported\n");
126                 return -EINVAL;
127         }
128
129         snd_soc_dai_set_dma_data(dai->cpu_dai, substream, dma_data);
130
131         spin_lock_irqsave(&spdif->lock, flags);
132
133         con = readl(regs + CON);
134         cstas = readl(regs + CSTAS);
135         clkcon = readl(regs + CLKCON);
136
137         con &= ~CON_FIFO_TH_MASK;
138         con |= (0x7 << CON_FIFO_TH_SHIFT);
139         con |= CON_USERDATA_23RDBIT;
140         con |= CON_PCM_DATA;
141
142         con &= ~CON_PCM_MASK;
143         switch (params_format(params)) {
144         case SNDRV_PCM_FORMAT_S16_LE:
145                 con |= CON_PCM_16BIT;
146                 break;
147         default:
148                 dev_err(spdif->dev, "Unsupported data size.\n");
149                 goto err;
150         }
151
152         if (spdif->use_int_clk)
153                 clkcon &= ~CLKCTL_MCLK_EXT;
154         else
155                 clkcon |= CLKCTL_MCLK_EXT;
156
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])
160                         break;
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));
164                 goto err;
165         }
166
167         if (spdif->use_int_clk)
168                 clk_set_rate(spdif->sclk, spdif->clk_rate);
169
170         con &= ~CON_MCLKDIV_MASK;
171         switch (ratio) {
172         case 256:
173                 con |= CON_MCLKDIV_256FS;
174                 break;
175         case 384:
176                 con |= CON_MCLKDIV_384FS;
177                 break;
178         case 512:
179                 con |= CON_MCLKDIV_512FS;
180                 break;
181         }
182
183         cstas &= ~CSTAS_SAMP_FREQ_MASK;
184         switch (params_rate(params)) {
185         case 44100:
186                 cstas |= CSTAS_SAMP_FREQ_44;
187                 break;
188         case 48000:
189                 cstas |= CSTAS_SAMP_FREQ_48;
190                 break;
191         case 32000:
192                 cstas |= CSTAS_SAMP_FREQ_32;
193                 break;
194         case 96000:
195                 cstas |= CSTAS_SAMP_FREQ_96;
196                 break;
197         default:
198                 goto err;
199         }
200
201         cstas &= ~CSTAS_CATEGORY_MASK;
202         cstas |= CSTAS_CATEGORY_CODE_CDP;
203         cstas |= CSTAS_NO_COPYRIGHT;
204
205         writel(con, regs + CON);
206         writel(cstas, regs + CSTAS);
207         writel(clkcon, regs + CLKCON);
208
209         spin_unlock_irqrestore(&spdif->lock, flags);
210
211         return 0;
212
213 err:
214         spin_unlock_irqrestore(&spdif->lock, flags);
215
216         return -EINVAL;
217 }
218
219 static void spdif_shutdown(struct snd_pcm_substream *substream,
220                                 struct snd_soc_dai *dai)
221 {
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;
225         u32 con, clkcon;
226
227         dev_dbg(spdif->dev, "Entered %s\n", __func__);
228
229         con = readl(regs + CON);
230         clkcon = readl(regs + CLKCON);
231
232         writel(con | CON_SW_RESET, regs + CON);
233         cpu_relax();
234
235         writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
236 }
237
238 #ifdef CONFIG_PM
239 static int spdif_suspend(struct snd_soc_dai *cpu_dai)
240 {
241         struct samsung_spdif_info *spdif = to_info(cpu_dai);
242         u32 con = spdif->saved_con;
243
244         dev_dbg(spdif->dev, "Entered %s\n", __func__);
245
246         spdif->saved_clkcon = readl(spdif->regs + CLKCON);
247         spdif->saved_con = readl(spdif->regs + CON);
248         spdif->saved_cstas = readl(spdif->regs + CSTAS);
249
250         writel(CLKCTL_PWR_ON, spdif->regs + CLKCON);
251         writel(con | CON_SW_RESET, spdif->regs + CON);
252         cpu_relax();
253
254         writel(~CLKCTL_PWR_ON, spdif->regs + CLKCON);
255
256         return 0;
257 }
258
259 static int spdif_resume(struct snd_soc_dai *cpu_dai)
260 {
261         struct samsung_spdif_info *spdif = to_info(cpu_dai);
262
263         dev_dbg(spdif->dev, "Entered %s\n", __func__);
264
265         writel(spdif->saved_clkcon, spdif->regs + CLKCON);
266         writel(spdif->saved_con, spdif->regs + CON);
267         writel(spdif->saved_cstas, spdif->regs + CSTAS);
268
269         return 0;
270 }
271 #else
272 #define spdif_suspend NULL
273 #define spdif_resume NULL
274 #endif
275
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,
281 };
282
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
288
289 static __devinit int spdif_probe(struct platform_device *pdev)
290 {
291         int ret;
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;
296
297         spdif_pdata = pdev->dev.platform_data;
298
299         dev_dbg(&pdev->dev, "Entered %s\n", __func__);
300
301         dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
302         if (!dma_res) {
303                 dev_err(&pdev->dev, "Unable to get dma resource.\n");
304                 return -ENXIO;
305         }
306
307         mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
308         if (!mem_res) {
309                 dev_err(&pdev->dev, "Unable to get register resource.\n");
310                 return -ENXIO;
311         }
312
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");
316                 return -EINVAL;
317         }
318
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;
328
329         spdif = &spdif_info;
330         spdif->dev = &pdev->dev;
331
332         spin_lock_init(&spdif->lock);
333
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");
337                 ret = -ENOENT;
338                 goto err0;
339         }
340         clk_enable(spdif->pclk);
341
342         spdif->use_int_clk = 1;
343         spdif->clk_rate = 0;
344
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.
348          */
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");
352                 ret = -ENOENT;
353                 goto err1;
354         }
355         clk_enable(spdif->sclk);
356
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");
361                 ret = -EBUSY;
362                 goto err2;
363         }
364
365         spdif->regs = ioremap(mem_res->start, 0x100);
366         if (spdif->regs == NULL) {
367                 dev_err(&pdev->dev, "Cannot ioremap registers\n");
368                 ret = -ENXIO;
369                 goto err4;
370         }
371
372         /* Register cpu dai */
373         dai = &samsung_spdif_dai;
374         dai->dev = &pdev->dev;
375         dai->private_data = spdif;
376
377         ret = snd_soc_register_dai(dai);
378         if (ret != 0) {
379                 dev_err(&pdev->dev, "Failed to register cpu dai\n");
380                 goto err5;
381         }
382
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;
387
388         spdif->dma_playback = &spdif_stereo_out;
389
390         return 0;
391 err5:
392         iounmap(spdif->regs);
393 err4:
394         release_mem_region(mem_res->start, resource_size(mem_res));
395 err2:
396         clk_disable(spdif->sclk);
397         clk_put(spdif->sclk);
398 err1:
399         clk_disable(spdif->pclk);
400         clk_put(spdif->pclk);
401 err0:
402         return ret;
403 }
404
405 static __devexit int spdif_remove(struct platform_device *pdev)
406 {
407         struct snd_soc_dai *dai = &samsung_spdif_dai;
408         struct samsung_spdif_info *spdif = &spdif_info;
409         struct resource *mem_res;
410
411         snd_soc_unregister_dai(dai);
412
413         iounmap(spdif->regs);
414
415         mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
416         release_mem_region(mem_res->start, resource_size(mem_res));
417
418         clk_disable(spdif->pclk);
419         clk_disable(spdif->sclk);
420         clk_put(spdif->pclk);
421         clk_put(spdif->sclk);
422
423         return 0;
424 }
425
426 static struct platform_driver samsung_spdif_driver = {
427         .probe  = spdif_probe,
428         .remove = spdif_remove,
429         .driver = {
430                 .name   = "samsung-spdif",
431                 .owner  = THIS_MODULE,
432         },
433 };
434
435 static int __init spdif_init(void)
436 {
437         return platform_driver_register(&samsung_spdif_driver);
438 }
439 module_init(spdif_init);
440
441 static void __exit spdif_exit(void)
442 {
443         platform_driver_unregister(&samsung_spdif_driver);
444 }
445 module_exit(spdif_exit);
446
447 MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
448 MODULE_DESCRIPTION("Samsung S/PDIF Controller Driver");
449 MODULE_LICENSE("GPL");