patch_conexant.c 34 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
 * HD audio interface patch for Conexant HDA audio codec
 *
 * Copyright (c) 2006 Pototskiy Akex <alex.pototskiy@gmail.com>
 * 		      Takashi Iwai <tiwai@suse.de>
 * 		      Tobin Davis  <tdavis@dsl-only.net>
 *
 *  This driver is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This driver is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
26
#include <linux/module.h>
27
#include <sound/core.h>
28
29
#include <sound/jack.h>

30
31
#include "hda_codec.h"
#include "hda_local.h"
32
#include "hda_auto_parser.h"
33
#include "hda_beep.h"
34
#include "hda_jack.h"
35
#include "hda_generic.h"
36

37
38
struct conexant_spec {
	struct hda_gen_spec gen;
39

40
	unsigned int beep_amp;
41

42
43
44
45
	/* extra EAPD pins */
	unsigned int num_eapds;
	hda_nid_t eapds[4];
	bool dynamic_eapd;
46
	hda_nid_t mute_led_eapd;
47

48
	unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
49

50
51
52
53
54
	/* OPLC XO specific */
	bool recording;
	bool dc_enable;
	unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */
	struct nid_path *dc_mode_path;
55
56
57
58
59
60

	int mute_led_polarity;
	unsigned int gpio_led;
	unsigned int gpio_mute_led_mask;
	unsigned int gpio_mic_led_mask;

61
62
};

63

64
65
66
#ifdef CONFIG_SND_HDA_INPUT_BEEP
static inline void set_beep_amp(struct conexant_spec *spec, hda_nid_t nid,
				int idx, int dir)
67
{
68
69
	spec->gen.beep_nid = nid;
	spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
70
}
71
72
73
74
/* additional beep mixers; the actual parameters are overwritten at build */
static const struct snd_kcontrol_new cxt_beep_mixer[] = {
	HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
	HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
75
76
77
	{ } /* end */
};

78
79
/* create beep controls if needed */
static int add_beep_ctls(struct hda_codec *codec)
80
{
81
82
	struct conexant_spec *spec = codec->spec;
	int err;
83

84
85
86
87
88
89
90
91
92
93
94
95
	if (spec->beep_amp) {
		const struct snd_kcontrol_new *knew;
		for (knew = cxt_beep_mixer; knew->name; knew++) {
			struct snd_kcontrol *kctl;
			kctl = snd_ctl_new1(knew, codec);
			if (!kctl)
				return -ENOMEM;
			kctl->private_value = spec->beep_amp;
			err = snd_hda_ctl_add(codec, 0, kctl);
			if (err < 0)
				return err;
		}
96
97
98
	}
	return 0;
}
99
100
101
102
#else
#define set_beep_amp(spec, nid, idx, dir) /* NOP */
#define add_beep_ctls(codec)	0
#endif
103

104
105
106
107
108
109
/*
 * Automatic parser for CX20641 & co
 */

#ifdef CONFIG_SND_HDA_INPUT_BEEP
static void cx_auto_parse_beep(struct hda_codec *codec)
110
111
{
	struct conexant_spec *spec = codec->spec;
112
	hda_nid_t nid;
113

114
	for_each_hda_codec_node(nid, codec)
115
116
117
118
119
120
121
122
123
		if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
			set_beep_amp(spec, nid, 0, HDA_OUTPUT);
			break;
		}
}
#else
#define cx_auto_parse_beep(codec)
#endif

124
/* parse EAPDs */
125
static void cx_auto_parse_eapd(struct hda_codec *codec)
126
{
127
	struct conexant_spec *spec = codec->spec;
128
	hda_nid_t nid;
129

130
	for_each_hda_codec_node(nid, codec) {
131
		if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
132
			continue;
133
		if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD))
134
			continue;
135
136
		spec->eapds[spec->num_eapds++] = nid;
		if (spec->num_eapds >= ARRAY_SIZE(spec->eapds))
137
138
139
			break;
	}

140
141
142
143
144
145
	/* NOTE: below is a wild guess; if we have more than two EAPDs,
	 * it's a new chip, where EAPDs are supposed to be associated to
	 * pins, and we can control EAPD per pin.
	 * OTOH, if only one or two EAPDs are found, it's an old chip,
	 * thus it might control over all pins.
	 */
146
	if (spec->num_eapds > 2)
147
		spec->dynamic_eapd = 1;
148
149
}

150
151
static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins,
			      hda_nid_t *pins, bool on)
152
153
154
{
	int i;
	for (i = 0; i < num_pins; i++) {
155
156
		if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
			snd_hda_codec_write(codec, pins[i], 0,
157
158
					    AC_VERB_SET_EAPD_BTLENABLE,
					    on ? 0x02 : 0);
159
	}
160
161
}

162
163
/* turn on/off EAPD according to Master switch */
static void cx_auto_vmaster_hook(void *private_data, int enabled)
164
{
165
	struct hda_codec *codec = private_data;
166
167
	struct conexant_spec *spec = codec->spec;

168
	cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
169
170
}

