]> git.karo-electronics.de Git - mv-sheeva.git/blob - sound/soc/s3c24xx/s3c24xx_uda134x.c
ASoC: multi-component - ASoC Multi-Component Support
[mv-sheeva.git] / sound / soc / s3c24xx / s3c24xx_uda134x.c
1 /*
2  * Modifications by Christian Pellegrin <chripell@evolware.org>
3  *
4  * s3c24xx_uda134x.c  --  S3C24XX_UDA134X ALSA SoC Audio board driver
5  *
6  * Copyright 2007 Dension Audio Systems Ltd.
7  * Author: Zoltan Devai
8  *
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.
12  */
13
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>
24
25 #include <plat/regs-iis.h>
26
27 #include "s3c-dma.h"
28 #include "s3c24xx-i2s.h"
29 #include "../codecs/uda134x.h"
30
31
32 /* #define ENFORCE_RATES 1 */
33 /*
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.
44 */
45
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 */
52 static int clk_users;
53 static DEFINE_MUTEX(clk_lock);
54
55 static unsigned int rates[33 * 2];
56 #ifdef ENFORCE_RATES
57 static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
58         .count  = ARRAY_SIZE(rates),
59         .list   = rates,
60         .mask   = 0,
61 };
62 #endif
63
64 static struct platform_device *s3c24xx_uda134x_snd_device;
65
66 static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
67 {
68         int ret = 0;
69 #ifdef ENFORCE_RATES
70         struct snd_pcm_runtime *runtime = substream->runtime;
71 #endif
72
73         mutex_lock(&clk_lock);
74         pr_debug("%s %d\n", __func__, clk_users);
75         if (clk_users == 0) {
76                 xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
77                 if (!xtal) {
78                         printk(KERN_ERR "%s cannot get xtal\n", __func__);
79                         ret = -EBUSY;
80                 } else {
81                         pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
82                                        "pclk");
83                         if (!pclk) {
84                                 printk(KERN_ERR "%s cannot get pclk\n",
85                                        __func__);
86                                 clk_put(xtal);
87                                 ret = -EBUSY;
88                         }
89                 }
90                 if (!ret) {
91                         int i, j;
92
93                         for (i = 0; i < 2; i++) {
94                                 int fs = i ? 256 : 384;
95
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) /
99                                                 (j * fs);
100                         }
101                 }
102         }
103         clk_users += 1;
104         mutex_unlock(&clk_lock);
105         if (!ret) {
106 #ifdef ENFORCE_RATES
107                 ret = snd_pcm_hw_constraint_list(runtime, 0,
108                                                  SNDRV_PCM_HW_PARAM_RATE,
109                                                  &hw_constraints_rates);
110                 if (ret < 0)
111                         printk(KERN_ERR "%s cannot set constraints\n",
112                                __func__);
113 #endif
114         }
115         return ret;
116 }
117
118 static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
119 {
120         mutex_lock(&clk_lock);
121         pr_debug("%s %d\n", __func__, clk_users);
122         clk_users -= 1;
123         if (clk_users == 0) {
124                 clk_put(xtal);
125                 xtal = NULL;
126                 clk_put(pclk);
127                 pclk = NULL;
128         }
129         mutex_unlock(&clk_lock);
130 }
131
132 static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
133                                         struct snd_pcm_hw_params *params)
134 {
135         struct snd_soc_pcm_runtime *rtd = substream->private_data;
136         struct snd_soc_dai *codec_dai = rtd->codec_dai;
137         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
138         unsigned int clk = 0;
139         int ret = 0;
140         int clk_source, fs_mode;
141         unsigned long rate = params_rate(params);
142         long err, cerr;
143         unsigned int div;
144         int i, bi;
145
146         err = 999999;
147         bi = 0;
148         for (i = 0; i < 2*33; i++) {
149                 cerr = rates[i] - rate;
150                 if (cerr < 0)
151                         cerr = -cerr;
152                 if (cerr < err) {
153                         err = cerr;
154                         bi = i;
155                 }
156         }
157         if (bi / 33 == 1)
158                 fs_mode = S3C2410_IISMOD_256FS;
159         else
160                 fs_mode = S3C2410_IISMOD_384FS;
161         if (bi % 33 == 0) {
162                 clk_source = S3C24XX_CLKSRC_MPLL;
163                 div = 1;
164         } else {
165                 clk_source = S3C24XX_CLKSRC_PCLK;
166                 div = bi % 33;
167         }
168         pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
169
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",
174                  div, clk, err);
175
176         if ((err * 100 / rate) > 5) {
177                 printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
178                        "too different from desired (%ld%%)\n",
179                        err * 100 / rate);
180                 return -EINVAL;
181         }
182
183         ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
184                         SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
185         if (ret < 0)
186                 return ret;
187
188         ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
189                         SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
190         if (ret < 0)
191                 return ret;
192
193         ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
194                         SND_SOC_CLOCK_IN);
195         if (ret < 0)
196                 return ret;
197
198         ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
199         if (ret < 0)
200                 return ret;
201
202         ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
203                         S3C2410_IISMOD_32FS);
204         if (ret < 0)
205                 return ret;
206
207         ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
208                         S3C24XX_PRESCALE(div, div));
209         if (ret < 0)
210                 return ret;
211
212         /* set the codec system clock for DAC and ADC */
213         ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
214                         SND_SOC_CLOCK_OUT);
215         if (ret < 0)
216                 return ret;
217
218         return 0;
219 }
220
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,
225 };
226
227 static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
228         .name = "UDA134X",
229         .stream_name = "UDA134X",
230         .codec_name = "uda134x-hifi",
231         .codec_dai_name = "uda134x-hifi",
232         .cpu_dai_name = "s3c24xx-i2s",
233         .ops = &s3c24xx_uda134x_ops,
234         .platform_name  = "s3c24xx-pcm-audio",
235 };
236
237 static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
238         .name = "S3C24XX_UDA134X",
239         .dai_link = &s3c24xx_uda134x_dai_link,
240         .num_links = 1,
241 };
242
243 static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
244
245 static void setdat(int v)
246 {
247         gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
248 }
249
250 static void setclk(int v)
251 {
252         gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
253 }
254
255 static void setmode(int v)
256 {
257         gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
258 }
259
260 /* FIXME - This must be codec platform data but in which board file ?? */
261 static struct uda134x_platform_data s3c24xx_uda134x = {
262         .l3 = {
263                 .setdat = setdat,
264                 .setclk = setclk,
265                 .setmode = setmode,
266                 .data_hold = 1,
267                 .data_setup = 1,
268                 .clock_high = 1,
269                 .mode_hold = 1,
270                 .mode = 1,
271                 .mode_setup = 1,
272         },
273 };
274
275 static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
276 {
277         if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
278                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
279                        "l3 %s pin already in use", fun);
280                 return -EBUSY;
281         }
282         gpio_direction_output(pin, 0);
283         return 0;
284 }
285
286 static int s3c24xx_uda134x_probe(struct platform_device *pdev)
287 {
288         int ret;
289
290         printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
291
292         s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
293         if (s3c24xx_uda134x_l3_pins == NULL) {
294                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
295                        "unable to find platform data\n");
296                 return -ENODEV;
297         }
298         s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
299         s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
300
301         if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
302                                       "data") < 0)
303                 return -EBUSY;
304         if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
305                                       "clk") < 0) {
306                 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
307                 return -EBUSY;
308         }
309         if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
310                                       "mode") < 0) {
311                 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
312                 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
313                 return -EBUSY;
314         }
315
316         s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
317         if (!s3c24xx_uda134x_snd_device) {
318                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
319                        "Unable to register\n");
320                 return -ENOMEM;
321         }
322
323         platform_set_drvdata(s3c24xx_uda134x_snd_device,
324                              &snd_soc_s3c24xx_uda134x);
325         ret = platform_device_add(s3c24xx_uda134x_snd_device);
326         if (ret) {
327                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
328                 platform_device_put(s3c24xx_uda134x_snd_device);
329         }
330
331         return ret;
332 }
333
334 static int s3c24xx_uda134x_remove(struct platform_device *pdev)
335 {
336         platform_device_unregister(s3c24xx_uda134x_snd_device);
337         gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
338         gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
339         gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
340         return 0;
341 }
342
343 static struct platform_driver s3c24xx_uda134x_driver = {
344         .probe  = s3c24xx_uda134x_probe,
345         .remove = s3c24xx_uda134x_remove,
346         .driver = {
347                 .name = "s3c24xx_uda134x",
348                 .owner = THIS_MODULE,
349         },
350 };
351
352 static int __init s3c24xx_uda134x_init(void)
353 {
354         return platform_driver_register(&s3c24xx_uda134x_driver);
355 }
356
357 static void __exit s3c24xx_uda134x_exit(void)
358 {
359         platform_driver_unregister(&s3c24xx_uda134x_driver);
360 }
361
362
363 module_init(s3c24xx_uda134x_init);
364 module_exit(s3c24xx_uda134x_exit);
365
366 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
367 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
368 MODULE_LICENSE("GPL");