📄 patch_analog.c
字号:
{ static hda_nid_t idx_to_dac[8] = { /* A B C D E F G H */ 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a }; static hda_nid_t idx_to_dac_rev2[8] = { /* A B C D E F G H */ 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06 }; if (codec->revision_id == AD1988A_REV2) return idx_to_dac_rev2[idx]; else return idx_to_dac[idx];}static hda_nid_t ad1988_boost_nids[8] = { 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0};static int ad1988_pin_idx(hda_nid_t nid){ static hda_nid_t ad1988_io_pins[8] = { 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25 }; int i; for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++) if (ad1988_io_pins[i] == nid) return i; return 0; /* should be -1 */}static int ad1988_pin_to_loopback_idx(hda_nid_t nid){ static int loopback_idx[8] = { 2, 0, 1, 3, 4, 5, 1, 4 }; switch (nid) { case AD1988_PIN_CD_NID: return 6; default: return loopback_idx[ad1988_pin_idx(nid)]; }}static int ad1988_pin_to_adc_idx(hda_nid_t nid){ static int adc_idx[8] = { 0, 1, 2, 8, 4, 3, 6, 7 }; switch (nid) { case AD1988_PIN_CD_NID: return 5; default: return adc_idx[ad1988_pin_idx(nid)]; }}/* fill in the dac_nids table from the parsed pin configuration */static int ad1988_auto_fill_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg){ struct ad198x_spec *spec = codec->spec; int i, idx; spec->multiout.dac_nids = spec->private_dac_nids; /* check the pins hardwired to audio widget */ for (i = 0; i < cfg->line_outs; i++) { idx = ad1988_pin_idx(cfg->line_out_pins[i]); spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx); } spec->multiout.num_dacs = cfg->line_outs; return 0;}/* add playback controls from the parsed DAC table */static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec, const struct auto_pin_cfg *cfg){ char name[32]; static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; hda_nid_t nid; int i, err; for (i = 0; i < cfg->line_outs; i++) { hda_nid_t dac = spec->multiout.dac_nids[i]; if (! dac) continue; nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])]; if (i == 2) { /* Center/LFE */ err = add_control(spec, AD_CTL_WIDGET_VOL, "Center Playback Volume", HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT)); if (err < 0) return err; err = add_control(spec, AD_CTL_WIDGET_VOL, "LFE Playback Volume", HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT)); if (err < 0) return err; err = add_control(spec, AD_CTL_BIND_MUTE, "Center Playback Switch", HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT)); if (err < 0) return err; err = add_control(spec, AD_CTL_BIND_MUTE, "LFE Playback Switch", HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT)); if (err < 0) return err; } else { sprintf(name, "%s Playback Volume", chname[i]); err = add_control(spec, AD_CTL_WIDGET_VOL, name, HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = add_control(spec, AD_CTL_BIND_MUTE, name, HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); if (err < 0) return err; } } return 0;}/* add playback controls for speaker and HP outputs */static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, const char *pfx){ struct ad198x_spec *spec = codec->spec; hda_nid_t nid; int idx, err; char name[32]; if (! pin) return 0; idx = ad1988_pin_idx(pin); nid = ad1988_idx_to_dac(codec, idx); if (! spec->multiout.dac_nids[0]) { /* use this as the primary output */ spec->multiout.dac_nids[0] = nid; if (! spec->multiout.num_dacs) spec->multiout.num_dacs = 1; } else /* specify the DAC as the extra output */ spec->multiout.hp_nid = nid; /* control HP volume/switch on the output mixer amp */ sprintf(name, "%s Playback Volume", pfx); if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name, HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0) return err; nid = ad1988_mixer_nids[idx]; sprintf(name, "%s Playback Switch", pfx); if ((err = add_control(spec, AD_CTL_BIND_MUTE, name, HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0) return err; return 0;}/* create input playback/capture controls for the given pin */static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin, const char *ctlname, int boost){ char name[32]; int err, idx; sprintf(name, "%s Playback Volume", ctlname); idx = ad1988_pin_to_loopback_idx(pin); if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name, HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0) return err; sprintf(name, "%s Playback Switch", ctlname); if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name, HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0) return err; if (boost) { hda_nid_t bnid; idx = ad1988_pin_idx(pin); bnid = ad1988_boost_nids[idx]; if (bnid) { sprintf(name, "%s Boost", ctlname); return add_control(spec, AD_CTL_WIDGET_VOL, name, HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT)); } } return 0;}/* create playback/capture controls for input pins */static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec, const struct auto_pin_cfg *cfg){ struct hda_input_mux *imux = &spec->private_imux; int i, err; for (i = 0; i < AUTO_PIN_LAST; i++) { err = new_analog_input(spec, cfg->input_pins[i], auto_pin_cfg_labels[i], i <= AUTO_PIN_FRONT_MIC); if (err < 0) return err; imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]); imux->num_items++; } imux->items[imux->num_items].label = "Mix"; imux->items[imux->num_items].index = 9; imux->num_items++; if ((err = add_control(spec, AD_CTL_WIDGET_VOL, "Analog Mix Playback Volume", HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0) return err; if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, "Analog Mix Playback Switch", HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0) return err; return 0;}static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx){ /* set as output */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); switch (nid) { case 0x11: /* port-A - DAC 04 */ snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01); break; case 0x14: /* port-B - DAC 06 */ snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02); break; case 0x15: /* port-C - DAC 05 */ snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00); break; case 0x17: /* port-E - DAC 0a */ snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01); break; case 0x13: /* mono - DAC 04 */ snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01); break; }}static void ad1988_auto_init_multi_out(struct hda_codec *codec){ struct ad198x_spec *spec = codec->spec; int i; for (i = 0; i < spec->autocfg.line_outs; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); }}static void ad1988_auto_init_extra_out(struct hda_codec *codec){ struct ad198x_spec *spec = codec->spec; hda_nid_t pin; pin = spec->autocfg.speaker_pin; if (pin) /* connect to front */ ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); pin = spec->autocfg.hp_pin; if (pin) /* connect to front */ ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);}static void ad1988_auto_init_analog_input(struct hda_codec *codec){ struct ad198x_spec *spec = codec->spec; int i, idx; for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = spec->autocfg.input_pins[i]; if (! nid) continue; switch (nid) { case 0x15: /* port-C */ snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0); break; case 0x17: /* port-E */ snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0); break; } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN); if (nid != AD1988_PIN_CD_NID) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); idx = ad1988_pin_idx(nid); if (ad1988_boost_nids[idx]) snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); }}/* parse the BIOS configuration and set up the alc_spec *//* return 1 if successful, 0 if the proper config is not found, or a negative error code */static int ad1988_parse_auto_config(struct hda_codec *codec){ struct ad198x_spec *spec = codec->spec; int err; if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0) return err; if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) return err; if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin && ! spec->autocfg.hp_pin) return 0; /* can't find valid BIOS pin config */ if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 || (err = ad1988_auto_create_extra_out(codec, spec->autocfg.speaker_pin, "Speaker")) < 0 || (err = ad1988_auto_create_extra_out(codec, spec->autocfg.speaker_pin, "Headphone")) < 0 || (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0) return err; spec->multiout.max_channels = spec->multiout.num_dacs * 2; if (spec->autocfg.dig_out_pin) spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; if (spec->autocfg.dig_in_pin) spec->dig_in_nid = AD1988_SPDIF_IN; if (spec->kctl_alloc) spec->mixers[spec->num_mixers++] = spec->kctl_alloc; spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs; spec->input_mux = &spec->private_imux; return 1;}/* init callback for auto-configuration model -- overriding the default init */static int ad1988_auto_init(struct hda_codec *codec){ ad198x_init(codec); ad1988_auto_init_multi_out(codec); ad1988_auto_init_extra_out(codec); ad1988_auto_init_analog_input(codec); return 0;}/* */static struct hda_board_config ad1988_cfg_tbl[] = { { .modelname = "6stack", .config = AD1988_6STACK }, { .modelname = "6stack-dig", .config = AD1988_6STACK_DIG }, { .modelname = "3stack", .config = AD1988_3STACK }, { .modelname = "3stack-dig", .config = AD1988_3STACK_DIG }, { .modelname = "laptop", .config = AD1988_LAPTOP }, { .modelname = "laptop-dig", .config = AD1988_LAPTOP_DIG }, { .modelname = "auto", .config = AD1988_AUTO }, {}};static int patch_ad1988(struct hda_codec *codec){ struct ad198x_spec *spec; int board_config; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; init_MUTEX(&spec->amp_mutex); codec->spec = spec; if (codec->revision_id == AD1988A_REV2) snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n"); board_config = snd_hda_check_board_config(codec, ad1988_cfg_tbl); if (board_config < 0 || board_config >= AD1988_MODEL_LAST) { printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n"); board_config = AD1988_AUTO; } if (board_config == AD1988_AUTO) { /* automatic parse from the BIOS config */ int err = ad1988_parse_auto_config(codec); if (err < 0) { ad198x_free(codec); return err; } else if (! err) { printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n"); board_config = AD1988_6STACK; } } switch (board_config) { case AD1988_6STACK: case AD1988_6STACK_DIG: spec->multiout.max_channels = 8; spec->multiout.num_dacs = 4; if (codec->revision_id == AD1988A_REV2) spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2; else spec->multiout.dac_nids = ad1988_6stack_
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -