#include "hda_codec.h"
#include "hda_local.h"
#include "hda_beep.h"
+#include "hda_jack.h"
/* unsol event tags */
#define ALC_FRONT_EVENT 0x01
unsigned int single_input_src:1;
unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */
unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
+ unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
+ unsigned int use_jack_tbl:1; /* 1 for model=auto */
/* auto-mute control */
int automute_mode;
spec->capsrc_nids[idx] : spec->adc_nids[idx];
}
+static void call_update_outputs(struct hda_codec *codec);
+
/* select the given imux item; either unmute exclusively or select the route */
static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
unsigned int idx, bool force)
return 0;
spec->cur_mux[adc_idx] = idx;
+ /* for shared I/O, change the pin-control accordingly */
+ if (spec->shared_mic_hp) {
+ /* NOTE: this assumes that there are only two inputs, the
+ * first is the real internal mic and the second is HP jack.
+ */
+ snd_hda_codec_write(codec, spec->autocfg.inputs[1].pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ spec->cur_mux[adc_idx] ?
+ PIN_VREF80 : PIN_HP);
+ spec->automute_speaker = !spec->cur_mux[adc_idx];
+ call_update_outputs(codec);
+ }
+
if (spec->dyn_adc_switch) {
alc_dyn_adc_pcm_resetup(codec, idx);
adc_idx = spec->dyn_adc_idx[idx];
alc_fix_pll(codec);
}
-/*
- * Jack-reporting via input-jack layer
- */
-
-/* initialization of jacks; currently checks only a few known pins */
-static int alc_init_jacks(struct hda_codec *codec)
-{
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- struct alc_spec *spec = codec->spec;
- int err;
- unsigned int hp_nid = spec->autocfg.hp_pins[0];
- unsigned int mic_nid = spec->ext_mic_pin;
- unsigned int dock_nid = spec->dock_mic_pin;
-
- if (hp_nid) {
- err = snd_hda_input_jack_add(codec, hp_nid,
- SND_JACK_HEADPHONE, NULL);
- if (err < 0)
- return err;
- snd_hda_input_jack_report(codec, hp_nid);
- }
-
- if (mic_nid) {
- err = snd_hda_input_jack_add(codec, mic_nid,
- SND_JACK_MICROPHONE, NULL);
- if (err < 0)
- return err;
- snd_hda_input_jack_report(codec, mic_nid);
- }
- if (dock_nid) {
- err = snd_hda_input_jack_add(codec, dock_nid,
- SND_JACK_MICROPHONE, NULL);
- if (err < 0)
- return err;
- snd_hda_input_jack_report(codec, dock_nid);
- }
-#endif /* CONFIG_SND_HDA_INPUT_JACK */
- return 0;
-}
-
/*
* Jack detections for HP auto-mute and mic-switch
*/
hda_nid_t nid = pins[i];
if (!nid)
break;
- snd_hda_input_jack_report(codec, nid);
present |= snd_hda_jack_detect(codec, nid);
}
return present;
* in general, HP pins/amps control should be enabled in all cases,
* but currently set only for master_mute, just to be safe
*/
- do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+ if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
spec->autocfg.hp_pins, spec->master_mute, true);
if (!spec->automute_speaker)
alc_mux_select(codec, 0, spec->dock_mic_idx, false);
else
alc_mux_select(codec, 0, spec->int_mic_idx, false);
-
- snd_hda_input_jack_report(codec, pins[spec->ext_mic_idx]);
- if (spec->dock_mic_idx >= 0)
- snd_hda_input_jack_report(codec, pins[spec->dock_mic_idx]);
}
/* unsolicited event for HP jack sensing */
static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
{
+ struct alc_spec *spec = codec->spec;
if (codec->vendor_id == 0x10ec0880)
res >>= 28;
else
res >>= 26;
+ if (spec->use_jack_tbl)
+ res = snd_hda_jack_get_action(codec, res);
switch (res) {
case ALC_HP_EVENT:
alc_hp_automute(codec);
alc_mic_automute(codec);
break;
}
+ snd_hda_jack_report_sync(codec);
}
/* call init functions of standard auto-mute helpers */
continue;
snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
nid);
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | ALC_HP_EVENT);
+ snd_hda_jack_detect_enable(codec, nid, ALC_HP_EVENT);
spec->detect_hp = 1;
}
continue;
snd_printdd("realtek: Enable Line-Out "
"auto-muting on NID 0x%x\n", nid);
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | ALC_FRONT_EVENT);
+ snd_hda_jack_detect_enable(codec, nid,
+ ALC_FRONT_EVENT);
spec->detect_lo = 1;
}
spec->automute_lo_possible = spec->detect_hp;
return false; /* no corresponding imux */
}
- snd_hda_codec_write_cache(codec, spec->ext_mic_pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | ALC_MIC_EVENT);
+ snd_hda_jack_detect_enable(codec, spec->ext_mic_pin, ALC_MIC_EVENT);
if (spec->dock_mic_pin)
- snd_hda_codec_write_cache(codec, spec->dock_mic_pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | ALC_MIC_EVENT);
+ snd_hda_jack_detect_enable(codec, spec->dock_mic_pin,
+ ALC_MIC_EVENT);
spec->auto_mic_valid_imux = 1;
spec->auto_mic = 1;
hda_nid_t fixed, ext, dock;
int i;
+ if (spec->shared_mic_hp)
+ return; /* no auto-mic for the shared I/O */
+
spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1;
fixed = ext = dock = 0;
const struct alc_fixup *fixlist)
{
struct alc_spec *spec = codec->spec;
+ const struct snd_pci_quirk *q;
int id = -1;
const char *name = NULL;
}
}
if (id < 0) {
- quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
- if (quirk) {
- id = quirk->value;
+ q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
+ if (q) {
+ id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ name = q->name;
+#endif
+ }
+ }
+ if (id < 0) {
+ for (q = quirk; q->subvendor; q++) {
+ unsigned int vendorid =
+ q->subdevice | (q->subvendor << 16);
+ if (vendorid == codec->subsystem_id) {
+ id = q->value;
#ifdef CONFIG_SND_DEBUG_VERBOSE
- name = quirk->name;
+ name = q->name;
#endif
+ break;
+ }
}
}
alc_free_kctls(codec); /* no longer needed */
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
return 0;
}
alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
+ snd_hda_jack_report_sync(codec);
+
hda_call_check_power_status(codec, 0x01);
return 0;
}
return;
alc_shutup(codec);
- snd_hda_input_jack_free(codec);
alc_free_kctls(codec);
alc_free_bind_ctls(codec);
kfree(spec);
int max_nums = ARRAY_SIZE(spec->private_adc_nids);
int i, nums = 0;
+ if (spec->shared_mic_hp)
+ max_nums = 1; /* no multi streams with the shared HP/mic */
+
nid = codec->start_nid;
for (i = 0; i < codec->num_nodes; i++, nid++) {
hda_nid_t src;
continue;
label = hda_get_autocfg_input_label(codec, cfg, i);
+ if (spec->shared_mic_hp && !strcmp(label, "Misc"))
+ label = "Headphone Mic";
if (prev_label && !strcmp(label, prev_label))
type_idx++;
else
return 0;
}
+/* create a shared input with the headphone out */
+static int alc_auto_create_shared_input(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int defcfg;
+ hda_nid_t nid;
+
+ /* only one internal input pin? */
+ if (cfg->num_inputs != 1)
+ return 0;
+ defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
+ if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ return 0;
+
+ if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */
+ else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT)
+ nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */
+ else
+ return 0; /* both not available */
+
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN))
+ return 0; /* no input */
+
+ cfg->inputs[1].pin = nid;
+ cfg->inputs[1].type = AUTO_PIN_MIC;
+ cfg->num_inputs = 2;
+ spec->shared_mic_hp = 1;
+ snd_printdd("realtek: Enable shared I/O jack on NID 0x%x\n", nid);
+ return 0;
+}
+
static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
unsigned int pin_type)
{
if (!nid)
continue;
if (found_in_nid_list(nid, spec->multiout.dac_nids,
- spec->multiout.num_dacs))
+ ARRAY_SIZE(spec->private_dac_nids)))
continue;
if (found_in_nid_list(nid, spec->multiout.hp_out_nid,
ARRAY_SIZE(spec->multiout.hp_out_nid)))
return 0;
}
+/* check whether the DAC is reachable from the pin */
+static bool alc_auto_is_dac_reachable(struct hda_codec *codec,
+ hda_nid_t pin, hda_nid_t dac)
+{
+ hda_nid_t srcs[5];
+ int i, num;
+
+ pin = alc_go_down_to_selector(codec, pin);
+ num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
+ for (i = 0; i < num; i++) {
+ hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
+ if (nid == dac)
+ return true;
+ }
+ return false;
+}
+
static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
{
hda_nid_t sel = alc_go_down_to_selector(codec, pin);
return 0;
}
+/* return 0 if no possible DAC is found, 1 if one or more found */
static int alc_auto_fill_extra_dacs(struct hda_codec *codec, int num_outs,
const hda_nid_t *pins, hda_nid_t *dacs)
{
if (!dacs[i])
dacs[i] = alc_auto_look_for_dac(codec, pins[i]);
}
- return 0;
+ return 1;
}
static int alc_auto_fill_multi_ios(struct hda_codec *codec,
- unsigned int location);
+ unsigned int location, int offset);
+static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
+ hda_nid_t pin, hda_nid_t dac);
/* fill in the dac_nids table from the parsed pin configuration */
static int alc_auto_fill_dac_nids(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int location, defcfg;
+ int num_pins;
bool redone = false;
int i;
spec->multiout.extra_out_nid[0] = 0;
memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
spec->multiout.dac_nids = spec->private_dac_nids;
+ spec->multi_ios = 0;
/* fill hard-wired DACs first */
if (!redone) {
for (i = 0; i < cfg->line_outs; i++) {
if (spec->private_dac_nids[i])
spec->multiout.num_dacs++;
- else
+ else {
memmove(spec->private_dac_nids + i,
spec->private_dac_nids + i + 1,
sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+ spec->private_dac_nids[cfg->line_outs - 1] = 0;
+ }
}
if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
/* try to fill multi-io first */
- unsigned int location, defcfg;
- int num_pins;
-
defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]);
location = get_defcfg_location(defcfg);
- num_pins = alc_auto_fill_multi_ios(codec, location);
+ num_pins = alc_auto_fill_multi_ios(codec, location, 0);
if (num_pins > 0) {
spec->multi_ios = num_pins;
spec->ext_channel_count = 2;
if (cfg->line_out_type != AUTO_PIN_HP_OUT)
alc_auto_fill_extra_dacs(codec, cfg->hp_outs, cfg->hp_pins,
spec->multiout.hp_out_nid);
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
- alc_auto_fill_extra_dacs(codec, cfg->speaker_outs, cfg->speaker_pins,
- spec->multiout.extra_out_nid);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ int err = alc_auto_fill_extra_dacs(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid);
+ /* if no speaker volume is assigned, try again as the primary
+ * output
+ */
+ if (!err && cfg->speaker_outs > 0 &&
+ cfg->line_out_type == AUTO_PIN_HP_OUT) {
+ cfg->hp_outs = cfg->line_outs;
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->line_outs = cfg->speaker_outs;
+ memcpy(cfg->line_out_pins, cfg->speaker_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = 0;
+ memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+ cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+ redone = false;
+ goto again;
+ }
+ }
+
+ if (!spec->multi_ios &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ cfg->hp_outs) {
+ /* try multi-ios with HP + inputs */
+ defcfg = snd_hda_codec_get_pincfg(codec, cfg->hp_pins[0]);
+ location = get_defcfg_location(defcfg);
+
+ num_pins = alc_auto_fill_multi_ios(codec, location, 1);
+ if (num_pins > 0) {
+ spec->multi_ios = num_pins;
+ spec->ext_channel_count = 2;
+ spec->multiout.num_dacs = num_pins + 1;
+ }
+ }
+ if (cfg->line_out_pins[0])
+ spec->vmaster_nid =
+ alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0],
+ spec->multiout.dac_nids[0]);
return 0;
}
val);
}
-#define alc_auto_add_stereo_vol(codec, pfx, cidx, nid) \
- alc_auto_add_vol_ctl(codec, pfx, cidx, nid, 3)
+static int alc_auto_add_stereo_vol(struct hda_codec *codec,
+ const char *pfx, int cidx,
+ hda_nid_t nid)
+{
+ int chs = 1;
+ if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
+ chs = 3;
+ return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs);
+}
/* create a mute-switch for the given mixer widget;
* if it has multiple sources (e.g. DAC and loopback), create a bind-mute
return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
}
-#define alc_auto_add_stereo_sw(codec, pfx, cidx, nid) \
- alc_auto_add_sw_ctl(codec, pfx, cidx, nid, 3)
+static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx,
+ int cidx, hda_nid_t nid)
+{
+ int chs = 1;
+ if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
+ chs = 3;
+ return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs);
+}
static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
hda_nid_t pin, hda_nid_t dac)
* multi-io helper
*/
static int alc_auto_fill_multi_ios(struct hda_codec *codec,
- unsigned int location)
+ unsigned int location,
+ int offset)
{
struct alc_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t prime_dac = spec->private_dac_nids[0];
- int type, i, num_pins = 0;
+ int type, i, dacs, num_pins = 0;
+ dacs = spec->multiout.num_dacs;
for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = cfg->inputs[i].pin;
- hda_nid_t dac;
+ hda_nid_t dac = 0;
unsigned int defcfg, caps;
if (cfg->inputs[i].type != type)
continue;
caps = snd_hda_query_pin_caps(codec, nid);
if (!(caps & AC_PINCAP_OUT))
continue;
- dac = alc_auto_look_for_dac(codec, nid);
+ if (offset && offset + num_pins < dacs) {
+ dac = spec->private_dac_nids[offset + num_pins];
+ if (!alc_auto_is_dac_reachable(codec, nid, dac))
+ dac = 0;
+ }
+ if (!dac)
+ dac = alc_auto_look_for_dac(codec, nid);
if (!dac)
continue;
spec->multi_io[num_pins].pin = nid;
spec->private_dac_nids[spec->multiout.num_dacs++] = dac;
}
}
- spec->multiout.num_dacs = 1;
+ spec->multiout.num_dacs = dacs;
if (num_pins < 2) {
/* clear up again */
- memset(spec->private_dac_nids, 0,
- sizeof(spec->private_dac_nids));
+ memset(spec->private_dac_nids + dacs, 0,
+ sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - dacs));
spec->private_dac_nids[0] = prime_dac;
return 0;
}
char boost_label[32];
label = hda_get_autocfg_input_label(codec, cfg, i);
+ if (spec->shared_mic_hp && !strcmp(label, "Misc"))
+ label = "Headphone Mic";
if (prev_label && !strcmp(label, prev_label))
type_idx++;
else
static void alc_auto_init_std(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ spec->use_jack_tbl = 1;
alc_auto_init_multi_out(codec);
alc_auto_init_extra_out(codec);
alc_auto_init_analog_input(codec);
if (err < 0)
return err;
err = alc_auto_create_speaker_out(codec);
+ if (err < 0)
+ return err;
+ err = alc_auto_create_shared_input(codec);
if (err < 0)
return err;
err = alc_auto_create_input_ctls(codec);
};
#endif
+/*
+ * ALC880 fix-ups
+ */
+enum {
+ ALC880_FIXUP_GPIO2,
+ ALC880_FIXUP_MEDION_RIM,
+};
+
+static const struct alc_fixup alc880_fixups[] = {
+ [ALC880_FIXUP_GPIO2] = {
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = alc_gpio2_init_verbs,
+ },
+ [ALC880_FIXUP_MEDION_RIM] = {
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO2,
+ },
+};
+
+static const struct snd_pci_quirk alc880_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM),
+ {}
+};
+
+
/*
* board setups
*/
board_config = ALC_MODEL_AUTO;
}
+ if (board_config == ALC_MODEL_AUTO) {
+ alc_pick_fixup(codec, NULL, alc880_fixup_tbl, alc880_fixups);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
+ }
+
if (board_config == ALC_MODEL_AUTO) {
/* automatic parse from the BIOS config */
err = alc880_parse_auto_config(codec);
#endif
}
- if (board_config != ALC_MODEL_AUTO)
+ if (board_config != ALC_MODEL_AUTO) {
+ spec->vmaster_nid = 0x0c;
setup_preset(codec, &alc880_presets[board_config]);
+ }
if (!spec->no_analog && !spec->adc_nids) {
alc_auto_fill_adc_caps(codec);
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
}
- spec->vmaster_nid = 0x0c;
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
codec->patch_ops = alc_patch_ops;
if (board_config == ALC_MODEL_AUTO)
#endif
}
- if (board_config != ALC_MODEL_AUTO)
+ if (board_config != ALC_MODEL_AUTO) {
setup_preset(codec, &alc260_presets[board_config]);
+ spec->vmaster_nid = 0x08;
+ }
if (!spec->no_analog && !spec->adc_nids) {
alc_auto_fill_adc_caps(codec);
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
- spec->vmaster_nid = 0x08;
-
codec->patch_ops = alc_patch_ops;
if (board_config == ALC_MODEL_AUTO)
spec->init_hook = alc_auto_init_std;
* Pin config fixes
*/
enum {
- PINFIX_ABIT_AW9D_MAX,
- PINFIX_LENOVO_Y530,
- PINFIX_PB_M5210,
- PINFIX_ACER_ASPIRE_7736,
- PINFIX_ASUS_W90V,
+ ALC882_FIXUP_ABIT_AW9D_MAX,
+ ALC882_FIXUP_LENOVO_Y530,
+ ALC882_FIXUP_PB_M5210,
+ ALC882_FIXUP_ACER_ASPIRE_7736,
+ ALC882_FIXUP_ASUS_W90V,
+ ALC889_FIXUP_VAIO_TT,
+ ALC888_FIXUP_EEE1601,
+ ALC882_FIXUP_EAPD,
+ ALC883_FIXUP_EAPD,
+ ALC883_FIXUP_ACER_EAPD,
+ ALC882_FIXUP_GPIO3,
+ ALC889_FIXUP_COEF,
+ ALC882_FIXUP_ASUS_W2JC,
+ ALC882_FIXUP_ACER_ASPIRE_4930G,
+ ALC882_FIXUP_ACER_ASPIRE_8930G,
+ ALC882_FIXUP_ASPIRE_8930G_VERBS,
+ ALC885_FIXUP_MACPRO_GPIO,
};
+static void alc889_fixup_coef(struct hda_codec *codec,
+ const struct alc_fixup *fix, int action)
+{
+ if (action != ALC_FIXUP_ACT_INIT)
+ return;
+ alc889_coef_init(codec);
+}
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted)
+{
+ unsigned int gpiostate, gpiomask, gpiodir;
+
+ gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+
+ if (!muted)
+ gpiostate |= (1 << pin);
+ else
+ gpiostate &= ~(1 << pin);
+
+ gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_MASK, 0);
+ gpiomask |= (1 << pin);
+
+ gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DIRECTION, 0);
+ gpiodir |= (1 << pin);
+
+
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_MASK, gpiomask);
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, gpiodir);
+
+ msleep(1);
+
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DATA, gpiostate);
+}
+
+/* set up GPIO at initialization */
+static void alc885_fixup_macpro_gpio(struct hda_codec *codec,
+ const struct alc_fixup *fix, int action)
+{
+ if (action != ALC_FIXUP_ACT_INIT)
+ return;
+ alc882_gpio_mute(codec, 0, 0);
+ alc882_gpio_mute(codec, 1, 0);
+}
+
static const struct alc_fixup alc882_fixups[] = {
- [PINFIX_ABIT_AW9D_MAX] = {
+ [ALC882_FIXUP_ABIT_AW9D_MAX] = {
.type = ALC_FIXUP_PINS,
.v.pins = (const struct alc_pincfg[]) {
{ 0x15, 0x01080104 }, /* side */
{ }
}
},
- [PINFIX_LENOVO_Y530] = {
+ [ALC882_FIXUP_LENOVO_Y530] = {
.type = ALC_FIXUP_PINS,
.v.pins = (const struct alc_pincfg[]) {
{ 0x15, 0x99130112 }, /* rear int speakers */
{ }
}
},
- [PINFIX_PB_M5210] = {
+ [ALC882_FIXUP_PB_M5210] = {
.type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
{}
}
},
- [PINFIX_ACER_ASPIRE_7736] = {
+ [ALC882_FIXUP_ACER_ASPIRE_7736] = {
.type = ALC_FIXUP_SKU,
.v.sku = ALC_FIXUP_SKU_IGNORE,
},
- [PINFIX_ASUS_W90V] = {
+ [ALC882_FIXUP_ASUS_W90V] = {
.type = ALC_FIXUP_PINS,
.v.pins = (const struct alc_pincfg[]) {
{ 0x16, 0x99130110 }, /* fix sequence for CLFE */
{ }
}
},
+ [ALC889_FIXUP_VAIO_TT] = {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
+ { 0x17, 0x90170111 }, /* hidden surround speaker */
+ { }
+ }
+ },
+ [ALC888_FIXUP_EEE1601] = {
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 },
+ { }
+ }
+ },
+ [ALC882_FIXUP_EAPD] = {
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
+ { }
+ }
+ },
+ [ALC883_FIXUP_EAPD] = {
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
+ { }
+ }
+ },
+ [ALC883_FIXUP_ACER_EAPD] = {
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* eanable EAPD on Acer laptops */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ { }
+ }
+ },
+ [ALC882_FIXUP_GPIO3] = {
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = alc_gpio3_init_verbs,
+ },
+ [ALC882_FIXUP_ASUS_W2JC] = {
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = alc_gpio1_init_verbs,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_EAPD,
+ },
+ [ALC889_FIXUP_COEF] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc889_fixup_coef,
+ },
+ [ALC882_FIXUP_ACER_ASPIRE_4930G] = {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
+ { 0x16, 0x99130111 }, /* CLFE speaker */
+ { 0x17, 0x99130112 }, /* surround speaker */
+ { }
+ }
+ },
+ [ALC882_FIXUP_ACER_ASPIRE_8930G] = {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
+ { 0x16, 0x99130111 }, /* CLFE speaker */
+ { 0x1b, 0x99130112 }, /* surround speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC882_FIXUP_ASPIRE_8930G_VERBS,
+ },
+ [ALC882_FIXUP_ASPIRE_8930G_VERBS] = {
+ /* additional init verbs for Acer Aspire 8930G */
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enable all DACs */
+ /* DAC DISABLE/MUTE 1? */
+ /* setting bits 1-5 disables DAC nids 0x02-0x06
+ * apparently. Init=0x38 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x03 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+ /* DAC DISABLE/MUTE 2? */
+ /* some bit here disables the other DACs.
+ * Init=0x4900 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x08 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+ /* DMIC fix
+ * This laptop has a stereo digital microphone.
+ * The mics are only 1cm apart which makes the stereo
+ * useless. However, either the mic or the ALC889
+ * makes the signal become a difference/sum signal
+ * instead of standard stereo, which is annoying.
+ * So instead we flip this bit which makes the
+ * codec replicate the sum signal to both channels,
+ * turning it into a normal mono mic.
+ */
+ /* DMIC_CONTROL? Init value = 0x0001 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ { }
+ }
+ },
+ [ALC885_FIXUP_MACPRO_GPIO] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc885_fixup_macpro_gpio,
+ },
};
static const struct snd_pci_quirk alc882_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210),
- SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", PINFIX_ASUS_W90V),
- SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530),
- SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
- SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736),
+ SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G",
+ ALC882_FIXUP_ACER_ASPIRE_8930G),
+ SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G",
+ ALC882_FIXUP_ACER_ASPIRE_8930G),
+ SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210),
+ SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736),
+ SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V),
+ SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC),
+ SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601),
+ SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
+
+ /* All Apple entries are in codec SSIDs */
+ SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO),
+
+ SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3),
+ SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX),
+ SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530),
+ SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_FIXUP_COEF),
{}
};
goto error;
board_config = alc_board_config(codec, ALC882_MODEL_LAST,
- alc882_models, alc882_cfg_tbl);
-
+ alc882_models, NULL);
if (board_config < 0)
board_config = alc_board_codec_sid_config(codec,
ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl);
err = alc882_parse_auto_config(codec);
if (err < 0)
goto error;
-#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS
- else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC882_3ST_DIG;
- }
-#endif
}
- if (board_config != ALC_MODEL_AUTO)
+ if (board_config != ALC_MODEL_AUTO) {
setup_preset(codec, &alc882_presets[board_config]);
+ spec->vmaster_nid = 0x0c;
+ }
if (!spec->no_analog && !spec->adc_nids) {
alc_auto_fill_adc_caps(codec);
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
- spec->vmaster_nid = 0x0c;
-
codec->patch_ops = alc_patch_ops;
if (board_config == ALC_MODEL_AUTO)
spec->init_hook = alc_auto_init_std;
- alc_init_jacks(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc882_loopbacks;
* Pin config fixes
*/
enum {
- PINFIX_FSC_H270,
- PINFIX_HP_Z200,
+ ALC262_FIXUP_FSC_H270,
+ ALC262_FIXUP_HP_Z200,
+ ALC262_FIXUP_TYAN,
+ ALC262_FIXUP_TOSHIBA_RX1,
+ ALC262_FIXUP_LENOVO_3000,
+ ALC262_FIXUP_BENQ,
+ ALC262_FIXUP_BENQ_T31,
};
static const struct alc_fixup alc262_fixups[] = {
- [PINFIX_FSC_H270] = {
+ [ALC262_FIXUP_FSC_H270] = {
.type = ALC_FIXUP_PINS,
.v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ }
}
},
- [PINFIX_HP_Z200] = {
+ [ALC262_FIXUP_HP_Z200] = {
.type = ALC_FIXUP_PINS,
.v.pins = (const struct alc_pincfg[]) {
{ 0x16, 0x99130120 }, /* internal speaker */
{ }
}
},
+ [ALC262_FIXUP_TYAN] = {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
+ { 0x14, 0x1993e1f0 }, /* int AUX */
+ { }
+ }
+ },
+ [ALC262_FIXUP_TOSHIBA_RX1] = {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
+ { 0x14, 0x90170110 }, /* speaker */
+ { 0x15, 0x0421101f }, /* HP */
+ { 0x1a, 0x40f000f0 }, /* N/A */
+ { 0x1b, 0x40f000f0 }, /* N/A */
+ { 0x1e, 0x40f000f0 }, /* N/A */
+ }
+ },
+ [ALC262_FIXUP_LENOVO_3000] = {
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC262_FIXUP_BENQ,
+ },
+ [ALC262_FIXUP_BENQ] = {
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
+ {}
+ }
+ },
+ [ALC262_FIXUP_BENQ_T31] = {
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ {}
+ }
+ },
};
static const struct snd_pci_quirk alc262_fixup_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", PINFIX_HP_Z200),
- SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270),
+ SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200),
+ SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FIXUP_BENQ),
+ SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ),
+ SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN),
+ SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
+ ALC262_FIXUP_TOSHIBA_RX1),
+ SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270),
+ SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000),
+ SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ),
+ SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_FIXUP_BENQ_T31),
{}
};
/*
*/
-#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS
-#include "alc262_quirks.c"
-#endif
-
static int patch_alc262(struct hda_codec *codec)
{
struct alc_spec *spec;
- int board_config;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
alc_fix_pll_init(codec, 0x20, 0x0a, 10);
- board_config = alc_board_config(codec, ALC262_MODEL_LAST,
- alc262_models, alc262_cfg_tbl);
-
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC_MODEL_AUTO;
- }
-
- if (board_config == ALC_MODEL_AUTO) {
- alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- }
-
- if (board_config == ALC_MODEL_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc262_parse_auto_config(codec);
- if (err < 0)
- goto error;
-#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS
- else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC262_BASIC;
- }
-#endif
- }
+ alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- if (board_config != ALC_MODEL_AUTO)
- setup_preset(codec, &alc262_presets[board_config]);
+ /* automatic parse from the BIOS config */
+ err = alc262_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
if (!spec->no_analog && !spec->adc_nids) {
alc_auto_fill_adc_caps(codec);
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
- spec->vmaster_nid = 0x0c;
-
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC_MODEL_AUTO)
- spec->init_hook = alc_auto_init_std;
+ spec->init_hook = alc_auto_init_std;
spec->shutup = alc_eapd_shutup;
- alc_init_jacks(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc262_loopbacks;
if (!spec->no_analog && !spec->cap_mixer)
set_capture_mixer(codec);
- spec->vmaster_nid = 0x02;
-
codec->patch_ops = alc_patch_ops;
spec->init_hook = alc_auto_init_std;
spec->shutup = alc_eapd_shutup;
- alc_init_jacks(codec);
-
return 0;
error:
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
- spec->vmaster_nid = 0x02;
-
codec->patch_ops = alc_patch_ops;
#ifdef CONFIG_PM
codec->patch_ops.resume = alc269_resume;
spec->init_hook = alc_auto_init_std;
spec->shutup = alc269_shutup;
- alc_init_jacks(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc269_loopbacks;
set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
}
- spec->vmaster_nid = 0x03;
-
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
codec->patch_ops = alc_patch_ops;
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
}
- spec->vmaster_nid = 0x02;
-
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
codec->patch_ops = alc_patch_ops;
break;
}
}
- spec->vmaster_nid = 0x02;
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
spec->init_hook = alc_auto_init_std;
spec->shutup = alc_eapd_shutup;
- alc_init_jacks(codec);
-
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc662_loopbacks;
if (!spec->no_analog && !spec->cap_mixer)
set_capture_mixer(codec);
- spec->vmaster_nid = 0x02;
-
codec->patch_ops = alc_patch_ops;
spec->init_hook = alc_auto_init_std;