diff options
author | Nikesh Oswal <noswal@nvidia.com> | 2011-07-13 10:07:46 +0530 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-07-22 17:54:42 -0700 |
commit | f378054cd7872c6065605e4d40afe619a90c40d2 (patch) | |
tree | 23991ffb2252b208fa1ea570b440a2193171543e | |
parent | 7c9090874729c5b01c9e07cd2e6a2af96af73b0b (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>
-rw-r--r-- | sound/soc/codecs/max98088.c | 87 | ||||
-rw-r--r-- | sound/soc/codecs/max98088.h | 16 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_soc_max98088.c | 71 |
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); |