[PATCH v1 3/3] ALSA: hda/cs8409: Support manual mode detection for CS42L42
Chris Chiu
chris.chiu at canonical.com
Thu May 5 05:59:34 CEST 2022
On 2022/5/5 00:12, Stefan Binding wrote:
> For Jack detection on CS42L42, detection is normally done using
> "auto" mode, which automatically detects what type of jack is
> connected to the device. However, some headsets are not
> automatically detected, and as such and alternative detection
> method "manual mode" can be used to detect these headsets.
>
> Signed-off-by: Stefan Binding <sbinding at opensource.cirrus.com>
> ---
Tested-by: Chris Chiu <chris.chiu at canonical.com>
> sound/pci/hda/patch_cs8409-tables.c | 3 -
> sound/pci/hda/patch_cs8409.c | 159 +++++++++++++++++++++++-----
> sound/pci/hda/patch_cs8409.h | 1 -
> 3 files changed, 132 insertions(+), 31 deletions(-)
>
> diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
> index a7ee489e6aec..0d11b24a1317 100644
> --- a/sound/pci/hda/patch_cs8409-tables.c
> +++ b/sound/pci/hda/patch_cs8409-tables.c
> @@ -252,7 +252,6 @@ struct sub_codec cs8409_cs42l42_codec = {
> .init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq),
> .hp_jack_in = 0,
> .mic_jack_in = 0,
> - .force_status_change = 1,
> .paged = 1,
> .suspended = 1,
> .no_type_dect = 0,
> @@ -444,7 +443,6 @@ struct sub_codec dolphin_cs42l42_0 = {
> .init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq),
> .hp_jack_in = 0,
> .mic_jack_in = 0,
> - .force_status_change = 1,
> .paged = 1,
> .suspended = 1,
> .no_type_dect = 0,
> @@ -458,7 +456,6 @@ struct sub_codec dolphin_cs42l42_1 = {
> .init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq),
> .hp_jack_in = 0,
> .mic_jack_in = 0,
> - .force_status_change = 1,
> .paged = 1,
> .suspended = 1,
> .no_type_dect = 1,
> diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
> index d35d124bf3dc..c3a8b04c71d8 100644
> --- a/sound/pci/hda/patch_cs8409.c
> +++ b/sound/pci/hda/patch_cs8409.c
> @@ -634,38 +634,128 @@ static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
> cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0xc0);
> }
>
> -static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
> +static int cs42l42_manual_hs_det(struct sub_codec *cs42l42)
> {
> - int status_changed = cs42l42->force_status_change;
> + unsigned int hs_det_status;
> + unsigned int hs_det_comp1;
> + unsigned int hs_det_comp2;
> + unsigned int hs_det_sw;
> + unsigned int hs_type;
> +
> + /* Set hs detect to manual, active mode */
> + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
> + (1 << CS42L42_HSDET_CTRL_SHIFT) |
> + (0 << CS42L42_HSDET_SET_SHIFT) |
> + (0 << CS42L42_HSBIAS_REF_SHIFT) |
> + (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
> +
> + /* Configure HS DET comparator reference levels. */
> + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
> + (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) |
> + (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT));
> +
> + /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */
> + cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1);
> +
> + msleep(100);
> +
> + hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
> +
> + hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
> + CS42L42_HSDET_COMP1_OUT_SHIFT;
> + hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
> + CS42L42_HSDET_COMP2_OUT_SHIFT;
> +
> + /* Close the SW_HSB_HS3 switch for a Type 2 headset. */
> + cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2);
>
> - cs42l42->force_status_change = 0;
> + msleep(100);
> +
> + hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
> +
> + hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
> + CS42L42_HSDET_COMP1_OUT_SHIFT) << 1;
> + hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
> + CS42L42_HSDET_COMP2_OUT_SHIFT) << 1;
> +
> + /* Use Comparator 1 with 1.25V Threshold. */
> + switch (hs_det_comp1) {
> + case CS42L42_HSDET_COMP_TYPE1:
> + hs_type = CS42L42_PLUG_CTIA;
> + hs_det_sw = CS42L42_HSDET_SW_TYPE1;
> + break;
> + case CS42L42_HSDET_COMP_TYPE2:
> + hs_type = CS42L42_PLUG_OMTP;
> + hs_det_sw = CS42L42_HSDET_SW_TYPE2;
> + break;
> + default:
> + /* Fallback to Comparator 2 with 1.75V Threshold. */
> + switch (hs_det_comp2) {
> + case CS42L42_HSDET_COMP_TYPE1:
> + hs_type = CS42L42_PLUG_CTIA;
> + hs_det_sw = CS42L42_HSDET_SW_TYPE1;
> + break;
> + case CS42L42_HSDET_COMP_TYPE2:
> + hs_type = CS42L42_PLUG_OMTP;
> + hs_det_sw = CS42L42_HSDET_SW_TYPE2;
> + break;
> + case CS42L42_HSDET_COMP_TYPE3:
> + hs_type = CS42L42_PLUG_HEADPHONE;
> + hs_det_sw = CS42L42_HSDET_SW_TYPE3;
> + break;
> + default:
> + hs_type = CS42L42_PLUG_INVALID;
> + hs_det_sw = CS42L42_HSDET_SW_TYPE4;
> + break;
> + }
> + }
> +
> + /* Set Switches */
> + cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, hs_det_sw);
> +
> + /* Set HSDET mode to Manual—Disabled */
> + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
> + (0 << CS42L42_HSDET_CTRL_SHIFT) |
> + (0 << CS42L42_HSDET_SET_SHIFT) |
> + (0 << CS42L42_HSBIAS_REF_SHIFT) |
> + (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
> +
> + /* Configure HS DET comparator reference levels. */
> + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
> + (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) |
> + (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT));
> +
> + return hs_type;
> +}
> +
> +static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
> +{
> + int status_changed = 0;
>
> /* TIP_SENSE INSERT/REMOVE */
> switch (reg_ts_status) {
> case CS42L42_TS_PLUG:
> - if (!cs42l42->hp_jack_in) {
> - if (cs42l42->no_type_dect) {
> - status_changed = 1;
> - cs42l42->hp_jack_in = 1;
> - cs42l42->mic_jack_in = 0;
> - } else {
> - cs42l42_run_jack_detect(cs42l42);
> - }
> + if (cs42l42->no_type_dect) {
> + status_changed = 1;
> + cs42l42->hp_jack_in = 1;
> + cs42l42->mic_jack_in = 0;
> + } else {
> + cs42l42_run_jack_detect(cs42l42);
> }
> break;
>
> case CS42L42_TS_UNPLUG:
> - if (cs42l42->hp_jack_in || cs42l42->mic_jack_in) {
> - status_changed = 1;
> - cs42l42->hp_jack_in = 0;
> - cs42l42->mic_jack_in = 0;
> - }
> + status_changed = 1;
> + cs42l42->hp_jack_in = 0;
> + cs42l42->mic_jack_in = 0;
> break;
> default:
> /* jack in transition */
> break;
> }
>
> + codec_dbg(cs42l42->codec, "Tip Sense Detection: (%d)\n", reg_ts_status);
> +
> return status_changed;
> }
>
> @@ -698,24 +788,40 @@ static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
>
> type = (reg_hs_status & CS42L42_HSDET_TYPE_MASK) >> CS42L42_HSDET_TYPE_SHIFT;
>
> + /* Configure the HSDET mode. */
> + cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
> +
> if (cs42l42->no_type_dect) {
> status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status);
> - } else if (type == CS42L42_PLUG_INVALID) {
> - /* Type CS42L42_PLUG_INVALID not supported */
> - status_changed = cs42l42_handle_tip_sense(cs42l42, CS42L42_TS_UNPLUG);
> } else {
> - if (!cs42l42->hp_jack_in) {
> - status_changed = 1;
> - cs42l42->hp_jack_in = 1;
> + if (type == CS42L42_PLUG_INVALID || type == CS42L42_PLUG_HEADPHONE) {
> + codec_dbg(cs42l42->codec,
> + "Auto detect value not valid (%d), running manual det\n",
> + type);
> + type = cs42l42_manual_hs_det(cs42l42);
> }
> - /* type = CS42L42_PLUG_HEADPHONE has no mic */
> - if ((!cs42l42->mic_jack_in) && (type != CS42L42_PLUG_HEADPHONE)) {
> +
> + switch (type) {
> + case CS42L42_PLUG_CTIA:
> + case CS42L42_PLUG_OMTP:
> status_changed = 1;
> + cs42l42->hp_jack_in = 1;
> cs42l42->mic_jack_in = 1;
> + break;
> + case CS42L42_PLUG_HEADPHONE:
> + status_changed = 1;
> + cs42l42->hp_jack_in = 1;
> + cs42l42->mic_jack_in = 0;
> + break;
> + default:
> + status_changed = 1;
> + cs42l42->hp_jack_in = 0;
> + cs42l42->mic_jack_in = 0;
> + break;
> }
> + codec_dbg(cs42l42->codec, "Detection done (%d)\n", type);
> }
> - /* Configure the HSDET mode. */
> - cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
> +
> /* Enable the HPOUT ground clamp and configure the HP pull-down */
> cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x02);
> /* Re-Enable Tip Sense Interrupt */
> @@ -803,7 +909,6 @@ static void cs42l42_suspend(struct sub_codec *cs42l42)
> cs42l42->last_page = 0;
> cs42l42->hp_jack_in = 0;
> cs42l42->mic_jack_in = 0;
> - cs42l42->force_status_change = 1;
>
> /* Put CS42L42 into Reset */
> gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
> diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
> index 988259f8a940..ebf473a3f109 100644
> --- a/sound/pci/hda/patch_cs8409.h
> +++ b/sound/pci/hda/patch_cs8409.h
> @@ -304,7 +304,6 @@ struct sub_codec {
>
> unsigned int hp_jack_in:1;
> unsigned int mic_jack_in:1;
> - unsigned int force_status_change:1;
> unsigned int suspended:1;
> unsigned int paged:1;
> unsigned int last_page;
>
More information about the Alsa-devel
mailing list