171
172
173
174
175
176
177
178
179
180
181
/* turn on/off EAPD according to Master switch (inversely!) for mute LED */
static void cx_auto_vmaster_hook_mute_led(void *private_data, int enabled)
{
	struct hda_codec *codec = private_data;
	struct conexant_spec *spec = codec->spec;

	snd_hda_codec_write(codec, spec->mute_led_eapd, 0,
			    AC_VERB_SET_EAPD_BTLENABLE,
			    enabled ? 0x00 : 0x02);
}

182
static int cx_auto_build_controls(struct hda_codec *codec)
183
{
184
	int err;
185

186
	err = snd_hda_gen_build_controls(codec);
187
188
	if (err < 0)
		return err;
189

190
	err = add_beep_ctls(codec);
191
192
	if (err < 0)
		return err;
193
194
195
196

	return 0;
}

197
198
199
200
201
202
static int cx_auto_init(struct hda_codec *codec)
{
	struct conexant_spec *spec = codec->spec;
	snd_hda_gen_init(codec);
	if (!spec->dynamic_eapd)
		cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
203
204
205

	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);

206
207
208
	return 0;
}

209
210
211
212
static void cx_auto_reboot_notify(struct hda_codec *codec)
{
	struct conexant_spec *spec = codec->spec;

213
214
215
216
217
	switch (codec->core.vendor_id) {
	case 0x14f150f2: /* CX20722 */
	case 0x14f150f4: /* CX20724 */
		break;
	default:
218
		return;
219
	}
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234

	/* Turn the CX20722 codec into D3 to avoid spurious noises
	   from the internal speaker during (and after) reboot */
	cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false);

	snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3);
	snd_hda_codec_write(codec, codec->core.afg, 0,
			    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
}

static void cx_auto_free(struct hda_codec *codec)
{
	cx_auto_reboot_notify(codec);
	snd_hda_gen_free(codec);
}
235

236
static const struct hda_codec_ops cx_auto_patch_ops = {
237
	.build_controls = cx_auto_build_controls,
238
	.build_pcms = snd_hda_gen_build_pcms,
239
	.init = cx_auto_init,
240
	.reboot_notify = cx_auto_reboot_notify,
241
	.free = cx_auto_free,
242
	.unsol_event = snd_hda_jack_unsol_event,
243
244
245
#ifdef CONFIG_PM
	.check_power_status = snd_hda_gen_check_power_status,
#endif
246
247
};

248
249
250
/*
 * pin fix-up
 */
251
252
enum {
	CXT_PINCFG_LENOVO_X200,
253
	CXT_PINCFG_LENOVO_TP410,
254
255
	CXT_PINCFG_LEMOTE_A1004,
	CXT_PINCFG_LEMOTE_A1205,
256
	CXT_PINCFG_COMPAQ_CQ60,
257
	CXT_FIXUP_STEREO_DMIC,
258
	CXT_FIXUP_INC_MIC_BOOST,
259
260
	CXT_FIXUP_HEADPHONE_MIC_PIN,
	CXT_FIXUP_HEADPHONE_MIC,
261
	CXT_FIXUP_GPIO1,
262
	CXT_FIXUP_ASPIRE_DMIC,
263
	CXT_FIXUP_THINKPAD_ACPI,
264
	CXT_FIXUP_OLPC_XO,
265
	CXT_FIXUP_CAP_MIX_AMP,
266
	CXT_FIXUP_TOSHIBA_P105,
267
	CXT_FIXUP_HP_530,
268
	CXT_FIXUP_CAP_MIX_AMP_5047,
269
	CXT_FIXUP_MUTE_LED_EAPD,
270
	CXT_FIXUP_HP_DOCK,
271
	CXT_FIXUP_HP_SPECTRE,
272
	CXT_FIXUP_HP_GATE_MIC,
273
	CXT_FIXUP_MUTE_LED_GPIO,
274
275
	CXT_FIXUP_HEADSET_MIC,
	CXT_FIXUP_HP_MIC_NO_PRESENCE,
276
277
};

278
279
/* for hda_fixup_thinkpad_acpi() */
#include "thinkpad_helper.c"
280

281
282
static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
				  const struct hda_fixup *fix, int action)
283
284
{
	struct conexant_spec *spec = codec->spec;
285
	spec->gen.inv_dmic_split = 1;
286
287
}

288
289
static void cxt5066_increase_mic_boost(struct hda_codec *codec,
				   const struct hda_fixup *fix, int action)
290
{
291
292
	if (action != HDA_FIXUP_ACT_PRE_PROBE)
		return;
293

294
295
296
297
298
299
300
	snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT,
				  (0x3 << AC_AMPCAP_OFFSET_SHIFT) |
				  (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) |
				  (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
				  (0 << AC_AMPCAP_MUTE_SHIFT));
}

301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
static void cxt_update_headset_mode(struct hda_codec *codec)
{
	/* The verbs used in this function were tested on a Conexant CX20751/2 codec. */
	int i;
	bool mic_mode = false;
	struct conexant_spec *spec = codec->spec;
	struct auto_pin_cfg *cfg = &spec->gen.autocfg;

	hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];

	for (i = 0; i < cfg->num_inputs; i++)
		if (cfg->inputs[i].pin == mux_pin) {
			mic_mode = !!cfg->inputs[i].is_headphone_mic;
			break;
		}

	if (mic_mode) {
		snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */
		spec->gen.hp_jack_present = false;
	} else {
		snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */
		spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]);
	}

	snd_hda_gen_update_outputs(codec);
}

