1
2
3
4
5
6
7
8
9
10
11
12
13
14#include <linux/clk.h>
15#include <linux/gpio.h>
16
17#include <sound/soc.h>
18#include <sound/s3c24xx_uda134x.h>
19
20#include <plat/regs-iis.h>
21
22#include "s3c24xx-i2s.h"
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38static struct clk *xtal;
39static struct clk *pclk;
40
41
42
43
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
58static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
59{
60 int ret = 0;
61#ifdef ENFORCE_RATES
62 struct snd_pcm_runtime *runtime = substream->runtime;
63#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
110static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
111{
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;
128 struct snd_soc_dai *codec_dai = rtd->codec_dai;
129 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
130 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
175 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
176 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
177 if (ret < 0)
178 return ret;
179
180 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
181 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
182 if (ret < 0)
183 return ret;
184
185 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
186 SND_SOC_CLOCK_IN);
187 if (ret < 0)
188 return ret;
189
190 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
191 if (ret < 0)
192 return ret;
193
194 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
195 S3C2410_IISMOD_32FS);
196 if (ret < 0)
197 return ret;
198
199 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
200 S3C24XX_PRESCALE(div, div));
201 if (ret < 0)
202 return ret;
203
204
205 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
206 SND_SOC_CLOCK_OUT);
207 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",
222 .codec_name = "uda134x-codec",
223 .codec_dai_name = "uda134x-hifi",
224 .cpu_dai_name = "s3c24xx-iis",
225 .ops = &s3c24xx_uda134x_ops,
226 .platform_name = "samsung-audio",
227};
228
229static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
230 .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
252
253static 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
267static 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,
316 &snd_soc_s3c24xx_uda134x);
317 platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
318 ret = platform_device_add(s3c24xx_uda134x_snd_device);
319 if (ret) {
320 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
321 platform_device_put(s3c24xx_uda134x_snd_device);
322 }
323
324 return ret;
325}
326
327static int s3c24xx_uda134x_remove(struct platform_device *pdev)
328{
329 platform_device_unregister(s3c24xx_uda134x_snd_device);
330 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
331 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
332 gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
333 return 0;
334}
335
336static struct platform_driver s3c24xx_uda134x_driver = {
337 .probe = s3c24xx_uda134x_probe,
338 .remove = s3c24xx_uda134x_remove,
339 .driver = {
340 .name = "s3c24xx_uda134x",
341 .owner = THIS_MODULE,
342 },
343};
344
345static int __init s3c24xx_uda134x_init(void)
346{
347 return platform_driver_register(&s3c24xx_uda134x_driver);
348}
349
350static void __exit s3c24xx_uda134x_exit(void)
351{
352 platform_driver_unregister(&s3c24xx_uda134x_driver);
353}
354
355
356module_init(s3c24xx_uda134x_init);
357module_exit(s3c24xx_uda134x_exit);
358
359MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
360MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
361MODULE_LICENSE("GPL");
362