diff options
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/Makefile | 2 | ||||
-rw-r--r-- | sound/pci/hda/hda_auto_parser.c | 760 | ||||
-rw-r--r-- | sound/pci/hda/hda_auto_parser.h | 160 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 949 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 8 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 26 | ||||
-rw-r--r-- | sound/pci/hda/hda_jack.c | 1 | ||||
-rw-r--r-- | sound/pci/hda/hda_jack.h | 2 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 122 | ||||
-rw-r--r-- | sound/pci/hda/patch_analog.c | 14 | ||||
-rw-r--r-- | sound/pci/hda/patch_ca0110.c | 8 | ||||
-rw-r--r-- | sound/pci/hda/patch_ca0132.c | 9 | ||||
-rw-r--r-- | sound/pci/hda/patch_cirrus.c | 30 | ||||
-rw-r--r-- | sound/pci/hda/patch_cmedia.c | 1 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 186 | ||||
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 4 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 465 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 68 | ||||
-rw-r--r-- | sound/pci/hda/patch_via.c | 31 |
19 files changed, 1538 insertions, 1308 deletions
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index ace157cc3d15..bd4149f1aaf4 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,6 +1,6 @@ snd-hda-intel-objs := hda_intel.o -snd-hda-codec-y := hda_codec.o hda_jack.o +snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c new file mode 100644 index 000000000000..6e9ef3e25093 --- /dev/null +++ b/sound/pci/hda/hda_auto_parser.c @@ -0,0 +1,760 @@ +/* + * BIOS auto-parser helper functions for HD-audio + * + * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de> + * + * 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. + */ + +#include <linux/slab.h> +#include <linux/export.h> +#include <sound/core.h> +#include "hda_codec.h" +#include "hda_local.h" +#include "hda_auto_parser.h" + +#define SFX "hda_codec: " + +/* + * Helper for automatic pin configuration + */ + +static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list) +{ + for (; *list; list++) + if (*list == nid) + return 1; + return 0; +} + + +/* + * Sort an associated group of pins according to their sequence numbers. + */ +static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences, + int num_pins) +{ + int i, j; + short seq; + hda_nid_t nid; + + for (i = 0; i < num_pins; i++) { + for (j = i + 1; j < num_pins; j++) { + if (sequences[i] > sequences[j]) { + seq = sequences[i]; + sequences[i] = sequences[j]; + sequences[j] = seq; + nid = pins[i]; + pins[i] = pins[j]; + pins[j] = nid; + } + } + } +} + + +/* add the found input-pin to the cfg->inputs[] table */ +static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid, + int type) +{ + if (cfg->num_inputs < AUTO_CFG_MAX_INS) { + cfg->inputs[cfg->num_inputs].pin = nid; + cfg->inputs[cfg->num_inputs].type = type; + cfg->num_inputs++; + } +} + +/* sort inputs in the order of AUTO_PIN_* type */ +static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg) +{ + int i, j; + + for (i = 0; i < cfg->num_inputs; i++) { + for (j = i + 1; j < cfg->num_inputs; j++) { + if (cfg->inputs[i].type > cfg->inputs[j].type) { + struct auto_pin_cfg_item tmp; + tmp = cfg->inputs[i]; + cfg->inputs[i] = cfg->inputs[j]; + cfg->inputs[j] = tmp; + } + } + } +} + +/* Reorder the surround channels + * ALSA sequence is front/surr/clfe/side + * HDA sequence is: + * 4-ch: front/surr => OK as it is + * 6-ch: front/clfe/surr + * 8-ch: front/clfe/rear/side|fc + */ +static void reorder_outputs(unsigned int nums, hda_nid_t *pins) +{ + hda_nid_t nid; + + switch (nums) { + case 3: + case 4: + nid = pins[1]; + pins[1] = pins[2]; + pins[2] = nid; + break; + } +} + +/* + * Parse all pin widgets and store the useful pin nids to cfg + * + * The number of line-outs or any primary output is stored in line_outs, + * and the corresponding output pins are assigned to line_out_pins[], + * in the order of front, rear, CLFE, side, ... + * + * If more extra outputs (speaker and headphone) are found, the pins are + * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack + * is detected, one of speaker of HP pins is assigned as the primary + * output, i.e. to line_out_pins[0]. So, line_outs is always positive + * if any analog output exists. + * + * The analog input pins are assigned to inputs array. + * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, + * respectively. + */ +int snd_hda_parse_pin_defcfg(struct hda_codec *codec, + struct auto_pin_cfg *cfg, + const hda_nid_t *ignore_nids, + unsigned int cond_flags) +{ + hda_nid_t nid, end_nid; + short seq, assoc_line_out; + short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)]; + short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)]; + short sequences_hp[ARRAY_SIZE(cfg->hp_pins)]; + int i; + + memset(cfg, 0, sizeof(*cfg)); + + memset(sequences_line_out, 0, sizeof(sequences_line_out)); + memset(sequences_speaker, 0, sizeof(sequences_speaker)); + memset(sequences_hp, 0, sizeof(sequences_hp)); + assoc_line_out = 0; + + codec->ignore_misc_bit = true; + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int wid_caps = get_wcaps(codec, nid); + unsigned int wid_type = get_wcaps_type(wid_caps); + unsigned int def_conf; + short assoc, loc, conn, dev; + + /* read all default configuration for pin complex */ + if (wid_type != AC_WID_PIN) + continue; + /* ignore the given nids (e.g. pc-beep returns error) */ + if (ignore_nids && is_in_nid_list(nid, ignore_nids)) + continue; + + def_conf = snd_hda_codec_get_pincfg(codec, nid); + if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & + AC_DEFCFG_MISC_NO_PRESENCE)) + codec->ignore_misc_bit = false; + conn = get_defcfg_connect(def_conf); + if (conn == AC_JACK_PORT_NONE) + continue; + loc = get_defcfg_location(def_conf); + dev = get_defcfg_device(def_conf); + + /* workaround for buggy BIOS setups */ + if (dev == AC_JACK_LINE_OUT) { + if (conn == AC_JACK_PORT_FIXED) + dev = AC_JACK_SPEAKER; + } + + switch (dev) { + case AC_JACK_LINE_OUT: + seq = get_defcfg_sequence(def_conf); + assoc = get_defcfg_association(def_conf); + + if (!(wid_caps & AC_WCAP_STEREO)) + if (!cfg->mono_out_pin) + cfg->mono_out_pin = nid; + if (!assoc) + continue; + if (!assoc_line_out) + assoc_line_out = assoc; + else if (assoc_line_out != assoc) + continue; + if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) + continue; + cfg->line_out_pins[cfg->line_outs] = nid; + sequences_line_out[cfg->line_outs] = seq; + cfg->line_outs++; + break; + case AC_JACK_SPEAKER: + seq = get_defcfg_sequence(def_conf); + assoc = get_defcfg_association(def_conf); + if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) + continue; + cfg->speaker_pins[cfg->speaker_outs] = nid; + sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq; + cfg->speaker_outs++; + break; + case AC_JACK_HP_OUT: + seq = get_defcfg_sequence(def_conf); + assoc = get_defcfg_association(def_conf); + if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) + continue; + cfg->hp_pins[cfg->hp_outs] = nid; + sequences_hp[cfg->hp_outs] = (assoc << 4) | seq; + cfg->hp_outs++; + break; + case AC_JACK_MIC_IN: + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC); + break; + case AC_JACK_LINE_IN: + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN); + break; + case AC_JACK_CD: + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD); + break; + case AC_JACK_AUX: + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX); + break; + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) + continue; + cfg->dig_out_pins[cfg->dig_outs] = nid; + cfg->dig_out_type[cfg->dig_outs] = + (loc == AC_JACK_LOC_HDMI) ? + HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF; + cfg->dig_outs++; + break; + case AC_JACK_SPDIF_IN: + case AC_JACK_DIG_OTHER_IN: + cfg->dig_in_pin = nid; + if (loc == AC_JACK_LOC_HDMI) + cfg->dig_in_type = HDA_PCM_TYPE_HDMI; + else + cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; + break; + } + } + + /* FIX-UP: + * If no line-out is defined but multiple HPs are found, + * some of them might be the real line-outs. + */ + if (!cfg->line_outs && cfg->hp_outs > 1 && + !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) { + int i = 0; + while (i < cfg->hp_outs) { + /* The real HPs should have the sequence 0x0f */ + if ((sequences_hp[i] & 0x0f) == 0x0f) { + i++; + continue; + } + /* Move it to the line-out table */ + cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i]; + sequences_line_out[cfg->line_outs] = sequences_hp[i]; + cfg->line_outs++; + cfg->hp_outs--; + memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1, + sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i)); + memmove(sequences_hp + i, sequences_hp + i + 1, + sizeof(sequences_hp[0]) * (cfg->hp_outs - i)); + } + memset(cfg->hp_pins + cfg->hp_outs, 0, + sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs)); + if (!cfg->hp_outs) + cfg->line_out_type = AUTO_PIN_HP_OUT; + + } + + /* sort by sequence */ + sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out, + cfg->line_outs); + sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker, + cfg->speaker_outs); + sort_pins_by_sequence(cfg->hp_pins, sequences_hp, + cfg->hp_outs); + + /* + * FIX-UP: if no line-outs are detected, try to use speaker or HP pin + * as a primary output + */ + if (!cfg->line_outs && + !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) { + if (cfg->speaker_outs) { + 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; + } else if (cfg->hp_outs) { + cfg->line_outs = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, + sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + } + } + + reorder_outputs(cfg->line_outs, cfg->line_out_pins); + reorder_outputs(cfg->hp_outs, cfg->hp_pins); + reorder_outputs(cfg->speaker_outs, cfg->speaker_pins); + + sort_autocfg_input_pins(cfg); + + /* + * debug prints of the parsed results + */ + snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", + cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1], + cfg->line_out_pins[2], cfg->line_out_pins[3], + cfg->line_out_pins[4], + cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" : + (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ? + "speaker" : "line")); + snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", + cfg->speaker_outs, cfg->speaker_pins[0], + cfg->speaker_pins[1], cfg->speaker_pins[2], + cfg->speaker_pins[3], cfg->speaker_pins[4]); + snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", + cfg->hp_outs, cfg->hp_pins[0], + cfg->hp_pins[1], cfg->hp_pins[2], + cfg->hp_pins[3], cfg->hp_pins[4]); + snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin); + if (cfg->dig_outs) + snd_printd(" dig-out=0x%x/0x%x\n", + cfg->dig_out_pins[0], cfg->dig_out_pins[1]); + snd_printd(" inputs:"); + for (i = 0; i < cfg->num_inputs; i++) { + snd_printd(" %s=0x%x", + hda_get_autocfg_input_label(codec, cfg, i), + cfg->inputs[i].pin); + } + snd_printd("\n"); + if (cfg->dig_in_pin) + snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin); + + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg); + +int snd_hda_get_input_pin_attr(unsigned int def_conf) +{ + unsigned int loc = get_defcfg_location(def_conf); + unsigned int conn = get_defcfg_connect(def_conf); + if (conn == AC_JACK_PORT_NONE) + return INPUT_PIN_ATTR_UNUSED; + /* Windows may claim the internal mic to be BOTH, too */ + if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH) + return INPUT_PIN_ATTR_INT; + if ((loc & 0x30) == AC_JACK_LOC_INTERNAL) + return INPUT_PIN_ATTR_INT; + if ((loc & 0x30) == AC_JACK_LOC_SEPARATE) + return INPUT_PIN_ATTR_DOCK; + if (loc == AC_JACK_LOC_REAR) + return INPUT_PIN_ATTR_REAR; + if (loc == AC_JACK_LOC_FRONT) + return INPUT_PIN_ATTR_FRONT; + return INPUT_PIN_ATTR_NORMAL; +} +EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr); + +/** + * hda_get_input_pin_label - Give a label for the given input pin + * + * When check_location is true, the function checks the pin location + * for mic and line-in pins, and set an appropriate prefix like "Front", + * "Rear", "Internal". + */ + +static const char *hda_get_input_pin_label(struct hda_codec *codec, + hda_nid_t pin, bool check_location) +{ + unsigned int def_conf; + static const char * const mic_names[] = { + "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic", + }; + int attr; + + def_conf = snd_hda_codec_get_pincfg(codec, pin); + + switch (get_defcfg_device(def_conf)) { + case AC_JACK_MIC_IN: + if (!check_location) + return "Mic"; + attr = snd_hda_get_input_pin_attr(def_conf); + if (!attr) + return "None"; + return mic_names[attr - 1]; + case AC_JACK_LINE_IN: + if (!check_location) + return "Line"; + attr = snd_hda_get_input_pin_attr(def_conf); + if (!attr) + return "None"; + if (attr == INPUT_PIN_ATTR_DOCK) + return "Dock Line"; + return "Line"; + case AC_JACK_AUX: + return "Aux"; + case AC_JACK_CD: + return "CD"; + case AC_JACK_SPDIF_IN: + return "SPDIF In"; + case AC_JACK_DIG_OTHER_IN: + return "Digital In"; + default: + return "Misc"; + } +} + +/* Check whether the location prefix needs to be added to the label. + * If all mic-jacks are in the same location (e.g. rear panel), we don't + * have to put "Front" prefix to each label. In such a case, returns false. + */ +static int check_mic_location_need(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + int input) +{ + unsigned int defc; + int i, attr, attr2; + + defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin); + attr = snd_hda_get_input_pin_attr(defc); + /* for internal or docking mics, we need locations */ + if (attr <= INPUT_PIN_ATTR_NORMAL) + return 1; + + attr = 0; + for (i = 0; i < cfg->num_inputs; i++) { + defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin); + attr2 = snd_hda_get_input_pin_attr(defc); + if (attr2 >= INPUT_PIN_ATTR_NORMAL) { + if (attr && attr != attr2) + return 1; /* different locations found */ + attr = attr2; + } + } + return 0; +} + +/** + * hda_get_autocfg_input_label - Get a label for the given input + * + * Get a label for the given input pin defined by the autocfg item. + * Unlike hda_get_input_pin_label(), this function checks all inputs + * defined in autocfg and avoids the redundant mic/line prefix as much as + * possible. + */ +const char *hda_get_autocfg_input_label(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + int input) +{ + int type = cfg->inputs[input].type; + int has_multiple_pins = 0; + + if ((input > 0 && cfg->inputs[input - 1].type == type) || + (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type)) + has_multiple_pins = 1; + if (has_multiple_pins && type == AUTO_PIN_MIC) + has_multiple_pins &= check_mic_location_need(codec, cfg, input); + return hda_get_input_pin_label(codec, cfg->inputs[input].pin, + has_multiple_pins); +} +EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label); + +/* return the position of NID in the list, or -1 if not found */ +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} + +/* get a unique suffix or an index number */ +static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, + int num_pins, int *indexp) +{ + static const char * const channel_sfx[] = { + " Front", " Surround", " CLFE", " Side" + }; + int i; + + i = find_idx_in_nid_list(nid, pins, num_pins); + if (i < 0) + return NULL; + if (num_pins == 1) + return ""; + if (num_pins > ARRAY_SIZE(channel_sfx)) { + if (indexp) + *indexp = i; + return ""; + } + return channel_sfx[i]; +} + +static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg, + const char *name, char *label, int maxlen, + int *indexp) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + int attr = snd_hda_get_input_pin_attr(def_conf); + const char *pfx = "", *sfx = ""; + + /* handle as a speaker if it's a fixed line-out */ + if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT) + name = "Speaker"; + /* check the location */ + switch (attr) { + case INPUT_PIN_ATTR_DOCK: + pfx = "Dock "; + break; + case INPUT_PIN_ATTR_FRONT: + pfx = "Front "; + break; + } + if (cfg) { + /* try to give a unique suffix if needed */ + sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, + indexp); + if (!sfx) + sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs, + indexp); + if (!sfx) { + /* don't add channel suffix for Headphone controls */ + int idx = find_idx_in_nid_list(nid, cfg->hp_pins, + cfg->hp_outs); + if (idx >= 0) + *indexp = idx; + sfx = ""; + } + } + snprintf(label, maxlen, "%s%s%s", pfx, name, sfx); + return 1; +} + +/** + * snd_hda_get_pin_label - Get a label for the given I/O pin + * + * Get a label for the given pin. This function works for both input and + * output pins. When @cfg is given as non-NULL, the function tries to get + * an optimized label using hda_get_autocfg_input_label(). + * + * This function tries to give a unique label string for the pin as much as + * possible. For example, when the multiple line-outs are present, it adds + * the channel suffix like "Front", "Surround", etc (only when @cfg is given). + * If no unique name with a suffix is available and @indexp is non-NULL, the + * index number is stored in the pointer. + */ +int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg, + char *label, int maxlen, int *indexp) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + const char *name = NULL; + int i; + + if (indexp) + *indexp = 0; + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) + return 0; + + switch (get_defcfg_device(def_conf)) { + case AC_JACK_LINE_OUT: + return fill_audio_out_name(codec, nid, cfg, "Line Out", + label, maxlen, indexp); + case AC_JACK_SPEAKER: + return fill_audio_out_name(codec, nid, cfg, "Speaker", + label, maxlen, indexp); + case AC_JACK_HP_OUT: + return fill_audio_out_name(codec, nid, cfg, "Headphone", + label, maxlen, indexp); + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI) + name = "HDMI"; + else + name = "SPDIF"; + if (cfg && indexp) { + i = find_idx_in_nid_list(nid, cfg->dig_out_pins, + cfg->dig_outs); + if (i >= 0) + *indexp = i; + } + break; + default: + if (cfg) { + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].pin != nid) + continue; + name = hda_get_autocfg_input_label(codec, cfg, i); + if (name) + break; + } + } + if (!name) + name = hda_get_input_pin_label(codec, nid, true); + break; + } + if (!name) + return 0; + strlcpy(label, name, maxlen); + return 1; +} +EXPORT_SYMBOL_HDA(snd_hda_get_pin_label); + +int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, + const struct hda_verb *list) +{ + const struct hda_verb **v; + snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8); + v = snd_array_new(&spec->verbs); + if (!v) + return -ENOMEM; + *v = list; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs); + +void snd_hda_gen_apply_verbs(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + for (i = 0; i < spec->verbs.used; i++) { + struct hda_verb **v = snd_array_elem(&spec->verbs, i); + snd_hda_sequence_write(codec, *v); + } +} +EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs); + +void snd_hda_apply_pincfgs(struct hda_codec *codec, + const struct hda_pintbl *cfg) +{ + for (; cfg->nid; cfg++) + snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); +} +EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs); + +void snd_hda_apply_fixup(struct hda_codec *codec, int action) +{ + struct hda_gen_spec *spec = codec->spec; + int id = spec->fixup_id; +#ifdef CONFIG_SND_DEBUG_VERBOSE + const char *modelname = spec->fixup_name; +#endif + int depth = 0; + + if (!spec->fixup_list) + return; + + while (id >= 0) { + const struct hda_fixup *fix = spec->fixup_list + id; + + switch (fix->type) { + case HDA_FIXUP_PINS: + if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins) + break; + snd_printdd(KERN_INFO SFX + "%s: Apply pincfg for %s\n", + codec->chip_name, modelname); + snd_hda_apply_pincfgs(codec, fix->v.pins); + break; + case HDA_FIXUP_VERBS: + if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs) + break; + snd_printdd(KERN_INFO SFX + "%s: Apply fix-verbs for %s\n", + codec->chip_name, modelname); + snd_hda_gen_add_verbs(codec->spec, fix->v.verbs); + break; + case HDA_FIXUP_FUNC: + if (!fix->v.func) + break; + snd_printdd(KERN_INFO SFX + "%s: Apply fix-func for %s\n", + codec->chip_name, modelname); + fix->v.func(codec, fix, action); + break; + default: + snd_printk(KERN_ERR SFX + "%s: Invalid fixup type %d\n", + codec->chip_name, fix->type); + break; + } + if (!fix->chained) + break; + if (++depth > 10) + break; + id = fix->chain_id; + } +} +EXPORT_SYMBOL_HDA(snd_hda_apply_fixup); + +void snd_hda_pick_fixup(struct hda_codec *codec, + const struct hda_model_fixup *models, + const struct snd_pci_quirk *quirk, + const struct hda_fixup *fixlist) +{ + struct hda_gen_spec *spec = codec->spec; + const struct snd_pci_quirk *q; + int id = -1; + const char *name = NULL; + + /* when model=nofixup is given, don't pick up any fixups */ + if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { + spec->fixup_list = NULL; + spec->fixup_id = -1; + return; + } + + if (codec->modelname && models) { + while (models->name) { + if (!strcmp(codec->modelname, models->name)) { + id = models->id; + name = models->name; + break; + } + models++; + } + } + if (id < 0) { + 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 = q->name; +#endif + break; + } + } + } + + spec->fixup_id = id; + if (id >= 0) { + spec->fixup_list = fixlist; + spec->fixup_name = name; + } +} +EXPORT_SYMBOL_HDA(snd_hda_pick_fixup); diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h new file mode 100644 index 000000000000..2a7889dfbd1b --- /dev/null +++ b/sound/pci/hda/hda_auto_parser.h @@ -0,0 +1,160 @@ +/* + * BIOS auto-parser helper functions for HD-audio + * + * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de> + * + * 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. + */ + +#ifndef __SOUND_HDA_AUTO_PARSER_H +#define __SOUND_HDA_AUTO_PARSER_H + +/* + * Helper for automatic pin configuration + */ + +enum { + AUTO_PIN_MIC, + AUTO_PIN_LINE_IN, + AUTO_PIN_CD, + AUTO_PIN_AUX, + AUTO_PIN_LAST +}; + +enum { + AUTO_PIN_LINE_OUT, + AUTO_PIN_SPEAKER_OUT, + AUTO_PIN_HP_OUT +}; + +#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS +#define AUTO_CFG_MAX_INS 8 + +struct auto_pin_cfg_item { + hda_nid_t pin; + int type; +}; + +struct auto_pin_cfg; +const char *hda_get_autocfg_input_label(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + int input); +int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg, + char *label, int maxlen, int *indexp); + +enum { + INPUT_PIN_ATTR_UNUSED, /* pin not connected */ + INPUT_PIN_ATTR_INT, /* internal mic/line-in */ + INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */ + INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */ + INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ + INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ +}; + +int snd_hda_get_input_pin_attr(unsigned int def_conf); + +struct auto_pin_cfg { + int line_outs; + /* sorted in the order of Front/Surr/CLFE/Side */ + hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS]; + int speaker_outs; + hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS]; + int hp_outs; + int line_out_type; /* AUTO_PIN_XXX_OUT */ + hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; + int num_inputs; + struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS]; + int dig_outs; + hda_nid_t dig_out_pins[2]; + hda_nid_t dig_in_pin; + hda_nid_t mono_out_pin; + int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */ + int dig_in_type; /* HDA_PCM_TYPE_XXX */ +}; + +/* bit-flags for snd_hda_parse_pin_def_config() behavior */ +#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */ +#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */ + +int snd_hda_parse_pin_defcfg(struct hda_codec *codec, + struct auto_pin_cfg *cfg, + const hda_nid_t *ignore_nids, + unsigned int cond_flags); + +/* older function */ +#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ + snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0) + +/* + */ + +struct hda_gen_spec { + /* fix-up list */ + int fixup_id; + const struct hda_fixup *fixup_list; + const char *fixup_name; + + /* additional init verbs */ + struct snd_array verbs; +}; + + +/* + * Fix-up pin default configurations and add default verbs + */ + +struct hda_pintbl { + hda_nid_t nid; + u32 val; +}; + +struct hda_model_fixup { + const int id; + const char *name; +}; + +struct hda_fixup { + int type; + bool chained; + int chain_id; + union { + const struct hda_pintbl *pins; + const struct hda_verb *verbs; + void (*func)(struct hda_codec *codec, + const struct hda_fixup *fix, + int action); + } v; +}; + +/* fixup types */ +enum { + HDA_FIXUP_INVALID, + HDA_FIXUP_PINS, + HDA_FIXUP_VERBS, + HDA_FIXUP_FUNC, +}; + +/* fixup action definitions */ +enum { + HDA_FIXUP_ACT_PRE_PROBE, + HDA_FIXUP_ACT_PROBE, + HDA_FIXUP_ACT_INIT, + HDA_FIXUP_ACT_BUILD, +}; + +int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, + const struct hda_verb *list); +void snd_hda_gen_apply_verbs(struct hda_codec *codec); +void snd_hda_apply_pincfgs(struct hda_codec *codec, + const struct hda_pintbl *cfg); +void snd_hda_apply_fixup(struct hda_codec *codec, int action); +void snd_hda_pick_fixup(struct hda_codec *codec, + const struct hda_model_fixup *models, + const struct snd_pci_quirk *quirk, + const struct hda_fixup *fixlist); + +#endif /* __SOUND_HDA_AUTO_PARSER_H */ diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7a8fcc4c15f8..c556fe1c25eb 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1255,6 +1255,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, codec->addr = codec_addr; mutex_init(&codec->spdif_mutex); mutex_init(&codec->control_mutex); + mutex_init(&codec->hash_mutex); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); @@ -1264,15 +1265,9 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); - if (codec->bus->modelname) { - codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); - if (!codec->modelname) { - snd_hda_codec_free(codec); - return -ENODEV; - } - } #ifdef CONFIG_SND_HDA_POWER_SAVE + spin_lock_init(&codec->power_lock); INIT_DELAYED_WORK(&codec->power_work, hda_power_work); /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. * the caller has to power down appropriatley after initialization @@ -1281,6 +1276,14 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, hda_keep_power_on(codec); #endif + if (codec->bus->modelname) { + codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); + if (!codec->modelname) { + snd_hda_codec_free(codec); + return -ENODEV; + } + } + list_add_tail(&codec->list, &bus->codec_list); bus->caddr_tbl[codec_addr] = codec; @@ -1603,6 +1606,60 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key) return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); } +/* overwrite the value with the key in the caps hash */ +static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val) +{ + struct hda_amp_info *info; + + mutex_lock(&codec->hash_mutex); + info = get_alloc_amp_hash(codec, key); + if (!info) { + mutex_unlock(&codec->hash_mutex); + return -EINVAL; + } + info->amp_caps = val; + info->head.val |= INFO_AMP_CAPS; + mutex_unlock(&codec->hash_mutex); + return 0; +} + +/* query the value from the caps hash; if not found, fetch the current + * value from the given function and store in the hash + */ +static unsigned int +query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key, + unsigned int (*func)(struct hda_codec *, hda_nid_t, int)) +{ + struct hda_amp_info *info; + unsigned int val; + + mutex_lock(&codec->hash_mutex); + info = get_alloc_amp_hash(codec, key); + if (!info) { + mutex_unlock(&codec->hash_mutex); + return 0; + } + if (!(info->head.val & INFO_AMP_CAPS)) { + mutex_unlock(&codec->hash_mutex); /* for reentrance */ + val = func(codec, nid, dir); + write_caps_hash(codec, key, val); + } else { + val = info->amp_caps; + mutex_unlock(&codec->hash_mutex); + } + return val; +} + +static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid, + int direction) +{ + if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) + nid = codec->afg; + return snd_hda_param_read(codec, nid, + direction == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); +} + /** * query_amp_caps - query AMP capabilities * @codec: the HD-auio codec @@ -1617,22 +1674,9 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key) */ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) { - struct hda_amp_info *info; - - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0)); - if (!info) - return 0; - if (!(info->head.val & INFO_AMP_CAPS)) { - if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) - nid = codec->afg; - info->amp_caps = snd_hda_param_read(codec, nid, - direction == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : - AC_PAR_AMP_IN_CAP); - if (info->amp_caps) - info->head.val |= INFO_AMP_CAPS; - } - return info->amp_caps; + return query_caps_hash(codec, nid, direction, + HDA_HASH_KEY(nid, direction, 0), + read_amp_cap); } EXPORT_SYMBOL_HDA(query_amp_caps); @@ -1652,34 +1696,12 @@ EXPORT_SYMBOL_HDA(query_amp_caps); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps) { - struct hda_amp_info *info; - - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, dir, 0)); - if (!info) - return -EINVAL; - info->amp_caps = caps; - info->head.val |= INFO_AMP_CAPS; - return 0; + return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps); } EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps); -static unsigned int -query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key, - unsigned int (*func)(struct hda_codec *, hda_nid_t)) -{ - struct hda_amp_info *info; - - info = get_alloc_amp_hash(codec, key); - if (!info) - return 0; - if (!info->head.val) { - info->head.val |= INFO_AMP_CAPS; - info->amp_caps = func(codec, nid); - } - return info->amp_caps; -} - -static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid) +static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid, + int dir) { return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); } @@ -1697,7 +1719,7 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid) */ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) { - return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid), + return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid), read_pin_cap); } EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps); @@ -1715,41 +1737,47 @@ EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps); int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, unsigned int caps) { - struct hda_amp_info *info; - info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid)); - if (!info) - return -ENOMEM; - info->amp_caps = caps; - info->head.val |= INFO_AMP_CAPS; - return 0; + return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps); } EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps); -/* - * read the current volume to info - * if the cache exists, read the cache value. +/* read or sync the hash value with the current value; + * call within hash_mutex */ -static unsigned int get_vol_mute(struct hda_codec *codec, - struct hda_amp_info *info, hda_nid_t nid, - int ch, int direction, int index) +static struct hda_amp_info * +update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int index) { - u32 val, parm; - - if (info->head.val & INFO_AMP_VOL(ch)) - return info->vol[ch]; + struct hda_amp_info *info; + unsigned int parm, val = 0; + bool val_read = false; - parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; - parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; - parm |= index; - val = snd_hda_codec_read(codec, nid, 0, + retry: + info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); + if (!info) + return NULL; + if (!(info->head.val & INFO_AMP_VOL(ch))) { + if (!val_read) { + mutex_unlock(&codec->hash_mutex); + parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; + parm |= direction == HDA_OUTPUT ? + AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; + parm |= index; + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm); - info->vol[ch] = val & 0xff; - info->head.val |= INFO_AMP_VOL(ch); - return info->vol[ch]; + val &= 0xff; + val_read = true; + mutex_lock(&codec->hash_mutex); + goto retry; + } + info->vol[ch] = val; + info->head.val |= INFO_AMP_VOL(ch); + } + return info; } /* - * write the current volume in info to the h/w and update the cache + * write the current volume in info to the h/w */ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, hda_nid_t nid, int ch, int direction, int index, @@ -1766,7 +1794,6 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, else parm |= val; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); - info->vol[ch] = val; } /** @@ -1783,10 +1810,14 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) { struct hda_amp_info *info; - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); - if (!info) - return 0; - return get_vol_mute(codec, info, nid, ch, direction, index); + unsigned int val = 0; + + mutex_lock(&codec->hash_mutex); + info = update_amp_hash(codec, nid, ch, direction, index); + if (info) + val = info->vol[ch]; + mutex_unlock(&codec->hash_mutex); + return val; } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); @@ -1808,15 +1839,23 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, { struct hda_amp_info *info; - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); - if (!info) - return 0; if (snd_BUG_ON(mask & ~0xff)) mask &= 0xff; val &= mask; - val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask; - if (info->vol[ch] == val) + + mutex_lock(&codec->hash_mutex); + info = update_amp_hash(codec, nid, ch, direction, idx); + if (!info) { + mutex_unlock(&codec->hash_mutex); + return 0; + } + val |= info->vol[ch] & ~mask; + if (info->vol[ch] == val) { + mutex_unlock(&codec->hash_mutex); return 0; + } + info->vol[ch] = val; + mutex_unlock(&codec->hash_mutex); put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } @@ -2263,7 +2302,10 @@ int snd_hda_codec_reset(struct hda_codec *codec) /* OK, let it free */ #ifdef CONFIG_SND_HDA_POWER_SAVE - cancel_delayed_work(&codec->power_work); + cancel_delayed_work_sync(&codec->power_work); + codec->power_on = 0; + codec->power_transition = 0; + codec->power_jiffies = jiffies; flush_workqueue(codec->bus->workq); #endif snd_hda_ctls_clear(codec); @@ -2859,12 +2901,15 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + struct hda_spdif_out *spdif; + mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); ucontrol->value.iec958.status[0] = spdif->status & 0xff; ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff; ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff; ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff; + mutex_unlock(&codec->spdif_mutex); return 0; } @@ -2950,12 +2995,14 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - hda_nid_t nid = spdif->nid; + struct hda_spdif_out *spdif; + hda_nid_t nid; unsigned short val; int change; mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); + nid = spdif->nid; spdif->status = ucontrol->value.iec958.status[0] | ((unsigned int)ucontrol->value.iec958.status[1] << 8) | ((unsigned int)ucontrol->value.iec958.status[2] << 16) | @@ -2977,9 +3024,12 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + struct hda_spdif_out *spdif; + mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE; + mutex_unlock(&codec->spdif_mutex); return 0; } @@ -2999,12 +3049,14 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - hda_nid_t nid = spdif->nid; + struct hda_spdif_out *spdif; + hda_nid_t nid; unsigned short val; int change; mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); + nid = spdif->nid; val = spdif->ctls & ~AC_DIG1_ENABLE; if (ucontrol->value.integer.value[0]) val |= AC_DIG1_ENABLE; @@ -3092,6 +3144,9 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls); +/* get the hda_spdif_out entry from the given NID + * call within spdif_mutex lock + */ struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, hda_nid_t nid) { @@ -3108,9 +3163,10 @@ EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid); void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) { - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + struct hda_spdif_out *spdif; mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); spdif->nid = (u16)-1; mutex_unlock(&codec->spdif_mutex); } @@ -3118,10 +3174,11 @@ EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_unassign); void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid) { - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + struct hda_spdif_out *spdif; unsigned short val; mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); if (spdif->nid != nid) { spdif->nid = nid; val = spdif->ctls; @@ -3486,11 +3543,14 @@ static void hda_call_codec_suspend(struct hda_codec *codec) codec->afg ? codec->afg : codec->mfg, AC_PWRST_D3); #ifdef CONFIG_SND_HDA_POWER_SAVE - snd_hda_update_power_acct(codec); cancel_delayed_work(&codec->power_work); + spin_lock(&codec->power_lock); + snd_hda_update_power_acct(codec); + trace_hda_power_down(codec); codec->power_on = 0; codec->power_transition = 0; codec->power_jiffies = jiffies; + spin_unlock(&codec->power_lock); #endif } @@ -3499,6 +3559,10 @@ static void hda_call_codec_suspend(struct hda_codec *codec) */ static void hda_call_codec_resume(struct hda_codec *codec) { + /* set as if powered on for avoiding re-entering the resume + * in the resume / power-save sequence + */ + hda_keep_power_on(codec); hda_set_power_state(codec, codec->afg ? codec->afg : codec->mfg, AC_PWRST_D0); @@ -3514,6 +3578,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); } + snd_hda_power_down(codec); /* flag down before returning */ } #endif /* CONFIG_PM */ @@ -3665,7 +3730,8 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, } EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format); -static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid) +static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid, + int dir) { unsigned int val = 0; if (nid != codec->afg && @@ -3680,11 +3746,12 @@ static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid) static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) { - return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid), + return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid), get_pcm_param); } -static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid) +static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid, + int dir) { unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); if (!streams || streams == -1) @@ -3696,7 +3763,7 @@ static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid) static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) { - return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid), + return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid), get_stream_param); } @@ -3775,11 +3842,13 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, bps = 20; } } +#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */ if (streams & AC_SUPFMT_FLOAT32) { formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; if (!bps) bps = 32; } +#endif if (streams == AC_SUPFMT_AC3) { /* should be exclusive */ /* temporary hack: we have still no proper support @@ -4283,12 +4352,18 @@ static void hda_power_work(struct work_struct *work) container_of(work, struct hda_codec, power_work.work); struct hda_bus *bus = codec->bus; + spin_lock(&codec->power_lock); + if (codec->power_transition > 0) { /* during power-up sequence? */ + spin_unlock(&codec->power_lock); + return; + } if (!codec->power_on || codec->power_count) { codec->power_transition = 0; + spin_unlock(&codec->power_lock); return; } + spin_unlock(&codec->power_lock); - trace_hda_power_down(codec); hda_call_codec_suspend(codec); if (bus->ops.pm_notify) bus->ops.pm_notify(bus); @@ -4296,9 +4371,11 @@ static void hda_power_work(struct work_struct *work) static void hda_keep_power_on(struct hda_codec *codec) { + spin_lock(&codec->power_lock); codec->power_count++; codec->power_on = 1; codec->power_jiffies = jiffies; + spin_unlock(&codec->power_lock); } /* update the power on/off account with the current jiffies */ @@ -4323,19 +4400,31 @@ void snd_hda_power_up(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; + spin_lock(&codec->power_lock); codec->power_count++; - if (codec->power_on || codec->power_transition) + if (codec->power_on || codec->power_transition > 0) { + spin_unlock(&codec->power_lock); return; + } + spin_unlock(&codec->power_lock); + cancel_delayed_work_sync(&codec->power_work); + + spin_lock(&codec->power_lock); trace_hda_power_up(codec); snd_hda_update_power_acct(codec); codec->power_on = 1; codec->power_jiffies = jiffies; + codec->power_transition = 1; /* avoid reentrance */ + spin_unlock(&codec->power_lock); + if (bus->ops.pm_notify) bus->ops.pm_notify(bus); hda_call_codec_resume(codec); - cancel_delayed_work(&codec->power_work); + + spin_lock(&codec->power_lock); codec->power_transition = 0; + spin_unlock(&codec->power_lock); } EXPORT_SYMBOL_HDA(snd_hda_power_up); @@ -4351,14 +4440,18 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up); */ void snd_hda_power_down(struct hda_codec *codec) { + spin_lock(&codec->power_lock); --codec->power_count; - if (!codec->power_on || codec->power_count || codec->power_transition) + if (!codec->power_on || codec->power_count || codec->power_transition) { + spin_unlock(&codec->power_lock); return; + } if (power_save(codec)) { - codec->power_transition = 1; /* avoid reentrance */ + codec->power_transition = -1; /* avoid reentrance */ queue_delayed_work(codec->bus->workq, &codec->power_work, msecs_to_jiffies(power_save(codec) * 1000)); } + spin_unlock(&codec->power_lock); } EXPORT_SYMBOL_HDA(snd_hda_power_down); @@ -4710,11 +4803,11 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, { const hda_nid_t *nids = mout->dac_nids; int chs = substream->runtime->channels; - struct hda_spdif_out *spdif = - snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid); + struct hda_spdif_out *spdif; int i; mutex_lock(&codec->spdif_mutex); + spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid); if (mout->dig_out_nid && mout->share_spdif && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { if (chs == 2 && @@ -4795,601 +4888,58 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup); -/* - * Helper for automatic pin configuration - */ - -static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list) -{ - for (; *list; list++) - if (*list == nid) - return 1; - return 0; -} - - -/* - * Sort an associated group of pins according to their sequence numbers. - */ -static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences, - int num_pins) -{ - int i, j; - short seq; - hda_nid_t nid; - - for (i = 0; i < num_pins; i++) { - for (j = i + 1; j < num_pins; j++) { - if (sequences[i] > sequences[j]) { - seq = sequences[i]; - sequences[i] = sequences[j]; - sequences[j] = seq; - nid = pins[i]; - pins[i] = pins[j]; - pins[j] = nid; - } - } - } -} - - -/* add the found input-pin to the cfg->inputs[] table */ -static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid, - int type) -{ - if (cfg->num_inputs < AUTO_CFG_MAX_INS) { - cfg->inputs[cfg->num_inputs].pin = nid; - cfg->inputs[cfg->num_inputs].type = type; - cfg->num_inputs++; - } -} - -/* sort inputs in the order of AUTO_PIN_* type */ -static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg) -{ - int i, j; - - for (i = 0; i < cfg->num_inputs; i++) { - for (j = i + 1; j < cfg->num_inputs; j++) { - if (cfg->inputs[i].type > cfg->inputs[j].type) { - struct auto_pin_cfg_item tmp; - tmp = cfg->inputs[i]; - cfg->inputs[i] = cfg->inputs[j]; - cfg->inputs[j] = tmp; - } - } - } -} - -/* Reorder the surround channels - * ALSA sequence is front/surr/clfe/side - * HDA sequence is: - * 4-ch: front/surr => OK as it is - * 6-ch: front/clfe/surr - * 8-ch: front/clfe/rear/side|fc - */ -static void reorder_outputs(unsigned int nums, hda_nid_t *pins) -{ - hda_nid_t nid; - - switch (nums) { - case 3: - case 4: - nid = pins[1]; - pins[1] = pins[2]; - pins[2] = nid; - break; - } -} - -/* - * Parse all pin widgets and store the useful pin nids to cfg - * - * The number of line-outs or any primary output is stored in line_outs, - * and the corresponding output pins are assigned to line_out_pins[], - * in the order of front, rear, CLFE, side, ... - * - * If more extra outputs (speaker and headphone) are found, the pins are - * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack - * is detected, one of speaker of HP pins is assigned as the primary - * output, i.e. to line_out_pins[0]. So, line_outs is always positive - * if any analog output exists. - * - * The analog input pins are assigned to inputs array. - * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, - * respectively. - */ -int snd_hda_parse_pin_defcfg(struct hda_codec *codec, - struct auto_pin_cfg *cfg, - const hda_nid_t *ignore_nids, - unsigned int cond_flags) -{ - hda_nid_t nid, end_nid; - short seq, assoc_line_out; - short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)]; - short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)]; - short sequences_hp[ARRAY_SIZE(cfg->hp_pins)]; - int i; - - memset(cfg, 0, sizeof(*cfg)); - - memset(sequences_line_out, 0, sizeof(sequences_line_out)); - memset(sequences_speaker, 0, sizeof(sequences_speaker)); - memset(sequences_hp, 0, sizeof(sequences_hp)); - assoc_line_out = 0; - - codec->ignore_misc_bit = true; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { - unsigned int wid_caps = get_wcaps(codec, nid); - unsigned int wid_type = get_wcaps_type(wid_caps); - unsigned int def_conf; - short assoc, loc, conn, dev; - - /* read all default configuration for pin complex */ - if (wid_type != AC_WID_PIN) - continue; - /* ignore the given nids (e.g. pc-beep returns error) */ - if (ignore_nids && is_in_nid_list(nid, ignore_nids)) - continue; - - def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & - AC_DEFCFG_MISC_NO_PRESENCE)) - codec->ignore_misc_bit = false; - conn = get_defcfg_connect(def_conf); - if (conn == AC_JACK_PORT_NONE) - continue; - loc = get_defcfg_location(def_conf); - dev = get_defcfg_device(def_conf); - - /* workaround for buggy BIOS setups */ - if (dev == AC_JACK_LINE_OUT) { - if (conn == AC_JACK_PORT_FIXED) - dev = AC_JACK_SPEAKER; - } - - switch (dev) { - case AC_JACK_LINE_OUT: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - - if (!(wid_caps & AC_WCAP_STEREO)) - if (!cfg->mono_out_pin) - cfg->mono_out_pin = nid; - if (!assoc) - continue; - if (!assoc_line_out) - assoc_line_out = assoc; - else if (assoc_line_out != assoc) - continue; - if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) - continue; - cfg->line_out_pins[cfg->line_outs] = nid; - sequences_line_out[cfg->line_outs] = seq; - cfg->line_outs++; - break; - case AC_JACK_SPEAKER: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) - continue; - cfg->speaker_pins[cfg->speaker_outs] = nid; - sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq; - cfg->speaker_outs++; - break; - case AC_JACK_HP_OUT: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) - continue; - cfg->hp_pins[cfg->hp_outs] = nid; - sequences_hp[cfg->hp_outs] = (assoc << 4) | seq; - cfg->hp_outs++; - break; - case AC_JACK_MIC_IN: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC); - break; - case AC_JACK_LINE_IN: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN); - break; - case AC_JACK_CD: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD); - break; - case AC_JACK_AUX: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX); - break; - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) - continue; - cfg->dig_out_pins[cfg->dig_outs] = nid; - cfg->dig_out_type[cfg->dig_outs] = - (loc == AC_JACK_LOC_HDMI) ? - HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF; - cfg->dig_outs++; - break; - case AC_JACK_SPDIF_IN: - case AC_JACK_DIG_OTHER_IN: - cfg->dig_in_pin = nid; - if (loc == AC_JACK_LOC_HDMI) - cfg->dig_in_type = HDA_PCM_TYPE_HDMI; - else - cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; - break; - } - } - - /* FIX-UP: - * If no line-out is defined but multiple HPs are found, - * some of them might be the real line-outs. - */ - if (!cfg->line_outs && cfg->hp_outs > 1 && - !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) { - int i = 0; - while (i < cfg->hp_outs) { - /* The real HPs should have the sequence 0x0f */ - if ((sequences_hp[i] & 0x0f) == 0x0f) { - i++; - continue; - } - /* Move it to the line-out table */ - cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i]; - sequences_line_out[cfg->line_outs] = sequences_hp[i]; - cfg->line_outs++; - cfg->hp_outs--; - memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1, - sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i)); - memmove(sequences_hp + i, sequences_hp + i + 1, - sizeof(sequences_hp[0]) * (cfg->hp_outs - i)); - } - memset(cfg->hp_pins + cfg->hp_outs, 0, - sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs)); - if (!cfg->hp_outs) - cfg->line_out_type = AUTO_PIN_HP_OUT; - - } - - /* sort by sequence */ - sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out, - cfg->line_outs); - sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker, - cfg->speaker_outs); - sort_pins_by_sequence(cfg->hp_pins, sequences_hp, - cfg->hp_outs); - - /* - * FIX-UP: if no line-outs are detected, try to use speaker or HP pin - * as a primary output - */ - if (!cfg->line_outs && - !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) { - if (cfg->speaker_outs) { - 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; - } else if (cfg->hp_outs) { - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - } - } - - reorder_outputs(cfg->line_outs, cfg->line_out_pins); - reorder_outputs(cfg->hp_outs, cfg->hp_pins); - reorder_outputs(cfg->speaker_outs, cfg->speaker_pins); - - sort_autocfg_input_pins(cfg); - - /* - * debug prints of the parsed results - */ - snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", - cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[3], - cfg->line_out_pins[4], - cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" : - (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ? - "speaker" : "line")); - snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", - cfg->speaker_outs, cfg->speaker_pins[0], - cfg->speaker_pins[1], cfg->speaker_pins[2], - cfg->speaker_pins[3], cfg->speaker_pins[4]); - snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", - cfg->hp_outs, cfg->hp_pins[0], - cfg->hp_pins[1], cfg->hp_pins[2], - cfg->hp_pins[3], cfg->hp_pins[4]); - snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin); - if (cfg->dig_outs) - snd_printd(" dig-out=0x%x/0x%x\n", - cfg->dig_out_pins[0], cfg->dig_out_pins[1]); - snd_printd(" inputs:"); - for (i = 0; i < cfg->num_inputs; i++) { - snd_printd(" %s=0x%x", - hda_get_autocfg_input_label(codec, cfg, i), - cfg->inputs[i].pin); - } - snd_printd("\n"); - if (cfg->dig_in_pin) - snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin); - - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg); - -int snd_hda_get_input_pin_attr(unsigned int def_conf) -{ - unsigned int loc = get_defcfg_location(def_conf); - unsigned int conn = get_defcfg_connect(def_conf); - if (conn == AC_JACK_PORT_NONE) - return INPUT_PIN_ATTR_UNUSED; - /* Windows may claim the internal mic to be BOTH, too */ - if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH) - return INPUT_PIN_ATTR_INT; - if ((loc & 0x30) == AC_JACK_LOC_INTERNAL) - return INPUT_PIN_ATTR_INT; - if ((loc & 0x30) == AC_JACK_LOC_SEPARATE) - return INPUT_PIN_ATTR_DOCK; - if (loc == AC_JACK_LOC_REAR) - return INPUT_PIN_ATTR_REAR; - if (loc == AC_JACK_LOC_FRONT) - return INPUT_PIN_ATTR_FRONT; - return INPUT_PIN_ATTR_NORMAL; -} -EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr); - /** - * hda_get_input_pin_label - Give a label for the given input pin + * snd_hda_get_default_vref - Get the default (mic) VREF pin bits * - * When check_location is true, the function checks the pin location - * for mic and line-in pins, and set an appropriate prefix like "Front", - * "Rear", "Internal". - */ - -static const char *hda_get_input_pin_label(struct hda_codec *codec, - hda_nid_t pin, bool check_location) -{ - unsigned int def_conf; - static const char * const mic_names[] = { - "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic", - }; - int attr; - - def_conf = snd_hda_codec_get_pincfg(codec, pin); - - switch (get_defcfg_device(def_conf)) { - case AC_JACK_MIC_IN: - if (!check_location) - return "Mic"; - attr = snd_hda_get_input_pin_attr(def_conf); - if (!attr) - return "None"; - return mic_names[attr - 1]; - case AC_JACK_LINE_IN: - if (!check_location) - return "Line"; - attr = snd_hda_get_input_pin_attr(def_conf); - if (!attr) - return "None"; - if (attr == INPUT_PIN_ATTR_DOCK) - return "Dock Line"; - return "Line"; - case AC_JACK_AUX: - return "Aux"; - case AC_JACK_CD: - return "CD"; - case AC_JACK_SPDIF_IN: - return "SPDIF In"; - case AC_JACK_DIG_OTHER_IN: - return "Digital In"; - default: - return "Misc"; - } -} - -/* Check whether the location prefix needs to be added to the label. - * If all mic-jacks are in the same location (e.g. rear panel), we don't - * have to put "Front" prefix to each label. In such a case, returns false. - */ -static int check_mic_location_need(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input) -{ - unsigned int defc; - int i, attr, attr2; - - defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin); - attr = snd_hda_get_input_pin_attr(defc); - /* for internal or docking mics, we need locations */ - if (attr <= INPUT_PIN_ATTR_NORMAL) - return 1; - - attr = 0; - for (i = 0; i < cfg->num_inputs; i++) { - defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin); - attr2 = snd_hda_get_input_pin_attr(defc); - if (attr2 >= INPUT_PIN_ATTR_NORMAL) { - if (attr && attr != attr2) - return 1; /* different locations found */ - attr = attr2; + * Guess the suitable VREF pin bits to be set as the pin-control value. + * Note: the function doesn't set the AC_PINCTL_IN_EN bit. + */ +unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin) +{ + unsigned int pincap; + unsigned int oldval; + oldval = snd_hda_codec_read(codec, pin, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + pincap = snd_hda_query_pin_caps(codec, pin); + pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + /* Exception: if the default pin setup is vref50, we give it priority */ + if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50) + return AC_PINCTL_VREF_80; + else if (pincap & AC_PINCAP_VREF_50) + return AC_PINCTL_VREF_50; + else if (pincap & AC_PINCAP_VREF_100) + return AC_PINCTL_VREF_100; + else if (pincap & AC_PINCAP_VREF_GRD) + return AC_PINCTL_VREF_GRD; + return AC_PINCTL_VREF_HIZ; +} +EXPORT_SYMBOL_HDA(snd_hda_get_default_vref); + +int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, + unsigned int val, bool cached) +{ + if (val) { + unsigned int cap = snd_hda_query_pin_caps(codec, pin); + if (cap && (val & AC_PINCTL_OUT_EN)) { + if (!(cap & AC_PINCAP_OUT)) + val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); + else if ((val & AC_PINCTL_HP_EN) && + !(cap & AC_PINCAP_HP_DRV)) + val &= ~AC_PINCTL_HP_EN; } - } - return 0; -} - -/** - * hda_get_autocfg_input_label - Get a label for the given input - * - * Get a label for the given input pin defined by the autocfg item. - * Unlike hda_get_input_pin_label(), this function checks all inputs - * defined in autocfg and avoids the redundant mic/line prefix as much as - * possible. - */ -const char *hda_get_autocfg_input_label(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input) -{ - int type = cfg->inputs[input].type; - int has_multiple_pins = 0; - - if ((input > 0 && cfg->inputs[input - 1].type == type) || - (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type)) - has_multiple_pins = 1; - if (has_multiple_pins && type == AUTO_PIN_MIC) - has_multiple_pins &= check_mic_location_need(codec, cfg, input); - return hda_get_input_pin_label(codec, cfg->inputs[input].pin, - has_multiple_pins); -} -EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label); - -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} - -/* get a unique suffix or an index number */ -static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, - int num_pins, int *indexp) -{ - static const char * const channel_sfx[] = { - " Front", " Surround", " CLFE", " Side" - }; - int i; - - i = find_idx_in_nid_list(nid, pins, num_pins); - if (i < 0) - return NULL; - if (num_pins == 1) - return ""; - if (num_pins > ARRAY_SIZE(channel_sfx)) { - if (indexp) - *indexp = i; - return ""; - } - return channel_sfx[i]; -} - -static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - const char *name, char *label, int maxlen, - int *indexp) -{ - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int attr = snd_hda_get_input_pin_attr(def_conf); - const char *pfx = "", *sfx = ""; - - /* handle as a speaker if it's a fixed line-out */ - if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT) - name = "Speaker"; - /* check the location */ - switch (attr) { - case INPUT_PIN_ATTR_DOCK: - pfx = "Dock "; - break; - case INPUT_PIN_ATTR_FRONT: - pfx = "Front "; - break; - } - if (cfg) { - /* try to give a unique suffix if needed */ - sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, - indexp); - if (!sfx) - sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs, - indexp); - if (!sfx) { - /* don't add channel suffix for Headphone controls */ - int idx = find_idx_in_nid_list(nid, cfg->hp_pins, - cfg->hp_outs); - if (idx >= 0) - *indexp = idx; - sfx = ""; + if (cap && (val & AC_PINCTL_IN_EN)) { + if (!(cap & AC_PINCAP_IN)) + val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN); } } - snprintf(label, maxlen, "%s%s%s", pfx, name, sfx); - return 1; -} - -/** - * snd_hda_get_pin_label - Get a label for the given I/O pin - * - * Get a label for the given pin. This function works for both input and - * output pins. When @cfg is given as non-NULL, the function tries to get - * an optimized label using hda_get_autocfg_input_label(). - * - * This function tries to give a unique label string for the pin as much as - * possible. For example, when the multiple line-outs are present, it adds - * the channel suffix like "Front", "Surround", etc (only when @cfg is given). - * If no unique name with a suffix is available and @indexp is non-NULL, the - * index number is stored in the pointer. - */ -int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp) -{ - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - const char *name = NULL; - int i; - - if (indexp) - *indexp = 0; - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) - return 0; - - switch (get_defcfg_device(def_conf)) { - case AC_JACK_LINE_OUT: - return fill_audio_out_name(codec, nid, cfg, "Line Out", - label, maxlen, indexp); - case AC_JACK_SPEAKER: - return fill_audio_out_name(codec, nid, cfg, "Speaker", - label, maxlen, indexp); - case AC_JACK_HP_OUT: - return fill_audio_out_name(codec, nid, cfg, "Headphone", - label, maxlen, indexp); - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI) - name = "HDMI"; - else - name = "SPDIF"; - if (cfg && indexp) { - i = find_idx_in_nid_list(nid, cfg->dig_out_pins, - cfg->dig_outs); - if (i >= 0) - *indexp = i; - } - break; - default: - if (cfg) { - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].pin != nid) - continue; - name = hda_get_autocfg_input_label(codec, cfg, i); - if (name) - break; - } - } - if (!name) - name = hda_get_input_pin_label(codec, nid, true); - break; - } - if (!name) - return 0; - strlcpy(label, name, maxlen); - return 1; + if (cached) + return snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, val); + else + return snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, val); } -EXPORT_SYMBOL_HDA(snd_hda_get_pin_label); +EXPORT_SYMBOL_HDA(_snd_hda_set_pin_ctl); /** * snd_hda_add_imux_item - Add an item to input_mux @@ -5444,12 +4994,6 @@ int snd_hda_suspend(struct hda_bus *bus) list_for_each_entry(codec, &bus->codec_list, list) { if (hda_codec_is_power_on(codec)) hda_call_codec_suspend(codec); - else /* forcibly change the power to D3 even if not used */ - hda_set_power_state(codec, - codec->afg ? codec->afg : codec->mfg, - AC_PWRST_D3); - if (codec->patch_ops.post_suspend) - codec->patch_ops.post_suspend(codec); } return 0; } @@ -5469,10 +5013,7 @@ int snd_hda_resume(struct hda_bus *bus) struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { - if (codec->patch_ops.pre_resume) - codec->patch_ops.pre_resume(codec); - if (snd_hda_codec_needs_resume(codec)) - hda_call_codec_resume(codec); + hda_call_codec_resume(codec); } return 0; } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 56b4f74c0b13..29a311b05f2d 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -704,8 +704,6 @@ struct hda_codec_ops { unsigned int power_state); #ifdef CONFIG_PM int (*suspend)(struct hda_codec *codec, pm_message_t state); - int (*post_suspend)(struct hda_codec *codec); - int (*pre_resume)(struct hda_codec *codec); int (*resume)(struct hda_codec *codec); #endif #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -829,6 +827,7 @@ struct hda_codec { struct mutex spdif_mutex; struct mutex control_mutex; + struct mutex hash_mutex; struct snd_array spdif_out; unsigned int spdif_in_enable; /* SPDIF input enable? */ const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ @@ -861,12 +860,13 @@ struct hda_codec { unsigned int no_jack_detect:1; /* Machine has no jack-detection */ #ifdef CONFIG_SND_HDA_POWER_SAVE unsigned int power_on :1; /* current (global) power-state */ - unsigned int power_transition :1; /* power-state in transition */ + int power_transition; /* power-state in transition */ int power_count; /* current (global) power refcount */ struct delayed_work power_work; /* delayed task for powerdown */ unsigned long power_on_acct; unsigned long power_off_acct; unsigned long power_jiffies; + spinlock_t power_lock; #endif /* codec-specific additional proc output */ @@ -1051,12 +1051,10 @@ const char *snd_hda_get_jack_location(u32 cfg); #ifdef CONFIG_SND_HDA_POWER_SAVE void snd_hda_power_up(struct hda_codec *codec); void snd_hda_power_down(struct hda_codec *codec); -#define snd_hda_codec_needs_resume(codec) codec->power_count void snd_hda_update_power_acct(struct hda_codec *codec); #else static inline void snd_hda_power_up(struct hda_codec *codec) {} static inline void snd_hda_power_down(struct hda_codec *codec) {} -#define snd_hda_codec_needs_resume(codec) 1 #endif #ifdef CONFIG_SND_HDA_PATCH_LOADER diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c19e71a94e1b..a70d7e5443aa 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -497,6 +497,7 @@ enum { AZX_DRIVER_NVIDIA, AZX_DRIVER_TERA, AZX_DRIVER_CTX, + AZX_DRIVER_CTHDA, AZX_DRIVER_GENERIC, AZX_NUM_DRIVERS, /* keep this as last entry */ }; @@ -518,6 +519,7 @@ enum { #define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ #define AZX_DCAPS_BUFSIZE (1 << 21) /* no buffer size alignment */ #define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */ +#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ @@ -533,6 +535,9 @@ enum { (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\ AZX_DCAPS_ALIGN_BUFSIZE) +#define AZX_DCAPS_PRESET_CTHDA \ + (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY) + static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_ICH] = "HDA Intel", [AZX_DRIVER_PCH] = "HDA Intel PCH", @@ -546,6 +551,7 @@ static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_NVIDIA] = "HDA NVidia", [AZX_DRIVER_TERA] = "HDA Teradici", [AZX_DRIVER_CTX] = "HDA Creative", + [AZX_DRIVER_CTHDA] = "HDA Creative", [AZX_DRIVER_GENERIC] = "HD-Audio Generic", }; @@ -1283,7 +1289,8 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) /* * set up a BDL entry */ -static int setup_bdle(struct snd_pcm_substream *substream, +static int setup_bdle(struct azx *chip, + struct snd_pcm_substream *substream, struct azx_dev *azx_dev, u32 **bdlp, int ofs, int size, int with_ioc) { @@ -1302,6 +1309,12 @@ static int setup_bdle(struct snd_pcm_substream *substream, bdl[1] = cpu_to_le32(upper_32_bits(addr)); /* program the size field of the BDL entry */ chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size); + /* one BDLE cannot cross 4K boundary on CTHDA chips */ + if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) { + u32 remain = 0x1000 - (ofs & 0xfff); + if (chunk > remain) + chunk = remain; + } bdl[2] = cpu_to_le32(chunk); /* program the IOC to enable interrupt * only when the whole fragment is processed @@ -1354,7 +1367,7 @@ static int azx_setup_periods(struct azx *chip, bdl_pos_adj[chip->dev_index]); pos_adj = 0; } else { - ofs = setup_bdle(substream, azx_dev, + ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs, pos_adj, !substream->runtime->no_period_wakeup); if (ofs < 0) @@ -1364,10 +1377,10 @@ static int azx_setup_periods(struct azx *chip, pos_adj = 0; for (i = 0; i < periods; i++) { if (i == periods - 1 && pos_adj) - ofs = setup_bdle(substream, azx_dev, &bdl, ofs, + ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs, period_bytes - pos_adj, 0); else - ofs = setup_bdle(substream, azx_dev, &bdl, ofs, + ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs, period_bytes, !substream->runtime->no_period_wakeup); if (ofs < 0) @@ -3116,6 +3129,11 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND | AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB }, #endif + /* CTHDA chips */ + { PCI_DEVICE(0x1102, 0x0010), + .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA }, + { PCI_DEVICE(0x1102, 0x0012), + .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA }, /* Vortex86MX */ { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC }, /* VMware HDAudio */ diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index d68948499fbc..2dd1c113a4c1 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -17,6 +17,7 @@ #include <sound/jack.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_jack.h" bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index c66655cf413a..8ae52465ec5d 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -12,6 +12,8 @@ #ifndef __SOUND_HDA_JACK_H #define __SOUND_HDA_JACK_H +struct auto_pin_cfg; + struct hda_jack_tbl { hda_nid_t nid; unsigned char action; /* event action (0 = none) */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 0ec9248165bc..9a096a8e0fc5 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -262,6 +262,8 @@ int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, struct snd_ctl_elem_value *ucontrol, hda_nid_t nid, unsigned int *cur_val); +int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label, + int index, int *type_index_ret); /* * Channel mode helper @@ -393,72 +395,7 @@ struct hda_bus_unsolicited { struct hda_bus *bus; }; -/* - * Helper for automatic pin configuration - */ - -enum { - AUTO_PIN_MIC, - AUTO_PIN_LINE_IN, - AUTO_PIN_CD, - AUTO_PIN_AUX, - AUTO_PIN_LAST -}; - -enum { - AUTO_PIN_LINE_OUT, - AUTO_PIN_SPEAKER_OUT, - AUTO_PIN_HP_OUT -}; - -#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS -#define AUTO_CFG_MAX_INS 8 - -struct auto_pin_cfg_item { - hda_nid_t pin; - int type; -}; - -struct auto_pin_cfg; -const char *hda_get_autocfg_input_label(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input); -int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp); -int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label, - int index, int *type_index_ret); - -enum { - INPUT_PIN_ATTR_UNUSED, /* pin not connected */ - INPUT_PIN_ATTR_INT, /* internal mic/line-in */ - INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */ - INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */ - INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ - INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ -}; - -int snd_hda_get_input_pin_attr(unsigned int def_conf); - -struct auto_pin_cfg { - int line_outs; - /* sorted in the order of Front/Surr/CLFE/Side */ - hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS]; - int speaker_outs; - hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS]; - int hp_outs; - int line_out_type; /* AUTO_PIN_XXX_OUT */ - hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; - int num_inputs; - struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS]; - int dig_outs; - hda_nid_t dig_out_pins[2]; - hda_nid_t dig_in_pin; - hda_nid_t mono_out_pin; - int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */ - int dig_in_type; /* HDA_PCM_TYPE_XXX */ -}; - +/* helper macros to retrieve pin default-config values */ #define get_defcfg_connect(cfg) \ ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) #define get_defcfg_association(cfg) \ @@ -472,19 +409,6 @@ struct auto_pin_cfg { #define get_defcfg_misc(cfg) \ ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) -/* bit-flags for snd_hda_parse_pin_def_config() behavior */ -#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */ -#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */ - -int snd_hda_parse_pin_defcfg(struct hda_codec *codec, - struct auto_pin_cfg *cfg, - const hda_nid_t *ignore_nids, - unsigned int cond_flags); - -/* older function */ -#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ - snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0) - /* amp values */ #define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8)) #define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8)) @@ -502,6 +426,46 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, #define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN) #define PIN_HP_AMP (AC_PINCTL_HP_EN) +unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin); +int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, + unsigned int val, bool cached); + +/** + * _snd_hda_set_pin_ctl - Set a pin-control value safely + * @codec: the codec instance + * @pin: the pin NID to set the control + * @val: the pin-control value (AC_PINCTL_* bits) + * + * This function sets the pin-control value to the given pin, but + * filters out the invalid pin-control bits when the pin has no such + * capabilities. For example, when PIN_HP is passed but the pin has no + * HP-drive capability, the HP bit is omitted. + * + * The function doesn't check the input VREF capability bits, though. + * Use snd_hda_get_default_vref() to guess the right value. + * Also, this function is only for analog pins, not for HDMI pins. + */ +static inline int +snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val) +{ + return _snd_hda_set_pin_ctl(codec, pin, val, false); +} + +/** + * snd_hda_set_pin_ctl_cache - Set a pin-control value safely + * @codec: the codec instance + * @pin: the pin NID to set the control + * @val: the pin-control value (AC_PINCTL_* bits) + * + * Just like snd_hda_set_pin_ctl() but write to cache as well. + */ +static inline int +snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin, + unsigned int val) +{ + return _snd_hda_set_pin_ctl(codec, pin, val, true); +} + /* * get widget capabilities */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 7143393927da..d8b2d6dee986 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -28,6 +28,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" @@ -1742,9 +1743,7 @@ static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol, if (! ad198x_eapd_put(kcontrol, ucontrol)) return 0; /* change speaker pin appropriately */ - snd_hda_codec_write(codec, 0x05, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->cur_eapd ? PIN_OUT : 0); + snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0); /* toggle HP mute appropriately */ snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0, HDA_AMP_MUTE, @@ -3103,7 +3102,7 @@ static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec, int dac_idx) { /* set as output */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); + snd_hda_set_pin_ctl(codec, nid, 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 03 */ @@ -3157,6 +3156,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec) for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; int type = cfg->inputs[i].type; + int val; switch (nid) { case 0x15: /* port-C */ snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0); @@ -3165,8 +3165,10 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec) 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, - type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN); + val = PIN_IN; + if (type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, nid); + snd_hda_set_pin_ctl(codec, nid, val); if (nid != AD1988_PIN_CD_NID) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 09ccfabb4a17..19ae14f739cb 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -26,6 +26,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" /* */ @@ -341,8 +342,7 @@ static int ca0110_build_pcms(struct hda_codec *codec) static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + snd_hda_set_pin_ctl(codec, pin, PIN_HP); if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -356,8 +356,8 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) { if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80); + snd_hda_set_pin_ctl(codec, pin, PIN_IN | + snd_hda_get_default_vref(codec, pin)); if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 21d91d580da8..d0d3540e39e7 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -30,6 +30,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #define WIDGET_CHIP_CTRL 0x15 #define WIDGET_DSP_CTRL 0x16 @@ -239,8 +240,7 @@ enum get_set { static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + snd_hda_set_pin_ctl(codec, pin, PIN_HP); if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -254,9 +254,8 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) { if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_VREF80); + snd_hda_set_pin_ctl(codec, pin, PIN_IN | + snd_hda_get_default_vref(codec, pin)); if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index c83ccdba1e5a..9647ed4d7929 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -26,6 +26,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_jack.h" #include <sound/tlv.h> @@ -933,8 +934,7 @@ static void cs_automute(struct hda_codec *codec) pin_ctl = 0; nid = cfg->speaker_pins[i]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_ctl); + snd_hda_set_pin_ctl(codec, nid, pin_ctl); } if (spec->gpio_eapd_hp) { unsigned int gpio = hp_present ? @@ -948,16 +948,14 @@ static void cs_automute(struct hda_codec *codec) /* mute HPs if spdif jack (SENSE_B) is present */ for (i = 0; i < cfg->hp_outs; i++) { nid = cfg->hp_pins[i]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_set_pin_ctl(codec, nid, (spdif_present && spec->sense_b) ? 0 : PIN_HP); } /* SPDIF TX on/off */ if (cfg->dig_outs) { nid = cfg->dig_out_pins[0]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_set_pin_ctl(codec, nid, spdif_present ? PIN_OUT : 0); } @@ -1024,13 +1022,11 @@ static void init_output(struct hda_codec *codec) /* set appropriate pin controls */ for (i = 0; i < cfg->line_outs; i++) - snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_set_pin_ctl(codec, cfg->line_out_pins[i], PIN_OUT); /* HP */ for (i = 0; i < cfg->hp_outs; i++) { hda_nid_t nid = cfg->hp_pins[i]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + snd_hda_set_pin_ctl(codec, nid, PIN_HP); if (!cfg->speaker_outs) continue; if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { @@ -1041,8 +1037,7 @@ static void init_output(struct hda_codec *codec) /* Speaker */ for (i = 0; i < cfg->speaker_outs; i++) - snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_set_pin_ctl(codec, cfg->speaker_pins[i], PIN_OUT); /* SPDIF is enabled on presence detect for CS421x */ if (spec->hp_detect || spec->spdif_detect) @@ -1063,14 +1058,9 @@ static void init_input(struct hda_codec *codec) continue; /* set appropriate pin control and mute first */ ctl = PIN_IN; - if (cfg->inputs[i].type == AUTO_PIN_MIC) { - unsigned int caps = snd_hda_query_pin_caps(codec, pin); - caps >>= AC_PINCAP_VREF_SHIFT; - if (caps & AC_PINCAP_VREF_80) - ctl = PIN_VREF80; - } - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); + if (cfg->inputs[i].type == AUTO_PIN_MIC) + ctl |= snd_hda_get_default_vref(codec, pin); + snd_hda_set_pin_ctl(codec, pin, ctl); snd_hda_codec_write(codec, spec->adc_nid[i], 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(spec->adc_idx[i])); diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index b6767b4ced44..c8fdaaefe702 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -29,6 +29,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #define NUM_PINS 11 diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index d906c5b74cf0..3acb5824ad39 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -30,6 +30,7 @@ #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" @@ -66,6 +67,7 @@ struct imux_info { }; struct conexant_spec { + struct hda_gen_spec gen; const struct snd_kcontrol_new *mixers[5]; int num_mixers; @@ -141,6 +143,7 @@ struct conexant_spec { unsigned int hp_laptop:1; unsigned int asus:1; unsigned int pin_eapd_ctrls:1; + unsigned int fixup_stereo_dmic:1; unsigned int adc_switching:1; @@ -1601,17 +1604,13 @@ static void cxt5051_update_speaker(struct hda_codec *codec) unsigned int pinctl; /* headphone pin */ pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0; - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x16, pinctl); /* speaker pin */ pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x1a, pinctl); /* on ideapad there is an additional speaker (subwoofer) to mute */ if (spec->ideapad) - snd_hda_codec_write(codec, 0x1b, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x1b, pinctl); } /* turn on/off EAPD (+ mute HP) as a master switch */ @@ -1996,8 +1995,7 @@ static void cxt5066_update_speaker(struct hda_codec *codec) /* Port A (HP) */ pinctl = (hp_port_a_present(spec) && spec->cur_eapd) ? PIN_HP : 0; - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x19, pinctl); /* Port D (HP/LO) */ pinctl = spec->cur_eapd ? spec->port_d_mode : 0; @@ -2010,13 +2008,11 @@ static void cxt5066_update_speaker(struct hda_codec *codec) if (!hp_port_d_present(spec)) pinctl = 0; } - snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x1c, pinctl); /* CLASS_D AMP */ pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; - snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x1f, pinctl); } /* turn on/off EAPD (+ mute HP) as a master switch */ @@ -2047,8 +2043,7 @@ static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec) /* 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. */ - return snd_hda_codec_write_cache(codec, 0x1a, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, + return snd_hda_set_pin_ctl_cache(codec, 0x1a, cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index); } @@ -2081,14 +2076,14 @@ static void cxt5066_olpc_select_mic(struct hda_codec *codec) } /* disable DC (port F) */ - snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, 0x1e, 0); /* external mic, port B */ - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_set_pin_ctl(codec, 0x1a, spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); /* internal mic, port C */ - snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_set_pin_ctl(codec, 0x1b, spec->ext_mic_present ? 0 : PIN_VREF80); } @@ -3357,9 +3352,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, struct conexant_spec *spec = codec->spec; int i; for (i = 0; i < num_pins; i++) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - on ? PIN_OUT : 0); + snd_hda_set_pin_ctl(codec, pins[i], on ? PIN_OUT : 0); if (spec->pin_eapd_ctrls) cx_auto_turn_eapd(codec, num_pins, pins, on); } @@ -3976,8 +3969,7 @@ static void cx_auto_init_output(struct hda_codec *codec) if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) & AC_PINCAP_HP_DRV) val |= AC_PINCTL_HP_EN; - snd_hda_codec_write(codec, cfg->hp_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); + snd_hda_set_pin_ctl(codec, cfg->hp_pins[i], val); } mute_outputs(codec, cfg->hp_outs, cfg->hp_pins); mute_outputs(codec, cfg->line_outs, cfg->line_out_pins); @@ -4030,13 +4022,11 @@ static void cx_auto_init_input(struct hda_codec *codec) } for (i = 0; i < cfg->num_inputs; i++) { - unsigned int type; + hda_nid_t pin = cfg->inputs[i].pin; + unsigned int type = PIN_IN; if (cfg->inputs[i].type == AUTO_PIN_MIC) - type = PIN_VREF80; - else - type = PIN_IN; - snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, type); + type |= snd_hda_get_default_vref(codec, pin); + snd_hda_set_pin_ctl(codec, pin, type); } if (spec->auto_mic) { @@ -4063,11 +4053,9 @@ static void cx_auto_init_digital(struct hda_codec *codec) struct auto_pin_cfg *cfg = &spec->autocfg; if (spec->multiout.dig_out_nid) - snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_set_pin_ctl(codec, cfg->dig_out_pins[0], PIN_OUT); if (spec->dig_in_nid) - snd_hda_codec_write(codec, cfg->dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); + snd_hda_set_pin_ctl(codec, cfg->dig_in_pin, PIN_IN); } static int cx_auto_init(struct hda_codec *codec) @@ -4084,9 +4072,9 @@ static int cx_auto_init(struct hda_codec *codec) static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, const char *dir, int cidx, - hda_nid_t nid, int hda_dir, int amp_idx) + hda_nid_t nid, int hda_dir, int amp_idx, int chs) { - static char name[32]; + static char name[44]; static struct snd_kcontrol_new knew[] = { HDA_CODEC_VOLUME(name, 0, 0, 0), HDA_CODEC_MUTE(name, 0, 0, 0), @@ -4096,7 +4084,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, for (i = 0; i < 2; i++) { struct snd_kcontrol *kctl; - knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx, + knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx, hda_dir); knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; knew[i].index = cidx; @@ -4115,7 +4103,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, } #define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \ - cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0) + cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3) #define cx_auto_add_pb_volume(codec, nid, str, idx) \ cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) @@ -4185,6 +4173,36 @@ static int cx_auto_build_output_controls(struct hda_codec *codec) return 0; } +/* Returns zero if this is a normal stereo channel, and non-zero if it should + be split in two independent channels. + dest_label must be at least 44 characters. */ +static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label, + char *dest_label, int nid) +{ + struct conexant_spec *spec = codec->spec; + int i; + + if (!spec->fixup_stereo_dmic) + return 0; + + for (i = 0; i < AUTO_CFG_MAX_INS; i++) { + int def_conf; + if (spec->autocfg.inputs[i].pin != nid) + continue; + + if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC) + return 0; + def_conf = snd_hda_codec_get_pincfg(codec, nid); + if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) + return 0; + + /* Finally found the inverted internal mic! */ + snprintf(dest_label, 44, "Inverted %s", label); + return 1; + } + return 0; +} + static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, const char *label, const char *pfx, int cidx) @@ -4193,14 +4211,25 @@ static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, int i; for (i = 0; i < spec->num_adc_nids; i++) { + char rightch_label[44]; hda_nid_t adc_nid = spec->adc_nids[i]; int idx = get_input_connection(codec, adc_nid, nid); if (idx < 0) continue; if (codec->single_adc_amp) idx = 0; + + if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { + /* Make two independent kcontrols for left and right */ + int err = cx_auto_add_volume_idx(codec, label, pfx, + cidx, adc_nid, HDA_INPUT, idx, 1); + if (err < 0) + return err; + return cx_auto_add_volume_idx(codec, rightch_label, pfx, + cidx, adc_nid, HDA_INPUT, idx, 2); + } return cx_auto_add_volume_idx(codec, label, pfx, - cidx, adc_nid, HDA_INPUT, idx); + cidx, adc_nid, HDA_INPUT, idx, 3); } return 0; } @@ -4213,9 +4242,19 @@ static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx, int i, con; nid = spec->imux_info[idx].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) + if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { + char rightch_label[44]; + if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { + int err = cx_auto_add_volume_idx(codec, label, " Boost", + cidx, nid, HDA_INPUT, 0, 1); + if (err < 0) + return err; + return cx_auto_add_volume_idx(codec, rightch_label, " Boost", + cidx, nid, HDA_INPUT, 0, 2); + } return cx_auto_add_volume(codec, label, " Boost", cidx, nid, HDA_INPUT); + } con = __select_input_connection(codec, spec->imux_info[idx].adc, nid, &mux, false, 0); if (con < 0) @@ -4370,37 +4409,21 @@ static const struct hda_codec_ops cx_auto_patch_ops = { /* * pin fix-up */ -struct cxt_pincfg { - hda_nid_t nid; - u32 val; -}; - -static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg) -{ - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); - -} - -static void apply_pin_fixup(struct hda_codec *codec, - const struct snd_pci_quirk *quirk, - const struct cxt_pincfg **table) -{ - quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); - if (quirk) { - snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n", - quirk->name); - apply_pincfg(codec, table[quirk->value]); - } -} - enum { CXT_PINCFG_LENOVO_X200, CXT_PINCFG_LENOVO_TP410, + CXT_FIXUP_STEREO_DMIC, }; +static void cxt_fixup_stereo_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct conexant_spec *spec = codec->spec; + spec->fixup_stereo_dmic = 1; +} + /* ThinkPad X200 & co with cxt5051 */ -static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { +static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { { 0x16, 0x042140ff }, /* HP (seq# overridden) */ { 0x17, 0x21a11000 }, /* dock-mic */ { 0x19, 0x2121103f }, /* dock-HP */ @@ -4409,16 +4432,26 @@ static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { }; /* ThinkPad 410/420/510/520, X201 & co with cxt5066 */ -static const struct cxt_pincfg cxt_pincfg_lenovo_tp410[] = { +static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = { { 0x19, 0x042110ff }, /* HP (seq# overridden) */ { 0x1a, 0x21a190f0 }, /* dock-mic */ { 0x1c, 0x212140ff }, /* dock-HP */ {} }; -static const struct cxt_pincfg *cxt_pincfg_tbl[] = { - [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200, - [CXT_PINCFG_LENOVO_TP410] = cxt_pincfg_lenovo_tp410, +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, + }, + [CXT_FIXUP_STEREO_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_stereo_dmic, + }, }; static const struct snd_pci_quirk cxt5051_fixups[] = { @@ -4432,6 +4465,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { 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), + SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), {} }; @@ -4471,13 +4505,16 @@ static int patch_conexant_auto(struct hda_codec *codec) case 0x14f15051: add_cx5051_fake_mutes(codec); codec->pin_amp_workaround = 1; - apply_pin_fixup(codec, cxt5051_fixups, cxt_pincfg_tbl); + snd_hda_pick_fixup(codec, NULL, cxt5051_fixups, cxt_fixups); break; default: codec->pin_amp_workaround = 1; - apply_pin_fixup(codec, cxt5066_fixups, cxt_pincfg_tbl); + snd_hda_pick_fixup(codec, NULL, cxt5066_fixups, cxt_fixups); + break; } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + /* 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, @@ -4556,6 +4593,12 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_conexant_auto }, { .id = 0x14f150b9, .name = "CX20665", .patch = patch_conexant_auto }, + { .id = 0x14f1510f, .name = "CX20751/2", + .patch = patch_conexant_auto }, + { .id = 0x14f15110, .name = "CX20751/2", + .patch = patch_conexant_auto }, + { .id = 0x14f15111, .name = "CX20753/4", + .patch = patch_conexant_auto }, {} /* terminator */ }; @@ -4576,6 +4619,9 @@ MODULE_ALIAS("snd-hda-codec-id:14f150ab"); MODULE_ALIAS("snd-hda-codec-id:14f150ac"); MODULE_ALIAS("snd-hda-codec-id:14f150b8"); MODULE_ALIAS("snd-hda-codec-id:14f150b9"); +MODULE_ALIAS("snd-hda-codec-id:14f1510f"); +MODULE_ALIAS("snd-hda-codec-id:14f15110"); +MODULE_ALIAS("snd-hda-codec-id:14f15111"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 83f345f3c961..ad319d4dc32f 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1592,10 +1592,10 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, unsigned int dataDCC2, channel_id; int i; struct hdmi_spec *spec = codec->spec; - struct hda_spdif_out *spdif = - snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid); + struct hda_spdif_out *spdif; mutex_lock(&codec->spdif_mutex); + spdif = snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid); chs = substream->runtime->channels; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 818f90bc7d57..152f458afd2b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -32,6 +32,7 @@ #include <sound/jack.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" @@ -66,8 +67,6 @@ struct alc_customize_define { unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ }; -struct alc_fixup; - struct alc_multi_io { hda_nid_t pin; /* multi-io widget pin NID */ hda_nid_t dac; /* DAC to be connected */ @@ -82,19 +81,33 @@ enum { #define MAX_VOL_NIDS 0x40 +/* make compatible with old code */ +#define alc_apply_pincfgs snd_hda_apply_pincfgs +#define alc_apply_fixup snd_hda_apply_fixup +#define alc_pick_fixup snd_hda_pick_fixup +#define alc_fixup hda_fixup +#define alc_pincfg hda_pintbl +#define alc_model_fixup hda_model_fixup + +#define ALC_FIXUP_PINS HDA_FIXUP_PINS +#define ALC_FIXUP_VERBS HDA_FIXUP_VERBS +#define ALC_FIXUP_FUNC HDA_FIXUP_FUNC + +#define ALC_FIXUP_ACT_PRE_PROBE HDA_FIXUP_ACT_PRE_PROBE +#define ALC_FIXUP_ACT_PROBE HDA_FIXUP_ACT_PROBE +#define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT +#define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD + + struct alc_spec { + struct hda_gen_spec gen; + /* codec parameterization */ const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ unsigned int num_mixers; const struct snd_kcontrol_new *cap_mixer; /* capture mixer */ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - const struct hda_verb *init_verbs[10]; /* initialization verbs - * don't forget NULL - * termination! - */ - unsigned int num_init_verbs; - char stream_name_analog[32]; /* analog PCM stream */ const struct hda_pcm_stream *stream_analog_playback; const struct hda_pcm_stream *stream_analog_capture; @@ -210,11 +223,6 @@ struct alc_spec { unsigned int pll_coef_idx, pll_coef_bit; unsigned int coef0; - /* fix-up list */ - int fixup_id; - const struct alc_fixup *fixup_list; - const char *fixup_name; - /* multi-io */ int multi_ios; struct alc_multi_io multi_io[4]; @@ -319,13 +327,16 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, /* for shared I/O, change the pin-control accordingly */ if (spec->shared_mic_hp) { + unsigned int val; + hda_nid_t pin = spec->autocfg.inputs[1].pin; /* 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); + if (spec->cur_mux[adc_idx]) + val = snd_hda_get_default_vref(codec, pin) | PIN_IN; + else + val = PIN_HP; + snd_hda_set_pin_ctl(codec, pin, val); spec->automute_speaker = !spec->cur_mux[adc_idx]; call_update_outputs(codec); } @@ -376,25 +387,9 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, int auto_pin_type) { unsigned int val = PIN_IN; - - if (auto_pin_type == AUTO_PIN_MIC) { - unsigned int pincap; - unsigned int oldval; - oldval = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - pincap = snd_hda_query_pin_caps(codec, nid); - pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - /* if the default pin setup is vref50, we give it priority */ - if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50) - val = PIN_VREF80; - else if (pincap & AC_PINCAP_VREF_50) - val = PIN_VREF50; - else if (pincap & AC_PINCAP_VREF_100) - val = PIN_VREF100; - else if (pincap & AC_PINCAP_VREF_GRD) - val = PIN_VREFGRD; - } - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); + if (auto_pin_type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, nid); + snd_hda_set_pin_ctl(codec, nid, val); } /* @@ -409,13 +404,6 @@ static void add_mixer(struct alc_spec *spec, const struct snd_kcontrol_new *mix) spec->mixers[spec->num_mixers++] = mix; } -static void add_verb(struct alc_spec *spec, const struct hda_verb *verb) -{ - if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs))) - return; - spec->init_verbs[spec->num_init_verbs++] = verb; -} - /* * GPIO setup tables, used in initialization */ @@ -517,9 +505,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, } else val = 0; val |= pin_bits; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - val); + snd_hda_set_pin_ctl(codec, nid, val); break; case ALC_AUTOMUTE_AMP: snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, @@ -1200,6 +1186,16 @@ static void alc_auto_check_switches(struct hda_codec *codec) */ #define ALC_FIXUP_SKU_IGNORE (2) +static void alc_fixup_sku_ignore(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->cdefine.fixup = 1; + spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; + } +} + static int alc_auto_parse_customize_define(struct hda_codec *codec) { unsigned int ass, tmp, i; @@ -1403,178 +1399,6 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) } /* - * Fix-up pin default configurations and add default verbs - */ - -struct alc_pincfg { - hda_nid_t nid; - u32 val; -}; - -struct alc_model_fixup { - const int id; - const char *name; -}; - -struct alc_fixup { - int type; - bool chained; - int chain_id; - union { - unsigned int sku; - const struct alc_pincfg *pins; - const struct hda_verb *verbs; - void (*func)(struct hda_codec *codec, - const struct alc_fixup *fix, - int action); - } v; -}; - -enum { - ALC_FIXUP_INVALID, - ALC_FIXUP_SKU, - ALC_FIXUP_PINS, - ALC_FIXUP_VERBS, - ALC_FIXUP_FUNC, -}; - -enum { - ALC_FIXUP_ACT_PRE_PROBE, - ALC_FIXUP_ACT_PROBE, - ALC_FIXUP_ACT_INIT, - ALC_FIXUP_ACT_BUILD, -}; - -static void alc_apply_pincfgs(struct hda_codec *codec, - const struct alc_pincfg *cfg) -{ - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); -} - -static void alc_apply_fixup(struct hda_codec *codec, int action) -{ - struct alc_spec *spec = codec->spec; - int id = spec->fixup_id; -#ifdef CONFIG_SND_DEBUG_VERBOSE - const char *modelname = spec->fixup_name; -#endif - int depth = 0; - - if (!spec->fixup_list) - return; - - while (id >= 0) { - const struct alc_fixup *fix = spec->fixup_list + id; - const struct alc_pincfg *cfg; - - switch (fix->type) { - case ALC_FIXUP_SKU: - if (action != ALC_FIXUP_ACT_PRE_PROBE || !fix->v.sku) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply sku override for %s\n", - codec->chip_name, modelname); - spec->cdefine.sku_cfg = fix->v.sku; - spec->cdefine.fixup = 1; - break; - case ALC_FIXUP_PINS: - cfg = fix->v.pins; - if (action != ALC_FIXUP_ACT_PRE_PROBE || !cfg) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply pincfg for %s\n", - codec->chip_name, modelname); - alc_apply_pincfgs(codec, cfg); - break; - case ALC_FIXUP_VERBS: - if (action != ALC_FIXUP_ACT_PROBE || !fix->v.verbs) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply fix-verbs for %s\n", - codec->chip_name, modelname); - add_verb(codec->spec, fix->v.verbs); - break; - case ALC_FIXUP_FUNC: - if (!fix->v.func) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply fix-func for %s\n", - codec->chip_name, modelname); - fix->v.func(codec, fix, action); - break; - default: - snd_printk(KERN_ERR "hda_codec: %s: " - "Invalid fixup type %d\n", - codec->chip_name, fix->type); - break; - } - if (!fix->chained) - break; - if (++depth > 10) - break; - id = fix->chain_id; - } -} - -static void alc_pick_fixup(struct hda_codec *codec, - const struct alc_model_fixup *models, - const struct snd_pci_quirk *quirk, - const struct alc_fixup *fixlist) -{ - struct alc_spec *spec = codec->spec; - const struct snd_pci_quirk *q; - int id = -1; - const char *name = NULL; - - /* when model=nofixup is given, don't pick up any fixups */ - if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { - spec->fixup_list = NULL; - spec->fixup_id = -1; - return; - } - - if (codec->modelname && models) { - while (models->name) { - if (!strcmp(codec->modelname, models->name)) { - id = models->id; - name = models->name; - break; - } - models++; - } - } - if (id < 0) { - 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 = q->name; -#endif - break; - } - } - } - - spec->fixup_id = id; - if (id >= 0) { - spec->fixup_list = fixlist; - spec->fixup_name = name; - } -} - -/* * COEF access helper functions */ static int alc_read_coef_idx(struct hda_codec *codec, @@ -1621,8 +1445,7 @@ static void alc_auto_init_digital(struct hda_codec *codec) pin = spec->autocfg.dig_out_pins[i]; if (!pin) continue; - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_set_pin_ctl(codec, pin, PIN_OUT); if (!i) dac = spec->multiout.dig_out_nid; else @@ -1635,9 +1458,7 @@ static void alc_auto_init_digital(struct hda_codec *codec) } pin = spec->autocfg.dig_in_pin; if (pin) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_IN); + snd_hda_set_pin_ctl(codec, pin, PIN_IN); } /* parse digital I/Os and set up NIDs in BIOS auto-parse mode */ @@ -2068,7 +1889,6 @@ static void alc_auto_init_std(struct hda_codec *codec); static int alc_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int i; if (spec->init_hook) spec->init_hook(codec); @@ -2076,8 +1896,6 @@ static int alc_init(struct hda_codec *codec) alc_fix_pll(codec); alc_auto_init_amp(codec, spec->init_amp); - for (i = 0; i < spec->num_init_verbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); alc_init_special_input_src(codec); alc_auto_init_std(codec); @@ -2856,8 +2674,7 @@ static int alc_auto_create_shared_input(struct hda_codec *codec) static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_type) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); + snd_hda_set_pin_ctl(codec, nid, pin_type); /* unmute pin */ if (nid_has_mute(codec, nid, HDA_OUTPUT)) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -3998,9 +3815,7 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); if (output) { - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); + snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); @@ -4009,9 +3824,8 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->multi_io[idx].ctl_in); + snd_hda_set_pin_ctl_cache(codec, nid, + spec->multi_io[idx].ctl_in); } return 0; } @@ -4427,6 +4241,25 @@ static int alc_parse_auto_config(struct hda_codec *codec, return 1; } +/* common preparation job for alc_spec */ +static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) +{ + struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); + int err; + + if (!spec) + return -ENOMEM; + codec->spec = spec; + spec->mixer_nid = mixer_nid; + + err = alc_codec_rename_from_preset(codec); + if (err < 0) { + kfree(spec); + return err; + } + return 0; +} + static int alc880_parse_auto_config(struct hda_codec *codec) { static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; @@ -4808,13 +4641,11 @@ static int patch_alc880(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; - spec->mixer_nid = 0x0b; + spec = codec->spec; spec->need_dac_fix = 1; alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, @@ -4890,7 +4721,7 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT); spec->unsol_event = alc_sku_unsol_event; - add_verb(codec->spec, alc_gpio1_init_verbs); + snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs); } } @@ -5001,13 +4832,11 @@ static int patch_alc260(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x07); + if (err < 0) + return err; - spec->mixer_nid = 0x07; + spec = codec->spec; alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); @@ -5171,8 +5000,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec, val = snd_hda_codec_read(codec, nids[i], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); val |= AC_PINCTL_VREF_80; - snd_hda_codec_write(codec, nids[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); + snd_hda_set_pin_ctl(codec, nids[i], val); spec->keep_vref_in_automute = 1; break; } @@ -5193,8 +5021,7 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec, val = snd_hda_codec_read(codec, nids[i], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); val |= AC_PINCTL_VREF_50; - snd_hda_codec_write(codec, nids[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); + snd_hda_set_pin_ctl(codec, nids[i], val); } spec->keep_vref_in_automute = 1; } @@ -5225,8 +5052,8 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_ACER_ASPIRE_7736] = { - .type = ALC_FIXUP_SKU, - .v.sku = ALC_FIXUP_SKU_IGNORE, + .type = ALC_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, }, [ALC882_FIXUP_ASUS_W90V] = { .type = ALC_FIXUP_PINS, @@ -5405,6 +5232,8 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { 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, 0x021e, "Acer Aspire 5739G", + ALC882_FIXUP_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE), SND_PCI_QUIRK(0x1025, 0x026b, "Acer Aspire 8940G", ALC882_FIXUP_ACER_ASPIRE_8930G), SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736), @@ -5438,6 +5267,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_IMAC91_VREF), SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3", ALC889_FIXUP_CD), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), @@ -5473,13 +5303,11 @@ static int patch_alc882(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; - spec->mixer_nid = 0x0b; + spec = codec->spec; switch (codec->vendor_id) { case 0x10ec0882: @@ -5491,10 +5319,6 @@ static int patch_alc882(struct hda_codec *codec) break; } - err = alc_codec_rename_from_preset(codec); - if (err < 0) - goto error; - alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, alc882_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); @@ -5618,13 +5442,11 @@ static int patch_alc262(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; - spec->mixer_nid = 0x0b; + spec = codec->spec; #if 0 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is @@ -5638,13 +5460,13 @@ static int patch_alc262(struct hda_codec *codec) snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80); } #endif - alc_auto_parse_customize_define(codec); - alc_fix_pll_init(codec, 0x20, 0x0a, 10); alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + alc_auto_parse_customize_define(codec); + /* automatic parse from the BIOS config */ err = alc262_parse_auto_config(codec); if (err < 0) @@ -5707,7 +5529,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (err > 0) { if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { add_mixer(spec, alc268_beep_mixer); - add_verb(spec, alc268_beep_init_verbs); + snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs); } } return err; @@ -5720,13 +5542,12 @@ static int patch_alc268(struct hda_codec *codec) struct alc_spec *spec; int i, has_beep, err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - /* ALC268 has no aa-loopback mixer */ + err = alc_alloc_spec(codec, 0); + if (err < 0) + return err; + + spec = codec->spec; /* automatic parse from the BIOS config */ err = alc268_parse_auto_config(codec); @@ -5943,9 +5764,7 @@ static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled) { struct hda_codec *codec = private_data; unsigned int pinval = enabled ? 0x20 : 0x24; - snd_hda_codec_update_cache(codec, 0x19, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinval); + snd_hda_set_pin_ctl_cache(codec, 0x19, pinval); } static void alc269_fixup_mic2_mute(struct hda_codec *codec, @@ -6012,8 +5831,8 @@ static const struct alc_fixup alc269_fixups[] = { } }, [ALC269_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_SKU, - .v.sku = ALC_FIXUP_SKU_IGNORE, + .type = ALC_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, }, [ALC269_FIXUP_ASUS_G73JW] = { .type = ALC_FIXUP_PINS, @@ -6239,21 +6058,13 @@ static void alc269_fill_coef(struct hda_codec *codec) static int patch_alc269(struct hda_codec *codec) { struct alc_spec *spec; - int err = 0; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x0b; - - alc_auto_parse_customize_define(codec); + int err; - err = alc_codec_rename_from_preset(codec); + err = alc_alloc_spec(codec, 0x0b); if (err < 0) - goto error; + return err; + + spec = codec->spec; if (codec->vendor_id == 0x10ec0269) { spec->codec_variant = ALC269_TYPE_ALC269VA; @@ -6283,6 +6094,8 @@ static int patch_alc269(struct hda_codec *codec) alc269_fixup_tbl, alc269_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + alc_auto_parse_customize_define(codec); + /* automatic parse from the BIOS config */ err = alc269_parse_auto_config(codec); if (err < 0) @@ -6343,8 +6156,7 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) val |= AC_PINCTL_IN_EN; val |= AC_PINCTL_VREF_50; - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); + snd_hda_set_pin_ctl(codec, 0x0f, val); spec->keep_vref_in_automute = 1; } @@ -6398,13 +6210,11 @@ static int patch_alc861(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x15); + if (err < 0) + return err; - spec->mixer_nid = 0x15; + spec = codec->spec; alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); @@ -6501,13 +6311,11 @@ static int patch_alc861vd(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; - spec->mixer_nid = 0x0b; + spec = codec->spec; alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); @@ -6519,7 +6327,7 @@ static int patch_alc861vd(struct hda_codec *codec) if (codec->vendor_id == 0x10ec0660) { /* always turn on EAPD */ - add_verb(spec, alc660vd_eapd_verbs); + snd_hda_gen_add_verbs(&spec->gen, alc660vd_eapd_verbs); } if (!spec->no_analog) { @@ -6632,8 +6440,8 @@ static const struct alc_fixup alc662_fixups[] = { } }, [ALC662_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_SKU, - .v.sku = ALC_FIXUP_SKU_IGNORE, + .type = ALC_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, }, [ALC662_FIXUP_HP_RP5800] = { .type = ALC_FIXUP_PINS, @@ -6846,27 +6654,19 @@ static const struct alc_model_fixup alc662_fixup_models[] = { static int patch_alc662(struct hda_codec *codec) { struct alc_spec *spec; - int err = 0; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; + int err; - codec->spec = spec; + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; - spec->mixer_nid = 0x0b; + spec = codec->spec; /* handle multiple HPs as is */ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; - alc_auto_parse_customize_define(codec); - alc_fix_pll_init(codec, 0x20, 0x04, 15); - err = alc_codec_rename_from_preset(codec); - if (err < 0) - goto error; - if ((alc_get_coef0(codec) & (1 << 14)) && codec->bus->pci->subsystem_vendor == 0x1025 && spec->cdefine.platform_type == 1) { @@ -6877,6 +6677,9 @@ static int patch_alc662(struct hda_codec *codec) alc_pick_fixup(codec, alc662_fixup_models, alc662_fixup_tbl, alc662_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + + alc_auto_parse_customize_define(codec); + /* automatic parse from the BIOS config */ err = alc662_parse_auto_config(codec); if (err < 0) @@ -6926,16 +6729,12 @@ static int alc680_parse_auto_config(struct hda_codec *codec) */ static int patch_alc680(struct hda_codec *codec) { - struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - /* ALC680 has no aa-loopback mixer */ + err = alc_alloc_spec(codec, 0); + if (err < 0) + return err; /* automatic parse from the BIOS config */ err = alc680_parse_auto_config(codec); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 4742cac26aa9..b04179be6205 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -36,6 +36,7 @@ #include <sound/tlv.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" @@ -681,8 +682,7 @@ static int stac_vrefout_set(struct hda_codec *codec, pinctl &= ~AC_PINCTL_VREFEN; pinctl |= (new_vref & AC_PINCTL_VREFEN); - error = snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); + error = snd_hda_set_pin_ctl_cache(codec, nid, pinctl); if (error < 0) return error; @@ -706,8 +706,7 @@ static unsigned int stac92xx_vref_set(struct hda_codec *codec, else pincfg |= AC_PINCTL_IN_EN; - error = snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg); + error = snd_hda_set_pin_ctl_cache(codec, nid, pincfg); if (error < 0) return error; else @@ -2505,27 +2504,10 @@ static int stac92xx_build_pcms(struct hda_codec *codec) return 0; } -static unsigned int stac92xx_get_default_vref(struct hda_codec *codec, - hda_nid_t nid) -{ - unsigned int pincap = snd_hda_query_pin_caps(codec, nid); - pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - if (pincap & AC_PINCAP_VREF_100) - return AC_PINCTL_VREF_100; - if (pincap & AC_PINCAP_VREF_80) - return AC_PINCTL_VREF_80; - if (pincap & AC_PINCAP_VREF_50) - return AC_PINCTL_VREF_50; - if (pincap & AC_PINCAP_VREF_GRD) - return AC_PINCTL_VREF_GRD; - return 0; -} - static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type) { - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); + snd_hda_set_pin_ctl_cache(codec, nid, pin_type); } #define stac92xx_hp_switch_info snd_ctl_boolean_mono_info @@ -2594,7 +2576,7 @@ static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol, hda_nid_t nid = kcontrol->private_value; unsigned int vref = stac92xx_vref_get(codec, nid); - if (vref == stac92xx_get_default_vref(codec, nid)) + if (vref == snd_hda_get_default_vref(codec, nid)) ucontrol->value.enumerated.item[0] = 0; else if (vref == AC_PINCTL_VREF_GRD) ucontrol->value.enumerated.item[0] = 1; @@ -2613,7 +2595,7 @@ static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol, hda_nid_t nid = kcontrol->private_value; if (ucontrol->value.enumerated.item[0] == 0) - new_vref = stac92xx_get_default_vref(codec, nid); + new_vref = snd_hda_get_default_vref(codec, nid); else if (ucontrol->value.enumerated.item[0] == 1) new_vref = AC_PINCTL_VREF_GRD; else if (ucontrol->value.enumerated.item[0] == 2) @@ -2679,7 +2661,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ else { unsigned int pinctl = AC_PINCTL_IN_EN; if (io_idx) /* set VREF for mic */ - pinctl |= stac92xx_get_default_vref(codec, nid); + pinctl |= snd_hda_get_default_vref(codec, nid); stac92xx_auto_set_pinctl(codec, nid, pinctl); } @@ -2847,7 +2829,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec, char name[22]; if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) { - if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD + if (snd_hda_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD && nid == spec->line_switch) control = STAC_CTL_WIDGET_IO_SWITCH; else if (snd_hda_query_pin_caps(codec, nid) @@ -4354,7 +4336,7 @@ static int stac92xx_init(struct hda_codec *codec) unsigned int pinctl, conf; if (type == AUTO_PIN_MIC) { /* for mic pins, force to initialize */ - pinctl = stac92xx_get_default_vref(codec, nid); + pinctl = snd_hda_get_default_vref(codec, nid); pinctl |= AC_PINCTL_IN_EN; stac92xx_auto_set_pinctl(codec, nid, pinctl); } else { @@ -4460,8 +4442,7 @@ static void stac92xx_shutup_pins(struct hda_codec *codec) struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); def_conf = snd_hda_codec_get_pincfg(codec, pin->nid); if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) - snd_hda_codec_write(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, pin->nid, 0); } } @@ -4517,9 +4498,7 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, pin_ctl |= flag; if (old_ctl != pin_ctl) - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_ctl); + snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl); } static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, @@ -4528,9 +4507,7 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_ctl = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); if (pin_ctl & flag) - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_ctl & ~flag); + snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl & ~flag); } static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) @@ -5009,20 +4986,6 @@ static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) return 0; } -static int stac92xx_pre_resume(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - /* sync mute LED */ - if (spec->vref_mute_led_nid) - stac_vrefout_set(codec, spec->vref_mute_led_nid, - spec->vref_led); - else if (spec->gpio_led) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); - return 0; -} - static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state) { @@ -5046,7 +5009,6 @@ static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg, #else #define stac92xx_suspend NULL #define stac92xx_resume NULL -#define stac92xx_pre_resume NULL #define stac92xx_set_power_state NULL #endif /* CONFIG_PM */ @@ -5592,9 +5554,6 @@ again: codec->patch_ops.set_power_state = stac92xx_set_power_state; } -#ifdef CONFIG_PM - codec->patch_ops.pre_resume = stac92xx_pre_resume; -#endif } err = stac92xx_parse_auto_config(codec); @@ -5901,9 +5860,6 @@ again: codec->patch_ops.set_power_state = stac92xx_set_power_state; } -#ifdef CONFIG_PM - codec->patch_ops.pre_resume = stac92xx_pre_resume; -#endif } spec->multiout.dac_nids = spec->dac_nids; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 06214fdc9486..db272fb5e579 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -54,6 +54,7 @@ #include <sound/asoundef.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_jack.h" /* Pin Widget NID */ @@ -532,8 +533,7 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, { if (!pin) return; - snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); + snd_hda_set_pin_ctl(codec, pin, pin_type); if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x02); @@ -662,12 +662,12 @@ static void via_auto_init_analog_input(struct hda_codec *codec) hda_nid_t nid = cfg->inputs[i].pin; if (spec->smart51_enabled && is_smart51_pins(codec, nid)) ctl = PIN_OUT; - else if (cfg->inputs[i].type == AUTO_PIN_MIC) - ctl = PIN_VREF50; - else + else { ctl = PIN_IN; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); + if (cfg->inputs[i].type == AUTO_PIN_MIC) + ctl |= snd_hda_get_default_vref(codec, nid); + } + snd_hda_set_pin_ctl(codec, nid, ctl); } /* init input-src */ @@ -1006,9 +1006,7 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); parm |= out_in; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - parm); + snd_hda_set_pin_ctl(codec, nid, parm); if (out_in == AC_PINCTL_OUT_EN) { mute_aa_path(codec, 1); notify_aa_path_ctls(codec); @@ -1647,8 +1645,7 @@ static void toggle_output_mutes(struct hda_codec *codec, int num_pins, parm &= ~AC_PINCTL_OUT_EN; else parm |= AC_PINCTL_OUT_EN; - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, parm); + snd_hda_set_pin_ctl(codec, pins[i], parm); } } @@ -1709,8 +1706,7 @@ static void via_gpio_control(struct hda_codec *codec) if (gpio_data == 0x02) { /* unmute line out */ - snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0], PIN_OUT); if (vol_counter & 0x20) { /* decrease volume */ @@ -1728,9 +1724,7 @@ static void via_gpio_control(struct hda_codec *codec) } } else if (!(gpio_data & 0x02)) { /* mute line out */ - snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - 0); + snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0], 0); } } @@ -2757,8 +2751,7 @@ static void via_auto_init_dig_in(struct hda_codec *codec) struct via_spec *spec = codec->spec; if (!spec->dig_in_nid) return; - snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); + snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN); } /* initialize the unsolicited events */ |