aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/pxa/tosa.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/pxa/tosa.c')
-rw-r--r--sound/soc/pxa/tosa.c287
1 files changed, 287 insertions, 0 deletions
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
new file mode 100644
index 000000000000..8c3c6b0534d6
--- /dev/null
+++ b/sound/soc/pxa/tosa.c
@@ -0,0 +1,287 @@
1/*
2 * tosa.c -- SoC audio for Tosa
3 *
4 * Copyright 2005 Wolfson Microelectronics PLC.
5 * Copyright 2005 Openedhand Ltd.
6 *
7 * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
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 * Revision history
16 * 30th Nov 2005 Initial version.
17 *
18 * GPIO's
19 * 1 - Jack Insertion
20 * 5 - Hookswitch (headset answer/hang up switch)
21 *
22 */
23
24#include <linux/module.h>
25#include <linux/moduleparam.h>
26#include <linux/device.h>
27
28#include <sound/driver.h>
29#include <sound/core.h>
30#include <sound/pcm.h>
31#include <sound/soc.h>
32#include <sound/soc-dapm.h>
33
34#include <asm/mach-types.h>
35#include <asm/hardware/tmio.h>
36#include <asm/arch/pxa-regs.h>
37#include <asm/arch/hardware.h>
38#include <asm/arch/audio.h>
39#include <asm/arch/tosa.h>
40
41#include "../codecs/wm9712.h"
42#include "pxa2xx-pcm.h"
43
44static struct snd_soc_machine tosa;
45
46#define TOSA_HP 0
47#define TOSA_MIC_INT 1
48#define TOSA_HEADSET 2
49#define TOSA_HP_OFF 3
50#define TOSA_SPK_ON 0
51#define TOSA_SPK_OFF 1
52
53static int tosa_jack_func;
54static int tosa_spk_func;
55
56static void tosa_ext_control(struct snd_soc_codec *codec)
57{
58 int spk = 0, mic_int = 0, hp = 0, hs = 0;
59
60 /* set up jack connection */
61 switch (tosa_jack_func) {
62 case TOSA_HP:
63 hp = 1;
64 break;
65 case TOSA_MIC_INT:
66 mic_int = 1;
67 break;
68 case TOSA_HEADSET:
69 hs = 1;
70 break;
71 }
72
73 if (tosa_spk_func == TOSA_SPK_ON)
74 spk = 1;
75
76 snd_soc_dapm_set_endpoint(codec, "Speaker", spk);
77 snd_soc_dapm_set_endpoint(codec, "Mic (Internal)", mic_int);
78 snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
79 snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
80 snd_soc_dapm_sync_endpoints(codec);
81}
82
83static int tosa_startup(struct snd_pcm_substream *substream)
84{
85 struct snd_soc_pcm_runtime *rtd = substream->private_data;
86 struct snd_soc_codec *codec = rtd->socdev->codec;
87
88 /* check the jack status at stream startup */
89 tosa_ext_control(codec);
90 return 0;
91}
92
93static struct snd_soc_ops tosa_ops = {
94 .startup = tosa_startup,
95};
96
97static int tosa_get_jack(struct snd_kcontrol *kcontrol,
98 struct snd_ctl_elem_value *ucontrol)
99{
100 ucontrol->value.integer.value[0] = tosa_jack_func;
101 return 0;
102}
103
104static int tosa_set_jack(struct snd_kcontrol *kcontrol,
105 struct snd_ctl_elem_value *ucontrol)
106{
107 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
108
109 if (tosa_jack_func == ucontrol->value.integer.value[0])
110 return 0;
111
112 tosa_jack_func = ucontrol->value.integer.value[0];
113 tosa_ext_control(codec);
114 return 1;
115}
116
117static int tosa_get_spk(struct snd_kcontrol *kcontrol,
118 struct snd_ctl_elem_value *ucontrol)
119{
120 ucontrol->value.integer.value[0] = tosa_spk_func;
121 return 0;
122}
123
124static int tosa_set_spk(struct snd_kcontrol *kcontrol,
125 struct snd_ctl_elem_value *ucontrol)
126{
127 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
128
129 if (tosa_spk_func == ucontrol->value.integer.value[0])
130 return 0;
131
132 tosa_spk_func = ucontrol->value.integer.value[0];
133 tosa_ext_control(codec);
134 return 1;
135}
136
137/* tosa dapm event handlers */
138static int tosa_hp_event(struct snd_soc_dapm_widget *w, int event)
139{
140 if (SND_SOC_DAPM_EVENT_ON(event))
141 set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
142 else
143 reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
144 return 0;
145}
146
147/* tosa machine dapm widgets */
148static const struct snd_soc_dapm_widget tosa_dapm_widgets[] = {
149SND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event),
150SND_SOC_DAPM_HP("Headset Jack", NULL),
151SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
152SND_SOC_DAPM_SPK("Speaker", NULL),
153};
154
155/* tosa audio map */
156static const char *audio_map[][3] = {
157
158 /* headphone connected to HPOUTL, HPOUTR */
159 {"Headphone Jack", NULL, "HPOUTL"},
160 {"Headphone Jack", NULL, "HPOUTR"},
161
162 /* ext speaker connected to LOUT2, ROUT2 */
163 {"Speaker", NULL, "LOUT2"},
164 {"Speaker", NULL, "ROUT2"},
165
166 /* internal mic is connected to mic1, mic2 differential - with bias */
167 {"MIC1", NULL, "Mic Bias"},
168 {"MIC2", NULL, "Mic Bias"},
169 {"Mic Bias", NULL, "Mic (Internal)"},
170
171 /* headset is connected to HPOUTR, and LINEINR with bias */
172 {"Headset Jack", NULL, "HPOUTR"},
173 {"LINEINR", NULL, "Mic Bias"},
174 {"Mic Bias", NULL, "Headset Jack"},
175
176 {NULL, NULL, NULL},
177};
178
179static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
180 "Off"};
181static const char *spk_function[] = {"On", "Off"};
182static const struct soc_enum tosa_enum[] = {
183 SOC_ENUM_SINGLE_EXT(5, jack_function),
184 SOC_ENUM_SINGLE_EXT(2, spk_function),
185};
186
187static const struct snd_kcontrol_new tosa_controls[] = {
188 SOC_ENUM_EXT("Jack Function", tosa_enum[0], tosa_get_jack,
189 tosa_set_jack),
190 SOC_ENUM_EXT("Speaker Function", tosa_enum[1], tosa_get_spk,
191 tosa_set_spk),
192};
193
194static int tosa_ac97_init(struct snd_soc_codec *codec)
195{
196 int i, err;
197
198 snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
199 snd_soc_dapm_set_endpoint(codec, "MONOOUT", 0);
200
201 /* add tosa specific controls */
202 for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) {
203 err = snd_ctl_add(codec->card,
204 snd_soc_cnew(&tosa_controls[i],codec, NULL));
205 if (err < 0)
206 return err;
207 }
208
209 /* add tosa specific widgets */
210 for (i = 0; i < ARRAY_SIZE(tosa_dapm_widgets); i++) {
211 snd_soc_dapm_new_control(codec, &tosa_dapm_widgets[i]);
212 }
213
214 /* set up tosa specific audio path audio_map */
215 for (i = 0; audio_map[i][0] != NULL; i++) {
216 snd_soc_dapm_connect_input(codec, audio_map[i][0],
217 audio_map[i][1], audio_map[i][2]);
218 }
219
220 snd_soc_dapm_sync_endpoints(codec);
221 return 0;
222}
223
224static struct snd_soc_dai_link tosa_dai[] = {
225{
226 .name = "AC97",
227 .stream_name = "AC97 HiFi",
228 .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
229 .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
230 .init = tosa_ac97_init,
231},
232{
233 .name = "AC97 Aux",
234 .stream_name = "AC97 Aux",
235 .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
236 .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
237},
238};
239
240static struct snd_soc_machine tosa = {
241 .name = "Tosa",
242 .dai_link = tosa_dai,
243 .num_links = ARRAY_SIZE(tosa_dai),
244 .ops = &tosa_ops,
245};
246
247static struct snd_soc_device tosa_snd_devdata = {
248 .machine = &tosa,
249 .platform = &pxa2xx_soc_platform,
250 .codec_dev = &soc_codec_dev_wm9712,
251};
252
253static struct platform_device *tosa_snd_device;
254
255static int __init tosa_init(void)
256{
257 int ret;
258
259 if (!machine_is_tosa())
260 return -ENODEV;
261
262 tosa_snd_device = platform_device_alloc("soc-audio", -1);
263 if (!tosa_snd_device)
264 return -ENOMEM;
265
266 platform_set_drvdata(tosa_snd_device, &tosa_snd_devdata);
267 tosa_snd_devdata.dev = &tosa_snd_device->dev;
268 ret = platform_device_add(tosa_snd_device);
269
270 if (ret)
271 platform_device_put(tosa_snd_device);
272
273 return ret;
274}
275
276static void __exit tosa_exit(void)
277{
278 platform_device_unregister(tosa_snd_device);
279}
280
281module_init(tosa_init);
282module_exit(tosa_exit);
283
284/* Module information */
285MODULE_AUTHOR("Richard Purdie");
286MODULE_DESCRIPTION("ALSA SoC Tosa");
287MODULE_LICENSE("GPL");