blob: 92e88bca386e8199d4316898d27195e64251a626 [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
Arnd Bergmann5d229ce52013-04-11 19:08:42 +020021#include "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{
Sylwester Nawrocki1bc610e2016-08-04 11:51:25 +020061 struct snd_soc_pcm_runtime *rtd = substream->private_data;
62 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010063#ifdef ENFORCE_RATES
Joe Perchesa419aef2009-08-18 11:18:35 -070064 struct snd_pcm_runtime *runtime = substream->runtime;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010065#endif
Sylwester Nawrocki1bc610e2016-08-04 11:51:25 +020066 int ret = 0;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010067
68 mutex_lock(&clk_lock);
69 pr_debug("%s %d\n", __func__, clk_users);
70 if (clk_users == 0) {
71 xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
Axel Lin7803e322011-09-15 10:36:54 +080072 if (IS_ERR(xtal)) {
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010073 printk(KERN_ERR "%s cannot get xtal\n", __func__);
Axel Lin7803e322011-09-15 10:36:54 +080074 ret = PTR_ERR(xtal);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010075 } else {
Sylwester Nawrocki1bc610e2016-08-04 11:51:25 +020076 pclk = clk_get(cpu_dai->dev, "iis");
Axel Lin7803e322011-09-15 10:36:54 +080077 if (IS_ERR(pclk)) {
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010078 printk(KERN_ERR "%s cannot get pclk\n",
79 __func__);
80 clk_put(xtal);
Axel Lin7803e322011-09-15 10:36:54 +080081 ret = PTR_ERR(pclk);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +010082 }
83 }
84 if (!ret) {
85 int i, j;
86
87 for (i = 0; i < 2; i++) {
88 int fs = i ? 256 : 384;
89
90 rates[i*33] = clk_get_rate(xtal) / fs;
91 for (j = 1; j < 33; j++)
92 rates[i*33 + j] = clk_get_rate(pclk) /
93 (j * fs);
94 }
95 }
96 }
97 clk_users += 1;
98 mutex_unlock(&clk_lock);
99 if (!ret) {
100#ifdef ENFORCE_RATES
101 ret = snd_pcm_hw_constraint_list(runtime, 0,
102 SNDRV_PCM_HW_PARAM_RATE,
103 &hw_constraints_rates);
104 if (ret < 0)
105 printk(KERN_ERR "%s cannot set constraints\n",
106 __func__);
107#endif
108 }
109 return ret;
110}
111
Mark Brownd0c36632008-11-18 21:57:17 +0000112static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100113{
114 mutex_lock(&clk_lock);
115 pr_debug("%s %d\n", __func__, clk_users);
116 clk_users -= 1;
117 if (clk_users == 0) {
118 clk_put(xtal);
119 xtal = NULL;
120 clk_put(pclk);
121 pclk = NULL;
122 }
123 mutex_unlock(&clk_lock);
124}
125
126static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
127 struct snd_pcm_hw_params *params)
128{
129 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000130 struct snd_soc_dai *codec_dai = rtd->codec_dai;
131 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100132 unsigned int clk = 0;
133 int ret = 0;
134 int clk_source, fs_mode;
135 unsigned long rate = params_rate(params);
136 long err, cerr;
137 unsigned int div;
138 int i, bi;
139
140 err = 999999;
141 bi = 0;
142 for (i = 0; i < 2*33; i++) {
143 cerr = rates[i] - rate;
144 if (cerr < 0)
145 cerr = -cerr;
146 if (cerr < err) {
147 err = cerr;
148 bi = i;
149 }
150 }
151 if (bi / 33 == 1)
152 fs_mode = S3C2410_IISMOD_256FS;
153 else
154 fs_mode = S3C2410_IISMOD_384FS;
155 if (bi % 33 == 0) {
156 clk_source = S3C24XX_CLKSRC_MPLL;
157 div = 1;
158 } else {
159 clk_source = S3C24XX_CLKSRC_PCLK;
160 div = bi % 33;
161 }
162 pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
163
164 clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
165 pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
166 fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
167 clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
168 div, clk, err);
169
170 if ((err * 100 / rate) > 5) {
171 printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
172 "too different from desired (%ld%%)\n",
173 err * 100 / rate);
174 return -EINVAL;
175 }
176
Mark Brownd0c36632008-11-18 21:57:17 +0000177 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
178 SND_SOC_CLOCK_IN);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100179 if (ret < 0)
180 return ret;
181
Mark Brownd0c36632008-11-18 21:57:17 +0000182 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100183 if (ret < 0)
184 return ret;
185
Mark Brownd0c36632008-11-18 21:57:17 +0000186 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
187 S3C2410_IISMOD_32FS);
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_PRESCALER,
192 S3C24XX_PRESCALE(div, div));
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100193 if (ret < 0)
194 return ret;
195
196 /* set the codec system clock for DAC and ADC */
Mark Brownd0c36632008-11-18 21:57:17 +0000197 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
198 SND_SOC_CLOCK_OUT);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100199 if (ret < 0)
200 return ret;
201
202 return 0;
203}
204
205static struct snd_soc_ops s3c24xx_uda134x_ops = {
206 .startup = s3c24xx_uda134x_startup,
207 .shutdown = s3c24xx_uda134x_shutdown,
208 .hw_params = s3c24xx_uda134x_hw_params,
209};
210
211static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
212 .name = "UDA134X",
213 .stream_name = "UDA134X",
Marek Beliskoa110f4e2011-03-09 21:46:20 +0100214 .codec_name = "uda134x-codec",
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000215 .codec_dai_name = "uda134x-hifi",
Lars-Peter Clausen518aa592011-01-24 22:12:42 +0100216 .cpu_dai_name = "s3c24xx-iis",
Lars-Peter Clausen517b9a22015-01-01 17:16:25 +0100217 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
218 SND_SOC_DAIFMT_CBS_CFS,
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100219 .ops = &s3c24xx_uda134x_ops,
Padmavathi Vennaa08485d2012-12-07 13:59:21 +0530220 .platform_name = "s3c24xx-iis",
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100221};
222
Mark Brown87506542008-11-18 20:50:34 +0000223static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100224 .name = "S3C24XX_UDA134X",
Axel Lin095d79d2011-12-22 10:53:15 +0800225 .owner = THIS_MODULE,
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100226 .dai_link = &s3c24xx_uda134x_dai_link,
227 .num_links = 1,
228};
229
230static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
231
232static void setdat(int v)
233{
234 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
235}
236
237static void setclk(int v)
238{
239 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
240}
241
242static void setmode(int v)
243{
244 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
245}
246
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000247/* FIXME - This must be codec platform data but in which board file ?? */
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100248static struct uda134x_platform_data s3c24xx_uda134x = {
249 .l3 = {
250 .setdat = setdat,
251 .setclk = setclk,
252 .setmode = setmode,
253 .data_hold = 1,
254 .data_setup = 1,
255 .clock_high = 1,
256 .mode_hold = 1,
257 .mode = 1,
258 .mode_setup = 1,
259 },
260};
261
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100262static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
263{
264 if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
265 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
266 "l3 %s pin already in use", fun);
267 return -EBUSY;
268 }
269 gpio_direction_output(pin, 0);
270 return 0;
271}
272
273static int s3c24xx_uda134x_probe(struct platform_device *pdev)
274{
275 int ret;
276
277 printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
278
279 s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
280 if (s3c24xx_uda134x_l3_pins == NULL) {
281 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
282 "unable to find platform data\n");
283 return -ENODEV;
284 }
285 s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
286 s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
287
288 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
289 "data") < 0)
290 return -EBUSY;
291 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
292 "clk") < 0) {
293 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
294 return -EBUSY;
295 }
296 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
297 "mode") < 0) {
298 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
299 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
300 return -EBUSY;
301 }
302
303 s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
304 if (!s3c24xx_uda134x_snd_device) {
305 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
306 "Unable to register\n");
307 return -ENOMEM;
308 }
309
310 platform_set_drvdata(s3c24xx_uda134x_snd_device,
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000311 &snd_soc_s3c24xx_uda134x);
Marek Beliskoa110f4e2011-03-09 21:46:20 +0100312 platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100313 ret = platform_device_add(s3c24xx_uda134x_snd_device);
314 if (ret) {
315 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
316 platform_device_put(s3c24xx_uda134x_snd_device);
317 }
318
319 return ret;
320}
321
322static int s3c24xx_uda134x_remove(struct platform_device *pdev)
323{
324 platform_device_unregister(s3c24xx_uda134x_snd_device);
325 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
326 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
327 gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
328 return 0;
329}
330
331static struct platform_driver s3c24xx_uda134x_driver = {
332 .probe = s3c24xx_uda134x_probe,
333 .remove = s3c24xx_uda134x_remove,
334 .driver = {
335 .name = "s3c24xx_uda134x",
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100336 },
337};
338
Mark Browne00c3f52011-11-23 15:20:13 +0000339module_platform_driver(s3c24xx_uda134x_driver);
Christian Pellegrin7ad933d2008-11-15 08:58:32 +0100340
341MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
342MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
343MODULE_LICENSE("GPL");