Merge branch 'pxa-tosa' into pxa
[pandora-kernel.git] / sound / soc / pxa / tosa.c
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 #include <linux/gpio.h>
28
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/arch/tosa.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 #include "pxa2xx-ac97.h"
44
45 static struct snd_soc_machine tosa;
46
47 #define TOSA_HP        0
48 #define TOSA_MIC_INT   1
49 #define TOSA_HEADSET   2
50 #define TOSA_HP_OFF    3
51 #define TOSA_SPK_ON    0
52 #define TOSA_SPK_OFF   1
53
54 static int tosa_jack_func;
55 static int tosa_spk_func;
56
57 static void tosa_ext_control(struct snd_soc_codec *codec)
58 {
59         int spk = 0, mic_int = 0, hp = 0, hs = 0;
60
61         /* set up jack connection */
62         switch (tosa_jack_func) {
63         case TOSA_HP:
64                 hp = 1;
65                 break;
66         case TOSA_MIC_INT:
67                 mic_int = 1;
68                 break;
69         case TOSA_HEADSET:
70                 hs = 1;
71                 break;
72         }
73
74         if (tosa_spk_func == TOSA_SPK_ON)
75                 spk = 1;
76
77         snd_soc_dapm_set_endpoint(codec, "Speaker", spk);
78         snd_soc_dapm_set_endpoint(codec, "Mic (Internal)", mic_int);
79         snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
80         snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
81         snd_soc_dapm_sync_endpoints(codec);
82 }
83
84 static int tosa_startup(struct snd_pcm_substream *substream)
85 {
86         struct snd_soc_pcm_runtime *rtd = substream->private_data;
87         struct snd_soc_codec *codec = rtd->socdev->codec;
88
89         /* check the jack status at stream startup */
90         tosa_ext_control(codec);
91         return 0;
92 }
93
94 static struct snd_soc_ops tosa_ops = {
95         .startup = tosa_startup,
96 };
97
98 static int tosa_get_jack(struct snd_kcontrol *kcontrol,
99         struct snd_ctl_elem_value *ucontrol)
100 {
101         ucontrol->value.integer.value[0] = tosa_jack_func;
102         return 0;
103 }
104
105 static int tosa_set_jack(struct snd_kcontrol *kcontrol,
106         struct snd_ctl_elem_value *ucontrol)
107 {
108         struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
109
110         if (tosa_jack_func == ucontrol->value.integer.value[0])
111                 return 0;
112
113         tosa_jack_func = ucontrol->value.integer.value[0];
114         tosa_ext_control(codec);
115         return 1;
116 }
117
118 static int tosa_get_spk(struct snd_kcontrol *kcontrol,
119         struct snd_ctl_elem_value *ucontrol)
120 {
121         ucontrol->value.integer.value[0] = tosa_spk_func;
122         return 0;
123 }
124
125 static int tosa_set_spk(struct snd_kcontrol *kcontrol,
126         struct snd_ctl_elem_value *ucontrol)
127 {
128         struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
129
130         if (tosa_spk_func == ucontrol->value.integer.value[0])
131                 return 0;
132
133         tosa_spk_func = ucontrol->value.integer.value[0];
134         tosa_ext_control(codec);
135         return 1;
136 }
137
138 /* tosa dapm event handlers */
139 static int tosa_hp_event(struct snd_soc_dapm_widget *w,
140         struct snd_kcontrol *k, int event)
141 {
142         gpio_set_value(TOSA_GPIO_L_MUTE, SND_SOC_DAPM_EVENT_ON(event) ? 1 :0);
143         return 0;
144 }
145
146 /* tosa machine dapm widgets */
147 static const struct snd_soc_dapm_widget tosa_dapm_widgets[] = {
148 SND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event),
149 SND_SOC_DAPM_HP("Headset Jack", NULL),
150 SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
151 SND_SOC_DAPM_SPK("Speaker", NULL),
152 };
153
154 /* tosa audio map */
155 static const char *audio_map[][3] = {
156
157         /* headphone connected to HPOUTL, HPOUTR */
158         {"Headphone Jack", NULL, "HPOUTL"},
159         {"Headphone Jack", NULL, "HPOUTR"},
160
161         /* ext speaker connected to LOUT2, ROUT2 */
162         {"Speaker", NULL, "LOUT2"},
163         {"Speaker", NULL, "ROUT2"},
164
165         /* internal mic is connected to mic1, mic2 differential - with bias */
166         {"MIC1", NULL, "Mic Bias"},
167         {"MIC2", NULL, "Mic Bias"},
168         {"Mic Bias", NULL, "Mic (Internal)"},
169
170         /* headset is connected to HPOUTR, and LINEINR with bias */
171         {"Headset Jack", NULL, "HPOUTR"},
172         {"LINEINR", NULL, "Mic Bias"},
173         {"Mic Bias", NULL, "Headset Jack"},
174
175         {NULL, NULL, NULL},
176 };
177
178 static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
179         "Off"};
180 static const char *spk_function[] = {"On", "Off"};
181 static const struct soc_enum tosa_enum[] = {
182         SOC_ENUM_SINGLE_EXT(5, jack_function),
183         SOC_ENUM_SINGLE_EXT(2, spk_function),
184 };
185
186 static const struct snd_kcontrol_new tosa_controls[] = {
187         SOC_ENUM_EXT("Jack Function", tosa_enum[0], tosa_get_jack,
188                 tosa_set_jack),
189         SOC_ENUM_EXT("Speaker Function", tosa_enum[1], tosa_get_spk,
190                 tosa_set_spk),
191 };
192
193 static int tosa_ac97_init(struct snd_soc_codec *codec)
194 {
195         int i, err;
196
197         snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
198         snd_soc_dapm_set_endpoint(codec, "MONOOUT", 0);
199
200         /* add tosa specific controls */
201         for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) {
202                 err = snd_ctl_add(codec->card,
203                                 snd_soc_cnew(&tosa_controls[i],codec, NULL));
204                 if (err < 0)
205                         return err;
206         }
207
208         /* add tosa specific widgets */
209         for (i = 0; i < ARRAY_SIZE(tosa_dapm_widgets); i++) {
210                 snd_soc_dapm_new_control(codec, &tosa_dapm_widgets[i]);
211         }
212
213         /* set up tosa specific audio path audio_map */
214         for (i = 0; audio_map[i][0] != NULL; i++) {
215                 snd_soc_dapm_connect_input(codec, audio_map[i][0],
216                         audio_map[i][1], audio_map[i][2]);
217         }
218
219         snd_soc_dapm_sync_endpoints(codec);
220         return 0;
221 }
222
223 static struct snd_soc_dai_link tosa_dai[] = {
224 {
225         .name = "AC97",
226         .stream_name = "AC97 HiFi",
227         .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
228         .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
229         .init = tosa_ac97_init,
230         .ops = &tosa_ops,
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         .ops = &tosa_ops,
238 },
239 };
240
241 static struct snd_soc_machine tosa = {
242         .name = "Tosa",
243         .dai_link = tosa_dai,
244         .num_links = ARRAY_SIZE(tosa_dai),
245 };
246
247 static struct snd_soc_device tosa_snd_devdata = {
248         .machine = &tosa,
249         .platform = &pxa2xx_soc_platform,
250         .codec_dev = &soc_codec_dev_wm9712,
251 };
252
253 static struct platform_device *tosa_snd_device;
254
255 static int __init tosa_init(void)
256 {
257         int ret;
258
259         if (!machine_is_tosa())
260                 return -ENODEV;
261
262         ret = gpio_request(TOSA_GPIO_L_MUTE, "Headphone Jack");
263         if (ret)
264                 return ret;
265         gpio_direction_output(TOSA_GPIO_L_MUTE, 0);
266
267         tosa_snd_device = platform_device_alloc("soc-audio", -1);
268         if (!tosa_snd_device) {
269                 ret = -ENOMEM;
270                 goto err_alloc;
271         }
272
273         platform_set_drvdata(tosa_snd_device, &tosa_snd_devdata);
274         tosa_snd_devdata.dev = &tosa_snd_device->dev;
275         ret = platform_device_add(tosa_snd_device);
276
277         if (!ret)
278                 return 0;
279
280         platform_device_put(tosa_snd_device);
281
282 err_alloc:
283         gpio_free(TOSA_GPIO_L_MUTE);
284
285         return ret;
286 }
287
288 static void __exit tosa_exit(void)
289 {
290         platform_device_unregister(tosa_snd_device);
291         gpio_free(TOSA_GPIO_L_MUTE);
292 }
293
294 module_init(tosa_init);
295 module_exit(tosa_exit);
296
297 /* Module information */
298 MODULE_AUTHOR("Richard Purdie");
299 MODULE_DESCRIPTION("ALSA SoC Tosa");
300 MODULE_LICENSE("GPL");