static void cxt_update_headset_mode_hook(struct hda_codec *codec,
329
330
					 struct snd_kcontrol *kcontrol,
					 struct snd_ctl_elem_value *ucontrol)
331
332
333
334
335
336
337
338
339
340
341
342
{
	cxt_update_headset_mode(codec);
}

static void cxt_fixup_headphone_mic(struct hda_codec *codec,
				    const struct hda_fixup *fix, int action)
{
	struct conexant_spec *spec = codec->spec;

	switch (action) {
	case HDA_FIXUP_ACT_PRE_PROBE:
		spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC;
343
		snd_hdac_regmap_add_vendor_verb(&codec->core, 0x410);
344
345
346
347
348
349
350
351
352
353
354
		break;
	case HDA_FIXUP_ACT_PROBE:
		spec->gen.cap_sync_hook = cxt_update_headset_mode_hook;
		spec->gen.automute_hook = cxt_update_headset_mode;
		break;
	case HDA_FIXUP_ACT_INIT:
		cxt_update_headset_mode(codec);
		break;
	}
}

355
356
357
358
359
360
361
362
363
364
365
366
static void cxt_fixup_headset_mic(struct hda_codec *codec,
				    const struct hda_fixup *fix, int action)
{
	struct conexant_spec *spec = codec->spec;

	switch (action) {
	case HDA_FIXUP_ACT_PRE_PROBE:
		spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
		break;
	}
}

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
/* OPLC XO 1.5 fixup */

/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
 * through the microphone jack.
 * When the user enables this through a mixer switch, both internal and
 * external microphones are disabled. Gain is fixed at 0dB. In this mode,
 * we also allow the bias to be configured through a separate mixer
 * control. */

#define update_mic_pin(codec, nid, val)					\
	snd_hda_codec_update_cache(codec, nid, 0,			\
				   AC_VERB_SET_PIN_WIDGET_CONTROL, val)

static const struct hda_input_mux olpc_xo_dc_bias = {
	.num_items = 3,
	.items = {
		{ "Off", PIN_IN },
		{ "50%", PIN_VREF50 },
		{ "80%", PIN_VREF80 },
	},
};

static void olpc_xo_update_mic_boost(struct hda_codec *codec)
{
	struct conexant_spec *spec = codec->spec;
	int ch, val;

	for (ch = 0; ch < 2; ch++) {
		val = AC_AMP_SET_OUTPUT |
			(ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT);
		if (!spec->dc_enable)
			val |= snd_hda_codec_amp_read(codec, 0x17, ch, HDA_OUTPUT, 0);
		snd_hda_codec_write(codec, 0x17, 0,
				    AC_VERB_SET_AMP_GAIN_MUTE, val);
	}
}

static void olpc_xo_update_mic_pins(struct hda_codec *codec)
{
	struct conexant_spec *spec = codec->spec;
	int cur_input, val;
	struct nid_path *path;

	cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]];

	/* Set up mic pins for port-B, C and F dynamically as the recording
	 * LED is turned on/off by these pin controls
	 */
	if (!spec->dc_enable) {
		/* disable DC bias path and pin for port F */
		update_mic_pin(codec, 0x1e, 0);
		snd_hda_activate_path(codec, spec->dc_mode_path, false, false);

		/* update port B (ext mic) and C (int mic) */
		/* OLPC defers mic widget control until when capture is
		 * started because the microphone LED comes on as soon as
		 * these settings are put in place. if we did this before
		 * recording, it would give the false indication that
		 * recording is happening when it is not.
		 */
		update_mic_pin(codec, 0x1a, spec->recording ?
			       snd_hda_codec_get_pin_target(codec, 0x1a) : 0);
		update_mic_pin(codec, 0x1b, spec->recording ?
			       snd_hda_codec_get_pin_target(codec, 0x1b) : 0);
		/* enable normal mic path */
		path = snd_hda_get_path_from_idx(codec, cur_input);
		if (path)
			snd_hda_activate_path(codec, path, true, false);
	} else {
		/* disable normal mic path */
		path = snd_hda_get_path_from_idx(codec, cur_input);
		if (path)
			snd_hda_activate_path(codec, path, false, false);

		/* Even though port F is the DC input, the bias is controlled
		 * on port B.  We also leave that port as an active input (but
		 * unselected) in DC mode just in case that is necessary to
		 * make the bias setting take effect.
		 */
		if (spec->recording)
			val = olpc_xo_dc_bias.items[spec->dc_input_bias].index;
		else
			val = 0;
		update_mic_pin(codec, 0x1a, val);
		update_mic_pin(codec, 0x1b, 0);
		/* enable DC bias path and pin */
		update_mic_pin(codec, 0x1e, spec->recording ? PIN_IN : 0);
		snd_hda_activate_path(codec, spec->dc_mode_path, true, false);
	}
}

