Skip to content

Commit efb959d

Browse files
chadmedjannau
authored andcommitted
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 <jcalligeros99@gmail.com>
1 parent dd6afc7 commit efb959d

1 file changed

Lines changed: 52 additions & 20 deletions

File tree

sound/soc/codecs/cs42l84.c

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ struct cs42l84_private {
4646
struct gpio_desc *reset_gpio;
4747
struct snd_soc_jack *jack;
4848
struct mutex irq_lock;
49-
u8 plug_state;
49+
u8 tip_state;
50+
u8 ring_state;
5051
int pll_config;
5152
int bclk;
5253
u8 pll_mclk_f;
@@ -808,13 +809,23 @@ static void cs42l84_revert_hs(struct cs42l84_private *cs42l84)
808809
FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 2));
809810
}
810811

812+
static void cs42l84_set_interrupt_masks(struct cs42l84_private *cs42l84,
813+
unsigned int val)
814+
{
815+
regmap_update_bits(cs42l84->regmap, CS42L84_TSRS_PLUG_INT_MASK,
816+
CS42L84_RS_PLUG | CS42L84_RS_UNPLUG |
817+
CS42L84_TS_PLUG | CS42L84_TS_UNPLUG,
818+
val);
819+
}
820+
811821
static irqreturn_t cs42l84_irq_thread(int irq, void *data)
812822
{
813823
struct cs42l84_private *cs42l84 = (struct cs42l84_private *)data;
814824
unsigned int stickies[1];
815825
unsigned int masks[1];
816826
unsigned int reg;
817-
u8 current_plug_status;
827+
u8 current_tip_state;
828+
u8 current_ring_state;
818829
int i;
819830

820831
mutex_lock(&cs42l84->irq_lock);
@@ -828,15 +839,22 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data)
828839
irq_params_table[i].mask;
829840
}
830841

842+
/* When handling plug sene IRQs, we only care about EITHER tip OR ring.
843+
* Ring is useless on remove, and is only useful on insert for
844+
* detecting if the plug state has changed AFTER we have handled the
845+
* tip sense IRQ, e.g. if the plug was not fully seated within the tip
846+
* sense debounce time. */
847+
831848
if ((~masks[0]) & irq_params_table[0].mask) {
832849
regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
833-
current_plug_status = (((char) reg) &
850+
851+
current_tip_state = (((char) reg) &
834852
(CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
835853
CS42L84_TS_PLUG_SHIFT;
836854

837-
if (current_plug_status != cs42l84->plug_state) {
838-
cs42l84->plug_state = current_plug_status;
839-
switch (current_plug_status) {
855+
if (current_tip_state != cs42l84->tip_state) {
856+
cs42l84->tip_state = current_tip_state;
857+
switch (current_tip_state) {
840858
case CS42L84_PLUG:
841859
dev_dbg(cs42l84->dev, "Plug event\n");
842860

@@ -850,41 +868,55 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data)
850868
* was disconnected at any point during the detection procedure.
851869
*/
852870
regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
853-
current_plug_status = (((char) reg) &
871+
current_tip_state = (((char) reg) &
854872
(CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
855873
CS42L84_TS_PLUG_SHIFT;
856-
if (current_plug_status != CS42L84_PLUG) {
874+
if (current_tip_state != CS42L84_PLUG) {
857875
dev_dbg(cs42l84->dev, "Wobbly connection, detection invalidated\n");
858-
cs42l84->plug_state = CS42L84_UNPLUG;
876+
cs42l84->tip_state = CS42L84_UNPLUG;
859877
cs42l84_revert_hs(cs42l84);
860878
}
879+
880+
/* Unmask ring sense interrupts */
881+
cs42l84_set_interrupt_masks(cs42l84, 0);
861882
break;
862883
case CS42L84_UNPLUG:
884+
cs42l84->ring_state = CS42L84_UNPLUG;
863885
dev_dbg(cs42l84->dev, "Unplug event\n");
864886

865887
cs42l84_revert_hs(cs42l84);
866888
cs42l84->hs_type = 0;
867889
snd_soc_jack_report(cs42l84->jack, 0,
868890
SND_JACK_HEADSET);
891+
892+
/* Mask ring sense interrupts */
893+
cs42l84_set_interrupt_masks(cs42l84, CS42L84_RS_PLUG | CS42L84_RS_UNPLUG);
869894
break;
870895
default:
871-
break;
896+
cs42l84->ring_state = CS42L84_TRANS;
872897
}
898+
899+
mutex_unlock(&cs42l84->irq_lock);
900+
return IRQ_HANDLED;
873901
}
902+
903+
/* Tip state didn't change, we must've got a ring sense IRQ */
904+
current_ring_state = (((char) reg) &
905+
(CS42L84_RS_PLUG | CS42L84_RS_UNPLUG)) >>
906+
CS42L84_RS_PLUG_SHIFT;
907+
908+
if (current_ring_state != cs42l84->ring_state) {
909+
cs42l84->ring_state = current_ring_state;
910+
if (current_ring_state == CS42L84_PLUG)
911+
cs42l84_detect_hs(cs42l84);
912+
}
913+
}
914+
874915
mutex_unlock(&cs42l84->irq_lock);
875916

876917
return IRQ_HANDLED;
877918
}
878919

879-
static void cs42l84_set_interrupt_masks(struct cs42l84_private *cs42l84,
880-
unsigned int val)
881-
{
882-
regmap_update_bits(cs42l84->regmap, CS42L84_TSRS_PLUG_INT_MASK,
883-
CS42L84_RS_PLUG | CS42L84_RS_UNPLUG |
884-
CS42L84_TS_PLUG | CS42L84_TS_UNPLUG,
885-
val);
886-
}
887-
888920
static void cs42l84_setup_plug_detect(struct cs42l84_private *cs42l84)
889921
{
890922
unsigned int reg;
@@ -914,7 +946,7 @@ static void cs42l84_setup_plug_detect(struct cs42l84_private *cs42l84)
914946

915947
/* Save the initial status of the tip sense */
916948
regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
917-
cs42l84->plug_state = (((char) reg) &
949+
cs42l84->tip_state = (((char) reg) &
918950
(CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
919951
CS42L84_TS_PLUG_SHIFT;
920952

0 commit comments

Comments
 (0)