Commit f1291f41 authored by Mark Brown's avatar Mark Brown

Merge series "ASoC: cs42l42: Implement Manual Type detection as fallback" from...

Merge series "ASoC: cs42l42: Implement Manual Type detection as fallback" from Vitaly Rodionov <vitalyr@opensource.cirrus.com>:

For some headsets CS42L42 autodetect mode is not working correctly.
They will be detected as unknown types or as headphones. According
to the CS42L42 datasheet, if the headset autodetect failed,
then the driver should switch to manual mode and perform a manual steps sequence.
These steps were missing in the current driver code. This patch will add manual
mode fallback steps in case autodetect failed. The default behavior is not affected,
manual mode runs only when autodetect failed.

Tested for regression with autodetect with all known headsets - no regression.
Tested with all headsets customers reported as false detected:
Gumdrop DropTech B1 - detected as headset OK
HUAWEI AM115 - detected as headset OK
UGREEN EP103 - detected as headset OK
HONOR AM116 - detected as headset OK

Stefan Binding (1):
  ASoC: cs42l42: Implement Manual Type detection as fallback

 sound/soc/codecs/cs42l42.c | 104 ++++++++++++++++++++++++++++++++-----
 sound/soc/codecs/cs42l42.h |  54 +++++++++++++++++++
 2 files changed, 146 insertions(+), 12 deletions(-)

--
2.25.1
parents 8e0850f9 12451814
......@@ -1046,37 +1046,117 @@ static struct snd_soc_dai_driver cs42l42_dai = {
.ops = &cs42l42_ops,
};
static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
static void cs42l42_manual_hs_type_detect(struct cs42l42_private *cs42l42)
{
unsigned int hs_det_status;
unsigned int int_status;
unsigned int hs_det_comp;
unsigned int hs_det_sw;
/* Mask the auto detect interrupt */
/* Set hs detect to manual, active mode */
regmap_update_bits(cs42l42->regmap,
CS42L42_CODEC_INT_MASK,
CS42L42_PDN_DONE_MASK |
CS42L42_HSDET_AUTO_DONE_MASK,
(1 << CS42L42_PDN_DONE_SHIFT) |
(1 << CS42L42_HSDET_AUTO_DONE_SHIFT));
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 hs detect to automatic, disabled mode */
/* 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,
(2 << CS42L42_HSDET_CTRL_SHIFT) |
(2 << CS42L42_HSDET_SET_SHIFT) |
(0 << CS42L42_HSDET_CTRL_SHIFT) |
(0 << CS42L42_HSDET_SET_SHIFT) |
(0 << CS42L42_HSBIAS_REF_SHIFT) |
(3 << CS42L42_HSDET_AUTO_TIME_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,
CS42L42_PDN_DONE_MASK |
CS42L42_HSDET_AUTO_DONE_MASK,
(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,
CS42L42_HSDET_CTRL_MASK |
CS42L42_HSDET_SET_MASK |
CS42L42_HSBIAS_REF_MASK |
CS42L42_HSDET_AUTO_TIME_MASK,
(2 << CS42L42_HSDET_CTRL_SHIFT) |
(2 << CS42L42_HSDET_SET_SHIFT) |
(0 << CS42L42_HSBIAS_REF_SHIFT) |
(3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
}
/* Set up button detection */
if ((cs42l42->hs_type == CS42L42_PLUG_CTIA) ||
(cs42l42->hs_type == CS42L42_PLUG_OMTP)) {
......
......@@ -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)
......
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