/* mic_autoswitch hook */
459
460
static void olpc_xo_automic(struct hda_codec *codec,
			    struct hda_jack_callback *jack)
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
{
	struct conexant_spec *spec = codec->spec;

	/* in DC mode, we don't handle automic */
	if (!spec->dc_enable)
		snd_hda_gen_mic_autoswitch(codec, jack);
	olpc_xo_update_mic_pins(codec);
	if (spec->dc_enable)
		olpc_xo_update_mic_boost(codec);
}

/* pcm_capture hook */
static void olpc_xo_capture_hook(struct hda_pcm_stream *hinfo,
				 struct hda_codec *codec,
				 struct snd_pcm_substream *substream,
				 int action)
{
	struct conexant_spec *spec = codec->spec;

	/* toggle spec->recording flag and update mic pins accordingly
	 * for turning on/off LED
	 */
	switch (action) {
	case HDA_GEN_PCM_ACT_PREPARE:
		spec->recording = 1;
		olpc_xo_update_mic_pins(codec);
		break;
	case HDA_GEN_PCM_ACT_CLEANUP:
		spec->recording = 0;
		olpc_xo_update_mic_pins(codec);
		break;
	}
}

static int olpc_xo_dc_mode_get(struct snd_kcontrol *kcontrol,
			       struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct conexant_spec *spec = codec->spec;
	ucontrol->value.integer.value[0] = spec->dc_enable;
	return 0;
}

static int olpc_xo_dc_mode_put(struct snd_kcontrol *kcontrol,
			       struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct conexant_spec *spec = codec->spec;
	int dc_enable = !!ucontrol->value.integer.value[0];

	if (dc_enable == spec->dc_enable)
		return 0;

	spec->dc_enable = dc_enable;
	olpc_xo_update_mic_pins(codec);
	olpc_xo_update_mic_boost(codec);
	return 1;
}

static int olpc_xo_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
				    struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct conexant_spec *spec = codec->spec;
	ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
	return 0;
}

static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
				     struct snd_ctl_elem_info *uinfo)
{
	return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo);
}

static int olpc_xo_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
				    struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct conexant_spec *spec = codec->spec;
	const struct hda_input_mux *imux = &olpc_xo_dc_bias;
	unsigned int idx;

	idx = ucontrol->value.enumerated.item[0];
	if (idx >= imux->num_items)
		idx = imux->num_items - 1;
	if (spec->dc_input_bias == idx)
		return 0;

	spec->dc_input_bias = idx;
	if (spec->dc_enable)
		olpc_xo_update_mic_pins(codec);
	return 1;
}

static const struct snd_kcontrol_new olpc_xo_mixers[] = {
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "DC Mode Enable Switch",
		.info = snd_ctl_boolean_mono_info,
		.get = olpc_xo_dc_mode_get,
		.put = olpc_xo_dc_mode_put,
	},
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "DC Input Bias Enum",
		.info = olpc_xo_dc_bias_enum_info,
		.get = olpc_xo_dc_bias_enum_get,
		.put = olpc_xo_dc_bias_enum_put,
	},
	{}
};

/* overriding mic boost put callback; update mic boost volume only when
 * DC mode is disabled
 */
static int olpc_xo_mic_boost_put(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct conexant_spec *spec = codec->spec;
	int ret = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
	if (ret > 0 && spec->dc_enable)
		olpc_xo_update_mic_boost(codec);
	return ret;
}

static void cxt_fixup_olpc_xo(struct hda_codec *codec,
				    const struct hda_fixup *fix, int action)
{
	struct conexant_spec *spec = codec->spec;
591
	struct snd_kcontrol_new *kctl;
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
	int i;

	if (action != HDA_FIXUP_ACT_PROBE)
		return;

	spec->gen.mic_autoswitch_hook = olpc_xo_automic;
	spec->gen.pcm_capture_hook = olpc_xo_capture_hook;
	spec->dc_mode_path = snd_hda_add_new_path(codec, 0x1e, 0x14, 0);

	snd_hda_add_new_ctls(codec, olpc_xo_mixers);

	/* OLPC's microphone port is DC coupled for use with external sensors,
	 * therefore we use a 50% mic bias in order to center the input signal
	 * with the DC input range of the codec.
	 */
	snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50);

	/* override mic boost control */
610
	snd_array_for_each(&spec->gen.kctls, i, kctl) {
611
612
613
614
615
616
617
		if (!strcmp(kctl->name, "Mic Boost Volume")) {
			kctl->put = olpc_xo_mic_boost_put;
			break;
		}
	}
}

618
619
620
621
622
623
624
625
626
627
628
629
static void cxt_fixup_mute_led_eapd(struct hda_codec *codec,
				    const struct hda_fixup *fix, int action)
{
	struct conexant_spec *spec = codec->spec;

	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
		spec->mute_led_eapd = 0x1b;
		spec->dynamic_eapd = 1;
		spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook_mute_led;
	}
}

630
631
632
633
634
635
636
637
638
639
640
641
642
/*
 * Fix max input level on mixer widget to 0dB
 * (originally it has 0x2b steps with 0dB offset 0x14)
 */
