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_sysclk(cpu_dai, clk_source , clk,
177 SND_SOC_CLOCK_IN);
178 if (ret < 0)
179 return ret;
180
181 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
182 if (ret < 0)
183 return ret;
184
185 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
186 S3C2410_IISMOD_32FS);
187 if (ret < 0)
188 return ret;
189
190 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
191 S3C24XX_PRESCALE(div, div));
192 if (ret < 0)
193 return ret;
194
195
196 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
197 SND_SOC_CLOCK_OUT);
198 if (ret < 0)
199 return ret;
200
201 return 0;
202}
203
204static struct snd_soc_ops s3c24xx_uda134x_ops = {
205 .startup = s3c24xx_uda134x_startup,
206 .shutdown = s3c24xx_uda134x_shutdown,
207 .hw_params = s3c24xx_uda134x_hw_params,
208};
209
210static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
211 .name = "UDA134X",
212 .stream_name = "UDA134X",
213 .codec_name = "uda134x-codec",
214 .codec_dai_name = "uda134x-hifi",
215 .cpu_dai_name = "s3c24xx-iis",
216 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
217 SND_SOC_DAIFMT_CBS_CFS,
218 .ops = &s3c24xx_uda134x_ops,
219 .platform_name = "s3c24xx-iis",
220};
221
222static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
223 .name = "S3C24XX_UDA134X",
224 .owner = THIS_MODULE,
225 .dai_link = &s3c24xx_uda134x_dai_link,
226 .num_links = 1,
227};
228
229static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
230
231static void setdat(int v)
232{
233 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
234}
235
236static void setclk(int v)
237{
238 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
239}
240
241static void setmode(int v)
242{
243 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
244}
245
246
247static struct uda134x_platform_data s3c24xx_uda134x = {
248 .l3 = {
249 .setdat = setdat,
250 .setclk = setclk,
251 .setmode = setmode,
252 .data_hold = 1,
253 .data_setup = 1,
254 .clock_high = 1,
255 .mode_hold = 1,
256 .mode = 1,
257 .mode_setup = 1,
258 },
259};
260
261static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
262{
263 if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
264 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
265 "l3 %s pin already in use", fun);
266 return -EBUSY;
267 }
268 gpio_direction_output(pin, 0);
269 return 0;
270}
271
272static int s3c24xx_uda134x_probe(struct platform_device *pdev)
273{
274 int ret;
275
276 printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
277
278 s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
279 if (s3c24xx_uda134x_l3_pins == NULL) {
280 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
281 "unable to find platform data\n");
282 return -ENODEV;
283 }
284 s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
285 s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
286
287 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
288 "data") < 0)
289 return -EBUSY;
290 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
291 "clk") < 0) {
292 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
293 return -EBUSY;
294 }
295 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
296 "mode") < 0) {
297 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
298 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
299 return -EBUSY;
300 }
301
302 s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
303 if (!s3c24xx_uda134x_snd_device) {
304 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
305 "Unable to register\n");
306 return -ENOMEM;
307 }
308
309 platform_set_drvdata(s3c24xx_uda134x_snd_device,
310 &snd_soc_s3c24xx_uda134x);
311 platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
312 ret = platform_device_add(s3c24xx_uda134x_snd_device);
313 if (ret) {
314 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
315 platform_device_put(s3c24xx_uda134x_snd_device);
316 }
317
318 return ret;
319}
320
321static int s3c24xx_uda134x_remove(struct platform_device *pdev)
322{
323 platform_device_unregister(s3c24xx_uda134x_snd_device);
324 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
325 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
326 gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
327 return 0;
328}
329
330static struct platform_driver s3c24xx_uda134x_driver = {
331 .probe = s3c24xx_uda134x_probe,
332 .remove = s3c24xx_uda134x_remove,
333 .driver = {
334 .name = "s3c24xx_uda134x",
335 },
336};
337
338module_platform_driver(s3c24xx_uda134x_driver);
339
340MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
341MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
342MODULE_LICENSE("GPL");
343