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>
15 #include <linux/module.h>
17 #include <sound/soc.h>
18 #include <sound/pcm_params.h>
20 #include <linux/platform_data/asoc-s3c.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 s3c_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_width(params
)) {
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 const 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 static 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 const struct snd_soc_component_driver samsung_spdif_component
= {
360 .name
= "samsung-spdif",
363 static int spdif_probe(struct platform_device
*pdev
)
365 struct s3c_audio_pdata
*spdif_pdata
;
366 struct resource
*mem_res
, *dma_res
;
367 struct samsung_spdif_info
*spdif
;
370 spdif_pdata
= pdev
->dev
.platform_data
;
372 dev_dbg(&pdev
->dev
, "Entered %s\n", __func__
);
374 dma_res
= platform_get_resource(pdev
, IORESOURCE_DMA
, 0);
376 dev_err(&pdev
->dev
, "Unable to get dma resource.\n");
380 mem_res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
382 dev_err(&pdev
->dev
, "Unable to get register resource.\n");
386 if (spdif_pdata
&& spdif_pdata
->cfg_gpio
387 && spdif_pdata
->cfg_gpio(pdev
)) {
388 dev_err(&pdev
->dev
, "Unable to configure GPIO pins\n");
393 spdif
->dev
= &pdev
->dev
;
395 spin_lock_init(&spdif
->lock
);
397 spdif
->pclk
= devm_clk_get(&pdev
->dev
, "spdif");
398 if (IS_ERR(spdif
->pclk
)) {
399 dev_err(&pdev
->dev
, "failed to get peri-clock\n");
403 clk_prepare_enable(spdif
->pclk
);
405 spdif
->sclk
= devm_clk_get(&pdev
->dev
, "sclk_spdif");
406 if (IS_ERR(spdif
->sclk
)) {
407 dev_err(&pdev
->dev
, "failed to get internal source clock\n");
411 clk_prepare_enable(spdif
->sclk
);
413 /* Request S/PDIF Register's memory region */
414 if (!request_mem_region(mem_res
->start
,
415 resource_size(mem_res
), "samsung-spdif")) {
416 dev_err(&pdev
->dev
, "Unable to request register region\n");
421 spdif
->regs
= ioremap(mem_res
->start
, 0x100);
422 if (spdif
->regs
== NULL
) {
423 dev_err(&pdev
->dev
, "Cannot ioremap registers\n");
428 dev_set_drvdata(&pdev
->dev
, spdif
);
430 ret
= devm_snd_soc_register_component(&pdev
->dev
,
431 &samsung_spdif_component
, &samsung_spdif_dai
, 1);
433 dev_err(&pdev
->dev
, "fail to register dai\n");
437 spdif_stereo_out
.dma_size
= 2;
438 spdif_stereo_out
.client
= &spdif_dma_client_out
;
439 spdif_stereo_out
.dma_addr
= mem_res
->start
+ DATA_OUTBUF
;
440 spdif_stereo_out
.channel
= dma_res
->start
;
442 spdif
->dma_playback
= &spdif_stereo_out
;
444 ret
= samsung_asoc_dma_platform_register(&pdev
->dev
);
446 dev_err(&pdev
->dev
, "failed to register DMA: %d\n", ret
);
452 iounmap(spdif
->regs
);
454 release_mem_region(mem_res
->start
, resource_size(mem_res
));
456 clk_disable_unprepare(spdif
->sclk
);
458 clk_disable_unprepare(spdif
->pclk
);
463 static int spdif_remove(struct platform_device
*pdev
)
465 struct samsung_spdif_info
*spdif
= &spdif_info
;
466 struct resource
*mem_res
;
468 iounmap(spdif
->regs
);
470 mem_res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
472 release_mem_region(mem_res
->start
, resource_size(mem_res
));
474 clk_disable_unprepare(spdif
->sclk
);
475 clk_disable_unprepare(spdif
->pclk
);
480 static struct platform_driver samsung_spdif_driver
= {
481 .probe
= spdif_probe
,
482 .remove
= spdif_remove
,
484 .name
= "samsung-spdif",
485 .owner
= THIS_MODULE
,
489 module_platform_driver(samsung_spdif_driver
);
491 MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
492 MODULE_DESCRIPTION("Samsung S/PDIF Controller Driver");
493 MODULE_LICENSE("GPL");
494 MODULE_ALIAS("platform:samsung-spdif");