static void cxt_fixup_cap_mix_amp(struct hda_codec *codec,
				  const struct hda_fixup *fix, int action)
{
	snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
				  (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
				  (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
				  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
				  (1 << AC_AMPCAP_MUTE_SHIFT));
}
643

644
645
646
647
648
649
650
651
652
653
654
655
656
657
/*
 * Fix max input level on mixer widget to 0dB
 * (originally it has 0x1e steps with 0 dB offset 0x17)
 */
static void cxt_fixup_cap_mix_amp_5047(struct hda_codec *codec,
				  const struct hda_fixup *fix, int action)
{
	snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT,
				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
				  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
				  (1 << AC_AMPCAP_MUTE_SHIFT));
}

658
659
660
661
662
663
664
665
666
667
668
static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec,
				       const struct hda_fixup *fix,
				       int action)
{
	/* the mic pin (0x19) doesn't give an unsolicited event;
	 * probe the mic pin together with the headphone pin (0x16)
	 */
	if (action == HDA_FIXUP_ACT_PROBE)
		snd_hda_jack_set_gating_jack(codec, 0x19, 0x16);
}

669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
/* update LED status via GPIO */
static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask,
				bool enabled)
{
	struct conexant_spec *spec = codec->spec;
	unsigned int oldval = spec->gpio_led;

	if (spec->mute_led_polarity)
		enabled = !enabled;

	if (enabled)
		spec->gpio_led &= ~mask;
	else
		spec->gpio_led |= mask;
	if (spec->gpio_led != oldval)
		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
				    spec->gpio_led);
}

/* turn on/off mute LED via GPIO per vmaster hook */
static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled)
{
	struct hda_codec *codec = private_data;
	struct conexant_spec *spec = codec->spec;

	cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled);
}

/* turn on/off mic-mute LED via GPIO per capture hook */
static void cxt_fixup_gpio_mic_mute_hook(struct hda_codec *codec,
					 struct snd_kcontrol *kcontrol,
					 struct snd_ctl_elem_value *ucontrol)
{
	struct conexant_spec *spec = codec->spec;

	if (ucontrol)
		cxt_update_gpio_led(codec, spec->gpio_mic_led_mask,
				    ucontrol->value.integer.value[0] ||
				    ucontrol->value.integer.value[1]);
}


static void cxt_fixup_mute_led_gpio(struct hda_codec *codec,
				const struct hda_fixup *fix, int action)
{
	struct conexant_spec *spec = codec->spec;
	static const struct hda_verb gpio_init[] = {
		{ 0x01, AC_VERB_SET_GPIO_MASK, 0x03 },
		{ 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03 },
		{}
	};
	codec_info(codec, "action: %d gpio_led: %d\n", action, spec->gpio_led);

	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
		spec->gen.vmaster_mute.hook = cxt_fixup_gpio_mute_hook;
		spec->gen.cap_sync_hook = cxt_fixup_gpio_mic_mute_hook;
		spec->gpio_led = 0;
		spec->mute_led_polarity = 0;
		spec->gpio_mute_led_mask = 0x01;
		spec->gpio_mic_led_mask = 0x02;
	}
	snd_hda_add_verbs(codec, gpio_init);
	if (spec->gpio_led)
		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
				    spec->gpio_led);
}


737
/* ThinkPad X200 & co with cxt5051 */
738
static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
739
740
741
	{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
	{ 0x17, 0x21a11000 }, /* dock-mic */
	{ 0x19, 0x2121103f }, /* dock-HP */
742
	{ 0x1c, 0x21440100 }, /* dock SPDIF out */
743
744
745
	{}
};

746
/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */
747
static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = {
748
749
750
751
752
753
	{ 0x19, 0x042110ff }, /* HP (seq# overridden) */
	{ 0x1a, 0x21a190f0 }, /* dock-mic */
	{ 0x1c, 0x212140ff }, /* dock-HP */
	{}
};

754
755
756
757
758
759
760
761
762
763
764
765
/* Lemote A1004/A1205 with cxt5066 */
static const struct hda_pintbl cxt_pincfg_lemote[] = {
	{ 0x1a, 0x90a10020 }, /* Internal mic */
	{ 0x1b, 0x03a11020 }, /* External mic */
	{ 0x1d, 0x400101f0 }, /* Not used */
	{ 0x1e, 0x40a701f0 }, /* Not used */
	{ 0x20, 0x404501f0 }, /* Not used */
	{ 0x22, 0x404401f0 }, /* Not used */
	{ 0x23, 0x40a701f0 }, /* Not used */
	{}
};

