• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/sound/soc/pxa/
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 <lrg@slimlogic.co.uk>
8 *          Richard Purdie <richard@openedhand.com>
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#include <sound/soc-dapm.h>
27
28#include <asm/mach-types.h>
29#include <mach/corgi.h>
30#include <mach/audio.h>
31
32#include "../codecs/wm8731.h"
33#include "pxa2xx-pcm.h"
34#include "pxa2xx-i2s.h"
35
36#define CORGI_HP        0
37#define CORGI_MIC       1
38#define CORGI_LINE      2
39#define CORGI_HEADSET   3
40#define CORGI_HP_OFF    4
41#define CORGI_SPK_ON    0
42#define CORGI_SPK_OFF   1
43
44 /* audio clock in Hz - rounded from 12.235MHz */
45#define CORGI_AUDIO_CLOCK 12288000
46
47static int corgi_jack_func;
48static int corgi_spk_func;
49
50static void corgi_ext_control(struct snd_soc_codec *codec)
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(codec, "Mic Jack");
59		snd_soc_dapm_disable_pin(codec, "Line Jack");
60		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
61		snd_soc_dapm_disable_pin(codec, "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(codec, "Mic Jack");
68		snd_soc_dapm_disable_pin(codec, "Line Jack");
69		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
70		snd_soc_dapm_disable_pin(codec, "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(codec, "Mic Jack");
76		snd_soc_dapm_enable_pin(codec, "Line Jack");
77		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
78		snd_soc_dapm_disable_pin(codec, "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(codec, "Mic Jack");
84		snd_soc_dapm_disable_pin(codec, "Line Jack");
85		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
86		snd_soc_dapm_enable_pin(codec, "Headset Jack");
87		break;
88	}
89
90	if (corgi_spk_func == CORGI_SPK_ON)
91		snd_soc_dapm_enable_pin(codec, "Ext Spk");
92	else
93		snd_soc_dapm_disable_pin(codec, "Ext Spk");
94
95	/* signal a DAPM event */
96	snd_soc_dapm_sync(codec);
97}
98
99static 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->socdev->card->codec;
103
104	/* check the jack status at stream startup */
105	corgi_ext_control(codec);
106	return 0;
107}
108
109/* we need to unmute the HP at shutdown as the mute burns power on corgi */
110static void corgi_shutdown(struct snd_pcm_substream *substream)
111{
112	/* set = unmute headphone */
113	gpio_set_value(CORGI_GPIO_MUTE_L, 1);
114	gpio_set_value(CORGI_GPIO_MUTE_R, 1);
115}
116
117static int corgi_hw_params(struct snd_pcm_substream *substream,
118	struct snd_pcm_hw_params *params)
119{
120	struct snd_soc_pcm_runtime *rtd = substream->private_data;
121	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
122	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
123	unsigned int clk = 0;
124	int ret = 0;
125
126	switch (params_rate(params)) {
127	case 8000:
128	case 16000:
129	case 48000:
130	case 96000:
131		clk = 12288000;
132		break;
133	case 11025:
134	case 22050:
135	case 44100:
136		clk = 11289600;
137		break;
138	}
139
140	/* set codec DAI configuration */
141	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
142		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
143	if (ret < 0)
144		return ret;
145
146	/* set cpu DAI configuration */
147	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
148		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
149	if (ret < 0)
150		return ret;
151
152	/* set the codec system clock for DAC and ADC */
153	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk,
154		SND_SOC_CLOCK_IN);
155	if (ret < 0)
156		return ret;
157
158	/* set the I2S system clock as input (unused) */
159	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
160		SND_SOC_CLOCK_IN);
161	if (ret < 0)
162		return ret;
163
164	return 0;
165}
166
167static struct snd_soc_ops corgi_ops = {
168	.startup = corgi_startup,
169	.hw_params = corgi_hw_params,
170	.shutdown = corgi_shutdown,
171};
172
173static int corgi_get_jack(struct snd_kcontrol *kcontrol,
174	struct snd_ctl_elem_value *ucontrol)
175{
176	ucontrol->value.integer.value[0] = corgi_jack_func;
177	return 0;
178}
179
180static int corgi_set_jack(struct snd_kcontrol *kcontrol,
181	struct snd_ctl_elem_value *ucontrol)
182{
183	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
184
185	if (corgi_jack_func == ucontrol->value.integer.value[0])
186		return 0;
187
188	corgi_jack_func = ucontrol->value.integer.value[0];
189	corgi_ext_control(codec);
190	return 1;
191}
192
193static int corgi_get_spk(struct snd_kcontrol *kcontrol,
194	struct snd_ctl_elem_value *ucontrol)
195{
196	ucontrol->value.integer.value[0] = corgi_spk_func;
197	return 0;
198}
199
200static int corgi_set_spk(struct snd_kcontrol *kcontrol,
201	struct snd_ctl_elem_value *ucontrol)
202{
203	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
204
205	if (corgi_spk_func == ucontrol->value.integer.value[0])
206		return 0;
207
208	corgi_spk_func = ucontrol->value.integer.value[0];
209	corgi_ext_control(codec);
210	return 1;
211}
212
213static int corgi_amp_event(struct snd_soc_dapm_widget *w,
214	struct snd_kcontrol *k, int event)
215{
216	gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
217	return 0;
218}
219
220static int corgi_mic_event(struct snd_soc_dapm_widget *w,
221	struct snd_kcontrol *k, int event)
222{
223	gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
224	return 0;
225}
226
227/* corgi machine dapm widgets */
228static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
229SND_SOC_DAPM_HP("Headphone Jack", NULL),
230SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
231SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
232SND_SOC_DAPM_LINE("Line Jack", NULL),
233SND_SOC_DAPM_HP("Headset Jack", NULL),
234};
235
236/* Corgi machine audio map (connections to the codec pins) */
237static const struct snd_soc_dapm_route audio_map[] = {
238
239	/* headset Jack  - in = micin, out = LHPOUT*/
240	{"Headset Jack", NULL, "LHPOUT"},
241
242	/* headphone connected to LHPOUT1, RHPOUT1 */
243	{"Headphone Jack", NULL, "LHPOUT"},
244	{"Headphone Jack", NULL, "RHPOUT"},
245
246	/* speaker connected to LOUT, ROUT */
247	{"Ext Spk", NULL, "ROUT"},
248	{"Ext Spk", NULL, "LOUT"},
249
250	/* mic is connected to MICIN (via right channel of headphone jack) */
251	{"MICIN", NULL, "Mic Jack"},
252
253	/* Same as the above but no mic bias for line signals */
254	{"MICIN", NULL, "Line Jack"},
255};
256
257static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
258	"Off"};
259static const char *spk_function[] = {"On", "Off"};
260static const struct soc_enum corgi_enum[] = {
261	SOC_ENUM_SINGLE_EXT(5, jack_function),
262	SOC_ENUM_SINGLE_EXT(2, spk_function),
263};
264
265static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
266	SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
267		corgi_set_jack),
268	SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
269		corgi_set_spk),
270};
271
272/*
273 * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
274 */
275static int corgi_wm8731_init(struct snd_soc_codec *codec)
276{
277	int err;
278
279	snd_soc_dapm_nc_pin(codec, "LLINEIN");
280	snd_soc_dapm_nc_pin(codec, "RLINEIN");
281
282	/* Add corgi specific controls */
283	err = snd_soc_add_controls(codec, wm8731_corgi_controls,
284				ARRAY_SIZE(wm8731_corgi_controls));
285	if (err < 0)
286		return err;
287
288	/* Add corgi specific widgets */
289	snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
290				  ARRAY_SIZE(wm8731_dapm_widgets));
291
292	/* Set up corgi specific audio path audio_map */
293	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
294
295	snd_soc_dapm_sync(codec);
296	return 0;
297}
298
299/* corgi digital audio interface glue - connects codec <--> CPU */
300static struct snd_soc_dai_link corgi_dai = {
301	.name = "WM8731",
302	.stream_name = "WM8731",
303	.cpu_dai = &pxa_i2s_dai,
304	.codec_dai = &wm8731_dai,
305	.init = corgi_wm8731_init,
306	.ops = &corgi_ops,
307};
308
309/* corgi audio machine driver */
310static struct snd_soc_card snd_soc_corgi = {
311	.name = "Corgi",
312	.platform = &pxa2xx_soc_platform,
313	.dai_link = &corgi_dai,
314	.num_links = 1,
315};
316
317/* corgi audio subsystem */
318static struct snd_soc_device corgi_snd_devdata = {
319	.card = &snd_soc_corgi,
320	.codec_dev = &soc_codec_dev_wm8731,
321};
322
323static struct platform_device *corgi_snd_device;
324
325static int __init corgi_init(void)
326{
327	int ret;
328
329	if (!(machine_is_corgi() || machine_is_shepherd() ||
330	      machine_is_husky()))
331		return -ENODEV;
332
333	corgi_snd_device = platform_device_alloc("soc-audio", -1);
334	if (!corgi_snd_device)
335		return -ENOMEM;
336
337	platform_set_drvdata(corgi_snd_device, &corgi_snd_devdata);
338	corgi_snd_devdata.dev = &corgi_snd_device->dev;
339	ret = platform_device_add(corgi_snd_device);
340
341	if (ret)
342		platform_device_put(corgi_snd_device);
343
344	return ret;
345}
346
347static void __exit corgi_exit(void)
348{
349	platform_device_unregister(corgi_snd_device);
350}
351
352module_init(corgi_init);
353module_exit(corgi_exit);
354
355/* Module information */
356MODULE_AUTHOR("Richard Purdie");
357MODULE_DESCRIPTION("ALSA SoC Corgi");
358MODULE_LICENSE("GPL");
359