Unverified Commit 12451814 authored by Stefan Binding's avatar Stefan Binding Committed by Mark Brown
Browse files

ASoC: cs42l42: Implement Manual Type detection as fallback



Some headsets are not detected correctly by Automatic Type Detection
on cs42l42. Instead, Manual Type Detection can be used to give a
more accurate value.

Signed-off-by: default avatarStefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: default avatarVitaly Rodionov <vitalyr@opensource.cirrus.com>
Link: https://lore.kernel.org/r/20210916102750.9212-2-vitalyr@opensource.cirrus.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent bde4f08c
Loading
Loading
Loading
Loading
+92 −12
Original line number Diff line number Diff line
@@ -1046,11 +1046,83 @@ static struct snd_soc_dai_driver cs42l42_dai = {
		.ops = &cs42l42_ops,
};

static void cs42l42_manual_hs_type_detect(struct cs42l42_private *cs42l42)
{
	unsigned int hs_det_status;
	unsigned int hs_det_comp;
	unsigned int hs_det_sw;

	/* Set hs detect to manual, active mode */
	regmap_update_bits(cs42l42->regmap,
		CS42L42_HSDET_CTL2,
		CS42L42_HSDET_CTRL_MASK |
		CS42L42_HSDET_SET_MASK |
		CS42L42_HSBIAS_REF_MASK |
		CS42L42_HSDET_AUTO_TIME_MASK,
		(1 << CS42L42_HSDET_CTRL_SHIFT) |
		(0 << CS42L42_HSDET_SET_SHIFT) |
		(0 << CS42L42_HSBIAS_REF_SHIFT) |
		(0 << CS42L42_HSDET_AUTO_TIME_SHIFT));

	/* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */
	regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1);

	regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);

	hs_det_comp = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
			CS42L42_HSDET_COMP1_OUT_SHIFT;

	/* Close the SW_HSB_HS3 switch for a Type 2 headset. */
	regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2);

	regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);

	hs_det_comp |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
			CS42L42_HSDET_COMP2_OUT_SHIFT) << 1;

	switch (hs_det_comp) {
	case CS42L42_HSDET_COMP_TYPE1:
		cs42l42->hs_type = CS42L42_PLUG_CTIA;
		hs_det_sw = CS42L42_HSDET_SW_TYPE1;
		break;
	case CS42L42_HSDET_COMP_TYPE2:
		cs42l42->hs_type = CS42L42_PLUG_OMTP;
		hs_det_sw = CS42L42_HSDET_SW_TYPE2;
		break;
	case CS42L42_HSDET_COMP_TYPE3:
		cs42l42->hs_type = CS42L42_PLUG_HEADPHONE;
		hs_det_sw = CS42L42_HSDET_SW_TYPE3;
		break;
	default:
		cs42l42->hs_type = CS42L42_PLUG_INVALID;
		hs_det_sw = CS42L42_HSDET_SW_TYPE4;
		break;
	}

	/* Set Switches */
	regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, hs_det_sw);

	/* Set HSDET mode to Manual—Disabled */
	regmap_update_bits(cs42l42->regmap,
		CS42L42_HSDET_CTL2,
		CS42L42_HSDET_CTRL_MASK |
		CS42L42_HSDET_SET_MASK |
		CS42L42_HSBIAS_REF_MASK |
		CS42L42_HSDET_AUTO_TIME_MASK,
		(0 << CS42L42_HSDET_CTRL_SHIFT) |
		(0 << CS42L42_HSDET_SET_SHIFT) |
		(0 << CS42L42_HSBIAS_REF_SHIFT) |
		(0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
}

static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
{
	unsigned int hs_det_status;
	unsigned int int_status;

	/* Read and save the hs detection result */
	regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);

	/* Mask the auto detect interrupt */
	regmap_update_bits(cs42l42->regmap,
		CS42L42_CODEC_INT_MASK,
@@ -1059,6 +1131,19 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
		(1 << CS42L42_PDN_DONE_SHIFT) |
		(1 << CS42L42_HSDET_AUTO_DONE_SHIFT));


	cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >>
				CS42L42_HSDET_TYPE_SHIFT;

	/* Run Manual detection if auto detect has not found a headset.
	 * We Re-Run with Manual Detection if the original detection was invalid or headphones,
	 * to ensure that a headset mic is detected in all cases.
	 */
	if (cs42l42->hs_type == CS42L42_PLUG_INVALID ||
		cs42l42->hs_type == CS42L42_PLUG_HEADPHONE) {
		dev_dbg(cs42l42->component->dev, "Running Manual Detection Fallback\n");
		cs42l42_manual_hs_type_detect(cs42l42);
	} else {
		/* Set hs detect to automatic, disabled mode */
		regmap_update_bits(cs42l42->regmap,
			CS42L42_HSDET_CTL2,
@@ -1070,12 +1155,7 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
			(2 << CS42L42_HSDET_SET_SHIFT) |
			(0 << CS42L42_HSBIAS_REF_SHIFT) |
			(3 << CS42L42_HSDET_AUTO_TIME_SHIFT));

	/* Read and save the hs detection result */
	regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);

	cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >>
				CS42L42_HSDET_TYPE_SHIFT;
	}

	/* Set up button detection */
	if ((cs42l42->hs_type == CS42L42_PLUG_CTIA) ||
+54 −0
Original line number Diff line number Diff line
@@ -228,6 +228,60 @@
#define CS42L42_PLUG_HEADPHONE		2
#define CS42L42_PLUG_INVALID		3

#define CS42L42_HSDET_SW_COMP1		((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
					 (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
					 (1 << CS42L42_SW_HSB_HS4_SHIFT) | \
					 (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
					 (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
					 (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
					 (0 << CS42L42_SW_REF_HS4_SHIFT) | \
					 (1 << CS42L42_SW_REF_HS3_SHIFT))
#define CS42L42_HSDET_SW_COMP2		((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
					 (0 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
					 (0 << CS42L42_SW_HSB_HS4_SHIFT) | \
					 (1 << CS42L42_SW_HSB_HS3_SHIFT) | \
					 (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
					 (0 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
					 (1 << CS42L42_SW_REF_HS4_SHIFT) | \
					 (0 << CS42L42_SW_REF_HS3_SHIFT))
#define CS42L42_HSDET_SW_TYPE1		((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
					 (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
					 (1 << CS42L42_SW_HSB_HS4_SHIFT) | \
					 (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
					 (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
					 (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
					 (0 << CS42L42_SW_REF_HS4_SHIFT) | \
					 (1 << CS42L42_SW_REF_HS3_SHIFT))
#define CS42L42_HSDET_SW_TYPE2		((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
					 (0 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
					 (0 << CS42L42_SW_HSB_HS4_SHIFT) | \
					 (1 << CS42L42_SW_HSB_HS3_SHIFT) | \
					 (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
					 (0 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
					 (1 << CS42L42_SW_REF_HS4_SHIFT) | \
					 (0 << CS42L42_SW_REF_HS3_SHIFT))
#define CS42L42_HSDET_SW_TYPE3		((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
					 (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
					 (0 << CS42L42_SW_HSB_HS4_SHIFT) | \
					 (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
					 (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
					 (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
					 (1 << CS42L42_SW_REF_HS4_SHIFT) | \
					 (1 << CS42L42_SW_REF_HS3_SHIFT))
#define CS42L42_HSDET_SW_TYPE4		((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \
					 (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \
					 (1 << CS42L42_SW_HSB_HS4_SHIFT) | \
					 (0 << CS42L42_SW_HSB_HS3_SHIFT) | \
					 (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \
					 (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \
					 (0 << CS42L42_SW_REF_HS4_SHIFT) | \
					 (1 << CS42L42_SW_REF_HS3_SHIFT))

#define CS42L42_HSDET_COMP_TYPE1	1
#define CS42L42_HSDET_COMP_TYPE2	2
#define CS42L42_HSDET_COMP_TYPE3	0
#define CS42L42_HSDET_COMP_TYPE4	3

#define CS42L42_HS_CLAMP_DISABLE	(CS42L42_PAGE_11 + 0x29)
#define CS42L42_HS_CLAMP_DISABLE_SHIFT	0
#define CS42L42_HS_CLAMP_DISABLE_MASK	(1 << CS42L42_HS_CLAMP_DISABLE_SHIFT)