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_params spdif_stereo_out
;
97 static struct samsung_spdif_info spdif_info
;
99 static inline struct samsung_spdif_info
*to_info(struct snd_soc_dai
*cpu_dai
)
101 return snd_soc_dai_get_drvdata(cpu_dai
);
104 static void spdif_snd_txctrl(struct samsung_spdif_info
*spdif
, int on
)
106 void __iomem
*regs
= spdif
->regs
;
109 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
111 clkcon
= readl(regs
+ CLKCON
) & CLKCTL_MASK
;
113 writel(clkcon
| CLKCTL_PWR_ON
, regs
+ CLKCON
);
115 writel(clkcon
& ~CLKCTL_PWR_ON
, regs
+ CLKCON
);
118 static int spdif_set_sysclk(struct snd_soc_dai
*cpu_dai
,
119 int clk_id
, unsigned int freq
, int dir
)
121 struct samsung_spdif_info
*spdif
= to_info(cpu_dai
);
124 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
126 clkcon
= readl(spdif
->regs
+ CLKCON
);
128 if (clk_id
== SND_SOC_SPDIF_INT_MCLK
)
129 clkcon
&= ~CLKCTL_MCLK_EXT
;
131 clkcon
|= CLKCTL_MCLK_EXT
;
133 writel(clkcon
, spdif
->regs
+ CLKCON
);
135 spdif
->clk_rate
= freq
;
140 static int spdif_trigger(struct snd_pcm_substream
*substream
, int cmd
,
141 struct snd_soc_dai
*dai
)
143 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
144 struct samsung_spdif_info
*spdif
= to_info(rtd
->cpu_dai
);
147 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
150 case SNDRV_PCM_TRIGGER_START
:
151 case SNDRV_PCM_TRIGGER_RESUME
:
152 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
153 spin_lock_irqsave(&spdif
->lock
, flags
);
154 spdif_snd_txctrl(spdif
, 1);
155 spin_unlock_irqrestore(&spdif
->lock
, flags
);
157 case SNDRV_PCM_TRIGGER_STOP
:
158 case SNDRV_PCM_TRIGGER_SUSPEND
:
159 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
160 spin_lock_irqsave(&spdif
->lock
, flags
);
161 spdif_snd_txctrl(spdif
, 0);
162 spin_unlock_irqrestore(&spdif
->lock
, flags
);
171 static int spdif_sysclk_ratios
[] = {
175 static int spdif_hw_params(struct snd_pcm_substream
*substream
,
176 struct snd_pcm_hw_params
*params
,
177 struct snd_soc_dai
*socdai
)
179 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
180 struct samsung_spdif_info
*spdif
= to_info(rtd
->cpu_dai
);
181 void __iomem
*regs
= spdif
->regs
;
182 struct s3c_dma_params
*dma_data
;
183 u32 con
, clkcon
, cstas
;
187 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
189 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
190 dma_data
= spdif
->dma_playback
;
192 dev_err(spdif
->dev
, "Capture is not supported\n");
196 snd_soc_dai_set_dma_data(rtd
->cpu_dai
, substream
, dma_data
);
198 spin_lock_irqsave(&spdif
->lock
, flags
);
200 con
= readl(regs
+ CON
) & CON_MASK
;
201 cstas
= readl(regs
+ CSTAS
) & CSTAS_MASK
;
202 clkcon
= readl(regs
+ CLKCON
) & CLKCTL_MASK
;
204 con
&= ~CON_FIFO_TH_MASK
;
205 con
|= (0x7 << CON_FIFO_TH_SHIFT
);
206 con
|= CON_USERDATA_23RDBIT
;
209 con
&= ~CON_PCM_MASK
;
210 switch (params_width(params
)) {
212 con
|= CON_PCM_16BIT
;
215 dev_err(spdif
->dev
, "Unsupported data size.\n");
219 ratio
= spdif
->clk_rate
/ params_rate(params
);
220 for (i
= 0; i
< ARRAY_SIZE(spdif_sysclk_ratios
); i
++)
221 if (ratio
== spdif_sysclk_ratios
[i
])
223 if (i
== ARRAY_SIZE(spdif_sysclk_ratios
)) {
224 dev_err(spdif
->dev
, "Invalid clock ratio %ld/%d\n",
225 spdif
->clk_rate
, params_rate(params
));
229 con
&= ~CON_MCLKDIV_MASK
;
232 con
|= CON_MCLKDIV_256FS
;
235 con
|= CON_MCLKDIV_384FS
;
238 con
|= CON_MCLKDIV_512FS
;
242 cstas
&= ~CSTAS_SAMP_FREQ_MASK
;
243 switch (params_rate(params
)) {
245 cstas
|= CSTAS_SAMP_FREQ_44
;
248 cstas
|= CSTAS_SAMP_FREQ_48
;
251 cstas
|= CSTAS_SAMP_FREQ_32
;
254 cstas
|= CSTAS_SAMP_FREQ_96
;
257 dev_err(spdif
->dev
, "Invalid sampling rate %d\n",
258 params_rate(params
));
262 cstas
&= ~CSTAS_CATEGORY_MASK
;
263 cstas
|= CSTAS_CATEGORY_CODE_CDP
;
264 cstas
|= CSTAS_NO_COPYRIGHT
;
266 writel(con
, regs
+ CON
);
267 writel(cstas
, regs
+ CSTAS
);
268 writel(clkcon
, regs
+ CLKCON
);
270 spin_unlock_irqrestore(&spdif
->lock
, flags
);
274 spin_unlock_irqrestore(&spdif
->lock
, flags
);
278 static void spdif_shutdown(struct snd_pcm_substream
*substream
,
279 struct snd_soc_dai
*dai
)
281 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
282 struct samsung_spdif_info
*spdif
= to_info(rtd
->cpu_dai
);
283 void __iomem
*regs
= spdif
->regs
;
286 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
288 con
= readl(regs
+ CON
) & CON_MASK
;
289 clkcon
= readl(regs
+ CLKCON
) & CLKCTL_MASK
;
291 writel(con
| CON_SW_RESET
, regs
+ CON
);
294 writel(clkcon
& ~CLKCTL_PWR_ON
, regs
+ CLKCON
);
298 static int spdif_suspend(struct snd_soc_dai
*cpu_dai
)
300 struct samsung_spdif_info
*spdif
= to_info(cpu_dai
);
301 u32 con
= spdif
->saved_con
;
303 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
305 spdif
->saved_clkcon
= readl(spdif
->regs
+ CLKCON
) & CLKCTL_MASK
;
306 spdif
->saved_con
= readl(spdif
->regs
+ CON
) & CON_MASK
;
307 spdif
->saved_cstas
= readl(spdif
->regs
+ CSTAS
) & CSTAS_MASK
;
309 writel(con
| CON_SW_RESET
, spdif
->regs
+ CON
);
315 static int spdif_resume(struct snd_soc_dai
*cpu_dai
)
317 struct samsung_spdif_info
*spdif
= to_info(cpu_dai
);
319 dev_dbg(spdif
->dev
, "Entered %s\n", __func__
);
321 writel(spdif
->saved_clkcon
, spdif
->regs
+ CLKCON
);
322 writel(spdif
->saved_con
, spdif
->regs
+ CON
);
323 writel(spdif
->saved_cstas
, spdif
->regs
+ CSTAS
);
328 #define spdif_suspend NULL
329 #define spdif_resume NULL
332 static const struct snd_soc_dai_ops spdif_dai_ops
= {
333 .set_sysclk
= spdif_set_sysclk
,
334 .trigger
= spdif_trigger
,
335 .hw_params
= spdif_hw_params
,
336 .shutdown
= spdif_shutdown
,
339 static struct snd_soc_dai_driver samsung_spdif_dai
= {
340 .name
= "samsung-spdif",
342 .stream_name
= "S/PDIF Playback",
345 .rates
= (SNDRV_PCM_RATE_32000
|
346 SNDRV_PCM_RATE_44100
|
347 SNDRV_PCM_RATE_48000
|
348 SNDRV_PCM_RATE_96000
),
349 .formats
= SNDRV_PCM_FMTBIT_S16_LE
, },
350 .ops
= &spdif_dai_ops
,
351 .suspend
= spdif_suspend
,
352 .resume
= spdif_resume
,
355 static const struct snd_soc_component_driver samsung_spdif_component
= {
356 .name
= "samsung-spdif",
359 static 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
= devm_clk_get(&pdev
->dev
, "spdif");
394 if (IS_ERR(spdif
->pclk
)) {
395 dev_err(&pdev
->dev
, "failed to get peri-clock\n");
399 clk_prepare_enable(spdif
->pclk
);
401 spdif
->sclk
= devm_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_prepare_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
= devm_snd_soc_register_component(&pdev
->dev
,
427 &samsung_spdif_component
, &samsung_spdif_dai
, 1);
429 dev_err(&pdev
->dev
, "fail to register dai\n");
433 spdif_stereo_out
.dma_size
= 2;
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
;
439 ret
= samsung_asoc_dma_platform_register(&pdev
->dev
);
441 dev_err(&pdev
->dev
, "failed to register DMA: %d\n", ret
);
447 iounmap(spdif
->regs
);
449 release_mem_region(mem_res
->start
, resource_size(mem_res
));
451 clk_disable_unprepare(spdif
->sclk
);
453 clk_disable_unprepare(spdif
->pclk
);
458 static int spdif_remove(struct platform_device
*pdev
)
460 struct samsung_spdif_info
*spdif
= &spdif_info
;
461 struct resource
*mem_res
;
463 iounmap(spdif
->regs
);
465 mem_res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
467 release_mem_region(mem_res
->start
, resource_size(mem_res
));
469 clk_disable_unprepare(spdif
->sclk
);
470 clk_disable_unprepare(spdif
->pclk
);
475 static struct platform_driver samsung_spdif_driver
= {
476 .probe
= spdif_probe
,
477 .remove
= spdif_remove
,
479 .name
= "samsung-spdif",
480 .owner
= THIS_MODULE
,
484 module_platform_driver(samsung_spdif_driver
);
486 MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
487 MODULE_DESCRIPTION("Samsung S/PDIF Controller Driver");
488 MODULE_LICENSE("GPL");
489 MODULE_ALIAS("platform:samsung-spdif");