]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - sound/soc/samsung/s3c24xx_uda134x.c
sound: Add module.h to the previously silent sound users
[mirror_ubuntu-bionic-kernel.git] / sound / soc / samsung / s3c24xx_uda134x.c
CommitLineData
7ad933d7
CP
1/*
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
7ad933d7 14#include <linux/clk.h>
7ad933d7 15#include <linux/gpio.h>
da155d5b 16#include <linux/module.h>
0378b6ac 17
7ad933d7 18#include <sound/soc.h>
7ad933d7 19#include <sound/s3c24xx_uda134x.h>
7ad933d7 20
8150bc88 21#include <plat/regs-iis.h>
7ad933d7 22
7ad933d7 23#include "s3c24xx-i2s.h"
7ad933d7
CP
24
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
d0c36631 59static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
7ad933d7
CP
60{
61 int ret = 0;
62#ifdef ENFORCE_RATES
a419aef8 63 struct snd_pcm_runtime *runtime = substream->runtime;
7ad933d7
CP
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");
7803e329 70 if (IS_ERR(xtal)) {
7ad933d7 71 printk(KERN_ERR "%s cannot get xtal\n", __func__);
7803e329 72 ret = PTR_ERR(xtal);
7ad933d7
CP
73 } else {
74 pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
75 "pclk");
7803e329 76 if (IS_ERR(pclk)) {
7ad933d7
CP
77 printk(KERN_ERR "%s cannot get pclk\n",
78 __func__);
79 clk_put(xtal);
7803e329 80 ret = PTR_ERR(pclk);
7ad933d7
CP
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
d0c36631 111static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
7ad933d7
CP
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;
f0fba2ad
LG
129 struct snd_soc_dai *codec_dai = rtd->codec_dai;
130 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
7ad933d7
CP
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
d0c36631 176 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
7ad933d7
CP
177 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
178 if (ret < 0)
179 return ret;
180
d0c36631 181 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
7ad933d7
CP
182 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
183 if (ret < 0)
184 return ret;
185
d0c36631
MB
186 ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
187 SND_SOC_CLOCK_IN);
7ad933d7
CP
188 if (ret < 0)
189 return ret;
190
d0c36631 191 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
7ad933d7
CP
192 if (ret < 0)
193 return ret;
194
d0c36631
MB
195 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
196 S3C2410_IISMOD_32FS);
7ad933d7
CP
197 if (ret < 0)
198 return ret;
199
d0c36631
MB
200 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
201 S3C24XX_PRESCALE(div, div));
7ad933d7
CP
202 if (ret < 0)
203 return ret;
204
205 /* set the codec system clock for DAC and ADC */
d0c36631
MB
206 ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
207 SND_SOC_CLOCK_OUT);
7ad933d7
CP
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",
a110f4ef 223 .codec_name = "uda134x-codec",
f0fba2ad 224 .codec_dai_name = "uda134x-hifi",
518aa59f 225 .cpu_dai_name = "s3c24xx-iis",
7ad933d7 226 .ops = &s3c24xx_uda134x_ops,
58bb4072 227 .platform_name = "samsung-audio",
7ad933d7
CP
228};
229
87506549 230static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
7ad933d7
CP
231 .name = "S3C24XX_UDA134X",
232 .dai_link = &s3c24xx_uda134x_dai_link,
233 .num_links = 1,
234};
235
236static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
237
238static void setdat(int v)
239{
240 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
241}
242
243static void setclk(int v)
244{
245 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
246}
247
248static void setmode(int v)
249{
250 gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
251}
252
f0fba2ad 253/* FIXME - This must be codec platform data but in which board file ?? */
7ad933d7
CP
254static struct uda134x_platform_data s3c24xx_uda134x = {
255 .l3 = {
256 .setdat = setdat,
257 .setclk = setclk,
258 .setmode = setmode,
259 .data_hold = 1,
260 .data_setup = 1,
261 .clock_high = 1,
262 .mode_hold = 1,
263 .mode = 1,
264 .mode_setup = 1,
265 },
266};
267
7ad933d7
CP
268static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
269{
270 if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
271 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
272 "l3 %s pin already in use", fun);
273 return -EBUSY;
274 }
275 gpio_direction_output(pin, 0);
276 return 0;
277}
278
279static int s3c24xx_uda134x_probe(struct platform_device *pdev)
280{
281 int ret;
282
283 printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
284
285 s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
286 if (s3c24xx_uda134x_l3_pins == NULL) {
287 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
288 "unable to find platform data\n");
289 return -ENODEV;
290 }
291 s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
292 s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
293
294 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
295 "data") < 0)
296 return -EBUSY;
297 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
298 "clk") < 0) {
299 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
300 return -EBUSY;
301 }
302 if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
303 "mode") < 0) {
304 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
305 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
306 return -EBUSY;
307 }
308
309 s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
310 if (!s3c24xx_uda134x_snd_device) {
311 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
312 "Unable to register\n");
313 return -ENOMEM;
314 }
315
316 platform_set_drvdata(s3c24xx_uda134x_snd_device,
f0fba2ad 317 &snd_soc_s3c24xx_uda134x);
a110f4ef 318 platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));
7ad933d7
CP
319 ret = platform_device_add(s3c24xx_uda134x_snd_device);
320 if (ret) {
321 printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
322 platform_device_put(s3c24xx_uda134x_snd_device);
323 }
324
325 return ret;
326}
327
328static int s3c24xx_uda134x_remove(struct platform_device *pdev)
329{
330 platform_device_unregister(s3c24xx_uda134x_snd_device);
331 gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
332 gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
333 gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
334 return 0;
335}
336
337static struct platform_driver s3c24xx_uda134x_driver = {
338 .probe = s3c24xx_uda134x_probe,
339 .remove = s3c24xx_uda134x_remove,
340 .driver = {
341 .name = "s3c24xx_uda134x",
342 .owner = THIS_MODULE,
343 },
344};
345
346static int __init s3c24xx_uda134x_init(void)
347{
348 return platform_driver_register(&s3c24xx_uda134x_driver);
349}
350
351static void __exit s3c24xx_uda134x_exit(void)
352{
353 platform_driver_unregister(&s3c24xx_uda134x_driver);
354}
355
356
357module_init(s3c24xx_uda134x_init);
358module_exit(s3c24xx_uda134x_exit);
359
360MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
361MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
362MODULE_LICENSE("GPL");