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