766
767
768
769
770
771
772
773
static const struct hda_fixup cxt_fixups[] = {
	[CXT_PINCFG_LENOVO_X200] = {
		.type = HDA_FIXUP_PINS,
		.v.pins = cxt_pincfg_lenovo_x200,
	},
	[CXT_PINCFG_LENOVO_TP410] = {
		.type = HDA_FIXUP_PINS,
		.v.pins = cxt_pincfg_lenovo_tp410,
774
775
		.chained = true,
		.chain_id = CXT_FIXUP_THINKPAD_ACPI,
776
	},
777
778
779
780
781
782
783
784
785
786
	[CXT_PINCFG_LEMOTE_A1004] = {
		.type = HDA_FIXUP_PINS,
		.chained = true,
		.chain_id = CXT_FIXUP_INC_MIC_BOOST,
		.v.pins = cxt_pincfg_lemote,
	},
	[CXT_PINCFG_LEMOTE_A1205] = {
		.type = HDA_FIXUP_PINS,
		.v.pins = cxt_pincfg_lemote,
	},
787
788
789
790
791
792
793
794
795
	[CXT_PINCFG_COMPAQ_CQ60] = {
		.type = HDA_FIXUP_PINS,
		.v.pins = (const struct hda_pintbl[]) {
			/* 0x17 was falsely set up as a mic, it should 0x1d */
			{ 0x17, 0x400001f0 },
			{ 0x1d, 0x97a70120 },
			{ }
		}
	},
796
797
798
799
	[CXT_FIXUP_STEREO_DMIC] = {
		.type = HDA_FIXUP_FUNC,
		.v.func = cxt_fixup_stereo_dmic,
	},
800
801
802
803
	[CXT_FIXUP_INC_MIC_BOOST] = {
		.type = HDA_FIXUP_FUNC,
		.v.func = cxt5066_increase_mic_boost,
	},
804
805
806
807
808
809
810
811
812
813
814
815
816
	[CXT_FIXUP_HEADPHONE_MIC_PIN] = {
		.type = HDA_FIXUP_PINS,
		.chained = true,
		.chain_id = CXT_FIXUP_HEADPHONE_MIC,
		.v.pins = (const struct hda_pintbl[]) {
			{ 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
			{ }
		}
	},
	[CXT_FIXUP_HEADPHONE_MIC] = {
		.type = HDA_FIXUP_FUNC,
		.v.func = cxt_fixup_headphone_mic,
	},
817
818
819
820
821
822
823
824
825
	[CXT_FIXUP_GPIO1] = {
		.type = HDA_FIXUP_VERBS,
		.v.verbs = (const struct hda_verb[]) {
			{ 0x01, AC_VERB_SET_GPIO_MASK, 0x01 },
			{ 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 },
			{ 0x01, AC_VERB_SET_GPIO_DATA, 0x01 },
			{ }
		},
	},
826
827
828
829
830
831
	[CXT_FIXUP_ASPIRE_DMIC] = {
		.type = HDA_FIXUP_FUNC,
		.v.func = cxt_fixup_stereo_dmic,
		.chained = true,
		.chain_id = CXT_FIXUP_GPIO1,
	},
832
833
	[CXT_FIXUP_THINKPAD_ACPI] = {
		.type = HDA_FIXUP_FUNC,
834
		.v.func = hda_fixup_thinkpad_acpi,
835
	},
836
837
838
839
	[CXT_FIXUP_OLPC_XO] = {
		.type = HDA_FIXUP_FUNC,
		.v.func = cxt_fixup_olpc_xo,
	},
840
841
842
843
	[CXT_FIXUP_CAP_MIX_AMP] = {
		.type = HDA_FIXUP_FUNC,
		.v.func = cxt_fixup_cap_mix_amp,
	},
844
845
846
847
848
849
850
851
852
	[CXT_FIXUP_TOSHIBA_P105] = {
		.type = HDA_FIXUP_PINS,
		.v.pins = (const struct hda_pintbl[]) {
			{ 0x10, 0x961701f0 }, /* speaker/hp */
			{ 0x12, 0x02a1901e }, /* ext mic */
			{ 0x14, 0x95a70110 }, /* int mic */
			{}
		},
	},
853
854
855
856
857
858
859
860
861
	[CXT_FIXUP_HP_530] = {
		.type = HDA_FIXUP_PINS,
		.v.pins = (const struct hda_pintbl[]) {
			{ 0x12, 0x90a60160 }, /* int mic */
			{}
		},
		.chained = true,
		.chain_id = CXT_FIXUP_CAP_MIX_AMP,
	},
862
863
864
865
	[CXT_FIXUP_CAP_MIX_AMP_5047] = {
		.type = HDA_FIXUP_FUNC,
		.v.func = cxt_fixup_cap_mix_amp_5047,
	},
866
867
868
869
	[CXT_FIXUP_MUTE_LED_EAPD] = {
		.type = HDA_FIXUP_FUNC,
		.v.func = cxt_fixup_mute_led_eapd,
	},
870
871
872
873
874
875
	[CXT_FIXUP_HP_DOCK] = {
		.type = HDA_FIXUP_PINS,
		.v.pins = (const struct hda_pintbl[]) {
			{ 0x16, 0x21011020 }, /* line-out */
			{ 0x18, 0x2181103f }, /* line-in */
			{ }
876
877
878
		},
		.chained = true,
		.chain_id = CXT_FIXUP_MUTE_LED_GPIO,
879
	},
880
881
882
883
884
885
886
887
	[CXT_FIXUP_HP_SPECTRE] = {
		.type = HDA_FIXUP_PINS,
		.v.pins = (const struct hda_pintbl[]) {
			/* enable NID 0x1d for the speaker on top */
			{ 0x1d, 0x91170111 },
			{ }
		}
	},
888
889
890
891
	[CXT_FIXUP_HP_GATE_MIC] = {
		.type = HDA_FIXUP_FUNC,
		.v.func = cxt_fixup_hp_gate_mic_jack,
	},
892
893
894
895
	[CXT_FIXUP_MUTE_LED_GPIO] = {
		.type = HDA_FIXUP_FUNC,
		.v.func = cxt_fixup_mute_led_gpio,
	},
896
897
898
899
900
901
902
903
904
905
906
907
908
	[CXT_FIXUP_HEADSET_MIC] = {
		.type = HDA_FIXUP_FUNC,
		.v.func = cxt_fixup_headset_mic,
	},
	[CXT_FIXUP_HP_MIC_NO_PRESENCE] = {
		.type = HDA_FIXUP_PINS,
		.v.pins = (const struct hda_pintbl[]) {
			{ 0x1a, 0x02a1113c },
			{ }
		},
		.chained = true,
		.chain_id = CXT_FIXUP_HEADSET_MIC,
	},
909
910
911
};

static const struct snd_pci_quirk cxt5045_fixups[] = {
912
	SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT_FIXUP_HP_530),
913
	SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT_FIXUP_TOSHIBA_P105),
