• 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/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 
s3c24xx_uda134x_startup(struct snd_pcm_substream * substream)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 
s3c24xx_uda134x_shutdown(struct snd_pcm_substream * substream)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 
s3c24xx_uda134x_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)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 = 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_dai = &uda134x_dai,
231 	.cpu_dai = &s3c24xx_i2s_dai,
232 	.ops = &s3c24xx_uda134x_ops,
233 };
234 
235 static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
236 	.name = "S3C24XX_UDA134X",
237 	.platform = &s3c24xx_soc_platform,
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 
setdat(int v)244 static void setdat(int v)
245 {
246 	gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
247 }
248 
setclk(int v)249 static void setclk(int v)
250 {
251 	gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
252 }
253 
setmode(int v)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 	.card = &snd_soc_s3c24xx_uda134x,
275 	.codec_dev = &soc_codec_dev_uda134x,
276 	.codec_data = &s3c24xx_uda134x,
277 };
278 
s3c24xx_uda134x_setup_pin(int pin,char * fun)279 static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
280 {
281 	if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
282 		printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
283 		       "l3 %s pin already in use", fun);
284 		return -EBUSY;
285 	}
286 	gpio_direction_output(pin, 0);
287 	return 0;
288 }
289 
s3c24xx_uda134x_probe(struct platform_device * pdev)290 static int s3c24xx_uda134x_probe(struct platform_device *pdev)
291 {
292 	int ret;
293 
294 	printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
295 
296 	s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
297 	if (s3c24xx_uda134x_l3_pins == NULL) {
298 		printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
299 		       "unable to find platform data\n");
300 		return -ENODEV;
301 	}
302 	s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
303 	s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
304 
305 	if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
306 				      "data") < 0)
307 		return -EBUSY;
308 	if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
309 				      "clk") < 0) {
310 		gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
311 		return -EBUSY;
312 	}
313 	if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
314 				      "mode") < 0) {
315 		gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
316 		gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
317 		return -EBUSY;
318 	}
319 
320 	s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
321 	if (!s3c24xx_uda134x_snd_device) {
322 		printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
323 		       "Unable to register\n");
324 		return -ENOMEM;
325 	}
326 
327 	platform_set_drvdata(s3c24xx_uda134x_snd_device,
328 			     &s3c24xx_uda134x_snd_devdata);
329 	s3c24xx_uda134x_snd_devdata.dev = &s3c24xx_uda134x_snd_device->dev;
330 	ret = platform_device_add(s3c24xx_uda134x_snd_device);
331 	if (ret) {
332 		printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
333 		platform_device_put(s3c24xx_uda134x_snd_device);
334 	}
335 
336 	return ret;
337 }
338 
s3c24xx_uda134x_remove(struct platform_device * pdev)339 static int s3c24xx_uda134x_remove(struct platform_device *pdev)
340 {
341 	platform_device_unregister(s3c24xx_uda134x_snd_device);
342 	gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
343 	gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
344 	gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
345 	return 0;
346 }
347 
348 static struct platform_driver s3c24xx_uda134x_driver = {
349 	.probe  = s3c24xx_uda134x_probe,
350 	.remove = s3c24xx_uda134x_remove,
351 	.driver = {
352 		.name = "s3c24xx_uda134x",
353 		.owner = THIS_MODULE,
354 	},
355 };
356 
s3c24xx_uda134x_init(void)357 static int __init s3c24xx_uda134x_init(void)
358 {
359 	return platform_driver_register(&s3c24xx_uda134x_driver);
360 }
361 
s3c24xx_uda134x_exit(void)362 static void __exit s3c24xx_uda134x_exit(void)
363 {
364 	platform_driver_unregister(&s3c24xx_uda134x_driver);
365 }
366 
367 
368 module_init(s3c24xx_uda134x_init);
369 module_exit(s3c24xx_uda134x_exit);
370 
371 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
372 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
373 MODULE_LICENSE("GPL");
374