Skip to content

Commit ec6a8aa

Browse files
Stefan Bindingtiwai
authored andcommitted
ALSA: hda/cs8409: Support manual mode detection for CS42L42
For Jack detection on CS42L42, detection is normally done using "auto" mode, which automatically detects what type of jack is connected to the device. However, some headsets are not automatically detected, and as such and alternative detection method "manual mode" can be used to detect these headsets. Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com> Tested-by: Chris Chiu <chris.chiu@canonical.com> Link: https://lore.kernel.org/r/20220504161236.2490532-4-sbinding@opensource.cirrus.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
1 parent 9cd8273 commit ec6a8aa

3 files changed

Lines changed: 132 additions & 31 deletions

File tree

sound/pci/hda/patch_cs8409-tables.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,6 @@ struct sub_codec cs8409_cs42l42_codec = {
252252
.init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq),
253253
.hp_jack_in = 0,
254254
.mic_jack_in = 0,
255-
.force_status_change = 1,
256255
.paged = 1,
257256
.suspended = 1,
258257
.no_type_dect = 0,
@@ -444,7 +443,6 @@ struct sub_codec dolphin_cs42l42_0 = {
444443
.init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq),
445444
.hp_jack_in = 0,
446445
.mic_jack_in = 0,
447-
.force_status_change = 1,
448446
.paged = 1,
449447
.suspended = 1,
450448
.no_type_dect = 0,
@@ -458,7 +456,6 @@ struct sub_codec dolphin_cs42l42_1 = {
458456
.init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq),
459457
.hp_jack_in = 0,
460458
.mic_jack_in = 0,
461-
.force_status_change = 1,
462459
.paged = 1,
463460
.suspended = 1,
464461
.no_type_dect = 1,

sound/pci/hda/patch_cs8409.c

Lines changed: 132 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -634,38 +634,128 @@ static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
634634
cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0xc0);
635635
}
636636

637-
static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
637+
static int cs42l42_manual_hs_det(struct sub_codec *cs42l42)
638638
{
639-
int status_changed = cs42l42->force_status_change;
639+
unsigned int hs_det_status;
640+
unsigned int hs_det_comp1;
641+
unsigned int hs_det_comp2;
642+
unsigned int hs_det_sw;
643+
unsigned int hs_type;
644+
645+
/* Set hs detect to manual, active mode */
646+
cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
647+
(1 << CS42L42_HSDET_CTRL_SHIFT) |
648+
(0 << CS42L42_HSDET_SET_SHIFT) |
649+
(0 << CS42L42_HSBIAS_REF_SHIFT) |
650+
(0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
651+
652+
/* Configure HS DET comparator reference levels. */
653+
cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
654+
(CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) |
655+
(CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT));
656+
657+
/* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */
658+
cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1);
659+
660+
msleep(100);
661+
662+
hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
663+
664+
hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
665+
CS42L42_HSDET_COMP1_OUT_SHIFT;
666+
hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
667+
CS42L42_HSDET_COMP2_OUT_SHIFT;
668+
669+
/* Close the SW_HSB_HS3 switch for a Type 2 headset. */
670+
cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2);
640671