914
915
916
917
918
919
920
921
922
923
924
925
	/* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have
	 * really bad sound over 0dB on NID 0x17.
	 */
	SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP),
	SND_PCI_QUIRK_VENDOR(0x1631, "Packard Bell", CXT_FIXUP_CAP_MIX_AMP),
	SND_PCI_QUIRK_VENDOR(0x1734, "Fujitsu", CXT_FIXUP_CAP_MIX_AMP),
	SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT_FIXUP_CAP_MIX_AMP),
	{}
};

static const struct hda_model_fixup cxt5045_fixup_models[] = {
	{ .id = CXT_FIXUP_CAP_MIX_AMP, .name = "cap-mix-amp" },
926
	{ .id = CXT_FIXUP_TOSHIBA_P105, .name = "toshiba-p105" },
927
	{ .id = CXT_FIXUP_HP_530, .name = "hp-530" },
928
	{}
929
930
};

931
932
933
934
935
936
937
938
939
940
941
942
static const struct snd_pci_quirk cxt5047_fixups[] = {
	/* HP laptops have really bad sound over 0 dB on NID 0x10.
	 */
	SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP_5047),
	{}
};

static const struct hda_model_fixup cxt5047_fixup_models[] = {
	{ .id = CXT_FIXUP_CAP_MIX_AMP_5047, .name = "cap-mix-amp" },
	{}
};

943
static const struct snd_pci_quirk cxt5051_fixups[] = {
944
	SND_PCI_QUIRK(0x103c, 0x360b, "Compaq CQ60", CXT_PINCFG_COMPAQ_CQ60),
945
946
947
948
	SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200),
	{}
};

949
950
951
952
953
static const struct hda_model_fixup cxt5051_fixup_models[] = {
	{ .id = CXT_PINCFG_LENOVO_X200, .name = "lenovo-x200" },
	{}
};

954
static const struct snd_pci_quirk cxt5066_fixups[] = {
955
	SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
956
	SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC),
957
	SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC),
958
	SND_PCI_QUIRK(0x103c, 0x8079, "HP EliteBook 840 G3", CXT_FIXUP_HP_DOCK),
959
	SND_PCI_QUIRK(0x103c, 0x807C, "HP EliteBook 820 G3", CXT_FIXUP_HP_DOCK),
960
	SND_PCI_QUIRK(0x103c, 0x80FD, "HP ProBook 640 G2", CXT_FIXUP_HP_DOCK),
961
	SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK),
962
	SND_PCI_QUIRK(0x103c, 0x83d3, "HP ProBook 640 G4", CXT_FIXUP_HP_DOCK),
963
	SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
964
	SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC),
965
	SND_PCI_QUIRK(0x103c, 0x814f, "HP ZBook 15u G3", CXT_FIXUP_MUTE_LED_GPIO),
966
	SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO),
967
968
	SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
	SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE),
969
	SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE),
970
	SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
971
	SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
972
973
974
975
976
	SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
	SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
	SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
	SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410),
	SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
977
	SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
978
	SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
979
	SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD),
980
	SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC),
981
	SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
982
	SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
983
	SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC),
984
	SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
985
	SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI),
986
987
	SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004),
	SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205),
988
989
990
	{}
};

