Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/pxa/corgi.c
10817 views
1
/*
2
* corgi.c -- SoC audio for Corgi
3
*
4
* Copyright 2005 Wolfson Microelectronics PLC.
5
* Copyright 2005 Openedhand Ltd.
6
*
7
* Authors: Liam Girdwood <[email protected]>
8
* Richard Purdie <[email protected]>
9
*
10
* This program is free software; you can redistribute it and/or modify it
11
* under the terms of the GNU General Public License as published by the
12
* Free Software Foundation; either version 2 of the License, or (at your
13
* option) any later version.
14
*/
15
16
#include <linux/module.h>
17
#include <linux/moduleparam.h>
18
#include <linux/timer.h>
19
#include <linux/i2c.h>
20
#include <linux/interrupt.h>
21
#include <linux/platform_device.h>
22
#include <linux/gpio.h>
23
#include <sound/core.h>
24
#include <sound/pcm.h>
25
#include <sound/soc.h>
26
27
#include <asm/mach-types.h>
28
#include <mach/corgi.h>
29
#include <mach/audio.h>
30
31
#include "../codecs/wm8731.h"
32
#include "pxa2xx-i2s.h"
33
34
#define CORGI_HP 0
35
#define CORGI_MIC 1
36
#define CORGI_LINE 2
37
#define CORGI_HEADSET 3
38
#define CORGI_HP_OFF 4
39
#define CORGI_SPK_ON 0
40
#define CORGI_SPK_OFF 1
41
42
/* audio clock in Hz - rounded from 12.235MHz */
43
#define CORGI_AUDIO_CLOCK 12288000
44
45
static int corgi_jack_func;
46
static int corgi_spk_func;
47
48
static void corgi_ext_control(struct snd_soc_codec *codec)
49
{
50
struct snd_soc_dapm_context *dapm = &codec->dapm;
51
52
/* set up jack connection */
53
switch (corgi_jack_func) {
54
case CORGI_HP:
55
/* set = unmute headphone */
56
gpio_set_value(CORGI_GPIO_MUTE_L, 1);
57
gpio_set_value(CORGI_GPIO_MUTE_R, 1);
58
snd_soc_dapm_disable_pin(dapm, "Mic Jack");
59
snd_soc_dapm_disable_pin(dapm, "Line Jack");
60
snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
61
snd_soc_dapm_disable_pin(dapm, "Headset Jack");
62
break;
63
case CORGI_MIC:
64
/* reset = mute headphone */
65
gpio_set_value(CORGI_GPIO_MUTE_L, 0);
66
gpio_set_value(CORGI_GPIO_MUTE_R, 0);
67
snd_soc_dapm_enable_pin(dapm, "Mic Jack");
68
snd_soc_dapm_disable_pin(dapm, "Line Jack");
69
snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
70
snd_soc_dapm_disable_pin(dapm, "Headset Jack");
71
break;
72
case CORGI_LINE:
73
gpio_set_value(CORGI_GPIO_MUTE_L, 0);
74
gpio_set_value(CORGI_GPIO_MUTE_R, 0);
75
snd_soc_dapm_disable_pin(dapm, "Mic Jack");
76
snd_soc_dapm_enable_pin(dapm, "Line Jack");
77
snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
78
snd_soc_dapm_disable_pin(dapm, "Headset Jack");
79
break;
80
case CORGI_HEADSET:
81
gpio_set_value(CORGI_GPIO_MUTE_L, 0);
82
gpio_set_value(CORGI_GPIO_MUTE_R, 1);
83
snd_soc_dapm_enable_pin(dapm, "Mic Jack");
84
snd_soc_dapm_disable_pin(dapm, "Line Jack");
85
snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
86
snd_soc_dapm_enable_pin(dapm, "Headset Jack");
87
break;
88
}
89
90
if (corgi_spk_func == CORGI_SPK_ON)
91
snd_soc_dapm_enable_pin(dapm, "Ext Spk");
92
else
93
snd_soc_dapm_disable_pin(dapm, "Ext Spk");
94
95
/* signal a DAPM event */
96
snd_soc_dapm_sync(dapm);
97
}
98
99
static int corgi_startup(struct snd_pcm_substream *substream)
100
{
101
struct snd_soc_pcm_runtime *rtd = substream->private_data;
102
struct snd_soc_codec *codec = rtd->codec;
103
104
mutex_lock(&codec->mutex);
105
106
/* check the jack status at stream startup */
107
corgi_ext_control(codec);
108
109
mutex_unlock(&codec->mutex);
110
111
return 0;
112
}
113
114
/* we need to unmute the HP at shutdown as the mute burns power on corgi */
115
static void corgi_shutdown(struct snd_pcm_substream *substream)
116
{
117
/* set = unmute headphone */
118
gpio_set_value(CORGI_GPIO_MUTE_L, 1);
119
gpio_set_value(CORGI_GPIO_MUTE_R, 1);
120
}
121
122
static int corgi_hw_params(struct snd_pcm_substream *substream,
123
struct snd_pcm_hw_params *params)
124
{
125
struct snd_soc_pcm_runtime *rtd = substream->private_data;
126
struct snd_soc_dai *codec_dai = rtd->codec_dai;
127
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
128
unsigned int clk = 0;
129
int ret = 0;
130
131
switch (params_rate(params)) {
132
case 8000:
133
case 16000:
134
case 48000:
135
case 96000:
136
clk = 12288000;
137
break;
138
case 11025:
139
case 22050:
140
case 44100:
141
clk = 11289600;
142
break;
143
}
144
145
/* set codec DAI configuration */
146
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
147
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
148
if (ret < 0)
149
return ret;
150
151
/* set cpu DAI configuration */
152
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
153
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
154
if (ret < 0)
155
return ret;
156
157
/* set the codec system clock for DAC and ADC */
158
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk,
159
SND_SOC_CLOCK_IN);
160
if (ret < 0)
161
return ret;
162
163
/* set the I2S system clock as input (unused) */
164
ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
165
SND_SOC_CLOCK_IN);
166
if (ret < 0)
167
return ret;
168
169
return 0;
170
}
171
172
static struct snd_soc_ops corgi_ops = {
173
.startup = corgi_startup,
174
.hw_params = corgi_hw_params,
175
.shutdown = corgi_shutdown,
176
};
177
178
static int corgi_get_jack(struct snd_kcontrol *kcontrol,
179
struct snd_ctl_elem_value *ucontrol)
180
{
181
ucontrol->value.integer.value[0] = corgi_jack_func;
182
return 0;
183
}
184
185
static int corgi_set_jack(struct snd_kcontrol *kcontrol,
186
struct snd_ctl_elem_value *ucontrol)
187
{
188
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
189
190
if (corgi_jack_func == ucontrol->value.integer.value[0])
191
return 0;
192
193
corgi_jack_func = ucontrol->value.integer.value[0];
194
corgi_ext_control(codec);
195
return 1;
196
}
197
198
static int corgi_get_spk(struct snd_kcontrol *kcontrol,
199
struct snd_ctl_elem_value *ucontrol)
200
{
201
ucontrol->value.integer.value[0] = corgi_spk_func;
202
return 0;
203
}
204
205
static int corgi_set_spk(struct snd_kcontrol *kcontrol,
206
struct snd_ctl_elem_value *ucontrol)
207
{
208
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
209
210
if (corgi_spk_func == ucontrol->value.integer.value[0])
211
return 0;
212
213
corgi_spk_func = ucontrol->value.integer.value[0];
214
corgi_ext_control(codec);
215
return 1;
216
}
217
218
static int corgi_amp_event(struct snd_soc_dapm_widget *w,
219
struct snd_kcontrol *k, int event)
220
{
221
gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
222
return 0;
223
}
224
225
static int corgi_mic_event(struct snd_soc_dapm_widget *w,
226
struct snd_kcontrol *k, int event)
227
{
228
gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
229
return 0;
230
}
231
232
/* corgi machine dapm widgets */
233
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
234
SND_SOC_DAPM_HP("Headphone Jack", NULL),
235
SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
236
SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
237
SND_SOC_DAPM_LINE("Line Jack", NULL),
238
SND_SOC_DAPM_HP("Headset Jack", NULL),
239
};
240
241
/* Corgi machine audio map (connections to the codec pins) */
242
static const struct snd_soc_dapm_route audio_map[] = {
243
244
/* headset Jack - in = micin, out = LHPOUT*/
245
{"Headset Jack", NULL, "LHPOUT"},
246
247
/* headphone connected to LHPOUT1, RHPOUT1 */
248
{"Headphone Jack", NULL, "LHPOUT"},
249
{"Headphone Jack", NULL, "RHPOUT"},
250
251
/* speaker connected to LOUT, ROUT */
252
{"Ext Spk", NULL, "ROUT"},
253
{"Ext Spk", NULL, "LOUT"},
254
255
/* mic is connected to MICIN (via right channel of headphone jack) */
256
{"MICIN", NULL, "Mic Jack"},
257
258
/* Same as the above but no mic bias for line signals */
259
{"MICIN", NULL, "Line Jack"},
260
};
261
262
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
263
"Off"};
264
static const char *spk_function[] = {"On", "Off"};
265
static const struct soc_enum corgi_enum[] = {
266
SOC_ENUM_SINGLE_EXT(5, jack_function),
267
SOC_ENUM_SINGLE_EXT(2, spk_function),
268
};
269
270
static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
271
SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
272
corgi_set_jack),
273
SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
274
corgi_set_spk),
275
};
276
277
/*
278
* Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
279
*/
280
static int corgi_wm8731_init(struct snd_soc_pcm_runtime *rtd)
281
{
282
struct snd_soc_codec *codec = rtd->codec;
283
struct snd_soc_dapm_context *dapm = &codec->dapm;
284
int err;
285
286
snd_soc_dapm_nc_pin(dapm, "LLINEIN");
287
snd_soc_dapm_nc_pin(dapm, "RLINEIN");
288
289
/* Add corgi specific controls */
290
err = snd_soc_add_controls(codec, wm8731_corgi_controls,
291
ARRAY_SIZE(wm8731_corgi_controls));
292
if (err < 0)
293
return err;
294
295
/* Add corgi specific widgets */
296
snd_soc_dapm_new_controls(dapm, wm8731_dapm_widgets,
297
ARRAY_SIZE(wm8731_dapm_widgets));
298
299
/* Set up corgi specific audio path audio_map */
300
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
301
302
snd_soc_dapm_sync(dapm);
303
return 0;
304
}
305
306
/* corgi digital audio interface glue - connects codec <--> CPU */
307
static struct snd_soc_dai_link corgi_dai = {
308
.name = "WM8731",
309
.stream_name = "WM8731",
310
.cpu_dai_name = "pxa2xx-i2s",
311
.codec_dai_name = "wm8731-hifi",
312
.platform_name = "pxa-pcm-audio",
313
.codec_name = "wm8731.0-001b",
314
.init = corgi_wm8731_init,
315
.ops = &corgi_ops,
316
};
317
318
/* corgi audio machine driver */
319
static struct snd_soc_card snd_soc_corgi = {
320
.name = "Corgi",
321
.dai_link = &corgi_dai,
322
.num_links = 1,
323
};
324
325
static struct platform_device *corgi_snd_device;
326
327
static int __init corgi_init(void)
328
{
329
int ret;
330
331
if (!(machine_is_corgi() || machine_is_shepherd() ||
332
machine_is_husky()))
333
return -ENODEV;
334
335
corgi_snd_device = platform_device_alloc("soc-audio", -1);
336
if (!corgi_snd_device)
337
return -ENOMEM;
338
339
platform_set_drvdata(corgi_snd_device, &snd_soc_corgi);
340
ret = platform_device_add(corgi_snd_device);
341
342
if (ret)
343
platform_device_put(corgi_snd_device);
344
345
return ret;
346
}
347
348
static void __exit corgi_exit(void)
349
{
350
platform_device_unregister(corgi_snd_device);
351
}
352
353
module_init(corgi_init);
354
module_exit(corgi_exit);
355
356
/* Module information */
357
MODULE_AUTHOR("Richard Purdie");
358
MODULE_DESCRIPTION("ALSA SoC Corgi");
359
MODULE_LICENSE("GPL");
360
361