Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
spdif.c
Go to the documentation of this file.
1 /* sound/soc/samsung/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/clk.h>
14 #include <linux/io.h>
15 #include <linux/module.h>
16 
17 #include <sound/soc.h>
18 #include <sound/pcm_params.h>
19 
21 #include <mach/dma.h>
22 
23 #include "dma.h"
24 #include "spdif.h"
25 
26 /* Registers */
27 #define CLKCON 0x00
28 #define CON 0x04
29 #define BSTAS 0x08
30 #define CSTAS 0x0C
31 #define DATA_OUTBUF 0x10
32 #define DCNT 0x14
33 #define BSTAS_S 0x18
34 #define DCNT_S 0x1C
35 
36 #define CLKCTL_MASK 0x7
37 #define CLKCTL_MCLK_EXT (0x1 << 2)
38 #define CLKCTL_PWR_ON (0x1 << 0)
39 
40 #define CON_MASK 0x3ffffff
41 #define CON_FIFO_TH_SHIFT 19
42 #define CON_FIFO_TH_MASK (0x7 << 19)
43 #define CON_USERDATA_23RDBIT (0x1 << 12)
44 
45 #define CON_SW_RESET (0x1 << 5)
46 
47 #define CON_MCLKDIV_MASK (0x3 << 3)
48 #define CON_MCLKDIV_256FS (0x0 << 3)
49 #define CON_MCLKDIV_384FS (0x1 << 3)
50 #define CON_MCLKDIV_512FS (0x2 << 3)
51 
52 #define CON_PCM_MASK (0x3 << 1)
53 #define CON_PCM_16BIT (0x0 << 1)
54 #define CON_PCM_20BIT (0x1 << 1)
55 #define CON_PCM_24BIT (0x2 << 1)
56 
57 #define CON_PCM_DATA (0x1 << 0)
58 
59 #define CSTAS_MASK 0x3fffffff
60 #define CSTAS_SAMP_FREQ_MASK (0xF << 24)
61 #define CSTAS_SAMP_FREQ_44 (0x0 << 24)
62 #define CSTAS_SAMP_FREQ_48 (0x2 << 24)
63 #define CSTAS_SAMP_FREQ_32 (0x3 << 24)
64 #define CSTAS_SAMP_FREQ_96 (0xA << 24)
65 
66 #define CSTAS_CATEGORY_MASK (0xFF << 8)
67 #define CSTAS_CATEGORY_CODE_CDP (0x01 << 8)
68 
69 #define CSTAS_NO_COPYRIGHT (0x1 << 2)
70 
86  struct device *dev;
87  void __iomem *regs;
88  unsigned long clk_rate;
89  struct clk *pclk;
90  struct clk *sclk;
95 };
96 
97 static struct s3c2410_dma_client spdif_dma_client_out = {
98  .name = "S/PDIF Stereo out",
99 };
100 
101 static struct s3c_dma_params spdif_stereo_out;
102 static struct samsung_spdif_info spdif_info;
103 
104 static inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai)
105 {
106  return snd_soc_dai_get_drvdata(cpu_dai);
107 }
108 
109 static void spdif_snd_txctrl(struct samsung_spdif_info *spdif, int on)
110 {
111  void __iomem *regs = spdif->regs;
112  u32 clkcon;
113 
114  dev_dbg(spdif->dev, "Entered %s\n", __func__);
115 
116  clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
117  if (on)
118  writel(clkcon | CLKCTL_PWR_ON, regs + CLKCON);
119  else
120  writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
121 }
122 
123 static int spdif_set_sysclk(struct snd_soc_dai *cpu_dai,
124  int clk_id, unsigned int freq, int dir)
125 {
126  struct samsung_spdif_info *spdif = to_info(cpu_dai);
127  u32 clkcon;
128 
129  dev_dbg(spdif->dev, "Entered %s\n", __func__);
130 
131  clkcon = readl(spdif->regs + CLKCON);
132 
133  if (clk_id == SND_SOC_SPDIF_INT_MCLK)
134  clkcon &= ~CLKCTL_MCLK_EXT;
135  else
136  clkcon |= CLKCTL_MCLK_EXT;
137 
138  writel(clkcon, spdif->regs + CLKCON);
139 
140  spdif->clk_rate = freq;
141 
142  return 0;
143 }
144 
145 static int spdif_trigger(struct snd_pcm_substream *substream, int cmd,
146  struct snd_soc_dai *dai)
147 {
148  struct snd_soc_pcm_runtime *rtd = substream->private_data;
149  struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
150  unsigned long flags;
151 
152  dev_dbg(spdif->dev, "Entered %s\n", __func__);
153 
154  switch (cmd) {
158  spin_lock_irqsave(&spdif->lock, flags);
159  spdif_snd_txctrl(spdif, 1);
160  spin_unlock_irqrestore(&spdif->lock, flags);
161  break;
165  spin_lock_irqsave(&spdif->lock, flags);
166  spdif_snd_txctrl(spdif, 0);
167  spin_unlock_irqrestore(&spdif->lock, flags);
168  break;
169  default:
170  return -EINVAL;
171  }
172 
173  return 0;
174 }
175 
176 static int spdif_sysclk_ratios[] = {
177  512, 384, 256,
178 };
179 
180 static int spdif_hw_params(struct snd_pcm_substream *substream,
181  struct snd_pcm_hw_params *params,
182  struct snd_soc_dai *socdai)
183 {
184  struct snd_soc_pcm_runtime *rtd = substream->private_data;
185  struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
186  void __iomem *regs = spdif->regs;
187  struct s3c_dma_params *dma_data;
188  u32 con, clkcon, cstas;
189  unsigned long flags;
190  int i, ratio;
191 
192  dev_dbg(spdif->dev, "Entered %s\n", __func__);
193 
194  if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
195  dma_data = spdif->dma_playback;
196  else {
197  dev_err(spdif->dev, "Capture is not supported\n");
198  return -EINVAL;
199  }
200 
201  snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
202 
203  spin_lock_irqsave(&spdif->lock, flags);
204 
205  con = readl(regs + CON) & CON_MASK;
206  cstas = readl(regs + CSTAS) & CSTAS_MASK;
207  clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
208 
209  con &= ~CON_FIFO_TH_MASK;
210  con |= (0x7 << CON_FIFO_TH_SHIFT);
211  con |= CON_USERDATA_23RDBIT;
212  con |= CON_PCM_DATA;
213 
214  con &= ~CON_PCM_MASK;
215  switch (params_format(params)) {
217  con |= CON_PCM_16BIT;
218  break;
219  default:
220  dev_err(spdif->dev, "Unsupported data size.\n");
221  goto err;
222  }
223 
224  ratio = spdif->clk_rate / params_rate(params);
225  for (i = 0; i < ARRAY_SIZE(spdif_sysclk_ratios); i++)
226  if (ratio == spdif_sysclk_ratios[i])
227  break;
228  if (i == ARRAY_SIZE(spdif_sysclk_ratios)) {
229  dev_err(spdif->dev, "Invalid clock ratio %ld/%d\n",
230  spdif->clk_rate, params_rate(params));
231  goto err;
232  }
233 
234  con &= ~CON_MCLKDIV_MASK;
235  switch (ratio) {
236  case 256:
237  con |= CON_MCLKDIV_256FS;
238  break;
239  case 384:
240  con |= CON_MCLKDIV_384FS;
241  break;
242  case 512:
243  con |= CON_MCLKDIV_512FS;
244  break;
245  }
246 
247  cstas &= ~CSTAS_SAMP_FREQ_MASK;
248  switch (params_rate(params)) {
249  case 44100:
250  cstas |= CSTAS_SAMP_FREQ_44;
251  break;
252  case 48000:
253  cstas |= CSTAS_SAMP_FREQ_48;
254  break;
255  case 32000:
256  cstas |= CSTAS_SAMP_FREQ_32;
257  break;
258  case 96000:
259  cstas |= CSTAS_SAMP_FREQ_96;
260  break;
261  default:
262  dev_err(spdif->dev, "Invalid sampling rate %d\n",
263  params_rate(params));
264  goto err;
265  }
266 
267  cstas &= ~CSTAS_CATEGORY_MASK;
268  cstas |= CSTAS_CATEGORY_CODE_CDP;
269  cstas |= CSTAS_NO_COPYRIGHT;
270 
271  writel(con, regs + CON);
272  writel(cstas, regs + CSTAS);
273  writel(clkcon, regs + CLKCON);
274 
275  spin_unlock_irqrestore(&spdif->lock, flags);
276 
277  return 0;
278 err:
279  spin_unlock_irqrestore(&spdif->lock, flags);
280  return -EINVAL;
281 }
282 
283 static void spdif_shutdown(struct snd_pcm_substream *substream,
284  struct snd_soc_dai *dai)
285 {
286  struct snd_soc_pcm_runtime *rtd = substream->private_data;
287  struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
288  void __iomem *regs = spdif->regs;
289  u32 con, clkcon;
290 
291  dev_dbg(spdif->dev, "Entered %s\n", __func__);
292 
293  con = readl(regs + CON) & CON_MASK;
294  clkcon = readl(regs + CLKCON) & CLKCTL_MASK;
295 
296  writel(con | CON_SW_RESET, regs + CON);
297  cpu_relax();
298 
299  writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON);
300 }
301 
302 #ifdef CONFIG_PM
303 static int spdif_suspend(struct snd_soc_dai *cpu_dai)
304 {
305  struct samsung_spdif_info *spdif = to_info(cpu_dai);
306  u32 con = spdif->saved_con;
307 
308  dev_dbg(spdif->dev, "Entered %s\n", __func__);
309 
310  spdif->saved_clkcon = readl(spdif->regs + CLKCON) & CLKCTL_MASK;
311  spdif->saved_con = readl(spdif->regs + CON) & CON_MASK;
312  spdif->saved_cstas = readl(spdif->regs + CSTAS) & CSTAS_MASK;
313 
314  writel(con | CON_SW_RESET, spdif->regs + CON);
315  cpu_relax();
316 
317  return 0;
318 }
319 
320 static int spdif_resume(struct snd_soc_dai *cpu_dai)
321 {
322  struct samsung_spdif_info *spdif = to_info(cpu_dai);
323 
324  dev_dbg(spdif->dev, "Entered %s\n", __func__);
325 
326  writel(spdif->saved_clkcon, spdif->regs + CLKCON);
327  writel(spdif->saved_con, spdif->regs + CON);
328  writel(spdif->saved_cstas, spdif->regs + CSTAS);
329 
330  return 0;
331 }
332 #else
333 #define spdif_suspend NULL
334 #define spdif_resume NULL
335 #endif
336 
337 static const struct snd_soc_dai_ops spdif_dai_ops = {
338  .set_sysclk = spdif_set_sysclk,
339  .trigger = spdif_trigger,
340  .hw_params = spdif_hw_params,
341  .shutdown = spdif_shutdown,
342 };
343 
344 static struct snd_soc_dai_driver samsung_spdif_dai = {
345  .name = "samsung-spdif",
346  .playback = {
347  .stream_name = "S/PDIF Playback",
348  .channels_min = 2,
349  .channels_max = 2,
350  .rates = (SNDRV_PCM_RATE_32000 |
354  .formats = SNDRV_PCM_FMTBIT_S16_LE, },
355  .ops = &spdif_dai_ops,
357  .resume = spdif_resume,
358 };
359 
360 static __devinit int spdif_probe(struct platform_device *pdev)
361 {
362  struct s3c_audio_pdata *spdif_pdata;
363  struct resource *mem_res, *dma_res;
364  struct samsung_spdif_info *spdif;
365  int ret;
366 
367  spdif_pdata = pdev->dev.platform_data;
368 
369  dev_dbg(&pdev->dev, "Entered %s\n", __func__);
370 
371  dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
372  if (!dma_res) {
373  dev_err(&pdev->dev, "Unable to get dma resource.\n");
374  return -ENXIO;
375  }
376 
377  mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
378  if (!mem_res) {
379  dev_err(&pdev->dev, "Unable to get register resource.\n");
380  return -ENXIO;
381  }
382 
383  if (spdif_pdata && spdif_pdata->cfg_gpio
384  && spdif_pdata->cfg_gpio(pdev)) {
385  dev_err(&pdev->dev, "Unable to configure GPIO pins\n");
386  return -EINVAL;
387  }
388 
389  spdif = &spdif_info;
390  spdif->dev = &pdev->dev;
391 
392  spin_lock_init(&spdif->lock);
393 
394  spdif->pclk = clk_get(&pdev->dev, "spdif");
395  if (IS_ERR(spdif->pclk)) {
396  dev_err(&pdev->dev, "failed to get peri-clock\n");
397  ret = -ENOENT;
398  goto err0;
399  }
400  clk_enable(spdif->pclk);
401 
402  spdif->sclk = clk_get(&pdev->dev, "sclk_spdif");
403  if (IS_ERR(spdif->sclk)) {
404  dev_err(&pdev->dev, "failed to get internal source clock\n");
405  ret = -ENOENT;
406  goto err1;
407  }
408  clk_enable(spdif->sclk);
409 
410  /* Request S/PDIF Register's memory region */
411  if (!request_mem_region(mem_res->start,
412  resource_size(mem_res), "samsung-spdif")) {
413  dev_err(&pdev->dev, "Unable to request register region\n");
414  ret = -EBUSY;
415  goto err2;
416  }
417 
418  spdif->regs = ioremap(mem_res->start, 0x100);
419  if (spdif->regs == NULL) {
420  dev_err(&pdev->dev, "Cannot ioremap registers\n");
421  ret = -ENXIO;
422  goto err3;
423  }
424 
425  dev_set_drvdata(&pdev->dev, spdif);
426 
427  ret = snd_soc_register_dai(&pdev->dev, &samsung_spdif_dai);
428  if (ret != 0) {
429  dev_err(&pdev->dev, "fail to register dai\n");
430  goto err4;
431  }
432 
433  spdif_stereo_out.dma_size = 2;
434  spdif_stereo_out.client = &spdif_dma_client_out;
435  spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF;
436  spdif_stereo_out.channel = dma_res->start;
437 
438  spdif->dma_playback = &spdif_stereo_out;
439 
440  return 0;
441 
442 err4:
443  iounmap(spdif->regs);
444 err3:
445  release_mem_region(mem_res->start, resource_size(mem_res));
446 err2:
447  clk_disable(spdif->sclk);
448  clk_put(spdif->sclk);
449 err1:
450  clk_disable(spdif->pclk);
451  clk_put(spdif->pclk);
452 err0:
453  return ret;
454 }
455 
456 static __devexit int spdif_remove(struct platform_device *pdev)
457 {
458  struct samsung_spdif_info *spdif = &spdif_info;
459  struct resource *mem_res;
460 
461  snd_soc_unregister_dai(&pdev->dev);
462 
463  iounmap(spdif->regs);
464 
465  mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
466  if (mem_res)
467  release_mem_region(mem_res->start, resource_size(mem_res));
468 
469  clk_disable(spdif->sclk);
470  clk_put(spdif->sclk);
471  clk_disable(spdif->pclk);
472  clk_put(spdif->pclk);
473 
474  return 0;
475 }
476 
477 static struct platform_driver samsung_spdif_driver = {
478  .probe = spdif_probe,
479  .remove = __devexit_p(spdif_remove),
480  .driver = {
481  .name = "samsung-spdif",
482  .owner = THIS_MODULE,
483  },
484 };
485 
486 module_platform_driver(samsung_spdif_driver);
487 
488 MODULE_AUTHOR("Seungwhan Youn, <[email protected]>");
489 MODULE_DESCRIPTION("Samsung S/PDIF Controller Driver");
490 MODULE_LICENSE("GPL");
491 MODULE_ALIAS("platform:samsung-spdif");