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/soc-dapm.h>
22 #include <sound/s3c24xx_uda134x.h>
23 #include <sound/uda134x.h>
25 #include <plat/regs-iis.h>
27 #include "s3c24xx-pcm.h"
28 #include "s3c24xx-i2s.h"
29 #include "../codecs/uda134x.h"
32 /* #define ENFORCE_RATES 1 */
34 Unfortunately the S3C24XX in master mode has a limited capacity of
35 generating the clock for the codec. If you define this only rates
36 that are really available will be enforced. But be careful, most
37 user level application just want the usual sampling frequencies (8,
38 11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
39 operation for embedded systems. So if you aren't very lucky or your
40 hardware engineer wasn't very forward-looking it's better to leave
41 this undefined. If you do so an approximate value for the requested
42 sampling rate in the range -/+ 5% will be chosen. If this in not
43 possible an error will be returned.
46 static struct clk *xtal;
47 static struct clk *pclk;
48 /* this is need because we don't have a place where to keep the
49 * pointers to the clocks in each substream. We get the clocks only
50 * when we are actually using them so we don't block stuff like
51 * frequency change or oscillator power-off */
53 static DEFINE_MUTEX(clk_lock);
55 static unsigned int rates[33 * 2];
57 static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
58 .count = ARRAY_SIZE(rates),
64 static struct platform_device *s3c24xx_uda134x_snd_device;
66 static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
70 struct snd_pcm_runtime *runtime = substream->runtime;;
73 mutex_lock(&clk_lock);
74 pr_debug("%s %d\n", __func__, clk_users);
76 xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
78 printk(KERN_ERR "%s cannot get xtal\n", __func__);
81 pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
84 printk(KERN_ERR "%s cannot get pclk\n",
93 for (i = 0; i < 2; i++) {
94 int fs = i ? 256 : 384;
96 rates[i*33] = clk_get_rate(xtal) / fs;
97 for (j = 1; j < 33; j++)
98 rates[i*33 + j] = clk_get_rate(pclk) /
104 mutex_unlock(&clk_lock);
107 ret = snd_pcm_hw_constraint_list(runtime, 0,
108 SNDRV_PCM_HW_PARAM_RATE,
109 &hw_constraints_rates);
111 printk(KERN_ERR "%s cannot set constraints\n",
118 static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
120 mutex_lock(&clk_lock);
121 pr_debug("%s %d\n", __func__, clk_users);
123 if (clk_users == 0) {
129 mutex_unlock(&clk_lock);
132 static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
133 struct snd_pcm_hw_params *params)
135 struct snd_soc_pcm_runtime *rtd = substream->private_data;
136 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
137 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
138 unsigned int clk = 0;
140 int clk_source, fs_mode;
141 unsigned long rate = params_rate(params);
148 for (i = 0; i < 2*33; i++) {
149 cerr = rates[i] - rate;
158 fs_mode = S3C2410_IISMOD_256FS;
160 fs_mode = S3C2410_IISMOD_384FS;
162 clk_source = S3C24XX_CLKSRC_MPLL;
165 clk_source = S3C24XX_CLKSRC_PCLK;
168 pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
170 clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
171 pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
172 fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
173 clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
176 if ((err * 100 / rate) > 5) {
177 printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
178 "too different from desired (%ld%%)\n",
183 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
184 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
188 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
189 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
193 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
198 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
202 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
203 S3C2410_IISMOD_32FS);
207 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
208 S3C24XX_PRESCALE(div, div));
212 /* set the codec system clock for DAC and ADC */
213 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
221 static struct snd_soc_ops s3c24xx_uda134x_ops = {
222 .startup = s3c24xx_uda134x_startup,
223 .shutdown = s3c24xx_uda134x_shutdown,
224 .hw_params = s3c24xx_uda134x_hw_params,
227 static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
229 .stream_name = "UDA134X",
230 .codec_dai = &uda134x_dai,
231 .cpu_dai = &s3c24xx_i2s_dai,
232 .ops = &s3c24xx_uda134x_ops,
235 static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
236 .name = "S3C24XX_UDA134X",
237 .platform = &s3c24xx_soc_platform,
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 static struct uda134x_platform_data s3c24xx_uda134x = {
273 static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
274 .card = &snd_soc_s3c24xx_uda134x,
275 .codec_dev = &soc_codec_dev_uda134x,
276 .codec_data = &s3c24xx_uda134x,
279 static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
281 if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
282 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
283 "l3 %s pin already in use", fun);
286 gpio_direction_output(pin, 0);
290 static int s3c24xx_uda134x_probe(struct platform_device *pdev)
294 printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
296 s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
297 if (s3c24xx_uda134x_l3_pins == NULL) {
298 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
299 "unable to find platform data\n");
302 s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
303 s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
305 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
308 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
310 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
313 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
315 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
316 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
320 s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
321 if (!s3c24xx_uda134x_snd_device) {
322 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
323 "Unable to register\n");
327 platform_set_drvdata(s3c24xx_uda134x_snd_device,
328 &s3c24xx_uda134x_snd_devdata);
329 s3c24xx_uda134x_snd_devdata.dev = &s3c24xx_uda134x_snd_device->dev;
330 ret = platform_device_add(s3c24xx_uda134x_snd_device);
332 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
333 platform_device_put(s3c24xx_uda134x_snd_device);
339 static int s3c24xx_uda134x_remove(struct platform_device *pdev)
341 platform_device_unregister(s3c24xx_uda134x_snd_device);
342 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
343 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
344 gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
348 static struct platform_driver s3c24xx_uda134x_driver = {
349 .probe = s3c24xx_uda134x_probe,
350 .remove = s3c24xx_uda134x_remove,
352 .name = "s3c24xx_uda134x",
353 .owner = THIS_MODULE,
357 static int __init s3c24xx_uda134x_init(void)
359 return platform_driver_register(&s3c24xx_uda134x_driver);
362 static void __exit s3c24xx_uda134x_exit(void)
364 platform_driver_unregister(&s3c24xx_uda134x_driver);
368 module_init(s3c24xx_uda134x_init);
369 module_exit(s3c24xx_uda134x_exit);
371 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
372 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
373 MODULE_LICENSE("GPL");