Commit 6e747d53 authored by Bard Liao's avatar Bard Liao Committed by Mark Brown

ASoC: rt5645: Adds push button support for rt5650

rt5650 support headset button detection. Currently, the button detection
is only implemented for rt5650 codec. The button detection configuration
register's default value is different from rt5645.
And we didn't touch the register in the driver, so we will get the wrong
value when we dump the registers. We will fix it in another patch.
Signed-off-by: default avatarBard Liao <bardliao@realtek.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent c0d44e59
...@@ -2371,6 +2371,8 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, ...@@ -2371,6 +2371,8 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int rt5645_set_bias_level(struct snd_soc_codec *codec, static int rt5645_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level) enum snd_soc_bias_level level)
{ {
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
switch (level) { switch (level) {
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
...@@ -2401,8 +2403,9 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, ...@@ -2401,8 +2403,9 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_OFF:
snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100); snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100);
snd_soc_update_bits(codec, RT5645_GEN_CTRL1, if (!rt5645->en_button_func)
RT5645_DIG_GATE_CTRL, 0); snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
RT5645_DIG_GATE_CTRL, 0);
snd_soc_update_bits(codec, RT5645_PWR_ANLG1, snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB | RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2 | RT5645_PWR_BG | RT5645_PWR_VREF2 |
...@@ -2417,28 +2420,71 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, ...@@ -2417,28 +2420,71 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
return 0; return 0;
} }
static int rt5645_jack_detect(struct snd_soc_codec *codec) static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
bool enable)
{ {
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
int gpio_state, jack_type = 0;
unsigned int val;
if (!gpio_is_valid(rt5645->pdata.hp_det_gpio)) { if (enable) {
dev_err(codec->dev, "invalid gpio\n"); snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm,
return -EINVAL; "ADC L power");
snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm,
"ADC R power");
snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm,
"LDO2");
snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm,
"Mic Det Power");
snd_soc_dapm_sync_unlocked(&codec->dapm);
snd_soc_update_bits(codec,
RT5645_INT_IRQ_ST, 0x8, 0x8);
snd_soc_update_bits(codec,
RT5650_4BTN_IL_CMD2, 0x8000, 0x8000);
snd_soc_read(codec, RT5650_4BTN_IL_CMD1);
pr_debug("%s read %x = %x\n", __func__, RT5650_4BTN_IL_CMD1,
snd_soc_read(codec, RT5650_4BTN_IL_CMD1));
} else {
snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD2, 0x8000, 0x0);
snd_soc_update_bits(codec, RT5645_INT_IRQ_ST, 0x8, 0x0);
snd_soc_dapm_disable_pin_unlocked(&codec->dapm,
"ADC L power");
snd_soc_dapm_disable_pin_unlocked(&codec->dapm,
"ADC R power");
if (rt5645->pdata.jd_mode == 0)
snd_soc_dapm_disable_pin_unlocked(&codec->dapm,
"LDO2");
snd_soc_dapm_disable_pin_unlocked(&codec->dapm,
"Mic Det Power");
snd_soc_dapm_sync_unlocked(&codec->dapm);
} }
gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio); }
dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio, static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
gpio_state); {
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) || if (jack_insert) {
(!rt5645->pdata.gpio_hp_det_active_high && !gpio_state)) { if (codec->component.card->instantiated) {
snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias1"); snd_soc_dapm_force_enable_pin(&codec->dapm,
snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias2"); "micbias1");
snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2"); snd_soc_dapm_force_enable_pin(&codec->dapm,
snd_soc_dapm_force_enable_pin(&codec->dapm, "Mic Det Power"); "micbias2");
snd_soc_dapm_sync(&codec->dapm); snd_soc_dapm_force_enable_pin(&codec->dapm,
"LDO2");
snd_soc_dapm_force_enable_pin(&codec->dapm,
"Mic Det Power");
snd_soc_dapm_sync(&codec->dapm);
} else {
/* Power up necessary bits for JD if dapm is
not ready yet */
snd_soc_update_bits(codec, RT5645_PWR_ANLG2,
RT5645_PWR_MB1 | RT5645_PWR_MB2,
RT5645_PWR_MB1 | RT5645_PWR_MB2);
snd_soc_update_bits(codec, RT5645_PWR_MIXER,
RT5645_PWR_LDO2, RT5645_PWR_LDO2);
snd_soc_update_bits(codec, RT5645_PWR_VOL,
RT5645_PWR_MIC_DET, RT5645_PWR_MIC_DET);
}
snd_soc_write(codec, RT5645_IN1_CTRL1, 0x0006); snd_soc_write(codec, RT5645_IN1_CTRL1, 0x0006);
snd_soc_write(codec, RT5645_JD_CTRL3, 0x00b0); snd_soc_write(codec, RT5645_JD_CTRL3, 0x00b0);
...@@ -2452,32 +2498,62 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec) ...@@ -2452,32 +2498,62 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec)
val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7; val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7;
dev_dbg(codec->dev, "val = %d\n", val); dev_dbg(codec->dev, "val = %d\n", val);
if (val == 1 || val == 2) if (codec->component.card->instantiated) {
jack_type = SND_JACK_HEADSET; snd_soc_dapm_disable_pin(&codec->dapm, "micbias1");
else snd_soc_dapm_disable_pin(&codec->dapm, "micbias2");
jack_type = SND_JACK_HEADPHONE; if (rt5645->pdata.jd_mode == 0)
snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
snd_soc_dapm_disable_pin(&codec->dapm,
"Mic Det Power");
snd_soc_dapm_sync(&codec->dapm);
} else {
snd_soc_update_bits(codec, RT5645_PWR_ANLG2,
RT5645_PWR_MB1 | RT5645_PWR_MB2, 0);
if (rt5645->pdata.jd_mode == 0)
snd_soc_update_bits(codec, RT5645_PWR_MIXER,
RT5645_PWR_LDO2, 0);
snd_soc_update_bits(codec, RT5645_PWR_VOL,
RT5645_PWR_MIC_DET, 0);
}
snd_soc_dapm_disable_pin(&codec->dapm, "micbias1"); if (val == 1 || val == 2) {
snd_soc_dapm_disable_pin(&codec->dapm, "micbias2"); rt5645->jack_type = SND_JACK_HEADSET;
if (rt5645->pdata.jd_mode == 0) if (rt5645->en_button_func) {
snd_soc_dapm_disable_pin(&codec->dapm, "LDO2"); msleep(100);
snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); rt5645_enable_push_button_irq(codec, true);
snd_soc_dapm_sync(&codec->dapm); }
} else {
rt5645->jack_type = SND_JACK_HEADPHONE;
}
} else { /* jack out */
rt5645->jack_type = 0;
if (rt5645->en_button_func)
rt5645_enable_push_button_irq(codec, false);
} }
snd_soc_jack_report(rt5645->hp_jack, jack_type, SND_JACK_HEADPHONE); return rt5645->jack_type;
snd_soc_jack_report(rt5645->mic_jack, jack_type, SND_JACK_MICROPHONE);
return 0;
} }
int rt5645_set_jack_detect(struct snd_soc_codec *codec, int rt5645_set_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack) struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
struct snd_soc_jack *btn_jack)
{ {
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
rt5645->hp_jack = hp_jack; rt5645->hp_jack = hp_jack;
rt5645->mic_jack = mic_jack; rt5645->mic_jack = mic_jack;
rt5645_jack_detect(codec); rt5645->btn_jack = btn_jack;
if (rt5645->btn_jack && rt5645->codec_type == CODEC_TYPE_RT5650) {
rt5645->en_button_func = true;
regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ);
regmap_update_bits(rt5645->regmap, RT5645_DEPOP_M1,
RT5645_HP_CB_MASK, RT5645_HP_CB_PU);
regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL1,
RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL);
}
rt5645_irq_detection(rt5645);
return 0; return 0;
} }
...@@ -2488,7 +2564,7 @@ static void rt5645_jack_detect_work(struct work_struct *work) ...@@ -2488,7 +2564,7 @@ static void rt5645_jack_detect_work(struct work_struct *work)
struct rt5645_priv *rt5645 = struct rt5645_priv *rt5645 =
container_of(work, struct rt5645_priv, jack_detect_work.work); container_of(work, struct rt5645_priv, jack_detect_work.work);
rt5645_jack_detect(rt5645->codec); rt5645_irq_detection(rt5645);
} }
static irqreturn_t rt5645_irq(int irq, void *data) static irqreturn_t rt5645_irq(int irq, void *data)
...@@ -2501,6 +2577,125 @@ static irqreturn_t rt5645_irq(int irq, void *data) ...@@ -2501,6 +2577,125 @@ static irqreturn_t rt5645_irq(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int rt5645_button_detect(struct snd_soc_codec *codec)
{
int btn_type, val;
val = snd_soc_read(codec, RT5650_4BTN_IL_CMD1);
pr_debug("val=0x%x\n", val);
btn_type = val & 0xfff0;
snd_soc_write(codec, RT5650_4BTN_IL_CMD1, val);
return btn_type;
}
static int rt5645_irq_detection(struct rt5645_priv *rt5645)
{
int val, btn_type, gpio_state = 0, report = 0;
switch (rt5645->pdata.jd_mode) {
case 0: /* Not using rt5645 JD */
if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio);
dev_dbg(rt5645->codec->dev, "gpio = %d(%d)\n",
rt5645->pdata.hp_det_gpio, gpio_state);
}
if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) ||
(!rt5645->pdata.gpio_hp_det_active_high &&
!gpio_state)) {
report = rt5645_jack_detect(rt5645->codec, 1);
} else {
report = rt5645_jack_detect(rt5645->codec, 0);
}
snd_soc_jack_report(rt5645->hp_jack,
report, SND_JACK_HEADPHONE);
snd_soc_jack_report(rt5645->mic_jack,
report, SND_JACK_MICROPHONE);
return report;
case 1: /* 2 port */
val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0070;
break;
default: /* 1 port */
val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0020;
break;
}
switch (val) {
/* jack in */
case 0x30: /* 2 port */
case 0x0: /* 1 port or 2 port */
if (rt5645->jack_type == 0) {
report = rt5645_jack_detect(rt5645->codec, 1);
/* for push button and jack out */
break;
}
btn_type = 0;
if (snd_soc_read(rt5645->codec, RT5645_INT_IRQ_ST) & 0x4) {
/* button pressed */
report = SND_JACK_HEADSET;
btn_type = rt5645_button_detect(rt5645->codec);
/* rt5650 can report three kinds of button behavior,
one click, double click and hold. However,
currently we will report button pressed/released
event. So all the three button behaviors are
treated as button pressed. */
switch (btn_type) {
case 0x8000:
case 0x4000:
case 0x2000:
report |= SND_JACK_BTN_0;
break;
case 0x1000:
case 0x0800:
case 0x0400:
report |= SND_JACK_BTN_1;
break;
case 0x0200:
case 0x0100:
case 0x0080:
report |= SND_JACK_BTN_2;
break;
case 0x0040:
case 0x0020:
case 0x0010:
report |= SND_JACK_BTN_3;
break;
case 0x0000: /* unpressed */
break;
default:
dev_err(rt5645->codec->dev,
"Unexpected button code 0x%04x\n",
btn_type);
break;
}
}
if (btn_type == 0)/* button release */
report = rt5645->jack_type;
break;
/* jack out */
case 0x70: /* 2 port */
case 0x10: /* 2 port */
case 0x20: /* 1 port */
report = 0;
snd_soc_update_bits(rt5645->codec,
RT5645_INT_IRQ_ST, 0x1, 0x0);
rt5645_jack_detect(rt5645->codec, 0);
break;
default:
break;
}
snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE);
snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE);
if (rt5645->en_button_func)
snd_soc_jack_report(rt5645->btn_jack,
report, SND_JACK_MICROPHONE);
return report;
}
static int rt5645_probe(struct snd_soc_codec *codec) static int rt5645_probe(struct snd_soc_codec *codec)
{ {
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
...@@ -2840,8 +3035,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, ...@@ -2840,8 +3035,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
if (rt5645->pdata.en_jd_func) { if (rt5645->pdata.en_jd_func) {
regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3, regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU, RT5645_IRQ_CLK_GATE_CTRL, RT5645_IRQ_CLK_GATE_CTRL);
RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU);
regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1,
RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN); RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN);
regmap_update_bits(rt5645->regmap, RT5645_JD_CTRL3, regmap_update_bits(rt5645->regmap, RT5645_JD_CTRL3,
......
...@@ -2184,6 +2184,7 @@ struct rt5645_priv { ...@@ -2184,6 +2184,7 @@ struct rt5645_priv {
struct i2c_client *i2c; struct i2c_client *i2c;
struct snd_soc_jack *hp_jack; struct snd_soc_jack *hp_jack;
struct snd_soc_jack *mic_jack; struct snd_soc_jack *mic_jack;
struct snd_soc_jack *btn_jack;
struct delayed_work jack_detect_work; struct delayed_work jack_detect_work;
int codec_type; int codec_type;
...@@ -2196,9 +2197,12 @@ struct rt5645_priv { ...@@ -2196,9 +2197,12 @@ struct rt5645_priv {
int pll_src; int pll_src;
int pll_in; int pll_in;
int pll_out; int pll_out;
int jack_type;
bool en_button_func;
}; };
int rt5645_set_jack_detect(struct snd_soc_codec *codec, int rt5645_set_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack); struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
struct snd_soc_jack *btn_jack);
#endif /* __RT5645_H__ */ #endif /* __RT5645_H__ */
...@@ -185,7 +185,7 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) ...@@ -185,7 +185,7 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
return ret; return ret;
} }
rt5645_set_jack_detect(codec, &ctx->hp_jack, &ctx->mic_jack); rt5645_set_jack_detect(codec, &ctx->hp_jack, &ctx->mic_jack, NULL);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment