From f7ccf6cae50ef4fa40ff4a8fa93d290c86c67a8c Mon Sep 17 00:00:00 2001 From: Chao Jiang Date: Tue, 8 Mar 2011 16:02:20 +0900 Subject: [tegra ALSA] Enable mic jack detection Added support on Mic jack detection on Tegra. - External mic jack detection could be probed by trigger an interrupt via specific gpio or pull-up a generic gpio pin. The patch enabled the later option. - Tegra reference boards have gpio to switch external mic and internal mic. The switching is done in the patch automatically once jack happened. fixes bug 766757 Change-Id: If530f237d4564d1de6c019b206910f2a0ffe4163 Reviewed-on: http://git-master/r/22033 Reviewed-by: Chao Jiang Tested-by: Chao Jiang Reviewed-by: Bharat Nihalani Reviewed-by: Scott Peterson --- sound/soc/tegra/tegra_soc.h | 1 + sound/soc/tegra/tegra_soc_controls.c | 41 +++++++++-- sound/soc/tegra/tegra_wired_jack.c | 132 +++++++++++++++++++++++++++-------- 3 files changed, 142 insertions(+), 32 deletions(-) (limited to 'sound/soc/tegra') diff --git a/sound/soc/tegra/tegra_soc.h b/sound/soc/tegra/tegra_soc.h index ea3bf4a786d1..92f621bf87d0 100644 --- a/sound/soc/tegra/tegra_soc.h +++ b/sound/soc/tegra/tegra_soc.h @@ -108,6 +108,7 @@ struct wired_jack_conf { int hp_det_n; int en_mic_int; int en_mic_ext; + int cdc_irq; int en_spkr; }; diff --git a/sound/soc/tegra/tegra_soc_controls.c b/sound/soc/tegra/tegra_soc_controls.c index 4371130e1081..eb22a85af493 100644 --- a/sound/soc/tegra/tegra_soc_controls.c +++ b/sound/soc/tegra/tegra_soc_controls.c @@ -164,10 +164,39 @@ static int tegra_dapm_event_int_spk(struct snd_soc_dapm_widget* w, return 0; } +static int tegra_dapm_event_int_mic(struct snd_soc_dapm_widget* w, + struct snd_kcontrol* k, int event) +{ + if (tegra_wired_jack_conf.en_mic_int != -1) + gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_int, + SND_SOC_DAPM_EVENT_ON(event)); + + if (tegra_wired_jack_conf.en_mic_ext != -1) + gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_ext, + !(SND_SOC_DAPM_EVENT_ON(event))); + + return 0; +} + +static int tegra_dapm_event_ext_mic(struct snd_soc_dapm_widget* w, + struct snd_kcontrol* k, int event) +{ + if (tegra_wired_jack_conf.en_mic_ext != -1) + gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_ext, + SND_SOC_DAPM_EVENT_ON(event)); + + if (tegra_wired_jack_conf.en_mic_int != -1) + gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_int, + !(SND_SOC_DAPM_EVENT_ON(event))); + + return 0; +} + /*tegra machine dapm widgets */ static const struct snd_soc_dapm_widget tegra_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", tegra_dapm_event_ext_mic), + SND_SOC_DAPM_MIC("Int Mic", tegra_dapm_event_int_mic), SND_SOC_DAPM_SPK("Lineout", NULL), SND_SOC_DAPM_SPK("Int Spk", tegra_dapm_event_int_spk), SND_SOC_DAPM_LINE("Line Jack", NULL), @@ -193,11 +222,15 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Lineout", NULL, "LINEOUTR"}, {"Lineout", NULL, "LINEOUTL"}, - /* mic is connected to MICIN (via right channel of headphone jack) */ + /* external mic is stero */ {"IN1L", NULL, "Mic Jack"}, + {"IN1R", NULL, "Mic Jack"}, + + /* internal mic is mono */ + {"IN1R", NULL, "Int Mic"}, - /* Same as the above but no mic bias for line signals */ - {"IN2L", NULL, "Line Jack"}, + {"IN3L", NULL, "Line Jack"}, + {"IN3R", NULL, "Line Jack"}, }; static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", diff --git a/sound/soc/tegra/tegra_wired_jack.c b/sound/soc/tegra/tegra_wired_jack.c index 10fb237ce64a..b846fa003c62 100644 --- a/sound/soc/tegra/tegra_wired_jack.c +++ b/sound/soc/tegra/tegra_wired_jack.c @@ -29,6 +29,7 @@ #include "tegra_soc.h" #define HEAD_DET_GPIO 0 +#define MIC_DET_GPIO 1 struct wired_jack_conf tegra_wired_jack_conf = { -1, -1, -1, -1 @@ -37,7 +38,7 @@ struct wired_jack_conf tegra_wired_jack_conf = { /* jack */ static struct snd_soc_jack *tegra_wired_jack; -static struct snd_soc_jack_pin hs_jack_pins[] = { +static struct snd_soc_jack_pin wired_jack_pins[] = { { .pin = "Headset Jack", .mask = SND_JACK_HEADSET, @@ -52,7 +53,7 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { }, }; -static struct snd_soc_jack_gpio hs_jack_gpios[] = { +static struct snd_soc_jack_gpio wired_jack_gpios[] = { { /* gpio pin depends on board traits */ .name = "headphone-detect-gpio", @@ -60,6 +61,13 @@ static struct snd_soc_jack_gpio hs_jack_gpios[] = { .invert = 1, .debounce_time = 200, }, + { + /* gpio pin depens on board traits */ + .name = "mic-detect-gpio", + .report = SND_JACK_MICROPHONE, + .invert = 1, + .debounce_time = 200, + }, }; static struct switch_dev wired_switch_dev = { @@ -76,16 +84,49 @@ static int wired_swith_notify(struct notifier_block *self, unsigned long action, void* dev) { int state = 0; + int flag = 0; + int hp_gpio = -1; + int mic_gpio = -1;; + + /* hp_det_n is low active pin */ + if (tegra_wired_jack_conf.hp_det_n != -1) + hp_gpio = gpio_get_value(tegra_wired_jack_conf.hp_det_n); + if (tegra_wired_jack_conf.cdc_irq != -1) + mic_gpio = gpio_get_value(tegra_wired_jack_conf.cdc_irq); + + flag = (hp_gpio << 4) | mic_gpio; switch (action) { case SND_JACK_HEADSET: state = 1; break; case SND_JACK_HEADPHONE: - state = 2; + if (mic_gpio) + state = 1; + else + state = 2; + break; + case SND_JACK_MICROPHONE: + if (!hp_gpio) /* low = hp */ + state = 1; break; default: - state = 0; + switch (flag) { + case 0x010: + state = 0; + break; + case 0x01: + state = 1; + break; + case 0x11: + /* mic: would not report */ + break; + case 0x00: + state = 2; + break; + default: + state = 0; + } } tegra_switch_set_state(state); @@ -101,30 +142,55 @@ static struct notifier_block wired_switch_nb = { static int tegra_wired_jack_probe(struct platform_device *pdev) { int ret; - int hp_det_n; + int hp_det_n, cdc_irq; + int en_mic_int, en_mic_ext; int en_spkr; struct tegra_wired_jack_conf *pdata; pdata = (struct tegra_wired_jack_conf *)pdev->dev.platform_data; - if (!pdata || !pdata->hp_det_n || !pdata->en_spkr) { + if (!pdata || !pdata->hp_det_n || !pdata->en_spkr || + !pdata->cdc_irq || !pdata->en_mic_int || !pdata->en_mic_ext) { pr_err("Please set up gpio pins for jack.\n"); return -EBUSY; } hp_det_n = pdata->hp_det_n; - hs_jack_gpios[HEAD_DET_GPIO].gpio = hp_det_n; + wired_jack_gpios[HEAD_DET_GPIO].gpio = hp_det_n; + + cdc_irq = pdata->cdc_irq; + wired_jack_gpios[MIC_DET_GPIO].gpio = cdc_irq; ret = snd_soc_jack_add_gpios(tegra_wired_jack, - ARRAY_SIZE(hs_jack_gpios), - hs_jack_gpios); + ARRAY_SIZE(wired_jack_gpios), + wired_jack_gpios); if (ret) { pr_err("Could NOT set up gpio pins for jack.\n"); snd_soc_jack_free_gpios(tegra_wired_jack, - ARRAY_SIZE(hs_jack_gpios), - hs_jack_gpios); + ARRAY_SIZE(wired_jack_gpios), + wired_jack_gpios); return ret; } + /* Mic switch controlling pins */ + en_mic_int = pdata->en_mic_int; + en_mic_ext = pdata->en_mic_ext; + + ret = gpio_request(en_mic_int, "en_mic_int"); + if (ret) { + pr_err("Could NOT get gpio for internal mic controlling.\n"); + gpio_free(en_mic_int); + } + gpio_direction_output(en_mic_int, 0); + gpio_export(en_mic_int, false); + + ret = gpio_request(en_mic_ext, "en_mic_ext"); + if (ret) { + pr_err("Could NOT get gpio for external mic controlling.\n"); + gpio_free(en_mic_ext); + } + gpio_direction_output(en_mic_ext, 0); + gpio_export(en_mic_ext, false); + en_spkr = pdata->en_spkr; ret = gpio_request(en_spkr, "en_spkr"); if (ret) { @@ -136,23 +202,44 @@ static int tegra_wired_jack_probe(struct platform_device *pdev) /* restore configuration of these pins */ tegra_wired_jack_conf.hp_det_n = hp_det_n; + tegra_wired_jack_conf.en_mic_int = en_mic_int; + tegra_wired_jack_conf.en_mic_ext = en_mic_ext; + tegra_wired_jack_conf.cdc_irq = cdc_irq; tegra_wired_jack_conf.en_spkr = en_spkr; + /* Addd h2w swith class support */ + ret = switch_dev_register(&wired_switch_dev); + if (ret < 0) + goto switch_dev_failed; + + snd_soc_jack_notifier_register(tegra_wired_jack, + &wired_switch_nb); + return 0; + +switch_dev_failed: + switch_dev_unregister(&wired_switch_dev); + return ret; } static int tegra_wired_jack_remove(struct platform_device *pdev) { snd_soc_jack_free_gpios(tegra_wired_jack, - ARRAY_SIZE(hs_jack_gpios), - hs_jack_gpios); + ARRAY_SIZE(wired_jack_gpios), + wired_jack_gpios); + + gpio_free(tegra_wired_jack_conf.en_mic_int); + gpio_free(tegra_wired_jack_conf.en_mic_ext); + gpio_free(tegra_wired_jack_conf.en_spkr); + + switch_dev_unregister(&wired_switch_dev); return 0; } static struct platform_driver tegra_wired_jack_driver = { .probe = tegra_wired_jack_probe, - .remove = __devexit_p(tegra_wired_jack_remove), + .remove = tegra_wired_jack_remove, .driver = { .name = "tegra_wired_jack", .owner = THIS_MODULE, @@ -174,33 +261,23 @@ int tegra_jack_init(struct snd_soc_codec *codec) } /* Add jack detection */ - ret = snd_soc_jack_new(codec->socdev->card, "Headset Jack", + ret = snd_soc_jack_new(codec->socdev->card, "Wired Accessory Jack", SND_JACK_HEADSET, tegra_wired_jack); if (ret < 0) goto failed; ret = snd_soc_jack_add_pins(tegra_wired_jack, - ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); + ARRAY_SIZE(wired_jack_pins), + wired_jack_pins); if (ret < 0) goto failed; - /* Addd h2w swith class support */ - ret = switch_dev_register(&wired_switch_dev); - if (ret < 0) - goto switch_dev_failed; - - snd_soc_jack_notifier_register(tegra_wired_jack, - &wired_switch_nb); - ret = platform_driver_register(&tegra_wired_jack_driver); if (ret < 0) goto platform_dev_failed; return 0; -switch_dev_failed: - switch_dev_unregister(&wired_switch_dev); platform_dev_failed: platform_driver_unregister(&tegra_wired_jack_driver); failed: @@ -213,7 +290,6 @@ failed: void tegra_jack_exit(void) { - switch_dev_unregister(&wired_switch_dev); platform_driver_unregister(&tegra_wired_jack_driver); if (tegra_wired_jack) { -- cgit v1.2.3