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
39static struct clk *xtal;
40static 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 */
45static int clk_users;
46static DEFINE_MUTEX(clk_lock);
47
48static unsigned int rates[33 * 2];
49#ifdef ENFORCE_RATES
50static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
51	.count	= ARRAY_SIZE(rates),
52	.list	= rates,
53	.mask	= 0,
54};
55#endif
56
57static struct platform_device *s3c24xx_uda134x_snd_device;
58
59static 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
111static 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
125static 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
204static 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
210static 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
222static 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
229static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
230
231static void setdat(int v)
232{
233	gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
234}
235
236static void setclk(int v)
237{
238	gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
239}
240
241static 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 ?? */
247static 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
261static 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
272static 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
321static 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
330static 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
338module_platform_driver(s3c24xx_uda134x_driver);
339
340MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
341MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
342MODULE_LICENSE("GPL");
343