11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Universal Interface for Intel High Definition Audio Codec 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Generic widget tree parser 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This driver is free software; you can redistribute it and/or modify 91da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 101da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 111da177e4SLinus Torvalds * (at your option) any later version. 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * This driver is distributed in the hope that it will be useful, 141da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 151da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 161da177e4SLinus Torvalds * GNU General Public License for more details. 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 191da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 201da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 211da177e4SLinus Torvalds */ 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds #include <linux/init.h> 241da177e4SLinus Torvalds #include <linux/slab.h> 25d81a6d71SPaul Gortmaker #include <linux/export.h> 26352f7f91STakashi Iwai #include <linux/sort.h> 27f873e536STakashi Iwai #include <linux/ctype.h> 28f873e536STakashi Iwai #include <linux/string.h> 291da177e4SLinus Torvalds #include <sound/core.h> 30352f7f91STakashi Iwai #include <sound/jack.h> 311da177e4SLinus Torvalds #include "hda_codec.h" 321da177e4SLinus Torvalds #include "hda_local.h" 33352f7f91STakashi Iwai #include "hda_auto_parser.h" 34352f7f91STakashi Iwai #include "hda_jack.h" 35352f7f91STakashi Iwai #include "hda_generic.h" 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds 38352f7f91STakashi Iwai /* initialize hda_gen_spec struct */ 39352f7f91STakashi Iwai int snd_hda_gen_spec_init(struct hda_gen_spec *spec) 401da177e4SLinus Torvalds { 41352f7f91STakashi Iwai snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); 42352f7f91STakashi Iwai snd_array_init(&spec->paths, sizeof(struct nid_path), 8); 4338cf6f1aSTakashi Iwai mutex_init(&spec->pcm_mutex); 44352f7f91STakashi Iwai return 0; 45352f7f91STakashi Iwai } 46352f7f91STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init); 471da177e4SLinus Torvalds 4812c93df6STakashi Iwai struct snd_kcontrol_new * 4912c93df6STakashi Iwai snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, 50352f7f91STakashi Iwai const struct snd_kcontrol_new *temp) 51352f7f91STakashi Iwai { 52352f7f91STakashi Iwai struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); 53352f7f91STakashi Iwai if (!knew) 54352f7f91STakashi Iwai return NULL; 55352f7f91STakashi Iwai *knew = *temp; 56352f7f91STakashi Iwai if (name) 57352f7f91STakashi Iwai knew->name = kstrdup(name, GFP_KERNEL); 58352f7f91STakashi Iwai else if (knew->name) 59352f7f91STakashi Iwai knew->name = kstrdup(knew->name, GFP_KERNEL); 60352f7f91STakashi Iwai if (!knew->name) 61352f7f91STakashi Iwai return NULL; 62352f7f91STakashi Iwai return knew; 63352f7f91STakashi Iwai } 6412c93df6STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_add_kctl); 65352f7f91STakashi Iwai 66352f7f91STakashi Iwai static void free_kctls(struct hda_gen_spec *spec) 67352f7f91STakashi Iwai { 68352f7f91STakashi Iwai if (spec->kctls.list) { 69352f7f91STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 70352f7f91STakashi Iwai int i; 71352f7f91STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 72352f7f91STakashi Iwai kfree(kctl[i].name); 73352f7f91STakashi Iwai } 74352f7f91STakashi Iwai snd_array_free(&spec->kctls); 75352f7f91STakashi Iwai } 76352f7f91STakashi Iwai 77352f7f91STakashi Iwai void snd_hda_gen_spec_free(struct hda_gen_spec *spec) 78352f7f91STakashi Iwai { 791da177e4SLinus Torvalds if (!spec) 801da177e4SLinus Torvalds return; 81352f7f91STakashi Iwai free_kctls(spec); 82352f7f91STakashi Iwai snd_array_free(&spec->paths); 831da177e4SLinus Torvalds } 84352f7f91STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds /* 87352f7f91STakashi Iwai * parsing paths 881da177e4SLinus Torvalds */ 891da177e4SLinus Torvalds 90f5172a7eSTakashi Iwai static struct nid_path *get_nid_path(struct hda_codec *codec, 91f5172a7eSTakashi Iwai hda_nid_t from_nid, hda_nid_t to_nid, 92f5172a7eSTakashi Iwai int with_aa_mix) 931da177e4SLinus Torvalds { 94352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 95352f7f91STakashi Iwai int i; 961da177e4SLinus Torvalds 97352f7f91STakashi Iwai for (i = 0; i < spec->paths.used; i++) { 98352f7f91STakashi Iwai struct nid_path *path = snd_array_elem(&spec->paths, i); 99352f7f91STakashi Iwai if (path->depth <= 0) 100352f7f91STakashi Iwai continue; 101352f7f91STakashi Iwai if ((!from_nid || path->path[0] == from_nid) && 102f5172a7eSTakashi Iwai (!to_nid || path->path[path->depth - 1] == to_nid)) { 103f5172a7eSTakashi Iwai if (with_aa_mix == HDA_PARSE_ALL || 104f5172a7eSTakashi Iwai path->with_aa_mix == with_aa_mix) 105352f7f91STakashi Iwai return path; 1061da177e4SLinus Torvalds } 107f5172a7eSTakashi Iwai } 1081da177e4SLinus Torvalds return NULL; 1091da177e4SLinus Torvalds } 110f5172a7eSTakashi Iwai 111f5172a7eSTakashi Iwai /* get the path between the given NIDs; 112f5172a7eSTakashi Iwai * passing 0 to either @pin or @dac behaves as a wildcard 113f5172a7eSTakashi Iwai */ 114f5172a7eSTakashi Iwai struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, 115f5172a7eSTakashi Iwai hda_nid_t from_nid, hda_nid_t to_nid) 116f5172a7eSTakashi Iwai { 117f5172a7eSTakashi Iwai return get_nid_path(codec, from_nid, to_nid, HDA_PARSE_ALL); 118f5172a7eSTakashi Iwai } 119352f7f91STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_get_nid_path); 1201da177e4SLinus Torvalds 121196c1766STakashi Iwai /* get the index number corresponding to the path instance; 122196c1766STakashi Iwai * the index starts from 1, for easier checking the invalid value 123196c1766STakashi Iwai */ 124196c1766STakashi Iwai int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) 125196c1766STakashi Iwai { 126196c1766STakashi Iwai struct hda_gen_spec *spec = codec->spec; 127196c1766STakashi Iwai struct nid_path *array = spec->paths.list; 128196c1766STakashi Iwai ssize_t idx; 129196c1766STakashi Iwai 130196c1766STakashi Iwai if (!spec->paths.used) 131196c1766STakashi Iwai return 0; 132196c1766STakashi Iwai idx = path - array; 133196c1766STakashi Iwai if (idx < 0 || idx >= spec->paths.used) 134196c1766STakashi Iwai return 0; 135196c1766STakashi Iwai return idx + 1; 136196c1766STakashi Iwai } 137196c1766STakashi Iwai 138196c1766STakashi Iwai /* get the path instance corresponding to the given index number */ 139196c1766STakashi Iwai struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) 140196c1766STakashi Iwai { 141196c1766STakashi Iwai struct hda_gen_spec *spec = codec->spec; 142196c1766STakashi Iwai 143196c1766STakashi Iwai if (idx <= 0 || idx > spec->paths.used) 144196c1766STakashi Iwai return NULL; 145196c1766STakashi Iwai return snd_array_elem(&spec->paths, idx - 1); 146196c1766STakashi Iwai } 147196c1766STakashi Iwai 148352f7f91STakashi Iwai /* check whether the given DAC is already found in any existing paths */ 149352f7f91STakashi Iwai static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) 1501da177e4SLinus Torvalds { 151352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 152352f7f91STakashi Iwai int i; 153352f7f91STakashi Iwai 154352f7f91STakashi Iwai for (i = 0; i < spec->paths.used; i++) { 155352f7f91STakashi Iwai struct nid_path *path = snd_array_elem(&spec->paths, i); 156352f7f91STakashi Iwai if (path->path[0] == nid) 157352f7f91STakashi Iwai return true; 158352f7f91STakashi Iwai } 159352f7f91STakashi Iwai return false; 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds 162352f7f91STakashi Iwai /* check whether the given two widgets can be connected */ 163352f7f91STakashi Iwai static bool is_reachable_path(struct hda_codec *codec, 164352f7f91STakashi Iwai hda_nid_t from_nid, hda_nid_t to_nid) 1651da177e4SLinus Torvalds { 166352f7f91STakashi Iwai if (!from_nid || !to_nid) 167352f7f91STakashi Iwai return false; 168352f7f91STakashi Iwai return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds 171352f7f91STakashi Iwai /* nid, dir and idx */ 172352f7f91STakashi Iwai #define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19)) 173352f7f91STakashi Iwai 174352f7f91STakashi Iwai /* check whether the given ctl is already assigned in any path elements */ 175352f7f91STakashi Iwai static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) 1761da177e4SLinus Torvalds { 177352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 178352f7f91STakashi Iwai int i; 179352f7f91STakashi Iwai 180352f7f91STakashi Iwai val &= AMP_VAL_COMPARE_MASK; 181352f7f91STakashi Iwai for (i = 0; i < spec->paths.used; i++) { 182352f7f91STakashi Iwai struct nid_path *path = snd_array_elem(&spec->paths, i); 183352f7f91STakashi Iwai if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val) 184352f7f91STakashi Iwai return true; 185352f7f91STakashi Iwai } 186352f7f91STakashi Iwai return false; 1871da177e4SLinus Torvalds } 1881da177e4SLinus Torvalds 189352f7f91STakashi Iwai /* check whether a control with the given (nid, dir, idx) was assigned */ 190352f7f91STakashi Iwai static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, 191cb53c626STakashi Iwai int dir, int idx) 192cb53c626STakashi Iwai { 193352f7f91STakashi Iwai unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); 194352f7f91STakashi Iwai return is_ctl_used(codec, val, NID_PATH_VOL_CTL) || 195352f7f91STakashi Iwai is_ctl_used(codec, val, NID_PATH_MUTE_CTL); 196cb53c626STakashi Iwai } 197352f7f91STakashi Iwai 1980c8c0f56STakashi Iwai static void print_nid_path(const char *pfx, struct nid_path *path) 1990c8c0f56STakashi Iwai { 2000c8c0f56STakashi Iwai char buf[40]; 2010c8c0f56STakashi Iwai int i; 2020c8c0f56STakashi Iwai 2030c8c0f56STakashi Iwai 2040c8c0f56STakashi Iwai buf[0] = 0; 2050c8c0f56STakashi Iwai for (i = 0; i < path->depth; i++) { 2060c8c0f56STakashi Iwai char tmp[4]; 2070c8c0f56STakashi Iwai sprintf(tmp, ":%02x", path->path[i]); 2080c8c0f56STakashi Iwai strlcat(buf, tmp, sizeof(buf)); 2090c8c0f56STakashi Iwai } 2100c8c0f56STakashi Iwai snd_printdd("%s path: depth=%d %s\n", pfx, path->depth, buf); 2110c8c0f56STakashi Iwai } 2120c8c0f56STakashi Iwai 213352f7f91STakashi Iwai /* called recursively */ 214352f7f91STakashi Iwai static bool __parse_nid_path(struct hda_codec *codec, 215352f7f91STakashi Iwai hda_nid_t from_nid, hda_nid_t to_nid, 216352f7f91STakashi Iwai int with_aa_mix, struct nid_path *path, int depth) 217352f7f91STakashi Iwai { 218352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 219ee8e765bSTakashi Iwai const hda_nid_t *conn; 220352f7f91STakashi Iwai int i, nums; 221352f7f91STakashi Iwai 222352f7f91STakashi Iwai if (to_nid == spec->mixer_nid) { 2234ac0eefaSTakashi Iwai if (with_aa_mix == HDA_PARSE_NO_AAMIX) 224352f7f91STakashi Iwai return false; 2254ac0eefaSTakashi Iwai with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */ 226352f7f91STakashi Iwai } 227352f7f91STakashi Iwai 228ee8e765bSTakashi Iwai nums = snd_hda_get_conn_list(codec, to_nid, &conn); 229352f7f91STakashi Iwai for (i = 0; i < nums; i++) { 230352f7f91STakashi Iwai if (conn[i] != from_nid) { 231352f7f91STakashi Iwai /* special case: when from_nid is 0, 232352f7f91STakashi Iwai * try to find an empty DAC 233352f7f91STakashi Iwai */ 234352f7f91STakashi Iwai if (from_nid || 235352f7f91STakashi Iwai get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || 236352f7f91STakashi Iwai is_dac_already_used(codec, conn[i])) 237352f7f91STakashi Iwai continue; 238352f7f91STakashi Iwai } 239352f7f91STakashi Iwai /* aa-mix is requested but not included? */ 2404ac0eefaSTakashi Iwai if (!(spec->mixer_nid && with_aa_mix == HDA_PARSE_ONLY_AAMIX)) 241352f7f91STakashi Iwai goto found; 242352f7f91STakashi Iwai } 243352f7f91STakashi Iwai if (depth >= MAX_NID_PATH_DEPTH) 244352f7f91STakashi Iwai return false; 245352f7f91STakashi Iwai for (i = 0; i < nums; i++) { 246352f7f91STakashi Iwai unsigned int type; 247352f7f91STakashi Iwai type = get_wcaps_type(get_wcaps(codec, conn[i])); 248352f7f91STakashi Iwai if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || 249352f7f91STakashi Iwai type == AC_WID_PIN) 250352f7f91STakashi Iwai continue; 251352f7f91STakashi Iwai if (__parse_nid_path(codec, from_nid, conn[i], 252352f7f91STakashi Iwai with_aa_mix, path, depth + 1)) 253352f7f91STakashi Iwai goto found; 254352f7f91STakashi Iwai } 255352f7f91STakashi Iwai return false; 256352f7f91STakashi Iwai 257352f7f91STakashi Iwai found: 258352f7f91STakashi Iwai path->path[path->depth] = conn[i]; 259f5172a7eSTakashi Iwai if (conn[i] == spec->mixer_nid) 260f5172a7eSTakashi Iwai path->with_aa_mix = true; 261352f7f91STakashi Iwai path->idx[path->depth + 1] = i; 262352f7f91STakashi Iwai if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) 263352f7f91STakashi Iwai path->multi[path->depth + 1] = 1; 264352f7f91STakashi Iwai path->depth++; 265352f7f91STakashi Iwai return true; 266352f7f91STakashi Iwai } 267352f7f91STakashi Iwai 268352f7f91STakashi Iwai /* parse the widget path from the given nid to the target nid; 269352f7f91STakashi Iwai * when @from_nid is 0, try to find an empty DAC; 2704ac0eefaSTakashi Iwai * when @with_aa_mix is HDA_PARSE_NO_AAMIX, paths with spec->mixer_nid are 2714ac0eefaSTakashi Iwai * excluded, only the paths that don't go through the mixer will be chosen. 2724ac0eefaSTakashi Iwai * when @with_aa_mix is HDA_PARSE_ONLY_AAMIX, only the paths going through 2734ac0eefaSTakashi Iwai * spec->mixer_nid will be chosen. 2744ac0eefaSTakashi Iwai * when @with_aa_mix is HDA_PARSE_ALL, no special handling about mixer widget. 275352f7f91STakashi Iwai */ 276352f7f91STakashi Iwai bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, 277352f7f91STakashi Iwai hda_nid_t to_nid, int with_aa_mix, 278352f7f91STakashi Iwai struct nid_path *path) 279352f7f91STakashi Iwai { 280352f7f91STakashi Iwai if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) { 281352f7f91STakashi Iwai path->path[path->depth] = to_nid; 282352f7f91STakashi Iwai path->depth++; 283352f7f91STakashi Iwai return true; 284352f7f91STakashi Iwai } 285352f7f91STakashi Iwai return false; 286352f7f91STakashi Iwai } 287352f7f91STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_parse_nid_path); 288352f7f91STakashi Iwai 289352f7f91STakashi Iwai /* 290352f7f91STakashi Iwai * parse the path between the given NIDs and add to the path list. 291352f7f91STakashi Iwai * if no valid path is found, return NULL 292352f7f91STakashi Iwai */ 293352f7f91STakashi Iwai struct nid_path * 294352f7f91STakashi Iwai snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, 295352f7f91STakashi Iwai hda_nid_t to_nid, int with_aa_mix) 296352f7f91STakashi Iwai { 297352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 298352f7f91STakashi Iwai struct nid_path *path; 299352f7f91STakashi Iwai 300352f7f91STakashi Iwai if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) 301352f7f91STakashi Iwai return NULL; 302352f7f91STakashi Iwai 303f5172a7eSTakashi Iwai /* check whether the path has been already added */ 304f5172a7eSTakashi Iwai path = get_nid_path(codec, from_nid, to_nid, with_aa_mix); 305f5172a7eSTakashi Iwai if (path) 306f5172a7eSTakashi Iwai return path; 307f5172a7eSTakashi Iwai 308352f7f91STakashi Iwai path = snd_array_new(&spec->paths); 309352f7f91STakashi Iwai if (!path) 310352f7f91STakashi Iwai return NULL; 311352f7f91STakashi Iwai memset(path, 0, sizeof(*path)); 312352f7f91STakashi Iwai if (snd_hda_parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path)) 313352f7f91STakashi Iwai return path; 314352f7f91STakashi Iwai /* push back */ 315352f7f91STakashi Iwai spec->paths.used--; 316352f7f91STakashi Iwai return NULL; 317352f7f91STakashi Iwai } 318352f7f91STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_add_new_path); 319352f7f91STakashi Iwai 320352f7f91STakashi Iwai /* look for an empty DAC slot */ 321352f7f91STakashi Iwai static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin, 322352f7f91STakashi Iwai bool is_digital) 323352f7f91STakashi Iwai { 324352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 325352f7f91STakashi Iwai bool cap_digital; 326352f7f91STakashi Iwai int i; 327352f7f91STakashi Iwai 328352f7f91STakashi Iwai for (i = 0; i < spec->num_all_dacs; i++) { 329352f7f91STakashi Iwai hda_nid_t nid = spec->all_dacs[i]; 330352f7f91STakashi Iwai if (!nid || is_dac_already_used(codec, nid)) 331352f7f91STakashi Iwai continue; 332352f7f91STakashi Iwai cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); 333352f7f91STakashi Iwai if (is_digital != cap_digital) 334352f7f91STakashi Iwai continue; 335352f7f91STakashi Iwai if (is_reachable_path(codec, nid, pin)) 336352f7f91STakashi Iwai return nid; 337352f7f91STakashi Iwai } 338352f7f91STakashi Iwai return 0; 339352f7f91STakashi Iwai } 340352f7f91STakashi Iwai 341352f7f91STakashi Iwai /* replace the channels in the composed amp value with the given number */ 342352f7f91STakashi Iwai static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) 343352f7f91STakashi Iwai { 344352f7f91STakashi Iwai val &= ~(0x3U << 16); 345352f7f91STakashi Iwai val |= chs << 16; 346352f7f91STakashi Iwai return val; 347352f7f91STakashi Iwai } 348352f7f91STakashi Iwai 349352f7f91STakashi Iwai /* check whether the widget has the given amp capability for the direction */ 350352f7f91STakashi Iwai static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, 351352f7f91STakashi Iwai int dir, unsigned int bits) 352352f7f91STakashi Iwai { 353352f7f91STakashi Iwai if (!nid) 354352f7f91STakashi Iwai return false; 355352f7f91STakashi Iwai if (get_wcaps(codec, nid) & (1 << (dir + 1))) 356352f7f91STakashi Iwai if (query_amp_caps(codec, nid, dir) & bits) 357352f7f91STakashi Iwai return true; 358352f7f91STakashi Iwai return false; 359352f7f91STakashi Iwai } 360352f7f91STakashi Iwai 361352f7f91STakashi Iwai #define nid_has_mute(codec, nid, dir) \ 362352f7f91STakashi Iwai check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) 363352f7f91STakashi Iwai #define nid_has_volume(codec, nid, dir) \ 364352f7f91STakashi Iwai check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) 365352f7f91STakashi Iwai 366352f7f91STakashi Iwai /* look for a widget suitable for assigning a mute switch in the path */ 367352f7f91STakashi Iwai static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec, 368352f7f91STakashi Iwai struct nid_path *path) 369352f7f91STakashi Iwai { 370352f7f91STakashi Iwai int i; 371352f7f91STakashi Iwai 372352f7f91STakashi Iwai for (i = path->depth - 1; i >= 0; i--) { 373352f7f91STakashi Iwai if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) 374352f7f91STakashi Iwai return path->path[i]; 375352f7f91STakashi Iwai if (i != path->depth - 1 && i != 0 && 376352f7f91STakashi Iwai nid_has_mute(codec, path->path[i], HDA_INPUT)) 377352f7f91STakashi Iwai return path->path[i]; 378352f7f91STakashi Iwai } 379352f7f91STakashi Iwai return 0; 380352f7f91STakashi Iwai } 381352f7f91STakashi Iwai 382352f7f91STakashi Iwai /* look for a widget suitable for assigning a volume ctl in the path */ 383352f7f91STakashi Iwai static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec, 384352f7f91STakashi Iwai struct nid_path *path) 385352f7f91STakashi Iwai { 386352f7f91STakashi Iwai int i; 387352f7f91STakashi Iwai 388352f7f91STakashi Iwai for (i = path->depth - 1; i >= 0; i--) { 389352f7f91STakashi Iwai if (nid_has_volume(codec, path->path[i], HDA_OUTPUT)) 390352f7f91STakashi Iwai return path->path[i]; 391352f7f91STakashi Iwai } 392352f7f91STakashi Iwai return 0; 393352f7f91STakashi Iwai } 394352f7f91STakashi Iwai 395352f7f91STakashi Iwai /* 396352f7f91STakashi Iwai * path activation / deactivation 397352f7f91STakashi Iwai */ 398352f7f91STakashi Iwai 399352f7f91STakashi Iwai /* can have the amp-in capability? */ 400352f7f91STakashi Iwai static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) 401352f7f91STakashi Iwai { 402352f7f91STakashi Iwai hda_nid_t nid = path->path[idx]; 403352f7f91STakashi Iwai unsigned int caps = get_wcaps(codec, nid); 404352f7f91STakashi Iwai unsigned int type = get_wcaps_type(caps); 405352f7f91STakashi Iwai 406352f7f91STakashi Iwai if (!(caps & AC_WCAP_IN_AMP)) 407352f7f91STakashi Iwai return false; 408352f7f91STakashi Iwai if (type == AC_WID_PIN && idx > 0) /* only for input pins */ 409352f7f91STakashi Iwai return false; 410352f7f91STakashi Iwai return true; 411352f7f91STakashi Iwai } 412352f7f91STakashi Iwai 413352f7f91STakashi Iwai /* can have the amp-out capability? */ 414352f7f91STakashi Iwai static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) 415352f7f91STakashi Iwai { 416352f7f91STakashi Iwai hda_nid_t nid = path->path[idx]; 417352f7f91STakashi Iwai unsigned int caps = get_wcaps(codec, nid); 418352f7f91STakashi Iwai unsigned int type = get_wcaps_type(caps); 419352f7f91STakashi Iwai 420352f7f91STakashi Iwai if (!(caps & AC_WCAP_OUT_AMP)) 421352f7f91STakashi Iwai return false; 422352f7f91STakashi Iwai if (type == AC_WID_PIN && !idx) /* only for output pins */ 423352f7f91STakashi Iwai return false; 424352f7f91STakashi Iwai return true; 425352f7f91STakashi Iwai } 426352f7f91STakashi Iwai 427352f7f91STakashi Iwai /* check whether the given (nid,dir,idx) is active */ 428352f7f91STakashi Iwai static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, 429352f7f91STakashi Iwai unsigned int idx, unsigned int dir) 430352f7f91STakashi Iwai { 431352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 432352f7f91STakashi Iwai int i, n; 433352f7f91STakashi Iwai 434352f7f91STakashi Iwai for (n = 0; n < spec->paths.used; n++) { 435352f7f91STakashi Iwai struct nid_path *path = snd_array_elem(&spec->paths, n); 436352f7f91STakashi Iwai if (!path->active) 437352f7f91STakashi Iwai continue; 438352f7f91STakashi Iwai for (i = 0; i < path->depth; i++) { 439352f7f91STakashi Iwai if (path->path[i] == nid) { 440352f7f91STakashi Iwai if (dir == HDA_OUTPUT || path->idx[i] == idx) 441352f7f91STakashi Iwai return true; 442352f7f91STakashi Iwai break; 443352f7f91STakashi Iwai } 444352f7f91STakashi Iwai } 445352f7f91STakashi Iwai } 446352f7f91STakashi Iwai return false; 447352f7f91STakashi Iwai } 448352f7f91STakashi Iwai 449352f7f91STakashi Iwai /* get the default amp value for the target state */ 450352f7f91STakashi Iwai static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, 451352f7f91STakashi Iwai int dir, bool enable) 452352f7f91STakashi Iwai { 453352f7f91STakashi Iwai unsigned int caps; 454352f7f91STakashi Iwai unsigned int val = 0; 455352f7f91STakashi Iwai 456352f7f91STakashi Iwai caps = query_amp_caps(codec, nid, dir); 457352f7f91STakashi Iwai if (caps & AC_AMPCAP_NUM_STEPS) { 458352f7f91STakashi Iwai /* set to 0dB */ 459352f7f91STakashi Iwai if (enable) 460352f7f91STakashi Iwai val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; 461352f7f91STakashi Iwai } 462352f7f91STakashi Iwai if (caps & AC_AMPCAP_MUTE) { 463352f7f91STakashi Iwai if (!enable) 464352f7f91STakashi Iwai val |= HDA_AMP_MUTE; 465352f7f91STakashi Iwai } 466352f7f91STakashi Iwai return val; 467352f7f91STakashi Iwai } 468352f7f91STakashi Iwai 469352f7f91STakashi Iwai /* initialize the amp value (only at the first time) */ 470352f7f91STakashi Iwai static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) 471352f7f91STakashi Iwai { 472352f7f91STakashi Iwai int val = get_amp_val_to_activate(codec, nid, dir, false); 473352f7f91STakashi Iwai snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); 474352f7f91STakashi Iwai } 475352f7f91STakashi Iwai 476352f7f91STakashi Iwai static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, 477352f7f91STakashi Iwai int idx, bool enable) 478352f7f91STakashi Iwai { 479352f7f91STakashi Iwai int val; 480352f7f91STakashi Iwai if (is_ctl_associated(codec, nid, dir, idx) || 481985803caSTakashi Iwai (!enable && is_active_nid(codec, nid, dir, idx))) 482352f7f91STakashi Iwai return; 483352f7f91STakashi Iwai val = get_amp_val_to_activate(codec, nid, dir, enable); 484352f7f91STakashi Iwai snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val); 485352f7f91STakashi Iwai } 486352f7f91STakashi Iwai 487352f7f91STakashi Iwai static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, 488352f7f91STakashi Iwai int i, bool enable) 489352f7f91STakashi Iwai { 490352f7f91STakashi Iwai hda_nid_t nid = path->path[i]; 491352f7f91STakashi Iwai init_amp(codec, nid, HDA_OUTPUT, 0); 492352f7f91STakashi Iwai activate_amp(codec, nid, HDA_OUTPUT, 0, enable); 493352f7f91STakashi Iwai } 494352f7f91STakashi Iwai 495352f7f91STakashi Iwai static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, 496352f7f91STakashi Iwai int i, bool enable, bool add_aamix) 497352f7f91STakashi Iwai { 498352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 499ee8e765bSTakashi Iwai const hda_nid_t *conn; 500352f7f91STakashi Iwai int n, nums, idx; 501352f7f91STakashi Iwai int type; 502352f7f91STakashi Iwai hda_nid_t nid = path->path[i]; 503352f7f91STakashi Iwai 504ee8e765bSTakashi Iwai nums = snd_hda_get_conn_list(codec, nid, &conn); 505352f7f91STakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 506352f7f91STakashi Iwai if (type == AC_WID_PIN || 507352f7f91STakashi Iwai (type == AC_WID_AUD_IN && codec->single_adc_amp)) { 508352f7f91STakashi Iwai nums = 1; 509352f7f91STakashi Iwai idx = 0; 510352f7f91STakashi Iwai } else 511352f7f91STakashi Iwai idx = path->idx[i]; 512352f7f91STakashi Iwai 513352f7f91STakashi Iwai for (n = 0; n < nums; n++) 514352f7f91STakashi Iwai init_amp(codec, nid, HDA_INPUT, n); 515352f7f91STakashi Iwai 516352f7f91STakashi Iwai if (is_ctl_associated(codec, nid, HDA_INPUT, idx)) 517352f7f91STakashi Iwai return; 518352f7f91STakashi Iwai 519352f7f91STakashi Iwai /* here is a little bit tricky in comparison with activate_amp_out(); 520352f7f91STakashi Iwai * when aa-mixer is available, we need to enable the path as well 521352f7f91STakashi Iwai */ 522352f7f91STakashi Iwai for (n = 0; n < nums; n++) { 523352f7f91STakashi Iwai if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid)) 524352f7f91STakashi Iwai continue; 525352f7f91STakashi Iwai activate_amp(codec, nid, HDA_INPUT, n, enable); 526352f7f91STakashi Iwai } 527352f7f91STakashi Iwai } 528352f7f91STakashi Iwai 529352f7f91STakashi Iwai /* activate or deactivate the given path 530352f7f91STakashi Iwai * if @add_aamix is set, enable the input from aa-mix NID as well (if any) 531352f7f91STakashi Iwai */ 532352f7f91STakashi Iwai void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, 533352f7f91STakashi Iwai bool enable, bool add_aamix) 534352f7f91STakashi Iwai { 535352f7f91STakashi Iwai int i; 536352f7f91STakashi Iwai 537352f7f91STakashi Iwai if (!enable) 538352f7f91STakashi Iwai path->active = false; 539352f7f91STakashi Iwai 540352f7f91STakashi Iwai for (i = path->depth - 1; i >= 0; i--) { 541352f7f91STakashi Iwai if (enable && path->multi[i]) 542352f7f91STakashi Iwai snd_hda_codec_write_cache(codec, path->path[i], 0, 543352f7f91STakashi Iwai AC_VERB_SET_CONNECT_SEL, 544352f7f91STakashi Iwai path->idx[i]); 545352f7f91STakashi Iwai if (has_amp_in(codec, path, i)) 546352f7f91STakashi Iwai activate_amp_in(codec, path, i, enable, add_aamix); 547352f7f91STakashi Iwai if (has_amp_out(codec, path, i)) 548352f7f91STakashi Iwai activate_amp_out(codec, path, i, enable); 549352f7f91STakashi Iwai } 550352f7f91STakashi Iwai 551352f7f91STakashi Iwai if (enable) 552352f7f91STakashi Iwai path->active = true; 553352f7f91STakashi Iwai } 554352f7f91STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_activate_path); 555352f7f91STakashi Iwai 556d5a9f1bbSTakashi Iwai /* turn on/off EAPD on the given pin */ 557d5a9f1bbSTakashi Iwai static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) 558d5a9f1bbSTakashi Iwai { 559d5a9f1bbSTakashi Iwai struct hda_gen_spec *spec = codec->spec; 560d5a9f1bbSTakashi Iwai if (spec->own_eapd_ctl || 561d5a9f1bbSTakashi Iwai !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) 562d5a9f1bbSTakashi Iwai return; 563ecac3ed1STakashi Iwai if (codec->inv_eapd) 564ecac3ed1STakashi Iwai enable = !enable; 565d5a9f1bbSTakashi Iwai snd_hda_codec_update_cache(codec, pin, 0, 566d5a9f1bbSTakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 567d5a9f1bbSTakashi Iwai enable ? 0x02 : 0x00); 568d5a9f1bbSTakashi Iwai } 569d5a9f1bbSTakashi Iwai 570352f7f91STakashi Iwai 571352f7f91STakashi Iwai /* 572352f7f91STakashi Iwai * Helper functions for creating mixer ctl elements 573352f7f91STakashi Iwai */ 574352f7f91STakashi Iwai 575352f7f91STakashi Iwai enum { 576352f7f91STakashi Iwai HDA_CTL_WIDGET_VOL, 577352f7f91STakashi Iwai HDA_CTL_WIDGET_MUTE, 578352f7f91STakashi Iwai HDA_CTL_BIND_MUTE, 579352f7f91STakashi Iwai HDA_CTL_BIND_VOL, 580352f7f91STakashi Iwai HDA_CTL_BIND_SW, 581352f7f91STakashi Iwai }; 582352f7f91STakashi Iwai static const struct snd_kcontrol_new control_templates[] = { 583352f7f91STakashi Iwai HDA_CODEC_VOLUME(NULL, 0, 0, 0), 584352f7f91STakashi Iwai HDA_CODEC_MUTE(NULL, 0, 0, 0), 585352f7f91STakashi Iwai HDA_BIND_MUTE(NULL, 0, 0, 0), 586352f7f91STakashi Iwai HDA_BIND_VOL(NULL, 0), 587352f7f91STakashi Iwai HDA_BIND_SW(NULL, 0), 588352f7f91STakashi Iwai }; 589352f7f91STakashi Iwai 590352f7f91STakashi Iwai /* add dynamic controls from template */ 591352f7f91STakashi Iwai static int add_control(struct hda_gen_spec *spec, int type, const char *name, 592352f7f91STakashi Iwai int cidx, unsigned long val) 593352f7f91STakashi Iwai { 594352f7f91STakashi Iwai struct snd_kcontrol_new *knew; 595352f7f91STakashi Iwai 59612c93df6STakashi Iwai knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]); 597352f7f91STakashi Iwai if (!knew) 598352f7f91STakashi Iwai return -ENOMEM; 599352f7f91STakashi Iwai knew->index = cidx; 600352f7f91STakashi Iwai if (get_amp_nid_(val)) 601352f7f91STakashi Iwai knew->subdevice = HDA_SUBDEV_AMP_FLAG; 602352f7f91STakashi Iwai knew->private_value = val; 603352f7f91STakashi Iwai return 0; 604352f7f91STakashi Iwai } 605352f7f91STakashi Iwai 606352f7f91STakashi Iwai static int add_control_with_pfx(struct hda_gen_spec *spec, int type, 607352f7f91STakashi Iwai const char *pfx, const char *dir, 608352f7f91STakashi Iwai const char *sfx, int cidx, unsigned long val) 609352f7f91STakashi Iwai { 610352f7f91STakashi Iwai char name[32]; 611352f7f91STakashi Iwai snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); 612352f7f91STakashi Iwai return add_control(spec, type, name, cidx, val); 613352f7f91STakashi Iwai } 614352f7f91STakashi Iwai 615352f7f91STakashi Iwai #define add_pb_vol_ctrl(spec, type, pfx, val) \ 616352f7f91STakashi Iwai add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) 617352f7f91STakashi Iwai #define add_pb_sw_ctrl(spec, type, pfx, val) \ 618352f7f91STakashi Iwai add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) 619352f7f91STakashi Iwai #define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ 620352f7f91STakashi Iwai add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) 621352f7f91STakashi Iwai #define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ 622352f7f91STakashi Iwai add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) 623352f7f91STakashi Iwai 624352f7f91STakashi Iwai static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, 625352f7f91STakashi Iwai unsigned int chs, struct nid_path *path) 626352f7f91STakashi Iwai { 627352f7f91STakashi Iwai unsigned int val; 628352f7f91STakashi Iwai if (!path) 629352f7f91STakashi Iwai return 0; 630352f7f91STakashi Iwai val = path->ctls[NID_PATH_VOL_CTL]; 631352f7f91STakashi Iwai if (!val) 632352f7f91STakashi Iwai return 0; 633352f7f91STakashi Iwai val = amp_val_replace_channels(val, chs); 634352f7f91STakashi Iwai return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val); 635352f7f91STakashi Iwai } 636352f7f91STakashi Iwai 637352f7f91STakashi Iwai /* return the channel bits suitable for the given path->ctls[] */ 638352f7f91STakashi Iwai static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, 639352f7f91STakashi Iwai int type) 640352f7f91STakashi Iwai { 641352f7f91STakashi Iwai int chs = 1; /* mono (left only) */ 642352f7f91STakashi Iwai if (path) { 643352f7f91STakashi Iwai hda_nid_t nid = get_amp_nid_(path->ctls[type]); 644352f7f91STakashi Iwai if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) 645352f7f91STakashi Iwai chs = 3; /* stereo */ 646352f7f91STakashi Iwai } 647352f7f91STakashi Iwai return chs; 648352f7f91STakashi Iwai } 649352f7f91STakashi Iwai 650352f7f91STakashi Iwai static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, 651352f7f91STakashi Iwai struct nid_path *path) 652352f7f91STakashi Iwai { 653352f7f91STakashi Iwai int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); 654352f7f91STakashi Iwai return add_vol_ctl(codec, pfx, cidx, chs, path); 655352f7f91STakashi Iwai } 656352f7f91STakashi Iwai 657352f7f91STakashi Iwai /* create a mute-switch for the given mixer widget; 658352f7f91STakashi Iwai * if it has multiple sources (e.g. DAC and loopback), create a bind-mute 659352f7f91STakashi Iwai */ 660352f7f91STakashi Iwai static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, 661352f7f91STakashi Iwai unsigned int chs, struct nid_path *path) 662352f7f91STakashi Iwai { 663352f7f91STakashi Iwai unsigned int val; 664352f7f91STakashi Iwai int type = HDA_CTL_WIDGET_MUTE; 665352f7f91STakashi Iwai 666352f7f91STakashi Iwai if (!path) 667352f7f91STakashi Iwai return 0; 668352f7f91STakashi Iwai val = path->ctls[NID_PATH_MUTE_CTL]; 669352f7f91STakashi Iwai if (!val) 670352f7f91STakashi Iwai return 0; 671352f7f91STakashi Iwai val = amp_val_replace_channels(val, chs); 672352f7f91STakashi Iwai if (get_amp_direction_(val) == HDA_INPUT) { 673352f7f91STakashi Iwai hda_nid_t nid = get_amp_nid_(val); 674352f7f91STakashi Iwai int nums = snd_hda_get_num_conns(codec, nid); 675352f7f91STakashi Iwai if (nums > 1) { 676352f7f91STakashi Iwai type = HDA_CTL_BIND_MUTE; 677352f7f91STakashi Iwai val |= nums << 19; 678352f7f91STakashi Iwai } 679352f7f91STakashi Iwai } 680352f7f91STakashi Iwai return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); 681352f7f91STakashi Iwai } 682352f7f91STakashi Iwai 683352f7f91STakashi Iwai static int add_stereo_sw(struct hda_codec *codec, const char *pfx, 684352f7f91STakashi Iwai int cidx, struct nid_path *path) 685352f7f91STakashi Iwai { 686352f7f91STakashi Iwai int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); 687352f7f91STakashi Iwai return add_sw_ctl(codec, pfx, cidx, chs, path); 688352f7f91STakashi Iwai } 689352f7f91STakashi Iwai 690352f7f91STakashi Iwai static const char * const channel_name[4] = { 691352f7f91STakashi Iwai "Front", "Surround", "CLFE", "Side" 692352f7f91STakashi Iwai }; 693352f7f91STakashi Iwai 694352f7f91STakashi Iwai /* give some appropriate ctl name prefix for the given line out channel */ 695352f7f91STakashi Iwai static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch, 696352f7f91STakashi Iwai bool can_be_master, int *index) 697352f7f91STakashi Iwai { 698352f7f91STakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 699352f7f91STakashi Iwai 700352f7f91STakashi Iwai *index = 0; 701352f7f91STakashi Iwai if (cfg->line_outs == 1 && !spec->multi_ios && 702352f7f91STakashi Iwai !cfg->hp_outs && !cfg->speaker_outs && can_be_master) 703352f7f91STakashi Iwai return spec->vmaster_mute.hook ? "PCM" : "Master"; 704352f7f91STakashi Iwai 705352f7f91STakashi Iwai /* if there is really a single DAC used in the whole output paths, 706352f7f91STakashi Iwai * use it master (or "PCM" if a vmaster hook is present) 707352f7f91STakashi Iwai */ 708352f7f91STakashi Iwai if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && 709352f7f91STakashi Iwai !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) 710352f7f91STakashi Iwai return spec->vmaster_mute.hook ? "PCM" : "Master"; 711352f7f91STakashi Iwai 712352f7f91STakashi Iwai switch (cfg->line_out_type) { 713352f7f91STakashi Iwai case AUTO_PIN_SPEAKER_OUT: 714352f7f91STakashi Iwai if (cfg->line_outs == 1) 715352f7f91STakashi Iwai return "Speaker"; 716352f7f91STakashi Iwai if (cfg->line_outs == 2) 717352f7f91STakashi Iwai return ch ? "Bass Speaker" : "Speaker"; 718352f7f91STakashi Iwai break; 719352f7f91STakashi Iwai case AUTO_PIN_HP_OUT: 720352f7f91STakashi Iwai /* for multi-io case, only the primary out */ 721352f7f91STakashi Iwai if (ch && spec->multi_ios) 722352f7f91STakashi Iwai break; 723352f7f91STakashi Iwai *index = ch; 724352f7f91STakashi Iwai return "Headphone"; 725352f7f91STakashi Iwai default: 726352f7f91STakashi Iwai if (cfg->line_outs == 1 && !spec->multi_ios) 727352f7f91STakashi Iwai return "PCM"; 728352f7f91STakashi Iwai break; 729352f7f91STakashi Iwai } 730352f7f91STakashi Iwai if (ch >= ARRAY_SIZE(channel_name)) { 731352f7f91STakashi Iwai snd_BUG(); 732352f7f91STakashi Iwai return "PCM"; 733352f7f91STakashi Iwai } 734352f7f91STakashi Iwai 735352f7f91STakashi Iwai return channel_name[ch]; 736352f7f91STakashi Iwai } 737352f7f91STakashi Iwai 738352f7f91STakashi Iwai /* 739352f7f91STakashi Iwai * Parse output paths 740352f7f91STakashi Iwai */ 741352f7f91STakashi Iwai 742352f7f91STakashi Iwai /* badness definition */ 743352f7f91STakashi Iwai enum { 744352f7f91STakashi Iwai /* No primary DAC is found for the main output */ 745352f7f91STakashi Iwai BAD_NO_PRIMARY_DAC = 0x10000, 746352f7f91STakashi Iwai /* No DAC is found for the extra output */ 747352f7f91STakashi Iwai BAD_NO_DAC = 0x4000, 748352f7f91STakashi Iwai /* No possible multi-ios */ 749352f7f91STakashi Iwai BAD_MULTI_IO = 0x103, 750352f7f91STakashi Iwai /* No individual DAC for extra output */ 751352f7f91STakashi Iwai BAD_NO_EXTRA_DAC = 0x102, 752352f7f91STakashi Iwai /* No individual DAC for extra surrounds */ 753352f7f91STakashi Iwai BAD_NO_EXTRA_SURR_DAC = 0x101, 754352f7f91STakashi Iwai /* Primary DAC shared with main surrounds */ 755352f7f91STakashi Iwai BAD_SHARED_SURROUND = 0x100, 756352f7f91STakashi Iwai /* Primary DAC shared with main CLFE */ 757352f7f91STakashi Iwai BAD_SHARED_CLFE = 0x10, 758352f7f91STakashi Iwai /* Primary DAC shared with extra surrounds */ 759352f7f91STakashi Iwai BAD_SHARED_EXTRA_SURROUND = 0x10, 760352f7f91STakashi Iwai /* Volume widget is shared */ 761352f7f91STakashi Iwai BAD_SHARED_VOL = 0x10, 762352f7f91STakashi Iwai }; 763352f7f91STakashi Iwai 764352f7f91STakashi Iwai /* look for widgets in the path between the given NIDs appropriate for 765352f7f91STakashi Iwai * volume and mute controls, and assign the values to ctls[]. 766352f7f91STakashi Iwai * 767352f7f91STakashi Iwai * When no appropriate widget is found in the path, the badness value 768352f7f91STakashi Iwai * is incremented depending on the situation. The function returns the 769352f7f91STakashi Iwai * total badness for both volume and mute controls. 770352f7f91STakashi Iwai */ 771352f7f91STakashi Iwai static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin, 772352f7f91STakashi Iwai hda_nid_t dac) 773352f7f91STakashi Iwai { 774352f7f91STakashi Iwai struct nid_path *path = snd_hda_get_nid_path(codec, dac, pin); 775352f7f91STakashi Iwai hda_nid_t nid; 776352f7f91STakashi Iwai unsigned int val; 777352f7f91STakashi Iwai int badness = 0; 778352f7f91STakashi Iwai 779352f7f91STakashi Iwai if (!path) 780352f7f91STakashi Iwai return BAD_SHARED_VOL * 2; 781352f7f91STakashi Iwai nid = look_for_out_vol_nid(codec, path); 782352f7f91STakashi Iwai if (nid) { 783352f7f91STakashi Iwai val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 784352f7f91STakashi Iwai if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) 785352f7f91STakashi Iwai badness += BAD_SHARED_VOL; 786352f7f91STakashi Iwai else 787352f7f91STakashi Iwai path->ctls[NID_PATH_VOL_CTL] = val; 788352f7f91STakashi Iwai } else 789352f7f91STakashi Iwai badness += BAD_SHARED_VOL; 790352f7f91STakashi Iwai nid = look_for_out_mute_nid(codec, path); 791352f7f91STakashi Iwai if (nid) { 792352f7f91STakashi Iwai unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); 793352f7f91STakashi Iwai if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || 794352f7f91STakashi Iwai nid_has_mute(codec, nid, HDA_OUTPUT)) 795352f7f91STakashi Iwai val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 796352f7f91STakashi Iwai else 797352f7f91STakashi Iwai val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); 798352f7f91STakashi Iwai if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) 799352f7f91STakashi Iwai badness += BAD_SHARED_VOL; 800352f7f91STakashi Iwai else 801352f7f91STakashi Iwai path->ctls[NID_PATH_MUTE_CTL] = val; 802352f7f91STakashi Iwai } else 803352f7f91STakashi Iwai badness += BAD_SHARED_VOL; 804352f7f91STakashi Iwai return badness; 805352f7f91STakashi Iwai } 806352f7f91STakashi Iwai 807352f7f91STakashi Iwai struct badness_table { 808352f7f91STakashi Iwai int no_primary_dac; /* no primary DAC */ 809352f7f91STakashi Iwai int no_dac; /* no secondary DACs */ 810352f7f91STakashi Iwai int shared_primary; /* primary DAC is shared with main output */ 811352f7f91STakashi Iwai int shared_surr; /* secondary DAC shared with main or primary */ 812352f7f91STakashi Iwai int shared_clfe; /* third DAC shared with main or primary */ 813352f7f91STakashi Iwai int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ 814352f7f91STakashi Iwai }; 815352f7f91STakashi Iwai 816352f7f91STakashi Iwai static struct badness_table main_out_badness = { 817352f7f91STakashi Iwai .no_primary_dac = BAD_NO_PRIMARY_DAC, 818352f7f91STakashi Iwai .no_dac = BAD_NO_DAC, 819352f7f91STakashi Iwai .shared_primary = BAD_NO_PRIMARY_DAC, 820352f7f91STakashi Iwai .shared_surr = BAD_SHARED_SURROUND, 821352f7f91STakashi Iwai .shared_clfe = BAD_SHARED_CLFE, 822352f7f91STakashi Iwai .shared_surr_main = BAD_SHARED_SURROUND, 823352f7f91STakashi Iwai }; 824352f7f91STakashi Iwai 825352f7f91STakashi Iwai static struct badness_table extra_out_badness = { 826352f7f91STakashi Iwai .no_primary_dac = BAD_NO_DAC, 827352f7f91STakashi Iwai .no_dac = BAD_NO_DAC, 828352f7f91STakashi Iwai .shared_primary = BAD_NO_EXTRA_DAC, 829352f7f91STakashi Iwai .shared_surr = BAD_SHARED_EXTRA_SURROUND, 830352f7f91STakashi Iwai .shared_clfe = BAD_SHARED_EXTRA_SURROUND, 831352f7f91STakashi Iwai .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, 832352f7f91STakashi Iwai }; 833352f7f91STakashi Iwai 8347385df61STakashi Iwai /* get the DAC of the primary output corresponding to the given array index */ 8357385df61STakashi Iwai static hda_nid_t get_primary_out(struct hda_codec *codec, int idx) 8367385df61STakashi Iwai { 8377385df61STakashi Iwai struct hda_gen_spec *spec = codec->spec; 8387385df61STakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 8397385df61STakashi Iwai 8407385df61STakashi Iwai if (cfg->line_outs > idx) 8417385df61STakashi Iwai return spec->private_dac_nids[idx]; 8427385df61STakashi Iwai idx -= cfg->line_outs; 8437385df61STakashi Iwai if (spec->multi_ios > idx) 8447385df61STakashi Iwai return spec->multi_io[idx].dac; 8457385df61STakashi Iwai return 0; 8467385df61STakashi Iwai } 8477385df61STakashi Iwai 8487385df61STakashi Iwai /* return the DAC if it's reachable, otherwise zero */ 8497385df61STakashi Iwai static inline hda_nid_t try_dac(struct hda_codec *codec, 8507385df61STakashi Iwai hda_nid_t dac, hda_nid_t pin) 8517385df61STakashi Iwai { 8527385df61STakashi Iwai return is_reachable_path(codec, dac, pin) ? dac : 0; 8537385df61STakashi Iwai } 8547385df61STakashi Iwai 855352f7f91STakashi Iwai /* try to assign DACs to pins and return the resultant badness */ 856352f7f91STakashi Iwai static int try_assign_dacs(struct hda_codec *codec, int num_outs, 857352f7f91STakashi Iwai const hda_nid_t *pins, hda_nid_t *dacs, 858196c1766STakashi Iwai int *path_idx, 859352f7f91STakashi Iwai const struct badness_table *bad) 860352f7f91STakashi Iwai { 861352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 862352f7f91STakashi Iwai int i, j; 863352f7f91STakashi Iwai int badness = 0; 864352f7f91STakashi Iwai hda_nid_t dac; 865352f7f91STakashi Iwai 866352f7f91STakashi Iwai if (!num_outs) 867352f7f91STakashi Iwai return 0; 868352f7f91STakashi Iwai 869352f7f91STakashi Iwai for (i = 0; i < num_outs; i++) { 8700c8c0f56STakashi Iwai struct nid_path *path; 871352f7f91STakashi Iwai hda_nid_t pin = pins[i]; 8721e0b5286STakashi Iwai 8731e0b5286STakashi Iwai if (dacs[i]) { 8741e0b5286STakashi Iwai badness += assign_out_path_ctls(codec, pin, dacs[i]); 8751e0b5286STakashi Iwai continue; 8761e0b5286STakashi Iwai } 8771e0b5286STakashi Iwai 878352f7f91STakashi Iwai dacs[i] = look_for_dac(codec, pin, false); 879352f7f91STakashi Iwai if (!dacs[i] && !i) { 880352f7f91STakashi Iwai for (j = 1; j < num_outs; j++) { 881352f7f91STakashi Iwai if (is_reachable_path(codec, dacs[j], pin)) { 882352f7f91STakashi Iwai dacs[0] = dacs[j]; 883352f7f91STakashi Iwai dacs[j] = 0; 884196c1766STakashi Iwai path_idx[j] = 0; 885352f7f91STakashi Iwai break; 886352f7f91STakashi Iwai } 887352f7f91STakashi Iwai } 888352f7f91STakashi Iwai } 889352f7f91STakashi Iwai dac = dacs[i]; 890352f7f91STakashi Iwai if (!dac) { 8917385df61STakashi Iwai if (num_outs > 2) 8927385df61STakashi Iwai dac = try_dac(codec, get_primary_out(codec, i), pin); 8937385df61STakashi Iwai if (!dac) 8947385df61STakashi Iwai dac = try_dac(codec, dacs[0], pin); 8957385df61STakashi Iwai if (!dac) 8967385df61STakashi Iwai dac = try_dac(codec, get_primary_out(codec, i), pin); 897352f7f91STakashi Iwai if (dac) { 898352f7f91STakashi Iwai if (!i) 899352f7f91STakashi Iwai badness += bad->shared_primary; 900352f7f91STakashi Iwai else if (i == 1) 901352f7f91STakashi Iwai badness += bad->shared_surr; 902352f7f91STakashi Iwai else 903352f7f91STakashi Iwai badness += bad->shared_clfe; 904352f7f91STakashi Iwai } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { 905352f7f91STakashi Iwai dac = spec->private_dac_nids[0]; 906352f7f91STakashi Iwai badness += bad->shared_surr_main; 907352f7f91STakashi Iwai } else if (!i) 908352f7f91STakashi Iwai badness += bad->no_primary_dac; 909352f7f91STakashi Iwai else 910352f7f91STakashi Iwai badness += bad->no_dac; 911352f7f91STakashi Iwai } 9124ac0eefaSTakashi Iwai path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX); 913117688a9STakashi Iwai if (!path && !i && spec->mixer_nid) { 914b3a8c745STakashi Iwai /* try with aamix */ 915b3a8c745STakashi Iwai path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_ALL); 916b3a8c745STakashi Iwai } 9170c8c0f56STakashi Iwai if (!path) 918352f7f91STakashi Iwai dac = dacs[i] = 0; 919e1284af7STakashi Iwai else { 9200c8c0f56STakashi Iwai print_nid_path("output", path); 921e1284af7STakashi Iwai path->active = true; 922196c1766STakashi Iwai path_idx[i] = snd_hda_get_path_idx(codec, path); 923e1284af7STakashi Iwai } 924352f7f91STakashi Iwai if (dac) 925352f7f91STakashi Iwai badness += assign_out_path_ctls(codec, pin, dac); 926352f7f91STakashi Iwai } 927352f7f91STakashi Iwai 928352f7f91STakashi Iwai return badness; 929352f7f91STakashi Iwai } 930352f7f91STakashi Iwai 931352f7f91STakashi Iwai /* return NID if the given pin has only a single connection to a certain DAC */ 932352f7f91STakashi Iwai static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) 933352f7f91STakashi Iwai { 934352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 935352f7f91STakashi Iwai int i; 936352f7f91STakashi Iwai hda_nid_t nid_found = 0; 937352f7f91STakashi Iwai 938352f7f91STakashi Iwai for (i = 0; i < spec->num_all_dacs; i++) { 939352f7f91STakashi Iwai hda_nid_t nid = spec->all_dacs[i]; 940352f7f91STakashi Iwai if (!nid || is_dac_already_used(codec, nid)) 941352f7f91STakashi Iwai continue; 942352f7f91STakashi Iwai if (is_reachable_path(codec, nid, pin)) { 943352f7f91STakashi Iwai if (nid_found) 944352f7f91STakashi Iwai return 0; 945352f7f91STakashi Iwai nid_found = nid; 946352f7f91STakashi Iwai } 947352f7f91STakashi Iwai } 948352f7f91STakashi Iwai return nid_found; 949352f7f91STakashi Iwai } 950352f7f91STakashi Iwai 951352f7f91STakashi Iwai /* check whether the given pin can be a multi-io pin */ 952352f7f91STakashi Iwai static bool can_be_multiio_pin(struct hda_codec *codec, 953352f7f91STakashi Iwai unsigned int location, hda_nid_t nid) 954352f7f91STakashi Iwai { 955352f7f91STakashi Iwai unsigned int defcfg, caps; 956352f7f91STakashi Iwai 957352f7f91STakashi Iwai defcfg = snd_hda_codec_get_pincfg(codec, nid); 958352f7f91STakashi Iwai if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) 959352f7f91STakashi Iwai return false; 960352f7f91STakashi Iwai if (location && get_defcfg_location(defcfg) != location) 961352f7f91STakashi Iwai return false; 962352f7f91STakashi Iwai caps = snd_hda_query_pin_caps(codec, nid); 963352f7f91STakashi Iwai if (!(caps & AC_PINCAP_OUT)) 964352f7f91STakashi Iwai return false; 965352f7f91STakashi Iwai return true; 966352f7f91STakashi Iwai } 967352f7f91STakashi Iwai 968e22aab7dSTakashi Iwai /* count the number of input pins that are capable to be multi-io */ 969e22aab7dSTakashi Iwai static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin) 970e22aab7dSTakashi Iwai { 971e22aab7dSTakashi Iwai struct hda_gen_spec *spec = codec->spec; 972e22aab7dSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 973e22aab7dSTakashi Iwai unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); 974e22aab7dSTakashi Iwai unsigned int location = get_defcfg_location(defcfg); 975e22aab7dSTakashi Iwai int type, i; 976e22aab7dSTakashi Iwai int num_pins = 0; 977e22aab7dSTakashi Iwai 978e22aab7dSTakashi Iwai for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { 979e22aab7dSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 980e22aab7dSTakashi Iwai if (cfg->inputs[i].type != type) 981e22aab7dSTakashi Iwai continue; 982e22aab7dSTakashi Iwai if (can_be_multiio_pin(codec, location, 983e22aab7dSTakashi Iwai cfg->inputs[i].pin)) 984e22aab7dSTakashi Iwai num_pins++; 985e22aab7dSTakashi Iwai } 986e22aab7dSTakashi Iwai } 987e22aab7dSTakashi Iwai return num_pins; 988e22aab7dSTakashi Iwai } 989e22aab7dSTakashi Iwai 990352f7f91STakashi Iwai /* 991352f7f91STakashi Iwai * multi-io helper 992352f7f91STakashi Iwai * 993352f7f91STakashi Iwai * When hardwired is set, try to fill ony hardwired pins, and returns 994352f7f91STakashi Iwai * zero if any pins are filled, non-zero if nothing found. 995352f7f91STakashi Iwai * When hardwired is off, try to fill possible input pins, and returns 996352f7f91STakashi Iwai * the badness value. 997352f7f91STakashi Iwai */ 998352f7f91STakashi Iwai static int fill_multi_ios(struct hda_codec *codec, 999352f7f91STakashi Iwai hda_nid_t reference_pin, 1000e22aab7dSTakashi Iwai bool hardwired) 1001352f7f91STakashi Iwai { 1002352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1003352f7f91STakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 1004e22aab7dSTakashi Iwai int type, i, j, num_pins, old_pins; 1005352f7f91STakashi Iwai unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); 1006352f7f91STakashi Iwai unsigned int location = get_defcfg_location(defcfg); 1007352f7f91STakashi Iwai int badness = 0; 1008352f7f91STakashi Iwai 1009352f7f91STakashi Iwai old_pins = spec->multi_ios; 1010352f7f91STakashi Iwai if (old_pins >= 2) 1011352f7f91STakashi Iwai goto end_fill; 1012352f7f91STakashi Iwai 1013e22aab7dSTakashi Iwai num_pins = count_multiio_pins(codec, reference_pin); 1014352f7f91STakashi Iwai if (num_pins < 2) 1015352f7f91STakashi Iwai goto end_fill; 1016352f7f91STakashi Iwai 1017352f7f91STakashi Iwai for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { 1018352f7f91STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 10190c8c0f56STakashi Iwai struct nid_path *path; 1020352f7f91STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 1021352f7f91STakashi Iwai hda_nid_t dac = 0; 1022352f7f91STakashi Iwai 1023352f7f91STakashi Iwai if (cfg->inputs[i].type != type) 1024352f7f91STakashi Iwai continue; 1025352f7f91STakashi Iwai if (!can_be_multiio_pin(codec, location, nid)) 1026352f7f91STakashi Iwai continue; 1027352f7f91STakashi Iwai for (j = 0; j < spec->multi_ios; j++) { 1028352f7f91STakashi Iwai if (nid == spec->multi_io[j].pin) 1029352f7f91STakashi Iwai break; 1030352f7f91STakashi Iwai } 1031352f7f91STakashi Iwai if (j < spec->multi_ios) 1032352f7f91STakashi Iwai continue; 1033352f7f91STakashi Iwai 1034352f7f91STakashi Iwai if (hardwired) 1035352f7f91STakashi Iwai dac = get_dac_if_single(codec, nid); 1036352f7f91STakashi Iwai else if (!dac) 1037352f7f91STakashi Iwai dac = look_for_dac(codec, nid, false); 1038352f7f91STakashi Iwai if (!dac) { 1039352f7f91STakashi Iwai badness++; 1040352f7f91STakashi Iwai continue; 1041352f7f91STakashi Iwai } 10424ac0eefaSTakashi Iwai path = snd_hda_add_new_path(codec, dac, nid, HDA_PARSE_NO_AAMIX); 10430c8c0f56STakashi Iwai if (!path) { 1044352f7f91STakashi Iwai badness++; 1045352f7f91STakashi Iwai continue; 1046352f7f91STakashi Iwai } 10470c8c0f56STakashi Iwai print_nid_path("multiio", path); 1048352f7f91STakashi Iwai spec->multi_io[spec->multi_ios].pin = nid; 1049352f7f91STakashi Iwai spec->multi_io[spec->multi_ios].dac = dac; 1050196c1766STakashi Iwai spec->out_paths[cfg->line_outs + spec->multi_ios] = 1051196c1766STakashi Iwai snd_hda_get_path_idx(codec, path); 1052352f7f91STakashi Iwai spec->multi_ios++; 1053352f7f91STakashi Iwai if (spec->multi_ios >= 2) 1054352f7f91STakashi Iwai break; 1055352f7f91STakashi Iwai } 1056352f7f91STakashi Iwai } 1057352f7f91STakashi Iwai end_fill: 1058352f7f91STakashi Iwai if (badness) 1059352f7f91STakashi Iwai badness = BAD_MULTI_IO; 1060352f7f91STakashi Iwai if (old_pins == spec->multi_ios) { 1061352f7f91STakashi Iwai if (hardwired) 1062352f7f91STakashi Iwai return 1; /* nothing found */ 1063352f7f91STakashi Iwai else 1064352f7f91STakashi Iwai return badness; /* no badness if nothing found */ 1065352f7f91STakashi Iwai } 1066352f7f91STakashi Iwai if (!hardwired && spec->multi_ios < 2) { 1067352f7f91STakashi Iwai /* cancel newly assigned paths */ 1068352f7f91STakashi Iwai spec->paths.used -= spec->multi_ios - old_pins; 1069352f7f91STakashi Iwai spec->multi_ios = old_pins; 1070352f7f91STakashi Iwai return badness; 1071352f7f91STakashi Iwai } 1072352f7f91STakashi Iwai 1073352f7f91STakashi Iwai /* assign volume and mute controls */ 1074352f7f91STakashi Iwai for (i = old_pins; i < spec->multi_ios; i++) 1075352f7f91STakashi Iwai badness += assign_out_path_ctls(codec, spec->multi_io[i].pin, 1076352f7f91STakashi Iwai spec->multi_io[i].dac); 1077352f7f91STakashi Iwai 1078352f7f91STakashi Iwai return badness; 1079352f7f91STakashi Iwai } 1080352f7f91STakashi Iwai 1081352f7f91STakashi Iwai /* map DACs for all pins in the list if they are single connections */ 1082352f7f91STakashi Iwai static bool map_singles(struct hda_codec *codec, int outs, 1083196c1766STakashi Iwai const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx) 1084352f7f91STakashi Iwai { 1085b3a8c745STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1086352f7f91STakashi Iwai int i; 1087352f7f91STakashi Iwai bool found = false; 1088352f7f91STakashi Iwai for (i = 0; i < outs; i++) { 10890c8c0f56STakashi Iwai struct nid_path *path; 1090352f7f91STakashi Iwai hda_nid_t dac; 1091352f7f91STakashi Iwai if (dacs[i]) 1092352f7f91STakashi Iwai continue; 1093352f7f91STakashi Iwai dac = get_dac_if_single(codec, pins[i]); 1094352f7f91STakashi Iwai if (!dac) 1095352f7f91STakashi Iwai continue; 10964ac0eefaSTakashi Iwai path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX); 1097117688a9STakashi Iwai if (!path && !i && spec->mixer_nid) 1098b3a8c745STakashi Iwai path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_ALL); 10990c8c0f56STakashi Iwai if (path) { 1100352f7f91STakashi Iwai dacs[i] = dac; 1101352f7f91STakashi Iwai found = true; 11020c8c0f56STakashi Iwai print_nid_path("output", path); 1103e1284af7STakashi Iwai path->active = true; 1104196c1766STakashi Iwai path_idx[i] = snd_hda_get_path_idx(codec, path); 1105352f7f91STakashi Iwai } 1106352f7f91STakashi Iwai } 1107352f7f91STakashi Iwai return found; 1108352f7f91STakashi Iwai } 1109352f7f91STakashi Iwai 1110c30aa7b2STakashi Iwai /* create a new path including aamix if available, and return its index */ 1111c30aa7b2STakashi Iwai static int check_aamix_out_path(struct hda_codec *codec, int path_idx) 1112c30aa7b2STakashi Iwai { 1113c30aa7b2STakashi Iwai struct nid_path *path; 1114c30aa7b2STakashi Iwai 1115c30aa7b2STakashi Iwai path = snd_hda_get_path_from_idx(codec, path_idx); 1116c30aa7b2STakashi Iwai if (!path || !path->depth || path->with_aa_mix) 1117c30aa7b2STakashi Iwai return 0; 1118c30aa7b2STakashi Iwai path = snd_hda_add_new_path(codec, path->path[0], 1119c30aa7b2STakashi Iwai path->path[path->depth - 1], 1120c30aa7b2STakashi Iwai HDA_PARSE_ONLY_AAMIX); 1121c30aa7b2STakashi Iwai if (!path) 1122c30aa7b2STakashi Iwai return 0; 1123c30aa7b2STakashi Iwai print_nid_path("output-aamix", path); 1124c30aa7b2STakashi Iwai path->active = false; /* unused as default */ 1125c30aa7b2STakashi Iwai return snd_hda_get_path_idx(codec, path); 1126c30aa7b2STakashi Iwai } 1127c30aa7b2STakashi Iwai 1128352f7f91STakashi Iwai /* fill in the dac_nids table from the parsed pin configuration */ 1129352f7f91STakashi Iwai static int fill_and_eval_dacs(struct hda_codec *codec, 1130352f7f91STakashi Iwai bool fill_hardwired, 1131352f7f91STakashi Iwai bool fill_mio_first) 1132352f7f91STakashi Iwai { 1133352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1134352f7f91STakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 1135352f7f91STakashi Iwai int i, err, badness; 1136352f7f91STakashi Iwai 1137352f7f91STakashi Iwai /* set num_dacs once to full for look_for_dac() */ 1138352f7f91STakashi Iwai spec->multiout.num_dacs = cfg->line_outs; 1139352f7f91STakashi Iwai spec->multiout.dac_nids = spec->private_dac_nids; 1140352f7f91STakashi Iwai memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); 1141352f7f91STakashi Iwai memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); 1142352f7f91STakashi Iwai memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); 1143352f7f91STakashi Iwai spec->multi_ios = 0; 1144352f7f91STakashi Iwai snd_array_free(&spec->paths); 1145352f7f91STakashi Iwai badness = 0; 1146352f7f91STakashi Iwai 1147352f7f91STakashi Iwai /* fill hard-wired DACs first */ 1148352f7f91STakashi Iwai if (fill_hardwired) { 1149352f7f91STakashi Iwai bool mapped; 1150352f7f91STakashi Iwai do { 1151352f7f91STakashi Iwai mapped = map_singles(codec, cfg->line_outs, 1152352f7f91STakashi Iwai cfg->line_out_pins, 1153196c1766STakashi Iwai spec->private_dac_nids, 1154196c1766STakashi Iwai spec->out_paths); 1155352f7f91STakashi Iwai mapped |= map_singles(codec, cfg->hp_outs, 1156352f7f91STakashi Iwai cfg->hp_pins, 1157196c1766STakashi Iwai spec->multiout.hp_out_nid, 1158196c1766STakashi Iwai spec->hp_paths); 1159352f7f91STakashi Iwai mapped |= map_singles(codec, cfg->speaker_outs, 1160352f7f91STakashi Iwai cfg->speaker_pins, 1161196c1766STakashi Iwai spec->multiout.extra_out_nid, 1162196c1766STakashi Iwai spec->speaker_paths); 1163352f7f91STakashi Iwai if (fill_mio_first && cfg->line_outs == 1 && 1164352f7f91STakashi Iwai cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 1165e22aab7dSTakashi Iwai err = fill_multi_ios(codec, cfg->line_out_pins[0], true); 1166352f7f91STakashi Iwai if (!err) 1167352f7f91STakashi Iwai mapped = true; 1168352f7f91STakashi Iwai } 1169352f7f91STakashi Iwai } while (mapped); 1170352f7f91STakashi Iwai } 1171352f7f91STakashi Iwai 1172352f7f91STakashi Iwai badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins, 1173196c1766STakashi Iwai spec->private_dac_nids, spec->out_paths, 1174352f7f91STakashi Iwai &main_out_badness); 1175352f7f91STakashi Iwai 1176352f7f91STakashi Iwai /* re-count num_dacs and squash invalid entries */ 1177352f7f91STakashi Iwai spec->multiout.num_dacs = 0; 1178352f7f91STakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 1179352f7f91STakashi Iwai if (spec->private_dac_nids[i]) 1180352f7f91STakashi Iwai spec->multiout.num_dacs++; 1181352f7f91STakashi Iwai else { 1182352f7f91STakashi Iwai memmove(spec->private_dac_nids + i, 1183352f7f91STakashi Iwai spec->private_dac_nids + i + 1, 1184352f7f91STakashi Iwai sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); 1185352f7f91STakashi Iwai spec->private_dac_nids[cfg->line_outs - 1] = 0; 1186352f7f91STakashi Iwai } 1187352f7f91STakashi Iwai } 1188352f7f91STakashi Iwai 1189352f7f91STakashi Iwai if (fill_mio_first && 1190352f7f91STakashi Iwai cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 1191352f7f91STakashi Iwai /* try to fill multi-io first */ 1192e22aab7dSTakashi Iwai err = fill_multi_ios(codec, cfg->line_out_pins[0], false); 1193352f7f91STakashi Iwai if (err < 0) 1194352f7f91STakashi Iwai return err; 1195352f7f91STakashi Iwai /* we don't count badness at this stage yet */ 1196352f7f91STakashi Iwai } 1197352f7f91STakashi Iwai 1198352f7f91STakashi Iwai if (cfg->line_out_type != AUTO_PIN_HP_OUT) { 1199352f7f91STakashi Iwai err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins, 1200352f7f91STakashi Iwai spec->multiout.hp_out_nid, 1201196c1766STakashi Iwai spec->hp_paths, 1202352f7f91STakashi Iwai &extra_out_badness); 1203352f7f91STakashi Iwai if (err < 0) 1204352f7f91STakashi Iwai return err; 1205352f7f91STakashi Iwai badness += err; 1206352f7f91STakashi Iwai } 1207352f7f91STakashi Iwai if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 1208352f7f91STakashi Iwai err = try_assign_dacs(codec, cfg->speaker_outs, 1209352f7f91STakashi Iwai cfg->speaker_pins, 1210352f7f91STakashi Iwai spec->multiout.extra_out_nid, 1211196c1766STakashi Iwai spec->speaker_paths, 1212352f7f91STakashi Iwai &extra_out_badness); 1213352f7f91STakashi Iwai if (err < 0) 1214352f7f91STakashi Iwai return err; 1215352f7f91STakashi Iwai badness += err; 1216352f7f91STakashi Iwai } 1217352f7f91STakashi Iwai if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { 1218e22aab7dSTakashi Iwai err = fill_multi_ios(codec, cfg->line_out_pins[0], false); 1219352f7f91STakashi Iwai if (err < 0) 1220352f7f91STakashi Iwai return err; 1221352f7f91STakashi Iwai badness += err; 1222352f7f91STakashi Iwai } 1223e22aab7dSTakashi Iwai 1224c30aa7b2STakashi Iwai if (spec->mixer_nid) { 1225c30aa7b2STakashi Iwai spec->aamix_out_paths[0] = 1226c30aa7b2STakashi Iwai check_aamix_out_path(codec, spec->out_paths[0]); 1227c30aa7b2STakashi Iwai if (cfg->line_out_type != AUTO_PIN_HP_OUT) 1228c30aa7b2STakashi Iwai spec->aamix_out_paths[1] = 1229c30aa7b2STakashi Iwai check_aamix_out_path(codec, spec->hp_paths[0]); 1230c30aa7b2STakashi Iwai if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) 1231c30aa7b2STakashi Iwai spec->aamix_out_paths[2] = 1232c30aa7b2STakashi Iwai check_aamix_out_path(codec, spec->speaker_paths[0]); 1233c30aa7b2STakashi Iwai } 1234c30aa7b2STakashi Iwai 1235e22aab7dSTakashi Iwai if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) 1236e22aab7dSTakashi Iwai if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) 1237e22aab7dSTakashi Iwai spec->multi_ios = 1; /* give badness */ 1238352f7f91STakashi Iwai 1239352f7f91STakashi Iwai if (spec->multi_ios == 2) { 1240352f7f91STakashi Iwai for (i = 0; i < 2; i++) 1241352f7f91STakashi Iwai spec->private_dac_nids[spec->multiout.num_dacs++] = 1242352f7f91STakashi Iwai spec->multi_io[i].dac; 1243352f7f91STakashi Iwai spec->ext_channel_count = 2; 1244352f7f91STakashi Iwai } else if (spec->multi_ios) { 1245352f7f91STakashi Iwai spec->multi_ios = 0; 1246352f7f91STakashi Iwai badness += BAD_MULTI_IO; 1247352f7f91STakashi Iwai } 1248352f7f91STakashi Iwai 1249352f7f91STakashi Iwai return badness; 1250352f7f91STakashi Iwai } 1251352f7f91STakashi Iwai 1252352f7f91STakashi Iwai #define DEBUG_BADNESS 1253352f7f91STakashi Iwai 1254352f7f91STakashi Iwai #ifdef DEBUG_BADNESS 1255352f7f91STakashi Iwai #define debug_badness snd_printdd 1256352f7f91STakashi Iwai #else 1257352f7f91STakashi Iwai #define debug_badness(...) 1258352f7f91STakashi Iwai #endif 1259352f7f91STakashi Iwai 1260352f7f91STakashi Iwai static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *cfg) 1261352f7f91STakashi Iwai { 1262352f7f91STakashi Iwai debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", 1263352f7f91STakashi Iwai cfg->line_out_pins[0], cfg->line_out_pins[1], 1264708122e8STakashi Iwai cfg->line_out_pins[2], cfg->line_out_pins[3], 1265352f7f91STakashi Iwai spec->multiout.dac_nids[0], 1266352f7f91STakashi Iwai spec->multiout.dac_nids[1], 1267352f7f91STakashi Iwai spec->multiout.dac_nids[2], 1268352f7f91STakashi Iwai spec->multiout.dac_nids[3]); 1269352f7f91STakashi Iwai if (spec->multi_ios > 0) 1270352f7f91STakashi Iwai debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", 1271352f7f91STakashi Iwai spec->multi_ios, 1272352f7f91STakashi Iwai spec->multi_io[0].pin, spec->multi_io[1].pin, 1273352f7f91STakashi Iwai spec->multi_io[0].dac, spec->multi_io[1].dac); 1274352f7f91STakashi Iwai debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", 1275352f7f91STakashi Iwai cfg->hp_pins[0], cfg->hp_pins[1], 1276708122e8STakashi Iwai cfg->hp_pins[2], cfg->hp_pins[3], 1277352f7f91STakashi Iwai spec->multiout.hp_out_nid[0], 1278352f7f91STakashi Iwai spec->multiout.hp_out_nid[1], 1279352f7f91STakashi Iwai spec->multiout.hp_out_nid[2], 1280352f7f91STakashi Iwai spec->multiout.hp_out_nid[3]); 1281352f7f91STakashi Iwai debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", 1282352f7f91STakashi Iwai cfg->speaker_pins[0], cfg->speaker_pins[1], 1283352f7f91STakashi Iwai cfg->speaker_pins[2], cfg->speaker_pins[3], 1284352f7f91STakashi Iwai spec->multiout.extra_out_nid[0], 1285352f7f91STakashi Iwai spec->multiout.extra_out_nid[1], 1286352f7f91STakashi Iwai spec->multiout.extra_out_nid[2], 1287352f7f91STakashi Iwai spec->multiout.extra_out_nid[3]); 1288352f7f91STakashi Iwai } 1289352f7f91STakashi Iwai 1290352f7f91STakashi Iwai /* find all available DACs of the codec */ 1291352f7f91STakashi Iwai static void fill_all_dac_nids(struct hda_codec *codec) 1292352f7f91STakashi Iwai { 1293352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1294352f7f91STakashi Iwai int i; 1295352f7f91STakashi Iwai hda_nid_t nid = codec->start_nid; 1296352f7f91STakashi Iwai 1297352f7f91STakashi Iwai spec->num_all_dacs = 0; 1298352f7f91STakashi Iwai memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); 1299352f7f91STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 1300352f7f91STakashi Iwai if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) 1301352f7f91STakashi Iwai continue; 1302352f7f91STakashi Iwai if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { 1303352f7f91STakashi Iwai snd_printk(KERN_ERR "hda: Too many DACs!\n"); 1304352f7f91STakashi Iwai break; 1305352f7f91STakashi Iwai } 1306352f7f91STakashi Iwai spec->all_dacs[spec->num_all_dacs++] = nid; 1307352f7f91STakashi Iwai } 1308352f7f91STakashi Iwai } 1309352f7f91STakashi Iwai 1310352f7f91STakashi Iwai static int parse_output_paths(struct hda_codec *codec) 1311352f7f91STakashi Iwai { 1312352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1313352f7f91STakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 1314352f7f91STakashi Iwai struct auto_pin_cfg *best_cfg; 1315352f7f91STakashi Iwai int best_badness = INT_MAX; 1316352f7f91STakashi Iwai int badness; 1317352f7f91STakashi Iwai bool fill_hardwired = true, fill_mio_first = true; 1318352f7f91STakashi Iwai bool best_wired = true, best_mio = true; 1319352f7f91STakashi Iwai bool hp_spk_swapped = false; 1320352f7f91STakashi Iwai 1321352f7f91STakashi Iwai fill_all_dac_nids(codec); 1322352f7f91STakashi Iwai 1323352f7f91STakashi Iwai best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); 1324352f7f91STakashi Iwai if (!best_cfg) 1325352f7f91STakashi Iwai return -ENOMEM; 1326352f7f91STakashi Iwai *best_cfg = *cfg; 1327352f7f91STakashi Iwai 1328352f7f91STakashi Iwai for (;;) { 1329352f7f91STakashi Iwai badness = fill_and_eval_dacs(codec, fill_hardwired, 1330352f7f91STakashi Iwai fill_mio_first); 1331352f7f91STakashi Iwai if (badness < 0) { 1332352f7f91STakashi Iwai kfree(best_cfg); 1333352f7f91STakashi Iwai return badness; 1334352f7f91STakashi Iwai } 1335352f7f91STakashi Iwai debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", 1336352f7f91STakashi Iwai cfg->line_out_type, fill_hardwired, fill_mio_first, 1337352f7f91STakashi Iwai badness); 1338352f7f91STakashi Iwai debug_show_configs(spec, cfg); 1339352f7f91STakashi Iwai if (badness < best_badness) { 1340352f7f91STakashi Iwai best_badness = badness; 1341352f7f91STakashi Iwai *best_cfg = *cfg; 1342352f7f91STakashi Iwai best_wired = fill_hardwired; 1343352f7f91STakashi Iwai best_mio = fill_mio_first; 1344352f7f91STakashi Iwai } 1345352f7f91STakashi Iwai if (!badness) 1346352f7f91STakashi Iwai break; 1347352f7f91STakashi Iwai fill_mio_first = !fill_mio_first; 1348352f7f91STakashi Iwai if (!fill_mio_first) 1349352f7f91STakashi Iwai continue; 1350352f7f91STakashi Iwai fill_hardwired = !fill_hardwired; 1351352f7f91STakashi Iwai if (!fill_hardwired) 1352352f7f91STakashi Iwai continue; 1353352f7f91STakashi Iwai if (hp_spk_swapped) 1354352f7f91STakashi Iwai break; 1355352f7f91STakashi Iwai hp_spk_swapped = true; 1356352f7f91STakashi Iwai if (cfg->speaker_outs > 0 && 1357352f7f91STakashi Iwai cfg->line_out_type == AUTO_PIN_HP_OUT) { 1358352f7f91STakashi Iwai cfg->hp_outs = cfg->line_outs; 1359352f7f91STakashi Iwai memcpy(cfg->hp_pins, cfg->line_out_pins, 1360352f7f91STakashi Iwai sizeof(cfg->hp_pins)); 1361352f7f91STakashi Iwai cfg->line_outs = cfg->speaker_outs; 1362352f7f91STakashi Iwai memcpy(cfg->line_out_pins, cfg->speaker_pins, 1363352f7f91STakashi Iwai sizeof(cfg->speaker_pins)); 1364352f7f91STakashi Iwai cfg->speaker_outs = 0; 1365352f7f91STakashi Iwai memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); 1366352f7f91STakashi Iwai cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; 1367352f7f91STakashi Iwai fill_hardwired = true; 1368352f7f91STakashi Iwai continue; 1369352f7f91STakashi Iwai } 1370352f7f91STakashi Iwai if (cfg->hp_outs > 0 && 1371352f7f91STakashi Iwai cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { 1372352f7f91STakashi Iwai cfg->speaker_outs = cfg->line_outs; 1373352f7f91STakashi Iwai memcpy(cfg->speaker_pins, cfg->line_out_pins, 1374352f7f91STakashi Iwai sizeof(cfg->speaker_pins)); 1375352f7f91STakashi Iwai cfg->line_outs = cfg->hp_outs; 1376352f7f91STakashi Iwai memcpy(cfg->line_out_pins, cfg->hp_pins, 1377352f7f91STakashi Iwai sizeof(cfg->hp_pins)); 1378352f7f91STakashi Iwai cfg->hp_outs = 0; 1379352f7f91STakashi Iwai memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); 1380352f7f91STakashi Iwai cfg->line_out_type = AUTO_PIN_HP_OUT; 1381352f7f91STakashi Iwai fill_hardwired = true; 1382352f7f91STakashi Iwai continue; 1383352f7f91STakashi Iwai } 1384352f7f91STakashi Iwai break; 1385352f7f91STakashi Iwai } 1386352f7f91STakashi Iwai 1387352f7f91STakashi Iwai if (badness) { 13880c8c0f56STakashi Iwai debug_badness("==> restoring best_cfg\n"); 1389352f7f91STakashi Iwai *cfg = *best_cfg; 1390352f7f91STakashi Iwai fill_and_eval_dacs(codec, best_wired, best_mio); 1391352f7f91STakashi Iwai } 1392352f7f91STakashi Iwai debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", 1393352f7f91STakashi Iwai cfg->line_out_type, best_wired, best_mio); 1394352f7f91STakashi Iwai debug_show_configs(spec, cfg); 1395352f7f91STakashi Iwai 1396352f7f91STakashi Iwai if (cfg->line_out_pins[0]) { 1397352f7f91STakashi Iwai struct nid_path *path; 1398196c1766STakashi Iwai path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]); 1399352f7f91STakashi Iwai if (path) 1400352f7f91STakashi Iwai spec->vmaster_nid = look_for_out_vol_nid(codec, path); 1401352f7f91STakashi Iwai } 1402352f7f91STakashi Iwai 1403352f7f91STakashi Iwai kfree(best_cfg); 1404352f7f91STakashi Iwai return 0; 1405352f7f91STakashi Iwai } 1406352f7f91STakashi Iwai 1407352f7f91STakashi Iwai /* add playback controls from the parsed DAC table */ 1408352f7f91STakashi Iwai static int create_multi_out_ctls(struct hda_codec *codec, 1409352f7f91STakashi Iwai const struct auto_pin_cfg *cfg) 1410352f7f91STakashi Iwai { 1411352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1412352f7f91STakashi Iwai int i, err, noutputs; 1413352f7f91STakashi Iwai 1414352f7f91STakashi Iwai noutputs = cfg->line_outs; 1415352f7f91STakashi Iwai if (spec->multi_ios > 0 && cfg->line_outs < 3) 1416352f7f91STakashi Iwai noutputs += spec->multi_ios; 1417352f7f91STakashi Iwai 1418352f7f91STakashi Iwai for (i = 0; i < noutputs; i++) { 1419352f7f91STakashi Iwai const char *name; 1420352f7f91STakashi Iwai int index; 1421196c1766STakashi Iwai hda_nid_t dac; 1422352f7f91STakashi Iwai struct nid_path *path; 1423352f7f91STakashi Iwai 1424352f7f91STakashi Iwai dac = spec->multiout.dac_nids[i]; 1425352f7f91STakashi Iwai if (!dac) 1426352f7f91STakashi Iwai continue; 1427352f7f91STakashi Iwai if (i >= cfg->line_outs) { 1428352f7f91STakashi Iwai index = 0; 1429352f7f91STakashi Iwai name = channel_name[i]; 1430352f7f91STakashi Iwai } else { 1431352f7f91STakashi Iwai name = get_line_out_pfx(spec, i, true, &index); 1432352f7f91STakashi Iwai } 1433352f7f91STakashi Iwai 1434196c1766STakashi Iwai path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); 1435352f7f91STakashi Iwai if (!path) 1436352f7f91STakashi Iwai continue; 1437352f7f91STakashi Iwai if (!name || !strcmp(name, "CLFE")) { 1438352f7f91STakashi Iwai /* Center/LFE */ 1439352f7f91STakashi Iwai err = add_vol_ctl(codec, "Center", 0, 1, path); 1440352f7f91STakashi Iwai if (err < 0) 1441352f7f91STakashi Iwai return err; 1442352f7f91STakashi Iwai err = add_vol_ctl(codec, "LFE", 0, 2, path); 1443352f7f91STakashi Iwai if (err < 0) 1444352f7f91STakashi Iwai return err; 1445352f7f91STakashi Iwai err = add_sw_ctl(codec, "Center", 0, 1, path); 1446352f7f91STakashi Iwai if (err < 0) 1447352f7f91STakashi Iwai return err; 1448352f7f91STakashi Iwai err = add_sw_ctl(codec, "LFE", 0, 2, path); 1449352f7f91STakashi Iwai if (err < 0) 1450352f7f91STakashi Iwai return err; 1451352f7f91STakashi Iwai } else { 1452352f7f91STakashi Iwai err = add_stereo_vol(codec, name, index, path); 1453352f7f91STakashi Iwai if (err < 0) 1454352f7f91STakashi Iwai return err; 1455352f7f91STakashi Iwai err = add_stereo_sw(codec, name, index, path); 1456352f7f91STakashi Iwai if (err < 0) 1457352f7f91STakashi Iwai return err; 1458352f7f91STakashi Iwai } 1459352f7f91STakashi Iwai } 1460352f7f91STakashi Iwai return 0; 1461352f7f91STakashi Iwai } 1462352f7f91STakashi Iwai 1463c2c80383STakashi Iwai static int create_extra_out(struct hda_codec *codec, int path_idx, 1464196c1766STakashi Iwai const char *pfx, int cidx) 1465352f7f91STakashi Iwai { 1466352f7f91STakashi Iwai struct nid_path *path; 1467352f7f91STakashi Iwai int err; 1468352f7f91STakashi Iwai 1469196c1766STakashi Iwai path = snd_hda_get_path_from_idx(codec, path_idx); 1470352f7f91STakashi Iwai if (!path) 1471352f7f91STakashi Iwai return 0; 1472352f7f91STakashi Iwai err = add_stereo_vol(codec, pfx, cidx, path); 1473352f7f91STakashi Iwai if (err < 0) 1474352f7f91STakashi Iwai return err; 1475352f7f91STakashi Iwai err = add_stereo_sw(codec, pfx, cidx, path); 1476352f7f91STakashi Iwai if (err < 0) 1477352f7f91STakashi Iwai return err; 1478352f7f91STakashi Iwai return 0; 1479352f7f91STakashi Iwai } 1480352f7f91STakashi Iwai 1481352f7f91STakashi Iwai /* add playback controls for speaker and HP outputs */ 1482352f7f91STakashi Iwai static int create_extra_outs(struct hda_codec *codec, int num_pins, 1483196c1766STakashi Iwai const int *paths, const char *pfx) 1484352f7f91STakashi Iwai { 1485c2c80383STakashi Iwai int i; 1486352f7f91STakashi Iwai 1487352f7f91STakashi Iwai for (i = 0; i < num_pins; i++) { 1488c2c80383STakashi Iwai const char *name; 1489c2c80383STakashi Iwai char tmp[44]; 1490c2c80383STakashi Iwai int err, idx = 0; 1491c2c80383STakashi Iwai 1492c2c80383STakashi Iwai if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) 1493c2c80383STakashi Iwai name = "Bass Speaker"; 1494c2c80383STakashi Iwai else if (num_pins >= 3) { 1495c2c80383STakashi Iwai snprintf(tmp, sizeof(tmp), "%s %s", 1496352f7f91STakashi Iwai pfx, channel_name[i]); 1497c2c80383STakashi Iwai name = tmp; 1498352f7f91STakashi Iwai } else { 1499c2c80383STakashi Iwai name = pfx; 1500c2c80383STakashi Iwai idx = i; 1501352f7f91STakashi Iwai } 1502c2c80383STakashi Iwai err = create_extra_out(codec, paths[i], name, idx); 1503352f7f91STakashi Iwai if (err < 0) 1504352f7f91STakashi Iwai return err; 1505352f7f91STakashi Iwai } 1506352f7f91STakashi Iwai return 0; 1507352f7f91STakashi Iwai } 1508352f7f91STakashi Iwai 1509352f7f91STakashi Iwai static int create_hp_out_ctls(struct hda_codec *codec) 1510352f7f91STakashi Iwai { 1511352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1512352f7f91STakashi Iwai return create_extra_outs(codec, spec->autocfg.hp_outs, 1513196c1766STakashi Iwai spec->hp_paths, 1514352f7f91STakashi Iwai "Headphone"); 1515352f7f91STakashi Iwai } 1516352f7f91STakashi Iwai 1517352f7f91STakashi Iwai static int create_speaker_out_ctls(struct hda_codec *codec) 1518352f7f91STakashi Iwai { 1519352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1520352f7f91STakashi Iwai return create_extra_outs(codec, spec->autocfg.speaker_outs, 1521196c1766STakashi Iwai spec->speaker_paths, 1522352f7f91STakashi Iwai "Speaker"); 1523352f7f91STakashi Iwai } 1524352f7f91STakashi Iwai 1525352f7f91STakashi Iwai /* 152638cf6f1aSTakashi Iwai * independent HP controls 152738cf6f1aSTakashi Iwai */ 152838cf6f1aSTakashi Iwai 152938cf6f1aSTakashi Iwai static int indep_hp_info(struct snd_kcontrol *kcontrol, 153038cf6f1aSTakashi Iwai struct snd_ctl_elem_info *uinfo) 153138cf6f1aSTakashi Iwai { 153238cf6f1aSTakashi Iwai return snd_hda_enum_bool_helper_info(kcontrol, uinfo); 153338cf6f1aSTakashi Iwai } 153438cf6f1aSTakashi Iwai 153538cf6f1aSTakashi Iwai static int indep_hp_get(struct snd_kcontrol *kcontrol, 153638cf6f1aSTakashi Iwai struct snd_ctl_elem_value *ucontrol) 153738cf6f1aSTakashi Iwai { 153838cf6f1aSTakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 153938cf6f1aSTakashi Iwai struct hda_gen_spec *spec = codec->spec; 154038cf6f1aSTakashi Iwai ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled; 154138cf6f1aSTakashi Iwai return 0; 154238cf6f1aSTakashi Iwai } 154338cf6f1aSTakashi Iwai 154438cf6f1aSTakashi Iwai static int indep_hp_put(struct snd_kcontrol *kcontrol, 154538cf6f1aSTakashi Iwai struct snd_ctl_elem_value *ucontrol) 154638cf6f1aSTakashi Iwai { 154738cf6f1aSTakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 154838cf6f1aSTakashi Iwai struct hda_gen_spec *spec = codec->spec; 154938cf6f1aSTakashi Iwai unsigned int select = ucontrol->value.enumerated.item[0]; 155038cf6f1aSTakashi Iwai int ret = 0; 155138cf6f1aSTakashi Iwai 155238cf6f1aSTakashi Iwai mutex_lock(&spec->pcm_mutex); 155338cf6f1aSTakashi Iwai if (spec->active_streams) { 155438cf6f1aSTakashi Iwai ret = -EBUSY; 155538cf6f1aSTakashi Iwai goto unlock; 155638cf6f1aSTakashi Iwai } 155738cf6f1aSTakashi Iwai 155838cf6f1aSTakashi Iwai if (spec->indep_hp_enabled != select) { 155938cf6f1aSTakashi Iwai spec->indep_hp_enabled = select; 156038cf6f1aSTakashi Iwai if (spec->indep_hp_enabled) 156138cf6f1aSTakashi Iwai spec->multiout.hp_out_nid[0] = 0; 156238cf6f1aSTakashi Iwai else 156338cf6f1aSTakashi Iwai spec->multiout.hp_out_nid[0] = spec->alt_dac_nid; 156438cf6f1aSTakashi Iwai ret = 1; 156538cf6f1aSTakashi Iwai } 156638cf6f1aSTakashi Iwai unlock: 156738cf6f1aSTakashi Iwai mutex_unlock(&spec->pcm_mutex); 156838cf6f1aSTakashi Iwai return ret; 156938cf6f1aSTakashi Iwai } 157038cf6f1aSTakashi Iwai 157138cf6f1aSTakashi Iwai static const struct snd_kcontrol_new indep_hp_ctl = { 157238cf6f1aSTakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 157338cf6f1aSTakashi Iwai .name = "Independent HP", 157438cf6f1aSTakashi Iwai .info = indep_hp_info, 157538cf6f1aSTakashi Iwai .get = indep_hp_get, 157638cf6f1aSTakashi Iwai .put = indep_hp_put, 157738cf6f1aSTakashi Iwai }; 157838cf6f1aSTakashi Iwai 157938cf6f1aSTakashi Iwai 158038cf6f1aSTakashi Iwai static int create_indep_hp_ctls(struct hda_codec *codec) 158138cf6f1aSTakashi Iwai { 158238cf6f1aSTakashi Iwai struct hda_gen_spec *spec = codec->spec; 158338cf6f1aSTakashi Iwai 158438cf6f1aSTakashi Iwai if (!spec->indep_hp) 158538cf6f1aSTakashi Iwai return 0; 158638cf6f1aSTakashi Iwai if (!spec->multiout.hp_out_nid[0]) { 158738cf6f1aSTakashi Iwai spec->indep_hp = 0; 158838cf6f1aSTakashi Iwai return 0; 158938cf6f1aSTakashi Iwai } 159038cf6f1aSTakashi Iwai 159138cf6f1aSTakashi Iwai spec->indep_hp_enabled = false; 159238cf6f1aSTakashi Iwai spec->alt_dac_nid = spec->multiout.hp_out_nid[0]; 159338cf6f1aSTakashi Iwai if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl)) 159438cf6f1aSTakashi Iwai return -ENOMEM; 159538cf6f1aSTakashi Iwai return 0; 159638cf6f1aSTakashi Iwai } 159738cf6f1aSTakashi Iwai 159838cf6f1aSTakashi Iwai /* 1599352f7f91STakashi Iwai * channel mode enum control 1600352f7f91STakashi Iwai */ 1601352f7f91STakashi Iwai 1602352f7f91STakashi Iwai static int ch_mode_info(struct snd_kcontrol *kcontrol, 1603352f7f91STakashi Iwai struct snd_ctl_elem_info *uinfo) 1604352f7f91STakashi Iwai { 1605352f7f91STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1606352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1607352f7f91STakashi Iwai 1608352f7f91STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 1609352f7f91STakashi Iwai uinfo->count = 1; 1610352f7f91STakashi Iwai uinfo->value.enumerated.items = spec->multi_ios + 1; 1611352f7f91STakashi Iwai if (uinfo->value.enumerated.item > spec->multi_ios) 1612352f7f91STakashi Iwai uinfo->value.enumerated.item = spec->multi_ios; 1613352f7f91STakashi Iwai sprintf(uinfo->value.enumerated.name, "%dch", 1614352f7f91STakashi Iwai (uinfo->value.enumerated.item + 1) * 2); 1615352f7f91STakashi Iwai return 0; 1616352f7f91STakashi Iwai } 1617352f7f91STakashi Iwai 1618352f7f91STakashi Iwai static int ch_mode_get(struct snd_kcontrol *kcontrol, 1619352f7f91STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1620352f7f91STakashi Iwai { 1621352f7f91STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1622352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1623352f7f91STakashi Iwai ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; 1624352f7f91STakashi Iwai return 0; 1625352f7f91STakashi Iwai } 1626352f7f91STakashi Iwai 1627196c1766STakashi Iwai static inline struct nid_path * 1628196c1766STakashi Iwai get_multiio_path(struct hda_codec *codec, int idx) 1629196c1766STakashi Iwai { 1630196c1766STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1631196c1766STakashi Iwai return snd_hda_get_path_from_idx(codec, 1632196c1766STakashi Iwai spec->out_paths[spec->autocfg.line_outs + idx]); 1633196c1766STakashi Iwai } 1634196c1766STakashi Iwai 1635352f7f91STakashi Iwai static int set_multi_io(struct hda_codec *codec, int idx, bool output) 1636352f7f91STakashi Iwai { 1637352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1638352f7f91STakashi Iwai hda_nid_t nid = spec->multi_io[idx].pin; 1639352f7f91STakashi Iwai struct nid_path *path; 1640352f7f91STakashi Iwai 1641196c1766STakashi Iwai path = get_multiio_path(codec, idx); 1642352f7f91STakashi Iwai if (!path) 1643352f7f91STakashi Iwai return -EINVAL; 1644352f7f91STakashi Iwai 1645352f7f91STakashi Iwai if (path->active == output) 1646352f7f91STakashi Iwai return 0; 1647352f7f91STakashi Iwai 1648352f7f91STakashi Iwai if (output) { 1649352f7f91STakashi Iwai snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); 1650352f7f91STakashi Iwai snd_hda_activate_path(codec, path, true, true); 1651d5a9f1bbSTakashi Iwai set_pin_eapd(codec, nid, true); 1652352f7f91STakashi Iwai } else { 1653d5a9f1bbSTakashi Iwai set_pin_eapd(codec, nid, false); 1654352f7f91STakashi Iwai snd_hda_activate_path(codec, path, false, true); 1655352f7f91STakashi Iwai snd_hda_set_pin_ctl_cache(codec, nid, 1656352f7f91STakashi Iwai spec->multi_io[idx].ctl_in); 1657352f7f91STakashi Iwai } 1658352f7f91STakashi Iwai return 0; 1659352f7f91STakashi Iwai } 1660352f7f91STakashi Iwai 1661352f7f91STakashi Iwai static int ch_mode_put(struct snd_kcontrol *kcontrol, 1662352f7f91STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1663352f7f91STakashi Iwai { 1664352f7f91STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1665352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1666352f7f91STakashi Iwai int i, ch; 1667352f7f91STakashi Iwai 1668352f7f91STakashi Iwai ch = ucontrol->value.enumerated.item[0]; 1669352f7f91STakashi Iwai if (ch < 0 || ch > spec->multi_ios) 1670352f7f91STakashi Iwai return -EINVAL; 1671352f7f91STakashi Iwai if (ch == (spec->ext_channel_count - 1) / 2) 1672352f7f91STakashi Iwai return 0; 1673352f7f91STakashi Iwai spec->ext_channel_count = (ch + 1) * 2; 1674352f7f91STakashi Iwai for (i = 0; i < spec->multi_ios; i++) 1675352f7f91STakashi Iwai set_multi_io(codec, i, i < ch); 1676352f7f91STakashi Iwai spec->multiout.max_channels = max(spec->ext_channel_count, 1677352f7f91STakashi Iwai spec->const_channel_count); 1678352f7f91STakashi Iwai if (spec->need_dac_fix) 1679352f7f91STakashi Iwai spec->multiout.num_dacs = spec->multiout.max_channels / 2; 1680352f7f91STakashi Iwai return 1; 1681352f7f91STakashi Iwai } 1682352f7f91STakashi Iwai 1683352f7f91STakashi Iwai static const struct snd_kcontrol_new channel_mode_enum = { 1684352f7f91STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1685352f7f91STakashi Iwai .name = "Channel Mode", 1686352f7f91STakashi Iwai .info = ch_mode_info, 1687352f7f91STakashi Iwai .get = ch_mode_get, 1688352f7f91STakashi Iwai .put = ch_mode_put, 1689352f7f91STakashi Iwai }; 1690352f7f91STakashi Iwai 1691352f7f91STakashi Iwai static int create_multi_channel_mode(struct hda_codec *codec) 1692352f7f91STakashi Iwai { 1693352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1694352f7f91STakashi Iwai 1695352f7f91STakashi Iwai if (spec->multi_ios > 0) { 169612c93df6STakashi Iwai if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum)) 1697352f7f91STakashi Iwai return -ENOMEM; 1698352f7f91STakashi Iwai } 1699352f7f91STakashi Iwai return 0; 1700352f7f91STakashi Iwai } 1701352f7f91STakashi Iwai 1702352f7f91STakashi Iwai /* 1703c30aa7b2STakashi Iwai * aamix loopback enable/disable switch 1704c30aa7b2STakashi Iwai */ 1705c30aa7b2STakashi Iwai 1706c30aa7b2STakashi Iwai #define loopback_mixing_info indep_hp_info 1707c30aa7b2STakashi Iwai 1708c30aa7b2STakashi Iwai static int loopback_mixing_get(struct snd_kcontrol *kcontrol, 1709c30aa7b2STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1710c30aa7b2STakashi Iwai { 1711c30aa7b2STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1712c30aa7b2STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1713c30aa7b2STakashi Iwai ucontrol->value.enumerated.item[0] = spec->aamix_mode; 1714c30aa7b2STakashi Iwai return 0; 1715c30aa7b2STakashi Iwai } 1716c30aa7b2STakashi Iwai 1717c30aa7b2STakashi Iwai static void update_aamix_paths(struct hda_codec *codec, bool do_mix, 1718c30aa7b2STakashi Iwai int nomix_path_idx, int mix_path_idx) 1719c30aa7b2STakashi Iwai { 1720c30aa7b2STakashi Iwai struct nid_path *nomix_path, *mix_path; 1721c30aa7b2STakashi Iwai 1722c30aa7b2STakashi Iwai nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx); 1723c30aa7b2STakashi Iwai mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx); 1724c30aa7b2STakashi Iwai if (!nomix_path || !mix_path) 1725c30aa7b2STakashi Iwai return; 1726c30aa7b2STakashi Iwai if (do_mix) { 1727c30aa7b2STakashi Iwai snd_hda_activate_path(codec, nomix_path, false, true); 1728c30aa7b2STakashi Iwai snd_hda_activate_path(codec, mix_path, true, true); 1729c30aa7b2STakashi Iwai } else { 1730c30aa7b2STakashi Iwai snd_hda_activate_path(codec, mix_path, false, true); 1731c30aa7b2STakashi Iwai snd_hda_activate_path(codec, nomix_path, true, true); 1732c30aa7b2STakashi Iwai } 1733c30aa7b2STakashi Iwai } 1734c30aa7b2STakashi Iwai 1735c30aa7b2STakashi Iwai static int loopback_mixing_put(struct snd_kcontrol *kcontrol, 1736c30aa7b2STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1737c30aa7b2STakashi Iwai { 1738c30aa7b2STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1739c30aa7b2STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1740c30aa7b2STakashi Iwai unsigned int val = ucontrol->value.enumerated.item[0]; 1741c30aa7b2STakashi Iwai 1742c30aa7b2STakashi Iwai if (val == spec->aamix_mode) 1743c30aa7b2STakashi Iwai return 0; 1744c30aa7b2STakashi Iwai spec->aamix_mode = val; 1745c30aa7b2STakashi Iwai update_aamix_paths(codec, val, spec->out_paths[0], 1746c30aa7b2STakashi Iwai spec->aamix_out_paths[0]); 1747c30aa7b2STakashi Iwai update_aamix_paths(codec, val, spec->hp_paths[0], 1748c30aa7b2STakashi Iwai spec->aamix_out_paths[1]); 1749c30aa7b2STakashi Iwai update_aamix_paths(codec, val, spec->speaker_paths[0], 1750c30aa7b2STakashi Iwai spec->aamix_out_paths[2]); 1751c30aa7b2STakashi Iwai return 1; 1752c30aa7b2STakashi Iwai } 1753c30aa7b2STakashi Iwai 1754c30aa7b2STakashi Iwai static const struct snd_kcontrol_new loopback_mixing_enum = { 1755c30aa7b2STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1756c30aa7b2STakashi Iwai .name = "Loopback Mixing", 1757c30aa7b2STakashi Iwai .info = loopback_mixing_info, 1758c30aa7b2STakashi Iwai .get = loopback_mixing_get, 1759c30aa7b2STakashi Iwai .put = loopback_mixing_put, 1760c30aa7b2STakashi Iwai }; 1761c30aa7b2STakashi Iwai 1762c30aa7b2STakashi Iwai static int create_loopback_mixing_ctl(struct hda_codec *codec) 1763c30aa7b2STakashi Iwai { 1764c30aa7b2STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1765c30aa7b2STakashi Iwai 1766c30aa7b2STakashi Iwai if (!spec->mixer_nid) 1767c30aa7b2STakashi Iwai return 0; 1768c30aa7b2STakashi Iwai if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] || 1769c30aa7b2STakashi Iwai spec->aamix_out_paths[2])) 1770c30aa7b2STakashi Iwai return 0; 1771c30aa7b2STakashi Iwai if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum)) 1772c30aa7b2STakashi Iwai return -ENOMEM; 1773c30aa7b2STakashi Iwai return 0; 1774c30aa7b2STakashi Iwai } 1775c30aa7b2STakashi Iwai 1776c30aa7b2STakashi Iwai /* 1777352f7f91STakashi Iwai * shared headphone/mic handling 1778352f7f91STakashi Iwai */ 1779352f7f91STakashi Iwai 1780352f7f91STakashi Iwai static void call_update_outputs(struct hda_codec *codec); 1781352f7f91STakashi Iwai 1782352f7f91STakashi Iwai /* for shared I/O, change the pin-control accordingly */ 1783352f7f91STakashi Iwai static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) 1784352f7f91STakashi Iwai { 1785352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1786352f7f91STakashi Iwai unsigned int val; 1787352f7f91STakashi Iwai hda_nid_t pin = spec->autocfg.inputs[1].pin; 1788352f7f91STakashi Iwai /* NOTE: this assumes that there are only two inputs, the 1789352f7f91STakashi Iwai * first is the real internal mic and the second is HP/mic jack. 1790352f7f91STakashi Iwai */ 1791352f7f91STakashi Iwai 1792352f7f91STakashi Iwai val = snd_hda_get_default_vref(codec, pin); 1793352f7f91STakashi Iwai 1794352f7f91STakashi Iwai /* This pin does not have vref caps - let's enable vref on pin 0x18 1795352f7f91STakashi Iwai instead, as suggested by Realtek */ 1796352f7f91STakashi Iwai if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { 1797352f7f91STakashi Iwai const hda_nid_t vref_pin = spec->shared_mic_vref_pin; 1798352f7f91STakashi Iwai unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); 1799352f7f91STakashi Iwai if (vref_val != AC_PINCTL_VREF_HIZ) 18007594aa33STakashi Iwai snd_hda_set_pin_ctl_cache(codec, vref_pin, 18017594aa33STakashi Iwai PIN_IN | (set_as_mic ? vref_val : 0)); 1802352f7f91STakashi Iwai } 1803352f7f91STakashi Iwai 1804352f7f91STakashi Iwai val = set_as_mic ? val | PIN_IN : PIN_HP; 18057594aa33STakashi Iwai snd_hda_set_pin_ctl_cache(codec, pin, val); 1806352f7f91STakashi Iwai 1807352f7f91STakashi Iwai spec->automute_speaker = !set_as_mic; 1808352f7f91STakashi Iwai call_update_outputs(codec); 1809352f7f91STakashi Iwai } 1810352f7f91STakashi Iwai 1811352f7f91STakashi Iwai /* create a shared input with the headphone out */ 1812352f7f91STakashi Iwai static int create_shared_input(struct hda_codec *codec) 1813352f7f91STakashi Iwai { 1814352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1815352f7f91STakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 1816352f7f91STakashi Iwai unsigned int defcfg; 1817352f7f91STakashi Iwai hda_nid_t nid; 1818352f7f91STakashi Iwai 1819352f7f91STakashi Iwai /* only one internal input pin? */ 1820352f7f91STakashi Iwai if (cfg->num_inputs != 1) 1821352f7f91STakashi Iwai return 0; 1822352f7f91STakashi Iwai defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); 1823352f7f91STakashi Iwai if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) 1824352f7f91STakashi Iwai return 0; 1825352f7f91STakashi Iwai 1826352f7f91STakashi Iwai if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) 1827352f7f91STakashi Iwai nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */ 1828352f7f91STakashi Iwai else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT) 1829352f7f91STakashi Iwai nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */ 1830352f7f91STakashi Iwai else 1831352f7f91STakashi Iwai return 0; /* both not available */ 1832352f7f91STakashi Iwai 1833352f7f91STakashi Iwai if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) 1834352f7f91STakashi Iwai return 0; /* no input */ 1835352f7f91STakashi Iwai 1836352f7f91STakashi Iwai cfg->inputs[1].pin = nid; 1837352f7f91STakashi Iwai cfg->inputs[1].type = AUTO_PIN_MIC; 1838352f7f91STakashi Iwai cfg->num_inputs = 2; 1839352f7f91STakashi Iwai spec->shared_mic_hp = 1; 1840352f7f91STakashi Iwai snd_printdd("hda-codec: Enable shared I/O jack on NID 0x%x\n", nid); 1841352f7f91STakashi Iwai return 0; 1842352f7f91STakashi Iwai } 1843352f7f91STakashi Iwai 1844352f7f91STakashi Iwai 1845352f7f91STakashi Iwai /* 1846352f7f91STakashi Iwai * Parse input paths 1847352f7f91STakashi Iwai */ 1848352f7f91STakashi Iwai 1849352f7f91STakashi Iwai #ifdef CONFIG_PM 1850352f7f91STakashi Iwai /* add the powersave loopback-list entry */ 1851352f7f91STakashi Iwai static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) 1852352f7f91STakashi Iwai { 1853352f7f91STakashi Iwai struct hda_amp_list *list; 1854352f7f91STakashi Iwai 1855352f7f91STakashi Iwai if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) 1856352f7f91STakashi Iwai return; 1857352f7f91STakashi Iwai list = spec->loopback_list + spec->num_loopbacks; 1858352f7f91STakashi Iwai list->nid = mix; 1859352f7f91STakashi Iwai list->dir = HDA_INPUT; 1860352f7f91STakashi Iwai list->idx = idx; 1861352f7f91STakashi Iwai spec->num_loopbacks++; 1862cb53c626STakashi Iwai spec->loopback.amplist = spec->loopback_list; 1863cb53c626STakashi Iwai } 1864cb53c626STakashi Iwai #else 1865352f7f91STakashi Iwai #define add_loopback_list(spec, mix, idx) /* NOP */ 1866cb53c626STakashi Iwai #endif 1867cb53c626STakashi Iwai 1868352f7f91STakashi Iwai /* create input playback/capture controls for the given pin */ 1869196c1766STakashi Iwai static int new_analog_input(struct hda_codec *codec, int input_idx, 1870196c1766STakashi Iwai hda_nid_t pin, const char *ctlname, int ctlidx, 1871352f7f91STakashi Iwai hda_nid_t mix_nid) 18721da177e4SLinus Torvalds { 1873352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1874352f7f91STakashi Iwai struct nid_path *path; 1875352f7f91STakashi Iwai unsigned int val; 1876352f7f91STakashi Iwai int err, idx; 18771da177e4SLinus Torvalds 1878352f7f91STakashi Iwai if (!nid_has_volume(codec, mix_nid, HDA_INPUT) && 1879352f7f91STakashi Iwai !nid_has_mute(codec, mix_nid, HDA_INPUT)) 1880352f7f91STakashi Iwai return 0; /* no need for analog loopback */ 1881352f7f91STakashi Iwai 18824ac0eefaSTakashi Iwai path = snd_hda_add_new_path(codec, pin, mix_nid, HDA_PARSE_ALL); 1883352f7f91STakashi Iwai if (!path) 1884352f7f91STakashi Iwai return -EINVAL; 18850c8c0f56STakashi Iwai print_nid_path("loopback", path); 1886196c1766STakashi Iwai spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path); 1887352f7f91STakashi Iwai 1888352f7f91STakashi Iwai idx = path->idx[path->depth - 1]; 1889352f7f91STakashi Iwai if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { 1890352f7f91STakashi Iwai val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); 1891352f7f91STakashi Iwai err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, val); 1892d13bd412STakashi Iwai if (err < 0) 18931da177e4SLinus Torvalds return err; 1894352f7f91STakashi Iwai path->ctls[NID_PATH_VOL_CTL] = val; 18951da177e4SLinus Torvalds } 18961da177e4SLinus Torvalds 1897352f7f91STakashi Iwai if (nid_has_mute(codec, mix_nid, HDA_INPUT)) { 1898352f7f91STakashi Iwai val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); 1899352f7f91STakashi Iwai err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, val); 1900d13bd412STakashi Iwai if (err < 0) 19011da177e4SLinus Torvalds return err; 1902352f7f91STakashi Iwai path->ctls[NID_PATH_MUTE_CTL] = val; 19031da177e4SLinus Torvalds } 19041da177e4SLinus Torvalds 1905352f7f91STakashi Iwai path->active = true; 1906352f7f91STakashi Iwai add_loopback_list(spec, mix_nid, idx); 1907352f7f91STakashi Iwai return 0; 19081da177e4SLinus Torvalds } 19091da177e4SLinus Torvalds 1910352f7f91STakashi Iwai static int is_input_pin(struct hda_codec *codec, hda_nid_t nid) 19111da177e4SLinus Torvalds { 1912352f7f91STakashi Iwai unsigned int pincap = snd_hda_query_pin_caps(codec, nid); 1913352f7f91STakashi Iwai return (pincap & AC_PINCAP_IN) != 0; 1914352f7f91STakashi Iwai } 1915352f7f91STakashi Iwai 1916352f7f91STakashi Iwai /* Parse the codec tree and retrieve ADCs */ 1917352f7f91STakashi Iwai static int fill_adc_nids(struct hda_codec *codec) 1918352f7f91STakashi Iwai { 1919352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1920352f7f91STakashi Iwai hda_nid_t nid; 1921352f7f91STakashi Iwai hda_nid_t *adc_nids = spec->adc_nids; 1922352f7f91STakashi Iwai int max_nums = ARRAY_SIZE(spec->adc_nids); 1923352f7f91STakashi Iwai int i, nums = 0; 1924352f7f91STakashi Iwai 1925352f7f91STakashi Iwai nid = codec->start_nid; 1926352f7f91STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 1927352f7f91STakashi Iwai unsigned int caps = get_wcaps(codec, nid); 1928352f7f91STakashi Iwai int type = get_wcaps_type(caps); 1929352f7f91STakashi Iwai 1930352f7f91STakashi Iwai if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) 1931352f7f91STakashi Iwai continue; 1932352f7f91STakashi Iwai adc_nids[nums] = nid; 1933352f7f91STakashi Iwai if (++nums >= max_nums) 1934352f7f91STakashi Iwai break; 1935352f7f91STakashi Iwai } 1936352f7f91STakashi Iwai spec->num_adc_nids = nums; 1937352f7f91STakashi Iwai return nums; 1938352f7f91STakashi Iwai } 1939352f7f91STakashi Iwai 1940352f7f91STakashi Iwai /* filter out invalid adc_nids that don't give all active input pins; 1941352f7f91STakashi Iwai * if needed, check whether dynamic ADC-switching is available 1942352f7f91STakashi Iwai */ 1943352f7f91STakashi Iwai static int check_dyn_adc_switch(struct hda_codec *codec) 1944352f7f91STakashi Iwai { 1945352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 1946352f7f91STakashi Iwai struct hda_input_mux *imux = &spec->input_mux; 1947352f7f91STakashi Iwai hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)]; 1948352f7f91STakashi Iwai int i, n, nums; 1949352f7f91STakashi Iwai hda_nid_t pin, adc; 1950352f7f91STakashi Iwai 1951352f7f91STakashi Iwai again: 1952352f7f91STakashi Iwai nums = 0; 1953352f7f91STakashi Iwai for (n = 0; n < spec->num_adc_nids; n++) { 1954352f7f91STakashi Iwai adc = spec->adc_nids[n]; 1955352f7f91STakashi Iwai for (i = 0; i < imux->num_items; i++) { 1956352f7f91STakashi Iwai pin = spec->imux_pins[i]; 1957352f7f91STakashi Iwai if (!is_reachable_path(codec, pin, adc)) 1958352f7f91STakashi Iwai break; 1959352f7f91STakashi Iwai } 1960352f7f91STakashi Iwai if (i >= imux->num_items) 1961352f7f91STakashi Iwai adc_nids[nums++] = adc; 1962352f7f91STakashi Iwai } 1963352f7f91STakashi Iwai 1964352f7f91STakashi Iwai if (!nums) { 1965352f7f91STakashi Iwai if (spec->shared_mic_hp) { 1966352f7f91STakashi Iwai spec->shared_mic_hp = 0; 1967352f7f91STakashi Iwai imux->num_items = 1; 1968352f7f91STakashi Iwai goto again; 1969352f7f91STakashi Iwai } 1970352f7f91STakashi Iwai 1971352f7f91STakashi Iwai /* check whether ADC-switch is possible */ 1972352f7f91STakashi Iwai for (i = 0; i < imux->num_items; i++) { 1973352f7f91STakashi Iwai pin = spec->imux_pins[i]; 1974352f7f91STakashi Iwai for (n = 0; n < spec->num_adc_nids; n++) { 1975352f7f91STakashi Iwai adc = spec->adc_nids[n]; 1976352f7f91STakashi Iwai if (is_reachable_path(codec, pin, adc)) { 1977352f7f91STakashi Iwai spec->dyn_adc_idx[i] = n; 1978352f7f91STakashi Iwai break; 1979352f7f91STakashi Iwai } 1980352f7f91STakashi Iwai } 1981352f7f91STakashi Iwai } 1982352f7f91STakashi Iwai 1983352f7f91STakashi Iwai snd_printdd("hda-codec: enabling ADC switching\n"); 1984352f7f91STakashi Iwai spec->dyn_adc_switch = 1; 1985352f7f91STakashi Iwai } else if (nums != spec->num_adc_nids) { 1986352f7f91STakashi Iwai memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t)); 1987352f7f91STakashi Iwai spec->num_adc_nids = nums; 1988352f7f91STakashi Iwai } 1989352f7f91STakashi Iwai 1990352f7f91STakashi Iwai if (imux->num_items == 1 || spec->shared_mic_hp) { 1991352f7f91STakashi Iwai snd_printdd("hda-codec: reducing to a single ADC\n"); 1992352f7f91STakashi Iwai spec->num_adc_nids = 1; /* reduce to a single ADC */ 1993352f7f91STakashi Iwai } 1994352f7f91STakashi Iwai 1995352f7f91STakashi Iwai /* single index for individual volumes ctls */ 1996352f7f91STakashi Iwai if (!spec->dyn_adc_switch && spec->multi_cap_vol) 1997352f7f91STakashi Iwai spec->num_adc_nids = 1; 1998352f7f91STakashi Iwai 19991da177e4SLinus Torvalds return 0; 20001da177e4SLinus Torvalds } 20011da177e4SLinus Torvalds 20021da177e4SLinus Torvalds /* 2003352f7f91STakashi Iwai * create playback/capture controls for input pins 20041da177e4SLinus Torvalds */ 2005352f7f91STakashi Iwai static int create_input_ctls(struct hda_codec *codec) 2006a7da6ce5STakashi Iwai { 2007352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2008352f7f91STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 2009352f7f91STakashi Iwai hda_nid_t mixer = spec->mixer_nid; 2010352f7f91STakashi Iwai struct hda_input_mux *imux = &spec->input_mux; 2011352f7f91STakashi Iwai int num_adcs; 2012352f7f91STakashi Iwai int i, c, err, type_idx = 0; 2013352f7f91STakashi Iwai const char *prev_label = NULL; 2014a7da6ce5STakashi Iwai 2015352f7f91STakashi Iwai num_adcs = fill_adc_nids(codec); 2016352f7f91STakashi Iwai if (num_adcs < 0) 2017352f7f91STakashi Iwai return 0; 2018352f7f91STakashi Iwai 2019352f7f91STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2020352f7f91STakashi Iwai hda_nid_t pin; 2021352f7f91STakashi Iwai const char *label; 2022352f7f91STakashi Iwai bool imux_added; 2023352f7f91STakashi Iwai 2024352f7f91STakashi Iwai pin = cfg->inputs[i].pin; 2025352f7f91STakashi Iwai if (!is_input_pin(codec, pin)) 2026352f7f91STakashi Iwai continue; 2027352f7f91STakashi Iwai 2028352f7f91STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2029352f7f91STakashi Iwai if (spec->shared_mic_hp && !strcmp(label, "Misc")) 2030352f7f91STakashi Iwai label = "Headphone Mic"; 2031352f7f91STakashi Iwai if (prev_label && !strcmp(label, prev_label)) 2032352f7f91STakashi Iwai type_idx++; 2033352f7f91STakashi Iwai else 2034352f7f91STakashi Iwai type_idx = 0; 2035352f7f91STakashi Iwai prev_label = label; 2036352f7f91STakashi Iwai 2037352f7f91STakashi Iwai if (mixer) { 2038352f7f91STakashi Iwai if (is_reachable_path(codec, pin, mixer)) { 2039196c1766STakashi Iwai err = new_analog_input(codec, i, pin, 2040352f7f91STakashi Iwai label, type_idx, mixer); 2041a7da6ce5STakashi Iwai if (err < 0) 2042a7da6ce5STakashi Iwai return err; 2043a7da6ce5STakashi Iwai } 2044352f7f91STakashi Iwai } 2045352f7f91STakashi Iwai 2046352f7f91STakashi Iwai imux_added = false; 2047352f7f91STakashi Iwai for (c = 0; c < num_adcs; c++) { 2048352f7f91STakashi Iwai struct nid_path *path; 2049352f7f91STakashi Iwai hda_nid_t adc = spec->adc_nids[c]; 2050352f7f91STakashi Iwai 2051352f7f91STakashi Iwai if (!is_reachable_path(codec, pin, adc)) 2052352f7f91STakashi Iwai continue; 2053352f7f91STakashi Iwai path = snd_array_new(&spec->paths); 2054352f7f91STakashi Iwai if (!path) 2055352f7f91STakashi Iwai return -ENOMEM; 2056352f7f91STakashi Iwai memset(path, 0, sizeof(*path)); 20574ac0eefaSTakashi Iwai if (!snd_hda_parse_nid_path(codec, pin, adc, HDA_PARSE_ALL, path)) { 2058352f7f91STakashi Iwai snd_printd(KERN_ERR 2059352f7f91STakashi Iwai "invalid input path 0x%x -> 0x%x\n", 2060352f7f91STakashi Iwai pin, adc); 2061352f7f91STakashi Iwai spec->paths.used--; 2062352f7f91STakashi Iwai continue; 2063352f7f91STakashi Iwai } 20640c8c0f56STakashi Iwai print_nid_path("input", path); 2065352f7f91STakashi Iwai 2066352f7f91STakashi Iwai if (!imux_added) { 2067352f7f91STakashi Iwai spec->imux_pins[imux->num_items] = pin; 2068352f7f91STakashi Iwai snd_hda_add_imux_item(imux, label, 2069352f7f91STakashi Iwai imux->num_items, NULL); 2070352f7f91STakashi Iwai imux_added = true; 2071352f7f91STakashi Iwai } 2072352f7f91STakashi Iwai } 2073352f7f91STakashi Iwai } 2074352f7f91STakashi Iwai 2075a7da6ce5STakashi Iwai return 0; 2076a7da6ce5STakashi Iwai } 2077a7da6ce5STakashi Iwai 20781da177e4SLinus Torvalds 2079352f7f91STakashi Iwai /* 2080352f7f91STakashi Iwai * input source mux 2081352f7f91STakashi Iwai */ 2082352f7f91STakashi Iwai 2083352f7f91STakashi Iwai /* get the ADC NID corresponding to the given index */ 2084352f7f91STakashi Iwai static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) 2085352f7f91STakashi Iwai { 2086352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2087352f7f91STakashi Iwai if (spec->dyn_adc_switch) 2088352f7f91STakashi Iwai adc_idx = spec->dyn_adc_idx[imux_idx]; 2089352f7f91STakashi Iwai return spec->adc_nids[adc_idx]; 209097ec558aSTakashi Iwai } 2091352f7f91STakashi Iwai 2092352f7f91STakashi Iwai static int mux_select(struct hda_codec *codec, unsigned int adc_idx, 2093352f7f91STakashi Iwai unsigned int idx); 2094352f7f91STakashi Iwai 2095352f7f91STakashi Iwai static int mux_enum_info(struct snd_kcontrol *kcontrol, 2096352f7f91STakashi Iwai struct snd_ctl_elem_info *uinfo) 2097352f7f91STakashi Iwai { 2098352f7f91STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2099352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2100352f7f91STakashi Iwai return snd_hda_input_mux_info(&spec->input_mux, uinfo); 2101352f7f91STakashi Iwai } 2102352f7f91STakashi Iwai 2103352f7f91STakashi Iwai static int mux_enum_get(struct snd_kcontrol *kcontrol, 2104352f7f91STakashi Iwai struct snd_ctl_elem_value *ucontrol) 2105352f7f91STakashi Iwai { 2106352f7f91STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2107352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2108352f7f91STakashi Iwai unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 2109352f7f91STakashi Iwai 2110352f7f91STakashi Iwai ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 21111da177e4SLinus Torvalds return 0; 21121da177e4SLinus Torvalds } 21131da177e4SLinus Torvalds 2114352f7f91STakashi Iwai static int mux_enum_put(struct snd_kcontrol *kcontrol, 2115352f7f91STakashi Iwai struct snd_ctl_elem_value *ucontrol) 21161da177e4SLinus Torvalds { 2117352f7f91STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2118352f7f91STakashi Iwai unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 2119352f7f91STakashi Iwai return mux_select(codec, adc_idx, 2120352f7f91STakashi Iwai ucontrol->value.enumerated.item[0]); 2121352f7f91STakashi Iwai } 2122352f7f91STakashi Iwai 2123352f7f91STakashi Iwai static const struct snd_kcontrol_new cap_src_temp = { 21241da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2125352f7f91STakashi Iwai .name = "Input Source", 2126352f7f91STakashi Iwai .info = mux_enum_info, 2127352f7f91STakashi Iwai .get = mux_enum_get, 2128352f7f91STakashi Iwai .put = mux_enum_put, 21291da177e4SLinus Torvalds }; 2130071c73adSTakashi Iwai 213147d46abbSTakashi Iwai /* 213247d46abbSTakashi Iwai * capture volume and capture switch ctls 213347d46abbSTakashi Iwai */ 213447d46abbSTakashi Iwai 2135352f7f91STakashi Iwai typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, 2136352f7f91STakashi Iwai struct snd_ctl_elem_value *ucontrol); 2137071c73adSTakashi Iwai 213847d46abbSTakashi Iwai /* call the given amp update function for all amps in the imux list at once */ 2139352f7f91STakashi Iwai static int cap_put_caller(struct snd_kcontrol *kcontrol, 2140352f7f91STakashi Iwai struct snd_ctl_elem_value *ucontrol, 2141352f7f91STakashi Iwai put_call_t func, int type) 2142352f7f91STakashi Iwai { 2143352f7f91STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2144352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2145352f7f91STakashi Iwai const struct hda_input_mux *imux; 2146352f7f91STakashi Iwai struct nid_path *path; 2147352f7f91STakashi Iwai int i, adc_idx, err = 0; 2148071c73adSTakashi Iwai 2149352f7f91STakashi Iwai imux = &spec->input_mux; 2150352f7f91STakashi Iwai adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 2151352f7f91STakashi Iwai mutex_lock(&codec->control_mutex); 215247d46abbSTakashi Iwai /* we use the cache-only update at first since multiple input paths 215347d46abbSTakashi Iwai * may shared the same amp; by updating only caches, the redundant 215447d46abbSTakashi Iwai * writes to hardware can be reduced. 215547d46abbSTakashi Iwai */ 2156352f7f91STakashi Iwai codec->cached_write = 1; 2157352f7f91STakashi Iwai for (i = 0; i < imux->num_items; i++) { 2158352f7f91STakashi Iwai path = snd_hda_get_nid_path(codec, spec->imux_pins[i], 2159352f7f91STakashi Iwai get_adc_nid(codec, adc_idx, i)); 2160352f7f91STakashi Iwai if (!path->ctls[type]) 2161352f7f91STakashi Iwai continue; 2162352f7f91STakashi Iwai kcontrol->private_value = path->ctls[type]; 2163352f7f91STakashi Iwai err = func(kcontrol, ucontrol); 2164352f7f91STakashi Iwai if (err < 0) 2165352f7f91STakashi Iwai goto error; 2166352f7f91STakashi Iwai } 2167352f7f91STakashi Iwai error: 2168352f7f91STakashi Iwai codec->cached_write = 0; 2169352f7f91STakashi Iwai mutex_unlock(&codec->control_mutex); 217047d46abbSTakashi Iwai snd_hda_codec_flush_amp_cache(codec); /* flush the updates */ 2171352f7f91STakashi Iwai if (err >= 0 && spec->cap_sync_hook) 2172352f7f91STakashi Iwai spec->cap_sync_hook(codec); 2173352f7f91STakashi Iwai return err; 2174352f7f91STakashi Iwai } 2175352f7f91STakashi Iwai 2176352f7f91STakashi Iwai /* capture volume ctl callbacks */ 2177352f7f91STakashi Iwai #define cap_vol_info snd_hda_mixer_amp_volume_info 2178352f7f91STakashi Iwai #define cap_vol_get snd_hda_mixer_amp_volume_get 2179352f7f91STakashi Iwai #define cap_vol_tlv snd_hda_mixer_amp_tlv 2180352f7f91STakashi Iwai 2181352f7f91STakashi Iwai static int cap_vol_put(struct snd_kcontrol *kcontrol, 2182352f7f91STakashi Iwai struct snd_ctl_elem_value *ucontrol) 2183352f7f91STakashi Iwai { 2184352f7f91STakashi Iwai return cap_put_caller(kcontrol, ucontrol, 2185352f7f91STakashi Iwai snd_hda_mixer_amp_volume_put, 2186352f7f91STakashi Iwai NID_PATH_VOL_CTL); 2187352f7f91STakashi Iwai } 2188352f7f91STakashi Iwai 2189352f7f91STakashi Iwai static const struct snd_kcontrol_new cap_vol_temp = { 2190352f7f91STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2191352f7f91STakashi Iwai .name = "Capture Volume", 2192352f7f91STakashi Iwai .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 2193352f7f91STakashi Iwai SNDRV_CTL_ELEM_ACCESS_TLV_READ | 2194352f7f91STakashi Iwai SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), 2195352f7f91STakashi Iwai .info = cap_vol_info, 2196352f7f91STakashi Iwai .get = cap_vol_get, 2197352f7f91STakashi Iwai .put = cap_vol_put, 2198352f7f91STakashi Iwai .tlv = { .c = cap_vol_tlv }, 2199352f7f91STakashi Iwai }; 2200352f7f91STakashi Iwai 2201352f7f91STakashi Iwai /* capture switch ctl callbacks */ 2202352f7f91STakashi Iwai #define cap_sw_info snd_ctl_boolean_stereo_info 2203352f7f91STakashi Iwai #define cap_sw_get snd_hda_mixer_amp_switch_get 2204352f7f91STakashi Iwai 2205352f7f91STakashi Iwai static int cap_sw_put(struct snd_kcontrol *kcontrol, 2206352f7f91STakashi Iwai struct snd_ctl_elem_value *ucontrol) 2207352f7f91STakashi Iwai { 2208352f7f91STakashi Iwai return cap_put_caller(kcontrol, ucontrol, 2209352f7f91STakashi Iwai snd_hda_mixer_amp_switch_put, 2210352f7f91STakashi Iwai NID_PATH_MUTE_CTL); 2211352f7f91STakashi Iwai } 2212352f7f91STakashi Iwai 2213352f7f91STakashi Iwai static const struct snd_kcontrol_new cap_sw_temp = { 2214352f7f91STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2215352f7f91STakashi Iwai .name = "Capture Switch", 2216352f7f91STakashi Iwai .info = cap_sw_info, 2217352f7f91STakashi Iwai .get = cap_sw_get, 2218352f7f91STakashi Iwai .put = cap_sw_put, 2219352f7f91STakashi Iwai }; 2220352f7f91STakashi Iwai 2221352f7f91STakashi Iwai static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) 2222352f7f91STakashi Iwai { 2223352f7f91STakashi Iwai hda_nid_t nid; 2224352f7f91STakashi Iwai int i, depth; 2225352f7f91STakashi Iwai 2226352f7f91STakashi Iwai path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; 2227352f7f91STakashi Iwai for (depth = 0; depth < 3; depth++) { 2228352f7f91STakashi Iwai if (depth >= path->depth) 2229352f7f91STakashi Iwai return -EINVAL; 2230352f7f91STakashi Iwai i = path->depth - depth - 1; 2231352f7f91STakashi Iwai nid = path->path[i]; 2232352f7f91STakashi Iwai if (!path->ctls[NID_PATH_VOL_CTL]) { 2233352f7f91STakashi Iwai if (nid_has_volume(codec, nid, HDA_OUTPUT)) 2234352f7f91STakashi Iwai path->ctls[NID_PATH_VOL_CTL] = 2235352f7f91STakashi Iwai HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 2236352f7f91STakashi Iwai else if (nid_has_volume(codec, nid, HDA_INPUT)) { 2237352f7f91STakashi Iwai int idx = path->idx[i]; 2238352f7f91STakashi Iwai if (!depth && codec->single_adc_amp) 2239352f7f91STakashi Iwai idx = 0; 2240352f7f91STakashi Iwai path->ctls[NID_PATH_VOL_CTL] = 2241352f7f91STakashi Iwai HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); 2242352f7f91STakashi Iwai } 2243352f7f91STakashi Iwai } 2244352f7f91STakashi Iwai if (!path->ctls[NID_PATH_MUTE_CTL]) { 2245352f7f91STakashi Iwai if (nid_has_mute(codec, nid, HDA_OUTPUT)) 2246352f7f91STakashi Iwai path->ctls[NID_PATH_MUTE_CTL] = 2247352f7f91STakashi Iwai HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); 2248352f7f91STakashi Iwai else if (nid_has_mute(codec, nid, HDA_INPUT)) { 2249352f7f91STakashi Iwai int idx = path->idx[i]; 2250352f7f91STakashi Iwai if (!depth && codec->single_adc_amp) 2251352f7f91STakashi Iwai idx = 0; 2252352f7f91STakashi Iwai path->ctls[NID_PATH_MUTE_CTL] = 2253352f7f91STakashi Iwai HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); 2254352f7f91STakashi Iwai } 2255352f7f91STakashi Iwai } 2256352f7f91STakashi Iwai } 2257352f7f91STakashi Iwai return 0; 2258352f7f91STakashi Iwai } 2259352f7f91STakashi Iwai 2260352f7f91STakashi Iwai static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) 2261352f7f91STakashi Iwai { 2262352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2263352f7f91STakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 2264352f7f91STakashi Iwai unsigned int val; 2265352f7f91STakashi Iwai int i; 2266352f7f91STakashi Iwai 2267352f7f91STakashi Iwai if (!spec->inv_dmic_split) 2268352f7f91STakashi Iwai return false; 2269352f7f91STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2270352f7f91STakashi Iwai if (cfg->inputs[i].pin != nid) 2271352f7f91STakashi Iwai continue; 2272352f7f91STakashi Iwai if (cfg->inputs[i].type != AUTO_PIN_MIC) 2273352f7f91STakashi Iwai return false; 2274352f7f91STakashi Iwai val = snd_hda_codec_get_pincfg(codec, nid); 2275352f7f91STakashi Iwai return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; 2276352f7f91STakashi Iwai } 2277352f7f91STakashi Iwai return false; 2278352f7f91STakashi Iwai } 2279352f7f91STakashi Iwai 2280352f7f91STakashi Iwai static int add_single_cap_ctl(struct hda_codec *codec, const char *label, 2281352f7f91STakashi Iwai int idx, bool is_switch, unsigned int ctl, 2282352f7f91STakashi Iwai bool inv_dmic) 2283352f7f91STakashi Iwai { 2284352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2285352f7f91STakashi Iwai char tmpname[44]; 2286352f7f91STakashi Iwai int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL; 2287352f7f91STakashi Iwai const char *sfx = is_switch ? "Switch" : "Volume"; 2288352f7f91STakashi Iwai unsigned int chs = inv_dmic ? 1 : 3; 2289352f7f91STakashi Iwai int err; 2290352f7f91STakashi Iwai 2291352f7f91STakashi Iwai if (!ctl) 2292352f7f91STakashi Iwai return 0; 2293352f7f91STakashi Iwai 2294352f7f91STakashi Iwai if (label) 2295352f7f91STakashi Iwai snprintf(tmpname, sizeof(tmpname), 2296352f7f91STakashi Iwai "%s Capture %s", label, sfx); 2297352f7f91STakashi Iwai else 2298352f7f91STakashi Iwai snprintf(tmpname, sizeof(tmpname), 2299352f7f91STakashi Iwai "Capture %s", sfx); 2300352f7f91STakashi Iwai err = add_control(spec, type, tmpname, idx, 2301352f7f91STakashi Iwai amp_val_replace_channels(ctl, chs)); 2302352f7f91STakashi Iwai if (err < 0 || !inv_dmic) 2303352f7f91STakashi Iwai return err; 2304352f7f91STakashi Iwai 2305352f7f91STakashi Iwai /* Make independent right kcontrol */ 2306352f7f91STakashi Iwai if (label) 2307352f7f91STakashi Iwai snprintf(tmpname, sizeof(tmpname), 2308352f7f91STakashi Iwai "Inverted %s Capture %s", label, sfx); 2309352f7f91STakashi Iwai else 2310352f7f91STakashi Iwai snprintf(tmpname, sizeof(tmpname), 2311352f7f91STakashi Iwai "Inverted Capture %s", sfx); 2312352f7f91STakashi Iwai return add_control(spec, type, tmpname, idx, 2313352f7f91STakashi Iwai amp_val_replace_channels(ctl, 2)); 2314352f7f91STakashi Iwai } 2315352f7f91STakashi Iwai 2316352f7f91STakashi Iwai /* create single (and simple) capture volume and switch controls */ 2317352f7f91STakashi Iwai static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, 2318352f7f91STakashi Iwai unsigned int vol_ctl, unsigned int sw_ctl, 2319352f7f91STakashi Iwai bool inv_dmic) 2320352f7f91STakashi Iwai { 2321352f7f91STakashi Iwai int err; 2322352f7f91STakashi Iwai err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); 2323352f7f91STakashi Iwai if (err < 0) 2324352f7f91STakashi Iwai return err; 2325352f7f91STakashi Iwai err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); 2326071c73adSTakashi Iwai if (err < 0) 2327071c73adSTakashi Iwai return err; 2328071c73adSTakashi Iwai return 0; 23291da177e4SLinus Torvalds } 2330071c73adSTakashi Iwai 2331352f7f91STakashi Iwai /* create bound capture volume and switch controls */ 2332352f7f91STakashi Iwai static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, 2333352f7f91STakashi Iwai unsigned int vol_ctl, unsigned int sw_ctl) 2334352f7f91STakashi Iwai { 2335352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2336352f7f91STakashi Iwai struct snd_kcontrol_new *knew; 2337352f7f91STakashi Iwai 2338352f7f91STakashi Iwai if (vol_ctl) { 233912c93df6STakashi Iwai knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp); 2340352f7f91STakashi Iwai if (!knew) 2341352f7f91STakashi Iwai return -ENOMEM; 2342352f7f91STakashi Iwai knew->index = idx; 2343352f7f91STakashi Iwai knew->private_value = vol_ctl; 2344352f7f91STakashi Iwai knew->subdevice = HDA_SUBDEV_AMP_FLAG; 2345352f7f91STakashi Iwai } 2346352f7f91STakashi Iwai if (sw_ctl) { 234712c93df6STakashi Iwai knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp); 2348352f7f91STakashi Iwai if (!knew) 2349352f7f91STakashi Iwai return -ENOMEM; 2350352f7f91STakashi Iwai knew->index = idx; 2351352f7f91STakashi Iwai knew->private_value = sw_ctl; 2352352f7f91STakashi Iwai knew->subdevice = HDA_SUBDEV_AMP_FLAG; 2353352f7f91STakashi Iwai } 2354352f7f91STakashi Iwai return 0; 2355352f7f91STakashi Iwai } 2356352f7f91STakashi Iwai 2357352f7f91STakashi Iwai /* return the vol ctl when used first in the imux list */ 2358352f7f91STakashi Iwai static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) 2359352f7f91STakashi Iwai { 2360352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2361352f7f91STakashi Iwai struct nid_path *path; 2362352f7f91STakashi Iwai unsigned int ctl; 2363352f7f91STakashi Iwai int i; 2364352f7f91STakashi Iwai 2365352f7f91STakashi Iwai path = snd_hda_get_nid_path(codec, spec->imux_pins[idx], 2366352f7f91STakashi Iwai get_adc_nid(codec, 0, idx)); 2367352f7f91STakashi Iwai if (!path) 2368352f7f91STakashi Iwai return 0; 2369352f7f91STakashi Iwai ctl = path->ctls[type]; 2370352f7f91STakashi Iwai if (!ctl) 2371352f7f91STakashi Iwai return 0; 2372352f7f91STakashi Iwai for (i = 0; i < idx - 1; i++) { 2373352f7f91STakashi Iwai path = snd_hda_get_nid_path(codec, spec->imux_pins[i], 2374352f7f91STakashi Iwai get_adc_nid(codec, 0, i)); 2375352f7f91STakashi Iwai if (path && path->ctls[type] == ctl) 2376352f7f91STakashi Iwai return 0; 2377352f7f91STakashi Iwai } 2378352f7f91STakashi Iwai return ctl; 2379352f7f91STakashi Iwai } 2380352f7f91STakashi Iwai 2381352f7f91STakashi Iwai /* create individual capture volume and switch controls per input */ 2382352f7f91STakashi Iwai static int create_multi_cap_vol_ctl(struct hda_codec *codec) 2383352f7f91STakashi Iwai { 2384352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2385352f7f91STakashi Iwai struct hda_input_mux *imux = &spec->input_mux; 2386352f7f91STakashi Iwai int i, err, type, type_idx = 0; 2387352f7f91STakashi Iwai const char *prev_label = NULL; 2388352f7f91STakashi Iwai 2389352f7f91STakashi Iwai for (i = 0; i < imux->num_items; i++) { 2390352f7f91STakashi Iwai const char *label; 2391352f7f91STakashi Iwai bool inv_dmic; 2392352f7f91STakashi Iwai label = hda_get_autocfg_input_label(codec, &spec->autocfg, i); 2393352f7f91STakashi Iwai if (prev_label && !strcmp(label, prev_label)) 2394352f7f91STakashi Iwai type_idx++; 2395352f7f91STakashi Iwai else 2396352f7f91STakashi Iwai type_idx = 0; 2397352f7f91STakashi Iwai prev_label = label; 2398352f7f91STakashi Iwai inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); 2399352f7f91STakashi Iwai 2400352f7f91STakashi Iwai for (type = 0; type < 2; type++) { 2401352f7f91STakashi Iwai err = add_single_cap_ctl(codec, label, type_idx, type, 2402352f7f91STakashi Iwai get_first_cap_ctl(codec, i, type), 2403352f7f91STakashi Iwai inv_dmic); 2404d13bd412STakashi Iwai if (err < 0) 2405071c73adSTakashi Iwai return err; 2406352f7f91STakashi Iwai } 2407352f7f91STakashi Iwai } 2408071c73adSTakashi Iwai return 0; 2409352f7f91STakashi Iwai } 2410071c73adSTakashi Iwai 2411352f7f91STakashi Iwai static int create_capture_mixers(struct hda_codec *codec) 2412352f7f91STakashi Iwai { 2413352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2414352f7f91STakashi Iwai struct hda_input_mux *imux = &spec->input_mux; 2415352f7f91STakashi Iwai int i, n, nums, err; 2416352f7f91STakashi Iwai 2417352f7f91STakashi Iwai if (spec->dyn_adc_switch) 2418352f7f91STakashi Iwai nums = 1; 2419352f7f91STakashi Iwai else 2420352f7f91STakashi Iwai nums = spec->num_adc_nids; 2421352f7f91STakashi Iwai 2422352f7f91STakashi Iwai if (!spec->auto_mic && imux->num_items > 1) { 2423352f7f91STakashi Iwai struct snd_kcontrol_new *knew; 2424624d914dSTakashi Iwai const char *name; 2425624d914dSTakashi Iwai name = nums > 1 ? "Input Source" : "Capture Source"; 2426624d914dSTakashi Iwai knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp); 2427352f7f91STakashi Iwai if (!knew) 2428352f7f91STakashi Iwai return -ENOMEM; 2429352f7f91STakashi Iwai knew->count = nums; 2430352f7f91STakashi Iwai } 2431352f7f91STakashi Iwai 2432352f7f91STakashi Iwai for (n = 0; n < nums; n++) { 2433352f7f91STakashi Iwai bool multi = false; 2434352f7f91STakashi Iwai bool inv_dmic = false; 2435352f7f91STakashi Iwai int vol, sw; 2436352f7f91STakashi Iwai 2437352f7f91STakashi Iwai vol = sw = 0; 2438352f7f91STakashi Iwai for (i = 0; i < imux->num_items; i++) { 2439352f7f91STakashi Iwai struct nid_path *path; 2440352f7f91STakashi Iwai path = snd_hda_get_nid_path(codec, spec->imux_pins[i], 2441352f7f91STakashi Iwai get_adc_nid(codec, n, i)); 2442352f7f91STakashi Iwai if (!path) 2443352f7f91STakashi Iwai continue; 2444352f7f91STakashi Iwai parse_capvol_in_path(codec, path); 2445352f7f91STakashi Iwai if (!vol) 2446352f7f91STakashi Iwai vol = path->ctls[NID_PATH_VOL_CTL]; 2447352f7f91STakashi Iwai else if (vol != path->ctls[NID_PATH_VOL_CTL]) 2448352f7f91STakashi Iwai multi = true; 2449352f7f91STakashi Iwai if (!sw) 2450352f7f91STakashi Iwai sw = path->ctls[NID_PATH_MUTE_CTL]; 2451352f7f91STakashi Iwai else if (sw != path->ctls[NID_PATH_MUTE_CTL]) 2452352f7f91STakashi Iwai multi = true; 2453352f7f91STakashi Iwai if (is_inv_dmic_pin(codec, spec->imux_pins[i])) 2454352f7f91STakashi Iwai inv_dmic = true; 2455352f7f91STakashi Iwai } 2456352f7f91STakashi Iwai 2457352f7f91STakashi Iwai if (!multi) 2458352f7f91STakashi Iwai err = create_single_cap_vol_ctl(codec, n, vol, sw, 2459352f7f91STakashi Iwai inv_dmic); 2460352f7f91STakashi Iwai else if (!spec->multi_cap_vol) 2461352f7f91STakashi Iwai err = create_bind_cap_vol_ctl(codec, n, vol, sw); 2462352f7f91STakashi Iwai else 2463352f7f91STakashi Iwai err = create_multi_cap_vol_ctl(codec); 2464d13bd412STakashi Iwai if (err < 0) 2465071c73adSTakashi Iwai return err; 2466071c73adSTakashi Iwai } 2467071c73adSTakashi Iwai 24681da177e4SLinus Torvalds return 0; 24691da177e4SLinus Torvalds } 24701da177e4SLinus Torvalds 2471352f7f91STakashi Iwai /* 2472352f7f91STakashi Iwai * add mic boosts if needed 2473352f7f91STakashi Iwai */ 2474352f7f91STakashi Iwai static int parse_mic_boost(struct hda_codec *codec) 2475352f7f91STakashi Iwai { 2476352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2477352f7f91STakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 2478352f7f91STakashi Iwai int i, err; 2479352f7f91STakashi Iwai int type_idx = 0; 2480352f7f91STakashi Iwai hda_nid_t nid; 2481352f7f91STakashi Iwai const char *prev_label = NULL; 2482352f7f91STakashi Iwai 2483352f7f91STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2484352f7f91STakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_MIC) 2485352f7f91STakashi Iwai break; 2486352f7f91STakashi Iwai nid = cfg->inputs[i].pin; 2487352f7f91STakashi Iwai if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { 2488352f7f91STakashi Iwai const char *label; 24895abd4888STakashi Iwai char boost_label[44]; 2490352f7f91STakashi Iwai struct nid_path *path; 2491352f7f91STakashi Iwai unsigned int val; 2492352f7f91STakashi Iwai 2493352f7f91STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2494352f7f91STakashi Iwai if (spec->shared_mic_hp && !strcmp(label, "Misc")) 2495352f7f91STakashi Iwai label = "Headphone Mic"; 2496352f7f91STakashi Iwai if (prev_label && !strcmp(label, prev_label)) 2497352f7f91STakashi Iwai type_idx++; 2498352f7f91STakashi Iwai else 2499352f7f91STakashi Iwai type_idx = 0; 2500352f7f91STakashi Iwai prev_label = label; 2501352f7f91STakashi Iwai 2502352f7f91STakashi Iwai snprintf(boost_label, sizeof(boost_label), 2503352f7f91STakashi Iwai "%s Boost Volume", label); 2504352f7f91STakashi Iwai val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); 2505352f7f91STakashi Iwai err = add_control(spec, HDA_CTL_WIDGET_VOL, 2506352f7f91STakashi Iwai boost_label, type_idx, val); 2507352f7f91STakashi Iwai if (err < 0) 2508352f7f91STakashi Iwai return err; 2509352f7f91STakashi Iwai 2510352f7f91STakashi Iwai path = snd_hda_get_nid_path(codec, nid, 0); 2511352f7f91STakashi Iwai if (path) 2512352f7f91STakashi Iwai path->ctls[NID_PATH_BOOST_CTL] = val; 2513352f7f91STakashi Iwai } 2514352f7f91STakashi Iwai } 2515352f7f91STakashi Iwai return 0; 2516352f7f91STakashi Iwai } 2517352f7f91STakashi Iwai 2518352f7f91STakashi Iwai /* 2519352f7f91STakashi Iwai * parse digital I/Os and set up NIDs in BIOS auto-parse mode 2520352f7f91STakashi Iwai */ 2521352f7f91STakashi Iwai static void parse_digital(struct hda_codec *codec) 2522352f7f91STakashi Iwai { 2523352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 25240c8c0f56STakashi Iwai struct nid_path *path; 2525352f7f91STakashi Iwai int i, nums; 2526352f7f91STakashi Iwai hda_nid_t dig_nid; 2527352f7f91STakashi Iwai 2528352f7f91STakashi Iwai /* support multiple SPDIFs; the secondary is set up as a slave */ 2529352f7f91STakashi Iwai nums = 0; 2530352f7f91STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 2531352f7f91STakashi Iwai hda_nid_t pin = spec->autocfg.dig_out_pins[i]; 2532352f7f91STakashi Iwai dig_nid = look_for_dac(codec, pin, true); 2533352f7f91STakashi Iwai if (!dig_nid) 2534352f7f91STakashi Iwai continue; 25354ac0eefaSTakashi Iwai path = snd_hda_add_new_path(codec, dig_nid, pin, HDA_PARSE_ALL); 25360c8c0f56STakashi Iwai if (!path) 2537352f7f91STakashi Iwai continue; 25380c8c0f56STakashi Iwai print_nid_path("digout", path); 2539e1284af7STakashi Iwai path->active = true; 2540196c1766STakashi Iwai spec->digout_paths[i] = snd_hda_get_path_idx(codec, path); 2541352f7f91STakashi Iwai if (!nums) { 2542352f7f91STakashi Iwai spec->multiout.dig_out_nid = dig_nid; 2543352f7f91STakashi Iwai spec->dig_out_type = spec->autocfg.dig_out_type[0]; 2544352f7f91STakashi Iwai } else { 2545352f7f91STakashi Iwai spec->multiout.slave_dig_outs = spec->slave_dig_outs; 2546352f7f91STakashi Iwai if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1) 2547352f7f91STakashi Iwai break; 2548352f7f91STakashi Iwai spec->slave_dig_outs[nums - 1] = dig_nid; 2549352f7f91STakashi Iwai } 2550352f7f91STakashi Iwai nums++; 2551352f7f91STakashi Iwai } 2552352f7f91STakashi Iwai 2553352f7f91STakashi Iwai if (spec->autocfg.dig_in_pin) { 2554352f7f91STakashi Iwai dig_nid = codec->start_nid; 2555352f7f91STakashi Iwai for (i = 0; i < codec->num_nodes; i++, dig_nid++) { 2556352f7f91STakashi Iwai unsigned int wcaps = get_wcaps(codec, dig_nid); 2557352f7f91STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 2558352f7f91STakashi Iwai continue; 2559352f7f91STakashi Iwai if (!(wcaps & AC_WCAP_DIGITAL)) 2560352f7f91STakashi Iwai continue; 2561352f7f91STakashi Iwai path = snd_hda_add_new_path(codec, 2562352f7f91STakashi Iwai spec->autocfg.dig_in_pin, 25634ac0eefaSTakashi Iwai dig_nid, HDA_PARSE_ALL); 2564352f7f91STakashi Iwai if (path) { 25650c8c0f56STakashi Iwai print_nid_path("digin", path); 2566352f7f91STakashi Iwai path->active = true; 2567352f7f91STakashi Iwai spec->dig_in_nid = dig_nid; 25682430d7b7STakashi Iwai spec->digin_path = snd_hda_get_path_idx(codec, path); 2569352f7f91STakashi Iwai break; 2570352f7f91STakashi Iwai } 2571352f7f91STakashi Iwai } 2572352f7f91STakashi Iwai } 2573352f7f91STakashi Iwai } 2574352f7f91STakashi Iwai 25751da177e4SLinus Torvalds 25761da177e4SLinus Torvalds /* 2577352f7f91STakashi Iwai * input MUX handling 25781da177e4SLinus Torvalds */ 25791da177e4SLinus Torvalds 2580352f7f91STakashi Iwai static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur); 2581352f7f91STakashi Iwai 2582352f7f91STakashi Iwai /* select the given imux item; either unmute exclusively or select the route */ 2583352f7f91STakashi Iwai static int mux_select(struct hda_codec *codec, unsigned int adc_idx, 2584352f7f91STakashi Iwai unsigned int idx) 2585352f7f91STakashi Iwai { 2586352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2587352f7f91STakashi Iwai const struct hda_input_mux *imux; 2588352f7f91STakashi Iwai struct nid_path *path; 2589352f7f91STakashi Iwai 2590352f7f91STakashi Iwai imux = &spec->input_mux; 2591352f7f91STakashi Iwai if (!imux->num_items) 25921da177e4SLinus Torvalds return 0; 25931da177e4SLinus Torvalds 2594352f7f91STakashi Iwai if (idx >= imux->num_items) 2595352f7f91STakashi Iwai idx = imux->num_items - 1; 2596352f7f91STakashi Iwai if (spec->cur_mux[adc_idx] == idx) 2597352f7f91STakashi Iwai return 0; 2598352f7f91STakashi Iwai 2599352f7f91STakashi Iwai path = snd_hda_get_nid_path(codec, 2600352f7f91STakashi Iwai spec->imux_pins[spec->cur_mux[adc_idx]], 2601352f7f91STakashi Iwai spec->adc_nids[adc_idx]); 2602352f7f91STakashi Iwai if (!path) 2603352f7f91STakashi Iwai return 0; 2604352f7f91STakashi Iwai if (path->active) 2605352f7f91STakashi Iwai snd_hda_activate_path(codec, path, false, false); 2606352f7f91STakashi Iwai 2607352f7f91STakashi Iwai spec->cur_mux[adc_idx] = idx; 2608352f7f91STakashi Iwai 2609352f7f91STakashi Iwai if (spec->shared_mic_hp) 2610352f7f91STakashi Iwai update_shared_mic_hp(codec, spec->cur_mux[adc_idx]); 2611352f7f91STakashi Iwai 2612352f7f91STakashi Iwai if (spec->dyn_adc_switch) 2613352f7f91STakashi Iwai dyn_adc_pcm_resetup(codec, idx); 2614352f7f91STakashi Iwai 2615352f7f91STakashi Iwai path = snd_hda_get_nid_path(codec, spec->imux_pins[idx], 2616352f7f91STakashi Iwai get_adc_nid(codec, adc_idx, idx)); 2617352f7f91STakashi Iwai if (!path) 2618352f7f91STakashi Iwai return 0; 2619352f7f91STakashi Iwai if (path->active) 2620352f7f91STakashi Iwai return 0; 2621352f7f91STakashi Iwai snd_hda_activate_path(codec, path, true, false); 2622352f7f91STakashi Iwai if (spec->cap_sync_hook) 2623352f7f91STakashi Iwai spec->cap_sync_hook(codec); 26241da177e4SLinus Torvalds return 1; 26251da177e4SLinus Torvalds } 26261da177e4SLinus Torvalds 26271da177e4SLinus Torvalds 26281da177e4SLinus Torvalds /* 2629352f7f91STakashi Iwai * Jack detections for HP auto-mute and mic-switch 26301da177e4SLinus Torvalds */ 2631352f7f91STakashi Iwai 2632352f7f91STakashi Iwai /* check each pin in the given array; returns true if any of them is plugged */ 2633352f7f91STakashi Iwai static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) 26341da177e4SLinus Torvalds { 2635352f7f91STakashi Iwai int i, present = 0; 26361da177e4SLinus Torvalds 2637352f7f91STakashi Iwai for (i = 0; i < num_pins; i++) { 2638352f7f91STakashi Iwai hda_nid_t nid = pins[i]; 2639352f7f91STakashi Iwai if (!nid) 2640352f7f91STakashi Iwai break; 2641352f7f91STakashi Iwai present |= snd_hda_jack_detect(codec, nid); 26421da177e4SLinus Torvalds } 2643352f7f91STakashi Iwai return present; 26441da177e4SLinus Torvalds } 26451da177e4SLinus Torvalds 2646352f7f91STakashi Iwai /* standard HP/line-out auto-mute helper */ 2647352f7f91STakashi Iwai static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, 2648352f7f91STakashi Iwai bool mute, bool hp_out) 26491da177e4SLinus Torvalds { 2650352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2651352f7f91STakashi Iwai unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT); 2652352f7f91STakashi Iwai int i; 26531da177e4SLinus Torvalds 2654352f7f91STakashi Iwai for (i = 0; i < num_pins; i++) { 2655352f7f91STakashi Iwai hda_nid_t nid = pins[i]; 2656352f7f91STakashi Iwai unsigned int val; 2657352f7f91STakashi Iwai if (!nid) 2658352f7f91STakashi Iwai break; 2659352f7f91STakashi Iwai /* don't reset VREF value in case it's controlling 2660352f7f91STakashi Iwai * the amp (see alc861_fixup_asus_amp_vref_0f()) 2661352f7f91STakashi Iwai */ 2662352f7f91STakashi Iwai if (spec->keep_vref_in_automute) { 2663352f7f91STakashi Iwai val = snd_hda_codec_read(codec, nid, 0, 2664352f7f91STakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 2665352f7f91STakashi Iwai val &= ~PIN_HP; 2666352f7f91STakashi Iwai } else 2667352f7f91STakashi Iwai val = 0; 2668352f7f91STakashi Iwai val |= pin_bits; 26697594aa33STakashi Iwai snd_hda_set_pin_ctl_cache(codec, nid, val); 2670d5a9f1bbSTakashi Iwai set_pin_eapd(codec, nid, !mute); 2671352f7f91STakashi Iwai } 2672352f7f91STakashi Iwai } 26731da177e4SLinus Torvalds 2674352f7f91STakashi Iwai /* Toggle outputs muting */ 26755d550e15STakashi Iwai void snd_hda_gen_update_outputs(struct hda_codec *codec) 2676352f7f91STakashi Iwai { 2677352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2678352f7f91STakashi Iwai int on; 2679352f7f91STakashi Iwai 2680352f7f91STakashi Iwai /* Control HP pins/amps depending on master_mute state; 2681352f7f91STakashi Iwai * in general, HP pins/amps control should be enabled in all cases, 2682352f7f91STakashi Iwai * but currently set only for master_mute, just to be safe 2683352f7f91STakashi Iwai */ 2684352f7f91STakashi Iwai if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */ 2685352f7f91STakashi Iwai do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), 2686352f7f91STakashi Iwai spec->autocfg.hp_pins, spec->master_mute, true); 2687352f7f91STakashi Iwai 2688352f7f91STakashi Iwai if (!spec->automute_speaker) 2689352f7f91STakashi Iwai on = 0; 2690352f7f91STakashi Iwai else 2691352f7f91STakashi Iwai on = spec->hp_jack_present | spec->line_jack_present; 2692352f7f91STakashi Iwai on |= spec->master_mute; 2693352f7f91STakashi Iwai do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), 2694352f7f91STakashi Iwai spec->autocfg.speaker_pins, on, false); 2695352f7f91STakashi Iwai 2696352f7f91STakashi Iwai /* toggle line-out mutes if needed, too */ 2697352f7f91STakashi Iwai /* if LO is a copy of either HP or Speaker, don't need to handle it */ 2698352f7f91STakashi Iwai if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || 2699352f7f91STakashi Iwai spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) 2700352f7f91STakashi Iwai return; 2701352f7f91STakashi Iwai if (!spec->automute_lo) 2702352f7f91STakashi Iwai on = 0; 2703352f7f91STakashi Iwai else 2704352f7f91STakashi Iwai on = spec->hp_jack_present; 2705352f7f91STakashi Iwai on |= spec->master_mute; 2706352f7f91STakashi Iwai do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), 2707352f7f91STakashi Iwai spec->autocfg.line_out_pins, on, false); 2708352f7f91STakashi Iwai } 27095d550e15STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs); 2710352f7f91STakashi Iwai 2711352f7f91STakashi Iwai static void call_update_outputs(struct hda_codec *codec) 2712352f7f91STakashi Iwai { 2713352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2714352f7f91STakashi Iwai if (spec->automute_hook) 2715352f7f91STakashi Iwai spec->automute_hook(codec); 2716352f7f91STakashi Iwai else 27175d550e15STakashi Iwai snd_hda_gen_update_outputs(codec); 2718352f7f91STakashi Iwai } 2719352f7f91STakashi Iwai 2720352f7f91STakashi Iwai /* standard HP-automute helper */ 27215d550e15STakashi Iwai void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) 2722352f7f91STakashi Iwai { 2723352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2724352f7f91STakashi Iwai 2725352f7f91STakashi Iwai spec->hp_jack_present = 2726352f7f91STakashi Iwai detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins), 2727352f7f91STakashi Iwai spec->autocfg.hp_pins); 2728352f7f91STakashi Iwai if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) 2729352f7f91STakashi Iwai return; 2730352f7f91STakashi Iwai call_update_outputs(codec); 2731352f7f91STakashi Iwai } 27325d550e15STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_hp_automute); 2733352f7f91STakashi Iwai 2734352f7f91STakashi Iwai /* standard line-out-automute helper */ 27355d550e15STakashi Iwai void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) 2736352f7f91STakashi Iwai { 2737352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2738352f7f91STakashi Iwai 2739352f7f91STakashi Iwai if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) 2740352f7f91STakashi Iwai return; 2741352f7f91STakashi Iwai /* check LO jack only when it's different from HP */ 2742352f7f91STakashi Iwai if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) 2743352f7f91STakashi Iwai return; 2744352f7f91STakashi Iwai 2745352f7f91STakashi Iwai spec->line_jack_present = 2746352f7f91STakashi Iwai detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), 2747352f7f91STakashi Iwai spec->autocfg.line_out_pins); 2748352f7f91STakashi Iwai if (!spec->automute_speaker || !spec->detect_lo) 2749352f7f91STakashi Iwai return; 2750352f7f91STakashi Iwai call_update_outputs(codec); 2751352f7f91STakashi Iwai } 27525d550e15STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_line_automute); 2753352f7f91STakashi Iwai 2754352f7f91STakashi Iwai /* standard mic auto-switch helper */ 27555d550e15STakashi Iwai void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack) 2756352f7f91STakashi Iwai { 2757352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2758352f7f91STakashi Iwai int i; 2759352f7f91STakashi Iwai 2760352f7f91STakashi Iwai if (!spec->auto_mic) 2761352f7f91STakashi Iwai return; 2762352f7f91STakashi Iwai 2763352f7f91STakashi Iwai for (i = spec->am_num_entries - 1; i > 0; i--) { 2764352f7f91STakashi Iwai if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) { 2765352f7f91STakashi Iwai mux_select(codec, 0, spec->am_entry[i].idx); 2766352f7f91STakashi Iwai return; 2767352f7f91STakashi Iwai } 2768352f7f91STakashi Iwai } 2769352f7f91STakashi Iwai mux_select(codec, 0, spec->am_entry[0].idx); 27701da177e4SLinus Torvalds } 27715d550e15STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_mic_autoswitch); 27721da177e4SLinus Torvalds 27731da177e4SLinus Torvalds /* 2774352f7f91STakashi Iwai * Auto-Mute mode mixer enum support 27751da177e4SLinus Torvalds */ 2776352f7f91STakashi Iwai static int automute_mode_info(struct snd_kcontrol *kcontrol, 2777352f7f91STakashi Iwai struct snd_ctl_elem_info *uinfo) 2778352f7f91STakashi Iwai { 2779352f7f91STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2780352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2781352f7f91STakashi Iwai static const char * const texts3[] = { 2782352f7f91STakashi Iwai "Disabled", "Speaker Only", "Line Out+Speaker" 27831da177e4SLinus Torvalds }; 27841da177e4SLinus Torvalds 2785352f7f91STakashi Iwai if (spec->automute_speaker_possible && spec->automute_lo_possible) 2786352f7f91STakashi Iwai return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); 2787352f7f91STakashi Iwai return snd_hda_enum_bool_helper_info(kcontrol, uinfo); 2788352f7f91STakashi Iwai } 2789352f7f91STakashi Iwai 2790352f7f91STakashi Iwai static int automute_mode_get(struct snd_kcontrol *kcontrol, 2791352f7f91STakashi Iwai struct snd_ctl_elem_value *ucontrol) 2792352f7f91STakashi Iwai { 2793352f7f91STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2794352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2795352f7f91STakashi Iwai unsigned int val = 0; 2796352f7f91STakashi Iwai if (spec->automute_speaker) 2797352f7f91STakashi Iwai val++; 2798352f7f91STakashi Iwai if (spec->automute_lo) 2799352f7f91STakashi Iwai val++; 2800352f7f91STakashi Iwai 2801352f7f91STakashi Iwai ucontrol->value.enumerated.item[0] = val; 2802352f7f91STakashi Iwai return 0; 2803352f7f91STakashi Iwai } 2804352f7f91STakashi Iwai 2805352f7f91STakashi Iwai static int automute_mode_put(struct snd_kcontrol *kcontrol, 2806352f7f91STakashi Iwai struct snd_ctl_elem_value *ucontrol) 2807352f7f91STakashi Iwai { 2808352f7f91STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2809352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2810352f7f91STakashi Iwai 2811352f7f91STakashi Iwai switch (ucontrol->value.enumerated.item[0]) { 2812352f7f91STakashi Iwai case 0: 2813352f7f91STakashi Iwai if (!spec->automute_speaker && !spec->automute_lo) 2814352f7f91STakashi Iwai return 0; 2815352f7f91STakashi Iwai spec->automute_speaker = 0; 2816352f7f91STakashi Iwai spec->automute_lo = 0; 2817352f7f91STakashi Iwai break; 2818352f7f91STakashi Iwai case 1: 2819352f7f91STakashi Iwai if (spec->automute_speaker_possible) { 2820352f7f91STakashi Iwai if (!spec->automute_lo && spec->automute_speaker) 2821352f7f91STakashi Iwai return 0; 2822352f7f91STakashi Iwai spec->automute_speaker = 1; 2823352f7f91STakashi Iwai spec->automute_lo = 0; 2824352f7f91STakashi Iwai } else if (spec->automute_lo_possible) { 2825352f7f91STakashi Iwai if (spec->automute_lo) 2826352f7f91STakashi Iwai return 0; 2827352f7f91STakashi Iwai spec->automute_lo = 1; 2828352f7f91STakashi Iwai } else 2829352f7f91STakashi Iwai return -EINVAL; 2830352f7f91STakashi Iwai break; 2831352f7f91STakashi Iwai case 2: 2832352f7f91STakashi Iwai if (!spec->automute_lo_possible || !spec->automute_speaker_possible) 2833352f7f91STakashi Iwai return -EINVAL; 2834352f7f91STakashi Iwai if (spec->automute_speaker && spec->automute_lo) 2835352f7f91STakashi Iwai return 0; 2836352f7f91STakashi Iwai spec->automute_speaker = 1; 2837352f7f91STakashi Iwai spec->automute_lo = 1; 2838352f7f91STakashi Iwai break; 2839352f7f91STakashi Iwai default: 2840352f7f91STakashi Iwai return -EINVAL; 2841352f7f91STakashi Iwai } 2842352f7f91STakashi Iwai call_update_outputs(codec); 2843352f7f91STakashi Iwai return 1; 2844352f7f91STakashi Iwai } 2845352f7f91STakashi Iwai 2846352f7f91STakashi Iwai static const struct snd_kcontrol_new automute_mode_enum = { 2847352f7f91STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2848352f7f91STakashi Iwai .name = "Auto-Mute Mode", 2849352f7f91STakashi Iwai .info = automute_mode_info, 2850352f7f91STakashi Iwai .get = automute_mode_get, 2851352f7f91STakashi Iwai .put = automute_mode_put, 2852352f7f91STakashi Iwai }; 2853352f7f91STakashi Iwai 2854352f7f91STakashi Iwai static int add_automute_mode_enum(struct hda_codec *codec) 2855352f7f91STakashi Iwai { 2856352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2857352f7f91STakashi Iwai 285812c93df6STakashi Iwai if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum)) 2859352f7f91STakashi Iwai return -ENOMEM; 2860352f7f91STakashi Iwai return 0; 2861352f7f91STakashi Iwai } 2862352f7f91STakashi Iwai 2863352f7f91STakashi Iwai /* 2864352f7f91STakashi Iwai * Check the availability of HP/line-out auto-mute; 2865352f7f91STakashi Iwai * Set up appropriately if really supported 2866352f7f91STakashi Iwai */ 2867352f7f91STakashi Iwai static int check_auto_mute_availability(struct hda_codec *codec) 2868352f7f91STakashi Iwai { 2869352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2870352f7f91STakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 2871352f7f91STakashi Iwai int present = 0; 2872352f7f91STakashi Iwai int i, err; 2873352f7f91STakashi Iwai 2874352f7f91STakashi Iwai if (cfg->hp_pins[0]) 2875352f7f91STakashi Iwai present++; 2876352f7f91STakashi Iwai if (cfg->line_out_pins[0]) 2877352f7f91STakashi Iwai present++; 2878352f7f91STakashi Iwai if (cfg->speaker_pins[0]) 2879352f7f91STakashi Iwai present++; 2880352f7f91STakashi Iwai if (present < 2) /* need two different output types */ 2881352f7f91STakashi Iwai return 0; 2882352f7f91STakashi Iwai 2883352f7f91STakashi Iwai if (!cfg->speaker_pins[0] && 2884352f7f91STakashi Iwai cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { 2885352f7f91STakashi Iwai memcpy(cfg->speaker_pins, cfg->line_out_pins, 2886352f7f91STakashi Iwai sizeof(cfg->speaker_pins)); 2887352f7f91STakashi Iwai cfg->speaker_outs = cfg->line_outs; 2888352f7f91STakashi Iwai } 2889352f7f91STakashi Iwai 2890352f7f91STakashi Iwai if (!cfg->hp_pins[0] && 2891352f7f91STakashi Iwai cfg->line_out_type == AUTO_PIN_HP_OUT) { 2892352f7f91STakashi Iwai memcpy(cfg->hp_pins, cfg->line_out_pins, 2893352f7f91STakashi Iwai sizeof(cfg->hp_pins)); 2894352f7f91STakashi Iwai cfg->hp_outs = cfg->line_outs; 2895352f7f91STakashi Iwai } 2896352f7f91STakashi Iwai 2897352f7f91STakashi Iwai for (i = 0; i < cfg->hp_outs; i++) { 2898352f7f91STakashi Iwai hda_nid_t nid = cfg->hp_pins[i]; 2899352f7f91STakashi Iwai if (!is_jack_detectable(codec, nid)) 2900352f7f91STakashi Iwai continue; 2901352f7f91STakashi Iwai snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n", 2902352f7f91STakashi Iwai nid); 2903352f7f91STakashi Iwai snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT, 29042e03e952STakashi Iwai spec->hp_automute_hook ? 29052e03e952STakashi Iwai spec->hp_automute_hook : 29065d550e15STakashi Iwai snd_hda_gen_hp_automute); 2907352f7f91STakashi Iwai spec->detect_hp = 1; 2908352f7f91STakashi Iwai } 2909352f7f91STakashi Iwai 2910352f7f91STakashi Iwai if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { 2911352f7f91STakashi Iwai if (cfg->speaker_outs) 2912352f7f91STakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 2913352f7f91STakashi Iwai hda_nid_t nid = cfg->line_out_pins[i]; 2914352f7f91STakashi Iwai if (!is_jack_detectable(codec, nid)) 2915352f7f91STakashi Iwai continue; 2916352f7f91STakashi Iwai snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid); 2917352f7f91STakashi Iwai snd_hda_jack_detect_enable_callback(codec, nid, 2918352f7f91STakashi Iwai HDA_GEN_FRONT_EVENT, 29192e03e952STakashi Iwai spec->line_automute_hook ? 29202e03e952STakashi Iwai spec->line_automute_hook : 29215d550e15STakashi Iwai snd_hda_gen_line_automute); 2922352f7f91STakashi Iwai spec->detect_lo = 1; 2923352f7f91STakashi Iwai } 2924352f7f91STakashi Iwai spec->automute_lo_possible = spec->detect_hp; 2925352f7f91STakashi Iwai } 2926352f7f91STakashi Iwai 2927352f7f91STakashi Iwai spec->automute_speaker_possible = cfg->speaker_outs && 2928352f7f91STakashi Iwai (spec->detect_hp || spec->detect_lo); 2929352f7f91STakashi Iwai 2930352f7f91STakashi Iwai spec->automute_lo = spec->automute_lo_possible; 2931352f7f91STakashi Iwai spec->automute_speaker = spec->automute_speaker_possible; 2932352f7f91STakashi Iwai 2933352f7f91STakashi Iwai if (spec->automute_speaker_possible || spec->automute_lo_possible) { 2934352f7f91STakashi Iwai /* create a control for automute mode */ 2935352f7f91STakashi Iwai err = add_automute_mode_enum(codec); 2936352f7f91STakashi Iwai if (err < 0) 2937352f7f91STakashi Iwai return err; 2938352f7f91STakashi Iwai } 2939352f7f91STakashi Iwai return 0; 2940352f7f91STakashi Iwai } 2941352f7f91STakashi Iwai 2942352f7f91STakashi Iwai /* return the position of NID in the list, or -1 if not found */ 2943352f7f91STakashi Iwai static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) 2944352f7f91STakashi Iwai { 2945352f7f91STakashi Iwai int i; 2946352f7f91STakashi Iwai for (i = 0; i < nums; i++) 2947352f7f91STakashi Iwai if (list[i] == nid) 2948352f7f91STakashi Iwai return i; 2949352f7f91STakashi Iwai return -1; 2950352f7f91STakashi Iwai } 2951352f7f91STakashi Iwai 2952352f7f91STakashi Iwai /* check whether all auto-mic pins are valid; setup indices if OK */ 2953352f7f91STakashi Iwai static bool auto_mic_check_imux(struct hda_codec *codec) 2954352f7f91STakashi Iwai { 2955352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2956352f7f91STakashi Iwai const struct hda_input_mux *imux; 2957352f7f91STakashi Iwai int i; 2958352f7f91STakashi Iwai 2959352f7f91STakashi Iwai imux = &spec->input_mux; 2960352f7f91STakashi Iwai for (i = 0; i < spec->am_num_entries; i++) { 2961352f7f91STakashi Iwai spec->am_entry[i].idx = 2962352f7f91STakashi Iwai find_idx_in_nid_list(spec->am_entry[i].pin, 2963352f7f91STakashi Iwai spec->imux_pins, imux->num_items); 2964352f7f91STakashi Iwai if (spec->am_entry[i].idx < 0) 2965352f7f91STakashi Iwai return false; /* no corresponding imux */ 2966352f7f91STakashi Iwai } 2967352f7f91STakashi Iwai 2968352f7f91STakashi Iwai /* we don't need the jack detection for the first pin */ 2969352f7f91STakashi Iwai for (i = 1; i < spec->am_num_entries; i++) 2970352f7f91STakashi Iwai snd_hda_jack_detect_enable_callback(codec, 2971352f7f91STakashi Iwai spec->am_entry[i].pin, 2972352f7f91STakashi Iwai HDA_GEN_MIC_EVENT, 29732e03e952STakashi Iwai spec->mic_autoswitch_hook ? 29742e03e952STakashi Iwai spec->mic_autoswitch_hook : 29755d550e15STakashi Iwai snd_hda_gen_mic_autoswitch); 2976352f7f91STakashi Iwai return true; 2977352f7f91STakashi Iwai } 2978352f7f91STakashi Iwai 2979352f7f91STakashi Iwai static int compare_attr(const void *ap, const void *bp) 2980352f7f91STakashi Iwai { 2981352f7f91STakashi Iwai const struct automic_entry *a = ap; 2982352f7f91STakashi Iwai const struct automic_entry *b = bp; 2983352f7f91STakashi Iwai return (int)(a->attr - b->attr); 2984352f7f91STakashi Iwai } 2985352f7f91STakashi Iwai 2986352f7f91STakashi Iwai /* 2987352f7f91STakashi Iwai * Check the availability of auto-mic switch; 2988352f7f91STakashi Iwai * Set up if really supported 2989352f7f91STakashi Iwai */ 2990352f7f91STakashi Iwai static int check_auto_mic_availability(struct hda_codec *codec) 2991352f7f91STakashi Iwai { 2992352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 2993352f7f91STakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 2994352f7f91STakashi Iwai unsigned int types; 2995352f7f91STakashi Iwai int i, num_pins; 2996352f7f91STakashi Iwai 2997352f7f91STakashi Iwai types = 0; 2998352f7f91STakashi Iwai num_pins = 0; 2999352f7f91STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 3000352f7f91STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 3001352f7f91STakashi Iwai unsigned int attr; 3002352f7f91STakashi Iwai attr = snd_hda_codec_get_pincfg(codec, nid); 3003352f7f91STakashi Iwai attr = snd_hda_get_input_pin_attr(attr); 3004352f7f91STakashi Iwai if (types & (1 << attr)) 3005352f7f91STakashi Iwai return 0; /* already occupied */ 3006352f7f91STakashi Iwai switch (attr) { 3007352f7f91STakashi Iwai case INPUT_PIN_ATTR_INT: 3008352f7f91STakashi Iwai if (cfg->inputs[i].type != AUTO_PIN_MIC) 3009352f7f91STakashi Iwai return 0; /* invalid type */ 3010352f7f91STakashi Iwai break; 3011352f7f91STakashi Iwai case INPUT_PIN_ATTR_UNUSED: 3012352f7f91STakashi Iwai return 0; /* invalid entry */ 3013352f7f91STakashi Iwai default: 3014352f7f91STakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 3015352f7f91STakashi Iwai return 0; /* invalid type */ 3016352f7f91STakashi Iwai if (!spec->line_in_auto_switch && 3017352f7f91STakashi Iwai cfg->inputs[i].type != AUTO_PIN_MIC) 3018352f7f91STakashi Iwai return 0; /* only mic is allowed */ 3019352f7f91STakashi Iwai if (!is_jack_detectable(codec, nid)) 3020352f7f91STakashi Iwai return 0; /* no unsol support */ 3021352f7f91STakashi Iwai break; 3022352f7f91STakashi Iwai } 3023352f7f91STakashi Iwai if (num_pins >= MAX_AUTO_MIC_PINS) 3024352f7f91STakashi Iwai return 0; 3025352f7f91STakashi Iwai types |= (1 << attr); 3026352f7f91STakashi Iwai spec->am_entry[num_pins].pin = nid; 3027352f7f91STakashi Iwai spec->am_entry[num_pins].attr = attr; 3028352f7f91STakashi Iwai num_pins++; 3029352f7f91STakashi Iwai } 3030352f7f91STakashi Iwai 3031352f7f91STakashi Iwai if (num_pins < 2) 3032352f7f91STakashi Iwai return 0; 3033352f7f91STakashi Iwai 3034352f7f91STakashi Iwai spec->am_num_entries = num_pins; 3035352f7f91STakashi Iwai /* sort the am_entry in the order of attr so that the pin with a 3036352f7f91STakashi Iwai * higher attr will be selected when the jack is plugged. 3037352f7f91STakashi Iwai */ 3038352f7f91STakashi Iwai sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), 3039352f7f91STakashi Iwai compare_attr, NULL); 3040352f7f91STakashi Iwai 3041352f7f91STakashi Iwai if (!auto_mic_check_imux(codec)) 3042352f7f91STakashi Iwai return 0; 3043352f7f91STakashi Iwai 3044352f7f91STakashi Iwai spec->auto_mic = 1; 3045352f7f91STakashi Iwai spec->num_adc_nids = 1; 3046352f7f91STakashi Iwai spec->cur_mux[0] = spec->am_entry[0].idx; 3047352f7f91STakashi Iwai snd_printdd("hda-codec: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", 3048352f7f91STakashi Iwai spec->am_entry[0].pin, 3049352f7f91STakashi Iwai spec->am_entry[1].pin, 3050352f7f91STakashi Iwai spec->am_entry[2].pin); 3051352f7f91STakashi Iwai 3052352f7f91STakashi Iwai return 0; 3053352f7f91STakashi Iwai } 3054352f7f91STakashi Iwai 3055352f7f91STakashi Iwai 30569eb413e5STakashi Iwai /* 30579eb413e5STakashi Iwai * Parse the given BIOS configuration and set up the hda_gen_spec 30589eb413e5STakashi Iwai * 30599eb413e5STakashi Iwai * return 1 if successful, 0 if the proper config is not found, 3060352f7f91STakashi Iwai * or a negative error code 3061352f7f91STakashi Iwai */ 3062352f7f91STakashi Iwai int snd_hda_gen_parse_auto_config(struct hda_codec *codec, 30639eb413e5STakashi Iwai struct auto_pin_cfg *cfg) 3064352f7f91STakashi Iwai { 3065352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3066352f7f91STakashi Iwai int err; 3067352f7f91STakashi Iwai 30689eb413e5STakashi Iwai if (cfg != &spec->autocfg) { 30699eb413e5STakashi Iwai spec->autocfg = *cfg; 30709eb413e5STakashi Iwai cfg = &spec->autocfg; 30719eb413e5STakashi Iwai } 30729eb413e5STakashi Iwai 3073352f7f91STakashi Iwai if (!cfg->line_outs) { 3074352f7f91STakashi Iwai if (cfg->dig_outs || cfg->dig_in_pin) { 3075352f7f91STakashi Iwai spec->multiout.max_channels = 2; 3076352f7f91STakashi Iwai spec->no_analog = 1; 3077352f7f91STakashi Iwai goto dig_only; 3078352f7f91STakashi Iwai } 3079352f7f91STakashi Iwai return 0; /* can't find valid BIOS pin config */ 3080352f7f91STakashi Iwai } 3081352f7f91STakashi Iwai 3082352f7f91STakashi Iwai if (!spec->no_primary_hp && 3083352f7f91STakashi Iwai cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && 3084352f7f91STakashi Iwai cfg->line_outs <= cfg->hp_outs) { 3085352f7f91STakashi Iwai /* use HP as primary out */ 3086352f7f91STakashi Iwai cfg->speaker_outs = cfg->line_outs; 3087352f7f91STakashi Iwai memcpy(cfg->speaker_pins, cfg->line_out_pins, 3088352f7f91STakashi Iwai sizeof(cfg->speaker_pins)); 3089352f7f91STakashi Iwai cfg->line_outs = cfg->hp_outs; 3090352f7f91STakashi Iwai memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); 3091352f7f91STakashi Iwai cfg->hp_outs = 0; 3092352f7f91STakashi Iwai memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); 3093352f7f91STakashi Iwai cfg->line_out_type = AUTO_PIN_HP_OUT; 3094352f7f91STakashi Iwai } 3095352f7f91STakashi Iwai 3096352f7f91STakashi Iwai err = parse_output_paths(codec); 3097352f7f91STakashi Iwai if (err < 0) 3098352f7f91STakashi Iwai return err; 3099352f7f91STakashi Iwai err = create_multi_channel_mode(codec); 3100352f7f91STakashi Iwai if (err < 0) 3101352f7f91STakashi Iwai return err; 3102352f7f91STakashi Iwai err = create_multi_out_ctls(codec, cfg); 3103352f7f91STakashi Iwai if (err < 0) 3104352f7f91STakashi Iwai return err; 3105352f7f91STakashi Iwai err = create_hp_out_ctls(codec); 3106352f7f91STakashi Iwai if (err < 0) 3107352f7f91STakashi Iwai return err; 3108352f7f91STakashi Iwai err = create_speaker_out_ctls(codec); 3109352f7f91STakashi Iwai if (err < 0) 3110352f7f91STakashi Iwai return err; 311138cf6f1aSTakashi Iwai err = create_indep_hp_ctls(codec); 311238cf6f1aSTakashi Iwai if (err < 0) 311338cf6f1aSTakashi Iwai return err; 3114c30aa7b2STakashi Iwai err = create_loopback_mixing_ctl(codec); 3115c30aa7b2STakashi Iwai if (err < 0) 3116c30aa7b2STakashi Iwai return err; 3117352f7f91STakashi Iwai err = create_shared_input(codec); 3118352f7f91STakashi Iwai if (err < 0) 3119352f7f91STakashi Iwai return err; 3120352f7f91STakashi Iwai err = create_input_ctls(codec); 3121352f7f91STakashi Iwai if (err < 0) 3122352f7f91STakashi Iwai return err; 3123352f7f91STakashi Iwai 3124352f7f91STakashi Iwai /* check the multiple speaker pins */ 3125352f7f91STakashi Iwai if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) 3126352f7f91STakashi Iwai spec->const_channel_count = cfg->line_outs * 2; 3127352f7f91STakashi Iwai else 3128352f7f91STakashi Iwai spec->const_channel_count = cfg->speaker_outs * 2; 3129352f7f91STakashi Iwai 3130352f7f91STakashi Iwai if (spec->multi_ios > 0) 3131352f7f91STakashi Iwai spec->multiout.max_channels = max(spec->ext_channel_count, 3132352f7f91STakashi Iwai spec->const_channel_count); 3133352f7f91STakashi Iwai else 3134352f7f91STakashi Iwai spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3135352f7f91STakashi Iwai 3136352f7f91STakashi Iwai err = check_auto_mute_availability(codec); 3137352f7f91STakashi Iwai if (err < 0) 3138352f7f91STakashi Iwai return err; 3139352f7f91STakashi Iwai 3140352f7f91STakashi Iwai err = check_dyn_adc_switch(codec); 3141352f7f91STakashi Iwai if (err < 0) 3142352f7f91STakashi Iwai return err; 3143352f7f91STakashi Iwai 3144352f7f91STakashi Iwai if (!spec->shared_mic_hp) { 3145352f7f91STakashi Iwai err = check_auto_mic_availability(codec); 3146352f7f91STakashi Iwai if (err < 0) 3147352f7f91STakashi Iwai return err; 3148352f7f91STakashi Iwai } 3149352f7f91STakashi Iwai 3150352f7f91STakashi Iwai err = create_capture_mixers(codec); 3151352f7f91STakashi Iwai if (err < 0) 3152352f7f91STakashi Iwai return err; 3153352f7f91STakashi Iwai 3154352f7f91STakashi Iwai err = parse_mic_boost(codec); 3155352f7f91STakashi Iwai if (err < 0) 3156352f7f91STakashi Iwai return err; 3157352f7f91STakashi Iwai 3158352f7f91STakashi Iwai dig_only: 3159352f7f91STakashi Iwai parse_digital(codec); 3160352f7f91STakashi Iwai 3161352f7f91STakashi Iwai return 1; 3162352f7f91STakashi Iwai } 3163352f7f91STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_parse_auto_config); 3164352f7f91STakashi Iwai 3165352f7f91STakashi Iwai 3166352f7f91STakashi Iwai /* 3167352f7f91STakashi Iwai * Build control elements 3168352f7f91STakashi Iwai */ 3169352f7f91STakashi Iwai 3170352f7f91STakashi Iwai /* slave controls for virtual master */ 3171352f7f91STakashi Iwai static const char * const slave_pfxs[] = { 3172352f7f91STakashi Iwai "Front", "Surround", "Center", "LFE", "Side", 3173352f7f91STakashi Iwai "Headphone", "Speaker", "Mono", "Line Out", 3174352f7f91STakashi Iwai "CLFE", "Bass Speaker", "PCM", 3175ee79c69aSTakashi Iwai "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side", 3176ee79c69aSTakashi Iwai "Headphone Front", "Headphone Surround", "Headphone CLFE", 3177ee79c69aSTakashi Iwai "Headphone Side", 3178352f7f91STakashi Iwai NULL, 3179352f7f91STakashi Iwai }; 3180352f7f91STakashi Iwai 3181352f7f91STakashi Iwai int snd_hda_gen_build_controls(struct hda_codec *codec) 3182352f7f91STakashi Iwai { 3183352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3184352f7f91STakashi Iwai int err; 3185352f7f91STakashi Iwai 318636502d02STakashi Iwai if (spec->kctls.used) { 3187352f7f91STakashi Iwai err = snd_hda_add_new_ctls(codec, spec->kctls.list); 3188352f7f91STakashi Iwai if (err < 0) 3189352f7f91STakashi Iwai return err; 319036502d02STakashi Iwai } 3191352f7f91STakashi Iwai 3192352f7f91STakashi Iwai if (spec->multiout.dig_out_nid) { 3193352f7f91STakashi Iwai err = snd_hda_create_dig_out_ctls(codec, 3194352f7f91STakashi Iwai spec->multiout.dig_out_nid, 3195352f7f91STakashi Iwai spec->multiout.dig_out_nid, 3196352f7f91STakashi Iwai spec->pcm_rec[1].pcm_type); 3197352f7f91STakashi Iwai if (err < 0) 3198352f7f91STakashi Iwai return err; 3199352f7f91STakashi Iwai if (!spec->no_analog) { 3200352f7f91STakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 3201352f7f91STakashi Iwai &spec->multiout); 3202352f7f91STakashi Iwai if (err < 0) 3203352f7f91STakashi Iwai return err; 3204352f7f91STakashi Iwai spec->multiout.share_spdif = 1; 3205352f7f91STakashi Iwai } 3206352f7f91STakashi Iwai } 3207352f7f91STakashi Iwai if (spec->dig_in_nid) { 3208352f7f91STakashi Iwai err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 3209352f7f91STakashi Iwai if (err < 0) 3210352f7f91STakashi Iwai return err; 3211352f7f91STakashi Iwai } 3212352f7f91STakashi Iwai 3213352f7f91STakashi Iwai /* if we have no master control, let's create it */ 3214352f7f91STakashi Iwai if (!spec->no_analog && 3215352f7f91STakashi Iwai !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { 3216352f7f91STakashi Iwai unsigned int vmaster_tlv[4]; 3217352f7f91STakashi Iwai snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, 3218352f7f91STakashi Iwai HDA_OUTPUT, vmaster_tlv); 3219352f7f91STakashi Iwai err = snd_hda_add_vmaster(codec, "Master Playback Volume", 3220352f7f91STakashi Iwai vmaster_tlv, slave_pfxs, 3221352f7f91STakashi Iwai "Playback Volume"); 3222352f7f91STakashi Iwai if (err < 0) 3223352f7f91STakashi Iwai return err; 3224352f7f91STakashi Iwai } 3225352f7f91STakashi Iwai if (!spec->no_analog && 3226352f7f91STakashi Iwai !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { 3227352f7f91STakashi Iwai err = __snd_hda_add_vmaster(codec, "Master Playback Switch", 3228352f7f91STakashi Iwai NULL, slave_pfxs, 3229352f7f91STakashi Iwai "Playback Switch", 3230352f7f91STakashi Iwai true, &spec->vmaster_mute.sw_kctl); 3231352f7f91STakashi Iwai if (err < 0) 3232352f7f91STakashi Iwai return err; 3233352f7f91STakashi Iwai if (spec->vmaster_mute.hook) 3234fd25a97aSTakashi Iwai snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, 3235fd25a97aSTakashi Iwai spec->vmaster_mute_enum); 3236352f7f91STakashi Iwai } 3237352f7f91STakashi Iwai 3238352f7f91STakashi Iwai free_kctls(spec); /* no longer needed */ 3239352f7f91STakashi Iwai 3240352f7f91STakashi Iwai if (spec->shared_mic_hp) { 3241352f7f91STakashi Iwai int err; 3242352f7f91STakashi Iwai int nid = spec->autocfg.inputs[1].pin; 3243352f7f91STakashi Iwai err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0); 3244352f7f91STakashi Iwai if (err < 0) 3245352f7f91STakashi Iwai return err; 3246352f7f91STakashi Iwai err = snd_hda_jack_detect_enable(codec, nid, 0); 3247352f7f91STakashi Iwai if (err < 0) 3248352f7f91STakashi Iwai return err; 3249352f7f91STakashi Iwai } 3250352f7f91STakashi Iwai 3251352f7f91STakashi Iwai err = snd_hda_jack_add_kctls(codec, &spec->autocfg); 3252352f7f91STakashi Iwai if (err < 0) 3253352f7f91STakashi Iwai return err; 3254352f7f91STakashi Iwai 3255352f7f91STakashi Iwai return 0; 3256352f7f91STakashi Iwai } 3257352f7f91STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_build_controls); 3258352f7f91STakashi Iwai 3259352f7f91STakashi Iwai 3260352f7f91STakashi Iwai /* 3261352f7f91STakashi Iwai * PCM definitions 3262352f7f91STakashi Iwai */ 3263352f7f91STakashi Iwai 3264e6b85f3cSTakashi Iwai static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo, 3265e6b85f3cSTakashi Iwai struct hda_codec *codec, 3266e6b85f3cSTakashi Iwai struct snd_pcm_substream *substream, 3267e6b85f3cSTakashi Iwai int action) 3268e6b85f3cSTakashi Iwai { 3269e6b85f3cSTakashi Iwai struct hda_gen_spec *spec = codec->spec; 3270e6b85f3cSTakashi Iwai if (spec->pcm_playback_hook) 3271e6b85f3cSTakashi Iwai spec->pcm_playback_hook(hinfo, codec, substream, action); 3272e6b85f3cSTakashi Iwai } 3273e6b85f3cSTakashi Iwai 3274352f7f91STakashi Iwai /* 3275352f7f91STakashi Iwai * Analog playback callbacks 3276352f7f91STakashi Iwai */ 3277352f7f91STakashi Iwai static int playback_pcm_open(struct hda_pcm_stream *hinfo, 3278352f7f91STakashi Iwai struct hda_codec *codec, 3279352f7f91STakashi Iwai struct snd_pcm_substream *substream) 3280352f7f91STakashi Iwai { 3281352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 328238cf6f1aSTakashi Iwai int err; 328338cf6f1aSTakashi Iwai 328438cf6f1aSTakashi Iwai mutex_lock(&spec->pcm_mutex); 328538cf6f1aSTakashi Iwai err = snd_hda_multi_out_analog_open(codec, 328638cf6f1aSTakashi Iwai &spec->multiout, substream, 3287352f7f91STakashi Iwai hinfo); 3288e6b85f3cSTakashi Iwai if (!err) { 328938cf6f1aSTakashi Iwai spec->active_streams |= 1 << STREAM_MULTI_OUT; 3290e6b85f3cSTakashi Iwai call_pcm_playback_hook(hinfo, codec, substream, 3291e6b85f3cSTakashi Iwai HDA_GEN_PCM_ACT_OPEN); 3292e6b85f3cSTakashi Iwai } 329338cf6f1aSTakashi Iwai mutex_unlock(&spec->pcm_mutex); 329438cf6f1aSTakashi Iwai return err; 3295352f7f91STakashi Iwai } 3296352f7f91STakashi Iwai 3297352f7f91STakashi Iwai static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, 329897ec558aSTakashi Iwai struct hda_codec *codec, 329997ec558aSTakashi Iwai unsigned int stream_tag, 330097ec558aSTakashi Iwai unsigned int format, 330197ec558aSTakashi Iwai struct snd_pcm_substream *substream) 330297ec558aSTakashi Iwai { 3303352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3304e6b85f3cSTakashi Iwai int err; 3305e6b85f3cSTakashi Iwai 3306e6b85f3cSTakashi Iwai err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout, 3307352f7f91STakashi Iwai stream_tag, format, substream); 3308e6b85f3cSTakashi Iwai if (!err) 3309e6b85f3cSTakashi Iwai call_pcm_playback_hook(hinfo, codec, substream, 3310e6b85f3cSTakashi Iwai HDA_GEN_PCM_ACT_PREPARE); 3311e6b85f3cSTakashi Iwai return err; 3312352f7f91STakashi Iwai } 331397ec558aSTakashi Iwai 3314352f7f91STakashi Iwai static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 3315352f7f91STakashi Iwai struct hda_codec *codec, 3316352f7f91STakashi Iwai struct snd_pcm_substream *substream) 3317352f7f91STakashi Iwai { 3318352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3319e6b85f3cSTakashi Iwai int err; 3320e6b85f3cSTakashi Iwai 3321e6b85f3cSTakashi Iwai err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 3322e6b85f3cSTakashi Iwai if (!err) 3323e6b85f3cSTakashi Iwai call_pcm_playback_hook(hinfo, codec, substream, 3324e6b85f3cSTakashi Iwai HDA_GEN_PCM_ACT_CLEANUP); 3325e6b85f3cSTakashi Iwai return err; 3326352f7f91STakashi Iwai } 3327352f7f91STakashi Iwai 332838cf6f1aSTakashi Iwai static int playback_pcm_close(struct hda_pcm_stream *hinfo, 332938cf6f1aSTakashi Iwai struct hda_codec *codec, 333038cf6f1aSTakashi Iwai struct snd_pcm_substream *substream) 333138cf6f1aSTakashi Iwai { 333238cf6f1aSTakashi Iwai struct hda_gen_spec *spec = codec->spec; 333338cf6f1aSTakashi Iwai mutex_lock(&spec->pcm_mutex); 333438cf6f1aSTakashi Iwai spec->active_streams &= ~(1 << STREAM_MULTI_OUT); 3335e6b85f3cSTakashi Iwai call_pcm_playback_hook(hinfo, codec, substream, 3336e6b85f3cSTakashi Iwai HDA_GEN_PCM_ACT_CLOSE); 333738cf6f1aSTakashi Iwai mutex_unlock(&spec->pcm_mutex); 333838cf6f1aSTakashi Iwai return 0; 333938cf6f1aSTakashi Iwai } 334038cf6f1aSTakashi Iwai 334138cf6f1aSTakashi Iwai static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, 334238cf6f1aSTakashi Iwai struct hda_codec *codec, 334338cf6f1aSTakashi Iwai struct snd_pcm_substream *substream) 334438cf6f1aSTakashi Iwai { 334538cf6f1aSTakashi Iwai struct hda_gen_spec *spec = codec->spec; 334638cf6f1aSTakashi Iwai int err = 0; 334738cf6f1aSTakashi Iwai 334838cf6f1aSTakashi Iwai mutex_lock(&spec->pcm_mutex); 334938cf6f1aSTakashi Iwai if (!spec->indep_hp_enabled) 335038cf6f1aSTakashi Iwai err = -EBUSY; 335138cf6f1aSTakashi Iwai else 335238cf6f1aSTakashi Iwai spec->active_streams |= 1 << STREAM_INDEP_HP; 3353e6b85f3cSTakashi Iwai call_pcm_playback_hook(hinfo, codec, substream, 3354e6b85f3cSTakashi Iwai HDA_GEN_PCM_ACT_OPEN); 335538cf6f1aSTakashi Iwai mutex_unlock(&spec->pcm_mutex); 335638cf6f1aSTakashi Iwai return err; 335738cf6f1aSTakashi Iwai } 335838cf6f1aSTakashi Iwai 335938cf6f1aSTakashi Iwai static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo, 336038cf6f1aSTakashi Iwai struct hda_codec *codec, 336138cf6f1aSTakashi Iwai struct snd_pcm_substream *substream) 336238cf6f1aSTakashi Iwai { 336338cf6f1aSTakashi Iwai struct hda_gen_spec *spec = codec->spec; 336438cf6f1aSTakashi Iwai mutex_lock(&spec->pcm_mutex); 336538cf6f1aSTakashi Iwai spec->active_streams &= ~(1 << STREAM_INDEP_HP); 3366e6b85f3cSTakashi Iwai call_pcm_playback_hook(hinfo, codec, substream, 3367e6b85f3cSTakashi Iwai HDA_GEN_PCM_ACT_CLOSE); 336838cf6f1aSTakashi Iwai mutex_unlock(&spec->pcm_mutex); 336938cf6f1aSTakashi Iwai return 0; 337038cf6f1aSTakashi Iwai } 337138cf6f1aSTakashi Iwai 3372e6b85f3cSTakashi Iwai static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 3373e6b85f3cSTakashi Iwai struct hda_codec *codec, 3374e6b85f3cSTakashi Iwai unsigned int stream_tag, 3375e6b85f3cSTakashi Iwai unsigned int format, 3376e6b85f3cSTakashi Iwai struct snd_pcm_substream *substream) 3377e6b85f3cSTakashi Iwai { 3378e6b85f3cSTakashi Iwai snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); 3379e6b85f3cSTakashi Iwai call_pcm_playback_hook(hinfo, codec, substream, 3380e6b85f3cSTakashi Iwai HDA_GEN_PCM_ACT_PREPARE); 3381e6b85f3cSTakashi Iwai return 0; 3382e6b85f3cSTakashi Iwai } 3383e6b85f3cSTakashi Iwai 3384e6b85f3cSTakashi Iwai static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 3385e6b85f3cSTakashi Iwai struct hda_codec *codec, 3386e6b85f3cSTakashi Iwai struct snd_pcm_substream *substream) 3387e6b85f3cSTakashi Iwai { 3388e6b85f3cSTakashi Iwai snd_hda_codec_cleanup_stream(codec, hinfo->nid); 3389e6b85f3cSTakashi Iwai call_pcm_playback_hook(hinfo, codec, substream, 3390e6b85f3cSTakashi Iwai HDA_GEN_PCM_ACT_CLEANUP); 3391e6b85f3cSTakashi Iwai return 0; 3392e6b85f3cSTakashi Iwai } 3393e6b85f3cSTakashi Iwai 3394352f7f91STakashi Iwai /* 3395352f7f91STakashi Iwai * Digital out 3396352f7f91STakashi Iwai */ 3397352f7f91STakashi Iwai static int dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 3398352f7f91STakashi Iwai struct hda_codec *codec, 3399352f7f91STakashi Iwai struct snd_pcm_substream *substream) 3400352f7f91STakashi Iwai { 3401352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3402352f7f91STakashi Iwai return snd_hda_multi_out_dig_open(codec, &spec->multiout); 3403352f7f91STakashi Iwai } 3404352f7f91STakashi Iwai 3405352f7f91STakashi Iwai static int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 3406352f7f91STakashi Iwai struct hda_codec *codec, 3407352f7f91STakashi Iwai unsigned int stream_tag, 3408352f7f91STakashi Iwai unsigned int format, 3409352f7f91STakashi Iwai struct snd_pcm_substream *substream) 3410352f7f91STakashi Iwai { 3411352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3412352f7f91STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 3413352f7f91STakashi Iwai stream_tag, format, substream); 3414352f7f91STakashi Iwai } 3415352f7f91STakashi Iwai 3416352f7f91STakashi Iwai static int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 3417352f7f91STakashi Iwai struct hda_codec *codec, 3418352f7f91STakashi Iwai struct snd_pcm_substream *substream) 3419352f7f91STakashi Iwai { 3420352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3421352f7f91STakashi Iwai return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 3422352f7f91STakashi Iwai } 3423352f7f91STakashi Iwai 3424352f7f91STakashi Iwai static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 3425352f7f91STakashi Iwai struct hda_codec *codec, 3426352f7f91STakashi Iwai struct snd_pcm_substream *substream) 3427352f7f91STakashi Iwai { 3428352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3429352f7f91STakashi Iwai return snd_hda_multi_out_dig_close(codec, &spec->multiout); 3430352f7f91STakashi Iwai } 3431352f7f91STakashi Iwai 3432352f7f91STakashi Iwai /* 3433352f7f91STakashi Iwai * Analog capture 3434352f7f91STakashi Iwai */ 3435352f7f91STakashi Iwai static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 3436352f7f91STakashi Iwai struct hda_codec *codec, 3437352f7f91STakashi Iwai unsigned int stream_tag, 3438352f7f91STakashi Iwai unsigned int format, 3439352f7f91STakashi Iwai struct snd_pcm_substream *substream) 3440352f7f91STakashi Iwai { 3441352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3442352f7f91STakashi Iwai 3443352f7f91STakashi Iwai snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], 344497ec558aSTakashi Iwai stream_tag, 0, format); 344597ec558aSTakashi Iwai return 0; 344697ec558aSTakashi Iwai } 344797ec558aSTakashi Iwai 3448352f7f91STakashi Iwai static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 344997ec558aSTakashi Iwai struct hda_codec *codec, 345097ec558aSTakashi Iwai struct snd_pcm_substream *substream) 345197ec558aSTakashi Iwai { 3452352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 345397ec558aSTakashi Iwai 3454352f7f91STakashi Iwai snd_hda_codec_cleanup_stream(codec, 3455352f7f91STakashi Iwai spec->adc_nids[substream->number + 1]); 345697ec558aSTakashi Iwai return 0; 345797ec558aSTakashi Iwai } 345897ec558aSTakashi Iwai 3459352f7f91STakashi Iwai /* 3460352f7f91STakashi Iwai */ 3461352f7f91STakashi Iwai static const struct hda_pcm_stream pcm_analog_playback = { 3462352f7f91STakashi Iwai .substreams = 1, 3463352f7f91STakashi Iwai .channels_min = 2, 3464352f7f91STakashi Iwai .channels_max = 8, 3465352f7f91STakashi Iwai /* NID is set in build_pcms */ 3466352f7f91STakashi Iwai .ops = { 3467352f7f91STakashi Iwai .open = playback_pcm_open, 346838cf6f1aSTakashi Iwai .close = playback_pcm_close, 3469352f7f91STakashi Iwai .prepare = playback_pcm_prepare, 3470352f7f91STakashi Iwai .cleanup = playback_pcm_cleanup 3471352f7f91STakashi Iwai }, 3472352f7f91STakashi Iwai }; 3473352f7f91STakashi Iwai 3474352f7f91STakashi Iwai static const struct hda_pcm_stream pcm_analog_capture = { 3475352f7f91STakashi Iwai .substreams = 1, 3476352f7f91STakashi Iwai .channels_min = 2, 3477352f7f91STakashi Iwai .channels_max = 2, 3478352f7f91STakashi Iwai /* NID is set in build_pcms */ 3479352f7f91STakashi Iwai }; 3480352f7f91STakashi Iwai 3481352f7f91STakashi Iwai static const struct hda_pcm_stream pcm_analog_alt_playback = { 3482352f7f91STakashi Iwai .substreams = 1, 3483352f7f91STakashi Iwai .channels_min = 2, 3484352f7f91STakashi Iwai .channels_max = 2, 3485352f7f91STakashi Iwai /* NID is set in build_pcms */ 348638cf6f1aSTakashi Iwai .ops = { 348738cf6f1aSTakashi Iwai .open = alt_playback_pcm_open, 3488e6b85f3cSTakashi Iwai .close = alt_playback_pcm_close, 3489e6b85f3cSTakashi Iwai .prepare = alt_playback_pcm_prepare, 3490e6b85f3cSTakashi Iwai .cleanup = alt_playback_pcm_cleanup 349138cf6f1aSTakashi Iwai }, 3492352f7f91STakashi Iwai }; 3493352f7f91STakashi Iwai 3494352f7f91STakashi Iwai static const struct hda_pcm_stream pcm_analog_alt_capture = { 3495352f7f91STakashi Iwai .substreams = 2, /* can be overridden */ 3496352f7f91STakashi Iwai .channels_min = 2, 3497352f7f91STakashi Iwai .channels_max = 2, 3498352f7f91STakashi Iwai /* NID is set in build_pcms */ 3499352f7f91STakashi Iwai .ops = { 3500352f7f91STakashi Iwai .prepare = alt_capture_pcm_prepare, 3501352f7f91STakashi Iwai .cleanup = alt_capture_pcm_cleanup 3502352f7f91STakashi Iwai }, 3503352f7f91STakashi Iwai }; 3504352f7f91STakashi Iwai 3505352f7f91STakashi Iwai static const struct hda_pcm_stream pcm_digital_playback = { 3506352f7f91STakashi Iwai .substreams = 1, 3507352f7f91STakashi Iwai .channels_min = 2, 3508352f7f91STakashi Iwai .channels_max = 2, 3509352f7f91STakashi Iwai /* NID is set in build_pcms */ 3510352f7f91STakashi Iwai .ops = { 3511352f7f91STakashi Iwai .open = dig_playback_pcm_open, 3512352f7f91STakashi Iwai .close = dig_playback_pcm_close, 3513352f7f91STakashi Iwai .prepare = dig_playback_pcm_prepare, 3514352f7f91STakashi Iwai .cleanup = dig_playback_pcm_cleanup 3515352f7f91STakashi Iwai }, 3516352f7f91STakashi Iwai }; 3517352f7f91STakashi Iwai 3518352f7f91STakashi Iwai static const struct hda_pcm_stream pcm_digital_capture = { 3519352f7f91STakashi Iwai .substreams = 1, 3520352f7f91STakashi Iwai .channels_min = 2, 3521352f7f91STakashi Iwai .channels_max = 2, 3522352f7f91STakashi Iwai /* NID is set in build_pcms */ 3523352f7f91STakashi Iwai }; 3524352f7f91STakashi Iwai 3525352f7f91STakashi Iwai /* Used by build_pcms to flag that a PCM has no playback stream */ 3526352f7f91STakashi Iwai static const struct hda_pcm_stream pcm_null_stream = { 3527352f7f91STakashi Iwai .substreams = 0, 3528352f7f91STakashi Iwai .channels_min = 0, 3529352f7f91STakashi Iwai .channels_max = 0, 3530352f7f91STakashi Iwai }; 3531352f7f91STakashi Iwai 3532352f7f91STakashi Iwai /* 3533352f7f91STakashi Iwai * dynamic changing ADC PCM streams 3534352f7f91STakashi Iwai */ 3535352f7f91STakashi Iwai static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) 35361da177e4SLinus Torvalds { 3537352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3538352f7f91STakashi Iwai hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; 35391da177e4SLinus Torvalds 3540352f7f91STakashi Iwai if (spec->cur_adc && spec->cur_adc != new_adc) { 3541352f7f91STakashi Iwai /* stream is running, let's swap the current ADC */ 3542352f7f91STakashi Iwai __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); 3543352f7f91STakashi Iwai spec->cur_adc = new_adc; 3544352f7f91STakashi Iwai snd_hda_codec_setup_stream(codec, new_adc, 3545352f7f91STakashi Iwai spec->cur_adc_stream_tag, 0, 3546352f7f91STakashi Iwai spec->cur_adc_format); 3547352f7f91STakashi Iwai return true; 3548352f7f91STakashi Iwai } 3549352f7f91STakashi Iwai return false; 3550352f7f91STakashi Iwai } 3551352f7f91STakashi Iwai 3552352f7f91STakashi Iwai /* analog capture with dynamic dual-adc changes */ 3553352f7f91STakashi Iwai static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 3554352f7f91STakashi Iwai struct hda_codec *codec, 3555352f7f91STakashi Iwai unsigned int stream_tag, 3556352f7f91STakashi Iwai unsigned int format, 3557352f7f91STakashi Iwai struct snd_pcm_substream *substream) 3558352f7f91STakashi Iwai { 3559352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3560352f7f91STakashi Iwai spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; 3561352f7f91STakashi Iwai spec->cur_adc_stream_tag = stream_tag; 3562352f7f91STakashi Iwai spec->cur_adc_format = format; 3563352f7f91STakashi Iwai snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); 35641da177e4SLinus Torvalds return 0; 35651da177e4SLinus Torvalds } 35661da177e4SLinus Torvalds 3567352f7f91STakashi Iwai static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 3568352f7f91STakashi Iwai struct hda_codec *codec, 3569352f7f91STakashi Iwai struct snd_pcm_substream *substream) 3570352f7f91STakashi Iwai { 3571352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3572352f7f91STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->cur_adc); 3573352f7f91STakashi Iwai spec->cur_adc = 0; 3574352f7f91STakashi Iwai return 0; 3575352f7f91STakashi Iwai } 3576352f7f91STakashi Iwai 3577352f7f91STakashi Iwai static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { 3578352f7f91STakashi Iwai .substreams = 1, 3579352f7f91STakashi Iwai .channels_min = 2, 3580352f7f91STakashi Iwai .channels_max = 2, 3581352f7f91STakashi Iwai .nid = 0, /* fill later */ 3582352f7f91STakashi Iwai .ops = { 3583352f7f91STakashi Iwai .prepare = dyn_adc_capture_pcm_prepare, 3584352f7f91STakashi Iwai .cleanup = dyn_adc_capture_pcm_cleanup 3585352f7f91STakashi Iwai }, 3586352f7f91STakashi Iwai }; 3587352f7f91STakashi Iwai 3588f873e536STakashi Iwai static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, 3589f873e536STakashi Iwai const char *chip_name) 3590f873e536STakashi Iwai { 3591f873e536STakashi Iwai char *p; 3592f873e536STakashi Iwai 3593f873e536STakashi Iwai if (*str) 3594f873e536STakashi Iwai return; 3595f873e536STakashi Iwai strlcpy(str, chip_name, len); 3596f873e536STakashi Iwai 3597f873e536STakashi Iwai /* drop non-alnum chars after a space */ 3598f873e536STakashi Iwai for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) { 3599f873e536STakashi Iwai if (!isalnum(p[1])) { 3600f873e536STakashi Iwai *p = 0; 3601f873e536STakashi Iwai break; 3602f873e536STakashi Iwai } 3603f873e536STakashi Iwai } 3604f873e536STakashi Iwai strlcat(str, sfx, len); 3605f873e536STakashi Iwai } 3606f873e536STakashi Iwai 3607352f7f91STakashi Iwai /* build PCM streams based on the parsed results */ 3608352f7f91STakashi Iwai int snd_hda_gen_build_pcms(struct hda_codec *codec) 3609352f7f91STakashi Iwai { 3610352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3611352f7f91STakashi Iwai struct hda_pcm *info = spec->pcm_rec; 3612352f7f91STakashi Iwai const struct hda_pcm_stream *p; 3613352f7f91STakashi Iwai bool have_multi_adcs; 3614352f7f91STakashi Iwai 36151da177e4SLinus Torvalds codec->num_pcms = 1; 36161da177e4SLinus Torvalds codec->pcm_info = info; 36171da177e4SLinus Torvalds 3618352f7f91STakashi Iwai if (spec->no_analog) 3619352f7f91STakashi Iwai goto skip_analog; 3620352f7f91STakashi Iwai 3621f873e536STakashi Iwai fill_pcm_stream_name(spec->stream_name_analog, 3622f873e536STakashi Iwai sizeof(spec->stream_name_analog), 3623f873e536STakashi Iwai " Analog", codec->chip_name); 3624352f7f91STakashi Iwai info->name = spec->stream_name_analog; 3625352f7f91STakashi Iwai 3626352f7f91STakashi Iwai if (spec->multiout.num_dacs > 0) { 3627352f7f91STakashi Iwai p = spec->stream_analog_playback; 3628352f7f91STakashi Iwai if (!p) 3629352f7f91STakashi Iwai p = &pcm_analog_playback; 3630352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; 3631352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; 3632352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 3633352f7f91STakashi Iwai spec->multiout.max_channels; 3634352f7f91STakashi Iwai if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && 3635352f7f91STakashi Iwai spec->autocfg.line_outs == 2) 3636352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = 3637352f7f91STakashi Iwai snd_pcm_2_1_chmaps; 3638352f7f91STakashi Iwai } 3639352f7f91STakashi Iwai if (spec->num_adc_nids) { 3640352f7f91STakashi Iwai p = spec->stream_analog_capture; 3641352f7f91STakashi Iwai if (!p) { 3642352f7f91STakashi Iwai if (spec->dyn_adc_switch) 3643352f7f91STakashi Iwai p = &dyn_adc_pcm_analog_capture; 3644352f7f91STakashi Iwai else 3645352f7f91STakashi Iwai p = &pcm_analog_capture; 3646352f7f91STakashi Iwai } 3647352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; 3648352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 3649352f7f91STakashi Iwai } 3650352f7f91STakashi Iwai 3651352f7f91STakashi Iwai skip_analog: 3652352f7f91STakashi Iwai /* SPDIF for stream index #1 */ 3653352f7f91STakashi Iwai if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 3654f873e536STakashi Iwai fill_pcm_stream_name(spec->stream_name_digital, 3655352f7f91STakashi Iwai sizeof(spec->stream_name_digital), 3656f873e536STakashi Iwai " Digital", codec->chip_name); 3657352f7f91STakashi Iwai codec->num_pcms = 2; 3658352f7f91STakashi Iwai codec->slave_dig_outs = spec->multiout.slave_dig_outs; 3659352f7f91STakashi Iwai info = spec->pcm_rec + 1; 3660352f7f91STakashi Iwai info->name = spec->stream_name_digital; 3661352f7f91STakashi Iwai if (spec->dig_out_type) 3662352f7f91STakashi Iwai info->pcm_type = spec->dig_out_type; 3663352f7f91STakashi Iwai else 3664352f7f91STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 3665352f7f91STakashi Iwai if (spec->multiout.dig_out_nid) { 3666352f7f91STakashi Iwai p = spec->stream_digital_playback; 3667352f7f91STakashi Iwai if (!p) 3668352f7f91STakashi Iwai p = &pcm_digital_playback; 3669352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; 3670352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; 3671352f7f91STakashi Iwai } 3672352f7f91STakashi Iwai if (spec->dig_in_nid) { 3673352f7f91STakashi Iwai p = spec->stream_digital_capture; 3674352f7f91STakashi Iwai if (!p) 3675352f7f91STakashi Iwai p = &pcm_digital_capture; 3676352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; 3677352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; 3678352f7f91STakashi Iwai } 3679352f7f91STakashi Iwai } 3680352f7f91STakashi Iwai 3681352f7f91STakashi Iwai if (spec->no_analog) 3682352f7f91STakashi Iwai return 0; 3683352f7f91STakashi Iwai 3684352f7f91STakashi Iwai /* If the use of more than one ADC is requested for the current 3685352f7f91STakashi Iwai * model, configure a second analog capture-only PCM. 3686352f7f91STakashi Iwai */ 3687352f7f91STakashi Iwai have_multi_adcs = (spec->num_adc_nids > 1) && 3688352f7f91STakashi Iwai !spec->dyn_adc_switch && !spec->auto_mic; 3689352f7f91STakashi Iwai /* Additional Analaog capture for index #2 */ 3690352f7f91STakashi Iwai if (spec->alt_dac_nid || have_multi_adcs) { 3691352f7f91STakashi Iwai codec->num_pcms = 3; 3692352f7f91STakashi Iwai info = spec->pcm_rec + 2; 3693352f7f91STakashi Iwai info->name = spec->stream_name_analog; 3694352f7f91STakashi Iwai if (spec->alt_dac_nid) { 3695352f7f91STakashi Iwai p = spec->stream_analog_alt_playback; 3696352f7f91STakashi Iwai if (!p) 3697352f7f91STakashi Iwai p = &pcm_analog_alt_playback; 3698352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; 3699352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 3700352f7f91STakashi Iwai spec->alt_dac_nid; 3701352f7f91STakashi Iwai } else { 3702352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 3703352f7f91STakashi Iwai pcm_null_stream; 3704352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; 3705352f7f91STakashi Iwai } 3706352f7f91STakashi Iwai if (have_multi_adcs) { 3707352f7f91STakashi Iwai p = spec->stream_analog_alt_capture; 3708352f7f91STakashi Iwai if (!p) 3709352f7f91STakashi Iwai p = &pcm_analog_alt_capture; 3710352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; 3711352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 3712352f7f91STakashi Iwai spec->adc_nids[1]; 3713352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 3714352f7f91STakashi Iwai spec->num_adc_nids - 1; 3715352f7f91STakashi Iwai } else { 3716352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = 3717352f7f91STakashi Iwai pcm_null_stream; 3718352f7f91STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; 3719352f7f91STakashi Iwai } 37201da177e4SLinus Torvalds } 37211da177e4SLinus Torvalds 37221da177e4SLinus Torvalds return 0; 37231da177e4SLinus Torvalds } 3724352f7f91STakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); 3725352f7f91STakashi Iwai 3726352f7f91STakashi Iwai 3727352f7f91STakashi Iwai /* 3728352f7f91STakashi Iwai * Standard auto-parser initializations 3729352f7f91STakashi Iwai */ 3730352f7f91STakashi Iwai 3731d4156930STakashi Iwai /* configure the given path as a proper output */ 3732d4156930STakashi Iwai static void set_output_and_unmute(struct hda_codec *codec, 3733196c1766STakashi Iwai int pin_type, int path_idx) 3734352f7f91STakashi Iwai { 3735352f7f91STakashi Iwai struct nid_path *path; 3736d4156930STakashi Iwai hda_nid_t pin; 3737352f7f91STakashi Iwai 3738196c1766STakashi Iwai path = snd_hda_get_path_from_idx(codec, path_idx); 3739d4156930STakashi Iwai if (!path || !path->depth) 3740352f7f91STakashi Iwai return; 3741d4156930STakashi Iwai pin = path->path[path->depth - 1]; 3742d4156930STakashi Iwai snd_hda_set_pin_ctl_cache(codec, pin, pin_type); 3743e1284af7STakashi Iwai snd_hda_activate_path(codec, path, path->active, true); 3744e1284af7STakashi Iwai set_pin_eapd(codec, pin, path->active); 3745352f7f91STakashi Iwai } 3746352f7f91STakashi Iwai 3747352f7f91STakashi Iwai /* initialize primary output paths */ 3748352f7f91STakashi Iwai static void init_multi_out(struct hda_codec *codec) 3749352f7f91STakashi Iwai { 3750352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3751352f7f91STakashi Iwai int pin_type; 3752352f7f91STakashi Iwai int i; 3753352f7f91STakashi Iwai 3754352f7f91STakashi Iwai if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) 3755352f7f91STakashi Iwai pin_type = PIN_HP; 3756352f7f91STakashi Iwai else 3757352f7f91STakashi Iwai pin_type = PIN_OUT; 3758352f7f91STakashi Iwai 3759d4156930STakashi Iwai for (i = 0; i < spec->autocfg.line_outs; i++) 3760d4156930STakashi Iwai set_output_and_unmute(codec, pin_type, spec->out_paths[i]); 3761352f7f91STakashi Iwai } 3762352f7f91STakashi Iwai 3763db23fd19STakashi Iwai 3764db23fd19STakashi Iwai static void __init_extra_out(struct hda_codec *codec, int num_outs, 3765d4156930STakashi Iwai int *paths, int type) 3766352f7f91STakashi Iwai { 3767352f7f91STakashi Iwai int i; 3768352f7f91STakashi Iwai 3769d4156930STakashi Iwai for (i = 0; i < num_outs; i++) 3770d4156930STakashi Iwai set_output_and_unmute(codec, type, paths[i]); 3771352f7f91STakashi Iwai } 3772db23fd19STakashi Iwai 3773db23fd19STakashi Iwai /* initialize hp and speaker paths */ 3774db23fd19STakashi Iwai static void init_extra_out(struct hda_codec *codec) 3775db23fd19STakashi Iwai { 3776db23fd19STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3777db23fd19STakashi Iwai 3778db23fd19STakashi Iwai if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) 3779db23fd19STakashi Iwai __init_extra_out(codec, spec->autocfg.hp_outs, 3780196c1766STakashi Iwai spec->hp_paths, PIN_HP); 3781db23fd19STakashi Iwai if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) 3782db23fd19STakashi Iwai __init_extra_out(codec, spec->autocfg.speaker_outs, 3783196c1766STakashi Iwai spec->speaker_paths, PIN_OUT); 3784352f7f91STakashi Iwai } 3785352f7f91STakashi Iwai 3786352f7f91STakashi Iwai /* initialize multi-io paths */ 3787352f7f91STakashi Iwai static void init_multi_io(struct hda_codec *codec) 3788352f7f91STakashi Iwai { 3789352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3790352f7f91STakashi Iwai int i; 3791352f7f91STakashi Iwai 3792352f7f91STakashi Iwai for (i = 0; i < spec->multi_ios; i++) { 3793352f7f91STakashi Iwai hda_nid_t pin = spec->multi_io[i].pin; 3794352f7f91STakashi Iwai struct nid_path *path; 3795196c1766STakashi Iwai path = get_multiio_path(codec, i); 3796352f7f91STakashi Iwai if (!path) 3797352f7f91STakashi Iwai continue; 3798352f7f91STakashi Iwai if (!spec->multi_io[i].ctl_in) 3799352f7f91STakashi Iwai spec->multi_io[i].ctl_in = 3800352f7f91STakashi Iwai snd_hda_codec_update_cache(codec, pin, 0, 3801352f7f91STakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 3802352f7f91STakashi Iwai snd_hda_activate_path(codec, path, path->active, true); 3803352f7f91STakashi Iwai } 3804352f7f91STakashi Iwai } 3805352f7f91STakashi Iwai 3806352f7f91STakashi Iwai /* set up the input pin config, depending on the given auto-pin type */ 3807352f7f91STakashi Iwai static void set_input_pin(struct hda_codec *codec, hda_nid_t nid, 3808352f7f91STakashi Iwai int auto_pin_type) 3809352f7f91STakashi Iwai { 3810352f7f91STakashi Iwai unsigned int val = PIN_IN; 3811352f7f91STakashi Iwai if (auto_pin_type == AUTO_PIN_MIC) 3812352f7f91STakashi Iwai val |= snd_hda_get_default_vref(codec, nid); 38137594aa33STakashi Iwai snd_hda_set_pin_ctl_cache(codec, nid, val); 3814352f7f91STakashi Iwai } 3815352f7f91STakashi Iwai 3816352f7f91STakashi Iwai /* set up input pins and loopback paths */ 3817352f7f91STakashi Iwai static void init_analog_input(struct hda_codec *codec) 3818352f7f91STakashi Iwai { 3819352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3820352f7f91STakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 3821352f7f91STakashi Iwai int i; 3822352f7f91STakashi Iwai 3823352f7f91STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 3824352f7f91STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 3825352f7f91STakashi Iwai if (is_input_pin(codec, nid)) 3826352f7f91STakashi Iwai set_input_pin(codec, nid, cfg->inputs[i].type); 3827352f7f91STakashi Iwai 3828352f7f91STakashi Iwai /* init loopback inputs */ 3829352f7f91STakashi Iwai if (spec->mixer_nid) { 3830352f7f91STakashi Iwai struct nid_path *path; 3831196c1766STakashi Iwai path = snd_hda_get_path_from_idx(codec, spec->loopback_paths[i]); 3832352f7f91STakashi Iwai if (path) 3833352f7f91STakashi Iwai snd_hda_activate_path(codec, path, 3834352f7f91STakashi Iwai path->active, false); 3835352f7f91STakashi Iwai } 3836352f7f91STakashi Iwai } 3837352f7f91STakashi Iwai } 3838352f7f91STakashi Iwai 3839352f7f91STakashi Iwai /* initialize ADC paths */ 3840352f7f91STakashi Iwai static void init_input_src(struct hda_codec *codec) 3841352f7f91STakashi Iwai { 3842352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3843352f7f91STakashi Iwai struct hda_input_mux *imux = &spec->input_mux; 3844352f7f91STakashi Iwai struct nid_path *path; 3845352f7f91STakashi Iwai int i, c, nums; 3846352f7f91STakashi Iwai 3847352f7f91STakashi Iwai if (spec->dyn_adc_switch) 3848352f7f91STakashi Iwai nums = 1; 3849352f7f91STakashi Iwai else 3850352f7f91STakashi Iwai nums = spec->num_adc_nids; 3851352f7f91STakashi Iwai 3852352f7f91STakashi Iwai for (c = 0; c < nums; c++) { 3853352f7f91STakashi Iwai for (i = 0; i < imux->num_items; i++) { 3854352f7f91STakashi Iwai path = snd_hda_get_nid_path(codec, spec->imux_pins[i], 3855352f7f91STakashi Iwai get_adc_nid(codec, c, i)); 3856352f7f91STakashi Iwai if (path) { 3857352f7f91STakashi Iwai bool active = path->active; 3858352f7f91STakashi Iwai if (i == spec->cur_mux[c]) 3859352f7f91STakashi Iwai active = true; 3860352f7f91STakashi Iwai snd_hda_activate_path(codec, path, active, false); 3861352f7f91STakashi Iwai } 3862352f7f91STakashi Iwai } 3863352f7f91STakashi Iwai } 3864352f7f91STakashi Iwai 3865352f7f91STakashi Iwai if (spec->shared_mic_hp) 3866352f7f91STakashi Iwai update_shared_mic_hp(codec, spec->cur_mux[0]); 3867352f7f91STakashi Iwai 3868352f7f91STakashi Iwai if (spec->cap_sync_hook) 3869352f7f91STakashi Iwai spec->cap_sync_hook(codec); 3870352f7f91STakashi Iwai } 3871352f7f91STakashi Iwai 3872352f7f91STakashi Iwai /* set right pin controls for digital I/O */ 3873352f7f91STakashi Iwai static void init_digital(struct hda_codec *codec) 3874352f7f91STakashi Iwai { 3875352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3876352f7f91STakashi Iwai int i; 3877352f7f91STakashi Iwai hda_nid_t pin; 3878352f7f91STakashi Iwai 3879d4156930STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) 3880d4156930STakashi Iwai set_output_and_unmute(codec, PIN_OUT, spec->digout_paths[i]); 3881352f7f91STakashi Iwai pin = spec->autocfg.dig_in_pin; 38822430d7b7STakashi Iwai if (pin) { 38832430d7b7STakashi Iwai struct nid_path *path; 38847594aa33STakashi Iwai snd_hda_set_pin_ctl_cache(codec, pin, PIN_IN); 38852430d7b7STakashi Iwai path = snd_hda_get_path_from_idx(codec, spec->digin_path); 38862430d7b7STakashi Iwai if (path) 38872430d7b7STakashi Iwai snd_hda_activate_path(codec, path, path->active, false); 38882430d7b7STakashi Iwai } 3889352f7f91STakashi Iwai } 3890352f7f91STakashi Iwai 3891973e4972STakashi Iwai /* clear unsol-event tags on unused pins; Conexant codecs seem to leave 3892973e4972STakashi Iwai * invalid unsol tags by some reason 3893973e4972STakashi Iwai */ 3894973e4972STakashi Iwai static void clear_unsol_on_unused_pins(struct hda_codec *codec) 3895973e4972STakashi Iwai { 3896973e4972STakashi Iwai int i; 3897973e4972STakashi Iwai 3898973e4972STakashi Iwai for (i = 0; i < codec->init_pins.used; i++) { 3899973e4972STakashi Iwai struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); 3900973e4972STakashi Iwai hda_nid_t nid = pin->nid; 3901973e4972STakashi Iwai if (is_jack_detectable(codec, nid) && 3902973e4972STakashi Iwai !snd_hda_jack_tbl_get(codec, nid)) 3903973e4972STakashi Iwai snd_hda_codec_update_cache(codec, nid, 0, 3904973e4972STakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 0); 3905973e4972STakashi Iwai } 3906973e4972STakashi Iwai } 3907973e4972STakashi Iwai 3908352f7f91STakashi Iwai int snd_hda_gen_init(struct hda_codec *codec) 3909352f7f91STakashi Iwai { 3910352f7f91STakashi Iwai struct hda_gen_spec *spec = codec->spec; 3911352f7f91STakashi Iwai 3912352f7f91STakashi Iwai if (spec->init_hook) 3913352f7f91STakashi Iwai spec->init_hook(codec); 3914352f7f91STakashi Iwai 3915352f7f91STakashi Iwai snd_hda_apply_verbs(codec); 3916352f7f91STakashi Iwai 39173bbcd274STakashi Iwai codec->cached_write = 1; 39183bbcd274STakashi Iwai 3919352f7f91STakashi Iwai init_multi_out(codec); 3920352f7f91STakashi Iwai init_extra_out(codec); 3921352f7f91STakashi Iwai init_multi_io(codec); 3922352f7f91STakashi Iwai init_analog_input(codec); 3923352f7f91STakashi Iwai init_input_src(codec); 3924352f7f91STakashi Iwai init_digital(codec); 3925352f7f91STakashi Iwai 3926973e4972STakashi Iwai clear_unsol_on_unused_pins(codec); 3927973e4972STakashi Iwai 3928352f7f91STakashi Iwai /* call init functions of standard auto-mute helpers */ 39295d550e15STakashi Iwai snd_hda_gen_hp_automute(codec, NULL); 39305d550e15STakashi Iwai snd_hda_gen_line_automute(codec, NULL); 39315d550e15STakashi Iwai snd_hda_gen_mic_autoswitch(codec, NULL); 3932352f7f91STakashi Iwai 39333bbcd274STakashi Iwai snd_hda_codec_flush_amp_cache(codec); 39343bbcd274STakashi Iwai snd_hda_codec_flush_cmd_cache(codec); 39353bbcd274STakashi Iwai 3936352f7f91STakashi Iwai if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) 3937352f7f91STakashi Iwai snd_hda_sync_vmaster_hook(&spec->vmaster_mute); 3938352f7f91STakashi Iwai 3939352f7f91STakashi Iwai hda_call_check_power_status(codec, 0x01); 3940352f7f91STakashi Iwai return 0; 3941352f7f91STakashi Iwai } 3942*fce52a3bSTakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_init); 3943*fce52a3bSTakashi Iwai 3944*fce52a3bSTakashi Iwai 3945*fce52a3bSTakashi Iwai void snd_hda_gen_free(struct hda_codec *codec) 3946*fce52a3bSTakashi Iwai { 3947*fce52a3bSTakashi Iwai snd_hda_gen_spec_free(codec->spec); 3948*fce52a3bSTakashi Iwai kfree(codec->spec); 3949*fce52a3bSTakashi Iwai codec->spec = NULL; 3950*fce52a3bSTakashi Iwai } 3951*fce52a3bSTakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_free); 3952*fce52a3bSTakashi Iwai 3953*fce52a3bSTakashi Iwai #ifdef CONFIG_PM 3954*fce52a3bSTakashi Iwai int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) 3955*fce52a3bSTakashi Iwai { 3956*fce52a3bSTakashi Iwai struct hda_gen_spec *spec = codec->spec; 3957*fce52a3bSTakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 3958*fce52a3bSTakashi Iwai } 3959*fce52a3bSTakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_gen_check_power_status); 3960*fce52a3bSTakashi Iwai #endif 3961352f7f91STakashi Iwai 3962352f7f91STakashi Iwai 3963352f7f91STakashi Iwai /* 3964352f7f91STakashi Iwai * the generic codec support 3965352f7f91STakashi Iwai */ 39661da177e4SLinus Torvalds 3967352f7f91STakashi Iwai static const struct hda_codec_ops generic_patch_ops = { 3968352f7f91STakashi Iwai .build_controls = snd_hda_gen_build_controls, 3969352f7f91STakashi Iwai .build_pcms = snd_hda_gen_build_pcms, 3970352f7f91STakashi Iwai .init = snd_hda_gen_init, 3971*fce52a3bSTakashi Iwai .free = snd_hda_gen_free, 3972352f7f91STakashi Iwai .unsol_event = snd_hda_jack_unsol_event, 397383012a7cSTakashi Iwai #ifdef CONFIG_PM 3974*fce52a3bSTakashi Iwai .check_power_status = snd_hda_gen_check_power_status, 3975cb53c626STakashi Iwai #endif 39761da177e4SLinus Torvalds }; 39771da177e4SLinus Torvalds 39781da177e4SLinus Torvalds int snd_hda_parse_generic_codec(struct hda_codec *codec) 39791da177e4SLinus Torvalds { 3980352f7f91STakashi Iwai struct hda_gen_spec *spec; 39811da177e4SLinus Torvalds int err; 39821da177e4SLinus Torvalds 3983e560d8d8STakashi Iwai spec = kzalloc(sizeof(*spec), GFP_KERNEL); 3984352f7f91STakashi Iwai if (!spec) 39851da177e4SLinus Torvalds return -ENOMEM; 3986352f7f91STakashi Iwai snd_hda_gen_spec_init(spec); 39871da177e4SLinus Torvalds codec->spec = spec; 39881da177e4SLinus Torvalds 39899eb413e5STakashi Iwai err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); 39909eb413e5STakashi Iwai if (err < 0) 39919eb413e5STakashi Iwai return err; 39929eb413e5STakashi Iwai 39939eb413e5STakashi Iwai err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); 3994352f7f91STakashi Iwai if (err < 0) 39951da177e4SLinus Torvalds goto error; 39961da177e4SLinus Torvalds 39971da177e4SLinus Torvalds codec->patch_ops = generic_patch_ops; 39981da177e4SLinus Torvalds return 0; 39991da177e4SLinus Torvalds 40001da177e4SLinus Torvalds error: 4001*fce52a3bSTakashi Iwai snd_hda_gen_free(codec); 40021da177e4SLinus Torvalds return err; 40031da177e4SLinus Torvalds } 4004*fce52a3bSTakashi Iwai EXPORT_SYMBOL_HDA(snd_hda_parse_generic_codec); 4005