From 1310e941af4a45013e2fcf9148d0733be8d54f5e Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 6 Oct 2024 11:13:53 +1000 Subject: [PATCH 1/4] ASoC: cs42l84: halve headset detection delay 200ms of sleep was excessive for the standard headset detection routine. Halve this to 100ms. Signed-off-by: James Calligeros --- sound/soc/codecs/cs42l84.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index d8fc7bdb89bb00..86b0b9a25b4331 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -724,7 +724,7 @@ static void cs42l84_detect_hs(struct cs42l84_private *cs42l84) CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET, 0); /* TODO: Optimize */ - msleep(100); + msleep(50); /* Connect HSBIAS in CTIA wiring */ /* TODO: Should likely be subject of detection */ @@ -745,7 +745,7 @@ static void cs42l84_detect_hs(struct cs42l84_private *cs42l84) FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 3)); /* TODO: Optimize */ - msleep(100); + msleep(50); regmap_read(cs42l84->regmap, CS42L84_HS_DET_STATUS2, ®); regmap_update_bits(cs42l84->regmap, From 0756ab65de60873364c3c902e1b24184ff05bc28 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 6 Oct 2024 11:19:35 +1000 Subject: [PATCH 2/4] ASoC: cs42l84: rearrange tip sense interrupt handler Check and set the plug state only once so that we can guard the entire handler. This allows us to more easily add ring sense interrupt handling. Signed-off-by: James Calligeros --- sound/soc/codecs/cs42l84.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index 86b0b9a25b4331..19359a6643b08f 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -834,10 +834,10 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >> CS42L84_TS_PLUG_SHIFT; - switch (current_plug_status) { - case CS42L84_PLUG: - if (cs42l84->plug_state != CS42L84_PLUG) { - cs42l84->plug_state = CS42L84_PLUG; + if (current_plug_status != cs42l84->plug_state) { + cs42l84->plug_state = current_plug_status; + switch (current_plug_status) { + case CS42L84_PLUG: dev_dbg(cs42l84->dev, "Plug event\n"); cs42l84_detect_hs(cs42l84); @@ -858,26 +858,19 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) cs42l84->plug_state = CS42L84_UNPLUG; cs42l84_revert_hs(cs42l84); } - } - break; - - case CS42L84_UNPLUG: - if (cs42l84->plug_state != CS42L84_UNPLUG) { - cs42l84->plug_state = CS42L84_UNPLUG; + break; + case CS42L84_UNPLUG: dev_dbg(cs42l84->dev, "Unplug event\n"); cs42l84_revert_hs(cs42l84); cs42l84->hs_type = 0; snd_soc_jack_report(cs42l84->jack, 0, SND_JACK_HEADSET); + break; + default: + break; } - break; - - default: - if (cs42l84->plug_state != CS42L84_TRANS) - cs42l84->plug_state = CS42L84_TRANS; } - } mutex_unlock(&cs42l84->irq_lock); return IRQ_HANDLED; From f3eaa40754f031c44a22fea8c1769c82e2362e39 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 6 Oct 2024 11:26:03 +1000 Subject: [PATCH 3/4] ASoC: cs42l84: allow runtime mask/unmask of interrupts Make cs42L84_set_interrupt_masks accept a value for the interrupt mask register so that we can enable/disable ring sense interrupts at runtime. Signed-off-by: James Calligeros --- sound/soc/codecs/cs42l84.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index 19359a6643b08f..cb3b363d0b7cc4 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -876,12 +876,13 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) return IRQ_HANDLED; } -static void cs42l84_set_interrupt_masks(struct cs42l84_private *cs42l84) +static void cs42l84_set_interrupt_masks(struct cs42l84_private *cs42l84, + unsigned int val) { regmap_update_bits(cs42l84->regmap, CS42L84_TSRS_PLUG_INT_MASK, CS42L84_RS_PLUG | CS42L84_RS_UNPLUG | CS42L84_TS_PLUG | CS42L84_TS_UNPLUG, - CS42L84_RS_PLUG | CS42L84_RS_UNPLUG); + val); } static void cs42l84_setup_plug_detect(struct cs42l84_private *cs42l84) @@ -1020,8 +1021,8 @@ static int cs42l84_i2c_probe(struct i2c_client *i2c_client) /* Setup plug detection */ cs42l84_setup_plug_detect(cs42l84); - /* Mask/Unmask Interrupts */ - cs42l84_set_interrupt_masks(cs42l84); + /* Mask ring sense interrupts */ + cs42l84_set_interrupt_masks(cs42l84, CS42L84_RS_PLUG | CS42L84_RS_UNPLUG); /* Register codec for machine driver */ ret = devm_snd_soc_register_component(&i2c_client->dev, From 63d0a25f32b990b8f5c34171c67a89ddbfc1d857 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 6 Oct 2024 11:45:38 +1000 Subject: [PATCH 4/4] ASoC: cs42l84: handle ring sense interrupts Some jacks integrated on devices with this codec, such as certain Apple Silicon Macs, have quite trigger-happy tip sense switches that cause a tip sense IRQ before the plug is fully seated. If users are unfortunate with their timing, this can lead to headsets being detected as mic-less headphones among other issues with the codec's device detection routines. Introduce some rudimentary ring sense interrupt handling so that we can re-trigger the codec's detection routines when we are certain that the plug is fully seated. Signed-off-by: James Calligeros --- sound/soc/codecs/cs42l84.c | 72 +++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index cb3b363d0b7cc4..e8798806aeee9a 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -46,7 +46,8 @@ struct cs42l84_private { struct gpio_desc *reset_gpio; struct snd_soc_jack *jack; struct mutex irq_lock; - u8 plug_state; + u8 tip_state; + u8 ring_state; int pll_config; int bclk; u8 pll_mclk_f; @@ -808,13 +809,23 @@ static void cs42l84_revert_hs(struct cs42l84_private *cs42l84) FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 2)); } +static void cs42l84_set_interrupt_masks(struct cs42l84_private *cs42l84, + unsigned int val) +{ + regmap_update_bits(cs42l84->regmap, CS42L84_TSRS_PLUG_INT_MASK, + CS42L84_RS_PLUG | CS42L84_RS_UNPLUG | + CS42L84_TS_PLUG | CS42L84_TS_UNPLUG, + val); +} + static irqreturn_t cs42l84_irq_thread(int irq, void *data) { struct cs42l84_private *cs42l84 = (struct cs42l84_private *)data; unsigned int stickies[1]; unsigned int masks[1]; unsigned int reg; - u8 current_plug_status; + u8 current_tip_state; + u8 current_ring_state; int i; mutex_lock(&cs42l84->irq_lock); @@ -828,15 +839,22 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) irq_params_table[i].mask; } + /* When handling plug sene IRQs, we only care about EITHER tip OR ring. + * Ring is useless on remove, and is only useful on insert for + * detecting if the plug state has changed AFTER we have handled the + * tip sense IRQ, e.g. if the plug was not fully seated within the tip + * sense debounce time. */ + if ((~masks[0]) & irq_params_table[0].mask) { regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, ®); - current_plug_status = (((char) reg) & + + current_tip_state = (((char) reg) & (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >> CS42L84_TS_PLUG_SHIFT; - if (current_plug_status != cs42l84->plug_state) { - cs42l84->plug_state = current_plug_status; - switch (current_plug_status) { + if (current_tip_state != cs42l84->tip_state) { + cs42l84->tip_state = current_tip_state; + switch (current_tip_state) { case CS42L84_PLUG: dev_dbg(cs42l84->dev, "Plug event\n"); @@ -850,41 +868,55 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) * was disconnected at any point during the detection procedure. */ regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, ®); - current_plug_status = (((char) reg) & + current_tip_state = (((char) reg) & (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >> CS42L84_TS_PLUG_SHIFT; - if (current_plug_status != CS42L84_PLUG) { + if (current_tip_state != CS42L84_PLUG) { dev_dbg(cs42l84->dev, "Wobbly connection, detection invalidated\n"); - cs42l84->plug_state = CS42L84_UNPLUG; + cs42l84->tip_state = CS42L84_UNPLUG; cs42l84_revert_hs(cs42l84); } + + /* Unmask ring sense interrupts */ + cs42l84_set_interrupt_masks(cs42l84, 0); break; case CS42L84_UNPLUG: + cs42l84->ring_state = CS42L84_UNPLUG; dev_dbg(cs42l84->dev, "Unplug event\n"); cs42l84_revert_hs(cs42l84); cs42l84->hs_type = 0; snd_soc_jack_report(cs42l84->jack, 0, SND_JACK_HEADSET); + + /* Mask ring sense interrupts */ + cs42l84_set_interrupt_masks(cs42l84, CS42L84_RS_PLUG | CS42L84_RS_UNPLUG); break; default: - break; + cs42l84->ring_state = CS42L84_TRANS; } + + mutex_unlock(&cs42l84->irq_lock); + return IRQ_HANDLED; } + + /* Tip state didn't change, we must've got a ring sense IRQ */ + current_ring_state = (((char) reg) & + (CS42L84_RS_PLUG | CS42L84_RS_UNPLUG)) >> + CS42L84_RS_PLUG_SHIFT; + + if (current_ring_state != cs42l84->ring_state) { + cs42l84->ring_state = current_ring_state; + if (current_ring_state == CS42L84_PLUG) + cs42l84_detect_hs(cs42l84); + } + } + mutex_unlock(&cs42l84->irq_lock); return IRQ_HANDLED; } -static void cs42l84_set_interrupt_masks(struct cs42l84_private *cs42l84, - unsigned int val) -{ - regmap_update_bits(cs42l84->regmap, CS42L84_TSRS_PLUG_INT_MASK, - CS42L84_RS_PLUG | CS42L84_RS_UNPLUG | - CS42L84_TS_PLUG | CS42L84_TS_UNPLUG, - val); -} - static void cs42l84_setup_plug_detect(struct cs42l84_private *cs42l84) { unsigned int reg; @@ -914,7 +946,7 @@ static void cs42l84_setup_plug_detect(struct cs42l84_private *cs42l84) /* Save the initial status of the tip sense */ regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, ®); - cs42l84->plug_state = (((char) reg) & + cs42l84->tip_state = (((char) reg) & (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >> CS42L84_TS_PLUG_SHIFT;