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/module.h>
15 #include <linux/clk.h>
16 #include <linux/mutex.h>
17 #include <linux/gpio.h>
18 #include <sound/pcm.h>
19 #include <sound/pcm_params.h>
20 #include <sound/soc.h>
21 #include <sound/s3c24xx_uda134x.h>
22 #include <sound/uda134x.h>
24 #include <plat/regs-iis.h>
27 #include "s3c24xx-i2s.h"
28 #include "../codecs/uda134x.h"
31 /* #define ENFORCE_RATES 1 */
33 Unfortunately the S3C24XX in master mode has a limited capacity of
34 generating the clock for the codec. If you define this only rates
35 that are really available will be enforced. But be careful, most
36 user level application just want the usual sampling frequencies (8,
37 11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
38 operation for embedded systems. So if you aren't very lucky or your
39 hardware engineer wasn't very forward-looking it's better to leave
40 this undefined. If you do so an approximate value for the requested
41 sampling rate in the range -/+ 5% will be chosen. If this in not
42 possible an error will be returned.
45 static struct clk
*xtal
;
46 static struct clk
*pclk
;
47 /* this is need because we don't have a place where to keep the
48 * pointers to the clocks in each substream. We get the clocks only
49 * when we are actually using them so we don't block stuff like
50 * frequency change or oscillator power-off */
52 static DEFINE_MUTEX(clk_lock
);
54 static unsigned int rates
[33 * 2];
56 static struct snd_pcm_hw_constraint_list hw_constraints_rates
= {
57 .count
= ARRAY_SIZE(rates
),
63 static struct platform_device
*s3c24xx_uda134x_snd_device
;
65 static int s3c24xx_uda134x_startup(struct snd_pcm_substream
*substream
)
69 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
72 mutex_lock(&clk_lock
);
73 pr_debug("%s %d\n", __func__
, clk_users
);
75 xtal
= clk_get(&s3c24xx_uda134x_snd_device
->dev
, "xtal");
77 printk(KERN_ERR
"%s cannot get xtal\n", __func__
);
80 pclk
= clk_get(&s3c24xx_uda134x_snd_device
->dev
,
83 printk(KERN_ERR
"%s cannot get pclk\n",
92 for (i
= 0; i
< 2; i
++) {
93 int fs
= i
? 256 : 384;
95 rates
[i
*33] = clk_get_rate(xtal
) / fs
;
96 for (j
= 1; j
< 33; j
++)
97 rates
[i
*33 + j
] = clk_get_rate(pclk
) /
103 mutex_unlock(&clk_lock
);
106 ret
= snd_pcm_hw_constraint_list(runtime
, 0,
107 SNDRV_PCM_HW_PARAM_RATE
,
108 &hw_constraints_rates
);
110 printk(KERN_ERR
"%s cannot set constraints\n",
117 static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream
*substream
)
119 mutex_lock(&clk_lock
);
120 pr_debug("%s %d\n", __func__
, clk_users
);
122 if (clk_users
== 0) {
128 mutex_unlock(&clk_lock
);
131 static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream
*substream
,
132 struct snd_pcm_hw_params
*params
)
134 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
135 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
136 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
137 unsigned int clk
= 0;
139 int clk_source
, fs_mode
;
140 unsigned long rate
= params_rate(params
);
147 for (i
= 0; i
< 2*33; i
++) {
148 cerr
= rates
[i
] - rate
;
157 fs_mode
= S3C2410_IISMOD_256FS
;
159 fs_mode
= S3C2410_IISMOD_384FS
;
161 clk_source
= S3C24XX_CLKSRC_MPLL
;
164 clk_source
= S3C24XX_CLKSRC_PCLK
;
167 pr_debug("%s desired rate %lu, %d\n", __func__
, rate
, bi
);
169 clk
= (fs_mode
== S3C2410_IISMOD_384FS
? 384 : 256) * rate
;
170 pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__
,
171 fs_mode
== S3C2410_IISMOD_384FS
? "384FS" : "256FS",
172 clk_source
== S3C24XX_CLKSRC_MPLL
? "MPLLin" : "PCLK",
175 if ((err
* 100 / rate
) > 5) {
176 printk(KERN_ERR
"S3C24XX_UDA134X: effective frequency "
177 "too different from desired (%ld%%)\n",
182 ret
= snd_soc_dai_set_fmt(codec_dai
, SND_SOC_DAIFMT_I2S
|
183 SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS
);
187 ret
= snd_soc_dai_set_fmt(cpu_dai
, SND_SOC_DAIFMT_I2S
|
188 SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS
);
192 ret
= snd_soc_dai_set_sysclk(cpu_dai
, clk_source
, clk
,
197 ret
= snd_soc_dai_set_clkdiv(cpu_dai
, S3C24XX_DIV_MCLK
, fs_mode
);
201 ret
= snd_soc_dai_set_clkdiv(cpu_dai
, S3C24XX_DIV_BCLK
,
202 S3C2410_IISMOD_32FS
);
206 ret
= snd_soc_dai_set_clkdiv(cpu_dai
, S3C24XX_DIV_PRESCALER
,
207 S3C24XX_PRESCALE(div
, div
));
211 /* set the codec system clock for DAC and ADC */
212 ret
= snd_soc_dai_set_sysclk(codec_dai
, 0, clk
,
220 static struct snd_soc_ops s3c24xx_uda134x_ops
= {
221 .startup
= s3c24xx_uda134x_startup
,
222 .shutdown
= s3c24xx_uda134x_shutdown
,
223 .hw_params
= s3c24xx_uda134x_hw_params
,
226 static struct snd_soc_dai_link s3c24xx_uda134x_dai_link
= {
228 .stream_name
= "UDA134X",
229 .codec_name
= "uda134x-hifi",
230 .codec_dai_name
= "uda134x-hifi",
231 .cpu_dai_name
= "s3c24xx-iis",
232 .ops
= &s3c24xx_uda134x_ops
,
233 .platform_name
= "samsung-audio",
236 static struct snd_soc_card snd_soc_s3c24xx_uda134x
= {
237 .name
= "S3C24XX_UDA134X",
238 .dai_link
= &s3c24xx_uda134x_dai_link
,
242 static struct s3c24xx_uda134x_platform_data
*s3c24xx_uda134x_l3_pins
;
244 static void setdat(int v
)
246 gpio_set_value(s3c24xx_uda134x_l3_pins
->l3_data
, v
> 0);
249 static void setclk(int v
)
251 gpio_set_value(s3c24xx_uda134x_l3_pins
->l3_clk
, v
> 0);
254 static void setmode(int v
)
256 gpio_set_value(s3c24xx_uda134x_l3_pins
->l3_mode
, v
> 0);
259 /* FIXME - This must be codec platform data but in which board file ?? */
260 static struct uda134x_platform_data s3c24xx_uda134x
= {
274 static int s3c24xx_uda134x_setup_pin(int pin
, char *fun
)
276 if (gpio_request(pin
, "s3c24xx_uda134x") < 0) {
277 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: "
278 "l3 %s pin already in use", fun
);
281 gpio_direction_output(pin
, 0);
285 static int s3c24xx_uda134x_probe(struct platform_device
*pdev
)
289 printk(KERN_INFO
"S3C24XX_UDA134X SoC Audio driver\n");
291 s3c24xx_uda134x_l3_pins
= pdev
->dev
.platform_data
;
292 if (s3c24xx_uda134x_l3_pins
== NULL
) {
293 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: "
294 "unable to find platform data\n");
297 s3c24xx_uda134x
.power
= s3c24xx_uda134x_l3_pins
->power
;
298 s3c24xx_uda134x
.model
= s3c24xx_uda134x_l3_pins
->model
;
300 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins
->l3_data
,
303 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins
->l3_clk
,
305 gpio_free(s3c24xx_uda134x_l3_pins
->l3_data
);
308 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins
->l3_mode
,
310 gpio_free(s3c24xx_uda134x_l3_pins
->l3_data
);
311 gpio_free(s3c24xx_uda134x_l3_pins
->l3_clk
);
315 s3c24xx_uda134x_snd_device
= platform_device_alloc("soc-audio", -1);
316 if (!s3c24xx_uda134x_snd_device
) {
317 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: "
318 "Unable to register\n");
322 platform_set_drvdata(s3c24xx_uda134x_snd_device
,
323 &snd_soc_s3c24xx_uda134x
);
324 ret
= platform_device_add(s3c24xx_uda134x_snd_device
);
326 printk(KERN_ERR
"S3C24XX_UDA134X SoC Audio: Unable to add\n");
327 platform_device_put(s3c24xx_uda134x_snd_device
);
333 static int s3c24xx_uda134x_remove(struct platform_device
*pdev
)
335 platform_device_unregister(s3c24xx_uda134x_snd_device
);
336 gpio_free(s3c24xx_uda134x_l3_pins
->l3_data
);
337 gpio_free(s3c24xx_uda134x_l3_pins
->l3_clk
);
338 gpio_free(s3c24xx_uda134x_l3_pins
->l3_mode
);
342 static struct platform_driver s3c24xx_uda134x_driver
= {
343 .probe
= s3c24xx_uda134x_probe
,
344 .remove
= s3c24xx_uda134x_remove
,
346 .name
= "s3c24xx_uda134x",
347 .owner
= THIS_MODULE
,
351 static int __init
s3c24xx_uda134x_init(void)
353 return platform_driver_register(&s3c24xx_uda134x_driver
);
356 static void __exit
s3c24xx_uda134x_exit(void)
358 platform_driver_unregister(&s3c24xx_uda134x_driver
);
362 module_init(s3c24xx_uda134x_init
);
363 module_exit(s3c24xx_uda134x_exit
);
365 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
366 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
367 MODULE_LICENSE("GPL");