blob: d731042e51b07db12aff2adc6685d97f3af6e00e [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>
Paul Gortmakerda155d52011-07-15 12:38:28 -040016#include <linux/module.h>
Seungwhan Youn0378b6a2011-01-11 07:26:06 +090017
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010018#include <sound/soc.h>
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010019#include <sound/s3c24xx_uda134x.h>
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010020
Ben Dooks8150bc82009-03-04 00:49:26 +000021#include <plat/regs-iis.h>
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010022
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010023#include "s3c24xx-i2s.h"
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010024
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
Mark Brownd0c36632008-11-18 21:57:17 +000059static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010060{
61 int ret = 0;
62#ifdef ENFORCE_RATES
Joe Perchesa419aef2009-08-18 11:18:35 -070063 struct snd_pcm_runtime *runtime = substream->runtime;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010064#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");
Axel Lin7803e322011-09-15 10:36:54 +080070 if (IS_ERR(xtal)) {
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010071 printk(KERN_ERR "%s cannot get xtal\n", __func__);
Axel Lin7803e322011-09-15 10:36:54 +080072 ret = PTR_ERR(xtal);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010073 } else {
74 pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
75 "pclk");
Axel Lin7803e322011-09-15 10:36:54 +080076 if (IS_ERR(pclk)) {
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010077 printk(KERN_ERR "%s cannot get pclk\n",
78 __func__);
79 clk_put(xtal);
Axel Lin7803e322011-09-15 10:36:54 +080080 ret = PTR_ERR(pclk);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010081 }
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
Mark Brownd0c36632008-11-18 21:57:17 +0000111static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100112{
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;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000129 struct snd_soc_dai *codec_dai = rtd->codec_dai;
130 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100131 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
Mark Brownd0c36632008-11-18 21:57:17 +0000176 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100177 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
178 if (ret < 0)
179 return ret;
180
Mark Brownd0c36632008-11-18 21:57:17 +0000181 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100182 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
183 if (ret < 0)
184 return ret;
185
Mark Brownd0c36632008-11-18 21:57:17 +0000186 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
187 SND_SOC_CLOCK_IN);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100188 if (ret < 0)
189 return ret;
190
Mark Brownd0c36632008-11-18 21:57:17 +0000191 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100192 if (ret < 0)
193 return ret;
194
Mark Brownd0c36632008-11-18 21:57:17 +0000195 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
196 S3C2410_IISMOD_32FS);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100197 if (ret < 0)
198 return ret;
199
Mark Brownd0c36632008-11-18 21:57:17 +0000200 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
201 S3C24XX_PRESCALE(div, div));
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100202 if (ret < 0)
203 return ret;
204
205 /* set the codec system clock for DAC and ADC */
Mark Brownd0c36632008-11-18 21:57:17 +0000206 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
207 SND_SOC_CLOCK_OUT);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100208 if (ret < 0)
209 return ret;
210
211 return 0;
212}
213
214static struct snd_soc_ops s3c24xx_uda134x_ops = {
215 .startup = s3c24xx_uda134x_startup,
216 .shutdown = s3c24xx_uda134x_shutdown,
217 .hw_params = s3c24xx_uda134x_hw_params,
218};
219
220static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
221 .name = "UDA134X",
222 .stream_name = "UDA134X",
Marek Beliskoa110f4e2011-03-09 21:46:20 +0100223 .codec_name = "uda134x-codec",
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000224 .codec_dai_name = "uda134x-hifi",
Lars-Peter Clausen518aa592011-01-24 22:12:42 +0100225 .cpu_dai_name = "s3c24xx-iis",
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100226 .ops = &s3c24xx_uda134x_ops,
Jassi Brar58bb4072010-11-22 15:35:50 +0900227 .platform_name = "samsung-audio",
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100228};
229
Mark Brown87506542008-11-18 20:50:34 +0000230static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100231 .name = "S3C24XX_UDA134X",
Axel Lin095d79d2011-12-22 10:53:15 +0800232 .owner = THIS_MODULE,
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100233 .dai_link = &s3c24xx_uda134x_dai_link,
234 .num_links = 1,
235};
236
237static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
238
239static void setdat(int v)
240{
241 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
242}
243
244static void setclk(int v)
245{
246 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
247}
248
249static void setmode(int v)
250{
251 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
252}
253
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000254/* FIXME - This must be codec platform data but in which board file ?? */
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100255static struct uda134x_platform_data s3c24xx_uda134x = {
256 .l3 = {
257 .setdat = setdat,
258 .setclk = setclk,
259 .setmode = setmode,
260 .data_hold = 1,
261 .data_setup = 1,
262 .clock_high = 1,
263 .mode_hold = 1,
264 .mode = 1,
265 .mode_setup = 1,
266 },
267};
268
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100269static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
270{
271 if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
272 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
273 "l3 %s pin already in use", fun);
274 return -EBUSY;
275 }
276 gpio_direction_output(pin, 0);
277 return 0;
278}
279
280static int s3c24xx_uda134x_probe(struct platform_device *pdev)
281{
282 int ret;
283
284 printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
285
286 s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
287 if (s3c24xx_uda134x_l3_pins == NULL) {
288 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
289 "unable to find platform data\n");
290 return -ENODEV;
291 }
292 s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
293 s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
294
295 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
296 "data") < 0)
297 return -EBUSY;
298 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
299 "clk") < 0) {
300 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
301 return -EBUSY;
302 }
303 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
304 "mode") < 0) {
305 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
306 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
307 return -EBUSY;
308 }
309
310 s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
311 if (!s3c24xx_uda134x_snd_device) {
312 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
313 "Unable to register\n");
314 return -ENOMEM;
315 }
316
317 platform_set_drvdata(s3c24xx_uda134x_snd_device,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000318 &snd_soc_s3c24xx_uda134x);
Marek Beliskoa110f4e2011-03-09 21:46:20 +0100319 platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100320 ret = platform_device_add(s3c24xx_uda134x_snd_device);
321 if (ret) {
322 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
323 platform_device_put(s3c24xx_uda134x_snd_device);
324 }
325
326 return ret;
327}
328
329static int s3c24xx_uda134x_remove(struct platform_device *pdev)
330{
331 platform_device_unregister(s3c24xx_uda134x_snd_device);
332 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
333 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
334 gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
335 return 0;
336}
337
338static struct platform_driver s3c24xx_uda134x_driver = {
339 .probe = s3c24xx_uda134x_probe,
340 .remove = s3c24xx_uda134x_remove,
341 .driver = {
342 .name = "s3c24xx_uda134x",
343 .owner = THIS_MODULE,
344 },
345};
346
Mark Browne00c3f52011-11-23 15:20:13 +0000347module_platform_driver(s3c24xx_uda134x_driver);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100348
349MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
350MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
351MODULE_LICENSE("GPL");