1
2
3
4
5
6
7
8
9
10
11
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39static struct clk *xtal;
40static struct clk *pclk;
41
42
43
44
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_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
177 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
178 if (ret < 0)
179 return ret;
180
181 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
182 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
183 if (ret < 0)
184 return ret;
185
186 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
187 SND_SOC_CLOCK_IN);
188 if (ret < 0)
189 return ret;
190
191 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
192 if (ret < 0)
193 return ret;
194
195 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
196 S3C2410_IISMOD_32FS);
197 if (ret < 0)
198 return ret;
199
200 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
201 S3C24XX_PRESCALE(div, div));
202 if (ret < 0)
203 return ret;
204
205
206 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
207 SND_SOC_CLOCK_OUT);
208 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",
223 .codec_name = "uda134x-codec",
224 .codec_dai_name = "uda134x-hifi",
225 .cpu_dai_name = "s3c24xx-iis",
226 .ops = &s3c24xx_uda134x_ops,
227 .platform_name = "s3c24xx-iis",
228};
229
230static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
231 .name = "S3C24XX_UDA134X",
232 .owner = THIS_MODULE,
233 .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
254
255static 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
269static 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,
318 &snd_soc_s3c24xx_uda134x);
319 platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
320 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
347module_platform_driver(s3c24xx_uda134x_driver);
348
349MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
350MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
351MODULE_LICENSE("GPL");
352