summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikesh Oswal <noswal@nvidia.com>2011-02-21 16:22:22 +0530
committerVarun Colbert <vcolbert@nvidia.com>2011-02-22 18:10:36 -0800
commite7f289e8956da115741ff6f277f123e835314da5 (patch)
tree56e71e4efe4c85bbdae4f9135d5946ab57b8cc58
parent44d15e83d697ebc2a1f8b7717d66f9c007371333 (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.h1
-rw-r--r--sound/soc/tegra/tegra_soc_wm8753.c169
-rw-r--r--sound/soc/tegra/tegra_wired_jack.c8
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;
}