Version:  2.0.40 2.2.26 2.4.37 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8

Linux/sound/soc/samsung/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/clk.h>
 15 #include <linux/gpio.h>
 16 #include <linux/module.h>
 17 
 18 #include <sound/soc.h>
 19 #include <sound/s3c24xx_uda134x.h>
 20 
 21 #include "regs-iis.h"
 22 
 23 #include "s3c24xx-i2s.h"
 24 
 25 /* #define ENFORCE_RATES 1 */
 26 /*
 27   Unfortunately the S3C24XX in master mode has a limited capacity of
 28   generating the clock for the codec. If you define this only rates
 29   that are really available will be enforced. But be careful, most
 30   user level application just want the usual sampling frequencies (8,
 31   11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
 32   operation for embedded systems. So if you aren't very lucky or your
 33   hardware engineer wasn't very forward-looking it's better to leave
 34   this undefined. If you do so an approximate value for the requested
 35   sampling rate in the range -/+ 5% will be chosen. If this in not
 36   possible an error will be returned.
 37 */
 38 
 39 static struct clk *xtal;
 40 static struct clk *pclk;
 41 /* this is need because we don't have a place where to keep the
 42  * pointers to the clocks in each substream. We get the clocks only
 43  * when we are actually using them so we don't block stuff like
 44  * frequency change or oscillator power-off */
 45 static int clk_users;
 46 static DEFINE_MUTEX(clk_lock);
 47 
 48 static unsigned int rates[33 * 2];
 49 #ifdef ENFORCE_RATES
 50 static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
 51         .count  = ARRAY_SIZE(rates),
 52         .list   = rates,
 53         .mask   = 0,
 54 };
 55 #endif
 56 
 57 static struct platform_device *s3c24xx_uda134x_snd_device;
 58 
 59 static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
 60 {
 61         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 62         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 63 #ifdef ENFORCE_RATES
 64         struct snd_pcm_runtime *runtime = substream->runtime;
 65 #endif
 66         int ret = 0;
 67 
 68         mutex_lock(&clk_lock);
 69         pr_debug("%s %d\n", __func__, clk_users);
 70         if (clk_users == 0) {
 71                 xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
 72                 if (IS_ERR(xtal)) {
 73                         printk(KERN_ERR "%s cannot get xtal\n", __func__);
 74                         ret = PTR_ERR(xtal);
 75                 } else {
 76                         pclk = clk_get(cpu_dai->dev, "iis");
 77                         if (IS_ERR(pclk)) {
 78                                 printk(KERN_ERR "%s cannot get pclk\n",
 79                                        __func__);
 80                                 clk_put(xtal);
 81                                 ret = PTR_ERR(pclk);
 82                         }
 83                 }
 84                 if (!ret) {
 85                         int i, j;
 86 
 87                         for (i = 0; i < 2; i++) {
 88                                 int fs = i ? 256 : 384;
 89 
 90                                 rates[i*33] = clk_get_rate(xtal) / fs;
 91                                 for (j = 1; j < 33; j++)
 92                                         rates[i*33 + j] = clk_get_rate(pclk) /
 93                                                 (j * fs);
 94                         }
 95                 }
 96         }
 97         clk_users += 1;
 98         mutex_unlock(&clk_lock);
 99         if (!ret) {
100 #ifdef ENFORCE_RATES
101                 ret = snd_pcm_hw_constraint_list(runtime, 0,
102                                                  SNDRV_PCM_HW_PARAM_RATE,
103                                                  &hw_constraints_rates);
104                 if (ret < 0)
105                         printk(KERN_ERR "%s cannot set constraints\n",
106                                __func__);
107 #endif
108         }
109         return ret;
110 }
111 
112 static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
113 {
114         mutex_lock(&clk_lock);
115         pr_debug("%s %d\n", __func__, clk_users);
116         clk_users -= 1;
117         if (clk_users == 0) {
118                 clk_put(xtal);
119                 xtal = NULL;
120                 clk_put(pclk);
121                 pclk = NULL;
122         }
123         mutex_unlock(&clk_lock);
124 }
125 
126 static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
127                                         struct snd_pcm_hw_params *params)
128 {
129         struct snd_soc_pcm_runtime *rtd = substream->private_data;
130         struct snd_soc_dai *codec_dai = rtd->codec_dai;
131         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
132         unsigned int clk = 0;
133         int ret = 0;
134         int clk_source, fs_mode;
135         unsigned long rate = params_rate(params);
136         long err, cerr;
137         unsigned int div;
138         int i, bi;
139 
140         err = 999999;
141         bi = 0;
142         for (i = 0; i < 2*33; i++) {
143                 cerr = rates[i] - rate;
144                 if (cerr < 0)
145                         cerr = -cerr;
146                 if (cerr < err) {
147                         err = cerr;
148                         bi = i;
149                 }
150         }
151         if (bi / 33 == 1)
152                 fs_mode = S3C2410_IISMOD_256FS;
153         else
154                 fs_mode = S3C2410_IISMOD_384FS;
155         if (bi % 33 == 0) {
156                 clk_source = S3C24XX_CLKSRC_MPLL;
157                 div = 1;
158         } else {
159                 clk_source = S3C24XX_CLKSRC_PCLK;
160                 div = bi % 33;
161         }
162         pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
163 
164         clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
165         pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
166                  fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
167                  clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
168                  div, clk, err);
169 
170         if ((err * 100 / rate) > 5) {
171                 printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
172                        "too different from desired (%ld%%)\n",
173                        err * 100 / rate);
174                 return -EINVAL;
175         }
176 
177         ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
178                         SND_SOC_CLOCK_IN);
179         if (ret < 0)
180                 return ret;
181 
182         ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
183         if (ret < 0)
184                 return ret;
185 
186         ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
187                         S3C2410_IISMOD_32FS);
188         if (ret < 0)
189                 return ret;
190 
191         ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
192                         S3C24XX_PRESCALE(div, div));
193         if (ret < 0)
194                 return ret;
195 
196         /* set the codec system clock for DAC and ADC */
197         ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
198                         SND_SOC_CLOCK_OUT);
199         if (ret < 0)
200                 return ret;
201 
202         return 0;
203 }
204 
205 static struct snd_soc_ops s3c24xx_uda134x_ops = {
206         .startup = s3c24xx_uda134x_startup,
207         .shutdown = s3c24xx_uda134x_shutdown,
208         .hw_params = s3c24xx_uda134x_hw_params,
209 };
210 
211 static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
212         .name = "UDA134X",
213         .stream_name = "UDA134X",
214         .codec_name = "uda134x-codec",
215         .codec_dai_name = "uda134x-hifi",
216         .cpu_dai_name = "s3c24xx-iis",
217         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
218                    SND_SOC_DAIFMT_CBS_CFS,
219         .ops = &s3c24xx_uda134x_ops,
220         .platform_name  = "s3c24xx-iis",
221 };
222 
223 static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
224         .name = "S3C24XX_UDA134X",
225         .owner = THIS_MODULE,
226         .dai_link = &s3c24xx_uda134x_dai_link,
227         .num_links = 1,
228 };
229 
230 static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
231 
232 static void setdat(int v)
233 {
234         gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
235 }
236 
237 static void setclk(int v)
238 {
239         gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
240 }
241 
242 static void setmode(int v)
243 {
244         gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
245 }
246 
247 /* FIXME - This must be codec platform data but in which board file ?? */
248 static struct uda134x_platform_data s3c24xx_uda134x = {
249         .l3 = {
250                 .setdat = setdat,
251                 .setclk = setclk,
252                 .setmode = setmode,
253                 .data_hold = 1,
254                 .data_setup = 1,
255                 .clock_high = 1,
256                 .mode_hold = 1,
257                 .mode = 1,
258                 .mode_setup = 1,
259         },
260 };
261 
262 static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
263 {
264         if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
265                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
266                        "l3 %s pin already in use", fun);
267                 return -EBUSY;
268         }
269         gpio_direction_output(pin, 0);
270         return 0;
271 }
272 
273 static int s3c24xx_uda134x_probe(struct platform_device *pdev)
274 {
275         int ret;
276 
277         printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
278 
279         s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
280         if (s3c24xx_uda134x_l3_pins == NULL) {
281                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
282                        "unable to find platform data\n");
283                 return -ENODEV;
284         }
285         s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
286         s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
287 
288         if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
289                                       "data") < 0)
290                 return -EBUSY;
291         if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
292                                       "clk") < 0) {
293                 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
294                 return -EBUSY;
295         }
296         if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
297                                       "mode") < 0) {
298                 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
299                 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
300                 return -EBUSY;
301         }
302 
303         s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
304         if (!s3c24xx_uda134x_snd_device) {
305                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
306                        "Unable to register\n");
307                 return -ENOMEM;
308         }
309 
310         platform_set_drvdata(s3c24xx_uda134x_snd_device,
311                              &snd_soc_s3c24xx_uda134x);
312         platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
313         ret = platform_device_add(s3c24xx_uda134x_snd_device);
314         if (ret) {
315                 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
316                 platform_device_put(s3c24xx_uda134x_snd_device);
317         }
318 
319         return ret;
320 }
321 
322 static int s3c24xx_uda134x_remove(struct platform_device *pdev)
323 {
324         platform_device_unregister(s3c24xx_uda134x_snd_device);
325         gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
326         gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
327         gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
328         return 0;
329 }
330 
331 static struct platform_driver s3c24xx_uda134x_driver = {
332         .probe  = s3c24xx_uda134x_probe,
333         .remove = s3c24xx_uda134x_remove,
334         .driver = {
335                 .name = "s3c24xx_uda134x",
336         },
337 };
338 
339 module_platform_driver(s3c24xx_uda134x_driver);
340 
341 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
342 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
343 MODULE_LICENSE("GPL");
344 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us