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_sysclk(cpu_dai, clk_source , clk,
177 			SND_SOC_CLOCK_IN);
178 	if (ret < 0)
179 		return ret;
180 
181 	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
182 	if (ret < 0)
183 		return ret;
184 
185 	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
186 			S3C2410_IISMOD_32FS);
187 	if (ret < 0)
188 		return ret;
189 
190 	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
191 			S3C24XX_PRESCALE(div, div));
192 	if (ret < 0)
193 		return ret;
194 
195 	/* set the codec system clock for DAC and ADC */
196 	ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
197 			SND_SOC_CLOCK_OUT);
198 	if (ret < 0)
199 		return ret;
200 
201 	return 0;
202 }
203 
204 static struct snd_soc_ops s3c24xx_uda134x_ops = {
205 	.startup = s3c24xx_uda134x_startup,
206 	.shutdown = s3c24xx_uda134x_shutdown,
207 	.hw_params = s3c24xx_uda134x_hw_params,
208 };
209 
210 static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
211 	.name = "UDA134X",
212 	.stream_name = "UDA134X",
213 	.codec_name = "uda134x-codec",
214 	.codec_dai_name = "uda134x-hifi",
215 	.cpu_dai_name = "s3c24xx-iis",
216 	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
217 		   SND_SOC_DAIFMT_CBS_CFS,
218 	.ops = &s3c24xx_uda134x_ops,
219 	.platform_name	= "s3c24xx-iis",
220 };
221 
222 static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
223 	.name = "S3C24XX_UDA134X",
224 	.owner = THIS_MODULE,
225 	.dai_link = &s3c24xx_uda134x_dai_link,
226 	.num_links = 1,
227 };
228 
229 static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
230 
setdat(int v)231 static void setdat(int v)
232 {
233 	gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
234 }
235 
setclk(int v)236 static void setclk(int v)
237 {
238 	gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
239 }
240 
setmode(int v)241 static void setmode(int v)
242 {
243 	gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
244 }
245 
246 /* FIXME - This must be codec platform data but in which board file ?? */
247 static struct uda134x_platform_data s3c24xx_uda134x = {
248 	.l3 = {
249 		.setdat = setdat,
250 		.setclk = setclk,
251 		.setmode = setmode,
252 		.data_hold = 1,
253 		.data_setup = 1,
254 		.clock_high = 1,
255 		.mode_hold = 1,
256 		.mode = 1,
257 		.mode_setup = 1,
258 	},
259 };
260 
s3c24xx_uda134x_setup_pin(int pin,char * fun)261 static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
262 {
263 	if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
264 		printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
265 		       "l3 %s pin already in use", fun);
266 		return -EBUSY;
267 	}
268 	gpio_direction_output(pin, 0);
269 	return 0;
270 }
271 
s3c24xx_uda134x_probe(struct platform_device * pdev)272 static int s3c24xx_uda134x_probe(struct platform_device *pdev)
273 {
274 	int ret;
275 
276 	printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
277 
278 	s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
279 	if (s3c24xx_uda134x_l3_pins == NULL) {
280 		printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
281 		       "unable to find platform data\n");
282 		return -ENODEV;
283 	}
284 	s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
285 	s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
286 
287 	if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
288 				      "data") < 0)
289 		return -EBUSY;
290 	if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
291 				      "clk") < 0) {
292 		gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
293 		return -EBUSY;
294 	}
295 	if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
296 				      "mode") < 0) {
297 		gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
298 		gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
299 		return -EBUSY;
300 	}
301 
302 	s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
303 	if (!s3c24xx_uda134x_snd_device) {
304 		printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
305 		       "Unable to register\n");
306 		return -ENOMEM;
307 	}
308 
309 	platform_set_drvdata(s3c24xx_uda134x_snd_device,
310 			     &snd_soc_s3c24xx_uda134x);
311 	platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
312 	ret = platform_device_add(s3c24xx_uda134x_snd_device);
313 	if (ret) {
314 		printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
315 		platform_device_put(s3c24xx_uda134x_snd_device);
316 	}
317 
318 	return ret;
319 }
320 
s3c24xx_uda134x_remove(struct platform_device * pdev)321 static int s3c24xx_uda134x_remove(struct platform_device *pdev)
322 {
323 	platform_device_unregister(s3c24xx_uda134x_snd_device);
324 	gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
325 	gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
326 	gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
327 	return 0;
328 }
329 
330 static struct platform_driver s3c24xx_uda134x_driver = {
331 	.probe  = s3c24xx_uda134x_probe,
332 	.remove = s3c24xx_uda134x_remove,
333 	.driver = {
334 		.name = "s3c24xx_uda134x",
335 	},
336 };
337 
338 module_platform_driver(s3c24xx_uda134x_driver);
339 
340 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
341 MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
342 MODULE_LICENSE("GPL");
343