Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/samsung/rx1950_uda1380.c
10817 views
1
/*
2
* rx1950.c -- ALSA Soc Audio Layer
3
*
4
* Copyright (c) 2010 Vasily Khoruzhick <[email protected]>
5
*
6
* Based on smdk2440.c and magician.c
7
*
8
* Authors: Graeme Gregory [email protected]
9
* Philipp Zabel <[email protected]>
10
* Denis Grigoriev <[email protected]>
11
* Vasily Khoruzhick <[email protected]>
12
*
13
* This program is free software; you can redistribute it and/or modify it
14
* under the terms of the GNU General Public License as published by the
15
* Free Software Foundation; either version 2 of the License, or (at your
16
* option) any later version.
17
*
18
*/
19
20
#include <linux/gpio.h>
21
22
#include <sound/soc.h>
23
#include <sound/jack.h>
24
25
#include <plat/regs-iis.h>
26
#include <asm/mach-types.h>
27
28
#include "s3c24xx-i2s.h"
29
30
static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd);
31
static int rx1950_startup(struct snd_pcm_substream *substream);
32
static int rx1950_hw_params(struct snd_pcm_substream *substream,
33
struct snd_pcm_hw_params *params);
34
static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
35
struct snd_kcontrol *kcontrol, int event);
36
37
static unsigned int rates[] = {
38
16000,
39
44100,
40
48000,
41
};
42
43
static struct snd_pcm_hw_constraint_list hw_rates = {
44
.count = ARRAY_SIZE(rates),
45
.list = rates,
46
.mask = 0,
47
};
48
49
static struct snd_soc_jack hp_jack;
50
51
static struct snd_soc_jack_pin hp_jack_pins[] = {
52
{
53
.pin = "Headphone Jack",
54
.mask = SND_JACK_HEADPHONE,
55
},
56
{
57
.pin = "Speaker",
58
.mask = SND_JACK_HEADPHONE,
59
.invert = 1,
60
},
61
};
62
63
static struct snd_soc_jack_gpio hp_jack_gpios[] = {
64
[0] = {
65
.gpio = S3C2410_GPG(12),
66
.name = "hp-gpio",
67
.report = SND_JACK_HEADPHONE,
68
.invert = 1,
69
.debounce_time = 200,
70
},
71
};
72
73
static struct snd_soc_ops rx1950_ops = {
74
.startup = rx1950_startup,
75
.hw_params = rx1950_hw_params,
76
};
77
78
/* s3c24xx digital audio interface glue - connects codec <--> CPU */
79
static struct snd_soc_dai_link rx1950_uda1380_dai[] = {
80
{
81
.name = "uda1380",
82
.stream_name = "UDA1380 Duplex",
83
.cpu_dai_name = "s3c24xx-iis",
84
.codec_dai_name = "uda1380-hifi",
85
.init = rx1950_uda1380_init,
86
.platform_name = "samsung-audio",
87
.codec_name = "uda1380-codec.0-001a",
88
.ops = &rx1950_ops,
89
},
90
};
91
92
static struct snd_soc_card rx1950_asoc = {
93
.name = "rx1950",
94
.dai_link = rx1950_uda1380_dai,
95
.num_links = ARRAY_SIZE(rx1950_uda1380_dai),
96
};
97
98
/* rx1950 machine dapm widgets */
99
static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
100
SND_SOC_DAPM_HP("Headphone Jack", NULL),
101
SND_SOC_DAPM_MIC("Mic Jack", NULL),
102
SND_SOC_DAPM_SPK("Speaker", rx1950_spk_power),
103
};
104
105
/* rx1950 machine audio_map */
106
static const struct snd_soc_dapm_route audio_map[] = {
107
/* headphone connected to VOUTLHP, VOUTRHP */
108
{"Headphone Jack", NULL, "VOUTLHP"},
109
{"Headphone Jack", NULL, "VOUTRHP"},
110
111
/* ext speaker connected to VOUTL, VOUTR */
112
{"Speaker", NULL, "VOUTL"},
113
{"Speaker", NULL, "VOUTR"},
114
115
/* mic is connected to VINM */
116
{"VINM", NULL, "Mic Jack"},
117
};
118
119
static struct platform_device *s3c24xx_snd_device;
120
121
static int rx1950_startup(struct snd_pcm_substream *substream)
122
{
123
struct snd_pcm_runtime *runtime = substream->runtime;
124
125
runtime->hw.rate_min = hw_rates.list[0];
126
runtime->hw.rate_max = hw_rates.list[hw_rates.count - 1];
127
runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
128
129
return snd_pcm_hw_constraint_list(runtime, 0,
130
SNDRV_PCM_HW_PARAM_RATE,
131
&hw_rates);
132
}
133
134
static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
135
struct snd_kcontrol *kcontrol, int event)
136
{
137
if (SND_SOC_DAPM_EVENT_ON(event))
138
gpio_set_value(S3C2410_GPA(1), 1);
139
else
140
gpio_set_value(S3C2410_GPA(1), 0);
141
142
return 0;
143
}
144
145
static int rx1950_hw_params(struct snd_pcm_substream *substream,
146
struct snd_pcm_hw_params *params)
147
{
148
struct snd_soc_pcm_runtime *rtd = substream->private_data;
149
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
150
struct snd_soc_dai *codec_dai = rtd->codec_dai;
151
int div;
152
int ret;
153
unsigned int rate = params_rate(params);
154
int clk_source, fs_mode;
155
156
switch (rate) {
157
case 16000:
158
case 48000:
159
clk_source = S3C24XX_CLKSRC_PCLK;
160
fs_mode = S3C2410_IISMOD_256FS;
161
div = s3c24xx_i2s_get_clockrate() / (256 * rate);
162
if (s3c24xx_i2s_get_clockrate() % (256 * rate) > (128 * rate))
163
div++;
164
break;
165
case 44100:
166
case 88200:
167
clk_source = S3C24XX_CLKSRC_MPLL;
168
fs_mode = S3C2410_IISMOD_384FS;
169
div = 1;
170
break;
171
default:
172
printk(KERN_ERR "%s: rate %d is not supported\n",
173
__func__, rate);
174
return -EINVAL;
175
}
176
177
/* set codec DAI configuration */
178
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
179
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
180
if (ret < 0)
181
return ret;
182
183
/* set cpu DAI configuration */
184
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
185
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
186
if (ret < 0)
187
return ret;
188
189
/* select clock source */
190
ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source, rate,
191
SND_SOC_CLOCK_OUT);
192
if (ret < 0)
193
return ret;
194
195
/* set MCLK division for sample rate */
196
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
197
fs_mode);
198
if (ret < 0)
199
return ret;
200
201
/* set BCLK division for sample rate */
202
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
203
S3C2410_IISMOD_32FS);
204
if (ret < 0)
205
return ret;
206
207
/* set prescaler division for sample rate */
208
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
209
S3C24XX_PRESCALE(div, div));
210
if (ret < 0)
211
return ret;
212
213
return 0;
214
}
215
216
static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
217
{
218
struct snd_soc_codec *codec = rtd->codec;
219
struct snd_soc_dapm_context *dapm = &codec->dapm;
220
int err;
221
222
/* Add rx1950 specific widgets */
223
err = snd_soc_dapm_new_controls(dapm, uda1380_dapm_widgets,
224
ARRAY_SIZE(uda1380_dapm_widgets));
225
226
if (err)
227
return err;
228
229
/* Set up rx1950 specific audio path audio_mapnects */
230
err = snd_soc_dapm_add_routes(dapm, audio_map,
231
ARRAY_SIZE(audio_map));
232
233
if (err)
234
return err;
235
236
snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
237
snd_soc_dapm_enable_pin(dapm, "Speaker");
238
snd_soc_dapm_enable_pin(dapm, "Mic Jack");
239
240
snd_soc_dapm_sync(dapm);
241
242
snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
243
&hp_jack);
244
245
snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
246
hp_jack_pins);
247
248
snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
249
hp_jack_gpios);
250
251
return 0;
252
}
253
254
static int __init rx1950_init(void)
255
{
256
int ret;
257
258
if (!machine_is_rx1950())
259
return -ENODEV;
260
261
/* configure some gpios */
262
ret = gpio_request(S3C2410_GPA(1), "speaker-power");
263
if (ret)
264
goto err_gpio;
265
266
ret = gpio_direction_output(S3C2410_GPA(1), 0);
267
if (ret)
268
goto err_gpio_conf;
269
270
s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
271
if (!s3c24xx_snd_device) {
272
ret = -ENOMEM;
273
goto err_plat_alloc;
274
}
275
276
platform_set_drvdata(s3c24xx_snd_device, &rx1950_asoc);
277
ret = platform_device_add(s3c24xx_snd_device);
278
279
if (ret) {
280
platform_device_put(s3c24xx_snd_device);
281
goto err_plat_add;
282
}
283
284
return 0;
285
286
err_plat_add:
287
err_plat_alloc:
288
err_gpio_conf:
289
gpio_free(S3C2410_GPA(1));
290
291
err_gpio:
292
return ret;
293
}
294
295
static void __exit rx1950_exit(void)
296
{
297
platform_device_unregister(s3c24xx_snd_device);
298
snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
299
hp_jack_gpios);
300
gpio_free(S3C2410_GPA(1));
301
}
302
303
module_init(rx1950_init);
304
module_exit(rx1950_exit);
305
306
/* Module information */
307
MODULE_AUTHOR("Vasily Khoruzhick");
308
MODULE_DESCRIPTION("ALSA SoC RX1950");
309
MODULE_LICENSE("GPL");
310
311