Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/samsung/s3c24xx_uda134x.c
10817 views
1
/*
2
* Modifications by Christian Pellegrin <[email protected]>
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
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
/* #define ENFORCE_RATES 1 */
25
/*
26
Unfortunately the S3C24XX in master mode has a limited capacity of
27
generating the clock for the codec. If you define this only rates
28
that are really available will be enforced. But be careful, most
29
user level application just want the usual sampling frequencies (8,
30
11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
31
operation for embedded systems. So if you aren't very lucky or your
32
hardware engineer wasn't very forward-looking it's better to leave
33
this undefined. If you do so an approximate value for the requested
34
sampling rate in the range -/+ 5% will be chosen. If this in not
35
possible an error will be returned.
36
*/
37
38
static struct clk *xtal;
39
static struct clk *pclk;
40
/* this is need because we don't have a place where to keep the
41
* pointers to the clocks in each substream. We get the clocks only
42
* when we are actually using them so we don't block stuff like
43
* frequency change or oscillator power-off */
44
static int clk_users;
45
static DEFINE_MUTEX(clk_lock);
46
47
static unsigned int rates[33 * 2];
48
#ifdef ENFORCE_RATES
49
static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
50
.count = ARRAY_SIZE(rates),
51
.list = rates,
52
.mask = 0,
53
};
54
#endif
55
56
static struct platform_device *s3c24xx_uda134x_snd_device;
57
58
static 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
110
static 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
124
static 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
/* set the codec system clock for DAC and ADC */
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
213
static 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
219
static 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
229
static 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
235
static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
236
237
static void setdat(int v)
238
{
239
gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
240
}
241
242
static void setclk(int v)
243
{
244
gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
245
}
246
247
static void setmode(int v)
248
{
249
gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
250
}
251
252
/* FIXME - This must be codec platform data but in which board file ?? */
253
static 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
267
static 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
278
static 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
327
static 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
336
static 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
345
static int __init s3c24xx_uda134x_init(void)
346
{
347
return platform_driver_register(&s3c24xx_uda134x_driver);
348
}
349
350
static void __exit s3c24xx_uda134x_exit(void)
351
{
352
platform_driver_unregister(&s3c24xx_uda134x_driver);
353
}
354
355
356
module_init(s3c24xx_uda134x_init);
357
module_exit(s3c24xx_uda134x_exit);
358
359
MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <[email protected]>");
360
MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
361
MODULE_LICENSE("GPL");
362
363