aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Zabel <philipp.zabel@gmail.com>2009-03-19 04:34:46 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-04-02 11:34:13 -0400
commit7377226c344a7295a7573dce400ce9ddd42f0ca4 (patch)
treea6a8253f4c0fd97408b9024e89430991ec62bd48
parent92429069d0fc9f52d436c9067c5b5c392e3f8876 (diff)
ASoC: Add Magician machine support
HTC Magician has a Philips UDA1380 codec connected via SSP1 (playback) and I2S (capture). There is a flip-flop between the SSP frame clock output and the codec's word select input pin. To make the codec see proper I2S input, the SSP has to send two frames per sample. Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--sound/soc/pxa/Kconfig10
-rw-r--r--sound/soc/pxa/Makefile2
-rw-r--r--sound/soc/pxa/magician.c560
3 files changed, 572 insertions, 0 deletions
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 5998ab366e83..ad8a10fe6298 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -116,6 +116,16 @@ config SND_SOC_ZYLONITE
116 Say Y if you want to add support for SoC audio on the 116 Say Y if you want to add support for SoC audio on the
117 Marvell Zylonite reference platform. 117 Marvell Zylonite reference platform.
118 118
119config SND_PXA2XX_SOC_MAGICIAN
120 tristate "SoC Audio support for HTC Magician"
121 depends on SND_PXA2XX_SOC && MACH_MAGICIAN
122 select SND_PXA2XX_SOC_I2S
123 select SND_PXA_SOC_SSP
124 select SND_SOC_UDA1380
125 help
126 Say Y if you want to add support for SoC audio on the
127 HTC Magician.
128
119config SND_PXA2XX_SOC_MIOA701 129config SND_PXA2XX_SOC_MIOA701
120 tristate "SoC Audio support for MIO A701" 130 tristate "SoC Audio support for MIO A701"
121 depends on SND_PXA2XX_SOC && MACH_MIOA701 131 depends on SND_PXA2XX_SOC && MACH_MIOA701
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 8ed881c5e5cc..4b90c3ccae45 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -20,6 +20,7 @@ snd-soc-spitz-objs := spitz.o
20snd-soc-em-x270-objs := em-x270.o 20snd-soc-em-x270-objs := em-x270.o
21snd-soc-palm27x-objs := palm27x.o 21snd-soc-palm27x-objs := palm27x.o
22snd-soc-zylonite-objs := zylonite.o 22snd-soc-zylonite-objs := zylonite.o
23snd-soc-magician-objs := magician.o
23snd-soc-mioa701-objs := mioa701_wm9713.o 24snd-soc-mioa701-objs := mioa701_wm9713.o
24 25
25obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o 26obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
@@ -31,5 +32,6 @@ obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
31obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o 32obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
32obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o 33obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
33obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o 34obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
35obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
34obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o 36obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
35obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o 37obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
new file mode 100644
index 000000000000..f7c4544f7859
--- /dev/null
+++ b/sound/soc/pxa/magician.c
@@ -0,0 +1,560 @@
1/*
2 * SoC audio for HTC Magician
3 *
4 * Copyright (c) 2006 Philipp Zabel <philipp.zabel@gmail.com>
5 *
6 * based on spitz.c,
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
17#include <linux/module.h>
18#include <linux/timer.h>
19#include <linux/interrupt.h>
20#include <linux/platform_device.h>
21#include <linux/delay.h>
22#include <linux/gpio.h>
23
24#include <sound/core.h>
25#include <sound/pcm.h>
26#include <sound/pcm_params.h>
27#include <sound/soc.h>
28#include <sound/soc-dapm.h>
29
30#include <mach/pxa-regs.h>
31#include <mach/hardware.h>
32#include <mach/magician.h>
33#include <asm/mach-types.h>
34#include "../codecs/uda1380.h"
35#include "pxa2xx-pcm.h"
36#include "pxa2xx-i2s.h"
37#include "pxa-ssp.h"
38
39#define MAGICIAN_MIC 0
40#define MAGICIAN_MIC_EXT 1
41
42static int magician_hp_switch;
43static int magician_spk_switch = 1;
44static int magician_in_sel = MAGICIAN_MIC;
45
46static void magician_ext_control(struct snd_soc_codec *codec)
47{
48 if (magician_spk_switch)
49 snd_soc_dapm_enable_pin(codec, "Speaker");
50 else
51 snd_soc_dapm_disable_pin(codec, "Speaker");
52 if (magician_hp_switch)
53 snd_soc_dapm_enable_pin(codec, "Headphone Jack");
54 else
55 snd_soc_dapm_disable_pin(codec, "Headphone Jack");
56
57 switch (magician_in_sel) {
58 case MAGICIAN_MIC:
59 snd_soc_dapm_disable_pin(codec, "Headset Mic");
60 snd_soc_dapm_enable_pin(codec, "Call Mic");
61 break;
62 case MAGICIAN_MIC_EXT:
63 snd_soc_dapm_disable_pin(codec, "Call Mic");
64 snd_soc_dapm_enable_pin(codec, "Headset Mic");
65 break;
66 }
67
68 snd_soc_dapm_sync(codec);
69}
70
71static int magician_startup(struct snd_pcm_substream *substream)
72{
73 struct snd_soc_pcm_runtime *rtd = substream->private_data;
74 struct snd_soc_codec *codec = rtd->socdev->card->codec;
75
76 /* check the jack status at stream startup */
77 magician_ext_control(codec);
78
79 return 0;
80}
81
82/*
83 * Magician uses SSP port for playback.
84 */
85static int magician_playback_hw_params(struct snd_pcm_substream *substream,
86 struct snd_pcm_hw_params *params)
87{
88 struct snd_soc_pcm_runtime *rtd = substream->private_data;
89 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
90 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
91 unsigned int acps, acds, width, rate;
92 unsigned int div4 = PXA_SSP_CLK_SCDB_4;
93 int ret = 0;
94
95 rate = params_rate(params);
96 width = snd_pcm_format_physical_width(params_format(params));
97
98 /*
99 * rate = SSPSCLK / (2 * width(16 or 32))
100 * SSPSCLK = (ACPS / ACDS) / SSPSCLKDIV(div4 or div1)
101 */
102 switch (params_rate(params)) {
103 case 8000:
104 /* off by a factor of 2: bug in the PXA27x audio clock? */
105 acps = 32842000;
106 switch (width) {
107 case 16:
108 /* 513156 Hz ~= _2_ * 8000 Hz * 32 (+0.23%) */
109 acds = PXA_SSP_CLK_AUDIO_DIV_16;
110 break;
111 case 32:
112 /* 1026312 Hz ~= _2_ * 8000 Hz * 64 (+0.23%) */
113 acds = PXA_SSP_CLK_AUDIO_DIV_8;
114 }
115 break;
116 case 11025:
117 acps = 5622000;
118 switch (width) {
119 case 16:
120 /* 351375 Hz ~= 11025 Hz * 32 (-0.41%) */
121 acds = PXA_SSP_CLK_AUDIO_DIV_4;
122 break;
123 case 32:
124 /* 702750 Hz ~= 11025 Hz * 64 (-0.41%) */
125 acds = PXA_SSP_CLK_AUDIO_DIV_2;
126 }
127 break;
128 case 22050:
129 acps = 5622000;
130 switch (width) {
131 case 16:
132 /* 702750 Hz ~= 22050 Hz * 32 (-0.41%) */
133 acds = PXA_SSP_CLK_AUDIO_DIV_2;
134 break;
135 case 32:
136 /* 1405500 Hz ~= 22050 Hz * 64 (-0.41%) */
137 acds = PXA_SSP_CLK_AUDIO_DIV_1;
138 }
139 break;
140 case 44100:
141 acps = 5622000;
142 switch (width) {
143 case 16:
144 /* 1405500 Hz ~= 44100 Hz * 32 (-0.41%) */
145 acds = PXA_SSP_CLK_AUDIO_DIV_2;
146 break;
147 case 32:
148 /* 2811000 Hz ~= 44100 Hz * 64 (-0.41%) */
149 acds = PXA_SSP_CLK_AUDIO_DIV_1;
150 }
151 break;
152 case 48000:
153 acps = 12235000;
154 switch (width) {
155 case 16:
156 /* 1529375 Hz ~= 48000 Hz * 32 (-0.44%) */
157 acds = PXA_SSP_CLK_AUDIO_DIV_2;
158 break;
159 case 32:
160 /* 3058750 Hz ~= 48000 Hz * 64 (-0.44%) */
161 acds = PXA_SSP_CLK_AUDIO_DIV_1;
162 }
163 break;
164 case 96000:
165 acps = 12235000;
166 switch (width) {
167 case 16:
168 /* 3058750 Hz ~= 96000 Hz * 32 (-0.44%) */
169 acds = PXA_SSP_CLK_AUDIO_DIV_1;
170 break;
171 case 32:
172 /* 6117500 Hz ~= 96000 Hz * 64 (-0.44%) */
173 acds = PXA_SSP_CLK_AUDIO_DIV_2;
174 div4 = PXA_SSP_CLK_SCDB_1;
175 break;
176 }
177 break;
178 }
179
180 /* set codec DAI configuration */
181 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MSB |
182 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
183 if (ret < 0)
184 return ret;
185
186 /* set cpu DAI configuration */
187 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
188 SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBS_CFS);
189 if (ret < 0)
190 return ret;
191
192 ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 1);
193 if (ret < 0)
194 return ret;
195
196 /* set audio clock as clock source */
197 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0,
198 SND_SOC_CLOCK_OUT);
199 if (ret < 0)
200 return ret;
201
202 /* set the SSP audio system clock ACDS divider */
203 ret = snd_soc_dai_set_clkdiv(cpu_dai,
204 PXA_SSP_AUDIO_DIV_ACDS, acds);
205 if (ret < 0)
206 return ret;
207
208 /* set the SSP audio system clock SCDB divider4 */
209 ret = snd_soc_dai_set_clkdiv(cpu_dai,
210 PXA_SSP_AUDIO_DIV_SCDB, div4);
211 if (ret < 0)
212 return ret;
213
214 /* set SSP audio pll clock */
215 ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, acps);
216 if (ret < 0)
217 return ret;
218
219 return 0;
220}
221
222/*
223 * Magician uses I2S for capture.
224 */
225static int magician_capture_hw_params(struct snd_pcm_substream *substream,
226 struct snd_pcm_hw_params *params)
227{
228 struct snd_soc_pcm_runtime *rtd = substream->private_data;
229 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
230 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
231 int ret = 0;
232
233 /* set codec DAI configuration */
234 ret = snd_soc_dai_set_fmt(codec_dai,
235 SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
236 SND_SOC_DAIFMT_CBS_CFS);
237 if (ret < 0)
238 return ret;
239
240 /* set cpu DAI configuration */
241 ret = snd_soc_dai_set_fmt(cpu_dai,
242 SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
243 SND_SOC_DAIFMT_CBS_CFS);
244 if (ret < 0)
245 return ret;
246
247 /* set the I2S system clock as output */
248 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
249 SND_SOC_CLOCK_OUT);
250 if (ret < 0)
251 return ret;
252
253 return 0;
254}
255
256static struct snd_soc_ops magician_capture_ops = {
257 .startup = magician_startup,
258 .hw_params = magician_capture_hw_params,
259};
260
261static struct snd_soc_ops magician_playback_ops = {
262 .startup = magician_startup,
263 .hw_params = magician_playback_hw_params,
264};
265
266static int magician_get_hp(struct snd_kcontrol *kcontrol,
267 struct snd_ctl_elem_value *ucontrol)
268{
269 ucontrol->value.integer.value[0] = magician_hp_switch;
270 return 0;
271}
272
273static int magician_set_hp(struct snd_kcontrol *kcontrol,
274 struct snd_ctl_elem_value *ucontrol)
275{
276 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
277
278 if (magician_hp_switch == ucontrol->value.integer.value[0])
279 return 0;
280
281 magician_hp_switch = ucontrol->value.integer.value[0];
282 magician_ext_control(codec);
283 return 1;
284}
285
286static int magician_get_spk(struct snd_kcontrol *kcontrol,
287 struct snd_ctl_elem_value *ucontrol)
288{
289 ucontrol->value.integer.value[0] = magician_spk_switch;
290 return 0;
291}
292
293static int magician_set_spk(struct snd_kcontrol *kcontrol,
294 struct snd_ctl_elem_value *ucontrol)
295{
296 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
297
298 if (magician_spk_switch == ucontrol->value.integer.value[0])
299 return 0;
300
301 magician_spk_switch = ucontrol->value.integer.value[0];
302 magician_ext_control(codec);
303 return 1;
304}
305
306static int magician_get_input(struct snd_kcontrol *kcontrol,
307 struct snd_ctl_elem_value *ucontrol)
308{
309 ucontrol->value.integer.value[0] = magician_in_sel;
310 return 0;
311}
312
313static int magician_set_input(struct snd_kcontrol *kcontrol,
314 struct snd_ctl_elem_value *ucontrol)
315{
316 if (magician_in_sel == ucontrol->value.integer.value[0])
317 return 0;
318
319 magician_in_sel = ucontrol->value.integer.value[0];
320
321 switch (magician_in_sel) {
322 case MAGICIAN_MIC:
323 gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 1);
324 break;
325 case MAGICIAN_MIC_EXT:
326 gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 0);
327 }
328
329 return 1;
330}
331
332static int magician_spk_power(struct snd_soc_dapm_widget *w,
333 struct snd_kcontrol *k, int event)
334{
335 gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, SND_SOC_DAPM_EVENT_ON(event));
336 return 0;
337}
338
339static int magician_hp_power(struct snd_soc_dapm_widget *w,
340 struct snd_kcontrol *k, int event)
341{
342 gpio_set_value(EGPIO_MAGICIAN_EP_POWER, SND_SOC_DAPM_EVENT_ON(event));
343 return 0;
344}
345
346static int magician_mic_bias(struct snd_soc_dapm_widget *w,
347 struct snd_kcontrol *k, int event)
348{
349 gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, SND_SOC_DAPM_EVENT_ON(event));
350 return 0;
351}
352
353/* magician machine dapm widgets */
354static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
355 SND_SOC_DAPM_HP("Headphone Jack", magician_hp_power),
356 SND_SOC_DAPM_SPK("Speaker", magician_spk_power),
357 SND_SOC_DAPM_MIC("Call Mic", magician_mic_bias),
358 SND_SOC_DAPM_MIC("Headset Mic", magician_mic_bias),
359};
360
361/* magician machine audio_map */
362static const struct snd_soc_dapm_route audio_map[] = {
363
364 /* Headphone connected to VOUTL, VOUTR */
365 {"Headphone Jack", NULL, "VOUTL"},
366 {"Headphone Jack", NULL, "VOUTR"},
367
368 /* Speaker connected to VOUTL, VOUTR */
369 {"Speaker", NULL, "VOUTL"},
370 {"Speaker", NULL, "VOUTR"},
371
372 /* Mics are connected to VINM */
373 {"VINM", NULL, "Headset Mic"},
374 {"VINM", NULL, "Call Mic"},
375};
376
377static const char *input_select[] = {"Call Mic", "Headset Mic"};
378static const struct soc_enum magician_in_sel_enum =
379 SOC_ENUM_SINGLE_EXT(2, input_select);
380
381static const struct snd_kcontrol_new uda1380_magician_controls[] = {
382 SOC_SINGLE_BOOL_EXT("Headphone Switch",
383 (unsigned long)&magician_hp_switch,
384 magician_get_hp, magician_set_hp),
385 SOC_SINGLE_BOOL_EXT("Speaker Switch",
386 (unsigned long)&magician_spk_switch,
387 magician_get_spk, magician_set_spk),
388 SOC_ENUM_EXT("Input Select", magician_in_sel_enum,
389 magician_get_input, magician_set_input),
390};
391
392/*
393 * Logic for a uda1380 as connected on a HTC Magician
394 */
395static int magician_uda1380_init(struct snd_soc_codec *codec)
396{
397 int err;
398
399 /* NC codec pins */
400 snd_soc_dapm_nc_pin(codec, "VOUTLHP");
401 snd_soc_dapm_nc_pin(codec, "VOUTRHP");
402
403 /* FIXME: is anything connected here? */
404 snd_soc_dapm_nc_pin(codec, "VINL");
405 snd_soc_dapm_nc_pin(codec, "VINR");
406
407 /* Add magician specific controls */
408 err = snd_soc_add_controls(codec, uda1380_magician_controls,
409 ARRAY_SIZE(uda1380_magician_controls));
410 if (err < 0)
411 return err;
412
413 /* Add magician specific widgets */
414 snd_soc_dapm_new_controls(codec, uda1380_dapm_widgets,
415 ARRAY_SIZE(uda1380_dapm_widgets));
416
417 /* Set up magician specific audio path interconnects */
418 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
419
420 snd_soc_dapm_sync(codec);
421 return 0;
422}
423
424/* magician digital audio interface glue - connects codec <--> CPU */
425static struct snd_soc_dai_link magician_dai[] = {
426{
427 .name = "uda1380",
428 .stream_name = "UDA1380 Playback",
429 .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP1],
430 .codec_dai = &uda1380_dai[UDA1380_DAI_PLAYBACK],
431 .init = magician_uda1380_init,
432 .ops = &magician_playback_ops,
433},
434{
435 .name = "uda1380",
436 .stream_name = "UDA1380 Capture",
437 .cpu_dai = &pxa_i2s_dai,
438 .codec_dai = &uda1380_dai[UDA1380_DAI_CAPTURE],
439 .ops = &magician_capture_ops,
440}
441};
442
443/* magician audio machine driver */
444static struct snd_soc_card snd_soc_card_magician = {
445 .name = "Magician",
446 .dai_link = magician_dai,
447 .num_links = ARRAY_SIZE(magician_dai),
448 .platform = &pxa2xx_soc_platform,
449};
450
451/* magician audio private data */
452static struct uda1380_setup_data magician_uda1380_setup = {
453 .i2c_address = 0x18,
454 .dac_clk = UDA1380_DAC_CLK_WSPLL,
455};
456
457/* magician audio subsystem */
458static struct snd_soc_device magician_snd_devdata = {
459 .card = &snd_soc_card_magician,
460 .codec_dev = &soc_codec_dev_uda1380,
461 .codec_data = &magician_uda1380_setup,
462};
463
464static struct platform_device *magician_snd_device;
465
466static int __init magician_init(void)
467{
468 int ret;
469
470 if (!machine_is_magician())
471 return -ENODEV;
472
473 ret = gpio_request(EGPIO_MAGICIAN_CODEC_POWER, "CODEC_POWER");
474 if (ret)
475 goto err_request_power;
476 ret = gpio_request(EGPIO_MAGICIAN_CODEC_RESET, "CODEC_RESET");
477 if (ret)
478 goto err_request_reset;
479 ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER");
480 if (ret)
481 goto err_request_spk;
482 ret = gpio_request(EGPIO_MAGICIAN_EP_POWER, "EP_POWER");
483 if (ret)
484 goto err_request_ep;
485 ret = gpio_request(EGPIO_MAGICIAN_MIC_POWER, "MIC_POWER");
486 if (ret)
487 goto err_request_mic;
488 ret = gpio_request(EGPIO_MAGICIAN_IN_SEL0, "IN_SEL0");
489 if (ret)
490 goto err_request_in_sel0;
491 ret = gpio_request(EGPIO_MAGICIAN_IN_SEL1, "IN_SEL1");
492 if (ret)
493 goto err_request_in_sel1;
494
495 gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 1);
496 gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0);
497
498 /* we may need to have the clock running here - pH5 */
499 gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 1);
500 udelay(5);
501 gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 0);
502
503 magician_snd_device = platform_device_alloc("soc-audio", -1);
504 if (!magician_snd_device) {
505 ret = -ENOMEM;
506 goto err_pdev;
507 }
508
509 platform_set_drvdata(magician_snd_device, &magician_snd_devdata);
510 magician_snd_devdata.dev = &magician_snd_device->dev;
511 ret = platform_device_add(magician_snd_device);
512 if (ret) {
513 platform_device_put(magician_snd_device);
514 goto err_pdev;
515 }
516
517 return 0;
518
519err_pdev:
520 gpio_free(EGPIO_MAGICIAN_IN_SEL1);
521err_request_in_sel1:
522 gpio_free(EGPIO_MAGICIAN_IN_SEL0);
523err_request_in_sel0:
524 gpio_free(EGPIO_MAGICIAN_MIC_POWER);
525err_request_mic:
526 gpio_free(EGPIO_MAGICIAN_EP_POWER);
527err_request_ep:
528 gpio_free(EGPIO_MAGICIAN_SPK_POWER);
529err_request_spk:
530 gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
531err_request_reset:
532 gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
533err_request_power:
534 return ret;
535}
536
537static void __exit magician_exit(void)
538{
539 platform_device_unregister(magician_snd_device);
540
541 gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0);
542 gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0);
543 gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0);
544 gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 0);
545
546 gpio_free(EGPIO_MAGICIAN_IN_SEL1);
547 gpio_free(EGPIO_MAGICIAN_IN_SEL0);
548 gpio_free(EGPIO_MAGICIAN_MIC_POWER);
549 gpio_free(EGPIO_MAGICIAN_EP_POWER);
550 gpio_free(EGPIO_MAGICIAN_SPK_POWER);
551 gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
552 gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
553}
554
555module_init(magician_init);
556module_exit(magician_exit);
557
558MODULE_AUTHOR("Philipp Zabel");
559MODULE_DESCRIPTION("ALSA SoC Magician");
560MODULE_LICENSE("GPL");