diff options
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/Kconfig | 11 | ||||
-rw-r--r-- | sound/pci/hda/hda_auto_parser.c | 115 | ||||
-rw-r--r-- | sound/pci/hda/hda_auto_parser.h | 81 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 469 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 33 | ||||
-rw-r--r-- | sound/pci/hda/hda_generic.c | 5209 | ||||
-rw-r--r-- | sound/pci/hda/hda_generic.h | 302 | ||||
-rw-r--r-- | sound/pci/hda/hda_hwdep.c | 87 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 11 | ||||
-rw-r--r-- | sound/pci/hda/hda_jack.c | 9 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 76 | ||||
-rw-r--r-- | sound/pci/hda/hda_proc.c | 13 | ||||
-rw-r--r-- | sound/pci/hda/patch_analog.c | 1416 | ||||
-rw-r--r-- | sound/pci/hda/patch_ca0110.c | 490 | ||||
-rw-r--r-- | sound/pci/hda/patch_cirrus.c | 1292 | ||||
-rw-r--r-- | sound/pci/hda/patch_cmedia.c | 166 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 1375 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 4312 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 7405 | ||||
-rw-r--r-- | sound/pci/hda/patch_via.c | 2611 |
20 files changed, 9779 insertions, 15704 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 6eeb8897624b..4466bf63d6bf 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -86,6 +86,7 @@ config SND_HDA_PATCH_LOADER config SND_HDA_CODEC_REALTEK bool "Build Realtek HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include Realtek HD-audio codec support in snd-hda-intel driver, such as ALC880. @@ -98,6 +99,7 @@ config SND_HDA_CODEC_REALTEK config SND_HDA_CODEC_ANALOG bool "Build Analog Device HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include Analog Device HD-audio codec support in snd-hda-intel driver, such as AD1986A. @@ -110,6 +112,7 @@ config SND_HDA_CODEC_ANALOG config SND_HDA_CODEC_SIGMATEL bool "Build IDT/Sigmatel HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include IDT (Sigmatel) HD-audio codec support in snd-hda-intel driver, such as STAC9200. @@ -122,6 +125,7 @@ config SND_HDA_CODEC_SIGMATEL config SND_HDA_CODEC_VIA bool "Build VIA HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include VIA HD-audio codec support in snd-hda-intel driver, such as VT1708. @@ -147,8 +151,8 @@ config SND_HDA_CODEC_HDMI config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" - depends on SND_HDA_INTEL default y + select SND_HDA_GENERIC help Say Y here to include Cirrus Logic codec support in snd-hda-intel driver, such as CS4206. @@ -161,6 +165,7 @@ config SND_HDA_CODEC_CIRRUS config SND_HDA_CODEC_CONEXANT bool "Build Conexant HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include Conexant HD-audio codec support in snd-hda-intel driver, such as CX20549. @@ -172,8 +177,8 @@ config SND_HDA_CODEC_CONEXANT config SND_HDA_CODEC_CA0110 bool "Build Creative CA0110-IBG codec support" - depends on SND_HDA_INTEL default y + select SND_HDA_GENERIC help Say Y here to include Creative CA0110-IBG codec support in snd-hda-intel driver, found on some Creative X-Fi cards. @@ -185,7 +190,6 @@ config SND_HDA_CODEC_CA0110 config SND_HDA_CODEC_CA0132 bool "Build Creative CA0132 codec support" - depends on SND_HDA_INTEL default y help Say Y here to include Creative CA0132 codec support in @@ -199,6 +203,7 @@ config SND_HDA_CODEC_CA0132 config SND_HDA_CODEC_CMEDIA bool "Build C-Media HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include C-Media HD-audio codec support in snd-hda-intel driver, such as CMI9880. diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 7da883a464e3..0088bb0b04de 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -97,6 +97,28 @@ static void reorder_outputs(unsigned int nums, hda_nid_t *pins) } } +/* check whether the given pin has a proper pin I/O capability bit */ +static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin, + unsigned int dev) +{ + unsigned int pincap = snd_hda_query_pin_caps(codec, pin); + + /* some old hardware don't return the proper pincaps */ + if (!pincap) + return true; + + switch (dev) { + case AC_JACK_LINE_OUT: + case AC_JACK_SPEAKER: + case AC_JACK_HP_OUT: + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + return !!(pincap & AC_PINCAP_OUT); + default: + return !!(pincap & AC_PINCAP_IN); + } +} + /* * Parse all pin widgets and store the useful pin nids to cfg * @@ -126,6 +148,9 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)]; int i; + if (!snd_hda_get_int_hint(codec, "parser_flags", &i)) + cond_flags = i; + memset(cfg, 0, sizeof(*cfg)); memset(line_out, 0, sizeof(line_out)); @@ -156,10 +181,14 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, /* workaround for buggy BIOS setups */ if (dev == AC_JACK_LINE_OUT) { - if (conn == AC_JACK_PORT_FIXED) + if (conn == AC_JACK_PORT_FIXED || + conn == AC_JACK_PORT_BOTH) dev = AC_JACK_SPEAKER; } + if (!check_pincap_validity(codec, nid, dev)) + continue; + switch (dev) { case AC_JACK_LINE_OUT: seq = get_defcfg_sequence(def_conf); @@ -363,7 +392,7 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec, { unsigned int def_conf; static const char * const mic_names[] = { - "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic", + "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic" }; int attr; @@ -394,6 +423,8 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec, return "SPDIF In"; case AC_JACK_DIG_OTHER_IN: return "Digital In"; + case AC_JACK_HP_OUT: + return "Headphone Mic"; default: return "Misc"; } @@ -552,6 +583,9 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, return 1; } +#define is_hdmi_cfg(conf) \ + (get_defcfg_location(conf) == AC_JACK_LOC_HDMI) + /** * snd_hda_get_pin_label - Get a label for the given I/O pin * @@ -572,6 +606,7 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); const char *name = NULL; int i; + bool hdmi; if (indexp) *indexp = 0; @@ -590,16 +625,18 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, 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; - } + hdmi = is_hdmi_cfg(def_conf); + name = hdmi ? "HDMI" : "SPDIF"; + if (cfg && indexp) + for (i = 0; i < cfg->dig_outs; i++) { + hda_nid_t pin = cfg->dig_out_pins[i]; + unsigned int c; + if (pin == nid) + break; + c = snd_hda_codec_get_pincfg(codec, pin); + if (hdmi == is_hdmi_cfg(c)) + (*indexp)++; + } break; default: if (cfg) { @@ -622,28 +659,27 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_pin_label); -int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, - const struct hda_verb *list) +int snd_hda_add_verbs(struct hda_codec *codec, + const struct hda_verb *list) { const struct hda_verb **v; - v = snd_array_new(&spec->verbs); + v = snd_array_new(&codec->verbs); if (!v) return -ENOMEM; *v = list; return 0; } -EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs); +EXPORT_SYMBOL_HDA(snd_hda_add_verbs); -void snd_hda_gen_apply_verbs(struct hda_codec *codec) +void snd_hda_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); + for (i = 0; i < codec->verbs.used; i++) { + struct hda_verb **v = snd_array_elem(&codec->verbs, i); snd_hda_sequence_write(codec, *v); } } -EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs); +EXPORT_SYMBOL_HDA(snd_hda_apply_verbs); void snd_hda_apply_pincfgs(struct hda_codec *codec, const struct hda_pintbl *cfg) @@ -653,20 +689,26 @@ void snd_hda_apply_pincfgs(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs); +static void set_pin_targets(struct hda_codec *codec, + const struct hda_pintbl *cfg) +{ + for (; cfg->nid; cfg++) + snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val); +} + void snd_hda_apply_fixup(struct hda_codec *codec, int action) { - struct hda_gen_spec *spec = codec->spec; - int id = spec->fixup_id; + int id = codec->fixup_id; #ifdef CONFIG_SND_DEBUG_VERBOSE - const char *modelname = spec->fixup_name; + const char *modelname = codec->fixup_name; #endif int depth = 0; - if (!spec->fixup_list) + if (!codec->fixup_list) return; while (id >= 0) { - const struct hda_fixup *fix = spec->fixup_list + id; + const struct hda_fixup *fix = codec->fixup_list + id; switch (fix->type) { case HDA_FIXUP_PINS: @@ -683,7 +725,7 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action) 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); + snd_hda_add_verbs(codec, fix->v.verbs); break; case HDA_FIXUP_FUNC: if (!fix->v.func) @@ -693,6 +735,14 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action) codec->chip_name, modelname); fix->v.func(codec, fix, action); break; + case HDA_FIXUP_PINCTLS: + if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins) + break; + snd_printdd(KERN_INFO SFX + "%s: Apply pinctl for %s\n", + codec->chip_name, modelname); + set_pin_targets(codec, fix->v.pins); + break; default: snd_printk(KERN_ERR SFX "%s: Invalid fixup type %d\n", @@ -713,15 +763,14 @@ void snd_hda_pick_fixup(struct hda_codec *codec, 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; + codec->fixup_list = NULL; + codec->fixup_id = -1; return; } @@ -759,10 +808,10 @@ void snd_hda_pick_fixup(struct hda_codec *codec, } } - spec->fixup_id = id; + codec->fixup_id = id; if (id >= 0) { - spec->fixup_list = fixlist; - spec->fixup_name = name; + codec->fixup_list = fixlist; + codec->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 index 632ad0ad3007..f74807138b49 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -51,8 +51,9 @@ enum { 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 */ + INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ + INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT, }; int snd_hda_get_input_pin_attr(unsigned int def_conf); @@ -89,82 +90,4 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, #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); - -static inline void snd_hda_gen_init(struct hda_gen_spec *spec) -{ - snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8); -} - -static inline void snd_hda_gen_free(struct hda_gen_spec *spec) -{ - snd_array_free(&spec->verbs); -} - #endif /* __SOUND_HDA_AUTO_PARSER_H */ diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b8fb0a5adb9b..77ddd34fffed 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -222,8 +222,14 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, again: snd_hda_power_up(codec); mutex_lock(&bus->cmd_mutex); - trace_hda_send_cmd(codec, cmd); - err = bus->ops.command(bus, cmd); + for (;;) { + trace_hda_send_cmd(codec, cmd); + err = bus->ops.command(bus, cmd); + if (err != -EAGAIN) + break; + /* process pending verbs */ + bus->ops.get_response(bus, codec->addr); + } if (!err && res) { *res = bus->ops.get_response(bus, codec->addr); trace_hda_get_response(codec, *res); @@ -328,20 +334,51 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); +/* connection list element */ +struct hda_conn_list { + struct list_head list; + int len; + hda_nid_t nid; + hda_nid_t conns[0]; +}; + /* look up the cached results */ -static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid) +static struct hda_conn_list * +lookup_conn_list(struct hda_codec *codec, hda_nid_t nid) { - int i, len; - for (i = 0; i < array->used; ) { - hda_nid_t *p = snd_array_elem(array, i); - if (nid == *p) + struct hda_conn_list *p; + list_for_each_entry(p, &codec->conn_list, list) { + if (p->nid == nid) return p; - len = p[1]; - i += len + 2; } return NULL; } +static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, + const hda_nid_t *list) +{ + struct hda_conn_list *p; + + p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL); + if (!p) + return -ENOMEM; + p->len = len; + p->nid = nid; + memcpy(p->conns, list, len * sizeof(hda_nid_t)); + list_add(&p->list, &codec->conn_list); + return 0; +} + +static void remove_conn_list(struct hda_codec *codec) +{ + while (!list_empty(&codec->conn_list)) { + struct hda_conn_list *p; + p = list_first_entry(&codec->conn_list, typeof(*p), list); + list_del(&p->list); + kfree(p); + } +} + /* read the connection and add to the cache */ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) { @@ -355,6 +392,49 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) } /** + * snd_hda_get_conn_list - get connection list + * @codec: the HDA codec + * @nid: NID to parse + * @len: number of connection list entries + * @listp: the pointer to store NID list + * + * Parses the connection list of the given widget and stores the pointer + * to the list of NIDs. + * + * Returns the number of connections, or a negative error code. + * + * Note that the returned pointer isn't protected against the list + * modification. If snd_hda_override_conn_list() might be called + * concurrently, protect with a mutex appropriately. + */ +int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t **listp) +{ + bool added = false; + + for (;;) { + int err; + const struct hda_conn_list *p; + + /* if the connection-list is already cached, read it */ + p = lookup_conn_list(codec, nid); + if (p) { + if (listp) + *listp = p->conns; + return p->len; + } + if (snd_BUG_ON(added)) + return -EINVAL; + + err = read_and_add_raw_conns(codec, nid); + if (err < 0) + return err; + added = true; + } +} +EXPORT_SYMBOL_HDA(snd_hda_get_conn_list); + +/** * snd_hda_get_connections - copy connection list * @codec: the HDA codec * @nid: NID to parse @@ -369,39 +449,20 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns) { - struct snd_array *array = &codec->conn_lists; - int len; - hda_nid_t *p; - bool added = false; + const hda_nid_t *list; + int len = snd_hda_get_conn_list(codec, nid, &list); - again: - mutex_lock(&codec->hash_mutex); - len = -1; - /* if the connection-list is already cached, read it */ - p = lookup_conn_list(array, nid); - if (p) { - len = p[1]; - if (conn_list && len > max_conns) { + if (len > 0 && conn_list) { + if (len > max_conns) { snd_printk(KERN_ERR "hda_codec: " "Too many connections %d for NID 0x%x\n", len, nid); - mutex_unlock(&codec->hash_mutex); return -EINVAL; } - if (conn_list && len) - memcpy(conn_list, p + 2, len * sizeof(hda_nid_t)); + memcpy(conn_list, list, len * sizeof(hda_nid_t)); } - mutex_unlock(&codec->hash_mutex); - if (len >= 0) - return len; - if (snd_BUG_ON(added)) - return -EINVAL; - len = read_and_add_raw_conns(codec, nid); - if (len < 0) - return len; - added = true; - goto again; + return len; } EXPORT_SYMBOL_HDA(snd_hda_get_connections); @@ -424,6 +485,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, unsigned int shift, num_elems, mask; unsigned int wcaps; hda_nid_t prev_nid; + int null_count = 0; if (snd_BUG_ON(!conn_list || max_conns <= 0)) return -EINVAL; @@ -474,7 +536,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, } range_val = !!(parm & (1 << (shift-1))); /* ranges */ val = parm & mask; - if (val == 0) { + if (val == 0 && null_count++) { /* no second chance */ snd_printk(KERN_WARNING "hda_codec: " "invalid CONNECT_LIST verb %x[%i]:%x\n", nid, i, parm); @@ -512,15 +574,6 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, return conns; } -static bool add_conn_list(struct snd_array *array, hda_nid_t nid) -{ - hda_nid_t *p = snd_array_new(array); - if (!p) - return false; - *p = nid; - return true; -} - /** * snd_hda_override_conn_list - add/modify the connection-list to cache * @codec: the HDA codec @@ -536,28 +589,15 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid) int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, const hda_nid_t *list) { - struct snd_array *array = &codec->conn_lists; - hda_nid_t *p; - int i, old_used; + struct hda_conn_list *p; - mutex_lock(&codec->hash_mutex); - p = lookup_conn_list(array, nid); - if (p) - *p = -1; /* invalidate the old entry */ - - old_used = array->used; - if (!add_conn_list(array, nid) || !add_conn_list(array, len)) - goto error_add; - for (i = 0; i < len; i++) - if (!add_conn_list(array, list[i])) - goto error_add; - mutex_unlock(&codec->hash_mutex); - return 0; + p = lookup_conn_list(codec, nid); + if (p) { + list_del(&p->list); + kfree(p); + } - error_add: - array->used = old_used; - mutex_unlock(&codec->hash_mutex); - return -ENOMEM; + return add_conn_list(codec, nid, len, list); } EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); @@ -575,16 +615,16 @@ EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive) { - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + const hda_nid_t *conn; int i, nums; - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + nums = snd_hda_get_conn_list(codec, mux, &conn); for (i = 0; i < nums; i++) if (conn[i] == nid) return i; if (!recursive) return -1; - if (recursive > 5) { + if (recursive > 10) { snd_printd("hda_codec: too deep connection for 0x%x\n", nid); return -1; } @@ -1046,9 +1086,16 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) struct hda_pincfg *pin; #ifdef CONFIG_SND_HDA_HWDEP - pin = look_up_pincfg(codec, &codec->user_pins, nid); - if (pin) - return pin->cfg; + { + unsigned int cfg = 0; + mutex_lock(&codec->user_mutex); + pin = look_up_pincfg(codec, &codec->user_pins, nid); + if (pin) + cfg = pin->cfg; + mutex_unlock(&codec->user_mutex); + if (cfg) + return cfg; + } #endif pin = look_up_pincfg(codec, &codec->driver_pins, nid); if (pin) @@ -1060,6 +1107,32 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg); +/* remember the current pinctl target value */ +int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, + unsigned int val) +{ + struct hda_pincfg *pin; + + pin = look_up_pincfg(codec, &codec->init_pins, nid); + if (!pin) + return -EINVAL; + pin->target = val; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_codec_set_pin_target); + +/* return the current pinctl target value */ +int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_pincfg *pin; + + pin = look_up_pincfg(codec, &codec->init_pins, nid); + if (!pin) + return 0; + return pin->target; +} +EXPORT_SYMBOL_HDA(snd_hda_codec_get_pin_target); + /** * snd_hda_shutup_pins - Shut up all pins * @codec: the HDA codec @@ -1179,8 +1252,8 @@ static void snd_hda_codec_free(struct hda_codec *codec) snd_array_free(&codec->mixers); snd_array_free(&codec->nids); snd_array_free(&codec->cvt_setups); - snd_array_free(&codec->conn_lists); snd_array_free(&codec->spdif_out); + remove_conn_list(codec); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); @@ -1250,9 +1323,11 @@ int snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); 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); snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); + snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); + INIT_LIST_HEAD(&codec->conn_list); + INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); #ifdef CONFIG_PM @@ -1451,7 +1526,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", nid, stream_tag, channel_id, format); p = get_hda_cvt_setup(codec, nid); - if (!p) + if (!p || p->active) return; if (codec->pcm_format_first) @@ -1498,7 +1573,7 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); p = get_hda_cvt_setup(codec, nid); - if (p) { + if (p && p->active) { /* here we just clear the active flag when do_now isn't set; * actual clean-ups will be done later in * purify_inactive_streams() called from snd_hda_codec_prpapre() @@ -1610,6 +1685,7 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, cur = snd_array_index(&cache->buf, info); info->key = key; info->val = 0; + info->dirty = 0; idx = key % (u16)ARRAY_SIZE(cache->hash); info->next = cache->hash[idx]; cache->hash[idx] = cur; @@ -1764,7 +1840,7 @@ EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps); */ static struct hda_amp_info * update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index) + int direction, int index, bool init_only) { struct hda_amp_info *info; unsigned int parm, val = 0; @@ -1790,14 +1866,15 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, } info->vol[ch] = val; info->head.val |= INFO_AMP_VOL(ch); - } + } else if (init_only) + return NULL; return info; } /* * write the current volume in info to the h/w */ -static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, +static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps, hda_nid_t nid, int ch, int direction, int index, int val) { @@ -1806,8 +1883,8 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT; parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT; parm |= index << AC_AMP_SET_INDEX_SHIFT; - if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) && - (info->amp_caps & AC_AMPCAP_MIN_MUTE)) + if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) && + (amp_caps & AC_AMPCAP_MIN_MUTE)) ; /* set the zero value as a fake mute */ else parm |= val; @@ -1831,7 +1908,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, unsigned int val = 0; mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, index); + info = update_amp_hash(codec, nid, ch, direction, index, false); if (info) val = info->vol[ch]; mutex_unlock(&codec->hash_mutex); @@ -1839,30 +1916,20 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); -/** - * snd_hda_codec_amp_update - update the AMP value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Update the AMP value with a bit mask. - * Returns 0 if the value is unchanged, 1 if changed. - */ -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val) +static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val, + bool init_only) { struct hda_amp_info *info; + unsigned int caps; + unsigned int cache_only; if (snd_BUG_ON(mask & ~0xff)) mask &= 0xff; val &= mask; mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, idx); + info = update_amp_hash(codec, nid, ch, direction, idx, init_only); if (!info) { mutex_unlock(&codec->hash_mutex); return 0; @@ -1873,10 +1940,32 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, return 0; } info->vol[ch] = val; + cache_only = info->head.dirty = codec->cached_write; + caps = info->amp_caps; mutex_unlock(&codec->hash_mutex); - put_vol_mute(codec, info, nid, ch, direction, idx, val); + if (!cache_only) + put_vol_mute(codec, caps, nid, ch, direction, idx, val); return 1; } + +/** + * snd_hda_codec_amp_update - update the AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP value with a bit mask. + * Returns 0 if the value is unchanged, 1 if changed. + */ +int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val) +{ + return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false); +} EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); /** @@ -1905,7 +1994,31 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); -#ifdef CONFIG_PM +/* Works like snd_hda_codec_amp_update() but it writes the value only at + * the first access. If the amp was already initialized / updated beforehand, + * this does nothing. + */ +int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, + int dir, int idx, int mask, int val) +{ + return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true); +} +EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init); + +int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx, int mask, int val) +{ + int ch, ret = 0; + + if (snd_BUG_ON(mask & ~0xff)) + mask &= 0xff; + for (ch = 0; ch < 2; ch++) + ret |= snd_hda_codec_amp_init(codec, nid, ch, dir, + idx, mask, val); + return ret; +} +EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init_stereo); + /** * snd_hda_codec_resume_amp - Resume all AMP commands from the cache * @codec: HD-audio codec @@ -1914,28 +2027,40 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); */ void snd_hda_codec_resume_amp(struct hda_codec *codec) { - struct hda_amp_info *buffer = codec->amp_cache.buf.list; int i; - for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) { - u32 key = buffer->head.key; + mutex_lock(&codec->hash_mutex); + codec->cached_write = 0; + for (i = 0; i < codec->amp_cache.buf.used; i++) { + struct hda_amp_info *buffer; + u32 key; hda_nid_t nid; unsigned int idx, dir, ch; + struct hda_amp_info info; + + buffer = snd_array_elem(&codec->amp_cache.buf, i); + if (!buffer->head.dirty) + continue; + buffer->head.dirty = 0; + info = *buffer; + key = info.head.key; if (!key) continue; nid = key & 0xff; idx = (key >> 16) & 0xff; dir = (key >> 24) & 0xff; for (ch = 0; ch < 2; ch++) { - if (!(buffer->head.val & INFO_AMP_VOL(ch))) + if (!(info.head.val & INFO_AMP_VOL(ch))) continue; - put_vol_mute(codec, buffer, nid, ch, dir, idx, - buffer->vol[ch]); + mutex_unlock(&codec->hash_mutex); + put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx, + info.vol[ch]); |