[alsa-devel] [PATCH 1/2] ASoC: rt5645: Adds push button support for rt5650
rt5650 support headset button detection. Currently, the button detection is only implemented for rt5650 codec.
Signed-off-by: Bard Liao bardliao@realtek.com --- sound/soc/codecs/rt5645.c | 262 +++++++++++++++++++++++++++++++++++++++------- sound/soc/codecs/rt5645.h | 8 +- 2 files changed, 231 insertions(+), 39 deletions(-)
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 69528ae..7c59f9c 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -198,7 +198,6 @@ static const struct reg_default rt5645_reg[] = { { 0xda, 0x0000 }, { 0xdb, 0x0003 }, { 0xdc, 0x0049 }, - { 0xdd, 0x001b }, { 0xdf, 0x0008 }, { 0xe0, 0x4000 }, { 0xe6, 0x8000 }, @@ -2368,6 +2367,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, enum snd_soc_bias_level level) { + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { @@ -2398,8 +2399,9 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF: snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100); - snd_soc_update_bits(codec, RT5645_GEN_CTRL1, - RT5645_DIG_GATE_CTRL, 0); + if (!rt5645->en_button_func) + snd_soc_update_bits(codec, RT5645_GEN_CTRL1, + RT5645_DIG_GATE_CTRL, 0); snd_soc_update_bits(codec, RT5645_PWR_ANLG1, RT5645_PWR_VREF1 | RT5645_PWR_MB | RT5645_PWR_BG | RT5645_PWR_VREF2 | @@ -2414,28 +2416,71 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, 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); - int gpio_state, jack_type = 0; - unsigned int val;
- if (!gpio_is_valid(rt5645->pdata.hp_det_gpio)) { - dev_err(codec->dev, "invalid gpio\n"); - return -EINVAL; + if (enable) { + snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, + "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, - gpio_state); +static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + unsigned int val;
- if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) || - (!rt5645->pdata.gpio_hp_det_active_high && !gpio_state)) { - snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias1"); - snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias2"); - 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); + if (jack_insert) { + if (codec->component.card->instantiated) { + snd_soc_dapm_force_enable_pin(&codec->dapm, + "micbias1"); + snd_soc_dapm_force_enable_pin(&codec->dapm, + "micbias2"); + 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_JD_CTRL3, 0x00b0); @@ -2449,32 +2494,62 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec) val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7; dev_dbg(codec->dev, "val = %d\n", val);
- if (val == 1 || val == 2) - jack_type = SND_JACK_HEADSET; - else - jack_type = SND_JACK_HEADPHONE; + if (codec->component.card->instantiated) { + snd_soc_dapm_disable_pin(&codec->dapm, "micbias1"); + snd_soc_dapm_disable_pin(&codec->dapm, "micbias2"); + 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"); - snd_soc_dapm_disable_pin(&codec->dapm, "micbias2"); - 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); + if (val == 1 || val == 2) { + rt5645->jack_type = SND_JACK_HEADSET; + if (rt5645->en_button_func) { + msleep(100); + rt5645_enable_push_button_irq(codec, true); + } + } 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); - snd_soc_jack_report(rt5645->mic_jack, jack_type, SND_JACK_MICROPHONE); - return 0; + return rt5645->jack_type; }
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);
rt5645->hp_jack = hp_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; } @@ -2485,7 +2560,7 @@ static void rt5645_jack_detect_work(struct work_struct *work) struct rt5645_priv *rt5645 = 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) @@ -2498,6 +2573,120 @@ static irqreturn_t rt5645_irq(int irq, void *data) 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); + 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) { struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); @@ -2784,8 +2973,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
if (rt5645->pdata.en_jd_func) { regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3, - RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU, - RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU); + RT5645_IRQ_CLK_GATE_CTRL, RT5645_IRQ_CLK_GATE_CTRL); regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN); regmap_update_bits(rt5645->regmap, RT5645_JD_CTRL3, diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index db78e94..4473636 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -2184,6 +2184,7 @@ struct rt5645_priv { struct i2c_client *i2c; struct snd_soc_jack *hp_jack; struct snd_soc_jack *mic_jack; + struct snd_soc_jack *btn_jack; struct delayed_work jack_detect_work;
int codec_type; @@ -2196,9 +2197,12 @@ struct rt5645_priv { int pll_src; int pll_in; int pll_out; + + int jack_type; + bool en_button_func; };
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__ */
rt5645_set_jack_detect is changed, so we need to modify the code which is using rt5645_set_jack_detect.
Signed-off-by: Bard Liao bardliao@realtek.com --- sound/soc/intel/cht_bsw_rt5645.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/intel/cht_bsw_rt5645.c b/sound/soc/intel/cht_bsw_rt5645.c index bd29617..3d0b549 100644 --- a/sound/soc/intel/cht_bsw_rt5645.c +++ b/sound/soc/intel/cht_bsw_rt5645.c @@ -185,7 +185,7 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) 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; }
On Mon, Apr 20, 2015 at 10:37:22AM +0800, Bard Liao wrote:
rt5645_set_jack_detect is changed, so we need to modify the code which is using rt5645_set_jack_detect.
This is all good, but it should have been part of patch 1 - if resending then please squash it in.
On Mon, Apr 20, 2015 at 10:37:21AM +0800, Bard Liao wrote:
--- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -198,7 +198,6 @@ static const struct reg_default rt5645_reg[] = { { 0xda, 0x0000 }, { 0xdb, 0x0003 }, { 0xdc, 0x0049 },
- { 0xdd, 0x001b }, { 0xdf, 0x0008 }, { 0xe0, 0x4000 }, { 0xe6, 0x8000 },
That's a bit weird, why is this in here?
- if (rt5645->btn_jack && rt5645->codec_type == CODEC_TYPE_RT5650) {
Can we have a switch statement for the device type so it's easier to add other devices if there's new derivatives please?
btn_type = rt5645_button_detect(rt5645->codec);
switch (btn_type) {
case 0x8000:
case 0x4000:
case 0x2000:
report |= SND_JACK_BTN_0;
break;
Why do we mash all these values together?
-----Original Message----- From: Mark Brown [mailto:broonie@kernel.org] Sent: Tuesday, April 21, 2015 4:41 AM To: Bard Liao Cc: lgirdwood@gmail.com; alsa-devel@alsa-project.org; lars@metafoo.de; Flove; Oder Chiou; John Lin; koro.chen@mediatek.com; yang.a.fang@intel.com; zhengxing@rock-chips.com Subject: Re: [PATCH 1/2] ASoC: rt5645: Adds push button support for rt5650
On Mon, Apr 20, 2015 at 10:37:21AM +0800, Bard Liao wrote:
--- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -198,7 +198,6 @@ static const struct reg_default rt5645_reg[] = { { 0xda, 0x0000 }, { 0xdb, 0x0003 }, { 0xdc, 0x0049 },
- { 0xdd, 0x001b }, { 0xdf, 0x0008 }, { 0xe0, 0x4000 }, { 0xe6, 0x8000 },
That's a bit weird, why is this in here?
The default value of register 0xdd is different between rt5645 and rt5650. And it is for button detection configuration. That's why I put it in this patch instead of a separate patch.
- if (rt5645->btn_jack && rt5645->codec_type ==
CODEC_TYPE_RT5650) {
Can we have a switch statement for the device type so it's easier to add other devices if there's new derivatives please?
btn_type = rt5645_button_detect(rt5645->codec);
switch (btn_type) {
case 0x8000:
case 0x4000:
case 0x2000:
report |= SND_JACK_BTN_0;
break;
Why do we mash all these values together?
rt5650 can report the action of button (one click, double click or hold). That is basically for Windows OS. For linux, we only care about whether the button is pressed or released. So all of the three actions are treated as button pressed.
------Please consider the environment before printing this e-mail.
On Tue, Apr 21, 2015 at 02:08:18AM +0000, Bard Liao wrote:
- { 0xdd, 0x001b },
That's a bit weird, why is this in here?
The default value of register 0xdd is different between rt5645 and rt5650. And it is for button detection configuration. That's why I put it in this patch instead of a separate patch.
OK, you probaly want to move to separate default tables per device. This is something that should have been called out in the commit log.
switch (btn_type) {
case 0x8000:
case 0x4000:
case 0x2000:
report |= SND_JACK_BTN_0;
break;
Why do we mash all these values together?
rt5650 can report the action of button (one click, double click or hold). That is basically for Windows OS. For linux, we only care about whether the button is pressed or released. So all of the three actions are treated as button pressed.
OK, can we have a comment for that please? We might want to extend this in the future as the userspace ABI grows richer (and depending on how things get reported we may need to synthesize at least a double press for userspace).
-----Original Message----- From: Mark Brown [mailto:broonie@kernel.org] Sent: Tuesday, April 21, 2015 6:48 PM To: Bard Liao Cc: lgirdwood@gmail.com; alsa-devel@alsa-project.org; lars@metafoo.de; Flove; Oder Chiou; John Lin; koro.chen@mediatek.com; yang.a.fang@intel.com; zhengxing@rock-chips.com Subject: Re: [PATCH 1/2] ASoC: rt5645: Adds push button support for rt5650
On Tue, Apr 21, 2015 at 02:08:18AM +0000, Bard Liao wrote:
- { 0xdd, 0x001b },
That's a bit weird, why is this in here?
The default value of register 0xdd is different between rt5645 and
rt5650.
And it is for button detection configuration. That's why I put it in this patch instead of a separate patch.
OK, you probaly want to move to separate default tables per device. This is something that should have been called out in the commit log.
Do you mean we need to write it down in the commit log and send another patch to use separate default tables for different device? In fact, there are about 10 different registers which default value are different between rt5645 and rt5650. We are planning to send a separate patch for that after this patch is applied.
Regarding the different default tables, do you mean something like: case RT5645_DEVICE_ID: rt5645->codec_type = CODEC_TYPE_RT5645; dev_info(&i2c->dev, "Codec type is: RT5645\n"); + rt5645_regmap. reg_defaults = rt5645_reg; break; case RT5650_DEVICE_ID: rt5645->codec_type = CODEC_TYPE_RT5650; dev_info(&i2c->dev, "Codec type is: RT5650\n"); + rt5645_regmap. reg_defaults = rt5650_reg; break;
switch (btn_type) {
case 0x8000:
case 0x4000:
case 0x2000:
report |= SND_JACK_BTN_0;
break;
Why do we mash all these values together?
rt5650 can report the action of button (one click, double click or hold). That is basically for Windows OS. For linux, we only care about whether the button is pressed or released. So all of the three actions are treated as button pressed.
OK, can we have a comment for that please? We might want to extend this in the future as the userspace ABI grows richer (and depending on how things get reported we may need to synthesize at least a double press for userspace).
Do you mean comment in the source code?
------Please consider the environment before printing this e-mail.
On Tue, Apr 21, 2015 at 11:41:48AM +0000, Bard Liao wrote:
OK, you probaly want to move to separate default tables per device. This is something that should have been called out in the commit log.
Do you mean we need to write it down in the commit log and send another patch to use separate default tables for different device?
Yes, that's fine.
Regarding the different default tables, do you mean something like: case RT5645_DEVICE_ID: rt5645->codec_type = CODEC_TYPE_RT5645; dev_info(&i2c->dev, "Codec type is: RT5645\n");
break; case RT5650_DEVICE_ID: rt5645->codec_type = CODEC_TYPE_RT5650; dev_info(&i2c->dev, "Codec type is: RT5650\n");rt5645_regmap. reg_defaults = rt5645_reg;
break;rt5645_regmap. reg_defaults = rt5650_reg;
It'd be better to have different regmap_config structures rather than changing the register defaults in the one structure but basically yes.
OK, can we have a comment for that please? We might want to extend this in the future as the userspace ABI grows richer (and depending on how things get reported we may need to synthesize at least a double press for userspace).
Do you mean comment in the source code?
Yes, please.
participants (2)
-
Bard Liao
-
Mark Brown