Skip to content

Commit

Permalink
ASoC: max98095: add jack detection
Browse files Browse the repository at this point in the history
This change adds the logic to support using the jack detect mechanism built
in to the codec to detect both when a jack was inserted and what type of
jack is present.

This change also supports the use of an external mechanism for headphone
detection. If this mechanism exists, when the max98095_jack_detect function
is called, the hp_jack is simply passed NULL.

This change supports both simple headphones, powered headphones, microphones
and headsets with both headphones and a mic.

Signed-off-by: Rhyland Klein <rklein@nvidia.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
  • Loading branch information
Rhyland Klein authored and Mark Brown committed Apr 1, 2012
1 parent 5f1cba6 commit 9dd90c5
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 1 deletion.
12 changes: 12 additions & 0 deletions include/sound/max98095.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ struct max98095_pdata {
*/
unsigned int digmic_left_mode:1;
unsigned int digmic_right_mode:1;

/* Pin5 is the mechanical method of sensing jack insertion
* but it is something that might not be supported.
* 0 = PIN5 not supported
* 1 = PIN5 supported
*/
int jack_detect_pin5en:1;

/* Slew amount for jack detection. Calculated as 4 * (delay + 1).
* Default delay is 24 to get a time of 100ms.
*/
unsigned int jack_detect_delay;
};

#endif
158 changes: 157 additions & 1 deletion sound/soc/codecs/max98095.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <asm/div64.h>
#include <sound/max98095.h>
#include <sound/jack.h>
#include "max98095.h"

enum max98095_type {
Expand Down Expand Up @@ -51,6 +52,8 @@ struct max98095_priv {
u8 lin_state;
unsigned int mic1pre;
unsigned int mic2pre;
struct snd_soc_jack *headphone_jack;
struct snd_soc_jack *mic_jack;
};

static const u8 max98095_reg_def[M98095_REG_CNT] = {
Expand Down Expand Up @@ -2173,18 +2176,142 @@ static void max98095_handle_pdata(struct snd_soc_codec *codec)
max98095_handle_bq_pdata(codec);
}

static irqreturn_t max98095_report_jack(int irq, void *data)
{
struct snd_soc_codec *codec = data;
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
unsigned int value;
int hp_report = 0;
int mic_report = 0;

/* Read the Jack Status Register */
value = snd_soc_read(codec, M98095_007_JACK_AUTO_STS);

/* If ddone is not set, then detection isn't finished yet */
if ((value & M98095_DDONE) == 0)
return IRQ_NONE;

/* if hp, check its bit, and if set, clear it */
if ((value & M98095_HP_IN || value & M98095_LO_IN) &&
max98095->headphone_jack)
hp_report |= SND_JACK_HEADPHONE;

/* if mic, check its bit, and if set, clear it */
if ((value & M98095_MIC_IN) && max98095->mic_jack)
mic_report |= SND_JACK_MICROPHONE;

if (max98095->headphone_jack == max98095->mic_jack) {
snd_soc_jack_report(max98095->headphone_jack,
hp_report | mic_report,
SND_JACK_HEADSET);
} else {
if (max98095->headphone_jack)
snd_soc_jack_report(max98095->headphone_jack,
hp_report, SND_JACK_HEADPHONE);
if (max98095->mic_jack)
snd_soc_jack_report(max98095->mic_jack,
mic_report, SND_JACK_MICROPHONE);
}

return IRQ_HANDLED;
}

int max98095_jack_detect_enable(struct snd_soc_codec *codec)
{
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
int detect_enable = M98095_JDEN;
unsigned int slew = M98095_DEFAULT_SLEW_DELAY;

if (max98095->pdata->jack_detect_pin5en)
detect_enable |= M98095_PIN5EN;

if (max98095->jack_detect_delay)
slew = max98095->jack_detect_delay;

ret = snd_soc_write(codec, M98095_08E_JACK_DC_SLEW, slew);
if (ret < 0) {
dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
return ret;
}

/* configure auto detection to be enabled */
ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, detect_enable);
if (ret < 0) {
dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
return ret;
}

return ret;
}

int max98095_jack_detect_disable(struct snd_soc_codec *codec)
{
int ret = 0;

/* configure auto detection to be disabled */
ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, 0x0);
if (ret < 0) {
dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
return ret;
}

return ret;
}

