18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * BIOS auto-parser helper functions for HD-audio 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci#include <linux/sort.h> 118c2ecf20Sopenharmony_ci#include <sound/core.h> 128c2ecf20Sopenharmony_ci#include <sound/hda_codec.h> 138c2ecf20Sopenharmony_ci#include "hda_local.h" 148c2ecf20Sopenharmony_ci#include "hda_auto_parser.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * Helper for automatic pin configuration 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci for (; *list; list++) 238c2ecf20Sopenharmony_ci if (*list == nid) 248c2ecf20Sopenharmony_ci return 1; 258c2ecf20Sopenharmony_ci return 0; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* a pair of input pin and its sequence */ 298c2ecf20Sopenharmony_cistruct auto_out_pin { 308c2ecf20Sopenharmony_ci hda_nid_t pin; 318c2ecf20Sopenharmony_ci short seq; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int compare_seq(const void *ap, const void *bp) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci const struct auto_out_pin *a = ap; 378c2ecf20Sopenharmony_ci const struct auto_out_pin *b = bp; 388c2ecf20Sopenharmony_ci return (int)(a->seq - b->seq); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * Sort an associated group of pins according to their sequence numbers. 438c2ecf20Sopenharmony_ci * then store it to a pin array. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic void sort_pins_by_sequence(hda_nid_t *pins, struct auto_out_pin *list, 468c2ecf20Sopenharmony_ci int num_pins) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci int i; 498c2ecf20Sopenharmony_ci sort(list, num_pins, sizeof(list[0]), compare_seq, NULL); 508c2ecf20Sopenharmony_ci for (i = 0; i < num_pins; i++) 518c2ecf20Sopenharmony_ci pins[i] = list[i].pin; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* add the found input-pin to the cfg->inputs[] table */ 568c2ecf20Sopenharmony_cistatic void add_auto_cfg_input_pin(struct hda_codec *codec, struct auto_pin_cfg *cfg, 578c2ecf20Sopenharmony_ci hda_nid_t nid, int type) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci if (cfg->num_inputs < AUTO_CFG_MAX_INS) { 608c2ecf20Sopenharmony_ci cfg->inputs[cfg->num_inputs].pin = nid; 618c2ecf20Sopenharmony_ci cfg->inputs[cfg->num_inputs].type = type; 628c2ecf20Sopenharmony_ci cfg->inputs[cfg->num_inputs].has_boost_on_pin = 638c2ecf20Sopenharmony_ci nid_has_volume(codec, nid, HDA_INPUT); 648c2ecf20Sopenharmony_ci cfg->num_inputs++; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int compare_input_type(const void *ap, const void *bp) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci const struct auto_pin_cfg_item *a = ap; 718c2ecf20Sopenharmony_ci const struct auto_pin_cfg_item *b = bp; 728c2ecf20Sopenharmony_ci if (a->type != b->type) 738c2ecf20Sopenharmony_ci return (int)(a->type - b->type); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* If has both hs_mic and hp_mic, pick the hs_mic ahead of hp_mic. */ 768c2ecf20Sopenharmony_ci if (a->is_headset_mic && b->is_headphone_mic) 778c2ecf20Sopenharmony_ci return -1; /* don't swap */ 788c2ecf20Sopenharmony_ci else if (a->is_headphone_mic && b->is_headset_mic) 798c2ecf20Sopenharmony_ci return 1; /* swap */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* In case one has boost and the other one has not, 828c2ecf20Sopenharmony_ci pick the one with boost first. */ 838c2ecf20Sopenharmony_ci return (int)(b->has_boost_on_pin - a->has_boost_on_pin); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* Reorder the surround channels 878c2ecf20Sopenharmony_ci * ALSA sequence is front/surr/clfe/side 888c2ecf20Sopenharmony_ci * HDA sequence is: 898c2ecf20Sopenharmony_ci * 4-ch: front/surr => OK as it is 908c2ecf20Sopenharmony_ci * 6-ch: front/clfe/surr 918c2ecf20Sopenharmony_ci * 8-ch: front/clfe/rear/side|fc 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_cistatic void reorder_outputs(unsigned int nums, hda_nid_t *pins) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci hda_nid_t nid; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci switch (nums) { 988c2ecf20Sopenharmony_ci case 3: 998c2ecf20Sopenharmony_ci case 4: 1008c2ecf20Sopenharmony_ci nid = pins[1]; 1018c2ecf20Sopenharmony_ci pins[1] = pins[2]; 1028c2ecf20Sopenharmony_ci pins[2] = nid; 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* check whether the given pin has a proper pin I/O capability bit */ 1088c2ecf20Sopenharmony_cistatic bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin, 1098c2ecf20Sopenharmony_ci unsigned int dev) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci unsigned int pincap = snd_hda_query_pin_caps(codec, pin); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* some old hardware don't return the proper pincaps */ 1148c2ecf20Sopenharmony_ci if (!pincap) 1158c2ecf20Sopenharmony_ci return true; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci switch (dev) { 1188c2ecf20Sopenharmony_ci case AC_JACK_LINE_OUT: 1198c2ecf20Sopenharmony_ci case AC_JACK_SPEAKER: 1208c2ecf20Sopenharmony_ci case AC_JACK_HP_OUT: 1218c2ecf20Sopenharmony_ci case AC_JACK_SPDIF_OUT: 1228c2ecf20Sopenharmony_ci case AC_JACK_DIG_OTHER_OUT: 1238c2ecf20Sopenharmony_ci return !!(pincap & AC_PINCAP_OUT); 1248c2ecf20Sopenharmony_ci default: 1258c2ecf20Sopenharmony_ci return !!(pincap & AC_PINCAP_IN); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic bool can_be_headset_mic(struct hda_codec *codec, 1308c2ecf20Sopenharmony_ci struct auto_pin_cfg_item *item, 1318c2ecf20Sopenharmony_ci int seq_number) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci int attr; 1348c2ecf20Sopenharmony_ci unsigned int def_conf; 1358c2ecf20Sopenharmony_ci if (item->type != AUTO_PIN_MIC) 1368c2ecf20Sopenharmony_ci return false; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (item->is_headset_mic || item->is_headphone_mic) 1398c2ecf20Sopenharmony_ci return false; /* Already assigned */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci def_conf = snd_hda_codec_get_pincfg(codec, item->pin); 1428c2ecf20Sopenharmony_ci attr = snd_hda_get_input_pin_attr(def_conf); 1438c2ecf20Sopenharmony_ci if (attr <= INPUT_PIN_ATTR_DOCK) 1448c2ecf20Sopenharmony_ci return false; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (seq_number >= 0) { 1478c2ecf20Sopenharmony_ci int seq = get_defcfg_sequence(def_conf); 1488c2ecf20Sopenharmony_ci if (seq != seq_number) 1498c2ecf20Sopenharmony_ci return false; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return true; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* 1568c2ecf20Sopenharmony_ci * Parse all pin widgets and store the useful pin nids to cfg 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * The number of line-outs or any primary output is stored in line_outs, 1598c2ecf20Sopenharmony_ci * and the corresponding output pins are assigned to line_out_pins[], 1608c2ecf20Sopenharmony_ci * in the order of front, rear, CLFE, side, ... 1618c2ecf20Sopenharmony_ci * 1628c2ecf20Sopenharmony_ci * If more extra outputs (speaker and headphone) are found, the pins are 1638c2ecf20Sopenharmony_ci * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack 1648c2ecf20Sopenharmony_ci * is detected, one of speaker of HP pins is assigned as the primary 1658c2ecf20Sopenharmony_ci * output, i.e. to line_out_pins[0]. So, line_outs is always positive 1668c2ecf20Sopenharmony_ci * if any analog output exists. 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * The analog input pins are assigned to inputs array. 1698c2ecf20Sopenharmony_ci * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, 1708c2ecf20Sopenharmony_ci * respectively. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ciint snd_hda_parse_pin_defcfg(struct hda_codec *codec, 1738c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg, 1748c2ecf20Sopenharmony_ci const hda_nid_t *ignore_nids, 1758c2ecf20Sopenharmony_ci unsigned int cond_flags) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci hda_nid_t nid; 1788c2ecf20Sopenharmony_ci short seq, assoc_line_out; 1798c2ecf20Sopenharmony_ci struct auto_out_pin line_out[ARRAY_SIZE(cfg->line_out_pins)]; 1808c2ecf20Sopenharmony_ci struct auto_out_pin speaker_out[ARRAY_SIZE(cfg->speaker_pins)]; 1818c2ecf20Sopenharmony_ci struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)]; 1828c2ecf20Sopenharmony_ci int i; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (!snd_hda_get_int_hint(codec, "parser_flags", &i)) 1858c2ecf20Sopenharmony_ci cond_flags = i; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci memset(cfg, 0, sizeof(*cfg)); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci memset(line_out, 0, sizeof(line_out)); 1908c2ecf20Sopenharmony_ci memset(speaker_out, 0, sizeof(speaker_out)); 1918c2ecf20Sopenharmony_ci memset(hp_out, 0, sizeof(hp_out)); 1928c2ecf20Sopenharmony_ci assoc_line_out = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci for_each_hda_codec_node(nid, codec) { 1958c2ecf20Sopenharmony_ci unsigned int wid_caps = get_wcaps(codec, nid); 1968c2ecf20Sopenharmony_ci unsigned int wid_type = get_wcaps_type(wid_caps); 1978c2ecf20Sopenharmony_ci unsigned int def_conf; 1988c2ecf20Sopenharmony_ci short assoc, loc, conn, dev; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* read all default configuration for pin complex */ 2018c2ecf20Sopenharmony_ci if (wid_type != AC_WID_PIN) 2028c2ecf20Sopenharmony_ci continue; 2038c2ecf20Sopenharmony_ci /* ignore the given nids (e.g. pc-beep returns error) */ 2048c2ecf20Sopenharmony_ci if (ignore_nids && is_in_nid_list(nid, ignore_nids)) 2058c2ecf20Sopenharmony_ci continue; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci def_conf = snd_hda_codec_get_pincfg(codec, nid); 2088c2ecf20Sopenharmony_ci conn = get_defcfg_connect(def_conf); 2098c2ecf20Sopenharmony_ci if (conn == AC_JACK_PORT_NONE) 2108c2ecf20Sopenharmony_ci continue; 2118c2ecf20Sopenharmony_ci loc = get_defcfg_location(def_conf); 2128c2ecf20Sopenharmony_ci dev = get_defcfg_device(def_conf); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* workaround for buggy BIOS setups */ 2158c2ecf20Sopenharmony_ci if (dev == AC_JACK_LINE_OUT) { 2168c2ecf20Sopenharmony_ci if (conn == AC_JACK_PORT_FIXED || 2178c2ecf20Sopenharmony_ci conn == AC_JACK_PORT_BOTH) 2188c2ecf20Sopenharmony_ci dev = AC_JACK_SPEAKER; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!check_pincap_validity(codec, nid, dev)) 2228c2ecf20Sopenharmony_ci continue; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci switch (dev) { 2258c2ecf20Sopenharmony_ci case AC_JACK_LINE_OUT: 2268c2ecf20Sopenharmony_ci seq = get_defcfg_sequence(def_conf); 2278c2ecf20Sopenharmony_ci assoc = get_defcfg_association(def_conf); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (!(wid_caps & AC_WCAP_STEREO)) 2308c2ecf20Sopenharmony_ci if (!cfg->mono_out_pin) 2318c2ecf20Sopenharmony_ci cfg->mono_out_pin = nid; 2328c2ecf20Sopenharmony_ci if (!assoc) 2338c2ecf20Sopenharmony_ci continue; 2348c2ecf20Sopenharmony_ci if (!assoc_line_out) 2358c2ecf20Sopenharmony_ci assoc_line_out = assoc; 2368c2ecf20Sopenharmony_ci else if (assoc_line_out != assoc) { 2378c2ecf20Sopenharmony_ci codec_info(codec, 2388c2ecf20Sopenharmony_ci "ignore pin 0x%x with mismatching assoc# 0x%x vs 0x%x\n", 2398c2ecf20Sopenharmony_ci nid, assoc, assoc_line_out); 2408c2ecf20Sopenharmony_ci continue; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) { 2438c2ecf20Sopenharmony_ci codec_info(codec, 2448c2ecf20Sopenharmony_ci "ignore pin 0x%x, too many assigned pins\n", 2458c2ecf20Sopenharmony_ci nid); 2468c2ecf20Sopenharmony_ci continue; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci line_out[cfg->line_outs].pin = nid; 2498c2ecf20Sopenharmony_ci line_out[cfg->line_outs].seq = seq; 2508c2ecf20Sopenharmony_ci cfg->line_outs++; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case AC_JACK_SPEAKER: 2538c2ecf20Sopenharmony_ci seq = get_defcfg_sequence(def_conf); 2548c2ecf20Sopenharmony_ci assoc = get_defcfg_association(def_conf); 2558c2ecf20Sopenharmony_ci if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) { 2568c2ecf20Sopenharmony_ci codec_info(codec, 2578c2ecf20Sopenharmony_ci "ignore pin 0x%x, too many assigned pins\n", 2588c2ecf20Sopenharmony_ci nid); 2598c2ecf20Sopenharmony_ci continue; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci speaker_out[cfg->speaker_outs].pin = nid; 2628c2ecf20Sopenharmony_ci speaker_out[cfg->speaker_outs].seq = (assoc << 4) | seq; 2638c2ecf20Sopenharmony_ci cfg->speaker_outs++; 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci case AC_JACK_HP_OUT: 2668c2ecf20Sopenharmony_ci seq = get_defcfg_sequence(def_conf); 2678c2ecf20Sopenharmony_ci assoc = get_defcfg_association(def_conf); 2688c2ecf20Sopenharmony_ci if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) { 2698c2ecf20Sopenharmony_ci codec_info(codec, 2708c2ecf20Sopenharmony_ci "ignore pin 0x%x, too many assigned pins\n", 2718c2ecf20Sopenharmony_ci nid); 2728c2ecf20Sopenharmony_ci continue; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci hp_out[cfg->hp_outs].pin = nid; 2758c2ecf20Sopenharmony_ci hp_out[cfg->hp_outs].seq = (assoc << 4) | seq; 2768c2ecf20Sopenharmony_ci cfg->hp_outs++; 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci case AC_JACK_MIC_IN: 2798c2ecf20Sopenharmony_ci add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_MIC); 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci case AC_JACK_LINE_IN: 2828c2ecf20Sopenharmony_ci add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_LINE_IN); 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case AC_JACK_CD: 2858c2ecf20Sopenharmony_ci add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_CD); 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci case AC_JACK_AUX: 2888c2ecf20Sopenharmony_ci add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_AUX); 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case AC_JACK_SPDIF_OUT: 2918c2ecf20Sopenharmony_ci case AC_JACK_DIG_OTHER_OUT: 2928c2ecf20Sopenharmony_ci if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) { 2938c2ecf20Sopenharmony_ci codec_info(codec, 2948c2ecf20Sopenharmony_ci "ignore pin 0x%x, too many assigned pins\n", 2958c2ecf20Sopenharmony_ci nid); 2968c2ecf20Sopenharmony_ci continue; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci cfg->dig_out_pins[cfg->dig_outs] = nid; 2998c2ecf20Sopenharmony_ci cfg->dig_out_type[cfg->dig_outs] = 3008c2ecf20Sopenharmony_ci (loc == AC_JACK_LOC_HDMI) ? 3018c2ecf20Sopenharmony_ci HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF; 3028c2ecf20Sopenharmony_ci cfg->dig_outs++; 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci case AC_JACK_SPDIF_IN: 3058c2ecf20Sopenharmony_ci case AC_JACK_DIG_OTHER_IN: 3068c2ecf20Sopenharmony_ci cfg->dig_in_pin = nid; 3078c2ecf20Sopenharmony_ci if (loc == AC_JACK_LOC_HDMI) 3088c2ecf20Sopenharmony_ci cfg->dig_in_type = HDA_PCM_TYPE_HDMI; 3098c2ecf20Sopenharmony_ci else 3108c2ecf20Sopenharmony_ci cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* Find a pin that could be a headset or headphone mic */ 3168c2ecf20Sopenharmony_ci if (cond_flags & HDA_PINCFG_HEADSET_MIC || cond_flags & HDA_PINCFG_HEADPHONE_MIC) { 3178c2ecf20Sopenharmony_ci bool hsmic = !!(cond_flags & HDA_PINCFG_HEADSET_MIC); 3188c2ecf20Sopenharmony_ci bool hpmic = !!(cond_flags & HDA_PINCFG_HEADPHONE_MIC); 3198c2ecf20Sopenharmony_ci for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) 3208c2ecf20Sopenharmony_ci if (hsmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xc)) { 3218c2ecf20Sopenharmony_ci cfg->inputs[i].is_headset_mic = 1; 3228c2ecf20Sopenharmony_ci hsmic = false; 3238c2ecf20Sopenharmony_ci } else if (hpmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xd)) { 3248c2ecf20Sopenharmony_ci cfg->inputs[i].is_headphone_mic = 1; 3258c2ecf20Sopenharmony_ci hpmic = false; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* If we didn't find our sequence number mark, fall back to any sequence number */ 3298c2ecf20Sopenharmony_ci for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) { 3308c2ecf20Sopenharmony_ci if (!can_be_headset_mic(codec, &cfg->inputs[i], -1)) 3318c2ecf20Sopenharmony_ci continue; 3328c2ecf20Sopenharmony_ci if (hsmic) { 3338c2ecf20Sopenharmony_ci cfg->inputs[i].is_headset_mic = 1; 3348c2ecf20Sopenharmony_ci hsmic = false; 3358c2ecf20Sopenharmony_ci } else if (hpmic) { 3368c2ecf20Sopenharmony_ci cfg->inputs[i].is_headphone_mic = 1; 3378c2ecf20Sopenharmony_ci hpmic = false; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (hsmic) 3428c2ecf20Sopenharmony_ci codec_dbg(codec, "Told to look for a headset mic, but didn't find any.\n"); 3438c2ecf20Sopenharmony_ci if (hpmic) 3448c2ecf20Sopenharmony_ci codec_dbg(codec, "Told to look for a headphone mic, but didn't find any.\n"); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* FIX-UP: 3488c2ecf20Sopenharmony_ci * If no line-out is defined but multiple HPs are found, 3498c2ecf20Sopenharmony_ci * some of them might be the real line-outs. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_ci if (!cfg->line_outs && cfg->hp_outs > 1 && 3528c2ecf20Sopenharmony_ci !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) { 3538c2ecf20Sopenharmony_ci i = 0; 3548c2ecf20Sopenharmony_ci while (i < cfg->hp_outs) { 3558c2ecf20Sopenharmony_ci /* The real HPs should have the sequence 0x0f */ 3568c2ecf20Sopenharmony_ci if ((hp_out[i].seq & 0x0f) == 0x0f) { 3578c2ecf20Sopenharmony_ci i++; 3588c2ecf20Sopenharmony_ci continue; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci /* Move it to the line-out table */ 3618c2ecf20Sopenharmony_ci line_out[cfg->line_outs++] = hp_out[i]; 3628c2ecf20Sopenharmony_ci cfg->hp_outs--; 3638c2ecf20Sopenharmony_ci memmove(hp_out + i, hp_out + i + 1, 3648c2ecf20Sopenharmony_ci sizeof(hp_out[0]) * (cfg->hp_outs - i)); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci memset(hp_out + cfg->hp_outs, 0, 3678c2ecf20Sopenharmony_ci sizeof(hp_out[0]) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs)); 3688c2ecf20Sopenharmony_ci if (!cfg->hp_outs) 3698c2ecf20Sopenharmony_ci cfg->line_out_type = AUTO_PIN_HP_OUT; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* sort by sequence */ 3748c2ecf20Sopenharmony_ci sort_pins_by_sequence(cfg->line_out_pins, line_out, cfg->line_outs); 3758c2ecf20Sopenharmony_ci sort_pins_by_sequence(cfg->speaker_pins, speaker_out, 3768c2ecf20Sopenharmony_ci cfg->speaker_outs); 3778c2ecf20Sopenharmony_ci sort_pins_by_sequence(cfg->hp_pins, hp_out, cfg->hp_outs); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * FIX-UP: if no line-outs are detected, try to use speaker or HP pin 3818c2ecf20Sopenharmony_ci * as a primary output 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci if (!cfg->line_outs && 3848c2ecf20Sopenharmony_ci !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) { 3858c2ecf20Sopenharmony_ci if (cfg->speaker_outs) { 3868c2ecf20Sopenharmony_ci cfg->line_outs = cfg->speaker_outs; 3878c2ecf20Sopenharmony_ci memcpy(cfg->line_out_pins, cfg->speaker_pins, 3888c2ecf20Sopenharmony_ci sizeof(cfg->speaker_pins)); 3898c2ecf20Sopenharmony_ci cfg->speaker_outs = 0; 3908c2ecf20Sopenharmony_ci memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); 3918c2ecf20Sopenharmony_ci cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; 3928c2ecf20Sopenharmony_ci } else if (cfg->hp_outs) { 3938c2ecf20Sopenharmony_ci cfg->line_outs = cfg->hp_outs; 3948c2ecf20Sopenharmony_ci memcpy(cfg->line_out_pins, cfg->hp_pins, 3958c2ecf20Sopenharmony_ci sizeof(cfg->hp_pins)); 3968c2ecf20Sopenharmony_ci cfg->hp_outs = 0; 3978c2ecf20Sopenharmony_ci memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); 3988c2ecf20Sopenharmony_ci cfg->line_out_type = AUTO_PIN_HP_OUT; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci reorder_outputs(cfg->line_outs, cfg->line_out_pins); 4038c2ecf20Sopenharmony_ci reorder_outputs(cfg->hp_outs, cfg->hp_pins); 4048c2ecf20Sopenharmony_ci reorder_outputs(cfg->speaker_outs, cfg->speaker_pins); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* sort inputs in the order of AUTO_PIN_* type */ 4078c2ecf20Sopenharmony_ci sort(cfg->inputs, cfg->num_inputs, sizeof(cfg->inputs[0]), 4088c2ecf20Sopenharmony_ci compare_input_type, NULL); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* 4118c2ecf20Sopenharmony_ci * debug prints of the parsed results 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ci codec_info(codec, "autoconfig for %s: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", 4148c2ecf20Sopenharmony_ci codec->core.chip_name, cfg->line_outs, cfg->line_out_pins[0], 4158c2ecf20Sopenharmony_ci cfg->line_out_pins[1], cfg->line_out_pins[2], 4168c2ecf20Sopenharmony_ci cfg->line_out_pins[3], cfg->line_out_pins[4], 4178c2ecf20Sopenharmony_ci cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" : 4188c2ecf20Sopenharmony_ci (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ? 4198c2ecf20Sopenharmony_ci "speaker" : "line")); 4208c2ecf20Sopenharmony_ci codec_info(codec, " speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", 4218c2ecf20Sopenharmony_ci cfg->speaker_outs, cfg->speaker_pins[0], 4228c2ecf20Sopenharmony_ci cfg->speaker_pins[1], cfg->speaker_pins[2], 4238c2ecf20Sopenharmony_ci cfg->speaker_pins[3], cfg->speaker_pins[4]); 4248c2ecf20Sopenharmony_ci codec_info(codec, " hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", 4258c2ecf20Sopenharmony_ci cfg->hp_outs, cfg->hp_pins[0], 4268c2ecf20Sopenharmony_ci cfg->hp_pins[1], cfg->hp_pins[2], 4278c2ecf20Sopenharmony_ci cfg->hp_pins[3], cfg->hp_pins[4]); 4288c2ecf20Sopenharmony_ci codec_info(codec, " mono: mono_out=0x%x\n", cfg->mono_out_pin); 4298c2ecf20Sopenharmony_ci if (cfg->dig_outs) 4308c2ecf20Sopenharmony_ci codec_info(codec, " dig-out=0x%x/0x%x\n", 4318c2ecf20Sopenharmony_ci cfg->dig_out_pins[0], cfg->dig_out_pins[1]); 4328c2ecf20Sopenharmony_ci codec_info(codec, " inputs:\n"); 4338c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 4348c2ecf20Sopenharmony_ci codec_info(codec, " %s=0x%x\n", 4358c2ecf20Sopenharmony_ci hda_get_autocfg_input_label(codec, cfg, i), 4368c2ecf20Sopenharmony_ci cfg->inputs[i].pin); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci if (cfg->dig_in_pin) 4398c2ecf20Sopenharmony_ci codec_info(codec, " dig-in=0x%x\n", cfg->dig_in_pin); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_parse_pin_defcfg); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci/** 4468c2ecf20Sopenharmony_ci * snd_hda_get_input_pin_attr - Get the input pin attribute from pin config 4478c2ecf20Sopenharmony_ci * @def_conf: pin configuration value 4488c2ecf20Sopenharmony_ci * 4498c2ecf20Sopenharmony_ci * Guess the input pin attribute (INPUT_PIN_ATTR_XXX) from the given 4508c2ecf20Sopenharmony_ci * default pin configuration value. 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ciint snd_hda_get_input_pin_attr(unsigned int def_conf) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci unsigned int loc = get_defcfg_location(def_conf); 4558c2ecf20Sopenharmony_ci unsigned int conn = get_defcfg_connect(def_conf); 4568c2ecf20Sopenharmony_ci if (conn == AC_JACK_PORT_NONE) 4578c2ecf20Sopenharmony_ci return INPUT_PIN_ATTR_UNUSED; 4588c2ecf20Sopenharmony_ci /* Windows may claim the internal mic to be BOTH, too */ 4598c2ecf20Sopenharmony_ci if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH) 4608c2ecf20Sopenharmony_ci return INPUT_PIN_ATTR_INT; 4618c2ecf20Sopenharmony_ci if ((loc & 0x30) == AC_JACK_LOC_INTERNAL) 4628c2ecf20Sopenharmony_ci return INPUT_PIN_ATTR_INT; 4638c2ecf20Sopenharmony_ci if ((loc & 0x30) == AC_JACK_LOC_SEPARATE) 4648c2ecf20Sopenharmony_ci return INPUT_PIN_ATTR_DOCK; 4658c2ecf20Sopenharmony_ci if (loc == AC_JACK_LOC_REAR) 4668c2ecf20Sopenharmony_ci return INPUT_PIN_ATTR_REAR; 4678c2ecf20Sopenharmony_ci if (loc == AC_JACK_LOC_FRONT) 4688c2ecf20Sopenharmony_ci return INPUT_PIN_ATTR_FRONT; 4698c2ecf20Sopenharmony_ci return INPUT_PIN_ATTR_NORMAL; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_input_pin_attr); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/** 4748c2ecf20Sopenharmony_ci * hda_get_input_pin_label - Give a label for the given input pin 4758c2ecf20Sopenharmony_ci * @codec: the HDA codec 4768c2ecf20Sopenharmony_ci * @item: ping config item to refer 4778c2ecf20Sopenharmony_ci * @pin: the pin NID 4788c2ecf20Sopenharmony_ci * @check_location: flag to add the jack location prefix 4798c2ecf20Sopenharmony_ci * 4808c2ecf20Sopenharmony_ci * When @check_location is true, the function checks the pin location 4818c2ecf20Sopenharmony_ci * for mic and line-in pins, and set an appropriate prefix like "Front", 4828c2ecf20Sopenharmony_ci * "Rear", "Internal". 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_cistatic const char *hda_get_input_pin_label(struct hda_codec *codec, 4858c2ecf20Sopenharmony_ci const struct auto_pin_cfg_item *item, 4868c2ecf20Sopenharmony_ci hda_nid_t pin, bool check_location) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci unsigned int def_conf; 4898c2ecf20Sopenharmony_ci static const char * const mic_names[] = { 4908c2ecf20Sopenharmony_ci "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic" 4918c2ecf20Sopenharmony_ci }; 4928c2ecf20Sopenharmony_ci int attr; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci def_conf = snd_hda_codec_get_pincfg(codec, pin); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci switch (get_defcfg_device(def_conf)) { 4978c2ecf20Sopenharmony_ci case AC_JACK_MIC_IN: 4988c2ecf20Sopenharmony_ci if (item && item->is_headset_mic) 4998c2ecf20Sopenharmony_ci return "Headset Mic"; 5008c2ecf20Sopenharmony_ci if (item && item->is_headphone_mic) 5018c2ecf20Sopenharmony_ci return "Headphone Mic"; 5028c2ecf20Sopenharmony_ci if (!check_location) 5038c2ecf20Sopenharmony_ci return "Mic"; 5048c2ecf20Sopenharmony_ci attr = snd_hda_get_input_pin_attr(def_conf); 5058c2ecf20Sopenharmony_ci if (!attr) 5068c2ecf20Sopenharmony_ci return "None"; 5078c2ecf20Sopenharmony_ci return mic_names[attr - 1]; 5088c2ecf20Sopenharmony_ci case AC_JACK_LINE_IN: 5098c2ecf20Sopenharmony_ci if (!check_location) 5108c2ecf20Sopenharmony_ci return "Line"; 5118c2ecf20Sopenharmony_ci attr = snd_hda_get_input_pin_attr(def_conf); 5128c2ecf20Sopenharmony_ci if (!attr) 5138c2ecf20Sopenharmony_ci return "None"; 5148c2ecf20Sopenharmony_ci if (attr == INPUT_PIN_ATTR_DOCK) 5158c2ecf20Sopenharmony_ci return "Dock Line"; 5168c2ecf20Sopenharmony_ci return "Line"; 5178c2ecf20Sopenharmony_ci case AC_JACK_AUX: 5188c2ecf20Sopenharmony_ci return "Aux"; 5198c2ecf20Sopenharmony_ci case AC_JACK_CD: 5208c2ecf20Sopenharmony_ci return "CD"; 5218c2ecf20Sopenharmony_ci case AC_JACK_SPDIF_IN: 5228c2ecf20Sopenharmony_ci return "SPDIF In"; 5238c2ecf20Sopenharmony_ci case AC_JACK_DIG_OTHER_IN: 5248c2ecf20Sopenharmony_ci return "Digital In"; 5258c2ecf20Sopenharmony_ci case AC_JACK_HP_OUT: 5268c2ecf20Sopenharmony_ci return "Headphone Mic"; 5278c2ecf20Sopenharmony_ci default: 5288c2ecf20Sopenharmony_ci return "Misc"; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci/* Check whether the location prefix needs to be added to the label. 5338c2ecf20Sopenharmony_ci * If all mic-jacks are in the same location (e.g. rear panel), we don't 5348c2ecf20Sopenharmony_ci * have to put "Front" prefix to each label. In such a case, returns false. 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_cistatic int check_mic_location_need(struct hda_codec *codec, 5378c2ecf20Sopenharmony_ci const struct auto_pin_cfg *cfg, 5388c2ecf20Sopenharmony_ci int input) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci unsigned int defc; 5418c2ecf20Sopenharmony_ci int i, attr, attr2; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin); 5448c2ecf20Sopenharmony_ci attr = snd_hda_get_input_pin_attr(defc); 5458c2ecf20Sopenharmony_ci /* for internal or docking mics, we need locations */ 5468c2ecf20Sopenharmony_ci if (attr <= INPUT_PIN_ATTR_NORMAL) 5478c2ecf20Sopenharmony_ci return 1; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci attr = 0; 5508c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 5518c2ecf20Sopenharmony_ci defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin); 5528c2ecf20Sopenharmony_ci attr2 = snd_hda_get_input_pin_attr(defc); 5538c2ecf20Sopenharmony_ci if (attr2 >= INPUT_PIN_ATTR_NORMAL) { 5548c2ecf20Sopenharmony_ci if (attr && attr != attr2) 5558c2ecf20Sopenharmony_ci return 1; /* different locations found */ 5568c2ecf20Sopenharmony_ci attr = attr2; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci return 0; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/** 5638c2ecf20Sopenharmony_ci * hda_get_autocfg_input_label - Get a label for the given input 5648c2ecf20Sopenharmony_ci * @codec: the HDA codec 5658c2ecf20Sopenharmony_ci * @cfg: the parsed pin configuration 5668c2ecf20Sopenharmony_ci * @input: the input index number 5678c2ecf20Sopenharmony_ci * 5688c2ecf20Sopenharmony_ci * Get a label for the given input pin defined by the autocfg item. 5698c2ecf20Sopenharmony_ci * Unlike hda_get_input_pin_label(), this function checks all inputs 5708c2ecf20Sopenharmony_ci * defined in autocfg and avoids the redundant mic/line prefix as much as 5718c2ecf20Sopenharmony_ci * possible. 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_ciconst char *hda_get_autocfg_input_label(struct hda_codec *codec, 5748c2ecf20Sopenharmony_ci const struct auto_pin_cfg *cfg, 5758c2ecf20Sopenharmony_ci int input) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci int type = cfg->inputs[input].type; 5788c2ecf20Sopenharmony_ci int has_multiple_pins = 0; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if ((input > 0 && cfg->inputs[input - 1].type == type) || 5818c2ecf20Sopenharmony_ci (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type)) 5828c2ecf20Sopenharmony_ci has_multiple_pins = 1; 5838c2ecf20Sopenharmony_ci if (has_multiple_pins && type == AUTO_PIN_MIC) 5848c2ecf20Sopenharmony_ci has_multiple_pins &= check_mic_location_need(codec, cfg, input); 5858c2ecf20Sopenharmony_ci has_multiple_pins |= codec->force_pin_prefix; 5868c2ecf20Sopenharmony_ci return hda_get_input_pin_label(codec, &cfg->inputs[input], 5878c2ecf20Sopenharmony_ci cfg->inputs[input].pin, 5888c2ecf20Sopenharmony_ci has_multiple_pins); 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hda_get_autocfg_input_label); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci/* return the position of NID in the list, or -1 if not found */ 5938c2ecf20Sopenharmony_cistatic int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci int i; 5968c2ecf20Sopenharmony_ci for (i = 0; i < nums; i++) 5978c2ecf20Sopenharmony_ci if (list[i] == nid) 5988c2ecf20Sopenharmony_ci return i; 5998c2ecf20Sopenharmony_ci return -1; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci/* get a unique suffix or an index number */ 6038c2ecf20Sopenharmony_cistatic const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, 6048c2ecf20Sopenharmony_ci int num_pins, int *indexp) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci static const char * const channel_sfx[] = { 6078c2ecf20Sopenharmony_ci " Front", " Surround", " CLFE", " Side" 6088c2ecf20Sopenharmony_ci }; 6098c2ecf20Sopenharmony_ci int i; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci i = find_idx_in_nid_list(nid, pins, num_pins); 6128c2ecf20Sopenharmony_ci if (i < 0) 6138c2ecf20Sopenharmony_ci return NULL; 6148c2ecf20Sopenharmony_ci if (num_pins == 1) 6158c2ecf20Sopenharmony_ci return ""; 6168c2ecf20Sopenharmony_ci if (num_pins > ARRAY_SIZE(channel_sfx)) { 6178c2ecf20Sopenharmony_ci if (indexp) 6188c2ecf20Sopenharmony_ci *indexp = i; 6198c2ecf20Sopenharmony_ci return ""; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci return channel_sfx[i]; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); 6278c2ecf20Sopenharmony_ci int attr = snd_hda_get_input_pin_attr(def_conf); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* check the location */ 6308c2ecf20Sopenharmony_ci switch (attr) { 6318c2ecf20Sopenharmony_ci case INPUT_PIN_ATTR_DOCK: 6328c2ecf20Sopenharmony_ci return "Dock "; 6338c2ecf20Sopenharmony_ci case INPUT_PIN_ATTR_FRONT: 6348c2ecf20Sopenharmony_ci return "Front "; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci return ""; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid, 6408c2ecf20Sopenharmony_ci const hda_nid_t *pins, int num_pins) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci int i, j, idx = 0; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci const char *pfx = check_output_pfx(codec, nid); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci i = find_idx_in_nid_list(nid, pins, num_pins); 6478c2ecf20Sopenharmony_ci if (i < 0) 6488c2ecf20Sopenharmony_ci return -1; 6498c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) 6508c2ecf20Sopenharmony_ci if (pfx == check_output_pfx(codec, pins[j])) 6518c2ecf20Sopenharmony_ci idx++; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci return idx; 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, 6578c2ecf20Sopenharmony_ci const struct auto_pin_cfg *cfg, 6588c2ecf20Sopenharmony_ci const char *name, char *label, int maxlen, 6598c2ecf20Sopenharmony_ci int *indexp) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); 6628c2ecf20Sopenharmony_ci int attr = snd_hda_get_input_pin_attr(def_conf); 6638c2ecf20Sopenharmony_ci const char *pfx, *sfx = ""; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* handle as a speaker if it's a fixed line-out */ 6668c2ecf20Sopenharmony_ci if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT) 6678c2ecf20Sopenharmony_ci name = "Speaker"; 6688c2ecf20Sopenharmony_ci pfx = check_output_pfx(codec, nid); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (cfg) { 6718c2ecf20Sopenharmony_ci /* try to give a unique suffix if needed */ 6728c2ecf20Sopenharmony_ci sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, 6738c2ecf20Sopenharmony_ci indexp); 6748c2ecf20Sopenharmony_ci if (!sfx) 6758c2ecf20Sopenharmony_ci sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs, 6768c2ecf20Sopenharmony_ci indexp); 6778c2ecf20Sopenharmony_ci if (!sfx) { 6788c2ecf20Sopenharmony_ci /* don't add channel suffix for Headphone controls */ 6798c2ecf20Sopenharmony_ci int idx = get_hp_label_index(codec, nid, cfg->hp_pins, 6808c2ecf20Sopenharmony_ci cfg->hp_outs); 6818c2ecf20Sopenharmony_ci if (idx >= 0 && indexp) 6828c2ecf20Sopenharmony_ci *indexp = idx; 6838c2ecf20Sopenharmony_ci sfx = ""; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci snprintf(label, maxlen, "%s%s%s", pfx, name, sfx); 6878c2ecf20Sopenharmony_ci return 1; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci#define is_hdmi_cfg(conf) \ 6918c2ecf20Sopenharmony_ci (get_defcfg_location(conf) == AC_JACK_LOC_HDMI) 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci/** 6948c2ecf20Sopenharmony_ci * snd_hda_get_pin_label - Get a label for the given I/O pin 6958c2ecf20Sopenharmony_ci * @codec: the HDA codec 6968c2ecf20Sopenharmony_ci * @nid: pin NID 6978c2ecf20Sopenharmony_ci * @cfg: the parsed pin configuration 6988c2ecf20Sopenharmony_ci * @label: the string buffer to store 6998c2ecf20Sopenharmony_ci * @maxlen: the max length of string buffer (including termination) 7008c2ecf20Sopenharmony_ci * @indexp: the pointer to return the index number (for multiple ctls) 7018c2ecf20Sopenharmony_ci * 7028c2ecf20Sopenharmony_ci * Get a label for the given pin. This function works for both input and 7038c2ecf20Sopenharmony_ci * output pins. When @cfg is given as non-NULL, the function tries to get 7048c2ecf20Sopenharmony_ci * an optimized label using hda_get_autocfg_input_label(). 7058c2ecf20Sopenharmony_ci * 7068c2ecf20Sopenharmony_ci * This function tries to give a unique label string for the pin as much as 7078c2ecf20Sopenharmony_ci * possible. For example, when the multiple line-outs are present, it adds 7088c2ecf20Sopenharmony_ci * the channel suffix like "Front", "Surround", etc (only when @cfg is given). 7098c2ecf20Sopenharmony_ci * If no unique name with a suffix is available and @indexp is non-NULL, the 7108c2ecf20Sopenharmony_ci * index number is stored in the pointer. 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_ciint snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, 7138c2ecf20Sopenharmony_ci const struct auto_pin_cfg *cfg, 7148c2ecf20Sopenharmony_ci char *label, int maxlen, int *indexp) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); 7178c2ecf20Sopenharmony_ci const char *name = NULL; 7188c2ecf20Sopenharmony_ci int i; 7198c2ecf20Sopenharmony_ci bool hdmi; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (indexp) 7228c2ecf20Sopenharmony_ci *indexp = 0; 7238c2ecf20Sopenharmony_ci if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) 7248c2ecf20Sopenharmony_ci return 0; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci switch (get_defcfg_device(def_conf)) { 7278c2ecf20Sopenharmony_ci case AC_JACK_LINE_OUT: 7288c2ecf20Sopenharmony_ci return fill_audio_out_name(codec, nid, cfg, "Line Out", 7298c2ecf20Sopenharmony_ci label, maxlen, indexp); 7308c2ecf20Sopenharmony_ci case AC_JACK_SPEAKER: 7318c2ecf20Sopenharmony_ci return fill_audio_out_name(codec, nid, cfg, "Speaker", 7328c2ecf20Sopenharmony_ci label, maxlen, indexp); 7338c2ecf20Sopenharmony_ci case AC_JACK_HP_OUT: 7348c2ecf20Sopenharmony_ci return fill_audio_out_name(codec, nid, cfg, "Headphone", 7358c2ecf20Sopenharmony_ci label, maxlen, indexp); 7368c2ecf20Sopenharmony_ci case AC_JACK_SPDIF_OUT: 7378c2ecf20Sopenharmony_ci case AC_JACK_DIG_OTHER_OUT: 7388c2ecf20Sopenharmony_ci hdmi = is_hdmi_cfg(def_conf); 7398c2ecf20Sopenharmony_ci name = hdmi ? "HDMI" : "SPDIF"; 7408c2ecf20Sopenharmony_ci if (cfg && indexp) 7418c2ecf20Sopenharmony_ci for (i = 0; i < cfg->dig_outs; i++) { 7428c2ecf20Sopenharmony_ci hda_nid_t pin = cfg->dig_out_pins[i]; 7438c2ecf20Sopenharmony_ci unsigned int c; 7448c2ecf20Sopenharmony_ci if (pin == nid) 7458c2ecf20Sopenharmony_ci break; 7468c2ecf20Sopenharmony_ci c = snd_hda_codec_get_pincfg(codec, pin); 7478c2ecf20Sopenharmony_ci if (hdmi == is_hdmi_cfg(c)) 7488c2ecf20Sopenharmony_ci (*indexp)++; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci default: 7528c2ecf20Sopenharmony_ci if (cfg) { 7538c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_inputs; i++) { 7548c2ecf20Sopenharmony_ci if (cfg->inputs[i].pin != nid) 7558c2ecf20Sopenharmony_ci continue; 7568c2ecf20Sopenharmony_ci name = hda_get_autocfg_input_label(codec, cfg, i); 7578c2ecf20Sopenharmony_ci if (name) 7588c2ecf20Sopenharmony_ci break; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci if (!name) 7628c2ecf20Sopenharmony_ci name = hda_get_input_pin_label(codec, NULL, nid, true); 7638c2ecf20Sopenharmony_ci break; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci if (!name) 7668c2ecf20Sopenharmony_ci return 0; 7678c2ecf20Sopenharmony_ci strlcpy(label, name, maxlen); 7688c2ecf20Sopenharmony_ci return 1; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_pin_label); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/** 7738c2ecf20Sopenharmony_ci * snd_hda_add_verbs - Add verbs to the init list 7748c2ecf20Sopenharmony_ci * @codec: the HDA codec 7758c2ecf20Sopenharmony_ci * @list: zero-terminated verb list to add 7768c2ecf20Sopenharmony_ci * 7778c2ecf20Sopenharmony_ci * Append the given verb list to the execution list. The verbs will be 7788c2ecf20Sopenharmony_ci * performed at init and resume time via snd_hda_apply_verbs(). 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_ciint snd_hda_add_verbs(struct hda_codec *codec, 7818c2ecf20Sopenharmony_ci const struct hda_verb *list) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci const struct hda_verb **v; 7848c2ecf20Sopenharmony_ci v = snd_array_new(&codec->verbs); 7858c2ecf20Sopenharmony_ci if (!v) 7868c2ecf20Sopenharmony_ci return -ENOMEM; 7878c2ecf20Sopenharmony_ci *v = list; 7888c2ecf20Sopenharmony_ci return 0; 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_add_verbs); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci/** 7938c2ecf20Sopenharmony_ci * snd_hda_apply_verbs - Execute the init verb lists 7948c2ecf20Sopenharmony_ci * @codec: the HDA codec 7958c2ecf20Sopenharmony_ci */ 7968c2ecf20Sopenharmony_civoid snd_hda_apply_verbs(struct hda_codec *codec) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci const struct hda_verb **v; 7998c2ecf20Sopenharmony_ci int i; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci snd_array_for_each(&codec->verbs, i, v) 8028c2ecf20Sopenharmony_ci snd_hda_sequence_write(codec, *v); 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_apply_verbs); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci/** 8078c2ecf20Sopenharmony_ci * snd_hda_apply_pincfgs - Set each pin config in the given list 8088c2ecf20Sopenharmony_ci * @codec: the HDA codec 8098c2ecf20Sopenharmony_ci * @cfg: NULL-terminated pin config table 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_civoid snd_hda_apply_pincfgs(struct hda_codec *codec, 8128c2ecf20Sopenharmony_ci const struct hda_pintbl *cfg) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci for (; cfg->nid; cfg++) 8158c2ecf20Sopenharmony_ci snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_apply_pincfgs); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic void set_pin_targets(struct hda_codec *codec, 8208c2ecf20Sopenharmony_ci const struct hda_pintbl *cfg) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci for (; cfg->nid; cfg++) 8238c2ecf20Sopenharmony_ci snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val); 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_civoid __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci const char *modelname = codec->fixup_name; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci while (id >= 0) { 8318c2ecf20Sopenharmony_ci const struct hda_fixup *fix = codec->fixup_list + id; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (++depth > 10) 8348c2ecf20Sopenharmony_ci break; 8358c2ecf20Sopenharmony_ci if (fix->chained_before) 8368c2ecf20Sopenharmony_ci __snd_hda_apply_fixup(codec, fix->chain_id, action, depth + 1); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci switch (fix->type) { 8398c2ecf20Sopenharmony_ci case HDA_FIXUP_PINS: 8408c2ecf20Sopenharmony_ci if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins) 8418c2ecf20Sopenharmony_ci break; 8428c2ecf20Sopenharmony_ci codec_dbg(codec, "%s: Apply pincfg for %s\n", 8438c2ecf20Sopenharmony_ci codec->core.chip_name, modelname); 8448c2ecf20Sopenharmony_ci snd_hda_apply_pincfgs(codec, fix->v.pins); 8458c2ecf20Sopenharmony_ci break; 8468c2ecf20Sopenharmony_ci case HDA_FIXUP_VERBS: 8478c2ecf20Sopenharmony_ci if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs) 8488c2ecf20Sopenharmony_ci break; 8498c2ecf20Sopenharmony_ci codec_dbg(codec, "%s: Apply fix-verbs for %s\n", 8508c2ecf20Sopenharmony_ci codec->core.chip_name, modelname); 8518c2ecf20Sopenharmony_ci snd_hda_add_verbs(codec, fix->v.verbs); 8528c2ecf20Sopenharmony_ci break; 8538c2ecf20Sopenharmony_ci case HDA_FIXUP_FUNC: 8548c2ecf20Sopenharmony_ci if (!fix->v.func) 8558c2ecf20Sopenharmony_ci break; 8568c2ecf20Sopenharmony_ci codec_dbg(codec, "%s: Apply fix-func for %s\n", 8578c2ecf20Sopenharmony_ci codec->core.chip_name, modelname); 8588c2ecf20Sopenharmony_ci fix->v.func(codec, fix, action); 8598c2ecf20Sopenharmony_ci break; 8608c2ecf20Sopenharmony_ci case HDA_FIXUP_PINCTLS: 8618c2ecf20Sopenharmony_ci if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins) 8628c2ecf20Sopenharmony_ci break; 8638c2ecf20Sopenharmony_ci codec_dbg(codec, "%s: Apply pinctl for %s\n", 8648c2ecf20Sopenharmony_ci codec->core.chip_name, modelname); 8658c2ecf20Sopenharmony_ci set_pin_targets(codec, fix->v.pins); 8668c2ecf20Sopenharmony_ci break; 8678c2ecf20Sopenharmony_ci default: 8688c2ecf20Sopenharmony_ci codec_err(codec, "%s: Invalid fixup type %d\n", 8698c2ecf20Sopenharmony_ci codec->core.chip_name, fix->type); 8708c2ecf20Sopenharmony_ci break; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci if (!fix->chained || fix->chained_before) 8738c2ecf20Sopenharmony_ci break; 8748c2ecf20Sopenharmony_ci id = fix->chain_id; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__snd_hda_apply_fixup); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci/** 8808c2ecf20Sopenharmony_ci * snd_hda_apply_fixup - Apply the fixup chain with the given action 8818c2ecf20Sopenharmony_ci * @codec: the HDA codec 8828c2ecf20Sopenharmony_ci * @action: fixup action (HDA_FIXUP_ACT_XXX) 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_civoid snd_hda_apply_fixup(struct hda_codec *codec, int action) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci if (codec->fixup_list) 8878c2ecf20Sopenharmony_ci __snd_hda_apply_fixup(codec, codec->fixup_id, action, 0); 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_apply_fixup); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC)) 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic bool pin_config_match(struct hda_codec *codec, 8948c2ecf20Sopenharmony_ci const struct hda_pintbl *pins, 8958c2ecf20Sopenharmony_ci bool match_all_pins) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci const struct hda_pincfg *pin; 8988c2ecf20Sopenharmony_ci int i; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci snd_array_for_each(&codec->init_pins, i, pin) { 9018c2ecf20Sopenharmony_ci hda_nid_t nid = pin->nid; 9028c2ecf20Sopenharmony_ci u32 cfg = pin->cfg; 9038c2ecf20Sopenharmony_ci const struct hda_pintbl *t_pins; 9048c2ecf20Sopenharmony_ci int found; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci t_pins = pins; 9078c2ecf20Sopenharmony_ci found = 0; 9088c2ecf20Sopenharmony_ci for (; t_pins->nid; t_pins++) { 9098c2ecf20Sopenharmony_ci if (t_pins->nid == nid) { 9108c2ecf20Sopenharmony_ci found = 1; 9118c2ecf20Sopenharmony_ci if ((t_pins->val & IGNORE_SEQ_ASSOC) == (cfg & IGNORE_SEQ_ASSOC)) 9128c2ecf20Sopenharmony_ci break; 9138c2ecf20Sopenharmony_ci else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000) 9148c2ecf20Sopenharmony_ci break; 9158c2ecf20Sopenharmony_ci else 9168c2ecf20Sopenharmony_ci return false; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci if (match_all_pins && 9208c2ecf20Sopenharmony_ci !found && (cfg & 0xf0000000) != 0x40000000) 9218c2ecf20Sopenharmony_ci return false; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci return true; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci/** 9288c2ecf20Sopenharmony_ci * snd_hda_pick_pin_fixup - Pick up a fixup matching with the pin quirk list 9298c2ecf20Sopenharmony_ci * @codec: the HDA codec 9308c2ecf20Sopenharmony_ci * @pin_quirk: zero-terminated pin quirk list 9318c2ecf20Sopenharmony_ci * @fixlist: the fixup list 9328c2ecf20Sopenharmony_ci * @match_all_pins: all valid pins must match with the table entries 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_civoid snd_hda_pick_pin_fixup(struct hda_codec *codec, 9358c2ecf20Sopenharmony_ci const struct snd_hda_pin_quirk *pin_quirk, 9368c2ecf20Sopenharmony_ci const struct hda_fixup *fixlist, 9378c2ecf20Sopenharmony_ci bool match_all_pins) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci const struct snd_hda_pin_quirk *pq; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET) 9428c2ecf20Sopenharmony_ci return; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci for (pq = pin_quirk; pq->subvendor; pq++) { 9458c2ecf20Sopenharmony_ci if ((codec->core.subsystem_id & 0xffff0000) != (pq->subvendor << 16)) 9468c2ecf20Sopenharmony_ci continue; 9478c2ecf20Sopenharmony_ci if (codec->core.vendor_id != pq->codec) 9488c2ecf20Sopenharmony_ci continue; 9498c2ecf20Sopenharmony_ci if (pin_config_match(codec, pq->pins, match_all_pins)) { 9508c2ecf20Sopenharmony_ci codec->fixup_id = pq->value; 9518c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG_VERBOSE 9528c2ecf20Sopenharmony_ci codec->fixup_name = pq->name; 9538c2ecf20Sopenharmony_ci codec_dbg(codec, "%s: picked fixup %s (pin match)\n", 9548c2ecf20Sopenharmony_ci codec->core.chip_name, codec->fixup_name); 9558c2ecf20Sopenharmony_ci#endif 9568c2ecf20Sopenharmony_ci codec->fixup_list = fixlist; 9578c2ecf20Sopenharmony_ci return; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci/** 9648c2ecf20Sopenharmony_ci * snd_hda_pick_fixup - Pick up a fixup matching with PCI/codec SSID or model string 9658c2ecf20Sopenharmony_ci * @codec: the HDA codec 9668c2ecf20Sopenharmony_ci * @models: NULL-terminated model string list 9678c2ecf20Sopenharmony_ci * @quirk: zero-terminated PCI/codec SSID quirk list 9688c2ecf20Sopenharmony_ci * @fixlist: the fixup list 9698c2ecf20Sopenharmony_ci * 9708c2ecf20Sopenharmony_ci * Pick up a fixup entry matching with the given model string or SSID. 9718c2ecf20Sopenharmony_ci * If a fixup was already set beforehand, the function doesn't do anything. 9728c2ecf20Sopenharmony_ci * When a special model string "nofixup" is given, also no fixup is applied. 9738c2ecf20Sopenharmony_ci * 9748c2ecf20Sopenharmony_ci * The function tries to find the matching model name at first, if given. 9758c2ecf20Sopenharmony_ci * If nothing matched, try to look up the PCI SSID. 9768c2ecf20Sopenharmony_ci * If still nothing matched, try to look up the codec SSID. 9778c2ecf20Sopenharmony_ci */ 9788c2ecf20Sopenharmony_civoid snd_hda_pick_fixup(struct hda_codec *codec, 9798c2ecf20Sopenharmony_ci const struct hda_model_fixup *models, 9808c2ecf20Sopenharmony_ci const struct snd_pci_quirk *quirk, 9818c2ecf20Sopenharmony_ci const struct hda_fixup *fixlist) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci const struct snd_pci_quirk *q; 9848c2ecf20Sopenharmony_ci int id = HDA_FIXUP_ID_NOT_SET; 9858c2ecf20Sopenharmony_ci const char *name = NULL; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET) 9888c2ecf20Sopenharmony_ci return; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci /* when model=nofixup is given, don't pick up any fixups */ 9918c2ecf20Sopenharmony_ci if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { 9928c2ecf20Sopenharmony_ci codec->fixup_list = NULL; 9938c2ecf20Sopenharmony_ci codec->fixup_name = NULL; 9948c2ecf20Sopenharmony_ci codec->fixup_id = HDA_FIXUP_ID_NO_FIXUP; 9958c2ecf20Sopenharmony_ci codec_dbg(codec, "%s: picked no fixup (nofixup specified)\n", 9968c2ecf20Sopenharmony_ci codec->core.chip_name); 9978c2ecf20Sopenharmony_ci return; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci if (codec->modelname && models) { 10018c2ecf20Sopenharmony_ci while (models->name) { 10028c2ecf20Sopenharmony_ci if (!strcmp(codec->modelname, models->name)) { 10038c2ecf20Sopenharmony_ci codec->fixup_id = models->id; 10048c2ecf20Sopenharmony_ci codec->fixup_name = models->name; 10058c2ecf20Sopenharmony_ci codec->fixup_list = fixlist; 10068c2ecf20Sopenharmony_ci codec_dbg(codec, "%s: picked fixup %s (model specified)\n", 10078c2ecf20Sopenharmony_ci codec->core.chip_name, codec->fixup_name); 10088c2ecf20Sopenharmony_ci return; 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci models++; 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci if (quirk) { 10148c2ecf20Sopenharmony_ci q = snd_pci_quirk_lookup(codec->bus->pci, quirk); 10158c2ecf20Sopenharmony_ci if (q) { 10168c2ecf20Sopenharmony_ci id = q->value; 10178c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG_VERBOSE 10188c2ecf20Sopenharmony_ci name = q->name; 10198c2ecf20Sopenharmony_ci codec_dbg(codec, "%s: picked fixup %s (PCI SSID%s)\n", 10208c2ecf20Sopenharmony_ci codec->core.chip_name, name, q->subdevice_mask ? "" : " - vendor generic"); 10218c2ecf20Sopenharmony_ci#endif 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci if (id < 0 && quirk) { 10258c2ecf20Sopenharmony_ci for (q = quirk; q->subvendor || q->subdevice; q++) { 10268c2ecf20Sopenharmony_ci unsigned int vendorid = 10278c2ecf20Sopenharmony_ci q->subdevice | (q->subvendor << 16); 10288c2ecf20Sopenharmony_ci unsigned int mask = 0xffff0000 | q->subdevice_mask; 10298c2ecf20Sopenharmony_ci if ((codec->core.subsystem_id & mask) == (vendorid & mask)) { 10308c2ecf20Sopenharmony_ci id = q->value; 10318c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG_VERBOSE 10328c2ecf20Sopenharmony_ci name = q->name; 10338c2ecf20Sopenharmony_ci codec_dbg(codec, "%s: picked fixup %s (codec SSID)\n", 10348c2ecf20Sopenharmony_ci codec->core.chip_name, name); 10358c2ecf20Sopenharmony_ci#endif 10368c2ecf20Sopenharmony_ci break; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci codec->fixup_id = id; 10428c2ecf20Sopenharmony_ci if (id >= 0) { 10438c2ecf20Sopenharmony_ci codec->fixup_list = fixlist; 10448c2ecf20Sopenharmony_ci codec->fixup_name = name; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_pick_fixup); 1048