991
992
993
994
995
996
997
static const struct hda_model_fixup cxt5066_fixup_models[] = {
	{ .id = CXT_FIXUP_STEREO_DMIC, .name = "stereo-dmic" },
	{ .id = CXT_FIXUP_GPIO1, .name = "gpio1" },
	{ .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" },
	{ .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" },
	{ .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" },
	{ .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
998
	{ .id = CXT_PINCFG_LEMOTE_A1205, .name = "lemote-a1205" },
999
	{ .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
1000
	{ .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" },
1001
	{ .id = CXT_FIXUP_HP_DOCK, .name = "hp-dock" },
1002
	{ .id = CXT_FIXUP_MUTE_LED_GPIO, .name = "mute-led-gpio" },
1003
	{ .id = CXT_FIXUP_HP_MIC_NO_PRESENCE, .name = "hp-mic-fix" },
1004
1005
1006
	{}
};

1007
1008
1009
1010
1011
/* add "fake" mute amp-caps to DACs on cx5051 so that mixer mute switches
 * can be created (bko#42825)
 */
static void add_cx5051_fake_mutes(struct hda_codec *codec)
{
1012
	struct conexant_spec *spec = codec->spec;
1013
1014
1015
1016
1017
1018
1019
1020
1021
	static hda_nid_t out_nids[] = {
		0x10, 0x11, 0
	};
	hda_nid_t *p;

	for (p = out_nids; *p; p++)
		snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT,
					  AC_AMPCAP_MIN_MUTE |
					  query_amp_caps(codec, *p, HDA_OUTPUT));
1022
	spec->gen.dac_min_mute = true;
1023
1024
}

1025
1026
1027
1028
1029
static int patch_conexant_auto(struct hda_codec *codec)
{
	struct conexant_spec *spec;
	int err;

1030
	codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name);
1031

1032
1033
1034
	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
	if (!spec)
		return -ENOMEM;
1035
	snd_hda_gen_spec_init(&spec->gen);
1036
	codec->spec = spec;
1037
	codec->patch_ops = cx_auto_patch_ops;
1038

1039
1040
	cx_auto_parse_beep(codec);
	cx_auto_parse_eapd(codec);
1041
1042
	spec->gen.own_eapd_ctl = 1;
	if (spec->dynamic_eapd)
1043
		spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook;
1044

1045
	switch (codec->core.vendor_id) {
1046
	case 0x14f15045:
1047
		codec->single_adc_amp = 1;
1048
		spec->gen.mixer_nid = 0x17;
1049
		spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
1050
1051
		snd_hda_pick_fixup(codec, cxt5045_fixup_models,
				   cxt5045_fixups, cxt_fixups);
1052
		break;
1053
1054
1055
	case 0x14f15047:
		codec->pin_amp_workaround = 1;
		spec->gen.mixer_nid = 0x19;
1056
		spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
1057
1058
		snd_hda_pick_fixup(codec, cxt5047_fixup_models,
				   cxt5047_fixups, cxt_fixups);
1059
		break;
1060
1061
	case 0x14f15051:
		add_cx5051_fake_mutes(codec);
1062
		codec->pin_amp_workaround = 1;
1063
1064
		snd_hda_pick_fixup(codec, cxt5051_fixup_models,
				   cxt5051_fixups, cxt_fixups);
1065
		break;
1066
1067
1068
	case 0x14f150f2:
		codec->power_save_node = 1;
		/* Fall through */
1069
1070
	default:
		codec->pin_amp_workaround = 1;
1071
1072
		snd_hda_pick_fixup(codec, cxt5066_fixup_models,
				   cxt5066_fixups, cxt_fixups);
1073
		break;
1074
1075
	}

1076
1077
1078
1079
1080
	/* Show mute-led control only on HP laptops
	 * This is a sort of white-list: on HP laptops, EAPD corresponds
	 * only to the mute-LED without actualy amp function.  Meanwhile,
	 * others may use EAPD really as an amp switch, so it might be
	 * not good to expose it blindly.
1081
	 */
1082
	switch (codec->core.subsystem_id >> 16) {
1083
	case 0x103c:
1084
		spec->gen.vmaster_mute_enum = 1;
1085
1086
		break;
	}
1087

1088
1089
	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);

1090
1091
	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
				       spec->parse_flags);
1092
	if (err < 0)
1093
1094
1095
1096
1097
1098
		goto error;

	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
	if (err < 0)
		goto error;

1099
1100
1101
1102
	/* Some laptops with Conexant chips show stalls in S3 resume,
	 * which falls into the single-cmd mode.
	 * Better to make reset, then.
	 */
1103
	if (!codec->bus->core.sync_write) {
1104
		codec_info(codec,
1105
			   "Enable sync_write for stable communication\n");
1106
		codec->bus->core.sync_write = 1;
1107
1108
1109
		codec->bus->allow_bus_reset = 1;
	}

1110
1111
	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);

1112
	return 0;
1113
1114

 error:
1115
	cx_auto_free(codec);
1116
	return err;
1117
1118
}

1119
1120
1121
/*
 */

1122
static const struct hda_device_id snd_hda_id_conexant[] = {
1123
	HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto),
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
	HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto),
1141
	HDA_CODEC_ENTRY(0x14f150f1, "CX21722", patch_conexant_auto),
1142
	HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto),
1143
	HDA_CODEC_ENTRY(0x14f150f3, "CX21724", patch_conexant_auto),
1144
1145
1146
1147
1148
1149
1150
1151
	HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto),
	HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto),
1152
1153
	{} /* terminator */
};
1154
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant);
1155
1156
1157
1158

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");

1159
static struct hda_codec_driver conexant_driver = {
1160
	.id = snd_hda_id_conexant,
1161
1162
};

1163
module_hda_codec_driver(conexant_driver);