[PATCH 0/2] ASoC: cs42l42: Implement Manual Type detection as fallback
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
Also, fixes some errors reported by checkpatch.pl script.
Stefan Binding (1): ASoC: cs42l42: Implement Manual Type detection as fallback
Vitaly Rodionov (1): ASoC: cs42l42: Minor fix all errors reported by checkpatch.pl script
sound/soc/codecs/cs42l42.c | 114 +++++++++++++++++++++++++++++++------ sound/soc/codecs/cs42l42.h | 54 ++++++++++++++++++ 2 files changed, 151 insertions(+), 17 deletions(-)
Signed-off-by: Vitaly Rodionov vitalyr@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index fb1e4c33e27d..ca76d907da52 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -851,7 +851,7 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, if (params_width(params) == 24) cs42l42->bclk = (cs42l42->bclk / 3) * 4;
- switch(substream->stream) { + switch (substream->stream) { case SNDRV_PCM_STREAM_CAPTURE: if (channels == 2) { val |= CS42L42_ASP_TX_CH2_AP_MASK; @@ -935,7 +935,7 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) CS42L42_HP_ANA_BMUTE_MASK);
cs42l42->stream_use &= ~(1 << stream); - if(!cs42l42->stream_use) { + if (!cs42l42->stream_use) { /* * Switch to the internal oscillator. * SCLK must remain running until after this clock switch. @@ -1015,7 +1015,7 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
#define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ - SNDRV_PCM_FMTBIT_S32_LE ) + SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops cs42l42_ops = { .startup = cs42l42_dai_startup, @@ -1492,7 +1492,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) if ((~masks[5]) & irq_params_table[5].mask) { if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) { cs42l42_process_hs_type_detect(cs42l42); - switch(cs42l42->hs_type){ + switch (cs42l42->hs_type) { case CS42L42_PLUG_CTIA: case CS42L42_PLUG_OMTP: snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADSET, @@ -1524,7 +1524,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) cs42l42->plug_state = CS42L42_TS_UNPLUG; cs42l42_cancel_hs_type_detect(cs42l42);
- switch(cs42l42->hs_type){ + switch (cs42l42->hs_type) { case CS42L42_PLUG_CTIA: case CS42L42_PLUG_OMTP: snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADSET);
On Tue, Sep 14, 2021 at 05:52:19PM +0100, Vitaly Rodionov wrote:
Signed-off-by: Vitaly Rodionov vitalyr@opensource.cirrus.com
sound/soc/codecs/cs42l42.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
This sort of minor cleanup should go after any fixes so that there's no dependency created needlessly.
On 14/09/2021 6:05 pm, Mark Brown wrote:
On Tue, Sep 14, 2021 at 05:52:19PM +0100, Vitaly Rodionov wrote:
Signed-off-by: Vitaly Rodionov vitalyr@opensource.cirrus.com
sound/soc/codecs/cs42l42.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
Hi Mark,
Thanks a lot for review. We will fix and push new version.
Thanks,
Vitaly
This sort of minor cleanup should go after any fixes so that there's no dependency created needlessly.
From: Stefan Binding sbinding@opensource.cirrus.com
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: Stefan Binding sbinding@opensource.cirrus.com Signed-off-by: Vitaly Rodionov vitalyr@opensource.cirrus.com --- sound/soc/codecs/cs42l42.c | 104 ++++++++++++++++++++++++++++++++----- sound/soc/codecs/cs42l42.h | 54 +++++++++++++++++++ 2 files changed, 146 insertions(+), 12 deletions(-)
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index ca76d907da52..d5e1e5228b5f 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -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)) { diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 8734f6828f3e..2aeabba73e05 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -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)
participants (2)
-
Mark Brown
-
Vitaly Rodionov