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>
17 #include <sound/soc.h>
18 #include <sound/s3c24xx_uda134x.h>
20 #include <plat/regs-iis.h>
22 #include "s3c24xx-i2s.h"
24 /* #define ENFORCE_RATES 1 */
26 Unfortunately the S3C24XX in master mode has a limited capacity of
27 generating the clock for the codec. If you define this only rates
28 that are really available will be enforced. But be careful, most
29 user level application just want the usual sampling frequencies (8,
30 11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
31 operation for embedded systems. So if you aren't very lucky or your
32 hardware engineer wasn't very forward-looking it's better to leave
33 this undefined. If you do so an approximate value for the requested
34 sampling rate in the range -/+ 5% will be chosen. If this in not
35 possible an error will be returned.
38 static struct clk
*xtal
;
39 static struct clk
*pclk
;
40 /* this is need because we don't have a place where to keep the
41 * pointers to the clocks in each substream. We get the clocks only
42 * when we are actually using them so we don't block stuff like
43 * frequency change or oscillator power-off */
45 static DEFINE_MUTEX(clk_lock
);
47 static unsigned int rates
[33 * 2];
49 static struct snd_pcm_hw_constraint_list hw_constraints_rates
= {
50 .count
= ARRAY_SIZE(rates
),
56 static struct platform_device
*s3c24xx_uda134x_snd_device
;
58 static int s3c24xx_uda134x_startup(struct snd_pcm_substream
*substream
)
62 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
65 mutex_lock(&clk_lock
);
66 pr_debug("%s %d\n", __func__
, clk_users
);
68 xtal
= clk_get(&s3c24xx_uda134x_snd_device
->dev
, "xtal");
70 printk(KERN_ERR
"%s cannot get xtal\n", __func__
);
73 pclk
= clk_get(&s3c24xx_uda134x_snd_device
->dev
,
76 printk(KERN_ERR
"%s cannot get pclk\n",
85 for (i
= 0; i
< 2; i
++) {
86 int fs
= i
? 256 : 384;
88 rates
[i
*33] = clk_get_rate(xtal
) / fs
;
89 for (j
= 1; j
< 33; j
++)
90 rates
[i
*33 + j
] = clk_get_rate(pclk
) /
96 mutex_unlock(&clk_lock
);
99 ret
= snd_pcm_hw_constraint_list(runtime
, 0,
100 SNDRV_PCM_HW_PARAM_RATE
,
101 &hw_constraints_rates
);
103 printk(KERN_ERR
"%s cannot set constraints\n",
110 static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream
*substream
)
112 mutex_lock(&clk_lock
);
113 pr_debug("%s %d\n", __func__
, clk_users
);
115 if (clk_users
== 0) {
121 mutex_unlock(&clk_lock
);
124 static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream
*substream
,
125 struct snd_pcm_hw_params
*params
)
127 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
128 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
129 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
130 unsigned int clk
= 0;
132 int clk_source
, fs_mode
;
133 unsigned long rate
= params_rate(params
);
140 for (i
= 0; i
< 2*33; i
++) {
141 cerr
= rates
[i
] - rate
;
150 fs_mode
= S3C2410_IISMOD_256FS
;
152 fs_mode
= S3C2410_IISMOD_384FS
;
154 clk_source
= S3C24XX_CLKSRC_MPLL
;
157 clk_source
= S3C24XX_CLKSRC_PCLK
;
160 pr_debug("%s desired rate %lu, %d\n", __func__
, rate
, bi
);
162 clk
= (fs_mode
== S3C2410_IISMOD_384FS
? 384 : 256) * rate
;
163 pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__
,
164 fs_mode
== S3C2410_IISMOD_384FS
? "384FS" : "256FS",
165 clk_source
== S3C24XX_CLKSRC_MPLL
? "MPLLin" : "PCLK",
168 if ((err
* 100 / rate
) > 5) {
169 printk(KERN_ERR
"S3C24XX_UDA134X: effective frequency "
170 "too different from desired (%ld%%)\n",
175 ret
= snd_soc_dai_set_fmt(codec_dai
, SND_SOC_DAIFMT_I2S
|
176 SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS
);
180 ret
= snd_soc_dai_set_fmt(cpu_dai
, SND_SOC_DAIFMT_I2S
|
181 SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS
);
185 ret
= snd_soc_dai_set_sysclk(cpu_dai
, clk_source
, clk
,
190 ret
= snd_soc_dai_set_clkdiv(cpu_dai
, S3C24XX_DIV_MCLK
, fs_mode
);
194 ret
= snd_soc_dai_set_clkdiv(cpu_dai
, S3C24XX_DIV_BCLK
,
195 S3C2410_IISMOD_32FS
);
199 ret
= snd_soc_dai_set_clkdiv(cpu_dai
, S3C24XX_DIV_PRESCALER
,
200 S3C24XX_PRESCALE(div
, div
));
204 /* set the codec system clock for DAC and ADC */
205 ret
= snd_soc_dai_set_sysclk(codec_dai
, 0, clk
,
213 static struct snd_soc_ops s3c24xx_uda134x_ops
= {
214 .startup
= s3c24xx_uda134x_startup
,
215 .shutdown
= s3c24xx_uda134x_shutdown
,
216 .hw_params
= s3c24xx_uda134x_hw_params
,
219 static struct snd_soc_dai_link s3c24xx_uda134x_dai_link
= {
221 .stream_name
= "UDA134X",
222 .codec_name
= "uda134x-codec",
223 .codec_dai_name
= "uda134x-hifi",
224 .cpu_dai_name
= "s3c24xx-iis",
225 .ops
= &s3c24xx_uda134x_ops
,
226 .platform_name
= "samsung-audio",
229 static struct snd_soc_card snd_soc_s3c24xx_uda134x
= {
230 .name
= "S3C24XX_UDA134X",
231 .dai_link
= &s3c24xx_uda134x_dai_link
,
235 static struct s3c24xx_uda134x_platform_data
*s3c24xx_uda134x_l3_pins
;
237 static void setdat(int v
)
239 gpio_set_value(s3c24xx_uda134x_l3_pins
->l3_data
, v
> 0);
242 static void setclk(int v
)
244 gpio_set_value(s3c24xx_uda134x_l3_pins
->l3_clk
, v
> 0);
247 static void setmode(int v
)
249 gpio_set_value(s3c24xx_uda134x_l3_pins
->l3_mode
, v
> 0);
252 /* FIXME - This must be codec platform data but in which board file ?? */
253 static struct uda134x_platform_data s3c24xx_uda134x
= {
267 static int s3c24xx_uda134x_setup_pin(int pin
, char *fun
)
269 if (gpio_request(pin
, "s3c24xx_uda134x") < 0) {
270 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: "
271 "l3 %s pin already in use", fun
);
274 gpio_direction_output(pin
, 0);
278 static int s3c24xx_uda134x_probe(struct platform_device
*pdev
)
282 printk(KERN_INFO
"S3C24XX_UDA134X SoC Audio driver\n");
284 s3c24xx_uda134x_l3_pins
= pdev
->dev
.platform_data
;
285 if (s3c24xx_uda134x_l3_pins
== NULL
) {
286 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: "
287 "unable to find platform data\n");
290 s3c24xx_uda134x
.power
= s3c24xx_uda134x_l3_pins
->power
;
291 s3c24xx_uda134x
.model
= s3c24xx_uda134x_l3_pins
->model
;
293 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins
->l3_data
,
296 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins
->l3_clk
,
298 gpio_free(s3c24xx_uda134x_l3_pins
->l3_data
);
301 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins
->l3_mode
,
303 gpio_free(s3c24xx_uda134x_l3_pins
->l3_data
);
304 gpio_free(s3c24xx_uda134x_l3_pins
->l3_clk
);
308 s3c24xx_uda134x_snd_device
= platform_device_alloc("soc-audio", -1);
309 if (!s3c24xx_uda134x_snd_device
) {
310 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: "
311 "Unable to register\n");
315 platform_set_drvdata(s3c24xx_uda134x_snd_device
,
316 &snd_soc_s3c24xx_uda134x
);
317 platform_device_add_data(s3c24xx_uda134x_snd_device
, &s3c24xx_uda134x
, sizeof(s3c24xx_uda134x
));
318 ret
= platform_device_add(s3c24xx_uda134x_snd_device
);
320 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: Unable to add\n");
321 platform_device_put(s3c24xx_uda134x_snd_device
);
327 static int s3c24xx_uda134x_remove(struct platform_device
*pdev
)
329 platform_device_unregister(s3c24xx_uda134x_snd_device
);
330 gpio_free(s3c24xx_uda134x_l3_pins
->l3_data
);
331 gpio_free(s3c24xx_uda134x_l3_pins
->l3_clk
);
332 gpio_free(s3c24xx_uda134x_l3_pins
->l3_mode
);
336 static struct platform_driver s3c24xx_uda134x_driver
= {
337 .probe
= s3c24xx_uda134x_probe
,
338 .remove
= s3c24xx_uda134x_remove
,
340 .name
= "s3c24xx_uda134x",
341 .owner
= THIS_MODULE
,
345 static int __init
s3c24xx_uda134x_init(void)
347 return platform_driver_register(&s3c24xx_uda134x_driver
);
350 static void __exit
s3c24xx_uda134x_exit(void)
352 platform_driver_unregister(&s3c24xx_uda134x_driver
);
356 module_init(s3c24xx_uda134x_init
);
357 module_exit(s3c24xx_uda134x_exit
);
359 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
360 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
361 MODULE_LICENSE("GPL");