aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/tegra/tegra_rt5640.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/tegra/tegra_rt5640.c')
-rw-r--r--sound/soc/tegra/tegra_rt5640.c738
1 files changed, 738 insertions, 0 deletions
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
new file mode 100644
index 00000000000..f6f4ed3d421
--- /dev/null
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -0,0 +1,738 @@
1/*
2 * tegra_rt5640.c - Tegra machine ASoC driver for boards using ALC5640 codec.
3 *
4 * Author: Johnny Qiu <joqiu@nvidia.com>
5 * Copyright (C) 2011-2012, NVIDIA, Inc.
6 *
7 * Based on code copyright/by:
8 *
9 * Copyright 2007 Wolfson Microelectronics PLC.
10 * Author: Graeme Gregory
11 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * version 2 as published by the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
25 * 02110-1301 USA
26 *
27 */
28
29#include <asm/mach-types.h>
30
31#include <linux/module.h>
32#include <linux/platform_device.h>
33#include <linux/slab.h>
34#include <linux/gpio.h>
35#include <linux/regulator/consumer.h>
36#ifdef CONFIG_SWITCH
37#include <linux/switch.h>
38#endif
39
40#include <mach/tegra_rt5640_pdata.h>
41
42#include <sound/core.h>
43#include <sound/jack.h>
44#include <sound/pcm.h>
45#include <sound/pcm_params.h>
46#include <sound/soc.h>
47
48#include "../codecs/rt5639.h"
49#include "../codecs/rt5640.h"
50
51#include "tegra_pcm.h"
52#include "tegra_asoc_utils.h"
53
54#define DRV_NAME "tegra-snd-rt5640"
55
56#define GPIO_SPKR_EN BIT(0)
57#define GPIO_HP_MUTE BIT(1)
58#define GPIO_INT_MIC_EN BIT(2)
59#define GPIO_EXT_MIC_EN BIT(3)
60#define GPIO_HP_DET BIT(4)
61
62struct tegra_rt5640 {
63 struct tegra_asoc_utils_data util_data;
64 struct tegra_rt5640_platform_data *pdata;
65 struct regulator *spk_reg;
66 struct regulator *dmic_reg;
67 struct regulator *cdc_en;
68 int gpio_requested;
69#ifdef CONFIG_SWITCH
70 int jack_status;
71#endif
72};
73
74static int tegra_rt5640_hw_params(struct snd_pcm_substream *substream,
75 struct snd_pcm_hw_params *params)
76{
77 struct snd_soc_pcm_runtime *rtd = substream->private_data;
78 struct snd_soc_dai *codec_dai = rtd->codec_dai;
79 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
80 struct snd_soc_codec *codec = rtd->codec;
81 struct snd_soc_card *card = codec->card;
82 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
83 int srate, mclk, i2s_daifmt;
84 int err;
85
86 srate = params_rate(params);
87 mclk = 256 * srate;
88 err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
89 if (err < 0) {
90 if (!(machine->util_data.set_mclk % mclk)) {
91 mclk = machine->util_data.set_mclk;
92 } else {
93 dev_err(card->dev, "Can't configure clocks\n");
94 return err;
95 }
96 }
97
98 tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
99
100 i2s_daifmt = SND_SOC_DAIFMT_NB_NF |
101 SND_SOC_DAIFMT_CBS_CFS;
102
103 i2s_daifmt |= SND_SOC_DAIFMT_I2S;
104
105 err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt);
106 if (err < 0) {
107 dev_err(card->dev, "codec_dai fmt not set\n");
108 return err;
109 }
110
111 err = snd_soc_dai_set_fmt(cpu_dai, i2s_daifmt);
112 if (err < 0) {
113 dev_err(card->dev, "cpu_dai fmt not set\n");
114 return err;
115 }
116
117 err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
118 SND_SOC_CLOCK_IN);
119 if (err < 0) {
120 dev_err(card->dev, "codec_dai clock not set\n");
121 return err;
122 }
123
124 return 0;
125}
126
127static int tegra_bt_sco_hw_params(struct snd_pcm_substream *substream,
128 struct snd_pcm_hw_params *params)
129{
130 struct snd_soc_pcm_runtime *rtd = substream->private_data;
131 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
132 struct snd_soc_card *card = rtd->card;
133 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
134 int srate, mclk, min_mclk;
135 int err;
136
137 srate = params_rate(params);
138 switch (srate) {
139 case 11025:
140 case 22050:
141 case 44100:
142 case 88200:
143 mclk = 11289600;
144 break;
145 case 8000:
146 case 16000:
147 case 32000:
148 case 48000:
149 case 64000:
150 case 96000:
151 mclk = 12288000;
152 break;
153 default:
154 return -EINVAL;
155 }
156 min_mclk = 64 * srate;
157
158 err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
159 if (err < 0) {
160 if (!(machine->util_data.set_mclk % min_mclk))
161 mclk = machine->util_data.set_mclk;
162 else {
163 dev_err(card->dev, "Can't configure clocks\n");
164 return err;
165 }
166 }
167
168 tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
169
170 err = snd_soc_dai_set_fmt(cpu_dai,
171 SND_SOC_DAIFMT_DSP_A |
172 SND_SOC_DAIFMT_NB_NF |
173 SND_SOC_DAIFMT_CBS_CFS);
174 if (err < 0) {
175 dev_err(card->dev, "cpu_dai fmt not set\n");
176 return err;
177 }
178
179 return 0;
180}
181
182static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
183 struct snd_pcm_hw_params *params)
184{
185 struct snd_soc_pcm_runtime *rtd = substream->private_data;
186 struct snd_soc_card *card = rtd->card;
187 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
188 int srate, mclk, min_mclk;
189 int err;
190
191 srate = params_rate(params);
192 switch (srate) {
193 case 11025:
194 case 22050:
195 case 44100:
196 case 88200:
197 mclk = 11289600;
198 break;
199 case 8000:
200 case 16000:
201 case 32000:
202 case 48000:
203 case 64000:
204 case 96000:
205 mclk = 12288000;
206 break;
207 default:
208 return -EINVAL;
209 }
210 min_mclk = 128 * srate;
211
212 err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
213 if (err < 0) {
214 if (!(machine->util_data.set_mclk % min_mclk))
215 mclk = machine->util_data.set_mclk;
216 else {
217 dev_err(card->dev, "Can't configure clocks\n");
218 return err;
219 }
220 }
221
222 tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
223
224 return 0;
225}
226
227static int tegra_hw_free(struct snd_pcm_substream *substream)
228{
229 struct snd_soc_pcm_runtime *rtd = substream->private_data;
230 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(rtd->card);
231
232 tegra_asoc_utils_lock_clk_rate(&machine->util_data, 0);
233
234 return 0;
235}
236
237static struct snd_soc_ops tegra_rt5640_ops = {
238 .hw_params = tegra_rt5640_hw_params,
239 .hw_free = tegra_hw_free,
240};
241
242static struct snd_soc_ops tegra_rt5640_bt_sco_ops = {
243 .hw_params = tegra_bt_sco_hw_params,
244 .hw_free = tegra_hw_free,
245};
246
247static struct snd_soc_ops tegra_spdif_ops = {
248 .hw_params = tegra_spdif_hw_params,
249 .hw_free = tegra_hw_free,
250};
251
252static struct snd_soc_jack tegra_rt5640_hp_jack;
253
254static struct snd_soc_jack_gpio tegra_rt5640_hp_jack_gpio = {
255 .name = "headphone detect",
256 .report = SND_JACK_HEADPHONE,
257 .debounce_time = 150,
258 .invert = 1,
259};
260
261#ifdef CONFIG_SWITCH
262/* These values are copied from Android WiredAccessoryObserver */
263enum headset_state {
264 BIT_NO_HEADSET = 0,
265 BIT_HEADSET = (1 << 0),
266 BIT_HEADSET_NO_MIC = (1 << 1),
267};
268
269static struct switch_dev tegra_rt5640_headset_switch = {
270 .name = "h2w",
271};
272
273static int tegra_rt5640_jack_notifier(struct notifier_block *self,
274 unsigned long action, void *dev)
275{
276 struct snd_soc_jack *jack = dev;
277 struct snd_soc_codec *codec = jack->codec;
278 struct snd_soc_card *card = codec->card;
279 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
280 enum headset_state state = BIT_NO_HEADSET;
281 unsigned char status_jack;
282
283 if (jack == &tegra_rt5640_hp_jack) {
284 if (action) {
285 if (!strncmp(machine->pdata->codec_name, "rt5639", 6))
286 status_jack = rt5639_headset_detect(codec, 1);
287 else if (!strncmp(machine->pdata->codec_name, "rt5640",
288 6))
289 status_jack = rt5640_headset_detect(codec, 1);
290
291 machine->jack_status &= ~SND_JACK_HEADPHONE;
292 machine->jack_status &= ~SND_JACK_MICROPHONE;
293 if (status_jack == RT5639_HEADPHO_DET ||
294 status_jack == RT5640_HEADPHO_DET)
295 machine->jack_status |=
296 SND_JACK_HEADPHONE;
297 else if (status_jack == RT5639_HEADSET_DET ||
298 status_jack == RT5640_HEADSET_DET) {
299 machine->jack_status |=
300 SND_JACK_HEADPHONE;
301 machine->jack_status |=
302 SND_JACK_MICROPHONE;
303 }
304 } else {
305 if (!strncmp(machine->pdata->codec_name, "rt5639", 6))
306 rt5639_headset_detect(codec, 0);
307 else if (!strncmp(machine->pdata->codec_name, "rt5640",
308 6))
309 rt5640_headset_detect(codec, 0);
310
311 machine->jack_status &= ~SND_JACK_HEADPHONE;
312 machine->jack_status &= ~SND_JACK_MICROPHONE;
313 }
314 }
315
316 switch (machine->jack_status) {
317 case SND_JACK_HEADPHONE:
318 state = BIT_HEADSET_NO_MIC;
319 break;
320 case SND_JACK_HEADSET:
321 state = BIT_HEADSET;
322 break;
323 case SND_JACK_MICROPHONE:
324 /* mic: would not report */
325 default:
326 state = BIT_NO_HEADSET;
327 }
328
329 switch_set_state(&tegra_rt5640_headset_switch, state);
330
331 return NOTIFY_OK;
332}
333
334static struct notifier_block tegra_rt5640_jack_detect_nb = {
335 .notifier_call = tegra_rt5640_jack_notifier,
336};
337#else
338static struct snd_soc_jack_pin tegra_rt5640_hp_jack_pins[] = {
339 {
340 .pin = "Headphone Jack",
341 .mask = SND_JACK_HEADPHONE,
342 },
343};
344
345#endif
346
347static int tegra_rt5640_event_int_spk(struct snd_soc_dapm_widget *w,
348 struct snd_kcontrol *k, int event)
349{
350 struct snd_soc_dapm_context *dapm = w->dapm;
351 struct snd_soc_card *card = dapm->card;
352 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
353 struct tegra_rt5640_platform_data *pdata = machine->pdata;
354
355 if (machine->spk_reg) {
356 if (SND_SOC_DAPM_EVENT_ON(event))
357 regulator_enable(machine->spk_reg);
358 else
359 regulator_disable(machine->spk_reg);
360 }
361
362 if (!(machine->gpio_requested & GPIO_SPKR_EN))
363 return 0;
364
365 gpio_set_value_cansleep(pdata->gpio_spkr_en,
366 SND_SOC_DAPM_EVENT_ON(event));
367
368 return 0;
369}
370
371static int tegra_rt5640_event_hp(struct snd_soc_dapm_widget *w,
372 struct snd_kcontrol *k, int event)
373{
374 struct snd_soc_dapm_context *dapm = w->dapm;
375 struct snd_soc_card *card = dapm->card;
376 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
377 struct tegra_rt5640_platform_data *pdata = machine->pdata;
378
379 if (!(machine->gpio_requested & GPIO_HP_MUTE))
380 return 0;
381
382 gpio_set_value_cansleep(pdata->gpio_hp_mute,
383 !SND_SOC_DAPM_EVENT_ON(event));
384
385 return 0;
386}
387
388static int tegra_rt5640_event_int_mic(struct snd_soc_dapm_widget *w,
389 struct snd_kcontrol *k, int event)
390{
391 struct snd_soc_dapm_context *dapm = w->dapm;
392 struct snd_soc_card *card = dapm->card;
393 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
394 struct tegra_rt5640_platform_data *pdata = machine->pdata;
395
396 if (machine->dmic_reg) {
397 if (SND_SOC_DAPM_EVENT_ON(event))
398 regulator_enable(machine->dmic_reg);
399 else
400 regulator_disable(machine->dmic_reg);
401 }
402
403 if (!(machine->gpio_requested & GPIO_INT_MIC_EN))
404 return 0;
405
406 gpio_set_value_cansleep(pdata->gpio_int_mic_en,
407 SND_SOC_DAPM_EVENT_ON(event));
408
409 return 0;
410}
411
412static int tegra_rt5640_event_ext_mic(struct snd_soc_dapm_widget *w,
413 struct snd_kcontrol *k, int event)
414{
415 struct snd_soc_dapm_context *dapm = w->dapm;
416 struct snd_soc_card *card = dapm->card;
417 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
418 struct tegra_rt5640_platform_data *pdata = machine->pdata;
419
420 if (!(machine->gpio_requested & GPIO_EXT_MIC_EN))
421 return 0;
422
423 gpio_set_value_cansleep(pdata->gpio_ext_mic_en,
424 !SND_SOC_DAPM_EVENT_ON(event));
425
426 return 0;
427}
428
429static const struct snd_soc_dapm_widget cardhu_dapm_widgets[] = {
430 SND_SOC_DAPM_SPK("Int Spk", tegra_rt5640_event_int_spk),
431 SND_SOC_DAPM_HP("Headphone Jack", tegra_rt5640_event_hp),
432 SND_SOC_DAPM_MIC("Mic Jack", tegra_rt5640_event_ext_mic),
433 SND_SOC_DAPM_MIC("Int Mic", tegra_rt5640_event_int_mic),
434};
435
436static const struct snd_soc_dapm_route cardhu_audio_map[] = {
437 {"Headphone Jack", NULL, "HPOR"},
438 {"Headphone Jack", NULL, "HPOL"},
439 {"Int Spk", NULL, "SPORP"},
440 {"Int Spk", NULL, "SPORN"},
441 {"Int Spk", NULL, "SPOLP"},
442 {"Int Spk", NULL, "SPOLN"},
443 {"micbias1", NULL, "Mic Jack"},
444 {"IN1P", NULL, "micbias1"},
445 {"IN1N", NULL, "micbias1"},
446 {"micbias1", NULL, "Int Mic"},
447 {"IN2P", NULL, "micbias1"},
448};
449
450static const struct snd_kcontrol_new cardhu_controls[] = {
451 SOC_DAPM_PIN_SWITCH("Int Spk"),
452 SOC_DAPM_PIN_SWITCH("Headphone Jack"),
453 SOC_DAPM_PIN_SWITCH("Mic Jack"),
454 SOC_DAPM_PIN_SWITCH("Int Mic"),
455};
456
457static int tegra_rt5640_init(struct snd_soc_pcm_runtime *rtd)
458{
459 struct snd_soc_codec *codec = rtd->codec;
460 struct snd_soc_dapm_context *dapm = &codec->dapm;
461 struct snd_soc_card *card = codec->card;
462 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
463 struct tegra_rt5640_platform_data *pdata = machine->pdata;
464 int ret;
465
466 if (gpio_is_valid(pdata->gpio_spkr_en)) {
467 ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
468 if (ret) {
469 dev_err(card->dev, "cannot get spkr_en gpio\n");
470 return ret;
471 }
472 machine->gpio_requested |= GPIO_SPKR_EN;
473
474 gpio_direction_output(pdata->gpio_spkr_en, 0);
475 }
476
477 if (gpio_is_valid(pdata->gpio_hp_mute)) {
478 ret = gpio_request(pdata->gpio_hp_mute, "hp_mute");
479 if (ret) {
480 dev_err(card->dev, "cannot get hp_mute gpio\n");
481 return ret;
482 }
483 machine->gpio_requested |= GPIO_HP_MUTE;
484
485 gpio_direction_output(pdata->gpio_hp_mute, 0);
486 }
487
488 if (gpio_is_valid(pdata->gpio_int_mic_en)) {
489 ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
490 if (ret) {
491 dev_err(card->dev, "cannot get int_mic_en gpio\n");
492 return ret;
493 }
494 machine->gpio_requested |= GPIO_INT_MIC_EN;
495
496 /* Disable int mic; enable signal is active-high */
497 gpio_direction_output(pdata->gpio_int_mic_en, 0);
498 }
499
500 if (gpio_is_valid(pdata->gpio_ext_mic_en)) {
501 ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
502 if (ret) {
503 dev_err(card->dev, "cannot get ext_mic_en gpio\n");
504 return ret;
505 }
506 machine->gpio_requested |= GPIO_EXT_MIC_EN;
507
508 /* Enable ext mic; enable signal is active-low */
509 gpio_direction_output(pdata->gpio_ext_mic_en, 0);
510 }
511
512 if (gpio_is_valid(pdata->gpio_hp_det)) {
513 tegra_rt5640_hp_jack_gpio.gpio = pdata->gpio_hp_det;
514 snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
515 &tegra_rt5640_hp_jack);
516#ifndef CONFIG_SWITCH
517 snd_soc_jack_add_pins(&tegra_rt5640_hp_jack,
518 ARRAY_SIZE(tegra_rt5640_hp_jack_pins),
519 tegra_rt5640_hp_jack_pins);
520#else
521 snd_soc_jack_notifier_register(&tegra_rt5640_hp_jack,
522 &tegra_rt5640_jack_detect_nb);
523#endif
524 snd_soc_jack_add_gpios(&tegra_rt5640_hp_jack,
525 1,
526 &tegra_rt5640_hp_jack_gpio);
527 machine->gpio_requested |= GPIO_HP_DET;
528 }
529
530 ret = snd_soc_add_controls(codec, cardhu_controls,
531 ARRAY_SIZE(cardhu_controls));
532 if (ret < 0)
533 return ret;
534
535 snd_soc_dapm_new_controls(dapm, cardhu_dapm_widgets,
536 ARRAY_SIZE(cardhu_dapm_widgets));
537
538 snd_soc_dapm_add_routes(dapm, cardhu_audio_map,
539 ARRAY_SIZE(cardhu_audio_map));
540 /* FIXME: Calculate automatically based on DAPM routes? */
541 snd_soc_dapm_nc_pin(dapm, "LOUTL");
542 snd_soc_dapm_nc_pin(dapm, "LOUTR");
543
544 snd_soc_dapm_sync(dapm);
545
546 return 0;
547}
548
549static struct snd_soc_dai_link tegra_rt5640_dai[] = {
550 {
551 .name = "RT5640",
552 .stream_name = "RT5640 PCM",
553 .codec_name = "rt5640.4-001c",
554 .platform_name = "tegra-pcm-audio",
555 .cpu_dai_name = "tegra30-i2s.1",
556 .codec_dai_name = "rt5640-aif1",
557 .init = tegra_rt5640_init,
558 .ops = &tegra_rt5640_ops,
559 },
560 {
561 .name = "SPDIF",
562 .stream_name = "SPDIF PCM",
563 .codec_name = "spdif-dit.0",
564 .platform_name = "tegra-pcm-audio",
565 .cpu_dai_name = "tegra30-spdif",
566 .codec_dai_name = "dit-hifi",
567 .ops = &tegra_spdif_ops,
568 },
569 {
570 .name = "BT-SCO",
571 .stream_name = "BT SCO PCM",
572 .codec_name = "spdif-dit.1",
573 .platform_name = "tegra-pcm-audio",
574 .cpu_dai_name = "tegra30-i2s.3",
575 .codec_dai_name = "dit-hifi",
576 .ops = &tegra_rt5640_bt_sco_ops,
577 },
578};
579
580static struct snd_soc_card snd_soc_tegra_rt5640 = {
581 .name = "tegra-rt5640",
582 .dai_link = tegra_rt5640_dai,
583 .num_links = ARRAY_SIZE(tegra_rt5640_dai),
584};
585
586static __devinit int tegra_rt5640_driver_probe(struct platform_device *pdev)
587{
588 struct snd_soc_card *card = &snd_soc_tegra_rt5640;
589 struct tegra_rt5640 *machine;
590 struct tegra_rt5640_platform_data *pdata;
591 int ret;
592
593 pdata = pdev->dev.platform_data;
594 if (!pdata) {
595 dev_err(&pdev->dev, "No platform data supplied\n");
596 return -EINVAL;
597 }
598
599 if (pdata->codec_name)
600 card->dai_link->codec_name = pdata->codec_name;
601 if (pdata->codec_dai_name)
602 card->dai_link->codec_dai_name = pdata->codec_dai_name;
603
604 machine = kzalloc(sizeof(struct tegra_rt5640), GFP_KERNEL);
605 if (!machine) {
606 dev_err(&pdev->dev, "Can't allocate tegra_rt5640 struct\n");
607 return -ENOMEM;
608 }
609
610 machine->pdata = pdata;
611
612 ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
613 if (ret)
614 goto err_free_machine;
615
616 machine->cdc_en = regulator_get(NULL, "cdc_en");
617 if (WARN_ON(IS_ERR(machine->cdc_en))) {
618 dev_err(&pdev->dev, "Couldn't get regulator cdc_en: %ld\n",
619 PTR_ERR(machine->cdc_en));
620 machine->cdc_en = 0;
621 } else {
622 regulator_enable(machine->cdc_en);
623 }
624
625 machine->spk_reg = regulator_get(&pdev->dev, "vdd_spk_amp");
626 if (IS_ERR(machine->spk_reg)) {
627 dev_info(&pdev->dev, "No speaker regulator found\n");
628 machine->spk_reg = 0;
629 }
630
631#ifdef CONFIG_SWITCH
632 /* Addd h2w swith class support */
633 ret = switch_dev_register(&tegra_rt5640_headset_switch);
634 if (ret < 0)
635 goto err_fini_utils;
636#endif
637
638 card->dev = &pdev->dev;
639 platform_set_drvdata(pdev, card);
640 snd_soc_card_set_drvdata(card, machine);
641
642 ret = snd_soc_register_card(card);
643 if (ret) {
644 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
645 ret);
646 goto err_unregister_switch;
647 }
648
649 if (!card->instantiated) {
650 ret = -ENODEV;
651 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
652 ret);
653 goto err_unregister_card;
654 }
655
656 return 0;
657
658err_unregister_card:
659 snd_soc_unregister_card(card);
660err_unregister_switch:
661#ifdef CONFIG_SWITCH
662 switch_dev_unregister(&tegra_rt5640_headset_switch);
663err_fini_utils:
664#endif
665 tegra_asoc_utils_fini(&machine->util_data);
666err_free_machine:
667 kfree(machine);
668 return ret;
669}
670
671static int __devexit tegra_rt5640_driver_remove(struct platform_device *pdev)
672{
673 struct snd_soc_card *card = platform_get_drvdata(pdev);
674 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
675 struct tegra_rt5640_platform_data *pdata = machine->pdata;
676
677 if (machine->gpio_requested & GPIO_HP_DET)
678 snd_soc_jack_free_gpios(&tegra_rt5640_hp_jack,
679 1,
680 &tegra_rt5640_hp_jack_gpio);
681 if (machine->gpio_requested & GPIO_EXT_MIC_EN)
682 gpio_free(pdata->gpio_ext_mic_en);
683 if (machine->gpio_requested & GPIO_INT_MIC_EN)
684 gpio_free(pdata->gpio_int_mic_en);
685 if (machine->gpio_requested & GPIO_HP_MUTE)
686 gpio_free(pdata->gpio_hp_mute);
687 if (machine->gpio_requested & GPIO_SPKR_EN)
688 gpio_free(pdata->gpio_spkr_en);
689 machine->gpio_requested = 0;
690
691 if (machine->spk_reg)
692 regulator_put(machine->spk_reg);
693 if (machine->dmic_reg)
694 regulator_put(machine->dmic_reg);
695
696 if (machine->cdc_en) {
697 regulator_disable(machine->cdc_en);
698 regulator_put(machine->cdc_en);
699 }
700
701 snd_soc_unregister_card(card);
702
703 tegra_asoc_utils_fini(&machine->util_data);
704
705#ifdef CONFIG_SWITCH
706 switch_dev_unregister(&tegra_rt5640_headset_switch);
707#endif
708 kfree(machine);
709
710 return 0;
711}
712
713static struct platform_driver tegra_rt5640_driver = {
714 .driver = {
715 .name = DRV_NAME,
716 .owner = THIS_MODULE,
717 .pm = &snd_soc_pm_ops,
718 },
719 .probe = tegra_rt5640_driver_probe,
720 .remove = __devexit_p(tegra_rt5640_driver_remove),
721};
722
723static int __init tegra_rt5640_modinit(void)
724{
725 return platform_driver_register(&tegra_rt5640_driver);
726}
727module_init(tegra_rt5640_modinit);
728
729static void __exit tegra_rt5640_modexit(void)
730{
731 platform_driver_unregister(&tegra_rt5640_driver);
732}
733module_exit(tegra_rt5640_modexit);
734
735MODULE_AUTHOR("Johnny Qiu <joqiu@nvidia.com>");
736MODULE_DESCRIPTION("Tegra+RT5640 machine ASoC driver");
737MODULE_LICENSE("GPL");
738MODULE_ALIAS("platform:" DRV_NAME);