diff options
author | Nikesh Oswal <noswal@nvidia.com> | 2011-02-21 16:22:22 +0530 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-02-22 18:10:36 -0800 |
commit | e7f289e8956da115741ff6f277f123e835314da5 (patch) | |
tree | 56e71e4efe4c85bbdae4f9135d5946ab57b8cc58 | |
parent | 44d15e83d697ebc2a1f8b7717d66f9c007371333 (diff) |
[tegra alsa] headphone detection
added headphone detection logic for WM8753 on whistler
Change-Id: I7c1c2a75480050ba03cb037393ead44761a7706f
Reviewed-on: http://git-master/r/20298
Tested-by: Nikesh Oswal <noswal@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r-- | sound/soc/tegra/tegra_soc.h | 1 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_soc_wm8753.c | 169 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_wired_jack.c | 8 |
3 files changed, 176 insertions, 2 deletions
diff --git a/sound/soc/tegra/tegra_soc.h b/sound/soc/tegra/tegra_soc.h index 4910b8669dd9..1510d6dbee76 100644 --- a/sound/soc/tegra/tegra_soc.h +++ b/sound/soc/tegra/tegra_soc.h @@ -106,6 +106,7 @@ void tegra_controls_exit(void); int tegra_jack_init(struct snd_soc_codec *codec); void tegra_jack_exit(void); +void tegra_switch_set_state(int state); void setup_dma_request(struct snd_pcm_substream *substream, struct tegra_dma_req *req, diff --git a/sound/soc/tegra/tegra_soc_wm8753.c b/sound/soc/tegra/tegra_soc_wm8753.c index b6a4017798b9..0be84a031354 100644 --- a/sound/soc/tegra/tegra_soc_wm8753.c +++ b/sound/soc/tegra/tegra_soc_wm8753.c @@ -28,6 +28,15 @@ #include "../codecs/wm8753.h" #include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <sound/jack.h> +#include <linux/switch.h> +#include <mach/gpio.h> +#include <mach/audio.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/workqueue.h> + #define WM8753_PWR1_VMIDSEL_1 1<<8 #define WM8753_PWR1_VMIDSEL_0 1<<7 #define WM8753_PWR1_VREF 1<<6 @@ -99,6 +108,36 @@ #define WM8753_RINVOL_MAX 0x11F +#define WM8753_GPIO2_GP2M_2 1<<5 +#define WM8753_GPIO2_GP2M_1 1<<4 +#define WM8753_GPIO2_GP2M_0 1<<3 + +#define WM8753_GPIO1_INTCON_1 1<<8 +#define WM8753_GPIO1_INTCON_0 1<<7 + +#define WM8753_INTPOL_GPIO4IPOL 1<<4 + +#define WM8753_INTEN_MICSHTEN 1<<0 +#define WM8753_INTEN_MICDETEN 1<<1 +#define WM8753_INTEN_GPIO3IEN 1<<3 +#define WM8753_INTEN_GPIO4IEN 1<<4 +#define WM8753_INTEN_GPIO5IEN 1<<5 +#define WM8753_INTEN_HPSWIEN 1<<6 +#define WM8753_INTEN_TSDIEN 1<<7 + +/* Board Specific GPIO configuration for Whistler */ +#define TEGRA_GPIO_PW3 179 + +static struct wm8753_headphone_jack +{ + struct snd_jack *jack; + int gpio; + struct work_struct work; + struct snd_soc_codec* pcodec; +}; + +static struct wm8753_headphone_jack* wm8753_jack = NULL; + static struct platform_device *tegra_snd_device; static struct regulator* wm8753_reg; @@ -325,10 +364,132 @@ static struct snd_soc_ops tegra_voice_ops = { .hw_free = tegra_voice_hw_free, }; +static void wm8753_intr_work(struct work_struct *work) +{ + unsigned int value; + + /* Do something here */ + mutex_lock(&wm8753_jack->pcodec->mutex); + + /* GPIO4 interrupt disable (also disable other interrupts) */ + value = snd_soc_read(wm8753_jack->pcodec, WM8753_INTEN); + value &= ~(WM8753_INTEN_MICSHTEN | WM8753_INTEN_MICDETEN | + WM8753_INTEN_GPIO3IEN | WM8753_INTEN_HPSWIEN | + WM8753_INTEN_GPIO5IEN | WM8753_INTEN_TSDIEN | + WM8753_INTEN_GPIO4IEN); + snd_soc_write(wm8753_jack->pcodec, WM8753_INTEN, value); + + /* Invert GPIO4 interrupt polarity */ + value = snd_soc_read(wm8753_jack->pcodec, WM8753_INTPOL); + value &= WM8753_INTPOL_GPIO4IPOL; + if ((value) & ((WM8753_INTPOL_GPIO4IPOL))) { + tegra_switch_set_state(SND_JACK_HEADPHONE); + snd_jack_report(wm8753_jack->jack, SND_JACK_HEADPHONE); + value &= ~(WM8753_INTPOL_GPIO4IPOL); + } + else { + tegra_switch_set_state(0); + snd_jack_report(wm8753_jack->jack, 0); + value |= (WM8753_INTPOL_GPIO4IPOL); + } + snd_soc_write(wm8753_jack->pcodec, WM8753_INTPOL, value); + + /* GPIO4 interrupt enable */ + value = snd_soc_read(wm8753_jack->pcodec, WM8753_INTEN); + value |= (WM8753_INTEN_GPIO4IEN); + value &= ~(WM8753_INTEN_MICSHTEN | WM8753_INTEN_MICDETEN | + WM8753_INTEN_GPIO3IEN | WM8753_INTEN_HPSWIEN | + WM8753_INTEN_GPIO5IEN | WM8753_INTEN_TSDIEN); + snd_soc_write(wm8753_jack->pcodec, WM8753_INTEN, value); + + mutex_unlock(&wm8753_jack->pcodec->mutex); +} + +static irqreturn_t wm8753_irq(int irq, void *data) +{ + schedule_work(&wm8753_jack->work); + return IRQ_HANDLED; +} static int tegra_codec_init(struct snd_soc_codec *codec) { - return tegra_controls_init(codec); + int ret; + unsigned int value; + + ret = tegra_controls_init(codec); + if (ret < 0) + goto failed; + + if (!wm8753_jack) { + + wm8753_jack = kzalloc(sizeof(*wm8753_jack), GFP_KERNEL); + if (!wm8753_jack) { + pr_err("failed to allocate wm8753-jack\n"); + return -ENOMEM; + } + + wm8753_jack->gpio = TEGRA_GPIO_PW3; + wm8753_jack->pcodec = codec; + + INIT_WORK(&wm8753_jack->work, wm8753_intr_work); + + ret = snd_jack_new(codec->card, "Headphone Jack", SND_JACK_HEADPHONE, + &wm8753_jack->jack); + if (ret < 0) + goto failed; + + ret = gpio_request(wm8753_jack->gpio, "headphone-detect-gpio"); + if (ret) + goto failed; + + ret = gpio_direction_input(wm8753_jack->gpio); + if (ret) + goto gpio_failed; + + tegra_gpio_enable(wm8753_jack->gpio); + + ret = request_irq(gpio_to_irq(wm8753_jack->gpio), + wm8753_irq, + IRQF_TRIGGER_FALLING, + "wm8753", + wm8753_jack); + + if (ret) + goto gpio_failed; + + /* Configure GPIO2 pin to generate the interrupt */ + value = snd_soc_read(codec, WM8753_GPIO2); + value |= (WM8753_GPIO2_GP2M_0 | WM8753_GPIO2_GP2M_1); + value &= ~(WM8753_GPIO2_GP2M_2); + snd_soc_write(codec, WM8753_GPIO2, value); + + /* Active low Interrupt */ + value = snd_soc_read(codec, WM8753_GPIO1); + value |= (WM8753_GPIO1_INTCON_1 | WM8753_GPIO1_INTCON_0); + snd_soc_write(codec, WM8753_GPIO1, value); + + /* GPIO4 interrupt polarity -- interupt when low i.e Headphone connected */ + value = snd_soc_read(codec, WM8753_INTPOL); + value |= (WM8753_INTPOL_GPIO4IPOL); + snd_soc_write(codec, WM8753_INTPOL, value); + + /* GPIO4 interrupt enable and disable other interrupts */ + value = snd_soc_read(codec, WM8753_INTEN); + value |= (WM8753_INTEN_GPIO4IEN); + value &= ~(WM8753_INTEN_MICSHTEN | WM8753_INTEN_MICDETEN | + WM8753_INTEN_GPIO3IEN | WM8753_INTEN_HPSWIEN | + WM8753_INTEN_GPIO5IEN | WM8753_INTEN_TSDIEN); + snd_soc_write(codec, WM8753_INTEN, value); + } + + return ret; + +gpio_failed: + gpio_free(wm8753_jack->gpio); +failed: + kfree(wm8753_jack); + wm8753_jack = NULL; + return ret; } static struct snd_soc_dai_link tegra_soc_dai[] = { @@ -414,6 +575,12 @@ static void __exit tegra_exit(void) platform_device_unregister(tegra_snd_device); regulator_disable(wm8753_reg); regulator_put(wm8753_reg); + + if (wm8753_jack) { + gpio_free(wm8753_jack->gpio); + kfree(wm8753_jack); + wm8753_jack = NULL; + } } module_init(tegra_init); diff --git a/sound/soc/tegra/tegra_wired_jack.c b/sound/soc/tegra/tegra_wired_jack.c index 23eeecbb3422..7967caefce33 100644 --- a/sound/soc/tegra/tegra_wired_jack.c +++ b/sound/soc/tegra/tegra_wired_jack.c @@ -61,6 +61,12 @@ static struct switch_dev wired_switch_dev = { .name = "h2w", }; +void tegra_switch_set_state(int state) +{ + + switch_set_state(&wired_switch_dev, state); +} + static int wired_swith_notify(struct notifier_block *self, unsigned long action, void* dev) { @@ -77,7 +83,7 @@ static int wired_swith_notify(struct notifier_block *self, state = 0; } - switch_set_state(&wired_switch_dev, state); + tegra_switch_set_state(state); return NOTIFY_OK; } |