1 /* sound/soc/samsung/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.
13 #include <linux/clk.h>
16 #include <sound/soc.h>
17 #include <sound/pcm_params.h>
19 #include <plat/audio.h>
30 #define DATA_OUTBUF 0x10
35 #define CLKCTL_MASK 0x7
36 #define CLKCTL_MCLK_EXT (0x1 << 2)
37 #define CLKCTL_PWR_ON (0x1 << 0)
39 #define CON_MASK 0x3ffffff
40 #define CON_FIFO_TH_SHIFT 19
41 #define CON_FIFO_TH_MASK (0x7 << 19)
42 #define CON_USERDATA_23RDBIT (0x1 << 12)
44 #define CON_SW_RESET (0x1 << 5)
46 #define CON_MCLKDIV_MASK (0x3 << 3)
47 #define CON_MCLKDIV_256FS (0x0 << 3)
48 #define CON_MCLKDIV_384FS (0x1 << 3)
49 #define CON_MCLKDIV_512FS (0x2 << 3)
51 #define CON_PCM_MASK (0x3 << 1)
52 #define CON_PCM_16BIT (0x0 << 1)
53 #define CON_PCM_20BIT (0x1 << 1)
54 #define CON_PCM_24BIT (0x2 << 1)
56 #define CON_PCM_DATA (0x1 << 0)
58 #define CSTAS_MASK 0x3fffffff
59 #define CSTAS_SAMP_FREQ_MASK (0xF << 24)
60 #define CSTAS_SAMP_FREQ_44 (0x0 << 24)
61 #define CSTAS_SAMP_FREQ_48 (0x2 << 24)
62 #define CSTAS_SAMP_FREQ_32 (0x3 << 24)
63 #define CSTAS_SAMP_FREQ_96 (0xA << 24)
65 #define CSTAS_CATEGORY_MASK (0xFF << 8)
66 #define CSTAS_CATEGORY_CODE_CDP (0x01 << 8)
68 #define CSTAS_NO_COPYRIGHT (0x1 << 2)
71 * struct samsung_spdif_info - Samsung S/PDIF Controller information
72 * @lock: Spin lock for S/PDIF.
73 * @dev: The parent device passed to use from the probe.
74 * @regs: The pointer to the device register block.
75 * @clk_rate: Current clock rate for calcurate ratio.
76 * @pclk: The peri-clock pointer for spdif master operation.
77 * @sclk: The source clock pointer for making sync signals.
78 * @save_clkcon: Backup clkcon reg. in suspend.
79 * @save_con: Backup con reg. in suspend.
80 * @save_cstas: Backup cstas reg. in suspend.
81 * @dma_playback: DMA information for playback channel.
83 struct samsung_spdif_info
{
87 unsigned long clk_rate
;
93 struct s3c_dma_params
*dma_playback
;
96 static struct s3c2410_dma_client spdif_dma_client_out
= {
97 .name
= "S/PDIF Stereo out",
100 static struct s3c_dma_params spdif_stereo_out
;
101 static struct samsung_spdif_info spdif_info
;
103 static inline struct samsung_spdif_info
*to_info(struct snd_soc_dai
*cpu_dai
)
105 return snd_soc_dai_get_drvdata(cpu_dai
);
108 static void spdif_snd_txctrl(struct samsung_spdif_info
*spdif
, int on
)
110 void __iomem
*regs
= spdif
->regs
;
113 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
115 clkcon
= readl(regs
+ CLKCON
) & CLKCTL_MASK
;
117 writel(clkcon
| CLKCTL_PWR_ON
, regs
+ CLKCON
);
119 writel(clkcon
& ~CLKCTL_PWR_ON
, regs
+ CLKCON
);
122 static int spdif_set_sysclk(struct snd_soc_dai
*cpu_dai
,
123 int clk_id
, unsigned int freq
, int dir
)
125 struct samsung_spdif_info
*spdif
= to_info(cpu_dai
);
128 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
130 clkcon
= readl(spdif
->regs
+ CLKCON
);
132 if (clk_id
== SND_SOC_SPDIF_INT_MCLK
)
133 clkcon
&= ~CLKCTL_MCLK_EXT
;
135 clkcon
|= CLKCTL_MCLK_EXT
;
137 writel(clkcon
, spdif
->regs
+ CLKCON
);
139 spdif
->clk_rate
= freq
;
144 static int spdif_trigger(struct snd_pcm_substream
*substream
, int cmd
,
145 struct snd_soc_dai
*dai
)
147 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
148 struct samsung_spdif_info
*spdif
= to_info(rtd
->cpu_dai
);
151 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
154 case SNDRV_PCM_TRIGGER_START
:
155 case SNDRV_PCM_TRIGGER_RESUME
:
156 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
157 spin_lock_irqsave(&spdif
->lock
, flags
);
158 spdif_snd_txctrl(spdif
, 1);
159 spin_unlock_irqrestore(&spdif
->lock
, flags
);
161 case SNDRV_PCM_TRIGGER_STOP
:
162 case SNDRV_PCM_TRIGGER_SUSPEND
:
163 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
164 spin_lock_irqsave(&spdif
->lock
, flags
);
165 spdif_snd_txctrl(spdif
, 0);
166 spin_unlock_irqrestore(&spdif
->lock
, flags
);
175 static int spdif_sysclk_ratios
[] = {
179 static int spdif_hw_params(struct snd_pcm_substream
*substream
,
180 struct snd_pcm_hw_params
*params
,
181 struct snd_soc_dai
*socdai
)
183 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
184 struct samsung_spdif_info
*spdif
= to_info(rtd
->cpu_dai
);
185 void __iomem
*regs
= spdif
->regs
;
186 struct s3c_dma_params
*dma_data
;
187 u32 con
, clkcon
, cstas
;
191 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
193 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
194 dma_data
= spdif
->dma_playback
;
196 dev_err(spdif
->dev
, "Capture is not supported\n");
200 snd_soc_dai_set_dma_data(rtd
->cpu_dai
, substream
, dma_data
);
202 spin_lock_irqsave(&spdif
->lock
, flags
);
204 con
= readl(regs
+ CON
) & CON_MASK
;
205 cstas
= readl(regs
+ CSTAS
) & CSTAS_MASK
;
206 clkcon
= readl(regs
+ CLKCON
) & CLKCTL_MASK
;
208 con
&= ~CON_FIFO_TH_MASK
;
209 con
|= (0x7 << CON_FIFO_TH_SHIFT
);
210 con
|= CON_USERDATA_23RDBIT
;
213 con
&= ~CON_PCM_MASK
;
214 switch (params_format(params
)) {
215 case SNDRV_PCM_FORMAT_S16_LE
:
216 con
|= CON_PCM_16BIT
;
219 dev_err(spdif
->dev
, "Unsupported data size.\n");
223 ratio
= spdif
->clk_rate
/ params_rate(params
);
224 for (i
= 0; i
< ARRAY_SIZE(spdif_sysclk_ratios
); i
++)
225 if (ratio
== spdif_sysclk_ratios
[i
])
227 if (i
== ARRAY_SIZE(spdif_sysclk_ratios
)) {
228 dev_err(spdif
->dev
, "Invalid clock ratio %ld/%d\n",
229 spdif
->clk_rate
, params_rate(params
));
233 con
&= ~CON_MCLKDIV_MASK
;
236 con
|= CON_MCLKDIV_256FS
;
239 con
|= CON_MCLKDIV_384FS
;
242 con
|= CON_MCLKDIV_512FS
;
246 cstas
&= ~CSTAS_SAMP_FREQ_MASK
;
247 switch (params_rate(params
)) {
249 cstas
|= CSTAS_SAMP_FREQ_44
;
252 cstas
|= CSTAS_SAMP_FREQ_48
;
255 cstas
|= CSTAS_SAMP_FREQ_32
;
258 cstas
|= CSTAS_SAMP_FREQ_96
;
261 dev_err(spdif
->dev
, "Invalid sampling rate %d\n",
262 params_rate(params
));
266 cstas
&= ~CSTAS_CATEGORY_MASK
;
267 cstas
|= CSTAS_CATEGORY_CODE_CDP
;
268 cstas
|= CSTAS_NO_COPYRIGHT
;
270 writel(con
, regs
+ CON
);
271 writel(cstas
, regs
+ CSTAS
);
272 writel(clkcon
, regs
+ CLKCON
);
274 spin_unlock_irqrestore(&spdif
->lock
, flags
);
278 spin_unlock_irqrestore(&spdif
->lock
, flags
);
282 static void spdif_shutdown(struct snd_pcm_substream
*substream
,
283 struct snd_soc_dai
*dai
)
285 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
286 struct samsung_spdif_info
*spdif
= to_info(rtd
->cpu_dai
);
287 void __iomem
*regs
= spdif
->regs
;
290 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
292 con
= readl(regs
+ CON
) & CON_MASK
;
293 clkcon
= readl(regs
+ CLKCON
) & CLKCTL_MASK
;
295 writel(con
| CON_SW_RESET
, regs
+ CON
);
298 writel(clkcon
& ~CLKCTL_PWR_ON
, regs
+ CLKCON
);
302 static int spdif_suspend(struct snd_soc_dai
*cpu_dai
)
304 struct samsung_spdif_info
*spdif
= to_info(cpu_dai
);
305 u32 con
= spdif
->saved_con
;
307 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
309 spdif
->saved_clkcon
= readl(spdif
->regs
+ CLKCON
) & CLKCTL_MASK
;
310 spdif
->saved_con
= readl(spdif
->regs
+ CON
) & CON_MASK
;
311 spdif
->saved_cstas
= readl(spdif
->regs
+ CSTAS
) & CSTAS_MASK
;
313 writel(con
| CON_SW_RESET
, spdif
->regs
+ CON
);
319 static int spdif_resume(struct snd_soc_dai
*cpu_dai
)
321 struct samsung_spdif_info
*spdif
= to_info(cpu_dai
);
323 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
325 writel(spdif
->saved_clkcon
, spdif
->regs
+ CLKCON
);
326 writel(spdif
->saved_con
, spdif
->regs
+ CON
);
327 writel(spdif
->saved_cstas
, spdif
->regs
+ CSTAS
);
332 #define spdif_suspend NULL
333 #define spdif_resume NULL
336 static struct snd_soc_dai_ops spdif_dai_ops
= {
337 .set_sysclk
= spdif_set_sysclk
,
338 .trigger
= spdif_trigger
,
339 .hw_params
= spdif_hw_params
,
340 .shutdown
= spdif_shutdown
,
343 struct snd_soc_dai_driver samsung_spdif_dai
= {
344 .name
= "samsung-spdif",
346 .stream_name
= "S/PDIF Playback",
349 .rates
= (SNDRV_PCM_RATE_32000
|
350 SNDRV_PCM_RATE_44100
|
351 SNDRV_PCM_RATE_48000
|
352 SNDRV_PCM_RATE_96000
),
353 .formats
= SNDRV_PCM_FMTBIT_S16_LE
, },
354 .ops
= &spdif_dai_ops
,
355 .suspend
= spdif_suspend
,
356 .resume
= spdif_resume
,
359 static __devinit
int spdif_probe(struct platform_device
*pdev
)
361 struct s3c_audio_pdata
*spdif_pdata
;
362 struct resource
*mem_res
, *dma_res
;
363 struct samsung_spdif_info
*spdif
;
366 spdif_pdata
= pdev
->dev
.platform_data
;
368 dev_dbg(&pdev
->dev
, "Entered %s\n", __func__
);
370 dma_res
= platform_get_resource(pdev
, IORESOURCE_DMA
, 0);
372 dev_err(&pdev
->dev
, "Unable to get dma resource.\n");
376 mem_res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
378 dev_err(&pdev
->dev
, "Unable to get register resource.\n");
382 if (spdif_pdata
&& spdif_pdata
->cfg_gpio
383 && spdif_pdata
->cfg_gpio(pdev
)) {
384 dev_err(&pdev
->dev
, "Unable to configure GPIO pins\n");
389 spdif
->dev
= &pdev
->dev
;
391 spin_lock_init(&spdif
->lock
);
393 spdif
->pclk
= clk_get(&pdev
->dev
, "spdif");
394 if (IS_ERR(spdif
->pclk
)) {
395 dev_err(&pdev
->dev
, "failed to get peri-clock\n");
399 clk_enable(spdif
->pclk
);
401 spdif
->sclk
= clk_get(&pdev
->dev
, "sclk_spdif");
402 if (IS_ERR(spdif
->sclk
)) {
403 dev_err(&pdev
->dev
, "failed to get internal source clock\n");
407 clk_enable(spdif
->sclk
);
409 /* Request S/PDIF Register's memory region */
410 if (!request_mem_region(mem_res
->start
,
411 resource_size(mem_res
), "samsung-spdif")) {
412 dev_err(&pdev
->dev
, "Unable to request register region\n");
417 spdif
->regs
= ioremap(mem_res
->start
, 0x100);
418 if (spdif
->regs
== NULL
) {
419 dev_err(&pdev
->dev
, "Cannot ioremap registers\n");
424 dev_set_drvdata(&pdev
->dev
, spdif
);
426 ret
= snd_soc_register_dai(&pdev
->dev
, &samsung_spdif_dai
);
428 dev_err(&pdev
->dev
, "fail to register dai\n");
432 spdif_stereo_out
.dma_size
= 2;
433 spdif_stereo_out
.client
= &spdif_dma_client_out
;
434 spdif_stereo_out
.dma_addr
= mem_res
->start
+ DATA_OUTBUF
;
435 spdif_stereo_out
.channel
= dma_res
->start
;
437 spdif
->dma_playback
= &spdif_stereo_out
;
442 iounmap(spdif
->regs
);
444 release_mem_region(mem_res
->start
, resource_size(mem_res
));
446 clk_disable(spdif
->sclk
);
447 clk_put(spdif
->sclk
);
449 clk_disable(spdif
->pclk
);
450 clk_put(spdif
->pclk
);
455 static __devexit
int spdif_remove(struct platform_device
*pdev
)
457 struct samsung_spdif_info
*spdif
= &spdif_info
;
458 struct resource
*mem_res
;
460 snd_soc_unregister_dai(&pdev
->dev
);
462 iounmap(spdif
->regs
);
464 mem_res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
466 release_mem_region(mem_res
->start
, resource_size(mem_res
));
468 clk_disable(spdif
->sclk
);
469 clk_put(spdif
->sclk
);
470 clk_disable(spdif
->pclk
);
471 clk_put(spdif
->pclk
);
476 static struct platform_driver samsung_spdif_driver
= {
477 .probe
= spdif_probe
,
478 .remove
= spdif_remove
,
480 .name
= "samsung-spdif",
481 .owner
= THIS_MODULE
,
485 static int __init
spdif_init(void)
487 return platform_driver_register(&samsung_spdif_driver
);
489 module_init(spdif_init
);
491 static void __exit
spdif_exit(void)
493 platform_driver_unregister(&samsung_spdif_driver
);
495 module_exit(spdif_exit
);
497 MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
498 MODULE_DESCRIPTION("Samsung S/PDIF Controller Driver");
499 MODULE_LICENSE("GPL");
500 MODULE_ALIAS("platform:samsung-spdif");