summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorNikesh Oswal <noswal@nvidia.com>2011-07-13 10:07:46 +0530
committerVarun Colbert <vcolbert@nvidia.com>2011-07-22 17:54:42 -0700
commitf378054cd7872c6065605e4d40afe619a90c40d2 (patch)
tree23991ffb2252b208fa1ea570b440a2193171543e /sound
parent7c9090874729c5b01c9e07cd2e6a2af96af73b0b (diff)
tegra-alsa: headset detection logic
added headset and headphone detection logic for maxim 98088 codec on enterprise board Bug: 836629 Change-Id: I5405b52fbd81042816bc2a1d288f5fc32274f2b5 Reviewed-on: http://git-master/r/40774 Tested-by: Ravindra Lokhande <rlokhande@nvidia.com> Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com> Reviewed-by: Thomas Cherry <tcherry@nvidia.com> Reviewed-by: Scott Peterson <speterson@nvidia.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/max98088.c87
-rw-r--r--sound/soc/codecs/max98088.h16
-rw-r--r--sound/soc/tegra/tegra_soc_max98088.c71
3 files changed, 168 insertions, 6 deletions
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 1453fa251b47..f397035f4728 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <asm/div64.h>
#include <sound/max98088.h>
+#include <sound/jack.h>
#include "max98088.h"
static struct snd_soc_codec *max98088_codec;
@@ -62,6 +63,7 @@ struct max98088_priv {
unsigned int mic1pre;
unsigned int mic2pre;
unsigned int extmic_mode;
+ struct snd_soc_jack *headset_jack;
};
static const u8 max98088_reg[M98088_REG_CNT] = {
@@ -2061,6 +2063,15 @@ static int max98088_resume(struct platform_device *pdev)
#define max98088_resume NULL
#endif
+int max98088_headset_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ max98088->headset_jack = jack;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max98088_headset_detect);
+
static int max98088_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -2145,6 +2156,22 @@ static int max98088_probe(struct platform_device *pdev)
snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
M98088_S2NORMAL|M98088_SDATA);
+ /* Enable Interrupts for Headphone and Headset Detection */
+
+ /* Set JDWK to 0 so as to detect both Headphone and Headset */
+ snd_soc_update_bits(codec, M98088_REG_4E_BIAS_CNTL,
+ M98088_REG_4E_BIAS_CNTL_JDWK, 0);
+
+ /* Enable the Jack Detection Circuitry */
+ snd_soc_update_bits(codec, M98088_REG_4B_CFG_JACKDET,
+ M98088_REG_4B_CFG_JACKDET_JDETEN,
+ M98088_REG_4B_CFG_JACKDET_JDETEN);
+
+ /* Enable the Jack Detect Interrupt */
+ snd_soc_update_bits(codec, M98088_REG_0F_IRQ_ENABLE,
+ M98088_REG_0F_IRQ_ENABLE_IJDET,
+ M98088_REG_0F_IRQ_ENABLE_IJDET);
+
max98088_handle_pdata(codec);
max98088_add_widgets(codec);
@@ -2158,6 +2185,54 @@ pcm_err:
return ret;
}
+static irqreturn_t max98088_jack_handler(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ unsigned int value;
+ unsigned int jksns_7;
+ unsigned int jksns_6;
+
+ /* Disable the Jack Detect Interrupt by reading the irq enable
+ and status registers*/
+ value = snd_soc_read(codec, M98088_REG_0F_IRQ_ENABLE);
+ value = snd_soc_read(codec, M98088_REG_00_IRQ_STATUS);
+
+
+ /* Read the Jack Status Register*/
+ value = snd_soc_read(codec, M98088_REG_02_JACK_STAUS);
+
+ if (value & M98088_REG_02_JACK_STAUS_JKSNS_7)
+ jksns_7 = 1;
+ else
+ jksns_7 = 0;
+
+ if (value & M98088_REG_02_JACK_STAUS_JKSNS_6)
+ jksns_6 = 1;
+ else
+ jksns_6 = 0;
+
+ /* No Headphone or Headset*/
+ if ((jksns_7 == 1) && (jksns_6 == 1)) {
+ snd_soc_jack_report(max98088->headset_jack,
+ SND_JACK_NO_TYPE_SPECIFIED, SND_JACK_HEADSET);
+ }
+
+ /* Headphone Detected */
+ if ((jksns_7 == 0) && (jksns_6 == 0)) {
+ snd_soc_jack_report(max98088->headset_jack,
+ SND_JACK_HEADPHONE, SND_JACK_HEADSET);
+ }
+
+ /* Headset Detected */
+ if ((jksns_7 == 0) && (jksns_6 == 1)) {
+ snd_soc_jack_report(max98088->headset_jack,
+ SND_JACK_HEADSET, SND_JACK_HEADSET);
+ }
+
+ return IRQ_HANDLED;
+}
+
static int max98088_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -2227,6 +2302,18 @@ static int max98088_i2c_probe(struct i2c_client *i2c,
codec->reg_cache = &max98088->reg_cache;
codec->volatile_register = max98088_volatile_register;
+ if (i2c->irq) {
+ /* audio interrupt */
+ ret = request_threaded_irq(i2c->irq, NULL,
+ max98088_jack_handler,
+ IRQF_TRIGGER_FALLING,
+ "max98088", codec);
+ if (ret) {
+ dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ goto err_codec;
+ }
+ }
+
/* power on device */
max98088_codec = codec;
for (i = 0; i < codec->num_dai; i++)
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
index 6bdcf4a7e957..e802cf6e0c0d 100644
--- a/sound/soc/codecs/max98088.h
+++ b/sound/soc/codecs/max98088.h
@@ -194,6 +194,22 @@
#define M98088_PWRSV8K (1<<1)
#define M98088_PWRSV (1<<0)
+/* M98088_REG_4E_BIAS_CNTL */
+#define M98088_REG_4E_BIAS_CNTL_JDWK (1<<1)
+
+/* M98088_REG_4B_CFG_JACKDET */
+#define M98088_REG_4B_CFG_JACKDET_JDETEN (1<<7)
+
+/* M98088_REG_0F_IRQ_ENABLE */
+#define M98088_REG_0F_IRQ_ENABLE_IJDET (1<<1)
+
+/* M98088_REG_02_JACK_STAUS */
+#define M98088_REG_02_JACK_STAUS_JKSNS_7 (1<<7)
+#define M98088_REG_02_JACK_STAUS_JKSNS_6 (1<<6)
+
+int max98088_headset_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack);
+
/* Line inputs */
#define LINE_INA 0
#define LINE_INB 1
diff --git a/sound/soc/tegra/tegra_soc_max98088.c b/sound/soc/tegra/tegra_soc_max98088.c
index 1243c7dc6407..0de23df927d1 100644
--- a/sound/soc/tegra/tegra_soc_max98088.c
+++ b/sound/soc/tegra/tegra_soc_max98088.c
@@ -23,6 +23,7 @@
#include <linux/gpio.h>
#include "tegra_soc.h"
#include "../codecs/max98088.h"
+#include <sound/jack.h>
static struct platform_device *tegra_snd_device;
@@ -32,6 +33,35 @@ extern struct snd_soc_dai tegra_generic_codec_dai[];
extern struct snd_soc_platform tegra_soc_platform;
extern struct wired_jack_conf tegra_wired_jack_conf;
+static struct snd_soc_jack *max98088_jack;
+
+/* These values are copied from WiredAccessoryObserver */
+enum headset_state {
+ BIT_NO_HEADSET = 0,
+ BIT_HEADSET = (1 << 0),
+ BIT_HEADSET_NO_MIC = (1 << 1),
+};
+
+static int headset_switch_notify(struct notifier_block *self,
+ unsigned long action, void *dev)
+{
+
+ if (action == SND_JACK_NO_TYPE_SPECIFIED)
+ tegra_switch_set_state(BIT_NO_HEADSET);
+
+ if (action == SND_JACK_HEADPHONE)
+ tegra_switch_set_state(BIT_HEADSET_NO_MIC);
+
+ if (action == SND_JACK_HEADSET)
+ tegra_switch_set_state(BIT_HEADSET);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block headset_switch_nb = {
+ .notifier_call = headset_switch_notify,
+};
+
void speaker_settings(struct snd_soc_codec *codec, int value)
{
}
@@ -366,6 +396,7 @@ static int tegra_codec_init(struct snd_soc_codec *codec)
{
struct tegra_audio_data *audio_data = codec->socdev->codec_data;
int err = 0;
+ int ret = 0;
if (!audio_data->init_done) {
@@ -373,14 +404,14 @@ static int tegra_codec_init(struct snd_soc_codec *codec)
if (err) {
pr_err(" Failed get dap mclk\n");
err = -ENODEV;
- goto wm8903_init_fail;
+ goto max98088_init_fail;
}
err = tegra_das_enable_mclk();
if (err) {
pr_err(" Failed to enable dap mclk\n");
err = -ENODEV;
- goto wm8903_init_fail;
+ goto max98088_init_fail;
}
/* Add tegra specific widgets */
@@ -395,7 +426,7 @@ static int tegra_codec_init(struct snd_soc_codec *codec)
err = tegra_jack_init(codec);
if (err < 0) {
pr_err("Failed in jack init\n");
- goto wm8903_init_fail;
+ goto max98088_init_fail;
}
/* Default to OFF */
@@ -404,7 +435,32 @@ static int tegra_codec_init(struct snd_soc_codec *codec)
err = tegra_controls_init(codec);
if (err < 0) {
pr_err("Failed in controls init\n");
- goto wm8903_init_fail;
+ goto max98088_init_fail;
+ }
+
+ if (!max98088_jack) {
+
+ /* Add jack detection */
+
+ max98088_jack = kzalloc(sizeof(*max98088_jack),
+ GFP_KERNEL);
+ if (!max98088_jack) {
+ pr_err("failed to allocate max98088-jack\n");
+ ret = -ENOMEM;
+ goto max98088_init_fail;
+ }
+
+ ret = snd_soc_jack_new(codec->socdev->card,
+ "Headset Jack",
+ SND_JACK_HEADSET,
+ max98088_jack);
+ if (ret < 0)
+ goto failed;
+
+ snd_soc_jack_notifier_register(max98088_jack,
+ &headset_switch_nb);
+
+ max98088_headset_detect(codec, max98088_jack);
}
audio_data->codec = codec;
@@ -415,8 +471,9 @@ static int tegra_codec_init(struct snd_soc_codec *codec)
return err;
-wm8903_init_fail:
-
+failed:
+ kfree(max98088_jack);
+max98088_init_fail:
tegra_das_disable_mclk();
tegra_das_close();
return err;
@@ -519,6 +576,8 @@ static void __exit tegra_exit(void)
{
tegra_jack_exit();
platform_device_unregister(tegra_snd_device);
+ kfree(max98088_jack);
+ max98088_jack = NULL;
}
module_init(tegra_init);