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>
31 #define DATA_OUTBUF 0x10
36 #define CLKCTL_MASK 0x7
37 #define CLKCTL_MCLK_EXT (0x1 << 2)
38 #define CLKCTL_PWR_ON (0x1 << 0)
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)
45 #define CON_SW_RESET (0x1 << 5)
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)
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)
57 #define CON_PCM_DATA (0x1 << 0)
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)
66 #define CSTAS_CATEGORY_MASK (0xFF << 8)
67 #define CSTAS_CATEGORY_CODE_CDP (0x01 << 8)
69 #define CSTAS_NO_COPYRIGHT (0x1 << 2)
72 * struct samsung_spdif_info - Samsung S/PDIF Controller information
73 * @lock: Spin lock for S/PDIF.
74 * @dev: The parent device passed to use from the probe.
75 * @regs: The pointer to the device register block.
76 * @clk_rate: Current clock rate for calcurate ratio.
77 * @pclk: The peri-clock pointer for spdif master operation.
78 * @sclk: The source clock pointer for making sync signals.
79 * @save_clkcon: Backup clkcon reg. in suspend.
80 * @save_con: Backup con reg. in suspend.
81 * @save_cstas: Backup cstas reg. in suspend.
82 * @dma_playback: DMA information for playback channel.
84 struct samsung_spdif_info
{
88 unsigned long clk_rate
;
94 struct s3c_dma_params
*dma_playback
;
97 static struct s3c2410_dma_client spdif_dma_client_out
= {
98 .name
= "S/PDIF Stereo out",
101 static struct s3c_dma_params spdif_stereo_out
;
102 static struct samsung_spdif_info spdif_info
;
104 static inline struct samsung_spdif_info
*to_info(struct snd_soc_dai
*cpu_dai
)
106 return snd_soc_dai_get_drvdata(cpu_dai
);
109 static void spdif_snd_txctrl(struct samsung_spdif_info
*spdif
, int on
)
111 void __iomem
*regs
= spdif
->regs
;
114 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
116 clkcon
= readl(regs
+ CLKCON
) & CLKCTL_MASK
;
118 writel(clkcon
| CLKCTL_PWR_ON
, regs
+ CLKCON
);
120 writel(clkcon
& ~CLKCTL_PWR_ON
, regs
+ CLKCON
);
123 static int spdif_set_sysclk(struct snd_soc_dai
*cpu_dai
,
124 int clk_id
, unsigned int freq
, int dir
)
126 struct samsung_spdif_info
*spdif
= to_info(cpu_dai
);
129 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
131 clkcon
= readl(spdif
->regs
+ CLKCON
);
133 if (clk_id
== SND_SOC_SPDIF_INT_MCLK
)
134 clkcon
&= ~CLKCTL_MCLK_EXT
;
136 clkcon
|= CLKCTL_MCLK_EXT
;
138 writel(clkcon
, spdif
->regs
+ CLKCON
);
140 spdif
->clk_rate
= freq
;
145 static int spdif_trigger(struct snd_pcm_substream
*substream
, int cmd
,
146 struct snd_soc_dai
*dai
)
148 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
149 struct samsung_spdif_info
*spdif
= to_info(rtd
->cpu_dai
);
152 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
155 case SNDRV_PCM_TRIGGER_START
:
156 case SNDRV_PCM_TRIGGER_RESUME
:
157 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
158 spin_lock_irqsave(&spdif
->lock
, flags
);
159 spdif_snd_txctrl(spdif
, 1);
160 spin_unlock_irqrestore(&spdif
->lock
, flags
);
162 case SNDRV_PCM_TRIGGER_STOP
:
163 case SNDRV_PCM_TRIGGER_SUSPEND
:
164 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
165 spin_lock_irqsave(&spdif
->lock
, flags
);
166 spdif_snd_txctrl(spdif
, 0);
167 spin_unlock_irqrestore(&spdif
->lock
, flags
);
176 static int spdif_sysclk_ratios
[] = {
180 static int spdif_hw_params(struct snd_pcm_substream
*substream
,
181 struct snd_pcm_hw_params
*params
,
182 struct snd_soc_dai
*socdai
)
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
;
192 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
194 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
195 dma_data
= spdif
->dma_playback
;
197 dev_err(spdif
->dev
, "Capture is not supported\n");
201 snd_soc_dai_set_dma_data(rtd
->cpu_dai
, substream
, dma_data
);
203 spin_lock_irqsave(&spdif
->lock
, flags
);
205 con
= readl(regs
+ CON
) & CON_MASK
;
206 cstas
= readl(regs
+ CSTAS
) & CSTAS_MASK
;
207 clkcon
= readl(regs
+ CLKCON
) & CLKCTL_MASK
;
209 con
&= ~CON_FIFO_TH_MASK
;
210 con
|= (0x7 << CON_FIFO_TH_SHIFT
);
211 con
|= CON_USERDATA_23RDBIT
;
214 con
&= ~CON_PCM_MASK
;
215 switch (params_format(params
)) {
216 case SNDRV_PCM_FORMAT_S16_LE
:
217 con
|= CON_PCM_16BIT
;
220 dev_err(spdif
->dev
, "Unsupported data size.\n");
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
])
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
));
234 con
&= ~CON_MCLKDIV_MASK
;
237 con
|= CON_MCLKDIV_256FS
;
240 con
|= CON_MCLKDIV_384FS
;
243 con
|= CON_MCLKDIV_512FS
;
247 cstas
&= ~CSTAS_SAMP_FREQ_MASK
;
248 switch (params_rate(params
)) {
250 cstas
|= CSTAS_SAMP_FREQ_44
;
253 cstas
|= CSTAS_SAMP_FREQ_48
;
256 cstas
|= CSTAS_SAMP_FREQ_32
;
259 cstas
|= CSTAS_SAMP_FREQ_96
;
262 dev_err(spdif
->dev
, "Invalid sampling rate %d\n",
263 params_rate(params
));
267 cstas
&= ~CSTAS_CATEGORY_MASK
;
268 cstas
|= CSTAS_CATEGORY_CODE_CDP
;
269 cstas
|= CSTAS_NO_COPYRIGHT
;
271 writel(con
, regs
+ CON
);
272 writel(cstas
, regs
+ CSTAS
);
273 writel(clkcon
, regs
+ CLKCON
);
275 spin_unlock_irqrestore(&spdif
->lock
, flags
);
279 spin_unlock_irqrestore(&spdif
->lock
, flags
);
283 static void spdif_shutdown(struct snd_pcm_substream
*substream
,
284 struct snd_soc_dai
*dai
)
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
;
291 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
293 con
= readl(regs
+ CON
) & CON_MASK
;
294 clkcon
= readl(regs
+ CLKCON
) & CLKCTL_MASK
;
296 writel(con
| CON_SW_RESET
, regs
+ CON
);
299 writel(clkcon
& ~CLKCTL_PWR_ON
, regs
+ CLKCON
);
303 static int spdif_suspend(struct snd_soc_dai
*cpu_dai
)
305 struct samsung_spdif_info
*spdif
= to_info(cpu_dai
);
306 u32 con
= spdif
->saved_con
;
308 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
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
;
314 writel(con
| CON_SW_RESET
, spdif
->regs
+ CON
);
320 static int spdif_resume(struct snd_soc_dai
*cpu_dai
)
322 struct samsung_spdif_info
*spdif
= to_info(cpu_dai
);
324 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
326 writel(spdif
->saved_clkcon
, spdif
->regs
+ CLKCON
);
327 writel(spdif
->saved_con
, spdif
->regs
+ CON
);
328 writel(spdif
->saved_cstas
, spdif
->regs
+ CSTAS
);
333 #define spdif_suspend NULL
334 #define spdif_resume NULL
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
,
344 static struct snd_soc_dai_driver samsung_spdif_dai
= {
345 .name
= "samsung-spdif",
347 .stream_name
= "S/PDIF Playback",
350 .rates
= (SNDRV_PCM_RATE_32000
|
351 SNDRV_PCM_RATE_44100
|
352 SNDRV_PCM_RATE_48000
|
353 SNDRV_PCM_RATE_96000
),
354 .formats
= SNDRV_PCM_FMTBIT_S16_LE
, },
355 .ops
= &spdif_dai_ops
,
356 .suspend
= spdif_suspend
,
357 .resume
= spdif_resume
,
360 static const struct snd_soc_component_driver samsung_spdif_component
= {
361 .name
= "samsung-spdif",
364 static int spdif_probe(struct platform_device
*pdev
)
366 struct s3c_audio_pdata
*spdif_pdata
;
367 struct resource
*mem_res
, *dma_res
;
368 struct samsung_spdif_info
*spdif
;
371 spdif_pdata
= pdev
->dev
.platform_data
;
373 dev_dbg(&pdev
->dev
, "Entered %s\n", __func__
);
375 dma_res
= platform_get_resource(pdev
, IORESOURCE_DMA
, 0);
377 dev_err(&pdev
->dev
, "Unable to get dma resource.\n");
381 mem_res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
383 dev_err(&pdev
->dev
, "Unable to get register resource.\n");
387 if (spdif_pdata
&& spdif_pdata
->cfg_gpio
388 && spdif_pdata
->cfg_gpio(pdev
)) {
389 dev_err(&pdev
->dev
, "Unable to configure GPIO pins\n");
394 spdif
->dev
= &pdev
->dev
;
396 spin_lock_init(&spdif
->lock
);
398 spdif
->pclk
= devm_clk_get(&pdev
->dev
, "spdif");
399 if (IS_ERR(spdif
->pclk
)) {
400 dev_err(&pdev
->dev
, "failed to get peri-clock\n");
404 clk_prepare_enable(spdif
->pclk
);
406 spdif
->sclk
= devm_clk_get(&pdev
->dev
, "sclk_spdif");
407 if (IS_ERR(spdif
->sclk
)) {
408 dev_err(&pdev
->dev
, "failed to get internal source clock\n");
412 clk_prepare_enable(spdif
->sclk
);
414 /* Request S/PDIF Register's memory region */
415 if (!request_mem_region(mem_res
->start
,
416 resource_size(mem_res
), "samsung-spdif")) {
417 dev_err(&pdev
->dev
, "Unable to request register region\n");
422 spdif
->regs
= ioremap(mem_res
->start
, 0x100);
423 if (spdif
->regs
== NULL
) {
424 dev_err(&pdev
->dev
, "Cannot ioremap registers\n");
429 dev_set_drvdata(&pdev
->dev
, spdif
);
431 ret
= snd_soc_register_component(&pdev
->dev
, &samsung_spdif_component
,
432 &samsung_spdif_dai
, 1);
434 dev_err(&pdev
->dev
, "fail to register dai\n");
438 spdif_stereo_out
.dma_size
= 2;
439 spdif_stereo_out
.client
= &spdif_dma_client_out
;
440 spdif_stereo_out
.dma_addr
= mem_res
->start
+ DATA_OUTBUF
;
441 spdif_stereo_out
.channel
= dma_res
->start
;
443 spdif
->dma_playback
= &spdif_stereo_out
;
445 ret
= samsung_asoc_dma_platform_register(&pdev
->dev
);
447 dev_err(&pdev
->dev
, "failed to register DMA: %d\n", ret
);
453 snd_soc_unregister_component(&pdev
->dev
);
455 iounmap(spdif
->regs
);
457 release_mem_region(mem_res
->start
, resource_size(mem_res
));
459 clk_disable_unprepare(spdif
->sclk
);
461 clk_disable_unprepare(spdif
->pclk
);
466 static int spdif_remove(struct platform_device
*pdev
)
468 struct samsung_spdif_info
*spdif
= &spdif_info
;
469 struct resource
*mem_res
;
471 samsung_asoc_dma_platform_unregister(&pdev
->dev
);
472 snd_soc_unregister_component(&pdev
->dev
);
474 iounmap(spdif
->regs
);
476 mem_res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
478 release_mem_region(mem_res
->start
, resource_size(mem_res
));
480 clk_disable_unprepare(spdif
->sclk
);
481 clk_disable_unprepare(spdif
->pclk
);
486 static struct platform_driver samsung_spdif_driver
= {
487 .probe
= spdif_probe
,
488 .remove
= spdif_remove
,
490 .name
= "samsung-spdif",
491 .owner
= THIS_MODULE
,
495 module_platform_driver(samsung_spdif_driver
);
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");