Version:  2.0.40 2.2.26 2.4.37 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 4.9

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 int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
 58 {
 59         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 60         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 61 #ifdef ENFORCE_RATES
 62         struct snd_pcm_runtime *runtime = substream->runtime;
 63 #endif
 64         int ret = 0;
 65 
 66         mutex_lock(&clk_lock);
 67 
 68         if (clk_users == 0) {
 69                 xtal = clk_get(rtd->dev, "xtal");
 70                 if (IS_ERR(xtal)) {
 71                         dev_err(rtd->dev, "%s cannot get xtal\n", __func__);
 72                         ret = PTR_ERR(xtal);
 73                 } else {
 74                         pclk = clk_get(cpu_dai->dev, "iis");
 75                         if (IS_ERR(pclk)) {
 76                                 dev_err(rtd->dev, "%s cannot get pclk\n",
 77                                         __func__);
 78                                 clk_put(xtal);
 79                                 ret = PTR_ERR(pclk);
 80                         }
 81                 }
 82                 if (!ret) {
 83                         int i, j;
 84 
 85                         for (i = 0; i < 2; i++) {
 86                                 int fs = i ? 256 : 384;
 87 
 88                                 rates[i*33] = clk_get_rate(xtal) / fs;
 89                                 for (j = 1; j < 33; j++)
 90                                         rates[i*33 + j] = clk_get_rate(pclk) /
 91                                                 (j * fs);
 92                         }
 93                 }
 94         }
 95         clk_users += 1;
 96         mutex_unlock(&clk_lock);
 97         if (!ret) {
 98 #ifdef ENFORCE_RATES
 99                 ret = snd_pcm_hw_constraint_list(runtime, 0,
100                                                  SNDRV_PCM_HW_PARAM_RATE,
101                                                  &hw_constraints_rates);
102                 if (ret < 0)
103                         dev_err(rtd->dev, "%s cannot set constraints\n",
104                                 __func__);
105 #endif
106         }
107         return ret;
108 }
109 
110 static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
111 {
112         mutex_lock(&clk_lock);
113         clk_users -= 1;
114         if (clk_users == 0) {
115                 clk_put(xtal);
116                 xtal = NULL;
117                 clk_put(pclk);
118                 pclk = NULL;
119         }
120         mutex_unlock(&clk_lock);
121 }
122 
123 static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
124                                         struct snd_pcm_hw_params *params)
125 {
126         struct snd_soc_pcm_runtime *rtd = substream->private_data;
127         struct snd_soc_dai *codec_dai = rtd->codec_dai;
128         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
129         unsigned int clk = 0;
130         int ret = 0;
131         int clk_source, fs_mode;
132         unsigned long rate = params_rate(params);
133         long err, cerr;
134         unsigned int div;
135         int i, bi;
136 
137         err = 999999;
138         bi = 0;
139         for (i = 0; i < 2*33; i++) {
140                 cerr = rates[i] - rate;
141                 if (cerr < 0)
142                         cerr = -cerr;
143                 if (cerr < err) {
144                         err = cerr;
145                         bi = i;
146                 }
147         }
148         if (bi / 33 == 1)
149                 fs_mode = S3C2410_IISMOD_256FS;
150         else
151                 fs_mode = S3C2410_IISMOD_384FS;
152         if (bi % 33 == 0) {
153                 clk_source = S3C24XX_CLKSRC_MPLL;
154                 div = 1;
155         } else {
156                 clk_source = S3C24XX_CLKSRC_PCLK;
157                 div = bi % 33;
158         }
159 
160         dev_dbg(rtd->dev, "%s desired rate %lu, %d\n", __func__, rate, bi);
161 
162         clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
163 
164         dev_dbg(rtd->dev, "%s will use: %s %s %d sysclk %d err %ld\n", __func__,
165                 fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
166                 clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
167                 div, clk, err);
168 
169         if ((err * 100 / rate) > 5) {
170                 dev_err(rtd->dev, "effective frequency too different "
171                                   "from desired (%ld%%)\n", err * 100 / rate);
172                 return -EINVAL;
173         }
174 
175         ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
176                         SND_SOC_CLOCK_IN);
177         if (ret < 0)
178                 return ret;
179 
180         ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
181         if (ret < 0)
182                 return ret;
183 
184         ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
185                         S3C2410_IISMOD_32FS);
186         if (ret < 0)
187                 return ret;
188 
189         ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
190                         S3C24XX_PRESCALE(div, div));
191         if (ret < 0)
192                 return ret;
193 
194         /* set the codec system clock for DAC and ADC */
195         ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
196                         SND_SOC_CLOCK_OUT);
197         if (ret < 0)
198                 return ret;
199 
200         return 0;
201 }
202 
203 static struct snd_soc_ops s3c24xx_uda134x_ops = {
204         .startup = s3c24xx_uda134x_startup,
205         .shutdown = s3c24xx_uda134x_shutdown,
206         .hw_params = s3c24xx_uda134x_hw_params,
207 };
208 
209 static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
210         .name = "UDA134X",
211         .stream_name = "UDA134X",
212         .codec_name = "uda134x-codec",
213         .codec_dai_name = "uda134x-hifi",
214         .cpu_dai_name = "s3c24xx-iis",
215         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
216                    SND_SOC_DAIFMT_CBS_CFS,
217         .ops = &s3c24xx_uda134x_ops,
218         .platform_name  = "s3c24xx-iis",
219 };
220 
221 static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
222         .name = "S3C24XX_UDA134X",
223         .owner = THIS_MODULE,
224         .dai_link = &s3c24xx_uda134x_dai_link,
225         .num_links = 1,
226 };
227 
228 static int s3c24xx_uda134x_probe(struct platform_device *pdev)
229 {
230         struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;
231         int ret;
232 
233         platform_set_drvdata(pdev, card);
234         card->dev = &pdev->dev;
235 
236         ret = devm_snd_soc_register_card(&pdev->dev, card);
237         if (ret)
238                 dev_err(&pdev->dev, "failed to register card: %d\n", ret);
239 
240         return ret;
241 }
242 
243 static struct platform_driver s3c24xx_uda134x_driver = {
244         .probe  = s3c24xx_uda134x_probe,
245         .driver = {
246                 .name = "s3c24xx_uda134x",
247         },
248 };
249 module_platform_driver(s3c24xx_uda134x_driver);
250 
251 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
252 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
253 MODULE_LICENSE("GPL");
254 

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