#include <linux/slab.h>
#include <asm/div64.h>
#include <sound/max98088.h>
+#include <sound/jack.h>
#include "max98088.h"
enum max98088_type {
unsigned int mic1pre;
unsigned int mic2pre;
unsigned int extmic_mode;
+ int irq;
+ struct snd_soc_jack *headset_jack;
+ unsigned int jk_sns;
+ int jack_report;
};
static const u8 max98088_reg[M98088_REG_CNT] = {
{ 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
{ 0xFF, 0xFF, 0 }, /* 2D SPK control */
{ 0xFF, 0xFF, 0 }, /* 2E sidetone */
- { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
+ { 0xFF, 0xFF, 1 }, /* 2F DAI1 playback level */
{ 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
{ 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
{ 0xFF, 0x00, 1 }, /* FF */
};
-static int max98088_volatile_register(unsigned int reg)
+static int max98088_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
{
return max98088_access[reg].vol;
}
ARRAY_SIZE(max98088_exmode_texts),
max98088_exmode_texts,
max98088_exmode_values);
-static const struct snd_kcontrol_new max98088_exmode_controls =
- SOC_DAPM_VALUE_ENUM("Route", max98088_exmode_enum);
static const char *max98088_ex_thresh[] = { /* volts PP */
"0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+ SOC_ENUM("EX Limiter Mode", max98088_exmode_enum),
SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
SOC_SINGLE("THD Limiter Threshold", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
SOC_SINGLE("THD Limiter Time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+ SOC_SINGLE("Digital Mic Enable", M98088_REG_48_CFG_MIC, 4, 3, 0),
};
/* Left speaker mixer switch */
static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
/* Left headphone mixer switch */
static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
/* Left earpiece/receiver mixer switch */
static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
- SND_SOC_DAPM_MUX("EX Limiter Mode", SND_SOC_NOPM, 0, 0,
- &max98088_exmode_controls),
-
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
SND_SOC_DAPM_OUTPUT("SPKL"),
SND_SOC_DAPM_INPUT("INB2"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route max98088_audio_map[] = {
/* Left headphone output mixer */
{"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
{"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
{"MIC2 Input", NULL, "MIC2"},
};
-static int max98088_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, max98088_dapm_widgets,
- ARRAY_SIZE(max98088_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- snd_soc_add_controls(codec, max98088_snd_controls,
- ARRAY_SIZE(max98088_snd_controls));
-
- snd_soc_dapm_new_widgets(dapm);
- return 0;
-}
-
/* codec mclk clock divider coefficients */
static const struct {
u32 rate;
if (freq == max98088->sysclk)
return 0;
- max98088->sysclk = freq; /* remember current sysclk */
-
/* Setup clocks for slave mode, and using the PLL
* PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
* 0x02 (when master clk is 20MHz to 30MHz)..
return 0;
}
+static int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg;
+
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, M98088_REG_2F_LVL_DAI1_PLAY,
+ M98088_DAI_MUTE_MASK, reg);
+ return 0;
+}
+
+static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg;
+
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, M98088_REG_31_LVL_DAI2_PLAY,
+ M98088_DAI_MUTE_MASK, reg);
+ return 0;
+}
+
static void max98088_sync_cache(struct snd_soc_codec *codec)
{
- u16 *reg_cache = codec->reg_cache;
+ u8 *reg_cache = codec->reg_cache;
int i;
if (!codec->cache_sync)
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
max98088_sync_cache(codec);
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ M98088_SHDNRUN, M98088_SHDNRUN);
+
snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
M98088_MBEN, M98088_MBEN);
break;
case SND_SOC_BIAS_OFF:
snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
M98088_MBEN, 0);
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ M98088_SHDNRUN, 0);
codec->cache_sync = 1;
break;
}
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai1_set_fmt,
.hw_params = max98088_dai1_hw_params,
+ .digital_mute = max98088_dai1_digital_mute,
};
static struct snd_soc_dai_ops max98088_dai2_ops = {
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai2_set_fmt,
.hw_params = max98088_dai2_hw_params,
+ .digital_mute = max98088_dai2_digital_mute,
};
static struct snd_soc_dai_driver max98088_dai[] = {
struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
struct max98088_pdata *pdata = max98088->pdata;
u8 regval = 0;
+ unsigned int debounce_time;
if (!pdata) {
dev_dbg(codec->dev, "No platform data\n");
/* Configure equalizers */
if (pdata->eq_cfgcnt)
max98088_handle_eq_pdata(codec);
+
+ /* Configure the debounce time */
+ if (max98088->irq) {
+ switch (pdata->debounce_time_ms) {
+ case 25:
+ debounce_time = M98088_JDEB_25;
+ break;
+ case 50:
+ debounce_time = M98088_JDEB_50;
+ break;
+ case 100:
+ debounce_time = M98088_JDEB_100;
+ break;
+ case 200:
+ default:
+ debounce_time = M98088_JDEB_200;
+ }
+ snd_soc_update_bits(codec, M98088_REG_4B_CFG_JACKDET,
+ M98088_JDEB, debounce_time);
+ }
}
-#ifdef CONFIG_PM
-static int max98088_suspend(struct snd_soc_codec *codec, pm_message_t state)
+int max98088_report_jack(struct snd_soc_codec *codec)
{
- max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ unsigned int jk_sns_curr;
+ int jack_report_curr = 0;
+
+ /* Read the Jack Status Register*/
+ jk_sns_curr = (snd_soc_read(codec, M98088_REG_02_JACK_STAUS))
+ & (M98088_JKSNS_7 | M98088_JKSNS_6);
+
+ if (max98088->jk_sns == M98088_NONE && jk_sns_curr == M98088_HP)
+ jack_report_curr = SND_JACK_HEADPHONE;
+ else if (max98088->jk_sns == M98088_NONE && jk_sns_curr == M98088_HS)
+ jack_report_curr = SND_JACK_HEADSET;
+ else if ((max98088->jk_sns == M98088_HP || max98088->jk_sns == M98088_HS)
+ && jk_sns_curr == M98088_NONE)
+ jack_report_curr = 0;
+ else
+ jack_report_curr = max98088->jack_report;
+
+ max98088->jack_report = jack_report_curr;
+ max98088->jk_sns = jk_sns_curr;
+
+ snd_soc_jack_report(max98088->headset_jack,
+ jack_report_curr, SND_JACK_HEADSET);
return 0;
}
-static int max98088_resume(struct snd_soc_codec *codec)
+static irqreturn_t max98088_jack_handler(int irq, void *data)
{
- max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ struct snd_soc_codec *codec = data;
+
+ /*clear the interrupt by reading the status register */
+ snd_soc_read(codec, M98088_REG_00_IRQ_STATUS);
+ max98088_report_jack(codec);
+
+ return IRQ_HANDLED;
+}
+
+int max98088_headset_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack, enum snd_jack_types type)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ max98088->headset_jack = jack;
+ max98088->jk_sns = M98088_NONE;
+ max98088->jack_report = 0;
+
+ if (max98088->irq) {
+ if (type & SND_JACK_HEADSET) {
+ /* headphone + microphone detection */
+ snd_soc_update_bits(codec, M98088_REG_4E_BIAS_CNTL,
+ M98088_JDWK, 0);
+ } else {
+ /* headphone detection only*/
+ snd_soc_update_bits(codec, M98088_REG_4E_BIAS_CNTL,
+ M98088_JDWK, 1);
+ }
+ /* Enable the Jack Detection Circuitry */
+ snd_soc_update_bits(codec, M98088_REG_4B_CFG_JACKDET,
+ M98088_JDETEN, M98088_JDETEN);
+
+ /*JDET is always set the first time JDETEN is set,
+ so clear it*/
+ snd_soc_read(codec, M98088_REG_00_IRQ_STATUS);
+
+ /*after setting JDETEN, JKSNS would be set after hw
+ debounce time so wait before reading the status*/
+ msleep(max98088->pdata->debounce_time_ms);
+
+ /*report jack status at boot-up*/
+ max98088_report_jack(codec);
+
+ /*Enable the jack detection interrupt*/
+ snd_soc_update_bits(codec, M98088_REG_0F_IRQ_ENABLE,
+ M98088_IJDET, M98088_IJDET);
+ }
return 0;
}
-#else
-#define max98088_suspend NULL
-#define max98088_resume NULL
-#endif
+EXPORT_SYMBOL_GPL(max98088_headset_detect);
static int max98088_probe(struct snd_soc_codec *codec)
{
int ret = 0;
codec->cache_sync = 1;
+ codec->dapm.idle_bias_off = 1;
ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
if (ret != 0) {
max98088->mic2pre = 0;
ret = snd_soc_read(codec, M98088_REG_FF_REV_ID);
- if (ret < 0) {
+ if (ret != 0x40) {
dev_err(codec->dev, "Failed to read device revision: %d\n",
ret);
+ ret = -ENODEV;
goto err_access;
}
dev_info(codec->dev, "revision %c\n", ret + 'A');
+ if (max98088->irq) {
+ /* register an audio interrupt */
+ ret = request_threaded_irq(max98088->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_access;
+ }
+ }
+
snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
/* initialize registers cache to hardware default */
max98088_handle_pdata(codec);
- max98088_add_widgets(codec);
+ snd_soc_add_controls(codec, max98088_snd_controls,
+ ARRAY_SIZE(max98088_snd_controls));
err_access:
return ret;
return 0;
}
+#ifdef CONFIG_PM
+static int max98088_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+ disable_irq(max98088->irq);
+ max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int max98088_resume(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+ max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ max98088_report_jack(codec);
+ enable_irq(max98088->irq);
+
+ return 0;
+}
+#else
+#define max98088_suspend NULL
+#define max98088_resume NULL
+#endif
+
static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
.probe = max98088_probe,
.remove = max98088_remove,
.reg_word_size = sizeof(u8),
.reg_cache_default = max98088_reg,
.volatile_register = max98088_volatile_register,
+ .dapm_widgets = max98088_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets),
+ .dapm_routes = max98088_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98088_audio_map),
};
static int max98088_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, max98088);
max98088->control_data = i2c;
max98088->pdata = i2c->dev.platform_data;
+ max98088->irq = i2c->irq;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_max98088, &max98088_dai[0], 2);