[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