2 * Modifications by Christian Pellegrin <chripell@evolware.org>
4 * s3c24xx_uda134x.c -- S3C24XX_UDA134X ALSA SoC Audio board driver
6 * Copyright 2007 Dension Audio Systems Ltd.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
14 #include <linux/clk.h>
15 #include <linux/gpio.h>
16 #include <linux/module.h>
18 #include <sound/soc.h>
19 #include <sound/s3c24xx_uda134x.h>
21 #include <plat/regs-iis.h>
23 #include "s3c24xx-i2s.h"
25 /* #define ENFORCE_RATES 1 */
27 Unfortunately the S3C24XX in master mode has a limited capacity of
28 generating the clock for the codec. If you define this only rates
29 that are really available will be enforced. But be careful, most
30 user level application just want the usual sampling frequencies (8,
31 11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
32 operation for embedded systems. So if you aren't very lucky or your
33 hardware engineer wasn't very forward-looking it's better to leave
34 this undefined. If you do so an approximate value for the requested
35 sampling rate in the range -/+ 5% will be chosen. If this in not
36 possible an error will be returned.
39 static struct clk
*xtal
;
40 static struct clk
*pclk
;
41 /* this is need because we don't have a place where to keep the
42 * pointers to the clocks in each substream. We get the clocks only
43 * when we are actually using them so we don't block stuff like
44 * frequency change or oscillator power-off */
46 static DEFINE_MUTEX(clk_lock
);
48 static unsigned int rates
[33 * 2];
50 static struct snd_pcm_hw_constraint_list hw_constraints_rates
= {
51 .count
= ARRAY_SIZE(rates
),
57 static struct platform_device
*s3c24xx_uda134x_snd_device
;
59 static int s3c24xx_uda134x_startup(struct snd_pcm_substream
*substream
)
63 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
66 mutex_lock(&clk_lock
);
67 pr_debug("%s %d\n", __func__
, clk_users
);
69 xtal
= clk_get(&s3c24xx_uda134x_snd_device
->dev
, "xtal");
71 printk(KERN_ERR
"%s cannot get xtal\n", __func__
);
74 pclk
= clk_get(&s3c24xx_uda134x_snd_device
->dev
,
77 printk(KERN_ERR
"%s cannot get pclk\n",
86 for (i
= 0; i
< 2; i
++) {
87 int fs
= i
? 256 : 384;
89 rates
[i
*33] = clk_get_rate(xtal
) / fs
;
90 for (j
= 1; j
< 33; j
++)
91 rates
[i
*33 + j
] = clk_get_rate(pclk
) /
97 mutex_unlock(&clk_lock
);
100 ret
= snd_pcm_hw_constraint_list(runtime
, 0,
101 SNDRV_PCM_HW_PARAM_RATE
,
102 &hw_constraints_rates
);
104 printk(KERN_ERR
"%s cannot set constraints\n",
111 static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream
*substream
)
113 mutex_lock(&clk_lock
);
114 pr_debug("%s %d\n", __func__
, clk_users
);
116 if (clk_users
== 0) {
122 mutex_unlock(&clk_lock
);
125 static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream
*substream
,
126 struct snd_pcm_hw_params
*params
)
128 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
129 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
130 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
131 unsigned int clk
= 0;
133 int clk_source
, fs_mode
;
134 unsigned long rate
= params_rate(params
);
141 for (i
= 0; i
< 2*33; i
++) {
142 cerr
= rates
[i
] - rate
;
151 fs_mode
= S3C2410_IISMOD_256FS
;
153 fs_mode
= S3C2410_IISMOD_384FS
;
155 clk_source
= S3C24XX_CLKSRC_MPLL
;
158 clk_source
= S3C24XX_CLKSRC_PCLK
;
161 pr_debug("%s desired rate %lu, %d\n", __func__
, rate
, bi
);
163 clk
= (fs_mode
== S3C2410_IISMOD_384FS
? 384 : 256) * rate
;
164 pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__
,
165 fs_mode
== S3C2410_IISMOD_384FS
? "384FS" : "256FS",
166 clk_source
== S3C24XX_CLKSRC_MPLL
? "MPLLin" : "PCLK",
169 if ((err
* 100 / rate
) > 5) {
170 printk(KERN_ERR
"S3C24XX_UDA134X: effective frequency "
171 "too different from desired (%ld%%)\n",
176 ret
= snd_soc_dai_set_fmt(codec_dai
, SND_SOC_DAIFMT_I2S
|
177 SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS
);
181 ret
= snd_soc_dai_set_fmt(cpu_dai
, SND_SOC_DAIFMT_I2S
|
182 SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS
);
186 ret
= snd_soc_dai_set_sysclk(cpu_dai
, clk_source
, clk
,
191 ret
= snd_soc_dai_set_clkdiv(cpu_dai
, S3C24XX_DIV_MCLK
, fs_mode
);
195 ret
= snd_soc_dai_set_clkdiv(cpu_dai
, S3C24XX_DIV_BCLK
,
196 S3C2410_IISMOD_32FS
);
200 ret
= snd_soc_dai_set_clkdiv(cpu_dai
, S3C24XX_DIV_PRESCALER
,
201 S3C24XX_PRESCALE(div
, div
));
205 /* set the codec system clock for DAC and ADC */
206 ret
= snd_soc_dai_set_sysclk(codec_dai
, 0, clk
,
214 static struct snd_soc_ops s3c24xx_uda134x_ops
= {
215 .startup
= s3c24xx_uda134x_startup
,
216 .shutdown
= s3c24xx_uda134x_shutdown
,
217 .hw_params
= s3c24xx_uda134x_hw_params
,
220 static struct snd_soc_dai_link s3c24xx_uda134x_dai_link
= {
222 .stream_name
= "UDA134X",
223 .codec_name
= "uda134x-codec",
224 .codec_dai_name
= "uda134x-hifi",
225 .cpu_dai_name
= "s3c24xx-iis",
226 .ops
= &s3c24xx_uda134x_ops
,
227 .platform_name
= "samsung-audio",
230 static struct snd_soc_card snd_soc_s3c24xx_uda134x
= {
231 .name
= "S3C24XX_UDA134X",
232 .dai_link
= &s3c24xx_uda134x_dai_link
,
236 static struct s3c24xx_uda134x_platform_data
*s3c24xx_uda134x_l3_pins
;
238 static void setdat(int v
)
240 gpio_set_value(s3c24xx_uda134x_l3_pins
->l3_data
, v
> 0);
243 static void setclk(int v
)
245 gpio_set_value(s3c24xx_uda134x_l3_pins
->l3_clk
, v
> 0);
248 static void setmode(int v
)
250 gpio_set_value(s3c24xx_uda134x_l3_pins
->l3_mode
, v
> 0);
253 /* FIXME - This must be codec platform data but in which board file ?? */
254 static struct uda134x_platform_data s3c24xx_uda134x
= {
268 static int s3c24xx_uda134x_setup_pin(int pin
, char *fun
)
270 if (gpio_request(pin
, "s3c24xx_uda134x") < 0) {
271 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: "
272 "l3 %s pin already in use", fun
);
275 gpio_direction_output(pin
, 0);
279 static int s3c24xx_uda134x_probe(struct platform_device
*pdev
)
283 printk(KERN_INFO
"S3C24XX_UDA134X SoC Audio driver\n");
285 s3c24xx_uda134x_l3_pins
= pdev
->dev
.platform_data
;
286 if (s3c24xx_uda134x_l3_pins
== NULL
) {
287 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: "
288 "unable to find platform data\n");
291 s3c24xx_uda134x
.power
= s3c24xx_uda134x_l3_pins
->power
;
292 s3c24xx_uda134x
.model
= s3c24xx_uda134x_l3_pins
->model
;
294 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins
->l3_data
,
297 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins
->l3_clk
,
299 gpio_free(s3c24xx_uda134x_l3_pins
->l3_data
);
302 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins
->l3_mode
,
304 gpio_free(s3c24xx_uda134x_l3_pins
->l3_data
);
305 gpio_free(s3c24xx_uda134x_l3_pins
->l3_clk
);
309 s3c24xx_uda134x_snd_device
= platform_device_alloc("soc-audio", -1);
310 if (!s3c24xx_uda134x_snd_device
) {
311 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: "
312 "Unable to register\n");
316 platform_set_drvdata(s3c24xx_uda134x_snd_device
,
317 &snd_soc_s3c24xx_uda134x
);
318 platform_device_add_data(s3c24xx_uda134x_snd_device
, &s3c24xx_uda134x
, sizeof(s3c24xx_uda134x
));
319 ret
= platform_device_add(s3c24xx_uda134x_snd_device
);
321 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: Unable to add\n");
322 platform_device_put(s3c24xx_uda134x_snd_device
);
328 static int s3c24xx_uda134x_remove(struct platform_device
*pdev
)
330 platform_device_unregister(s3c24xx_uda134x_snd_device
);
331 gpio_free(s3c24xx_uda134x_l3_pins
->l3_data
);
332 gpio_free(s3c24xx_uda134x_l3_pins
->l3_clk
);
333 gpio_free(s3c24xx_uda134x_l3_pins
->l3_mode
);
337 static struct platform_driver s3c24xx_uda134x_driver
= {
338 .probe
= s3c24xx_uda134x_probe
,
339 .remove
= s3c24xx_uda134x_remove
,
341 .name
= "s3c24xx_uda134x",
342 .owner
= THIS_MODULE
,
346 static int __init
s3c24xx_uda134x_init(void)
348 return platform_driver_register(&s3c24xx_uda134x_driver
);
351 static void __exit
s3c24xx_uda134x_exit(void)
353 platform_driver_unregister(&s3c24xx_uda134x_driver
);
357 module_init(s3c24xx_uda134x_init
);
358 module_exit(s3c24xx_uda134x_exit
);
360 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
361 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
362 MODULE_LICENSE("GPL");