📄 patch_realtek.c
字号:
if (item_num<alc_pin_mode_min(dir) || item_num>alc_pin_mode_max(dir)) item_num = alc_pin_mode_min(dir); strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]); return 0;}static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ unsigned int i; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char dir = (kcontrol->private_value >> 16) & 0xff; long *valp = ucontrol->value.integer.value; unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); /* Find enumerated value for current pinctl setting */ i = alc_pin_mode_min(dir); while (alc_pin_mode_values[i] != pinctl && i <= alc_pin_mode_max(dir)) i++; *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir); return 0;}static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ signed int change; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char dir = (kcontrol->private_value >> 16) & 0xff; long val = *ucontrol->value.integer.value; unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir)) val = alc_pin_mode_min(dir); change = pinctl != alc_pin_mode_values[val]; if (change) { /* Set pin mode to that requested */ snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, alc_pin_mode_values[val]); /* Also enable the retasking pin's input/output as required * for the requested pin mode. Enum values of 2 or less are * input modes. * * Dynamically switching the input/output buffers probably * reduces noise slightly (particularly on input) so we'll * do it. However, having both input and output buffers * enabled simultaneously doesn't seem to be problematic if * this turns out to be necessary in the future. */ if (val <= 2) { snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, HDA_AMP_MUTE, 0); } else { snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); } } return change;}#define ALC_PIN_MODE(xname, nid, dir) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ .info = alc_pin_mode_info, \ .get = alc_pin_mode_get, \ .put = alc_pin_mode_put, \ .private_value = nid | (dir<<16) }/* A switch control for ALC260 GPIO pins. Multiple GPIOs can be ganged * together using a mask with more than one bit set. This control is * currently used only by the ALC260 test model. At this stage they are not * needed for any "production" models. */#ifdef CONFIG_SND_DEBUG#define alc_gpio_data_info snd_ctl_boolean_mono_infostatic int alc_gpio_data_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long *valp = ucontrol->value.integer.value; unsigned int val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_GPIO_DATA, 0x00); *valp = (val & mask) != 0; return 0;}static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ signed int change; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long val = *ucontrol->value.integer.value; unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_GPIO_DATA, 0x00); /* Set/unset the masked GPIO bit(s) as needed */ change = (val == 0 ? 0 : mask) != (gpio_data & mask); if (val == 0) gpio_data &= ~mask; else gpio_data |= mask; snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_GPIO_DATA, gpio_data); return change;}#define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ .info = alc_gpio_data_info, \ .get = alc_gpio_data_get, \ .put = alc_gpio_data_put, \ .private_value = nid | (mask<<16) }#endif /* CONFIG_SND_DEBUG *//* A switch control to allow the enabling of the digital IO pins on the * ALC260. This is incredibly simplistic; the intention of this control is * to provide something in the test model allowing digital outputs to be * identified if present. If models are found which can utilise these * outputs a more complete mixer control can be devised for those models if * necessary. */#ifdef CONFIG_SND_DEBUG#define alc_spdif_ctrl_info snd_ctl_boolean_mono_infostatic int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long *valp = ucontrol->value.integer.value; unsigned int val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0x00); *valp = (val & mask) != 0; return 0;}static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ signed int change; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long val = *ucontrol->value.integer.value; unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0x00); /* Set/unset the masked control bit(s) as needed */ change = (val == 0 ? 0 : mask) != (ctrl_data & mask); if (val==0) ctrl_data &= ~mask; else ctrl_data |= mask; snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, ctrl_data); return change;}#define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ .info = alc_spdif_ctrl_info, \ .get = alc_spdif_ctrl_get, \ .put = alc_spdif_ctrl_put, \ .private_value = nid | (mask<<16) }#endif /* CONFIG_SND_DEBUG *//* * set up from the preset table */static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *preset){ int i; for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++) spec->mixers[spec->num_mixers++] = preset->mixers[i]; for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; i++) spec->init_verbs[spec->num_init_verbs++] = preset->init_verbs[i]; spec->channel_mode = preset->channel_mode; spec->num_channel_mode = preset->num_channel_mode; spec->need_dac_fix = preset->need_dac_fix; spec->multiout.max_channels = spec->channel_mode[0].channels; spec->multiout.num_dacs = preset->num_dacs; spec->multiout.dac_nids = preset->dac_nids; spec->multiout.dig_out_nid = preset->dig_out_nid; spec->multiout.hp_nid = preset->hp_nid; spec->num_mux_defs = preset->num_mux_defs; if (!spec->num_mux_defs) spec->num_mux_defs = 1; spec->input_mux = preset->input_mux; spec->num_adc_nids = preset->num_adc_nids; spec->adc_nids = preset->adc_nids; spec->dig_in_nid = preset->dig_in_nid; spec->unsol_event = preset->unsol_event; spec->init_hook = preset->init_hook;#ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = preset->loopbacks;#endif}/* Enable GPIO mask and set output */static struct hda_verb alc_gpio1_init_verbs[] = { {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, { }};static struct hda_verb alc_gpio2_init_verbs[] = { {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, { }};static struct hda_verb alc_gpio3_init_verbs[] = { {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03}, {0x01, AC_VERB_SET_GPIO_DATA, 0x03}, { }};static void alc_sku_automute(struct hda_codec *codec){ struct alc_spec *spec = codec->spec; unsigned int mute; unsigned int present; unsigned int hp_nid = spec->autocfg.hp_pins[0]; unsigned int sp_nid = spec->autocfg.speaker_pins[0]; /* need to execute and sync at first */ snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0); present = snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_GET_PIN_SENSE, 0); spec->jack_present = (present & 0x80000000) != 0; if (spec->jack_present) { /* mute internal speaker */ snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); } else { /* unmute internal speaker if necessary */ mute = snd_hda_codec_amp_read(codec, hp_nid, 0, HDA_OUTPUT, 0); snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, mute); }}/* unsolicited event for HP jack sensing */static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res){ if (codec->vendor_id == 0x10ec0880) res >>= 28; else res >>= 26; if (res != ALC880_HP_EVENT) return; alc_sku_automute(codec);}/* 32-bit subsystem ID for BIOS loading in HD Audio codec. * 31 ~ 16 : Manufacture ID * 15 ~ 8 : SKU ID * 7 ~ 0 : Assembly ID * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36 */static void alc_subsystem_id(struct hda_codec *codec, unsigned int porta, unsigned int porte, unsigned int portd){ unsigned int ass, tmp, i; unsigned nid; struct alc_spec *spec = codec->spec; ass = codec->subsystem_id & 0xffff; if ((ass != codec->bus->pci->subsystem_device) && (ass & 1)) goto do_sku; /* * 31~30 : port conetcivity * 29~21 : reserve * 20 : PCBEEP input * 19~16 : Check sum (15:1) * 15~1 : Custom * 0 : override */ nid = 0x1d; if (codec->vendor_id == 0x10ec0260) nid = 0x17; ass = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); if (!(ass & 1) && !(ass & 0x100000)) return; if ((ass >> 30) != 1) /* no physical connection */ return; /* check sum */ tmp = 0; for (i = 1; i < 16; i++) { if ((ass >> i) && 1) tmp++; } if (((ass >> 16) & 0xf) != tmp) return;do_sku: /* * 0 : override * 1 : Swap Jack * 2 : 0 --> Desktop, 1 --> Laptop * 3~5 : External Amplifier control * 7~6 : Reserved */ tmp = (ass & 0x38) >> 3; /* external Amp control */ switch (tmp) { case 1: snd_hda_sequence_write(codec, alc_gpio1_init_verbs); break; case 3: snd_hda_sequence_write(codec, alc_gpio2_init_verbs); break; case 7: snd_hda_sequence_write(codec, alc_gpio3_init_verbs); break; case 5: /* set EAPD output high */ switch (codec->vendor_id) { case 0x10ec0260: snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_EAPD_BTLENABLE, 2); snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_EAPD_BTLENABLE, 2); break; case 0x10ec0262: case 0x10ec0267: case 0x10ec0268: case 0x10ec0269: case 0x10ec0862: case 0x10ec0662: snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_EAPD_BTLENABLE, 2); snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_EAPD_BTLENABLE, 2); break; } switch (codec->vendor_id) { case 0x10ec0260: snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); tmp = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PROC_COEF, 0); snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x2010); break; case 0x10ec0262: case 0x10ec0880: case 0x10ec0882: case 0x10ec0883: case 0x10ec0885: case 0x10ec0888: snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp | 0x2010); break; case 0x10ec0267: case 0x10ec0268: snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp | 0x3000); break; } default: break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -