Skip to content

Commit 7aeb259

Browse files
bo liutiwai
authored andcommitted
ALSA: hda/conexant: Fix headset auto detect fail in cx8070 and SN6140
When OMTP headset plugin the headset jack of CX8070 and SN6160 sound cards, the headset type detection circuit will recognize the headset type as CTIA. At this point, plugout and plugin the headset will get the correct headset type as OMTP. The reason for the failure of headset type recognition is that the sound card creation will enable the VREF voltage of the headset mic, which interferes with the headset type automatic detection circuit. Plugout and plugin the headset will restart the headset detection and get the correct headset type. The patch is disable the VREF voltage when the headset is not present, and will enable the VREF voltage when the headset is present. Signed-off-by: bo liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent 0205f37 commit 7aeb259

File tree

1 file changed

+113
-2
lines changed

1 file changed

+113
-2
lines changed

sound/pci/hda/patch_conexant.c

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
#include "hda_jack.h"
2222
#include "hda_generic.h"
2323

24+
enum {
25+
CX_HEADSET_NOPRESENT = 0,
26+
CX_HEADSET_PARTPRESENT,
27+
CX_HEADSET_ALLPRESENT,
28+
};
29+
2430
struct conexant_spec {
2531
struct hda_gen_spec gen;
2632

@@ -42,7 +48,8 @@ struct conexant_spec {
4248
unsigned int gpio_led;
4349
unsigned int gpio_mute_led_mask;
4450
unsigned int gpio_mic_led_mask;
45-
51+
unsigned int headset_present_flag;
52+
bool is_cx8070_sn6140;
4653
};
4754

4855

@@ -164,6 +171,27 @@ static void cxt_init_gpio_led(struct hda_codec *codec)
164171
}
165172
}
166173

174+
static void cx_fixup_headset_recog(struct hda_codec *codec)
175+
{
176+
unsigned int mic_persent;
177+
178+
/* fix some headset type recognize fail issue, such as EDIFIER headset */
179+
/* set micbiasd output current comparator threshold from 66% to 55%. */
180+
snd_hda_codec_write(codec, 0x1c, 0, 0x320, 0x010);
181+
/* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias registor
182+
* value adjustment trim from 2.2K ohms to 2.0K ohms.
183+
*/
184+
snd_hda_codec_write(codec, 0x1c, 0, 0x3b0, 0xe10);
185+
/* fix reboot headset type recognize fail issue */
186+
mic_persent = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0);
187+
if (mic_persent & AC_PINSENSE_PRESENCE)
188+
/* enable headset mic VREF */
189+
snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
190+
else
191+
/* disable headset mic VREF */
192+
snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20);
193+
}
194+
167195
static int cx_auto_init(struct hda_codec *codec)
168196
{
169197
struct conexant_spec *spec = codec->spec;
@@ -174,6 +202,9 @@ static int cx_auto_init(struct hda_codec *codec)
174202
cxt_init_gpio_led(codec);
175203
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
176204

205+
if (spec->is_cx8070_sn6140)
206+
cx_fixup_headset_recog(codec);
207+
177208
return 0;
178209
}
179210

@@ -192,6 +223,77 @@ static void cx_auto_free(struct hda_codec *codec)
192223
snd_hda_gen_free(codec);
193224
}
194225