int max98095_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
{
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *client = to_i2c_client(codec->dev);
int ret = 0;

max98095->headphone_jack = hp_jack;
max98095->mic_jack = mic_jack;

/* only progress if we have at least 1 jack pointer */
if (!hp_jack && !mic_jack)
return -EINVAL;

max98095_jack_detect_enable(codec);

/* enable interrupts for headphone jack detection */
ret = snd_soc_update_bits(codec, M98095_013_JACK_INT_EN,
M98095_IDDONE, M98095_IDDONE);
if (ret < 0) {
dev_err(codec->dev, "Failed to cfg jack irqs %d\n", ret);
return ret;
}

max98095_report_jack(client->irq, codec);
return 0;
}

#ifdef CONFIG_PM
static int max98095_suspend(struct snd_soc_codec *codec)
{
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);

if (max98095->headphone_jack || max98095->mic_jack)
max98095_jack_detect_disable(codec);

max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);

return 0;
}

static int max98095_resume(struct snd_soc_codec *codec)
{
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *client = to_i2c_client(codec->dev);

max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

if (max98095->headphone_jack || max98095->mic_jack) {
max98095_jack_detect_enable(codec);
max98095_report_jack(client->irq, codec);
}

return 0;
}
#else
Expand Down Expand Up @@ -2227,6 +2354,7 @@ static int max98095_probe(struct snd_soc_codec *codec)
{
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
struct max98095_cdata *cdata;
struct i2c_client *client;
int ret = 0;

ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
Expand All @@ -2238,6 +2366,8 @@ static int max98095_probe(struct snd_soc_codec *codec)
/* reset the codec, the DSP core, and disable all interrupts */
max98095_reset(codec);

client = to_i2c_client(codec->dev);

/* initialize private data */

max98095->sysclk = (unsigned)-1;
Expand Down Expand Up @@ -2266,11 +2396,23 @@ static int max98095_probe(struct snd_soc_codec *codec)
max98095->mic1pre = 0;
max98095->mic2pre = 0;

if (client->irq) {
/* register an audio interrupt */
ret = request_threaded_irq(client->irq, NULL,
max98095_report_jack,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"max98095", codec);
if (ret) {
dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
goto err_access;
}
}

ret = snd_soc_read(codec, M98095_0FF_REV_ID);
if (ret < 0) {
dev_err(codec->dev, "Failure reading hardware revision: %d\n",
ret);
goto err_access;
goto err_irq;
}
dev_info(codec->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');

Expand Down Expand Up @@ -2306,14 +2448,28 @@ static int max98095_probe(struct snd_soc_codec *codec)

max98095_add_widgets(codec);

return 0;

err_irq:
if (client->irq)
free_irq(client->irq, codec);
err_access:
return ret;
}

static int max98095_remove(struct snd_soc_codec *codec)
{
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *client = to_i2c_client(codec->dev);

max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);

if (max98095->headphone_jack || max98095->mic_jack)
max98095_jack_detect_disable(codec);

if (client->irq)
free_irq(client->irq, codec);

return 0;
}

Expand Down
22 changes: 22 additions & 0 deletions sound/soc/codecs/max98095.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,23 @@

/* MAX98095 Registers Bit Fields */

/* M98095_007_JACK_AUTO_STS */
#define M98095_MIC_IN (1<<3)
#define M98095_LO_IN (1<<5)
#define M98095_HP_IN (1<<6)
#define M98095_DDONE (1<<7)

/* M98095_00F_HOST_CFG */
#define M98095_SEG (1<<0)
#define M98095_XTEN (1<<1)
#define M98095_MDLLEN (1<<2)

/* M98095_013_JACK_INT_EN */
#define M98095_IMIC_IN (1<<3)
#define M98095_ILO_IN (1<<5)
#define M98095_IHP_IN (1<<6)
#define M98095_IDDONE (1<<7)

/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */
#define M98095_CLKMODE_MASK 0xFF

Expand Down Expand Up @@ -255,6 +267,10 @@
#define M98095_EQ2EN (1<<1)
#define M98095_EQ1EN (1<<0)

/* M98095_089_JACK_DET_AUTO */
#define M98095_PIN5EN (1<<2)
#define M98095_JDEN (1<<7)

/* M98095_090_PWR_EN_IN */
#define M98095_INEN (1<<7)
#define M98095_MB2EN (1<<3)
Expand Down Expand Up @@ -296,4 +312,10 @@
#define M98095_174_DAI1_BQ_BASE 0x74
#define M98095_17E_DAI2_BQ_BASE 0x7E

/* Default Delay used in Slew Rate Calculation for Jack detection */
#define M98095_DEFAULT_SLEW_DELAY 0x18

extern int max98095_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);

#endif

0 comments on commit 9dd90c5

Please sign in to comment.