]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - sound/soc/s3c24xx/s3c24xx_uda134x.c
487b010730b8df4942d734b14ff6f44c6d116669
[linux-2.6-omap-h63xx.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 <asm/plat-s3c24xx/regs-iis.h>
26
27 #include "s3c24xx-pcm.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 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 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->dai->codec_dai;
137         struct snd_soc_dai *cpu_dai = rtd->dai->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 = codec_dai->dai_ops.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 = cpu_dai->dai_ops.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 = cpu_dai->dai_ops.set_sysclk(cpu_dai, clk_source , clk,
194                                                 SND_SOC_CLOCK_IN);
195         if (ret < 0)
196                 return ret;
197
198         ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
199                                                 fs_mode);
200         if (ret < 0)
201                 return ret;
202
203         ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
204                                                 S3C2410_IISMOD_32FS);
205         if (ret < 0)
206                 return ret;
207
208         ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
209                                           S3C24XX_PRESCALE(div, div));
210         if (ret < 0)
211                 return ret;
212
213         /* set the codec system clock for DAC and ADC */
214         ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0, clk,
215                                                 SND_SOC_CLOCK_OUT);
216         if (ret < 0)
217                 return ret;
218
219         return 0;
220 }
221
222 static struct snd_soc_ops s3c24xx_uda134x_ops = {
223         .startup = s3c24xx_uda134x_startup,
224         .shutdown = s3c24xx_uda134x_shutdown,
225         .hw_params = s3c24xx_uda134x_hw_params,
226 };
227
228 static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
229         .name = "UDA134X",
230         .stream_name = "UDA134X",
231         .codec_dai = &uda134x_dai,
232         .cpu_dai = &s3c24xx_i2s_dai,
233         .ops = &s3c24xx_uda134x_ops,
234 };
235
236 static struct snd_soc_machine snd_soc_machine_s3c24xx_uda134x = {
237         .name = "S3C24XX_UDA134X",
238         .dai_link = &s3c24xx_uda134x_dai_link,
239         .num_links = 1,
240 };
241
242 static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
243
244 static void setdat(int v)
245 {
246         gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
247 }
248
249 static void setclk(int v)
250 {
251         gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
252 }
253
254 static void setmode(int v)
255 {
256         gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
257 }
258
259 static struct uda134x_platform_data s3c24xx_uda134x = {
260         .l3 = {
261                 .setdat = setdat,
262                 .setclk = setclk,
263                 .setmode = setmode,
264                 .data_hold = 1,
265                 .data_setup = 1,
266                 .clock_high = 1,
267                 .mode_hold = 1,
268                 .mode = 1,
269                 .mode_setup = 1,
270         },
271 };
272
273 static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
274         .machine = &snd_soc_machine_s3c24xx_uda134x,
275         .platform = &s3c24xx_soc_platform,
276         .codec_dev = &soc_codec_dev_uda134x,
277         .codec_data = &s3c24xx_uda134x,
278 };
279
280 static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
281 {
282         if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
283                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
284                        "l3 %s pin already in use", fun);
285                 return -EBUSY;
286         }
287         gpio_direction_output(pin, 0);
288         return 0;
289 }
290
291 static int s3c24xx_uda134x_probe(struct platform_device *pdev)
292 {
293         int ret;
294
295         printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
296
297         s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
298         if (s3c24xx_uda134x_l3_pins == NULL) {
299                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
300                        "unable to find platform data\n");
301                 return -ENODEV;
302         }
303         s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
304         s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
305
306         if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
307                                       "data") < 0)
308                 return -EBUSY;
309         if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
310                                       "clk") < 0) {
311                 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
312                 return -EBUSY;
313         }
314         if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
315                                       "mode") < 0) {
316                 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
317                 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
318                 return -EBUSY;
319         }
320
321         s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
322         if (!s3c24xx_uda134x_snd_device) {
323                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
324                        "Unable to register\n");
325                 return -ENOMEM;
326         }
327
328         platform_set_drvdata(s3c24xx_uda134x_snd_device,
329                              &s3c24xx_uda134x_snd_devdata);
330         s3c24xx_uda134x_snd_devdata.dev = &s3c24xx_uda134x_snd_device->dev;
331         ret = platform_device_add(s3c24xx_uda134x_snd_device);
332         if (ret) {
333                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
334                 platform_device_put(s3c24xx_uda134x_snd_device);
335         }
336
337         return ret;
338 }
339
340 static int s3c24xx_uda134x_remove(struct platform_device *pdev)
341 {
342         platform_device_unregister(s3c24xx_uda134x_snd_device);
343         gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
344         gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
345         gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
346         return 0;
347 }
348
349 static struct platform_driver s3c24xx_uda134x_driver = {
350         .probe  = s3c24xx_uda134x_probe,
351         .remove = s3c24xx_uda134x_remove,
352         .driver = {
353                 .name = "s3c24xx_uda134x",
354                 .owner = THIS_MODULE,
355         },
356 };
357
358 static int __init s3c24xx_uda134x_init(void)
359 {
360         return platform_driver_register(&s3c24xx_uda134x_driver);
361 }
362
363 static void __exit s3c24xx_uda134x_exit(void)
364 {
365         platform_driver_unregister(&s3c24xx_uda134x_driver);
366 }
367
368
369 module_init(s3c24xx_uda134x_init);
370 module_exit(s3c24xx_uda134x_exit);
371
372 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
373 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
374 MODULE_LICENSE("GPL");