226+
static void cx_process_headset_plugin(struct hda_codec *codec)
227+
{
228+
unsigned int val;
229+
unsigned int count = 0;
230+
231+
/* Wait headset detect done. */
232+
do {
233+
val = snd_hda_codec_read(codec, 0x1c, 0, 0xca0, 0x0);
234+
if (val & 0x080) {
235+
codec_dbg(codec, "headset type detect done!\n");
236+
break;
237+
}
238+
msleep(20);
239+
count++;
240+
} while (count < 3);
241+
val = snd_hda_codec_read(codec, 0x1c, 0, 0xcb0, 0x0);
242+
if (val & 0x800) {
243+
codec_dbg(codec, "headset plugin, type is CTIA\n");
244+
snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
245+
} else if (val & 0x400) {
246+
codec_dbg(codec, "headset plugin, type is OMTP\n");
247+
snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
248+
} else {
249+
codec_dbg(codec, "headphone plugin\n");
250+
}
251+
}
252+
253+
static void cx_update_headset_mic_vref(struct hda_codec *codec, unsigned int res)
254+
{
255+
unsigned int phone_present, mic_persent, phone_tag, mic_tag;
256+
struct conexant_spec *spec = codec->spec;
257+
258+
/* In cx8070 and sn6140, the node 16 can only be config to headphone or disabled,
259+
* the node 19 can only be config to microphone or disabled.
260+
* Check hp&mic tag to process headset pulgin&plugout.
261+
*/
262+
phone_tag = snd_hda_codec_read(codec, 0x16, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0x0);
263+
mic_tag = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0x0);
264+
if ((phone_tag & (res >> AC_UNSOL_RES_TAG_SHIFT)) ||
265+
(mic_tag & (res >> AC_UNSOL_RES_TAG_SHIFT))) {
266+
phone_present = snd_hda_codec_read(codec, 0x16, 0, AC_VERB_GET_PIN_SENSE, 0x0);
267+
if (!(phone_present & AC_PINSENSE_PRESENCE)) {/* headphone plugout */
268+
spec->headset_present_flag = CX_HEADSET_NOPRESENT;
269+
snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20);
270+
return;
271+
}
272+
if (spec->headset_present_flag == CX_HEADSET_NOPRESENT) {
273+
spec->headset_present_flag = CX_HEADSET_PARTPRESENT;
274+
} else if (spec->headset_present_flag == CX_HEADSET_PARTPRESENT) {
275+
mic_persent = snd_hda_codec_read(codec, 0x19, 0,
276+
AC_VERB_GET_PIN_SENSE, 0x0);
277+
/* headset is present */
278+
if ((phone_present & AC_PINSENSE_PRESENCE) &&
279+
(mic_persent & AC_PINSENSE_PRESENCE)) {
280+
cx_process_headset_plugin(codec);
281+
spec->headset_present_flag = CX_HEADSET_ALLPRESENT;
282+
}
283+
}
284+
}
285+
}
286+
287+
static void cx_jack_unsol_event(struct hda_codec *codec, unsigned int res)
288+
{
289+
struct conexant_spec *spec = codec->spec;
290+
291+
if (spec->is_cx8070_sn6140)
292+
cx_update_headset_mic_vref(codec, res);
293+
294+
snd_hda_jack_unsol_event(codec, res);
295+
}
296+
195297
#ifdef CONFIG_PM
196298
static int cx_auto_suspend(struct hda_codec *codec)
197299
{
@@ -205,7 +307,7 @@ static const struct hda_codec_ops cx_auto_patch_ops = {
205307
.build_pcms = snd_hda_gen_build_pcms,
206308
.init = cx_auto_init,
207309
.free = cx_auto_free,
208-
.unsol_event = snd_hda_jack_unsol_event,
310+
.unsol_event = cx_jack_unsol_event,
209311
#ifdef CONFIG_PM
210312
.suspend = cx_auto_suspend,
211313
.check_power_status = snd_hda_gen_check_power_status,
@@ -1042,6 +1144,15 @@ static int patch_conexant_auto(struct hda_codec *codec)
10421144
codec->spec = spec;
10431145
codec->patch_ops = cx_auto_patch_ops;
10441146

1147+
/* init cx8070/sn6140 flag and reset headset_present_flag */
1148+
switch (codec->core.vendor_id) {
1149+
case 0x14f11f86:
1150+
case 0x14f11f87:
1151+
spec->is_cx8070_sn6140 = true;
1152+
spec->headset_present_flag = CX_HEADSET_NOPRESENT;
1153+
break;
1154+
}
1155+
10451156
cx_auto_parse_eapd(codec);
10461157
spec->gen.own_eapd_ctl = 1;
10471158

0 commit comments

Comments
 (0)