641-
cs42l42->force_status_change = 0;
672+
msleep(100);
673+
674+
hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
675+
676+
hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
677+
CS42L42_HSDET_COMP1_OUT_SHIFT) << 1;
678+
hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
679+
CS42L42_HSDET_COMP2_OUT_SHIFT) << 1;
680+
681+
/* Use Comparator 1 with 1.25V Threshold. */
682+
switch (hs_det_comp1) {
683+
case CS42L42_HSDET_COMP_TYPE1:
684+
hs_type = CS42L42_PLUG_CTIA;
685+
hs_det_sw = CS42L42_HSDET_SW_TYPE1;
686+
break;
687+
case CS42L42_HSDET_COMP_TYPE2:
688+
hs_type = CS42L42_PLUG_OMTP;
689+
hs_det_sw = CS42L42_HSDET_SW_TYPE2;
690+
break;
691+
default:
692+
/* Fallback to Comparator 2 with 1.75V Threshold. */
693+
switch (hs_det_comp2) {
694+
case CS42L42_HSDET_COMP_TYPE1:
695+
hs_type = CS42L42_PLUG_CTIA;
696+
hs_det_sw = CS42L42_HSDET_SW_TYPE1;
697+
break;
698+
case CS42L42_HSDET_COMP_TYPE2:
699+
hs_type = CS42L42_PLUG_OMTP;
700+
hs_det_sw = CS42L42_HSDET_SW_TYPE2;
701+
break;
702+
case CS42L42_HSDET_COMP_TYPE3:
703+
hs_type = CS42L42_PLUG_HEADPHONE;
704+
hs_det_sw = CS42L42_HSDET_SW_TYPE3;
705+
break;
706+
default:
707+
hs_type = CS42L42_PLUG_INVALID;
708+
hs_det_sw = CS42L42_HSDET_SW_TYPE4;
709+
break;
710+
}
711+
}
712+
713+
/* Set Switches */
714+
cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, hs_det_sw);
715+
716+
/* Set HSDET mode to Manual—Disabled */
717+
cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
718+
(0 << CS42L42_HSDET_CTRL_SHIFT) |
719+
(0 << CS42L42_HSDET_SET_SHIFT) |
720+
(0 << CS42L42_HSBIAS_REF_SHIFT) |
721+
(0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
722+
723+
/* Configure HS DET comparator reference levels. */
724+
cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
725+
(CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) |
726+
(CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT));
727+
728+
return hs_type;
729+
}
730+
731+
static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
732+
{
733+
int status_changed = 0;
642734

643735
/* TIP_SENSE INSERT/REMOVE */
644736
switch (reg_ts_status) {
645737
case CS42L42_TS_PLUG:
646-
if (!cs42l42->hp_jack_in) {
647-
if (cs42l42->no_type_dect) {
648-
status_changed = 1;
649-
cs42l42->hp_jack_in = 1;
650-
cs42l42->mic_jack_in = 0;
651-
} else {
652-
cs42l42_run_jack_detect(cs42l42);
653-
}
738+
if (cs42l42->no_type_dect) {
739+
status_changed = 1;
740+
cs42l42->hp_jack_in = 1;
741+
cs42l42->mic_jack_in = 0;
742+
} else {
743+
cs42l42_run_jack_detect(cs42l42);
654744
}
655745
break;
656746

657747
case CS42L42_TS_UNPLUG:
658-
if (cs42l42->hp_jack_in || cs42l42->mic_jack_in) {
659-
status_changed = 1;
660-
cs42l42->hp_jack_in = 0;
661-
cs42l42->mic_jack_in = 0;
662-
}
748+
status_changed = 1;
749+
cs42l42->hp_jack_in = 0;
750+
cs42l42->mic_jack_in = 0;
663751
break;
664752
default:
665753
/* jack in transition */
666754
break;
667755
}
668756

757+
codec_dbg(cs42l42->codec, "Tip Sense Detection: (%d)\n", reg_ts_status);
758+
669759
return status_changed;
670760
}
671761

@@ -698,24 +788,40 @@ static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
698788

699789
type = (reg_hs_status & CS42L42_HSDET_TYPE_MASK) >> CS42L42_HSDET_TYPE_SHIFT;
700790

791+
/* Configure the HSDET mode. */
792+
cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
793+
701794
if (cs42l42->no_type_dect) {
702795
status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status);
703-
} else if (type == CS42L42_PLUG_INVALID) {
704-
/* Type CS42L42_PLUG_INVALID not supported */
705-
status_changed = cs42l42_handle_tip_sense(cs42l42, CS42L42_TS_UNPLUG);
706796
} else {
707-
if (!cs42l42->hp_jack_in) {
708-
status_changed = 1;
709-
cs42l42->hp_jack_in = 1;
797+
if (type == CS42L42_PLUG_INVALID || type == CS42L42_PLUG_HEADPHONE) {
798+
codec_dbg(cs42l42->codec,
799+
"Auto detect value not valid (%d), running manual det\n",
800+
type);
801+
type = cs42l42_manual_hs_det(cs42l42);
710802
}
711-
/* type = CS42L42_PLUG_HEADPHONE has no mic */
712-
if ((!cs42l42->mic_jack_in) && (type != CS42L42_PLUG_HEADPHONE)) {
803+
804+
switch (type) {
805+
case CS42L42_PLUG_CTIA:
806+
case CS42L42_PLUG_OMTP:
713807
status_changed = 1;
808+
cs42l42->hp_jack_in = 1;
714809
cs42l42->mic_jack_in = 1;
810+
break;
811+
case CS42L42_PLUG_HEADPHONE:
812+
status_changed = 1;
813+
cs42l42->hp_jack_in = 1;
814+
cs42l42->mic_jack_in = 0;
815+
break;
816+
default:
817+
status_changed = 1;
818+
cs42l42->hp_jack_in = 0;
819+
cs42l42->mic_jack_in = 0;
820+
break;
715821
}
822+
codec_dbg(cs42l42->codec, "Detection done (%d)\n", type);
716823
}
717-
/* Configure the HSDET mode. */
718-
cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
824+
719825
/* Enable the HPOUT ground clamp and configure the HP pull-down */
720826
cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x02);
721827
/* Re-Enable Tip Sense Interrupt */
@@ -803,7 +909,6 @@ static void cs42l42_suspend(struct sub_codec *cs42l42)
803909
cs42l42->last_page = 0;
804910
cs42l42->hp_jack_in = 0;
805911
cs42l42->mic_jack_in = 0;
806-
cs42l42->force_status_change = 1;
807912

808913
/* Put CS42L42 into Reset */
809914
gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);

sound/pci/hda/patch_cs8409.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,6 @@ struct sub_codec {
304304

305305
unsigned int hp_jack_in:1;
306306
unsigned int mic_jack_in:1;
307-
unsigned int force_status_change:1;
308307
unsigned int suspended:1;
309308
unsigned int paged:1;
310309
unsigned int last_page;

0 commit comments

Comments
 (0)