blob: ce749a10ec07058284ec8533f9c7df4aa7dcf836 [file] [log] [blame]
Christian Pellegrin7ad933d2008-11-15 08:58:32 +01001/*
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
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010014#include <linux/clk.h>
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010015#include <linux/gpio.h>
Seungwhan Youn0378b6a2011-01-11 07:26:06 +090016
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010017#include <sound/soc.h>
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010018#include <sound/s3c24xx_uda134x.h>
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010019
Ben Dooks8150bc82009-03-04 00:49:26 +000020#include <plat/regs-iis.h>
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010021
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010022#include "s3c24xx-i2s.h"
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010023
24/* #define ENFORCE_RATES 1 */
25/*
26 Unfortunately the S3C24XX in master mode has a limited capacity of
27 generating the clock for the codec. If you define this only rates
28 that are really available will be enforced. But be careful, most
29 user level application just want the usual sampling frequencies (8,
30 11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
31 operation for embedded systems. So if you aren't very lucky or your
32 hardware engineer wasn't very forward-looking it's better to leave
33 this undefined. If you do so an approximate value for the requested
34 sampling rate in the range -/+ 5% will be chosen. If this in not
35 possible an error will be returned.
36*/
37
38static struct clk *xtal;
39static struct clk *pclk;
40/* this is need because we don't have a place where to keep the
41 * pointers to the clocks in each substream. We get the clocks only
42 * when we are actually using them so we don't block stuff like
43 * frequency change or oscillator power-off */
44static int clk_users;
45static DEFINE_MUTEX(clk_lock);
46
47static unsigned int rates[33 * 2];
48#ifdef ENFORCE_RATES
49static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
50 .count = ARRAY_SIZE(rates),
51 .list = rates,
52 .mask = 0,
53};
54#endif
55
56static struct platform_device *s3c24xx_uda134x_snd_device;
57
Mark Brownd0c36632008-11-18 21:57:17 +000058static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010059{
60 int ret = 0;
61#ifdef ENFORCE_RATES
Joe Perchesa419aef2009-08-18 11:18:35 -070062 struct snd_pcm_runtime *runtime = substream->runtime;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010063#endif
64
65 mutex_lock(&clk_lock);
66 pr_debug("%s %d\n", __func__, clk_users);
67 if (clk_users == 0) {
68 xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
69 if (!xtal) {
70 printk(KERN_ERR "%s cannot get xtal\n", __func__);
71 ret = -EBUSY;
72 } else {
73 pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
74 "pclk");
75 if (!pclk) {
76 printk(KERN_ERR "%s cannot get pclk\n",
77 __func__);
78 clk_put(xtal);
79 ret = -EBUSY;
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 printk(KERN_ERR "%s cannot set constraints\n",
104 __func__);
105#endif
106 }
107 return ret;
108}
109
Mark Brownd0c36632008-11-18 21:57:17 +0000110static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100111{
112 mutex_lock(&clk_lock);
113 pr_debug("%s %d\n", __func__, clk_users);
114 clk_users -= 1;
115 if (clk_users == 0) {
116 clk_put(xtal);
117 xtal = NULL;
118 clk_put(pclk);
119 pclk = NULL;
120 }
121 mutex_unlock(&clk_lock);
122}
123
124static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
125 struct snd_pcm_hw_params *params)
126{
127 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000128 struct snd_soc_dai *codec_dai = rtd->codec_dai;
129 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100130 unsigned int clk = 0;
131 int ret = 0;
132 int clk_source, fs_mode;
133 unsigned long rate = params_rate(params);
134 long err, cerr;
135 unsigned int div;
136 int i, bi;
137
138 err = 999999;
139 bi = 0;
140 for (i = 0; i < 2*33; i++) {
141 cerr = rates[i] - rate;
142 if (cerr < 0)
143 cerr = -cerr;
144 if (cerr < err) {
145 err = cerr;
146 bi = i;
147 }
148 }
149 if (bi / 33 == 1)
150 fs_mode = S3C2410_IISMOD_256FS;
151 else
152 fs_mode = S3C2410_IISMOD_384FS;
153 if (bi % 33 == 0) {
154 clk_source = S3C24XX_CLKSRC_MPLL;
155 div = 1;
156 } else {
157 clk_source = S3C24XX_CLKSRC_PCLK;
158 div = bi % 33;
159 }
160 pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
161
162 clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
163 pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
164 fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
165 clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
166 div, clk, err);
167
168 if ((err * 100 / rate) > 5) {
169 printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
170 "too different from desired (%ld%%)\n",
171 err * 100 / rate);
172 return -EINVAL;
173 }
174
Mark Brownd0c36632008-11-18 21:57:17 +0000175 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100176 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
177 if (ret < 0)
178 return ret;
179
Mark Brownd0c36632008-11-18 21:57:17 +0000180 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100181 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
182 if (ret < 0)
183 return ret;
184
Mark Brownd0c36632008-11-18 21:57:17 +0000185 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
186 SND_SOC_CLOCK_IN);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100187 if (ret < 0)
188 return ret;
189
Mark Brownd0c36632008-11-18 21:57:17 +0000190 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100191 if (ret < 0)
192 return ret;
193
Mark Brownd0c36632008-11-18 21:57:17 +0000194 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
195 S3C2410_IISMOD_32FS);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100196 if (ret < 0)
197 return ret;
198
Mark Brownd0c36632008-11-18 21:57:17 +0000199 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
200 S3C24XX_PRESCALE(div, div));
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100201 if (ret < 0)
202 return ret;
203
204 /* set the codec system clock for DAC and ADC */
Mark Brownd0c36632008-11-18 21:57:17 +0000205 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
206 SND_SOC_CLOCK_OUT);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100207 if (ret < 0)
208 return ret;
209
210 return 0;
211}
212
213static struct snd_soc_ops s3c24xx_uda134x_ops = {
214 .startup = s3c24xx_uda134x_startup,
215 .shutdown = s3c24xx_uda134x_shutdown,
216 .hw_params = s3c24xx_uda134x_hw_params,
217};
218
219static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
220 .name = "UDA134X",
221 .stream_name = "UDA134X",
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000222 .codec_name = "uda134x-hifi",
223 .codec_dai_name = "uda134x-hifi",
224 .cpu_dai_name = "s3c24xx-i2s",
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100225 .ops = &s3c24xx_uda134x_ops,
Jassi Brar58bb4072010-11-22 15:35:50 +0900226 .platform_name = "samsung-audio",
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100227};
228
Mark Brown87506542008-11-18 20:50:34 +0000229static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100230 .name = "S3C24XX_UDA134X",
231 .dai_link = &s3c24xx_uda134x_dai_link,
232 .num_links = 1,
233};
234
235static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
236
237static void setdat(int v)
238{
239 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
240}
241
242static void setclk(int v)
243{
244 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
245}
246
247static void setmode(int v)
248{
249 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
250}
251
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000252/* FIXME - This must be codec platform data but in which board file ?? */
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100253static struct uda134x_platform_data s3c24xx_uda134x = {
254 .l3 = {
255 .setdat = setdat,
256 .setclk = setclk,
257 .setmode = setmode,
258 .data_hold = 1,
259 .data_setup = 1,
260 .clock_high = 1,
261 .mode_hold = 1,
262 .mode = 1,
263 .mode_setup = 1,
264 },
265};
266
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100267static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
268{
269 if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
270 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
271 "l3 %s pin already in use", fun);
272 return -EBUSY;
273 }
274 gpio_direction_output(pin, 0);
275 return 0;
276}
277
278static int s3c24xx_uda134x_probe(struct platform_device *pdev)
279{
280 int ret;
281
282 printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
283
284 s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
285 if (s3c24xx_uda134x_l3_pins == NULL) {
286 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
287 "unable to find platform data\n");
288 return -ENODEV;
289 }
290 s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
291 s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
292
293 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
294 "data") < 0)
295 return -EBUSY;
296 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
297 "clk") < 0) {
298 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
299 return -EBUSY;
300 }
301 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
302 "mode") < 0) {
303 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
304 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
305 return -EBUSY;
306 }
307
308 s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
309 if (!s3c24xx_uda134x_snd_device) {
310 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
311 "Unable to register\n");
312 return -ENOMEM;
313 }
314
315 platform_set_drvdata(s3c24xx_uda134x_snd_device,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000316 &snd_soc_s3c24xx_uda134x);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100317 ret = platform_device_add(s3c24xx_uda134x_snd_device);
318 if (ret) {
319 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
320 platform_device_put(s3c24xx_uda134x_snd_device);
321 }
322
323 return ret;
324}
325
326static int s3c24xx_uda134x_remove(struct platform_device *pdev)
327{
328 platform_device_unregister(s3c24xx_uda134x_snd_device);
329 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
330 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
331 gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
332 return 0;
333}
334
335static struct platform_driver s3c24xx_uda134x_driver = {
336 .probe = s3c24xx_uda134x_probe,
337 .remove = s3c24xx_uda134x_remove,
338 .driver = {
339 .name = "s3c24xx_uda134x",
340 .owner = THIS_MODULE,
341 },
342};
343
344static int __init s3c24xx_uda134x_init(void)
345{
346 return platform_driver_register(&s3c24xx_uda134x_driver);
347}
348
349static void __exit s3c24xx_uda134x_exit(void)
350{
351 platform_driver_unregister(&s3c24xx_uda134x_driver);
352}
353
354
355module_init(s3c24xx_uda134x_init);
356module_exit(s3c24xx_uda134x_exit);
357
358MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
359MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
360MODULE_LICENSE("GPL");