blob: bd48ffbde8801701b2c5c18deb20b2e28457c697 [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
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
Ben Dooks8150bc82009-03-04 00:49:26 +000025#include <plat/regs-iis.h>
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010026
Jassi Brard3ff5a32009-11-17 16:53:31 +090027#include "s3c-dma.h"
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010028#include "s3c24xx-i2s.h"
Mark Brown72f2b892008-11-18 12:25:46 +000029#include "../codecs/uda134x.h"
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010030
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
46static struct clk *xtal;
47static 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 */
52static int clk_users;
53static DEFINE_MUTEX(clk_lock);
54
55static unsigned int rates[33 * 2];
56#ifdef ENFORCE_RATES
57static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
58 .count = ARRAY_SIZE(rates),
59 .list = rates,
60 .mask = 0,
61};
62#endif
63
64static struct platform_device *s3c24xx_uda134x_snd_device;
65
Mark Brownd0c36632008-11-18 21:57:17 +000066static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010067{
68 int ret = 0;
69#ifdef ENFORCE_RATES
Joe Perchesa419aef2009-08-18 11:18:35 -070070 struct snd_pcm_runtime *runtime = substream->runtime;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010071#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
Mark Brownd0c36632008-11-18 21:57:17 +0000118static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100119{
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
132static 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;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000136 struct snd_soc_dai *codec_dai = rtd->codec_dai;
137 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100138 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
Mark Brownd0c36632008-11-18 21:57:17 +0000183 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100184 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
185 if (ret < 0)
186 return ret;
187
Mark Brownd0c36632008-11-18 21:57:17 +0000188 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100189 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
190 if (ret < 0)
191 return ret;
192
Mark Brownd0c36632008-11-18 21:57:17 +0000193 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
194 SND_SOC_CLOCK_IN);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100195 if (ret < 0)
196 return ret;
197
Mark Brownd0c36632008-11-18 21:57:17 +0000198 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100199 if (ret < 0)
200 return ret;
201
Mark Brownd0c36632008-11-18 21:57:17 +0000202 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
203 S3C2410_IISMOD_32FS);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100204 if (ret < 0)
205 return ret;
206
Mark Brownd0c36632008-11-18 21:57:17 +0000207 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
208 S3C24XX_PRESCALE(div, div));
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100209 if (ret < 0)
210 return ret;
211
212 /* set the codec system clock for DAC and ADC */
Mark Brownd0c36632008-11-18 21:57:17 +0000213 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
214 SND_SOC_CLOCK_OUT);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100215 if (ret < 0)
216 return ret;
217
218 return 0;
219}
220
221static 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
227static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
228 .name = "UDA134X",
229 .stream_name = "UDA134X",
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000230 .codec_name = "uda134x-hifi",
231 .codec_dai_name = "uda134x-hifi",
232 .cpu_dai_name = "s3c24xx-i2s",
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100233 .ops = &s3c24xx_uda134x_ops,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000234 .platform_name = "s3c24xx-pcm-audio",
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100235};
236
Mark Brown87506542008-11-18 20:50:34 +0000237static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100238 .name = "S3C24XX_UDA134X",
239 .dai_link = &s3c24xx_uda134x_dai_link,
240 .num_links = 1,
241};
242
243static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
244
245static void setdat(int v)
246{
247 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
248}
249
250static void setclk(int v)
251{
252 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
253}
254
255static void setmode(int v)
256{
257 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
258}
259
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000260/* FIXME - This must be codec platform data but in which board file ?? */
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100261static struct uda134x_platform_data s3c24xx_uda134x = {
262 .l3 = {
263 .setdat = setdat,
264 .setclk = setclk,
265 .setmode = setmode,
266 .data_hold = 1,
267 .data_setup = 1,
268 .clock_high = 1,
269 .mode_hold = 1,
270 .mode = 1,
271 .mode_setup = 1,
272 },
273};
274
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100275static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
276{
277 if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
278 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
279 "l3 %s pin already in use", fun);
280 return -EBUSY;
281 }
282 gpio_direction_output(pin, 0);
283 return 0;
284}
285
286static int s3c24xx_uda134x_probe(struct platform_device *pdev)
287{
288 int ret;
289
290 printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
291
292 s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
293 if (s3c24xx_uda134x_l3_pins == NULL) {
294 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
295 "unable to find platform data\n");
296 return -ENODEV;
297 }
298 s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
299 s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
300
301 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
302 "data") < 0)
303 return -EBUSY;
304 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
305 "clk") < 0) {
306 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
307 return -EBUSY;
308 }
309 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
310 "mode") < 0) {
311 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
312 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
313 return -EBUSY;
314 }
315
316 s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
317 if (!s3c24xx_uda134x_snd_device) {
318 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
319 "Unable to register\n");
320 return -ENOMEM;
321 }
322
323 platform_set_drvdata(s3c24xx_uda134x_snd_device,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000324 &snd_soc_s3c24xx_uda134x);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100325 ret = platform_device_add(s3c24xx_uda134x_snd_device);
326 if (ret) {
327 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
328 platform_device_put(s3c24xx_uda134x_snd_device);
329 }
330
331 return ret;
332}
333
334static int s3c24xx_uda134x_remove(struct platform_device *pdev)
335{
336 platform_device_unregister(s3c24xx_uda134x_snd_device);
337 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
338 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
339 gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
340 return 0;
341}
342
343static struct platform_driver s3c24xx_uda134x_driver = {
344 .probe = s3c24xx_uda134x_probe,
345 .remove = s3c24xx_uda134x_remove,
346 .driver = {
347 .name = "s3c24xx_uda134x",
348 .owner = THIS_MODULE,
349 },
350};
351
352static int __init s3c24xx_uda134x_init(void)
353{
354 return platform_driver_register(&s3c24xx_uda134x_driver);
355}
356
357static void __exit s3c24xx_uda134x_exit(void)
358{
359 platform_driver_unregister(&s3c24xx_uda134x_driver);
360}
361
362
363module_init(s3c24xx_uda134x_init);
364module_exit(s3c24xx_uda134x_exit);
365
366MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
367MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
368MODULE_LICENSE("GPL");