18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Universal Interface for Intel High Definition Audio Codec 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/mutex.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/pm.h> 148c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 158c2ecf20Sopenharmony_ci#include <sound/core.h> 168c2ecf20Sopenharmony_ci#include <sound/hda_codec.h> 178c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 188c2ecf20Sopenharmony_ci#include <sound/tlv.h> 198c2ecf20Sopenharmony_ci#include <sound/initval.h> 208c2ecf20Sopenharmony_ci#include <sound/jack.h> 218c2ecf20Sopenharmony_ci#include "hda_local.h" 228c2ecf20Sopenharmony_ci#include "hda_beep.h" 238c2ecf20Sopenharmony_ci#include "hda_jack.h" 248c2ecf20Sopenharmony_ci#include <sound/hda_hwdep.h> 258c2ecf20Sopenharmony_ci#include <sound/hda_component.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define codec_in_pm(codec) snd_hdac_is_in_pm(&codec->core) 288c2ecf20Sopenharmony_ci#define hda_codec_is_power_on(codec) snd_hdac_is_power_on(&codec->core) 298c2ecf20Sopenharmony_ci#define codec_has_epss(codec) \ 308c2ecf20Sopenharmony_ci ((codec)->core.power_caps & AC_PWRST_EPSS) 318c2ecf20Sopenharmony_ci#define codec_has_clkstop(codec) \ 328c2ecf20Sopenharmony_ci ((codec)->core.power_caps & AC_PWRST_CLKSTOP) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci * Send and receive a verb - passed to exec_verb override for hdac_device 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_cistatic int codec_exec_verb(struct hdac_device *dev, unsigned int cmd, 388c2ecf20Sopenharmony_ci unsigned int flags, unsigned int *res) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct hda_codec *codec = container_of(dev, struct hda_codec, core); 418c2ecf20Sopenharmony_ci struct hda_bus *bus = codec->bus; 428c2ecf20Sopenharmony_ci int err; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (cmd == ~0) 458c2ecf20Sopenharmony_ci return -1; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci again: 488c2ecf20Sopenharmony_ci snd_hda_power_up_pm(codec); 498c2ecf20Sopenharmony_ci mutex_lock(&bus->core.cmd_mutex); 508c2ecf20Sopenharmony_ci if (flags & HDA_RW_NO_RESPONSE_FALLBACK) 518c2ecf20Sopenharmony_ci bus->no_response_fallback = 1; 528c2ecf20Sopenharmony_ci err = snd_hdac_bus_exec_verb_unlocked(&bus->core, codec->core.addr, 538c2ecf20Sopenharmony_ci cmd, res); 548c2ecf20Sopenharmony_ci bus->no_response_fallback = 0; 558c2ecf20Sopenharmony_ci mutex_unlock(&bus->core.cmd_mutex); 568c2ecf20Sopenharmony_ci snd_hda_power_down_pm(codec); 578c2ecf20Sopenharmony_ci if (!codec_in_pm(codec) && res && err == -EAGAIN) { 588c2ecf20Sopenharmony_ci if (bus->response_reset) { 598c2ecf20Sopenharmony_ci codec_dbg(codec, 608c2ecf20Sopenharmony_ci "resetting BUS due to fatal communication error\n"); 618c2ecf20Sopenharmony_ci snd_hda_bus_reset(bus); 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci goto again; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci /* clear reset-flag when the communication gets recovered */ 668c2ecf20Sopenharmony_ci if (!err || codec_in_pm(codec)) 678c2ecf20Sopenharmony_ci bus->response_reset = 0; 688c2ecf20Sopenharmony_ci return err; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/** 728c2ecf20Sopenharmony_ci * snd_hda_sequence_write - sequence writes 738c2ecf20Sopenharmony_ci * @codec: the HDA codec 748c2ecf20Sopenharmony_ci * @seq: VERB array to send 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * Send the commands sequentially from the given array. 778c2ecf20Sopenharmony_ci * The array must be terminated with NID=0. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_civoid snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci for (; seq->nid; seq++) 828c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_sequence_write); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* connection list element */ 878c2ecf20Sopenharmony_cistruct hda_conn_list { 888c2ecf20Sopenharmony_ci struct list_head list; 898c2ecf20Sopenharmony_ci int len; 908c2ecf20Sopenharmony_ci hda_nid_t nid; 918c2ecf20Sopenharmony_ci hda_nid_t conns[]; 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* look up the cached results */ 958c2ecf20Sopenharmony_cistatic struct hda_conn_list * 968c2ecf20Sopenharmony_cilookup_conn_list(struct hda_codec *codec, hda_nid_t nid) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct hda_conn_list *p; 998c2ecf20Sopenharmony_ci list_for_each_entry(p, &codec->conn_list, list) { 1008c2ecf20Sopenharmony_ci if (p->nid == nid) 1018c2ecf20Sopenharmony_ci return p; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci return NULL; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, 1078c2ecf20Sopenharmony_ci const hda_nid_t *list) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct hda_conn_list *p; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci p = kmalloc(struct_size(p, conns, len), GFP_KERNEL); 1128c2ecf20Sopenharmony_ci if (!p) 1138c2ecf20Sopenharmony_ci return -ENOMEM; 1148c2ecf20Sopenharmony_ci p->len = len; 1158c2ecf20Sopenharmony_ci p->nid = nid; 1168c2ecf20Sopenharmony_ci memcpy(p->conns, list, len * sizeof(hda_nid_t)); 1178c2ecf20Sopenharmony_ci list_add(&p->list, &codec->conn_list); 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void remove_conn_list(struct hda_codec *codec) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci while (!list_empty(&codec->conn_list)) { 1248c2ecf20Sopenharmony_ci struct hda_conn_list *p; 1258c2ecf20Sopenharmony_ci p = list_first_entry(&codec->conn_list, typeof(*p), list); 1268c2ecf20Sopenharmony_ci list_del(&p->list); 1278c2ecf20Sopenharmony_ci kfree(p); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* read the connection and add to the cache */ 1328c2ecf20Sopenharmony_cistatic int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci hda_nid_t list[32]; 1358c2ecf20Sopenharmony_ci hda_nid_t *result = list; 1368c2ecf20Sopenharmony_ci int len; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list)); 1398c2ecf20Sopenharmony_ci if (len == -ENOSPC) { 1408c2ecf20Sopenharmony_ci len = snd_hda_get_num_raw_conns(codec, nid); 1418c2ecf20Sopenharmony_ci result = kmalloc_array(len, sizeof(hda_nid_t), GFP_KERNEL); 1428c2ecf20Sopenharmony_ci if (!result) 1438c2ecf20Sopenharmony_ci return -ENOMEM; 1448c2ecf20Sopenharmony_ci len = snd_hda_get_raw_connections(codec, nid, result, len); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci if (len >= 0) 1478c2ecf20Sopenharmony_ci len = snd_hda_override_conn_list(codec, nid, len, result); 1488c2ecf20Sopenharmony_ci if (result != list) 1498c2ecf20Sopenharmony_ci kfree(result); 1508c2ecf20Sopenharmony_ci return len; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/** 1548c2ecf20Sopenharmony_ci * snd_hda_get_conn_list - get connection list 1558c2ecf20Sopenharmony_ci * @codec: the HDA codec 1568c2ecf20Sopenharmony_ci * @nid: NID to parse 1578c2ecf20Sopenharmony_ci * @listp: the pointer to store NID list 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * Parses the connection list of the given widget and stores the pointer 1608c2ecf20Sopenharmony_ci * to the list of NIDs. 1618c2ecf20Sopenharmony_ci * 1628c2ecf20Sopenharmony_ci * Returns the number of connections, or a negative error code. 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * Note that the returned pointer isn't protected against the list 1658c2ecf20Sopenharmony_ci * modification. If snd_hda_override_conn_list() might be called 1668c2ecf20Sopenharmony_ci * concurrently, protect with a mutex appropriately. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ciint snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, 1698c2ecf20Sopenharmony_ci const hda_nid_t **listp) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci bool added = false; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci for (;;) { 1748c2ecf20Sopenharmony_ci int err; 1758c2ecf20Sopenharmony_ci const struct hda_conn_list *p; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* if the connection-list is already cached, read it */ 1788c2ecf20Sopenharmony_ci p = lookup_conn_list(codec, nid); 1798c2ecf20Sopenharmony_ci if (p) { 1808c2ecf20Sopenharmony_ci if (listp) 1818c2ecf20Sopenharmony_ci *listp = p->conns; 1828c2ecf20Sopenharmony_ci return p->len; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci if (snd_BUG_ON(added)) 1858c2ecf20Sopenharmony_ci return -EINVAL; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci err = read_and_add_raw_conns(codec, nid); 1888c2ecf20Sopenharmony_ci if (err < 0) 1898c2ecf20Sopenharmony_ci return err; 1908c2ecf20Sopenharmony_ci added = true; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_conn_list); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/** 1968c2ecf20Sopenharmony_ci * snd_hda_get_connections - copy connection list 1978c2ecf20Sopenharmony_ci * @codec: the HDA codec 1988c2ecf20Sopenharmony_ci * @nid: NID to parse 1998c2ecf20Sopenharmony_ci * @conn_list: connection list array; when NULL, checks only the size 2008c2ecf20Sopenharmony_ci * @max_conns: max. number of connections to store 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * Parses the connection list of the given widget and stores the list 2038c2ecf20Sopenharmony_ci * of NIDs. 2048c2ecf20Sopenharmony_ci * 2058c2ecf20Sopenharmony_ci * Returns the number of connections, or a negative error code. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ciint snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, 2088c2ecf20Sopenharmony_ci hda_nid_t *conn_list, int max_conns) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci const hda_nid_t *list; 2118c2ecf20Sopenharmony_ci int len = snd_hda_get_conn_list(codec, nid, &list); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (len > 0 && conn_list) { 2148c2ecf20Sopenharmony_ci if (len > max_conns) { 2158c2ecf20Sopenharmony_ci codec_err(codec, "Too many connections %d for NID 0x%x\n", 2168c2ecf20Sopenharmony_ci len, nid); 2178c2ecf20Sopenharmony_ci return -EINVAL; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci memcpy(conn_list, list, len * sizeof(hda_nid_t)); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return len; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_connections); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/** 2278c2ecf20Sopenharmony_ci * snd_hda_override_conn_list - add/modify the connection-list to cache 2288c2ecf20Sopenharmony_ci * @codec: the HDA codec 2298c2ecf20Sopenharmony_ci * @nid: NID to parse 2308c2ecf20Sopenharmony_ci * @len: number of connection list entries 2318c2ecf20Sopenharmony_ci * @list: the list of connection entries 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * Add or modify the given connection-list to the cache. If the corresponding 2348c2ecf20Sopenharmony_ci * cache already exists, invalidate it and append a new one. 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * Returns zero or a negative error code. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ciint snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, 2398c2ecf20Sopenharmony_ci const hda_nid_t *list) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct hda_conn_list *p; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci p = lookup_conn_list(codec, nid); 2448c2ecf20Sopenharmony_ci if (p) { 2458c2ecf20Sopenharmony_ci list_del(&p->list); 2468c2ecf20Sopenharmony_ci kfree(p); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return add_conn_list(codec, nid, len, list); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_override_conn_list); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/** 2548c2ecf20Sopenharmony_ci * snd_hda_get_conn_index - get the connection index of the given NID 2558c2ecf20Sopenharmony_ci * @codec: the HDA codec 2568c2ecf20Sopenharmony_ci * @mux: NID containing the list 2578c2ecf20Sopenharmony_ci * @nid: NID to select 2588c2ecf20Sopenharmony_ci * @recursive: 1 when searching NID recursively, otherwise 0 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * Parses the connection list of the widget @mux and checks whether the 2618c2ecf20Sopenharmony_ci * widget @nid is present. If it is, return the connection index. 2628c2ecf20Sopenharmony_ci * Otherwise it returns -1. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ciint snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, 2658c2ecf20Sopenharmony_ci hda_nid_t nid, int recursive) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci const hda_nid_t *conn; 2688c2ecf20Sopenharmony_ci int i, nums; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci nums = snd_hda_get_conn_list(codec, mux, &conn); 2718c2ecf20Sopenharmony_ci for (i = 0; i < nums; i++) 2728c2ecf20Sopenharmony_ci if (conn[i] == nid) 2738c2ecf20Sopenharmony_ci return i; 2748c2ecf20Sopenharmony_ci if (!recursive) 2758c2ecf20Sopenharmony_ci return -1; 2768c2ecf20Sopenharmony_ci if (recursive > 10) { 2778c2ecf20Sopenharmony_ci codec_dbg(codec, "too deep connection for 0x%x\n", nid); 2788c2ecf20Sopenharmony_ci return -1; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci recursive++; 2818c2ecf20Sopenharmony_ci for (i = 0; i < nums; i++) { 2828c2ecf20Sopenharmony_ci unsigned int type = get_wcaps_type(get_wcaps(codec, conn[i])); 2838c2ecf20Sopenharmony_ci if (type == AC_WID_PIN || type == AC_WID_AUD_OUT) 2848c2ecf20Sopenharmony_ci continue; 2858c2ecf20Sopenharmony_ci if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0) 2868c2ecf20Sopenharmony_ci return i; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci return -1; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_conn_index); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/** 2938c2ecf20Sopenharmony_ci * snd_hda_get_num_devices - get DEVLIST_LEN parameter of the given widget 2948c2ecf20Sopenharmony_ci * @codec: the HDA codec 2958c2ecf20Sopenharmony_ci * @nid: NID of the pin to parse 2968c2ecf20Sopenharmony_ci * 2978c2ecf20Sopenharmony_ci * Get the device entry number on the given widget. This is a feature of 2988c2ecf20Sopenharmony_ci * DP MST audio. Each pin can have several device entries in it. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ciunsigned int snd_hda_get_num_devices(struct hda_codec *codec, hda_nid_t nid) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci unsigned int wcaps = get_wcaps(codec, nid); 3038c2ecf20Sopenharmony_ci unsigned int parm; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) || 3068c2ecf20Sopenharmony_ci get_wcaps_type(wcaps) != AC_WID_PIN) 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci parm = snd_hdac_read_parm_uncached(&codec->core, nid, AC_PAR_DEVLIST_LEN); 3108c2ecf20Sopenharmony_ci if (parm == -1) 3118c2ecf20Sopenharmony_ci parm = 0; 3128c2ecf20Sopenharmony_ci return parm & AC_DEV_LIST_LEN_MASK; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_num_devices); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci/** 3178c2ecf20Sopenharmony_ci * snd_hda_get_devices - copy device list without cache 3188c2ecf20Sopenharmony_ci * @codec: the HDA codec 3198c2ecf20Sopenharmony_ci * @nid: NID of the pin to parse 3208c2ecf20Sopenharmony_ci * @dev_list: device list array 3218c2ecf20Sopenharmony_ci * @max_devices: max. number of devices to store 3228c2ecf20Sopenharmony_ci * 3238c2ecf20Sopenharmony_ci * Copy the device list. This info is dynamic and so not cached. 3248c2ecf20Sopenharmony_ci * Currently called only from hda_proc.c, so not exported. 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ciint snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, 3278c2ecf20Sopenharmony_ci u8 *dev_list, int max_devices) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci unsigned int parm; 3308c2ecf20Sopenharmony_ci int i, dev_len, devices; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci parm = snd_hda_get_num_devices(codec, nid); 3338c2ecf20Sopenharmony_ci if (!parm) /* not multi-stream capable */ 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci dev_len = parm + 1; 3378c2ecf20Sopenharmony_ci dev_len = dev_len < max_devices ? dev_len : max_devices; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci devices = 0; 3408c2ecf20Sopenharmony_ci while (devices < dev_len) { 3418c2ecf20Sopenharmony_ci if (snd_hdac_read(&codec->core, nid, 3428c2ecf20Sopenharmony_ci AC_VERB_GET_DEVICE_LIST, devices, &parm)) 3438c2ecf20Sopenharmony_ci break; /* error */ 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 3468c2ecf20Sopenharmony_ci dev_list[devices] = (u8)parm; 3478c2ecf20Sopenharmony_ci parm >>= 4; 3488c2ecf20Sopenharmony_ci devices++; 3498c2ecf20Sopenharmony_ci if (devices >= dev_len) 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci return devices; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/** 3578c2ecf20Sopenharmony_ci * snd_hda_get_dev_select - get device entry select on the pin 3588c2ecf20Sopenharmony_ci * @codec: the HDA codec 3598c2ecf20Sopenharmony_ci * @nid: NID of the pin to get device entry select 3608c2ecf20Sopenharmony_ci * 3618c2ecf20Sopenharmony_ci * Get the devcie entry select on the pin. Return the device entry 3628c2ecf20Sopenharmony_ci * id selected on the pin. Return 0 means the first device entry 3638c2ecf20Sopenharmony_ci * is selected or MST is not supported. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_ciint snd_hda_get_dev_select(struct hda_codec *codec, hda_nid_t nid) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci /* not support dp_mst will always return 0, using first dev_entry */ 3688c2ecf20Sopenharmony_ci if (!codec->dp_mst) 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DEVICE_SEL, 0); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_dev_select); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/** 3768c2ecf20Sopenharmony_ci * snd_hda_set_dev_select - set device entry select on the pin 3778c2ecf20Sopenharmony_ci * @codec: the HDA codec 3788c2ecf20Sopenharmony_ci * @nid: NID of the pin to set device entry select 3798c2ecf20Sopenharmony_ci * @dev_id: device entry id to be set 3808c2ecf20Sopenharmony_ci * 3818c2ecf20Sopenharmony_ci * Set the device entry select on the pin nid. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ciint snd_hda_set_dev_select(struct hda_codec *codec, hda_nid_t nid, int dev_id) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci int ret, num_devices; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* not support dp_mst will always return 0, using first dev_entry */ 3888c2ecf20Sopenharmony_ci if (!codec->dp_mst) 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* AC_PAR_DEVLIST_LEN is 0 based. */ 3928c2ecf20Sopenharmony_ci num_devices = snd_hda_get_num_devices(codec, nid) + 1; 3938c2ecf20Sopenharmony_ci /* If Device List Length is 0 (num_device = 1), 3948c2ecf20Sopenharmony_ci * the pin is not multi stream capable. 3958c2ecf20Sopenharmony_ci * Do nothing in this case. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci if (num_devices == 1) 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* Behavior of setting index being equal to or greater than 4018c2ecf20Sopenharmony_ci * Device List Length is not predictable 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci if (num_devices <= dev_id) 4048c2ecf20Sopenharmony_ci return -EINVAL; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ret = snd_hda_codec_write(codec, nid, 0, 4078c2ecf20Sopenharmony_ci AC_VERB_SET_DEVICE_SEL, dev_id); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return ret; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_set_dev_select); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/* 4148c2ecf20Sopenharmony_ci * read widget caps for each widget and store in cache 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_cistatic int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci int i; 4198c2ecf20Sopenharmony_ci hda_nid_t nid; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci codec->wcaps = kmalloc_array(codec->core.num_nodes, 4, GFP_KERNEL); 4228c2ecf20Sopenharmony_ci if (!codec->wcaps) 4238c2ecf20Sopenharmony_ci return -ENOMEM; 4248c2ecf20Sopenharmony_ci nid = codec->core.start_nid; 4258c2ecf20Sopenharmony_ci for (i = 0; i < codec->core.num_nodes; i++, nid++) 4268c2ecf20Sopenharmony_ci codec->wcaps[i] = snd_hdac_read_parm_uncached(&codec->core, 4278c2ecf20Sopenharmony_ci nid, AC_PAR_AUDIO_WIDGET_CAP); 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci/* read all pin default configurations and save codec->init_pins */ 4328c2ecf20Sopenharmony_cistatic int read_pin_defaults(struct hda_codec *codec) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci hda_nid_t nid; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci for_each_hda_codec_node(nid, codec) { 4378c2ecf20Sopenharmony_ci struct hda_pincfg *pin; 4388c2ecf20Sopenharmony_ci unsigned int wcaps = get_wcaps(codec, nid); 4398c2ecf20Sopenharmony_ci unsigned int wid_type = get_wcaps_type(wcaps); 4408c2ecf20Sopenharmony_ci if (wid_type != AC_WID_PIN) 4418c2ecf20Sopenharmony_ci continue; 4428c2ecf20Sopenharmony_ci pin = snd_array_new(&codec->init_pins); 4438c2ecf20Sopenharmony_ci if (!pin) 4448c2ecf20Sopenharmony_ci return -ENOMEM; 4458c2ecf20Sopenharmony_ci pin->nid = nid; 4468c2ecf20Sopenharmony_ci pin->cfg = snd_hda_codec_read(codec, nid, 0, 4478c2ecf20Sopenharmony_ci AC_VERB_GET_CONFIG_DEFAULT, 0); 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * all device entries are the same widget control so far 4508c2ecf20Sopenharmony_ci * fixme: if any codec is different, need fix here 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci pin->ctrl = snd_hda_codec_read(codec, nid, 0, 4538c2ecf20Sopenharmony_ci AC_VERB_GET_PIN_WIDGET_CONTROL, 4548c2ecf20Sopenharmony_ci 0); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci/* look up the given pin config list and return the item matching with NID */ 4608c2ecf20Sopenharmony_cistatic struct hda_pincfg *look_up_pincfg(struct hda_codec *codec, 4618c2ecf20Sopenharmony_ci struct snd_array *array, 4628c2ecf20Sopenharmony_ci hda_nid_t nid) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct hda_pincfg *pin; 4658c2ecf20Sopenharmony_ci int i; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci snd_array_for_each(array, i, pin) { 4688c2ecf20Sopenharmony_ci if (pin->nid == nid) 4698c2ecf20Sopenharmony_ci return pin; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci return NULL; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci/* set the current pin config value for the given NID. 4758c2ecf20Sopenharmony_ci * the value is cached, and read via snd_hda_codec_get_pincfg() 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ciint snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, 4788c2ecf20Sopenharmony_ci hda_nid_t nid, unsigned int cfg) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct hda_pincfg *pin; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* the check below may be invalid when pins are added by a fixup 4838c2ecf20Sopenharmony_ci * dynamically (e.g. via snd_hda_codec_update_widgets()), so disabled 4848c2ecf20Sopenharmony_ci * for now 4858c2ecf20Sopenharmony_ci */ 4868c2ecf20Sopenharmony_ci /* 4878c2ecf20Sopenharmony_ci if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) 4888c2ecf20Sopenharmony_ci return -EINVAL; 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci pin = look_up_pincfg(codec, list, nid); 4928c2ecf20Sopenharmony_ci if (!pin) { 4938c2ecf20Sopenharmony_ci pin = snd_array_new(list); 4948c2ecf20Sopenharmony_ci if (!pin) 4958c2ecf20Sopenharmony_ci return -ENOMEM; 4968c2ecf20Sopenharmony_ci pin->nid = nid; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci pin->cfg = cfg; 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci/** 5038c2ecf20Sopenharmony_ci * snd_hda_codec_set_pincfg - Override a pin default configuration 5048c2ecf20Sopenharmony_ci * @codec: the HDA codec 5058c2ecf20Sopenharmony_ci * @nid: NID to set the pin config 5068c2ecf20Sopenharmony_ci * @cfg: the pin default config value 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * Override a pin default configuration value in the cache. 5098c2ecf20Sopenharmony_ci * This value can be read by snd_hda_codec_get_pincfg() in a higher 5108c2ecf20Sopenharmony_ci * priority than the real hardware value. 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_ciint snd_hda_codec_set_pincfg(struct hda_codec *codec, 5138c2ecf20Sopenharmony_ci hda_nid_t nid, unsigned int cfg) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg); 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_set_pincfg); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/** 5208c2ecf20Sopenharmony_ci * snd_hda_codec_get_pincfg - Obtain a pin-default configuration 5218c2ecf20Sopenharmony_ci * @codec: the HDA codec 5228c2ecf20Sopenharmony_ci * @nid: NID to get the pin config 5238c2ecf20Sopenharmony_ci * 5248c2ecf20Sopenharmony_ci * Get the current pin config value of the given pin NID. 5258c2ecf20Sopenharmony_ci * If the pincfg value is cached or overridden via sysfs or driver, 5268c2ecf20Sopenharmony_ci * returns the cached value. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ciunsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct hda_pincfg *pin; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_HDA_RECONFIG 5338c2ecf20Sopenharmony_ci { 5348c2ecf20Sopenharmony_ci unsigned int cfg = 0; 5358c2ecf20Sopenharmony_ci mutex_lock(&codec->user_mutex); 5368c2ecf20Sopenharmony_ci pin = look_up_pincfg(codec, &codec->user_pins, nid); 5378c2ecf20Sopenharmony_ci if (pin) 5388c2ecf20Sopenharmony_ci cfg = pin->cfg; 5398c2ecf20Sopenharmony_ci mutex_unlock(&codec->user_mutex); 5408c2ecf20Sopenharmony_ci if (cfg) 5418c2ecf20Sopenharmony_ci return cfg; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci#endif 5448c2ecf20Sopenharmony_ci pin = look_up_pincfg(codec, &codec->driver_pins, nid); 5458c2ecf20Sopenharmony_ci if (pin) 5468c2ecf20Sopenharmony_ci return pin->cfg; 5478c2ecf20Sopenharmony_ci pin = look_up_pincfg(codec, &codec->init_pins, nid); 5488c2ecf20Sopenharmony_ci if (pin) 5498c2ecf20Sopenharmony_ci return pin->cfg; 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_get_pincfg); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci/** 5558c2ecf20Sopenharmony_ci * snd_hda_codec_set_pin_target - remember the current pinctl target value 5568c2ecf20Sopenharmony_ci * @codec: the HDA codec 5578c2ecf20Sopenharmony_ci * @nid: pin NID 5588c2ecf20Sopenharmony_ci * @val: assigned pinctl value 5598c2ecf20Sopenharmony_ci * 5608c2ecf20Sopenharmony_ci * This function stores the given value to a pinctl target value in the 5618c2ecf20Sopenharmony_ci * pincfg table. This isn't always as same as the actually written value 5628c2ecf20Sopenharmony_ci * but can be referred at any time via snd_hda_codec_get_pin_target(). 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_ciint snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, 5658c2ecf20Sopenharmony_ci unsigned int val) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct hda_pincfg *pin; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci pin = look_up_pincfg(codec, &codec->init_pins, nid); 5708c2ecf20Sopenharmony_ci if (!pin) 5718c2ecf20Sopenharmony_ci return -EINVAL; 5728c2ecf20Sopenharmony_ci pin->target = val; 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_set_pin_target); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci/** 5788c2ecf20Sopenharmony_ci * snd_hda_codec_get_pin_target - return the current pinctl target value 5798c2ecf20Sopenharmony_ci * @codec: the HDA codec 5808c2ecf20Sopenharmony_ci * @nid: pin NID 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_ciint snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct hda_pincfg *pin; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci pin = look_up_pincfg(codec, &codec->init_pins, nid); 5878c2ecf20Sopenharmony_ci if (!pin) 5888c2ecf20Sopenharmony_ci return 0; 5898c2ecf20Sopenharmony_ci return pin->target; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_get_pin_target); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci/** 5948c2ecf20Sopenharmony_ci * snd_hda_shutup_pins - Shut up all pins 5958c2ecf20Sopenharmony_ci * @codec: the HDA codec 5968c2ecf20Sopenharmony_ci * 5978c2ecf20Sopenharmony_ci * Clear all pin controls to shup up before suspend for avoiding click noise. 5988c2ecf20Sopenharmony_ci * The controls aren't cached so that they can be resumed properly. 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_civoid snd_hda_shutup_pins(struct hda_codec *codec) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci const struct hda_pincfg *pin; 6038c2ecf20Sopenharmony_ci int i; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* don't shut up pins when unloading the driver; otherwise it breaks 6068c2ecf20Sopenharmony_ci * the default pin setup at the next load of the driver 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci if (codec->bus->shutdown) 6098c2ecf20Sopenharmony_ci return; 6108c2ecf20Sopenharmony_ci snd_array_for_each(&codec->init_pins, i, pin) { 6118c2ecf20Sopenharmony_ci /* use read here for syncing after issuing each verb */ 6128c2ecf20Sopenharmony_ci snd_hda_codec_read(codec, pin->nid, 0, 6138c2ecf20Sopenharmony_ci AC_VERB_SET_PIN_WIDGET_CONTROL, 0); 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci codec->pins_shutup = 1; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_shutup_pins); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 6208c2ecf20Sopenharmony_ci/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */ 6218c2ecf20Sopenharmony_cistatic void restore_shutup_pins(struct hda_codec *codec) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci const struct hda_pincfg *pin; 6248c2ecf20Sopenharmony_ci int i; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (!codec->pins_shutup) 6278c2ecf20Sopenharmony_ci return; 6288c2ecf20Sopenharmony_ci if (codec->bus->shutdown) 6298c2ecf20Sopenharmony_ci return; 6308c2ecf20Sopenharmony_ci snd_array_for_each(&codec->init_pins, i, pin) { 6318c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, pin->nid, 0, 6328c2ecf20Sopenharmony_ci AC_VERB_SET_PIN_WIDGET_CONTROL, 6338c2ecf20Sopenharmony_ci pin->ctrl); 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci codec->pins_shutup = 0; 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci#endif 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic void hda_jackpoll_work(struct work_struct *work) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct hda_codec *codec = 6428c2ecf20Sopenharmony_ci container_of(work, struct hda_codec, jackpoll_work.work); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci /* for non-polling trigger: we need nothing if already powered on */ 6458c2ecf20Sopenharmony_ci if (!codec->jackpoll_interval && snd_hdac_is_power_on(&codec->core)) 6468c2ecf20Sopenharmony_ci return; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* the power-up/down sequence triggers the runtime resume */ 6498c2ecf20Sopenharmony_ci snd_hda_power_up_pm(codec); 6508c2ecf20Sopenharmony_ci /* update jacks manually if polling is required, too */ 6518c2ecf20Sopenharmony_ci if (codec->jackpoll_interval) { 6528c2ecf20Sopenharmony_ci snd_hda_jack_set_dirty_all(codec); 6538c2ecf20Sopenharmony_ci snd_hda_jack_poll_all(codec); 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci snd_hda_power_down_pm(codec); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (!codec->jackpoll_interval) 6588c2ecf20Sopenharmony_ci return; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci schedule_delayed_work(&codec->jackpoll_work, 6618c2ecf20Sopenharmony_ci codec->jackpoll_interval); 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci/* release all pincfg lists */ 6658c2ecf20Sopenharmony_cistatic void free_init_pincfgs(struct hda_codec *codec) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci snd_array_free(&codec->driver_pins); 6688c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_HDA_RECONFIG 6698c2ecf20Sopenharmony_ci snd_array_free(&codec->user_pins); 6708c2ecf20Sopenharmony_ci#endif 6718c2ecf20Sopenharmony_ci snd_array_free(&codec->init_pins); 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci/* 6758c2ecf20Sopenharmony_ci * audio-converter setup caches 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_cistruct hda_cvt_setup { 6788c2ecf20Sopenharmony_ci hda_nid_t nid; 6798c2ecf20Sopenharmony_ci u8 stream_tag; 6808c2ecf20Sopenharmony_ci u8 channel_id; 6818c2ecf20Sopenharmony_ci u16 format_id; 6828c2ecf20Sopenharmony_ci unsigned char active; /* cvt is currently used */ 6838c2ecf20Sopenharmony_ci unsigned char dirty; /* setups should be cleared */ 6848c2ecf20Sopenharmony_ci}; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci/* get or create a cache entry for the given audio converter NID */ 6878c2ecf20Sopenharmony_cistatic struct hda_cvt_setup * 6888c2ecf20Sopenharmony_ciget_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci struct hda_cvt_setup *p; 6918c2ecf20Sopenharmony_ci int i; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci snd_array_for_each(&codec->cvt_setups, i, p) { 6948c2ecf20Sopenharmony_ci if (p->nid == nid) 6958c2ecf20Sopenharmony_ci return p; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci p = snd_array_new(&codec->cvt_setups); 6988c2ecf20Sopenharmony_ci if (p) 6998c2ecf20Sopenharmony_ci p->nid = nid; 7008c2ecf20Sopenharmony_ci return p; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci/* 7048c2ecf20Sopenharmony_ci * PCM device 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_cistatic void release_pcm(struct kref *kref) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (pcm->pcm) 7118c2ecf20Sopenharmony_ci snd_device_free(pcm->codec->card, pcm->pcm); 7128c2ecf20Sopenharmony_ci clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits); 7138c2ecf20Sopenharmony_ci kfree(pcm->name); 7148c2ecf20Sopenharmony_ci kfree(pcm); 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_civoid snd_hda_codec_pcm_put(struct hda_pcm *pcm) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci kref_put(&pcm->kref, release_pcm); 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistruct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, 7248c2ecf20Sopenharmony_ci const char *fmt, ...) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci struct hda_pcm *pcm; 7278c2ecf20Sopenharmony_ci va_list args; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); 7308c2ecf20Sopenharmony_ci if (!pcm) 7318c2ecf20Sopenharmony_ci return NULL; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci pcm->codec = codec; 7348c2ecf20Sopenharmony_ci kref_init(&pcm->kref); 7358c2ecf20Sopenharmony_ci va_start(args, fmt); 7368c2ecf20Sopenharmony_ci pcm->name = kvasprintf(GFP_KERNEL, fmt, args); 7378c2ecf20Sopenharmony_ci va_end(args); 7388c2ecf20Sopenharmony_ci if (!pcm->name) { 7398c2ecf20Sopenharmony_ci kfree(pcm); 7408c2ecf20Sopenharmony_ci return NULL; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci list_add_tail(&pcm->list, &codec->pcm_list_head); 7448c2ecf20Sopenharmony_ci return pcm; 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci/* 7498c2ecf20Sopenharmony_ci * codec destructor 7508c2ecf20Sopenharmony_ci */ 7518c2ecf20Sopenharmony_cistatic void codec_release_pcms(struct hda_codec *codec) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci struct hda_pcm *pcm, *n; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) { 7568c2ecf20Sopenharmony_ci list_del_init(&pcm->list); 7578c2ecf20Sopenharmony_ci if (pcm->pcm) 7588c2ecf20Sopenharmony_ci snd_device_disconnect(codec->card, pcm->pcm); 7598c2ecf20Sopenharmony_ci snd_hda_codec_pcm_put(pcm); 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_civoid snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci if (codec->registered) { 7668c2ecf20Sopenharmony_ci /* pm_runtime_put() is called in snd_hdac_device_exit() */ 7678c2ecf20Sopenharmony_ci pm_runtime_get_noresume(hda_codec_dev(codec)); 7688c2ecf20Sopenharmony_ci pm_runtime_disable(hda_codec_dev(codec)); 7698c2ecf20Sopenharmony_ci codec->registered = 0; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&codec->jackpoll_work); 7738c2ecf20Sopenharmony_ci if (!codec->in_freeing) 7748c2ecf20Sopenharmony_ci snd_hda_ctls_clear(codec); 7758c2ecf20Sopenharmony_ci codec_release_pcms(codec); 7768c2ecf20Sopenharmony_ci snd_hda_detach_beep_device(codec); 7778c2ecf20Sopenharmony_ci memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); 7788c2ecf20Sopenharmony_ci snd_hda_jack_tbl_clear(codec); 7798c2ecf20Sopenharmony_ci codec->proc_widget_hook = NULL; 7808c2ecf20Sopenharmony_ci codec->spec = NULL; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* free only driver_pins so that init_pins + user_pins are restored */ 7838c2ecf20Sopenharmony_ci snd_array_free(&codec->driver_pins); 7848c2ecf20Sopenharmony_ci snd_array_free(&codec->cvt_setups); 7858c2ecf20Sopenharmony_ci snd_array_free(&codec->spdif_out); 7868c2ecf20Sopenharmony_ci snd_array_free(&codec->verbs); 7878c2ecf20Sopenharmony_ci codec->follower_dig_outs = NULL; 7888c2ecf20Sopenharmony_ci codec->spdif_status_reset = 0; 7898c2ecf20Sopenharmony_ci snd_array_free(&codec->mixers); 7908c2ecf20Sopenharmony_ci snd_array_free(&codec->nids); 7918c2ecf20Sopenharmony_ci remove_conn_list(codec); 7928c2ecf20Sopenharmony_ci snd_hdac_regmap_exit(&codec->core); 7938c2ecf20Sopenharmony_ci codec->configured = 0; 7948c2ecf20Sopenharmony_ci} 7958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_for_unbind); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic unsigned int hda_set_power_state(struct hda_codec *codec, 7988c2ecf20Sopenharmony_ci unsigned int power_state); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci/* enable/disable display power per codec */ 8018c2ecf20Sopenharmony_cistatic void codec_display_power(struct hda_codec *codec, bool enable) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci if (codec->display_power_control) 8048c2ecf20Sopenharmony_ci snd_hdac_display_power(&codec->bus->core, codec->addr, enable); 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci/* also called from hda_bind.c */ 8088c2ecf20Sopenharmony_civoid snd_hda_codec_register(struct hda_codec *codec) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci if (codec->registered) 8118c2ecf20Sopenharmony_ci return; 8128c2ecf20Sopenharmony_ci if (device_is_registered(hda_codec_dev(codec))) { 8138c2ecf20Sopenharmony_ci codec_display_power(codec, true); 8148c2ecf20Sopenharmony_ci pm_runtime_enable(hda_codec_dev(codec)); 8158c2ecf20Sopenharmony_ci /* it was powered up in snd_hda_codec_new(), now all done */ 8168c2ecf20Sopenharmony_ci snd_hda_power_down(codec); 8178c2ecf20Sopenharmony_ci codec->registered = 1; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic int snd_hda_codec_dev_register(struct snd_device *device) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci snd_hda_codec_register(device->device_data); 8248c2ecf20Sopenharmony_ci return 0; 8258c2ecf20Sopenharmony_ci} 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_cistatic int snd_hda_codec_dev_free(struct snd_device *device) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci struct hda_codec *codec = device->device_data; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci codec->in_freeing = 1; 8328c2ecf20Sopenharmony_ci /* 8338c2ecf20Sopenharmony_ci * snd_hda_codec_device_new() is used by legacy HDA and ASoC driver. 8348c2ecf20Sopenharmony_ci * We can't unregister ASoC device since it will be unregistered in 8358c2ecf20Sopenharmony_ci * snd_hdac_ext_bus_device_remove(). 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_ci if (codec->core.type == HDA_DEV_LEGACY) 8388c2ecf20Sopenharmony_ci snd_hdac_device_unregister(&codec->core); 8398c2ecf20Sopenharmony_ci codec_display_power(codec, false); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* 8428c2ecf20Sopenharmony_ci * In the case of ASoC HD-audio bus, the device refcount is released in 8438c2ecf20Sopenharmony_ci * snd_hdac_ext_bus_device_remove() explicitly. 8448c2ecf20Sopenharmony_ci */ 8458c2ecf20Sopenharmony_ci if (codec->core.type == HDA_DEV_LEGACY) 8468c2ecf20Sopenharmony_ci put_device(hda_codec_dev(codec)); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci return 0; 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic void snd_hda_codec_dev_release(struct device *dev) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct hda_codec *codec = dev_to_hda_codec(dev); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci free_init_pincfgs(codec); 8568c2ecf20Sopenharmony_ci snd_hdac_device_exit(&codec->core); 8578c2ecf20Sopenharmony_ci snd_hda_sysfs_clear(codec); 8588c2ecf20Sopenharmony_ci kfree(codec->modelname); 8598c2ecf20Sopenharmony_ci kfree(codec->wcaps); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* 8628c2ecf20Sopenharmony_ci * In the case of ASoC HD-audio, hda_codec is device managed. 8638c2ecf20Sopenharmony_ci * It will be freed when the ASoC device is removed. 8648c2ecf20Sopenharmony_ci */ 8658c2ecf20Sopenharmony_ci if (codec->core.type == HDA_DEV_LEGACY) 8668c2ecf20Sopenharmony_ci kfree(codec); 8678c2ecf20Sopenharmony_ci} 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci#define DEV_NAME_LEN 31 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card, 8728c2ecf20Sopenharmony_ci unsigned int codec_addr, struct hda_codec **codecp) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci char name[DEV_NAME_LEN]; 8758c2ecf20Sopenharmony_ci struct hda_codec *codec; 8768c2ecf20Sopenharmony_ci int err; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci dev_dbg(card->dev, "%s: entry\n", __func__); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (snd_BUG_ON(!bus)) 8818c2ecf20Sopenharmony_ci return -EINVAL; 8828c2ecf20Sopenharmony_ci if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) 8838c2ecf20Sopenharmony_ci return -EINVAL; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci codec = kzalloc(sizeof(*codec), GFP_KERNEL); 8868c2ecf20Sopenharmony_ci if (!codec) 8878c2ecf20Sopenharmony_ci return -ENOMEM; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci sprintf(name, "hdaudioC%dD%d", card->number, codec_addr); 8908c2ecf20Sopenharmony_ci err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr); 8918c2ecf20Sopenharmony_ci if (err < 0) { 8928c2ecf20Sopenharmony_ci kfree(codec); 8938c2ecf20Sopenharmony_ci return err; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci codec->core.type = HDA_DEV_LEGACY; 8978c2ecf20Sopenharmony_ci *codecp = codec; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return err; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci/** 9038c2ecf20Sopenharmony_ci * snd_hda_codec_new - create a HDA codec 9048c2ecf20Sopenharmony_ci * @bus: the bus to assign 9058c2ecf20Sopenharmony_ci * @card: card for this codec 9068c2ecf20Sopenharmony_ci * @codec_addr: the codec address 9078c2ecf20Sopenharmony_ci * @codecp: the pointer to store the generated codec 9088c2ecf20Sopenharmony_ci * 9098c2ecf20Sopenharmony_ci * Returns 0 if successful, or a negative error code. 9108c2ecf20Sopenharmony_ci */ 9118c2ecf20Sopenharmony_ciint snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, 9128c2ecf20Sopenharmony_ci unsigned int codec_addr, struct hda_codec **codecp) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci int ret; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci ret = snd_hda_codec_device_init(bus, card, codec_addr, codecp); 9178c2ecf20Sopenharmony_ci if (ret < 0) 9188c2ecf20Sopenharmony_ci return ret; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci return snd_hda_codec_device_new(bus, card, codec_addr, *codecp); 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_new); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ciint snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, 9258c2ecf20Sopenharmony_ci unsigned int codec_addr, struct hda_codec *codec) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci char component[31]; 9288c2ecf20Sopenharmony_ci hda_nid_t fg; 9298c2ecf20Sopenharmony_ci int err; 9308c2ecf20Sopenharmony_ci static const struct snd_device_ops dev_ops = { 9318c2ecf20Sopenharmony_ci .dev_register = snd_hda_codec_dev_register, 9328c2ecf20Sopenharmony_ci .dev_free = snd_hda_codec_dev_free, 9338c2ecf20Sopenharmony_ci }; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci dev_dbg(card->dev, "%s: entry\n", __func__); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (snd_BUG_ON(!bus)) 9388c2ecf20Sopenharmony_ci return -EINVAL; 9398c2ecf20Sopenharmony_ci if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) 9408c2ecf20Sopenharmony_ci return -EINVAL; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci codec->core.dev.release = snd_hda_codec_dev_release; 9438c2ecf20Sopenharmony_ci codec->core.exec_verb = codec_exec_verb; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci codec->bus = bus; 9468c2ecf20Sopenharmony_ci codec->card = card; 9478c2ecf20Sopenharmony_ci codec->addr = codec_addr; 9488c2ecf20Sopenharmony_ci mutex_init(&codec->spdif_mutex); 9498c2ecf20Sopenharmony_ci mutex_init(&codec->control_mutex); 9508c2ecf20Sopenharmony_ci snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); 9518c2ecf20Sopenharmony_ci snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); 9528c2ecf20Sopenharmony_ci snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); 9538c2ecf20Sopenharmony_ci snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); 9548c2ecf20Sopenharmony_ci snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); 9558c2ecf20Sopenharmony_ci snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); 9568c2ecf20Sopenharmony_ci snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); 9578c2ecf20Sopenharmony_ci snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); 9588c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&codec->conn_list); 9598c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&codec->pcm_list_head); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); 9628c2ecf20Sopenharmony_ci codec->depop_delay = -1; 9638c2ecf20Sopenharmony_ci codec->fixup_id = HDA_FIXUP_ID_NOT_SET; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 9668c2ecf20Sopenharmony_ci codec->power_jiffies = jiffies; 9678c2ecf20Sopenharmony_ci#endif 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci snd_hda_sysfs_init(codec); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (codec->bus->modelname) { 9728c2ecf20Sopenharmony_ci codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); 9738c2ecf20Sopenharmony_ci if (!codec->modelname) { 9748c2ecf20Sopenharmony_ci err = -ENOMEM; 9758c2ecf20Sopenharmony_ci goto error; 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci fg = codec->core.afg ? codec->core.afg : codec->core.mfg; 9808c2ecf20Sopenharmony_ci err = read_widget_caps(codec, fg); 9818c2ecf20Sopenharmony_ci if (err < 0) 9828c2ecf20Sopenharmony_ci goto error; 9838c2ecf20Sopenharmony_ci err = read_pin_defaults(codec); 9848c2ecf20Sopenharmony_ci if (err < 0) 9858c2ecf20Sopenharmony_ci goto error; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci /* power-up all before initialization */ 9888c2ecf20Sopenharmony_ci hda_set_power_state(codec, AC_PWRST_D0); 9898c2ecf20Sopenharmony_ci codec->core.dev.power.power_state = PMSG_ON; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci snd_hda_codec_proc_new(codec); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci snd_hda_create_hwdep(codec); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci sprintf(component, "HDA:%08x,%08x,%08x", codec->core.vendor_id, 9968c2ecf20Sopenharmony_ci codec->core.subsystem_id, codec->core.revision_id); 9978c2ecf20Sopenharmony_ci snd_component_add(card, component); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops); 10008c2ecf20Sopenharmony_ci if (err < 0) 10018c2ecf20Sopenharmony_ci goto error; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* PM runtime needs to be enabled later after binding codec */ 10048c2ecf20Sopenharmony_ci pm_runtime_forbid(&codec->core.dev); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci return 0; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci error: 10098c2ecf20Sopenharmony_ci put_device(hda_codec_dev(codec)); 10108c2ecf20Sopenharmony_ci return err; 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_device_new); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci/** 10158c2ecf20Sopenharmony_ci * snd_hda_codec_update_widgets - Refresh widget caps and pin defaults 10168c2ecf20Sopenharmony_ci * @codec: the HDA codec 10178c2ecf20Sopenharmony_ci * 10188c2ecf20Sopenharmony_ci * Forcibly refresh the all widget caps and the init pin configurations of 10198c2ecf20Sopenharmony_ci * the given codec. 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_ciint snd_hda_codec_update_widgets(struct hda_codec *codec) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci hda_nid_t fg; 10248c2ecf20Sopenharmony_ci int err; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci err = snd_hdac_refresh_widgets(&codec->core); 10278c2ecf20Sopenharmony_ci if (err < 0) 10288c2ecf20Sopenharmony_ci return err; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* Assume the function group node does not change, 10318c2ecf20Sopenharmony_ci * only the widget nodes may change. 10328c2ecf20Sopenharmony_ci */ 10338c2ecf20Sopenharmony_ci kfree(codec->wcaps); 10348c2ecf20Sopenharmony_ci fg = codec->core.afg ? codec->core.afg : codec->core.mfg; 10358c2ecf20Sopenharmony_ci err = read_widget_caps(codec, fg); 10368c2ecf20Sopenharmony_ci if (err < 0) 10378c2ecf20Sopenharmony_ci return err; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci snd_array_free(&codec->init_pins); 10408c2ecf20Sopenharmony_ci err = read_pin_defaults(codec); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci return err; 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_update_widgets); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci/* update the stream-id if changed */ 10478c2ecf20Sopenharmony_cistatic void update_pcm_stream_id(struct hda_codec *codec, 10488c2ecf20Sopenharmony_ci struct hda_cvt_setup *p, hda_nid_t nid, 10498c2ecf20Sopenharmony_ci u32 stream_tag, int channel_id) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci unsigned int oldval, newval; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci if (p->stream_tag != stream_tag || p->channel_id != channel_id) { 10548c2ecf20Sopenharmony_ci oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); 10558c2ecf20Sopenharmony_ci newval = (stream_tag << 4) | channel_id; 10568c2ecf20Sopenharmony_ci if (oldval != newval) 10578c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, nid, 0, 10588c2ecf20Sopenharmony_ci AC_VERB_SET_CHANNEL_STREAMID, 10598c2ecf20Sopenharmony_ci newval); 10608c2ecf20Sopenharmony_ci p->stream_tag = stream_tag; 10618c2ecf20Sopenharmony_ci p->channel_id = channel_id; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci/* update the format-id if changed */ 10668c2ecf20Sopenharmony_cistatic void update_pcm_format(struct hda_codec *codec, struct hda_cvt_setup *p, 10678c2ecf20Sopenharmony_ci hda_nid_t nid, int format) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci unsigned int oldval; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci if (p->format_id != format) { 10728c2ecf20Sopenharmony_ci oldval = snd_hda_codec_read(codec, nid, 0, 10738c2ecf20Sopenharmony_ci AC_VERB_GET_STREAM_FORMAT, 0); 10748c2ecf20Sopenharmony_ci if (oldval != format) { 10758c2ecf20Sopenharmony_ci msleep(1); 10768c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, nid, 0, 10778c2ecf20Sopenharmony_ci AC_VERB_SET_STREAM_FORMAT, 10788c2ecf20Sopenharmony_ci format); 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci p->format_id = format; 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci/** 10858c2ecf20Sopenharmony_ci * snd_hda_codec_setup_stream - set up the codec for streaming 10868c2ecf20Sopenharmony_ci * @codec: the CODEC to set up 10878c2ecf20Sopenharmony_ci * @nid: the NID to set up 10888c2ecf20Sopenharmony_ci * @stream_tag: stream tag to pass, it's between 0x1 and 0xf. 10898c2ecf20Sopenharmony_ci * @channel_id: channel id to pass, zero based. 10908c2ecf20Sopenharmony_ci * @format: stream format. 10918c2ecf20Sopenharmony_ci */ 10928c2ecf20Sopenharmony_civoid snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, 10938c2ecf20Sopenharmony_ci u32 stream_tag, 10948c2ecf20Sopenharmony_ci int channel_id, int format) 10958c2ecf20Sopenharmony_ci{ 10968c2ecf20Sopenharmony_ci struct hda_codec *c; 10978c2ecf20Sopenharmony_ci struct hda_cvt_setup *p; 10988c2ecf20Sopenharmony_ci int type; 10998c2ecf20Sopenharmony_ci int i; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (!nid) 11028c2ecf20Sopenharmony_ci return; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci codec_dbg(codec, 11058c2ecf20Sopenharmony_ci "hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", 11068c2ecf20Sopenharmony_ci nid, stream_tag, channel_id, format); 11078c2ecf20Sopenharmony_ci p = get_hda_cvt_setup(codec, nid); 11088c2ecf20Sopenharmony_ci if (!p) 11098c2ecf20Sopenharmony_ci return; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (codec->patch_ops.stream_pm) 11128c2ecf20Sopenharmony_ci codec->patch_ops.stream_pm(codec, nid, true); 11138c2ecf20Sopenharmony_ci if (codec->pcm_format_first) 11148c2ecf20Sopenharmony_ci update_pcm_format(codec, p, nid, format); 11158c2ecf20Sopenharmony_ci update_pcm_stream_id(codec, p, nid, stream_tag, channel_id); 11168c2ecf20Sopenharmony_ci if (!codec->pcm_format_first) 11178c2ecf20Sopenharmony_ci update_pcm_format(codec, p, nid, format); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci p->active = 1; 11208c2ecf20Sopenharmony_ci p->dirty = 0; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci /* make other inactive cvts with the same stream-tag dirty */ 11238c2ecf20Sopenharmony_ci type = get_wcaps_type(get_wcaps(codec, nid)); 11248c2ecf20Sopenharmony_ci list_for_each_codec(c, codec->bus) { 11258c2ecf20Sopenharmony_ci snd_array_for_each(&c->cvt_setups, i, p) { 11268c2ecf20Sopenharmony_ci if (!p->active && p->stream_tag == stream_tag && 11278c2ecf20Sopenharmony_ci get_wcaps_type(get_wcaps(c, p->nid)) == type) 11288c2ecf20Sopenharmony_ci p->dirty = 1; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_setup_stream); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_cistatic void really_cleanup_stream(struct hda_codec *codec, 11358c2ecf20Sopenharmony_ci struct hda_cvt_setup *q); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci/** 11388c2ecf20Sopenharmony_ci * __snd_hda_codec_cleanup_stream - clean up the codec for closing 11398c2ecf20Sopenharmony_ci * @codec: the CODEC to clean up 11408c2ecf20Sopenharmony_ci * @nid: the NID to clean up 11418c2ecf20Sopenharmony_ci * @do_now: really clean up the stream instead of clearing the active flag 11428c2ecf20Sopenharmony_ci */ 11438c2ecf20Sopenharmony_civoid __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, 11448c2ecf20Sopenharmony_ci int do_now) 11458c2ecf20Sopenharmony_ci{ 11468c2ecf20Sopenharmony_ci struct hda_cvt_setup *p; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (!nid) 11498c2ecf20Sopenharmony_ci return; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci if (codec->no_sticky_stream) 11528c2ecf20Sopenharmony_ci do_now = 1; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci codec_dbg(codec, "hda_codec_cleanup_stream: NID=0x%x\n", nid); 11558c2ecf20Sopenharmony_ci p = get_hda_cvt_setup(codec, nid); 11568c2ecf20Sopenharmony_ci if (p) { 11578c2ecf20Sopenharmony_ci /* here we just clear the active flag when do_now isn't set; 11588c2ecf20Sopenharmony_ci * actual clean-ups will be done later in 11598c2ecf20Sopenharmony_ci * purify_inactive_streams() called from snd_hda_codec_prpapre() 11608c2ecf20Sopenharmony_ci */ 11618c2ecf20Sopenharmony_ci if (do_now) 11628c2ecf20Sopenharmony_ci really_cleanup_stream(codec, p); 11638c2ecf20Sopenharmony_ci else 11648c2ecf20Sopenharmony_ci p->active = 0; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__snd_hda_codec_cleanup_stream); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_cistatic void really_cleanup_stream(struct hda_codec *codec, 11708c2ecf20Sopenharmony_ci struct hda_cvt_setup *q) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci hda_nid_t nid = q->nid; 11738c2ecf20Sopenharmony_ci if (q->stream_tag || q->channel_id) 11748c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); 11758c2ecf20Sopenharmony_ci if (q->format_id) 11768c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0 11778c2ecf20Sopenharmony_ci); 11788c2ecf20Sopenharmony_ci memset(q, 0, sizeof(*q)); 11798c2ecf20Sopenharmony_ci q->nid = nid; 11808c2ecf20Sopenharmony_ci if (codec->patch_ops.stream_pm) 11818c2ecf20Sopenharmony_ci codec->patch_ops.stream_pm(codec, nid, false); 11828c2ecf20Sopenharmony_ci} 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci/* clean up the all conflicting obsolete streams */ 11858c2ecf20Sopenharmony_cistatic void purify_inactive_streams(struct hda_codec *codec) 11868c2ecf20Sopenharmony_ci{ 11878c2ecf20Sopenharmony_ci struct hda_codec *c; 11888c2ecf20Sopenharmony_ci struct hda_cvt_setup *p; 11898c2ecf20Sopenharmony_ci int i; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci list_for_each_codec(c, codec->bus) { 11928c2ecf20Sopenharmony_ci snd_array_for_each(&c->cvt_setups, i, p) { 11938c2ecf20Sopenharmony_ci if (p->dirty) 11948c2ecf20Sopenharmony_ci really_cleanup_stream(c, p); 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 12008c2ecf20Sopenharmony_ci/* clean up all streams; called from suspend */ 12018c2ecf20Sopenharmony_cistatic void hda_cleanup_all_streams(struct hda_codec *codec) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci struct hda_cvt_setup *p; 12048c2ecf20Sopenharmony_ci int i; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci snd_array_for_each(&codec->cvt_setups, i, p) { 12078c2ecf20Sopenharmony_ci if (p->stream_tag) 12088c2ecf20Sopenharmony_ci really_cleanup_stream(codec, p); 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci#endif 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci/* 12148c2ecf20Sopenharmony_ci * amp access functions 12158c2ecf20Sopenharmony_ci */ 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci/** 12188c2ecf20Sopenharmony_ci * query_amp_caps - query AMP capabilities 12198c2ecf20Sopenharmony_ci * @codec: the HD-auio codec 12208c2ecf20Sopenharmony_ci * @nid: the NID to query 12218c2ecf20Sopenharmony_ci * @direction: either #HDA_INPUT or #HDA_OUTPUT 12228c2ecf20Sopenharmony_ci * 12238c2ecf20Sopenharmony_ci * Query AMP capabilities for the given widget and direction. 12248c2ecf20Sopenharmony_ci * Returns the obtained capability bits. 12258c2ecf20Sopenharmony_ci * 12268c2ecf20Sopenharmony_ci * When cap bits have been already read, this doesn't read again but 12278c2ecf20Sopenharmony_ci * returns the cached value. 12288c2ecf20Sopenharmony_ci */ 12298c2ecf20Sopenharmony_ciu32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) 12308c2ecf20Sopenharmony_ci{ 12318c2ecf20Sopenharmony_ci if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) 12328c2ecf20Sopenharmony_ci nid = codec->core.afg; 12338c2ecf20Sopenharmony_ci return snd_hda_param_read(codec, nid, 12348c2ecf20Sopenharmony_ci direction == HDA_OUTPUT ? 12358c2ecf20Sopenharmony_ci AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(query_amp_caps); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci/** 12408c2ecf20Sopenharmony_ci * snd_hda_check_amp_caps - query AMP capabilities 12418c2ecf20Sopenharmony_ci * @codec: the HD-audio codec 12428c2ecf20Sopenharmony_ci * @nid: the NID to query 12438c2ecf20Sopenharmony_ci * @dir: either #HDA_INPUT or #HDA_OUTPUT 12448c2ecf20Sopenharmony_ci * @bits: bit mask to check the result 12458c2ecf20Sopenharmony_ci * 12468c2ecf20Sopenharmony_ci * Check whether the widget has the given amp capability for the direction. 12478c2ecf20Sopenharmony_ci */ 12488c2ecf20Sopenharmony_cibool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid, 12498c2ecf20Sopenharmony_ci int dir, unsigned int bits) 12508c2ecf20Sopenharmony_ci{ 12518c2ecf20Sopenharmony_ci if (!nid) 12528c2ecf20Sopenharmony_ci return false; 12538c2ecf20Sopenharmony_ci if (get_wcaps(codec, nid) & (1 << (dir + 1))) 12548c2ecf20Sopenharmony_ci if (query_amp_caps(codec, nid, dir) & bits) 12558c2ecf20Sopenharmony_ci return true; 12568c2ecf20Sopenharmony_ci return false; 12578c2ecf20Sopenharmony_ci} 12588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_check_amp_caps); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci/** 12618c2ecf20Sopenharmony_ci * snd_hda_override_amp_caps - Override the AMP capabilities 12628c2ecf20Sopenharmony_ci * @codec: the CODEC to clean up 12638c2ecf20Sopenharmony_ci * @nid: the NID to clean up 12648c2ecf20Sopenharmony_ci * @dir: either #HDA_INPUT or #HDA_OUTPUT 12658c2ecf20Sopenharmony_ci * @caps: the capability bits to set 12668c2ecf20Sopenharmony_ci * 12678c2ecf20Sopenharmony_ci * Override the cached AMP caps bits value by the given one. 12688c2ecf20Sopenharmony_ci * This function is useful if the driver needs to adjust the AMP ranges, 12698c2ecf20Sopenharmony_ci * e.g. limit to 0dB, etc. 12708c2ecf20Sopenharmony_ci * 12718c2ecf20Sopenharmony_ci * Returns zero if successful or a negative error code. 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_ciint snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, 12748c2ecf20Sopenharmony_ci unsigned int caps) 12758c2ecf20Sopenharmony_ci{ 12768c2ecf20Sopenharmony_ci unsigned int parm; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci snd_hda_override_wcaps(codec, nid, 12798c2ecf20Sopenharmony_ci get_wcaps(codec, nid) | AC_WCAP_AMP_OVRD); 12808c2ecf20Sopenharmony_ci parm = dir == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP; 12818c2ecf20Sopenharmony_ci return snd_hdac_override_parm(&codec->core, nid, parm, caps); 12828c2ecf20Sopenharmony_ci} 12838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_cistatic unsigned int encode_amp(struct hda_codec *codec, hda_nid_t nid, 12868c2ecf20Sopenharmony_ci int ch, int dir, int idx) 12878c2ecf20Sopenharmony_ci{ 12888c2ecf20Sopenharmony_ci unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci /* enable fake mute if no h/w mute but min=mute */ 12918c2ecf20Sopenharmony_ci if ((query_amp_caps(codec, nid, dir) & 12928c2ecf20Sopenharmony_ci (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE) 12938c2ecf20Sopenharmony_ci cmd |= AC_AMP_FAKE_MUTE; 12948c2ecf20Sopenharmony_ci return cmd; 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci/** 12988c2ecf20Sopenharmony_ci * snd_hda_codec_amp_update - update the AMP mono value 12998c2ecf20Sopenharmony_ci * @codec: HD-audio codec 13008c2ecf20Sopenharmony_ci * @nid: NID to read the AMP value 13018c2ecf20Sopenharmony_ci * @ch: channel to update (0 or 1) 13028c2ecf20Sopenharmony_ci * @dir: #HDA_INPUT or #HDA_OUTPUT 13038c2ecf20Sopenharmony_ci * @idx: the index value (only for input direction) 13048c2ecf20Sopenharmony_ci * @mask: bit mask to set 13058c2ecf20Sopenharmony_ci * @val: the bits value to set 13068c2ecf20Sopenharmony_ci * 13078c2ecf20Sopenharmony_ci * Update the AMP values for the given channel, direction and index. 13088c2ecf20Sopenharmony_ci */ 13098c2ecf20Sopenharmony_ciint snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, 13108c2ecf20Sopenharmony_ci int ch, int dir, int idx, int mask, int val) 13118c2ecf20Sopenharmony_ci{ 13128c2ecf20Sopenharmony_ci unsigned int cmd = encode_amp(codec, nid, ch, dir, idx); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci return snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val); 13158c2ecf20Sopenharmony_ci} 13168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_amp_update); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci/** 13198c2ecf20Sopenharmony_ci * snd_hda_codec_amp_stereo - update the AMP stereo values 13208c2ecf20Sopenharmony_ci * @codec: HD-audio codec 13218c2ecf20Sopenharmony_ci * @nid: NID to read the AMP value 13228c2ecf20Sopenharmony_ci * @direction: #HDA_INPUT or #HDA_OUTPUT 13238c2ecf20Sopenharmony_ci * @idx: the index value (only for input direction) 13248c2ecf20Sopenharmony_ci * @mask: bit mask to set 13258c2ecf20Sopenharmony_ci * @val: the bits value to set 13268c2ecf20Sopenharmony_ci * 13278c2ecf20Sopenharmony_ci * Update the AMP values like snd_hda_codec_amp_update(), but for a 13288c2ecf20Sopenharmony_ci * stereo widget with the same mask and value. 13298c2ecf20Sopenharmony_ci */ 13308c2ecf20Sopenharmony_ciint snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, 13318c2ecf20Sopenharmony_ci int direction, int idx, int mask, int val) 13328c2ecf20Sopenharmony_ci{ 13338c2ecf20Sopenharmony_ci int ch, ret = 0; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci if (snd_BUG_ON(mask & ~0xff)) 13368c2ecf20Sopenharmony_ci mask &= 0xff; 13378c2ecf20Sopenharmony_ci for (ch = 0; ch < 2; ch++) 13388c2ecf20Sopenharmony_ci ret |= snd_hda_codec_amp_update(codec, nid, ch, direction, 13398c2ecf20Sopenharmony_ci idx, mask, val); 13408c2ecf20Sopenharmony_ci return ret; 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci/** 13458c2ecf20Sopenharmony_ci * snd_hda_codec_amp_init - initialize the AMP value 13468c2ecf20Sopenharmony_ci * @codec: the HDA codec 13478c2ecf20Sopenharmony_ci * @nid: NID to read the AMP value 13488c2ecf20Sopenharmony_ci * @ch: channel (left=0 or right=1) 13498c2ecf20Sopenharmony_ci * @dir: #HDA_INPUT or #HDA_OUTPUT 13508c2ecf20Sopenharmony_ci * @idx: the index value (only for input direction) 13518c2ecf20Sopenharmony_ci * @mask: bit mask to set 13528c2ecf20Sopenharmony_ci * @val: the bits value to set 13538c2ecf20Sopenharmony_ci * 13548c2ecf20Sopenharmony_ci * Works like snd_hda_codec_amp_update() but it writes the value only at 13558c2ecf20Sopenharmony_ci * the first access. If the amp was already initialized / updated beforehand, 13568c2ecf20Sopenharmony_ci * this does nothing. 13578c2ecf20Sopenharmony_ci */ 13588c2ecf20Sopenharmony_ciint snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, 13598c2ecf20Sopenharmony_ci int dir, int idx, int mask, int val) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci unsigned int cmd = encode_amp(codec, nid, ch, dir, idx); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci if (!codec->core.regmap) 13648c2ecf20Sopenharmony_ci return -EINVAL; 13658c2ecf20Sopenharmony_ci return snd_hdac_regmap_update_raw_once(&codec->core, cmd, mask, val); 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_amp_init); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci/** 13708c2ecf20Sopenharmony_ci * snd_hda_codec_amp_init_stereo - initialize the stereo AMP value 13718c2ecf20Sopenharmony_ci * @codec: the HDA codec 13728c2ecf20Sopenharmony_ci * @nid: NID to read the AMP value 13738c2ecf20Sopenharmony_ci * @dir: #HDA_INPUT or #HDA_OUTPUT 13748c2ecf20Sopenharmony_ci * @idx: the index value (only for input direction) 13758c2ecf20Sopenharmony_ci * @mask: bit mask to set 13768c2ecf20Sopenharmony_ci * @val: the bits value to set 13778c2ecf20Sopenharmony_ci * 13788c2ecf20Sopenharmony_ci * Call snd_hda_codec_amp_init() for both stereo channels. 13798c2ecf20Sopenharmony_ci */ 13808c2ecf20Sopenharmony_ciint snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, 13818c2ecf20Sopenharmony_ci int dir, int idx, int mask, int val) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci int ch, ret = 0; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci if (snd_BUG_ON(mask & ~0xff)) 13868c2ecf20Sopenharmony_ci mask &= 0xff; 13878c2ecf20Sopenharmony_ci for (ch = 0; ch < 2; ch++) 13888c2ecf20Sopenharmony_ci ret |= snd_hda_codec_amp_init(codec, nid, ch, dir, 13898c2ecf20Sopenharmony_ci idx, mask, val); 13908c2ecf20Sopenharmony_ci return ret; 13918c2ecf20Sopenharmony_ci} 13928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_cistatic u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, 13958c2ecf20Sopenharmony_ci unsigned int ofs) 13968c2ecf20Sopenharmony_ci{ 13978c2ecf20Sopenharmony_ci u32 caps = query_amp_caps(codec, nid, dir); 13988c2ecf20Sopenharmony_ci /* get num steps */ 13998c2ecf20Sopenharmony_ci caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; 14008c2ecf20Sopenharmony_ci if (ofs < caps) 14018c2ecf20Sopenharmony_ci caps -= ofs; 14028c2ecf20Sopenharmony_ci return caps; 14038c2ecf20Sopenharmony_ci} 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci/** 14068c2ecf20Sopenharmony_ci * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer 14078c2ecf20Sopenharmony_ci * @kcontrol: referred ctl element 14088c2ecf20Sopenharmony_ci * @uinfo: pointer to get/store the data 14098c2ecf20Sopenharmony_ci * 14108c2ecf20Sopenharmony_ci * The control element is supposed to have the private_value field 14118c2ecf20Sopenharmony_ci * set up via HDA_COMPOSE_AMP_VAL*() or related macros. 14128c2ecf20Sopenharmony_ci */ 14138c2ecf20Sopenharmony_ciint snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, 14148c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 14158c2ecf20Sopenharmony_ci{ 14168c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 14178c2ecf20Sopenharmony_ci u16 nid = get_amp_nid(kcontrol); 14188c2ecf20Sopenharmony_ci u8 chs = get_amp_channels(kcontrol); 14198c2ecf20Sopenharmony_ci int dir = get_amp_direction(kcontrol); 14208c2ecf20Sopenharmony_ci unsigned int ofs = get_amp_offset(kcontrol); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 14238c2ecf20Sopenharmony_ci uinfo->count = chs == 3 ? 2 : 1; 14248c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 14258c2ecf20Sopenharmony_ci uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs); 14268c2ecf20Sopenharmony_ci if (!uinfo->value.integer.max) { 14278c2ecf20Sopenharmony_ci codec_warn(codec, 14288c2ecf20Sopenharmony_ci "num_steps = 0 for NID=0x%x (ctl = %s)\n", 14298c2ecf20Sopenharmony_ci nid, kcontrol->id.name); 14308c2ecf20Sopenharmony_ci return -EINVAL; 14318c2ecf20Sopenharmony_ci } 14328c2ecf20Sopenharmony_ci return 0; 14338c2ecf20Sopenharmony_ci} 14348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_info); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_cistatic inline unsigned int 14388c2ecf20Sopenharmony_ciread_amp_value(struct hda_codec *codec, hda_nid_t nid, 14398c2ecf20Sopenharmony_ci int ch, int dir, int idx, unsigned int ofs) 14408c2ecf20Sopenharmony_ci{ 14418c2ecf20Sopenharmony_ci unsigned int val; 14428c2ecf20Sopenharmony_ci val = snd_hda_codec_amp_read(codec, nid, ch, dir, idx); 14438c2ecf20Sopenharmony_ci val &= HDA_AMP_VOLMASK; 14448c2ecf20Sopenharmony_ci if (val >= ofs) 14458c2ecf20Sopenharmony_ci val -= ofs; 14468c2ecf20Sopenharmony_ci else 14478c2ecf20Sopenharmony_ci val = 0; 14488c2ecf20Sopenharmony_ci return val; 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic inline int 14528c2ecf20Sopenharmony_ciupdate_amp_value(struct hda_codec *codec, hda_nid_t nid, 14538c2ecf20Sopenharmony_ci int ch, int dir, int idx, unsigned int ofs, 14548c2ecf20Sopenharmony_ci unsigned int val) 14558c2ecf20Sopenharmony_ci{ 14568c2ecf20Sopenharmony_ci unsigned int maxval; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci if (val > 0) 14598c2ecf20Sopenharmony_ci val += ofs; 14608c2ecf20Sopenharmony_ci /* ofs = 0: raw max value */ 14618c2ecf20Sopenharmony_ci maxval = get_amp_max_value(codec, nid, dir, 0); 14628c2ecf20Sopenharmony_ci if (val > maxval) 14638c2ecf20Sopenharmony_ci val = maxval; 14648c2ecf20Sopenharmony_ci return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, 14658c2ecf20Sopenharmony_ci HDA_AMP_VOLMASK, val); 14668c2ecf20Sopenharmony_ci} 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci/** 14698c2ecf20Sopenharmony_ci * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume 14708c2ecf20Sopenharmony_ci * @kcontrol: ctl element 14718c2ecf20Sopenharmony_ci * @ucontrol: pointer to get/store the data 14728c2ecf20Sopenharmony_ci * 14738c2ecf20Sopenharmony_ci * The control element is supposed to have the private_value field 14748c2ecf20Sopenharmony_ci * set up via HDA_COMPOSE_AMP_VAL*() or related macros. 14758c2ecf20Sopenharmony_ci */ 14768c2ecf20Sopenharmony_ciint snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, 14778c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14788c2ecf20Sopenharmony_ci{ 14798c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 14808c2ecf20Sopenharmony_ci hda_nid_t nid = get_amp_nid(kcontrol); 14818c2ecf20Sopenharmony_ci int chs = get_amp_channels(kcontrol); 14828c2ecf20Sopenharmony_ci int dir = get_amp_direction(kcontrol); 14838c2ecf20Sopenharmony_ci int idx = get_amp_index(kcontrol); 14848c2ecf20Sopenharmony_ci unsigned int ofs = get_amp_offset(kcontrol); 14858c2ecf20Sopenharmony_ci long *valp = ucontrol->value.integer.value; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci if (chs & 1) 14888c2ecf20Sopenharmony_ci *valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs); 14898c2ecf20Sopenharmony_ci if (chs & 2) 14908c2ecf20Sopenharmony_ci *valp = read_amp_value(codec, nid, 1, dir, idx, ofs); 14918c2ecf20Sopenharmony_ci return 0; 14928c2ecf20Sopenharmony_ci} 14938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_get); 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci/** 14968c2ecf20Sopenharmony_ci * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume 14978c2ecf20Sopenharmony_ci * @kcontrol: ctl element 14988c2ecf20Sopenharmony_ci * @ucontrol: pointer to get/store the data 14998c2ecf20Sopenharmony_ci * 15008c2ecf20Sopenharmony_ci * The control element is supposed to have the private_value field 15018c2ecf20Sopenharmony_ci * set up via HDA_COMPOSE_AMP_VAL*() or related macros. 15028c2ecf20Sopenharmony_ci */ 15038c2ecf20Sopenharmony_ciint snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, 15048c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 15078c2ecf20Sopenharmony_ci hda_nid_t nid = get_amp_nid(kcontrol); 15088c2ecf20Sopenharmony_ci int chs = get_amp_channels(kcontrol); 15098c2ecf20Sopenharmony_ci int dir = get_amp_direction(kcontrol); 15108c2ecf20Sopenharmony_ci int idx = get_amp_index(kcontrol); 15118c2ecf20Sopenharmony_ci unsigned int ofs = get_amp_offset(kcontrol); 15128c2ecf20Sopenharmony_ci long *valp = ucontrol->value.integer.value; 15138c2ecf20Sopenharmony_ci int change = 0; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (chs & 1) { 15168c2ecf20Sopenharmony_ci change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp); 15178c2ecf20Sopenharmony_ci valp++; 15188c2ecf20Sopenharmony_ci } 15198c2ecf20Sopenharmony_ci if (chs & 2) 15208c2ecf20Sopenharmony_ci change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp); 15218c2ecf20Sopenharmony_ci return change; 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put); 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci/* inquiry the amp caps and convert to TLV */ 15268c2ecf20Sopenharmony_cistatic void get_ctl_amp_tlv(struct snd_kcontrol *kcontrol, unsigned int *tlv) 15278c2ecf20Sopenharmony_ci{ 15288c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 15298c2ecf20Sopenharmony_ci hda_nid_t nid = get_amp_nid(kcontrol); 15308c2ecf20Sopenharmony_ci int dir = get_amp_direction(kcontrol); 15318c2ecf20Sopenharmony_ci unsigned int ofs = get_amp_offset(kcontrol); 15328c2ecf20Sopenharmony_ci bool min_mute = get_amp_min_mute(kcontrol); 15338c2ecf20Sopenharmony_ci u32 caps, val1, val2; 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci caps = query_amp_caps(codec, nid, dir); 15368c2ecf20Sopenharmony_ci val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; 15378c2ecf20Sopenharmony_ci val2 = (val2 + 1) * 25; 15388c2ecf20Sopenharmony_ci val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT); 15398c2ecf20Sopenharmony_ci val1 += ofs; 15408c2ecf20Sopenharmony_ci val1 = ((int)val1) * ((int)val2); 15418c2ecf20Sopenharmony_ci if (min_mute || (caps & AC_AMPCAP_MIN_MUTE)) 15428c2ecf20Sopenharmony_ci val2 |= TLV_DB_SCALE_MUTE; 15438c2ecf20Sopenharmony_ci tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE; 15448c2ecf20Sopenharmony_ci tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int); 15458c2ecf20Sopenharmony_ci tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = val1; 15468c2ecf20Sopenharmony_ci tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = val2; 15478c2ecf20Sopenharmony_ci} 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci/** 15508c2ecf20Sopenharmony_ci * snd_hda_mixer_amp_tlv - TLV callback for a standard AMP mixer volume 15518c2ecf20Sopenharmony_ci * @kcontrol: ctl element 15528c2ecf20Sopenharmony_ci * @op_flag: operation flag 15538c2ecf20Sopenharmony_ci * @size: byte size of input TLV 15548c2ecf20Sopenharmony_ci * @_tlv: TLV data 15558c2ecf20Sopenharmony_ci * 15568c2ecf20Sopenharmony_ci * The control element is supposed to have the private_value field 15578c2ecf20Sopenharmony_ci * set up via HDA_COMPOSE_AMP_VAL*() or related macros. 15588c2ecf20Sopenharmony_ci */ 15598c2ecf20Sopenharmony_ciint snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, 15608c2ecf20Sopenharmony_ci unsigned int size, unsigned int __user *_tlv) 15618c2ecf20Sopenharmony_ci{ 15628c2ecf20Sopenharmony_ci unsigned int tlv[4]; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci if (size < 4 * sizeof(unsigned int)) 15658c2ecf20Sopenharmony_ci return -ENOMEM; 15668c2ecf20Sopenharmony_ci get_ctl_amp_tlv(kcontrol, tlv); 15678c2ecf20Sopenharmony_ci if (copy_to_user(_tlv, tlv, sizeof(tlv))) 15688c2ecf20Sopenharmony_ci return -EFAULT; 15698c2ecf20Sopenharmony_ci return 0; 15708c2ecf20Sopenharmony_ci} 15718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_mixer_amp_tlv); 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci/** 15748c2ecf20Sopenharmony_ci * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control 15758c2ecf20Sopenharmony_ci * @codec: HD-audio codec 15768c2ecf20Sopenharmony_ci * @nid: NID of a reference widget 15778c2ecf20Sopenharmony_ci * @dir: #HDA_INPUT or #HDA_OUTPUT 15788c2ecf20Sopenharmony_ci * @tlv: TLV data to be stored, at least 4 elements 15798c2ecf20Sopenharmony_ci * 15808c2ecf20Sopenharmony_ci * Set (static) TLV data for a virtual master volume using the AMP caps 15818c2ecf20Sopenharmony_ci * obtained from the reference NID. 15828c2ecf20Sopenharmony_ci * The volume range is recalculated as if the max volume is 0dB. 15838c2ecf20Sopenharmony_ci */ 15848c2ecf20Sopenharmony_civoid snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, 15858c2ecf20Sopenharmony_ci unsigned int *tlv) 15868c2ecf20Sopenharmony_ci{ 15878c2ecf20Sopenharmony_ci u32 caps; 15888c2ecf20Sopenharmony_ci int nums, step; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci caps = query_amp_caps(codec, nid, dir); 15918c2ecf20Sopenharmony_ci nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; 15928c2ecf20Sopenharmony_ci step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; 15938c2ecf20Sopenharmony_ci step = (step + 1) * 25; 15948c2ecf20Sopenharmony_ci tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE; 15958c2ecf20Sopenharmony_ci tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int); 15968c2ecf20Sopenharmony_ci tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = -nums * step; 15978c2ecf20Sopenharmony_ci tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = step; 15988c2ecf20Sopenharmony_ci} 15998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_set_vmaster_tlv); 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci/* find a mixer control element with the given name */ 16028c2ecf20Sopenharmony_cistatic struct snd_kcontrol * 16038c2ecf20Sopenharmony_cifind_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx) 16048c2ecf20Sopenharmony_ci{ 16058c2ecf20Sopenharmony_ci struct snd_ctl_elem_id id; 16068c2ecf20Sopenharmony_ci memset(&id, 0, sizeof(id)); 16078c2ecf20Sopenharmony_ci id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 16088c2ecf20Sopenharmony_ci id.device = dev; 16098c2ecf20Sopenharmony_ci id.index = idx; 16108c2ecf20Sopenharmony_ci if (snd_BUG_ON(strlen(name) >= sizeof(id.name))) 16118c2ecf20Sopenharmony_ci return NULL; 16128c2ecf20Sopenharmony_ci strcpy(id.name, name); 16138c2ecf20Sopenharmony_ci return snd_ctl_find_id(codec->card, &id); 16148c2ecf20Sopenharmony_ci} 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci/** 16178c2ecf20Sopenharmony_ci * snd_hda_find_mixer_ctl - Find a mixer control element with the given name 16188c2ecf20Sopenharmony_ci * @codec: HD-audio codec 16198c2ecf20Sopenharmony_ci * @name: ctl id name string 16208c2ecf20Sopenharmony_ci * 16218c2ecf20Sopenharmony_ci * Get the control element with the given id string and IFACE_MIXER. 16228c2ecf20Sopenharmony_ci */ 16238c2ecf20Sopenharmony_cistruct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, 16248c2ecf20Sopenharmony_ci const char *name) 16258c2ecf20Sopenharmony_ci{ 16268c2ecf20Sopenharmony_ci return find_mixer_ctl(codec, name, 0, 0); 16278c2ecf20Sopenharmony_ci} 16288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_find_mixer_ctl); 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_cistatic int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name, 16318c2ecf20Sopenharmony_ci int start_idx) 16328c2ecf20Sopenharmony_ci{ 16338c2ecf20Sopenharmony_ci int i, idx; 16348c2ecf20Sopenharmony_ci /* 16 ctlrs should be large enough */ 16358c2ecf20Sopenharmony_ci for (i = 0, idx = start_idx; i < 16; i++, idx++) { 16368c2ecf20Sopenharmony_ci if (!find_mixer_ctl(codec, name, 0, idx)) 16378c2ecf20Sopenharmony_ci return idx; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci return -EBUSY; 16408c2ecf20Sopenharmony_ci} 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci/** 16438c2ecf20Sopenharmony_ci * snd_hda_ctl_add - Add a control element and assign to the codec 16448c2ecf20Sopenharmony_ci * @codec: HD-audio codec 16458c2ecf20Sopenharmony_ci * @nid: corresponding NID (optional) 16468c2ecf20Sopenharmony_ci * @kctl: the control element to assign 16478c2ecf20Sopenharmony_ci * 16488c2ecf20Sopenharmony_ci * Add the given control element to an array inside the codec instance. 16498c2ecf20Sopenharmony_ci * All control elements belonging to a codec are supposed to be added 16508c2ecf20Sopenharmony_ci * by this function so that a proper clean-up works at the free or 16518c2ecf20Sopenharmony_ci * reconfiguration time. 16528c2ecf20Sopenharmony_ci * 16538c2ecf20Sopenharmony_ci * If non-zero @nid is passed, the NID is assigned to the control element. 16548c2ecf20Sopenharmony_ci * The assignment is shown in the codec proc file. 16558c2ecf20Sopenharmony_ci * 16568c2ecf20Sopenharmony_ci * snd_hda_ctl_add() checks the control subdev id field whether 16578c2ecf20Sopenharmony_ci * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower 16588c2ecf20Sopenharmony_ci * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit 16598c2ecf20Sopenharmony_ci * specifies if kctl->private_value is a HDA amplifier value. 16608c2ecf20Sopenharmony_ci */ 16618c2ecf20Sopenharmony_ciint snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, 16628c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl) 16638c2ecf20Sopenharmony_ci{ 16648c2ecf20Sopenharmony_ci int err; 16658c2ecf20Sopenharmony_ci unsigned short flags = 0; 16668c2ecf20Sopenharmony_ci struct hda_nid_item *item; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) { 16698c2ecf20Sopenharmony_ci flags |= HDA_NID_ITEM_AMP; 16708c2ecf20Sopenharmony_ci if (nid == 0) 16718c2ecf20Sopenharmony_ci nid = get_amp_nid_(kctl->private_value); 16728c2ecf20Sopenharmony_ci } 16738c2ecf20Sopenharmony_ci if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0) 16748c2ecf20Sopenharmony_ci nid = kctl->id.subdevice & 0xffff; 16758c2ecf20Sopenharmony_ci if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG)) 16768c2ecf20Sopenharmony_ci kctl->id.subdevice = 0; 16778c2ecf20Sopenharmony_ci err = snd_ctl_add(codec->card, kctl); 16788c2ecf20Sopenharmony_ci if (err < 0) 16798c2ecf20Sopenharmony_ci return err; 16808c2ecf20Sopenharmony_ci item = snd_array_new(&codec->mixers); 16818c2ecf20Sopenharmony_ci if (!item) 16828c2ecf20Sopenharmony_ci return -ENOMEM; 16838c2ecf20Sopenharmony_ci item->kctl = kctl; 16848c2ecf20Sopenharmony_ci item->nid = nid; 16858c2ecf20Sopenharmony_ci item->flags = flags; 16868c2ecf20Sopenharmony_ci return 0; 16878c2ecf20Sopenharmony_ci} 16888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_ctl_add); 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci/** 16918c2ecf20Sopenharmony_ci * snd_hda_add_nid - Assign a NID to a control element 16928c2ecf20Sopenharmony_ci * @codec: HD-audio codec 16938c2ecf20Sopenharmony_ci * @nid: corresponding NID (optional) 16948c2ecf20Sopenharmony_ci * @kctl: the control element to assign 16958c2ecf20Sopenharmony_ci * @index: index to kctl 16968c2ecf20Sopenharmony_ci * 16978c2ecf20Sopenharmony_ci * Add the given control element to an array inside the codec instance. 16988c2ecf20Sopenharmony_ci * This function is used when #snd_hda_ctl_add cannot be used for 1:1 16998c2ecf20Sopenharmony_ci * NID:KCTL mapping - for example "Capture Source" selector. 17008c2ecf20Sopenharmony_ci */ 17018c2ecf20Sopenharmony_ciint snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl, 17028c2ecf20Sopenharmony_ci unsigned int index, hda_nid_t nid) 17038c2ecf20Sopenharmony_ci{ 17048c2ecf20Sopenharmony_ci struct hda_nid_item *item; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci if (nid > 0) { 17078c2ecf20Sopenharmony_ci item = snd_array_new(&codec->nids); 17088c2ecf20Sopenharmony_ci if (!item) 17098c2ecf20Sopenharmony_ci return -ENOMEM; 17108c2ecf20Sopenharmony_ci item->kctl = kctl; 17118c2ecf20Sopenharmony_ci item->index = index; 17128c2ecf20Sopenharmony_ci item->nid = nid; 17138c2ecf20Sopenharmony_ci return 0; 17148c2ecf20Sopenharmony_ci } 17158c2ecf20Sopenharmony_ci codec_err(codec, "no NID for mapping control %s:%d:%d\n", 17168c2ecf20Sopenharmony_ci kctl->id.name, kctl->id.index, index); 17178c2ecf20Sopenharmony_ci return -EINVAL; 17188c2ecf20Sopenharmony_ci} 17198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_add_nid); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci/** 17228c2ecf20Sopenharmony_ci * snd_hda_ctls_clear - Clear all controls assigned to the given codec 17238c2ecf20Sopenharmony_ci * @codec: HD-audio codec 17248c2ecf20Sopenharmony_ci */ 17258c2ecf20Sopenharmony_civoid snd_hda_ctls_clear(struct hda_codec *codec) 17268c2ecf20Sopenharmony_ci{ 17278c2ecf20Sopenharmony_ci int i; 17288c2ecf20Sopenharmony_ci struct hda_nid_item *items = codec->mixers.list; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci down_write(&codec->card->controls_rwsem); 17318c2ecf20Sopenharmony_ci for (i = 0; i < codec->mixers.used; i++) 17328c2ecf20Sopenharmony_ci snd_ctl_remove(codec->card, items[i].kctl); 17338c2ecf20Sopenharmony_ci up_write(&codec->card->controls_rwsem); 17348c2ecf20Sopenharmony_ci snd_array_free(&codec->mixers); 17358c2ecf20Sopenharmony_ci snd_array_free(&codec->nids); 17368c2ecf20Sopenharmony_ci} 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci/** 17398c2ecf20Sopenharmony_ci * snd_hda_lock_devices - pseudo device locking 17408c2ecf20Sopenharmony_ci * @bus: the BUS 17418c2ecf20Sopenharmony_ci * 17428c2ecf20Sopenharmony_ci * toggle card->shutdown to allow/disallow the device access (as a hack) 17438c2ecf20Sopenharmony_ci */ 17448c2ecf20Sopenharmony_ciint snd_hda_lock_devices(struct hda_bus *bus) 17458c2ecf20Sopenharmony_ci{ 17468c2ecf20Sopenharmony_ci struct snd_card *card = bus->card; 17478c2ecf20Sopenharmony_ci struct hda_codec *codec; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci spin_lock(&card->files_lock); 17508c2ecf20Sopenharmony_ci if (card->shutdown) 17518c2ecf20Sopenharmony_ci goto err_unlock; 17528c2ecf20Sopenharmony_ci card->shutdown = 1; 17538c2ecf20Sopenharmony_ci if (!list_empty(&card->ctl_files)) 17548c2ecf20Sopenharmony_ci goto err_clear; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci list_for_each_codec(codec, bus) { 17578c2ecf20Sopenharmony_ci struct hda_pcm *cpcm; 17588c2ecf20Sopenharmony_ci list_for_each_entry(cpcm, &codec->pcm_list_head, list) { 17598c2ecf20Sopenharmony_ci if (!cpcm->pcm) 17608c2ecf20Sopenharmony_ci continue; 17618c2ecf20Sopenharmony_ci if (cpcm->pcm->streams[0].substream_opened || 17628c2ecf20Sopenharmony_ci cpcm->pcm->streams[1].substream_opened) 17638c2ecf20Sopenharmony_ci goto err_clear; 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci spin_unlock(&card->files_lock); 17678c2ecf20Sopenharmony_ci return 0; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci err_clear: 17708c2ecf20Sopenharmony_ci card->shutdown = 0; 17718c2ecf20Sopenharmony_ci err_unlock: 17728c2ecf20Sopenharmony_ci spin_unlock(&card->files_lock); 17738c2ecf20Sopenharmony_ci return -EINVAL; 17748c2ecf20Sopenharmony_ci} 17758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_lock_devices); 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci/** 17788c2ecf20Sopenharmony_ci * snd_hda_unlock_devices - pseudo device unlocking 17798c2ecf20Sopenharmony_ci * @bus: the BUS 17808c2ecf20Sopenharmony_ci */ 17818c2ecf20Sopenharmony_civoid snd_hda_unlock_devices(struct hda_bus *bus) 17828c2ecf20Sopenharmony_ci{ 17838c2ecf20Sopenharmony_ci struct snd_card *card = bus->card; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci spin_lock(&card->files_lock); 17868c2ecf20Sopenharmony_ci card->shutdown = 0; 17878c2ecf20Sopenharmony_ci spin_unlock(&card->files_lock); 17888c2ecf20Sopenharmony_ci} 17898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_unlock_devices); 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci/** 17928c2ecf20Sopenharmony_ci * snd_hda_codec_reset - Clear all objects assigned to the codec 17938c2ecf20Sopenharmony_ci * @codec: HD-audio codec 17948c2ecf20Sopenharmony_ci * 17958c2ecf20Sopenharmony_ci * This frees the all PCM and control elements assigned to the codec, and 17968c2ecf20Sopenharmony_ci * clears the caches and restores the pin default configurations. 17978c2ecf20Sopenharmony_ci * 17988c2ecf20Sopenharmony_ci * When a device is being used, it returns -EBSY. If successfully freed, 17998c2ecf20Sopenharmony_ci * returns zero. 18008c2ecf20Sopenharmony_ci */ 18018c2ecf20Sopenharmony_ciint snd_hda_codec_reset(struct hda_codec *codec) 18028c2ecf20Sopenharmony_ci{ 18038c2ecf20Sopenharmony_ci struct hda_bus *bus = codec->bus; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci if (snd_hda_lock_devices(bus) < 0) 18068c2ecf20Sopenharmony_ci return -EBUSY; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci /* OK, let it free */ 18098c2ecf20Sopenharmony_ci device_release_driver(hda_codec_dev(codec)); 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci /* allow device access again */ 18128c2ecf20Sopenharmony_ci snd_hda_unlock_devices(bus); 18138c2ecf20Sopenharmony_ci return 0; 18148c2ecf20Sopenharmony_ci} 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_citypedef int (*map_follower_func_t)(struct hda_codec *, void *, struct snd_kcontrol *); 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci/* apply the function to all matching follower ctls in the mixer list */ 18198c2ecf20Sopenharmony_cistatic int map_followers(struct hda_codec *codec, const char * const *followers, 18208c2ecf20Sopenharmony_ci const char *suffix, map_follower_func_t func, void *data) 18218c2ecf20Sopenharmony_ci{ 18228c2ecf20Sopenharmony_ci struct hda_nid_item *items; 18238c2ecf20Sopenharmony_ci const char * const *s; 18248c2ecf20Sopenharmony_ci int i, err; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci items = codec->mixers.list; 18278c2ecf20Sopenharmony_ci for (i = 0; i < codec->mixers.used; i++) { 18288c2ecf20Sopenharmony_ci struct snd_kcontrol *sctl = items[i].kctl; 18298c2ecf20Sopenharmony_ci if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER) 18308c2ecf20Sopenharmony_ci continue; 18318c2ecf20Sopenharmony_ci for (s = followers; *s; s++) { 18328c2ecf20Sopenharmony_ci char tmpname[sizeof(sctl->id.name)]; 18338c2ecf20Sopenharmony_ci const char *name = *s; 18348c2ecf20Sopenharmony_ci if (suffix) { 18358c2ecf20Sopenharmony_ci snprintf(tmpname, sizeof(tmpname), "%s %s", 18368c2ecf20Sopenharmony_ci name, suffix); 18378c2ecf20Sopenharmony_ci name = tmpname; 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci if (!strcmp(sctl->id.name, name)) { 18408c2ecf20Sopenharmony_ci err = func(codec, data, sctl); 18418c2ecf20Sopenharmony_ci if (err) 18428c2ecf20Sopenharmony_ci return err; 18438c2ecf20Sopenharmony_ci break; 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci } 18468c2ecf20Sopenharmony_ci } 18478c2ecf20Sopenharmony_ci return 0; 18488c2ecf20Sopenharmony_ci} 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_cistatic int check_follower_present(struct hda_codec *codec, 18518c2ecf20Sopenharmony_ci void *data, struct snd_kcontrol *sctl) 18528c2ecf20Sopenharmony_ci{ 18538c2ecf20Sopenharmony_ci return 1; 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci/* call kctl->put with the given value(s) */ 18578c2ecf20Sopenharmony_cistatic int put_kctl_with_value(struct snd_kcontrol *kctl, int val) 18588c2ecf20Sopenharmony_ci{ 18598c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol; 18608c2ecf20Sopenharmony_ci ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); 18618c2ecf20Sopenharmony_ci if (!ucontrol) 18628c2ecf20Sopenharmony_ci return -ENOMEM; 18638c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = val; 18648c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = val; 18658c2ecf20Sopenharmony_ci kctl->put(kctl, ucontrol); 18668c2ecf20Sopenharmony_ci kfree(ucontrol); 18678c2ecf20Sopenharmony_ci return 0; 18688c2ecf20Sopenharmony_ci} 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_cistruct follower_init_arg { 18718c2ecf20Sopenharmony_ci struct hda_codec *codec; 18728c2ecf20Sopenharmony_ci int step; 18738c2ecf20Sopenharmony_ci}; 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci/* initialize the follower volume with 0dB via snd_ctl_apply_vmaster_followers() */ 18768c2ecf20Sopenharmony_cistatic int init_follower_0dB(struct snd_kcontrol *follower, 18778c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl, 18788c2ecf20Sopenharmony_ci void *_arg) 18798c2ecf20Sopenharmony_ci{ 18808c2ecf20Sopenharmony_ci struct follower_init_arg *arg = _arg; 18818c2ecf20Sopenharmony_ci int _tlv[4]; 18828c2ecf20Sopenharmony_ci const int *tlv = NULL; 18838c2ecf20Sopenharmony_ci int step; 18848c2ecf20Sopenharmony_ci int val; 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 18878c2ecf20Sopenharmony_ci if (kctl->tlv.c != snd_hda_mixer_amp_tlv) { 18888c2ecf20Sopenharmony_ci codec_err(arg->codec, 18898c2ecf20Sopenharmony_ci "Unexpected TLV callback for follower %s:%d\n", 18908c2ecf20Sopenharmony_ci kctl->id.name, kctl->id.index); 18918c2ecf20Sopenharmony_ci return 0; /* ignore */ 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci get_ctl_amp_tlv(kctl, _tlv); 18948c2ecf20Sopenharmony_ci tlv = _tlv; 18958c2ecf20Sopenharmony_ci } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) 18968c2ecf20Sopenharmony_ci tlv = kctl->tlv.p; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci if (!tlv || tlv[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE) 18998c2ecf20Sopenharmony_ci return 0; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci step = tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP]; 19028c2ecf20Sopenharmony_ci step &= ~TLV_DB_SCALE_MUTE; 19038c2ecf20Sopenharmony_ci if (!step) 19048c2ecf20Sopenharmony_ci return 0; 19058c2ecf20Sopenharmony_ci if (arg->step && arg->step != step) { 19068c2ecf20Sopenharmony_ci codec_err(arg->codec, 19078c2ecf20Sopenharmony_ci "Mismatching dB step for vmaster follower (%d!=%d)\n", 19088c2ecf20Sopenharmony_ci arg->step, step); 19098c2ecf20Sopenharmony_ci return 0; 19108c2ecf20Sopenharmony_ci } 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci arg->step = step; 19138c2ecf20Sopenharmony_ci val = -tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] / step; 19148c2ecf20Sopenharmony_ci if (val > 0) { 19158c2ecf20Sopenharmony_ci put_kctl_with_value(follower, val); 19168c2ecf20Sopenharmony_ci return val; 19178c2ecf20Sopenharmony_ci } 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci return 0; 19208c2ecf20Sopenharmony_ci} 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci/* unmute the follower via snd_ctl_apply_vmaster_followers() */ 19238c2ecf20Sopenharmony_cistatic int init_follower_unmute(struct snd_kcontrol *follower, 19248c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl, 19258c2ecf20Sopenharmony_ci void *_arg) 19268c2ecf20Sopenharmony_ci{ 19278c2ecf20Sopenharmony_ci return put_kctl_with_value(follower, 1); 19288c2ecf20Sopenharmony_ci} 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_cistatic int add_follower(struct hda_codec *codec, 19318c2ecf20Sopenharmony_ci void *data, struct snd_kcontrol *follower) 19328c2ecf20Sopenharmony_ci{ 19338c2ecf20Sopenharmony_ci return snd_ctl_add_follower(data, follower); 19348c2ecf20Sopenharmony_ci} 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci/** 19378c2ecf20Sopenharmony_ci * __snd_hda_add_vmaster - create a virtual master control and add followers 19388c2ecf20Sopenharmony_ci * @codec: HD-audio codec 19398c2ecf20Sopenharmony_ci * @name: vmaster control name 19408c2ecf20Sopenharmony_ci * @tlv: TLV data (optional) 19418c2ecf20Sopenharmony_ci * @followers: follower control names (optional) 19428c2ecf20Sopenharmony_ci * @suffix: suffix string to each follower name (optional) 19438c2ecf20Sopenharmony_ci * @init_follower_vol: initialize followers to unmute/0dB 19448c2ecf20Sopenharmony_ci * @ctl_ret: store the vmaster kcontrol in return 19458c2ecf20Sopenharmony_ci * 19468c2ecf20Sopenharmony_ci * Create a virtual master control with the given name. The TLV data 19478c2ecf20Sopenharmony_ci * must be either NULL or a valid data. 19488c2ecf20Sopenharmony_ci * 19498c2ecf20Sopenharmony_ci * @followers is a NULL-terminated array of strings, each of which is a 19508c2ecf20Sopenharmony_ci * follower control name. All controls with these names are assigned to 19518c2ecf20Sopenharmony_ci * the new virtual master control. 19528c2ecf20Sopenharmony_ci * 19538c2ecf20Sopenharmony_ci * This function returns zero if successful or a negative error code. 19548c2ecf20Sopenharmony_ci */ 19558c2ecf20Sopenharmony_ciint __snd_hda_add_vmaster(struct hda_codec *codec, char *name, 19568c2ecf20Sopenharmony_ci unsigned int *tlv, const char * const *followers, 19578c2ecf20Sopenharmony_ci const char *suffix, bool init_follower_vol, 19588c2ecf20Sopenharmony_ci struct snd_kcontrol **ctl_ret) 19598c2ecf20Sopenharmony_ci{ 19608c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 19618c2ecf20Sopenharmony_ci int err; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci if (ctl_ret) 19648c2ecf20Sopenharmony_ci *ctl_ret = NULL; 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci err = map_followers(codec, followers, suffix, check_follower_present, NULL); 19678c2ecf20Sopenharmony_ci if (err != 1) { 19688c2ecf20Sopenharmony_ci codec_dbg(codec, "No follower found for %s\n", name); 19698c2ecf20Sopenharmony_ci return 0; 19708c2ecf20Sopenharmony_ci } 19718c2ecf20Sopenharmony_ci kctl = snd_ctl_make_virtual_master(name, tlv); 19728c2ecf20Sopenharmony_ci if (!kctl) 19738c2ecf20Sopenharmony_ci return -ENOMEM; 19748c2ecf20Sopenharmony_ci err = snd_hda_ctl_add(codec, 0, kctl); 19758c2ecf20Sopenharmony_ci if (err < 0) 19768c2ecf20Sopenharmony_ci return err; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci err = map_followers(codec, followers, suffix, add_follower, kctl); 19798c2ecf20Sopenharmony_ci if (err < 0) 19808c2ecf20Sopenharmony_ci return err; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci /* init with master mute & zero volume */ 19838c2ecf20Sopenharmony_ci put_kctl_with_value(kctl, 0); 19848c2ecf20Sopenharmony_ci if (init_follower_vol) { 19858c2ecf20Sopenharmony_ci struct follower_init_arg arg = { 19868c2ecf20Sopenharmony_ci .codec = codec, 19878c2ecf20Sopenharmony_ci .step = 0, 19888c2ecf20Sopenharmony_ci }; 19898c2ecf20Sopenharmony_ci snd_ctl_apply_vmaster_followers(kctl, 19908c2ecf20Sopenharmony_ci tlv ? init_follower_0dB : init_follower_unmute, 19918c2ecf20Sopenharmony_ci &arg); 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci if (ctl_ret) 19958c2ecf20Sopenharmony_ci *ctl_ret = kctl; 19968c2ecf20Sopenharmony_ci return 0; 19978c2ecf20Sopenharmony_ci} 19988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__snd_hda_add_vmaster); 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci/* 20018c2ecf20Sopenharmony_ci * mute-LED control using vmaster 20028c2ecf20Sopenharmony_ci */ 20038c2ecf20Sopenharmony_cistatic int vmaster_mute_mode_info(struct snd_kcontrol *kcontrol, 20048c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 20058c2ecf20Sopenharmony_ci{ 20068c2ecf20Sopenharmony_ci static const char * const texts[] = { 20078c2ecf20Sopenharmony_ci "On", "Off", "Follow Master" 20088c2ecf20Sopenharmony_ci }; 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 3, texts); 20118c2ecf20Sopenharmony_ci} 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_cistatic int vmaster_mute_mode_get(struct snd_kcontrol *kcontrol, 20148c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 20158c2ecf20Sopenharmony_ci{ 20168c2ecf20Sopenharmony_ci struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol); 20178c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = hook->mute_mode; 20188c2ecf20Sopenharmony_ci return 0; 20198c2ecf20Sopenharmony_ci} 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_cistatic int vmaster_mute_mode_put(struct snd_kcontrol *kcontrol, 20228c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 20238c2ecf20Sopenharmony_ci{ 20248c2ecf20Sopenharmony_ci struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol); 20258c2ecf20Sopenharmony_ci unsigned int old_mode = hook->mute_mode; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci hook->mute_mode = ucontrol->value.enumerated.item[0]; 20288c2ecf20Sopenharmony_ci if (hook->mute_mode > HDA_VMUTE_FOLLOW_MASTER) 20298c2ecf20Sopenharmony_ci hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER; 20308c2ecf20Sopenharmony_ci if (old_mode == hook->mute_mode) 20318c2ecf20Sopenharmony_ci return 0; 20328c2ecf20Sopenharmony_ci snd_hda_sync_vmaster_hook(hook); 20338c2ecf20Sopenharmony_ci return 1; 20348c2ecf20Sopenharmony_ci} 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new vmaster_mute_mode = { 20378c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 20388c2ecf20Sopenharmony_ci .name = "Mute-LED Mode", 20398c2ecf20Sopenharmony_ci .info = vmaster_mute_mode_info, 20408c2ecf20Sopenharmony_ci .get = vmaster_mute_mode_get, 20418c2ecf20Sopenharmony_ci .put = vmaster_mute_mode_put, 20428c2ecf20Sopenharmony_ci}; 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci/* meta hook to call each driver's vmaster hook */ 20458c2ecf20Sopenharmony_cistatic void vmaster_hook(void *private_data, int enabled) 20468c2ecf20Sopenharmony_ci{ 20478c2ecf20Sopenharmony_ci struct hda_vmaster_mute_hook *hook = private_data; 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci if (hook->mute_mode != HDA_VMUTE_FOLLOW_MASTER) 20508c2ecf20Sopenharmony_ci enabled = hook->mute_mode; 20518c2ecf20Sopenharmony_ci hook->hook(hook->codec, enabled); 20528c2ecf20Sopenharmony_ci} 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci/** 20558c2ecf20Sopenharmony_ci * snd_hda_add_vmaster_hook - Add a vmaster hook for mute-LED 20568c2ecf20Sopenharmony_ci * @codec: the HDA codec 20578c2ecf20Sopenharmony_ci * @hook: the vmaster hook object 20588c2ecf20Sopenharmony_ci * @expose_enum_ctl: flag to create an enum ctl 20598c2ecf20Sopenharmony_ci * 20608c2ecf20Sopenharmony_ci * Add a mute-LED hook with the given vmaster switch kctl. 20618c2ecf20Sopenharmony_ci * When @expose_enum_ctl is set, "Mute-LED Mode" control is automatically 20628c2ecf20Sopenharmony_ci * created and associated with the given hook. 20638c2ecf20Sopenharmony_ci */ 20648c2ecf20Sopenharmony_ciint snd_hda_add_vmaster_hook(struct hda_codec *codec, 20658c2ecf20Sopenharmony_ci struct hda_vmaster_mute_hook *hook, 20668c2ecf20Sopenharmony_ci bool expose_enum_ctl) 20678c2ecf20Sopenharmony_ci{ 20688c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci if (!hook->hook || !hook->sw_kctl) 20718c2ecf20Sopenharmony_ci return 0; 20728c2ecf20Sopenharmony_ci hook->codec = codec; 20738c2ecf20Sopenharmony_ci hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER; 20748c2ecf20Sopenharmony_ci snd_ctl_add_vmaster_hook(hook->sw_kctl, vmaster_hook, hook); 20758c2ecf20Sopenharmony_ci if (!expose_enum_ctl) 20768c2ecf20Sopenharmony_ci return 0; 20778c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&vmaster_mute_mode, hook); 20788c2ecf20Sopenharmony_ci if (!kctl) 20798c2ecf20Sopenharmony_ci return -ENOMEM; 20808c2ecf20Sopenharmony_ci return snd_hda_ctl_add(codec, 0, kctl); 20818c2ecf20Sopenharmony_ci} 20828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook); 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci/** 20858c2ecf20Sopenharmony_ci * snd_hda_sync_vmaster_hook - Sync vmaster hook 20868c2ecf20Sopenharmony_ci * @hook: the vmaster hook 20878c2ecf20Sopenharmony_ci * 20888c2ecf20Sopenharmony_ci * Call the hook with the current value for synchronization. 20898c2ecf20Sopenharmony_ci * Should be called in init callback. 20908c2ecf20Sopenharmony_ci */ 20918c2ecf20Sopenharmony_civoid snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook) 20928c2ecf20Sopenharmony_ci{ 20938c2ecf20Sopenharmony_ci if (!hook->hook || !hook->codec) 20948c2ecf20Sopenharmony_ci return; 20958c2ecf20Sopenharmony_ci /* don't call vmaster hook in the destructor since it might have 20968c2ecf20Sopenharmony_ci * been already destroyed 20978c2ecf20Sopenharmony_ci */ 20988c2ecf20Sopenharmony_ci if (hook->codec->bus->shutdown) 20998c2ecf20Sopenharmony_ci return; 21008c2ecf20Sopenharmony_ci snd_ctl_sync_vmaster_hook(hook->sw_kctl); 21018c2ecf20Sopenharmony_ci} 21028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_sync_vmaster_hook); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci/** 21068c2ecf20Sopenharmony_ci * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch 21078c2ecf20Sopenharmony_ci * @kcontrol: referred ctl element 21088c2ecf20Sopenharmony_ci * @uinfo: pointer to get/store the data 21098c2ecf20Sopenharmony_ci * 21108c2ecf20Sopenharmony_ci * The control element is supposed to have the private_value field 21118c2ecf20Sopenharmony_ci * set up via HDA_COMPOSE_AMP_VAL*() or related macros. 21128c2ecf20Sopenharmony_ci */ 21138c2ecf20Sopenharmony_ciint snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, 21148c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 21158c2ecf20Sopenharmony_ci{ 21168c2ecf20Sopenharmony_ci int chs = get_amp_channels(kcontrol); 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 21198c2ecf20Sopenharmony_ci uinfo->count = chs == 3 ? 2 : 1; 21208c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 21218c2ecf20Sopenharmony_ci uinfo->value.integer.max = 1; 21228c2ecf20Sopenharmony_ci return 0; 21238c2ecf20Sopenharmony_ci} 21248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_info); 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci/** 21278c2ecf20Sopenharmony_ci * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch 21288c2ecf20Sopenharmony_ci * @kcontrol: ctl element 21298c2ecf20Sopenharmony_ci * @ucontrol: pointer to get/store the data 21308c2ecf20Sopenharmony_ci * 21318c2ecf20Sopenharmony_ci * The control element is supposed to have the private_value field 21328c2ecf20Sopenharmony_ci * set up via HDA_COMPOSE_AMP_VAL*() or related macros. 21338c2ecf20Sopenharmony_ci */ 21348c2ecf20Sopenharmony_ciint snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, 21358c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 21368c2ecf20Sopenharmony_ci{ 21378c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 21388c2ecf20Sopenharmony_ci hda_nid_t nid = get_amp_nid(kcontrol); 21398c2ecf20Sopenharmony_ci int chs = get_amp_channels(kcontrol); 21408c2ecf20Sopenharmony_ci int dir = get_amp_direction(kcontrol); 21418c2ecf20Sopenharmony_ci int idx = get_amp_index(kcontrol); 21428c2ecf20Sopenharmony_ci long *valp = ucontrol->value.integer.value; 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci if (chs & 1) 21458c2ecf20Sopenharmony_ci *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 21468c2ecf20Sopenharmony_ci HDA_AMP_MUTE) ? 0 : 1; 21478c2ecf20Sopenharmony_ci if (chs & 2) 21488c2ecf20Sopenharmony_ci *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 21498c2ecf20Sopenharmony_ci HDA_AMP_MUTE) ? 0 : 1; 21508c2ecf20Sopenharmony_ci return 0; 21518c2ecf20Sopenharmony_ci} 21528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci/** 21558c2ecf20Sopenharmony_ci * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch 21568c2ecf20Sopenharmony_ci * @kcontrol: ctl element 21578c2ecf20Sopenharmony_ci * @ucontrol: pointer to get/store the data 21588c2ecf20Sopenharmony_ci * 21598c2ecf20Sopenharmony_ci * The control element is supposed to have the private_value field 21608c2ecf20Sopenharmony_ci * set up via HDA_COMPOSE_AMP_VAL*() or related macros. 21618c2ecf20Sopenharmony_ci */ 21628c2ecf20Sopenharmony_ciint snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, 21638c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 21648c2ecf20Sopenharmony_ci{ 21658c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 21668c2ecf20Sopenharmony_ci hda_nid_t nid = get_amp_nid(kcontrol); 21678c2ecf20Sopenharmony_ci int chs = get_amp_channels(kcontrol); 21688c2ecf20Sopenharmony_ci int dir = get_amp_direction(kcontrol); 21698c2ecf20Sopenharmony_ci int idx = get_amp_index(kcontrol); 21708c2ecf20Sopenharmony_ci long *valp = ucontrol->value.integer.value; 21718c2ecf20Sopenharmony_ci int change = 0; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci if (chs & 1) { 21748c2ecf20Sopenharmony_ci change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, 21758c2ecf20Sopenharmony_ci HDA_AMP_MUTE, 21768c2ecf20Sopenharmony_ci *valp ? 0 : HDA_AMP_MUTE); 21778c2ecf20Sopenharmony_ci valp++; 21788c2ecf20Sopenharmony_ci } 21798c2ecf20Sopenharmony_ci if (chs & 2) 21808c2ecf20Sopenharmony_ci change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, 21818c2ecf20Sopenharmony_ci HDA_AMP_MUTE, 21828c2ecf20Sopenharmony_ci *valp ? 0 : HDA_AMP_MUTE); 21838c2ecf20Sopenharmony_ci hda_call_check_power_status(codec, nid); 21848c2ecf20Sopenharmony_ci return change; 21858c2ecf20Sopenharmony_ci} 21868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put); 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci/* 21898c2ecf20Sopenharmony_ci * SPDIF out controls 21908c2ecf20Sopenharmony_ci */ 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_cistatic int snd_hda_spdif_mask_info(struct snd_kcontrol *kcontrol, 21938c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 21948c2ecf20Sopenharmony_ci{ 21958c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 21968c2ecf20Sopenharmony_ci uinfo->count = 1; 21978c2ecf20Sopenharmony_ci return 0; 21988c2ecf20Sopenharmony_ci} 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_cistatic int snd_hda_spdif_cmask_get(struct snd_kcontrol *kcontrol, 22018c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22028c2ecf20Sopenharmony_ci{ 22038c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | 22048c2ecf20Sopenharmony_ci IEC958_AES0_NONAUDIO | 22058c2ecf20Sopenharmony_ci IEC958_AES0_CON_EMPHASIS_5015 | 22068c2ecf20Sopenharmony_ci IEC958_AES0_CON_NOT_COPYRIGHT; 22078c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY | 22088c2ecf20Sopenharmony_ci IEC958_AES1_CON_ORIGINAL; 22098c2ecf20Sopenharmony_ci return 0; 22108c2ecf20Sopenharmony_ci} 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_cistatic int snd_hda_spdif_pmask_get(struct snd_kcontrol *kcontrol, 22138c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22148c2ecf20Sopenharmony_ci{ 22158c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | 22168c2ecf20Sopenharmony_ci IEC958_AES0_NONAUDIO | 22178c2ecf20Sopenharmony_ci IEC958_AES0_PRO_EMPHASIS_5015; 22188c2ecf20Sopenharmony_ci return 0; 22198c2ecf20Sopenharmony_ci} 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_cistatic int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol, 22228c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22238c2ecf20Sopenharmony_ci{ 22248c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22258c2ecf20Sopenharmony_ci int idx = kcontrol->private_value; 22268c2ecf20Sopenharmony_ci struct hda_spdif_out *spdif; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci if (WARN_ON(codec->spdif_out.used <= idx)) 22298c2ecf20Sopenharmony_ci return -EINVAL; 22308c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 22318c2ecf20Sopenharmony_ci spdif = snd_array_elem(&codec->spdif_out, idx); 22328c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[0] = spdif->status & 0xff; 22338c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff; 22348c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff; 22358c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff; 22368c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci return 0; 22398c2ecf20Sopenharmony_ci} 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci/* convert from SPDIF status bits to HDA SPDIF bits 22428c2ecf20Sopenharmony_ci * bit 0 (DigEn) is always set zero (to be filled later) 22438c2ecf20Sopenharmony_ci */ 22448c2ecf20Sopenharmony_cistatic unsigned short convert_from_spdif_status(unsigned int sbits) 22458c2ecf20Sopenharmony_ci{ 22468c2ecf20Sopenharmony_ci unsigned short val = 0; 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci if (sbits & IEC958_AES0_PROFESSIONAL) 22498c2ecf20Sopenharmony_ci val |= AC_DIG1_PROFESSIONAL; 22508c2ecf20Sopenharmony_ci if (sbits & IEC958_AES0_NONAUDIO) 22518c2ecf20Sopenharmony_ci val |= AC_DIG1_NONAUDIO; 22528c2ecf20Sopenharmony_ci if (sbits & IEC958_AES0_PROFESSIONAL) { 22538c2ecf20Sopenharmony_ci if ((sbits & IEC958_AES0_PRO_EMPHASIS) == 22548c2ecf20Sopenharmony_ci IEC958_AES0_PRO_EMPHASIS_5015) 22558c2ecf20Sopenharmony_ci val |= AC_DIG1_EMPHASIS; 22568c2ecf20Sopenharmony_ci } else { 22578c2ecf20Sopenharmony_ci if ((sbits & IEC958_AES0_CON_EMPHASIS) == 22588c2ecf20Sopenharmony_ci IEC958_AES0_CON_EMPHASIS_5015) 22598c2ecf20Sopenharmony_ci val |= AC_DIG1_EMPHASIS; 22608c2ecf20Sopenharmony_ci if (!(sbits & IEC958_AES0_CON_NOT_COPYRIGHT)) 22618c2ecf20Sopenharmony_ci val |= AC_DIG1_COPYRIGHT; 22628c2ecf20Sopenharmony_ci if (sbits & (IEC958_AES1_CON_ORIGINAL << 8)) 22638c2ecf20Sopenharmony_ci val |= AC_DIG1_LEVEL; 22648c2ecf20Sopenharmony_ci val |= sbits & (IEC958_AES1_CON_CATEGORY << 8); 22658c2ecf20Sopenharmony_ci } 22668c2ecf20Sopenharmony_ci return val; 22678c2ecf20Sopenharmony_ci} 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci/* convert to SPDIF status bits from HDA SPDIF bits 22708c2ecf20Sopenharmony_ci */ 22718c2ecf20Sopenharmony_cistatic unsigned int convert_to_spdif_status(unsigned short val) 22728c2ecf20Sopenharmony_ci{ 22738c2ecf20Sopenharmony_ci unsigned int sbits = 0; 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci if (val & AC_DIG1_NONAUDIO) 22768c2ecf20Sopenharmony_ci sbits |= IEC958_AES0_NONAUDIO; 22778c2ecf20Sopenharmony_ci if (val & AC_DIG1_PROFESSIONAL) 22788c2ecf20Sopenharmony_ci sbits |= IEC958_AES0_PROFESSIONAL; 22798c2ecf20Sopenharmony_ci if (sbits & IEC958_AES0_PROFESSIONAL) { 22808c2ecf20Sopenharmony_ci if (val & AC_DIG1_EMPHASIS) 22818c2ecf20Sopenharmony_ci sbits |= IEC958_AES0_PRO_EMPHASIS_5015; 22828c2ecf20Sopenharmony_ci } else { 22838c2ecf20Sopenharmony_ci if (val & AC_DIG1_EMPHASIS) 22848c2ecf20Sopenharmony_ci sbits |= IEC958_AES0_CON_EMPHASIS_5015; 22858c2ecf20Sopenharmony_ci if (!(val & AC_DIG1_COPYRIGHT)) 22868c2ecf20Sopenharmony_ci sbits |= IEC958_AES0_CON_NOT_COPYRIGHT; 22878c2ecf20Sopenharmony_ci if (val & AC_DIG1_LEVEL) 22888c2ecf20Sopenharmony_ci sbits |= (IEC958_AES1_CON_ORIGINAL << 8); 22898c2ecf20Sopenharmony_ci sbits |= val & (0x7f << 8); 22908c2ecf20Sopenharmony_ci } 22918c2ecf20Sopenharmony_ci return sbits; 22928c2ecf20Sopenharmony_ci} 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci/* set digital convert verbs both for the given NID and its followers */ 22958c2ecf20Sopenharmony_cistatic void set_dig_out(struct hda_codec *codec, hda_nid_t nid, 22968c2ecf20Sopenharmony_ci int mask, int val) 22978c2ecf20Sopenharmony_ci{ 22988c2ecf20Sopenharmony_ci const hda_nid_t *d; 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1, 23018c2ecf20Sopenharmony_ci mask, val); 23028c2ecf20Sopenharmony_ci d = codec->follower_dig_outs; 23038c2ecf20Sopenharmony_ci if (!d) 23048c2ecf20Sopenharmony_ci return; 23058c2ecf20Sopenharmony_ci for (; *d; d++) 23068c2ecf20Sopenharmony_ci snd_hdac_regmap_update(&codec->core, *d, 23078c2ecf20Sopenharmony_ci AC_VERB_SET_DIGI_CONVERT_1, mask, val); 23088c2ecf20Sopenharmony_ci} 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_cistatic inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid, 23118c2ecf20Sopenharmony_ci int dig1, int dig2) 23128c2ecf20Sopenharmony_ci{ 23138c2ecf20Sopenharmony_ci unsigned int mask = 0; 23148c2ecf20Sopenharmony_ci unsigned int val = 0; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci if (dig1 != -1) { 23178c2ecf20Sopenharmony_ci mask |= 0xff; 23188c2ecf20Sopenharmony_ci val = dig1; 23198c2ecf20Sopenharmony_ci } 23208c2ecf20Sopenharmony_ci if (dig2 != -1) { 23218c2ecf20Sopenharmony_ci mask |= 0xff00; 23228c2ecf20Sopenharmony_ci val |= dig2 << 8; 23238c2ecf20Sopenharmony_ci } 23248c2ecf20Sopenharmony_ci set_dig_out(codec, nid, mask, val); 23258c2ecf20Sopenharmony_ci} 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_cistatic int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, 23288c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 23298c2ecf20Sopenharmony_ci{ 23308c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 23318c2ecf20Sopenharmony_ci int idx = kcontrol->private_value; 23328c2ecf20Sopenharmony_ci struct hda_spdif_out *spdif; 23338c2ecf20Sopenharmony_ci hda_nid_t nid; 23348c2ecf20Sopenharmony_ci unsigned short val; 23358c2ecf20Sopenharmony_ci int change; 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci if (WARN_ON(codec->spdif_out.used <= idx)) 23388c2ecf20Sopenharmony_ci return -EINVAL; 23398c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 23408c2ecf20Sopenharmony_ci spdif = snd_array_elem(&codec->spdif_out, idx); 23418c2ecf20Sopenharmony_ci nid = spdif->nid; 23428c2ecf20Sopenharmony_ci spdif->status = ucontrol->value.iec958.status[0] | 23438c2ecf20Sopenharmony_ci ((unsigned int)ucontrol->value.iec958.status[1] << 8) | 23448c2ecf20Sopenharmony_ci ((unsigned int)ucontrol->value.iec958.status[2] << 16) | 23458c2ecf20Sopenharmony_ci ((unsigned int)ucontrol->value.iec958.status[3] << 24); 23468c2ecf20Sopenharmony_ci val = convert_from_spdif_status(spdif->status); 23478c2ecf20Sopenharmony_ci val |= spdif->ctls & 1; 23488c2ecf20Sopenharmony_ci change = spdif->ctls != val; 23498c2ecf20Sopenharmony_ci spdif->ctls = val; 23508c2ecf20Sopenharmony_ci if (change && nid != (u16)-1) 23518c2ecf20Sopenharmony_ci set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff); 23528c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 23538c2ecf20Sopenharmony_ci return change; 23548c2ecf20Sopenharmony_ci} 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_ci#define snd_hda_spdif_out_switch_info snd_ctl_boolean_mono_info 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_cistatic int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, 23598c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 23608c2ecf20Sopenharmony_ci{ 23618c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 23628c2ecf20Sopenharmony_ci int idx = kcontrol->private_value; 23638c2ecf20Sopenharmony_ci struct hda_spdif_out *spdif; 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci if (WARN_ON(codec->spdif_out.used <= idx)) 23668c2ecf20Sopenharmony_ci return -EINVAL; 23678c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 23688c2ecf20Sopenharmony_ci spdif = snd_array_elem(&codec->spdif_out, idx); 23698c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE; 23708c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 23718c2ecf20Sopenharmony_ci return 0; 23728c2ecf20Sopenharmony_ci} 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_cistatic inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid, 23758c2ecf20Sopenharmony_ci int dig1, int dig2) 23768c2ecf20Sopenharmony_ci{ 23778c2ecf20Sopenharmony_ci set_dig_out_convert(codec, nid, dig1, dig2); 23788c2ecf20Sopenharmony_ci /* unmute amp switch (if any) */ 23798c2ecf20Sopenharmony_ci if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && 23808c2ecf20Sopenharmony_ci (dig1 & AC_DIG1_ENABLE)) 23818c2ecf20Sopenharmony_ci snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, 23828c2ecf20Sopenharmony_ci HDA_AMP_MUTE, 0); 23838c2ecf20Sopenharmony_ci} 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_cistatic int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, 23868c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 23878c2ecf20Sopenharmony_ci{ 23888c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 23898c2ecf20Sopenharmony_ci int idx = kcontrol->private_value; 23908c2ecf20Sopenharmony_ci struct hda_spdif_out *spdif; 23918c2ecf20Sopenharmony_ci hda_nid_t nid; 23928c2ecf20Sopenharmony_ci unsigned short val; 23938c2ecf20Sopenharmony_ci int change; 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci if (WARN_ON(codec->spdif_out.used <= idx)) 23968c2ecf20Sopenharmony_ci return -EINVAL; 23978c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 23988c2ecf20Sopenharmony_ci spdif = snd_array_elem(&codec->spdif_out, idx); 23998c2ecf20Sopenharmony_ci nid = spdif->nid; 24008c2ecf20Sopenharmony_ci val = spdif->ctls & ~AC_DIG1_ENABLE; 24018c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0]) 24028c2ecf20Sopenharmony_ci val |= AC_DIG1_ENABLE; 24038c2ecf20Sopenharmony_ci change = spdif->ctls != val; 24048c2ecf20Sopenharmony_ci spdif->ctls = val; 24058c2ecf20Sopenharmony_ci if (change && nid != (u16)-1) 24068c2ecf20Sopenharmony_ci set_spdif_ctls(codec, nid, val & 0xff, -1); 24078c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 24088c2ecf20Sopenharmony_ci return change; 24098c2ecf20Sopenharmony_ci} 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new dig_mixes[] = { 24128c2ecf20Sopenharmony_ci { 24138c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 24148c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 24158c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), 24168c2ecf20Sopenharmony_ci .info = snd_hda_spdif_mask_info, 24178c2ecf20Sopenharmony_ci .get = snd_hda_spdif_cmask_get, 24188c2ecf20Sopenharmony_ci }, 24198c2ecf20Sopenharmony_ci { 24208c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 24218c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 24228c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK), 24238c2ecf20Sopenharmony_ci .info = snd_hda_spdif_mask_info, 24248c2ecf20Sopenharmony_ci .get = snd_hda_spdif_pmask_get, 24258c2ecf20Sopenharmony_ci }, 24268c2ecf20Sopenharmony_ci { 24278c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 24288c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), 24298c2ecf20Sopenharmony_ci .info = snd_hda_spdif_mask_info, 24308c2ecf20Sopenharmony_ci .get = snd_hda_spdif_default_get, 24318c2ecf20Sopenharmony_ci .put = snd_hda_spdif_default_put, 24328c2ecf20Sopenharmony_ci }, 24338c2ecf20Sopenharmony_ci { 24348c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 24358c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), 24368c2ecf20Sopenharmony_ci .info = snd_hda_spdif_out_switch_info, 24378c2ecf20Sopenharmony_ci .get = snd_hda_spdif_out_switch_get, 24388c2ecf20Sopenharmony_ci .put = snd_hda_spdif_out_switch_put, 24398c2ecf20Sopenharmony_ci }, 24408c2ecf20Sopenharmony_ci { } /* end */ 24418c2ecf20Sopenharmony_ci}; 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci/** 24448c2ecf20Sopenharmony_ci * snd_hda_create_dig_out_ctls - create Output SPDIF-related controls 24458c2ecf20Sopenharmony_ci * @codec: the HDA codec 24468c2ecf20Sopenharmony_ci * @associated_nid: NID that new ctls associated with 24478c2ecf20Sopenharmony_ci * @cvt_nid: converter NID 24488c2ecf20Sopenharmony_ci * @type: HDA_PCM_TYPE_* 24498c2ecf20Sopenharmony_ci * Creates controls related with the digital output. 24508c2ecf20Sopenharmony_ci * Called from each patch supporting the digital out. 24518c2ecf20Sopenharmony_ci * 24528c2ecf20Sopenharmony_ci * Returns 0 if successful, or a negative error code. 24538c2ecf20Sopenharmony_ci */ 24548c2ecf20Sopenharmony_ciint snd_hda_create_dig_out_ctls(struct hda_codec *codec, 24558c2ecf20Sopenharmony_ci hda_nid_t associated_nid, 24568c2ecf20Sopenharmony_ci hda_nid_t cvt_nid, 24578c2ecf20Sopenharmony_ci int type) 24588c2ecf20Sopenharmony_ci{ 24598c2ecf20Sopenharmony_ci int err; 24608c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 24618c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *dig_mix; 24628c2ecf20Sopenharmony_ci int idx = 0; 24638c2ecf20Sopenharmony_ci int val = 0; 24648c2ecf20Sopenharmony_ci const int spdif_index = 16; 24658c2ecf20Sopenharmony_ci struct hda_spdif_out *spdif; 24668c2ecf20Sopenharmony_ci struct hda_bus *bus = codec->bus; 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci if (bus->primary_dig_out_type == HDA_PCM_TYPE_HDMI && 24698c2ecf20Sopenharmony_ci type == HDA_PCM_TYPE_SPDIF) { 24708c2ecf20Sopenharmony_ci idx = spdif_index; 24718c2ecf20Sopenharmony_ci } else if (bus->primary_dig_out_type == HDA_PCM_TYPE_SPDIF && 24728c2ecf20Sopenharmony_ci type == HDA_PCM_TYPE_HDMI) { 24738c2ecf20Sopenharmony_ci /* suppose a single SPDIF device */ 24748c2ecf20Sopenharmony_ci for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { 24758c2ecf20Sopenharmony_ci kctl = find_mixer_ctl(codec, dig_mix->name, 0, 0); 24768c2ecf20Sopenharmony_ci if (!kctl) 24778c2ecf20Sopenharmony_ci break; 24788c2ecf20Sopenharmony_ci kctl->id.index = spdif_index; 24798c2ecf20Sopenharmony_ci } 24808c2ecf20Sopenharmony_ci bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI; 24818c2ecf20Sopenharmony_ci } 24828c2ecf20Sopenharmony_ci if (!bus->primary_dig_out_type) 24838c2ecf20Sopenharmony_ci bus->primary_dig_out_type = type; 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", idx); 24868c2ecf20Sopenharmony_ci if (idx < 0) { 24878c2ecf20Sopenharmony_ci codec_err(codec, "too many IEC958 outputs\n"); 24888c2ecf20Sopenharmony_ci return -EBUSY; 24898c2ecf20Sopenharmony_ci } 24908c2ecf20Sopenharmony_ci spdif = snd_array_new(&codec->spdif_out); 24918c2ecf20Sopenharmony_ci if (!spdif) 24928c2ecf20Sopenharmony_ci return -ENOMEM; 24938c2ecf20Sopenharmony_ci for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { 24948c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(dig_mix, codec); 24958c2ecf20Sopenharmony_ci if (!kctl) 24968c2ecf20Sopenharmony_ci return -ENOMEM; 24978c2ecf20Sopenharmony_ci kctl->id.index = idx; 24988c2ecf20Sopenharmony_ci kctl->private_value = codec->spdif_out.used - 1; 24998c2ecf20Sopenharmony_ci err = snd_hda_ctl_add(codec, associated_nid, kctl); 25008c2ecf20Sopenharmony_ci if (err < 0) 25018c2ecf20Sopenharmony_ci return err; 25028c2ecf20Sopenharmony_ci } 25038c2ecf20Sopenharmony_ci spdif->nid = cvt_nid; 25048c2ecf20Sopenharmony_ci snd_hdac_regmap_read(&codec->core, cvt_nid, 25058c2ecf20Sopenharmony_ci AC_VERB_GET_DIGI_CONVERT_1, &val); 25068c2ecf20Sopenharmony_ci spdif->ctls = val; 25078c2ecf20Sopenharmony_ci spdif->status = convert_to_spdif_status(spdif->ctls); 25088c2ecf20Sopenharmony_ci return 0; 25098c2ecf20Sopenharmony_ci} 25108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls); 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci/** 25138c2ecf20Sopenharmony_ci * snd_hda_spdif_out_of_nid - get the hda_spdif_out entry from the given NID 25148c2ecf20Sopenharmony_ci * @codec: the HDA codec 25158c2ecf20Sopenharmony_ci * @nid: widget NID 25168c2ecf20Sopenharmony_ci * 25178c2ecf20Sopenharmony_ci * call within spdif_mutex lock 25188c2ecf20Sopenharmony_ci */ 25198c2ecf20Sopenharmony_cistruct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, 25208c2ecf20Sopenharmony_ci hda_nid_t nid) 25218c2ecf20Sopenharmony_ci{ 25228c2ecf20Sopenharmony_ci struct hda_spdif_out *spdif; 25238c2ecf20Sopenharmony_ci int i; 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci snd_array_for_each(&codec->spdif_out, i, spdif) { 25268c2ecf20Sopenharmony_ci if (spdif->nid == nid) 25278c2ecf20Sopenharmony_ci return spdif; 25288c2ecf20Sopenharmony_ci } 25298c2ecf20Sopenharmony_ci return NULL; 25308c2ecf20Sopenharmony_ci} 25318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_spdif_out_of_nid); 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci/** 25348c2ecf20Sopenharmony_ci * snd_hda_spdif_ctls_unassign - Unassign the given SPDIF ctl 25358c2ecf20Sopenharmony_ci * @codec: the HDA codec 25368c2ecf20Sopenharmony_ci * @idx: the SPDIF ctl index 25378c2ecf20Sopenharmony_ci * 25388c2ecf20Sopenharmony_ci * Unassign the widget from the given SPDIF control. 25398c2ecf20Sopenharmony_ci */ 25408c2ecf20Sopenharmony_civoid snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) 25418c2ecf20Sopenharmony_ci{ 25428c2ecf20Sopenharmony_ci struct hda_spdif_out *spdif; 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci if (WARN_ON(codec->spdif_out.used <= idx)) 25458c2ecf20Sopenharmony_ci return; 25468c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 25478c2ecf20Sopenharmony_ci spdif = snd_array_elem(&codec->spdif_out, idx); 25488c2ecf20Sopenharmony_ci spdif->nid = (u16)-1; 25498c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 25508c2ecf20Sopenharmony_ci} 25518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_unassign); 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci/** 25548c2ecf20Sopenharmony_ci * snd_hda_spdif_ctls_assign - Assign the SPDIF controls to the given NID 25558c2ecf20Sopenharmony_ci * @codec: the HDA codec 25568c2ecf20Sopenharmony_ci * @idx: the SPDIF ctl idx 25578c2ecf20Sopenharmony_ci * @nid: widget NID 25588c2ecf20Sopenharmony_ci * 25598c2ecf20Sopenharmony_ci * Assign the widget to the SPDIF control with the given index. 25608c2ecf20Sopenharmony_ci */ 25618c2ecf20Sopenharmony_civoid snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid) 25628c2ecf20Sopenharmony_ci{ 25638c2ecf20Sopenharmony_ci struct hda_spdif_out *spdif; 25648c2ecf20Sopenharmony_ci unsigned short val; 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci if (WARN_ON(codec->spdif_out.used <= idx)) 25678c2ecf20Sopenharmony_ci return; 25688c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 25698c2ecf20Sopenharmony_ci spdif = snd_array_elem(&codec->spdif_out, idx); 25708c2ecf20Sopenharmony_ci if (spdif->nid != nid) { 25718c2ecf20Sopenharmony_ci spdif->nid = nid; 25728c2ecf20Sopenharmony_ci val = spdif->ctls; 25738c2ecf20Sopenharmony_ci set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff); 25748c2ecf20Sopenharmony_ci } 25758c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 25768c2ecf20Sopenharmony_ci} 25778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_assign); 25788c2ecf20Sopenharmony_ci 25798c2ecf20Sopenharmony_ci/* 25808c2ecf20Sopenharmony_ci * SPDIF sharing with analog output 25818c2ecf20Sopenharmony_ci */ 25828c2ecf20Sopenharmony_cistatic int spdif_share_sw_get(struct snd_kcontrol *kcontrol, 25838c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 25848c2ecf20Sopenharmony_ci{ 25858c2ecf20Sopenharmony_ci struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); 25868c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mout->share_spdif; 25878c2ecf20Sopenharmony_ci return 0; 25888c2ecf20Sopenharmony_ci} 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_cistatic int spdif_share_sw_put(struct snd_kcontrol *kcontrol, 25918c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 25928c2ecf20Sopenharmony_ci{ 25938c2ecf20Sopenharmony_ci struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); 25948c2ecf20Sopenharmony_ci mout->share_spdif = !!ucontrol->value.integer.value[0]; 25958c2ecf20Sopenharmony_ci return 0; 25968c2ecf20Sopenharmony_ci} 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new spdif_share_sw = { 25998c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 26008c2ecf20Sopenharmony_ci .name = "IEC958 Default PCM Playback Switch", 26018c2ecf20Sopenharmony_ci .info = snd_ctl_boolean_mono_info, 26028c2ecf20Sopenharmony_ci .get = spdif_share_sw_get, 26038c2ecf20Sopenharmony_ci .put = spdif_share_sw_put, 26048c2ecf20Sopenharmony_ci}; 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci/** 26078c2ecf20Sopenharmony_ci * snd_hda_create_spdif_share_sw - create Default PCM switch 26088c2ecf20Sopenharmony_ci * @codec: the HDA codec 26098c2ecf20Sopenharmony_ci * @mout: multi-out instance 26108c2ecf20Sopenharmony_ci */ 26118c2ecf20Sopenharmony_ciint snd_hda_create_spdif_share_sw(struct hda_codec *codec, 26128c2ecf20Sopenharmony_ci struct hda_multi_out *mout) 26138c2ecf20Sopenharmony_ci{ 26148c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci if (!mout->dig_out_nid) 26178c2ecf20Sopenharmony_ci return 0; 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&spdif_share_sw, mout); 26208c2ecf20Sopenharmony_ci if (!kctl) 26218c2ecf20Sopenharmony_ci return -ENOMEM; 26228c2ecf20Sopenharmony_ci /* ATTENTION: here mout is passed as private_data, instead of codec */ 26238c2ecf20Sopenharmony_ci return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl); 26248c2ecf20Sopenharmony_ci} 26258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw); 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci/* 26288c2ecf20Sopenharmony_ci * SPDIF input 26298c2ecf20Sopenharmony_ci */ 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci#define snd_hda_spdif_in_switch_info snd_hda_spdif_out_switch_info 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_cistatic int snd_hda_spdif_in_switch_get(struct snd_kcontrol *kcontrol, 26348c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 26358c2ecf20Sopenharmony_ci{ 26368c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = codec->spdif_in_enable; 26398c2ecf20Sopenharmony_ci return 0; 26408c2ecf20Sopenharmony_ci} 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_cistatic int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol, 26438c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 26448c2ecf20Sopenharmony_ci{ 26458c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 26468c2ecf20Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 26478c2ecf20Sopenharmony_ci unsigned int val = !!ucontrol->value.integer.value[0]; 26488c2ecf20Sopenharmony_ci int change; 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 26518c2ecf20Sopenharmony_ci change = codec->spdif_in_enable != val; 26528c2ecf20Sopenharmony_ci if (change) { 26538c2ecf20Sopenharmony_ci codec->spdif_in_enable = val; 26548c2ecf20Sopenharmony_ci snd_hdac_regmap_write(&codec->core, nid, 26558c2ecf20Sopenharmony_ci AC_VERB_SET_DIGI_CONVERT_1, val); 26568c2ecf20Sopenharmony_ci } 26578c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 26588c2ecf20Sopenharmony_ci return change; 26598c2ecf20Sopenharmony_ci} 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_cistatic int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol, 26628c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 26638c2ecf20Sopenharmony_ci{ 26648c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 26658c2ecf20Sopenharmony_ci hda_nid_t nid = kcontrol->private_value; 26668c2ecf20Sopenharmony_ci unsigned int val; 26678c2ecf20Sopenharmony_ci unsigned int sbits; 26688c2ecf20Sopenharmony_ci 26698c2ecf20Sopenharmony_ci snd_hdac_regmap_read(&codec->core, nid, 26708c2ecf20Sopenharmony_ci AC_VERB_GET_DIGI_CONVERT_1, &val); 26718c2ecf20Sopenharmony_ci sbits = convert_to_spdif_status(val); 26728c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[0] = sbits; 26738c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[1] = sbits >> 8; 26748c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[2] = sbits >> 16; 26758c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[3] = sbits >> 24; 26768c2ecf20Sopenharmony_ci return 0; 26778c2ecf20Sopenharmony_ci} 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new dig_in_ctls[] = { 26808c2ecf20Sopenharmony_ci { 26818c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 26828c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH), 26838c2ecf20Sopenharmony_ci .info = snd_hda_spdif_in_switch_info, 26848c2ecf20Sopenharmony_ci .get = snd_hda_spdif_in_switch_get, 26858c2ecf20Sopenharmony_ci .put = snd_hda_spdif_in_switch_put, 26868c2ecf20Sopenharmony_ci }, 26878c2ecf20Sopenharmony_ci { 26888c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 26898c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 26908c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), 26918c2ecf20Sopenharmony_ci .info = snd_hda_spdif_mask_info, 26928c2ecf20Sopenharmony_ci .get = snd_hda_spdif_in_status_get, 26938c2ecf20Sopenharmony_ci }, 26948c2ecf20Sopenharmony_ci { } /* end */ 26958c2ecf20Sopenharmony_ci}; 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci/** 26988c2ecf20Sopenharmony_ci * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls 26998c2ecf20Sopenharmony_ci * @codec: the HDA codec 27008c2ecf20Sopenharmony_ci * @nid: audio in widget NID 27018c2ecf20Sopenharmony_ci * 27028c2ecf20Sopenharmony_ci * Creates controls related with the SPDIF input. 27038c2ecf20Sopenharmony_ci * Called from each patch supporting the SPDIF in. 27048c2ecf20Sopenharmony_ci * 27058c2ecf20Sopenharmony_ci * Returns 0 if successful, or a negative error code. 27068c2ecf20Sopenharmony_ci */ 27078c2ecf20Sopenharmony_ciint snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) 27088c2ecf20Sopenharmony_ci{ 27098c2ecf20Sopenharmony_ci int err; 27108c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 27118c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *dig_mix; 27128c2ecf20Sopenharmony_ci int idx; 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch", 0); 27158c2ecf20Sopenharmony_ci if (idx < 0) { 27168c2ecf20Sopenharmony_ci codec_err(codec, "too many IEC958 inputs\n"); 27178c2ecf20Sopenharmony_ci return -EBUSY; 27188c2ecf20Sopenharmony_ci } 27198c2ecf20Sopenharmony_ci for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) { 27208c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(dig_mix, codec); 27218c2ecf20Sopenharmony_ci if (!kctl) 27228c2ecf20Sopenharmony_ci return -ENOMEM; 27238c2ecf20Sopenharmony_ci kctl->private_value = nid; 27248c2ecf20Sopenharmony_ci err = snd_hda_ctl_add(codec, nid, kctl); 27258c2ecf20Sopenharmony_ci if (err < 0) 27268c2ecf20Sopenharmony_ci return err; 27278c2ecf20Sopenharmony_ci } 27288c2ecf20Sopenharmony_ci codec->spdif_in_enable = 27298c2ecf20Sopenharmony_ci snd_hda_codec_read(codec, nid, 0, 27308c2ecf20Sopenharmony_ci AC_VERB_GET_DIGI_CONVERT_1, 0) & 27318c2ecf20Sopenharmony_ci AC_DIG1_ENABLE; 27328c2ecf20Sopenharmony_ci return 0; 27338c2ecf20Sopenharmony_ci} 27348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls); 27358c2ecf20Sopenharmony_ci 27368c2ecf20Sopenharmony_ci/** 27378c2ecf20Sopenharmony_ci * snd_hda_codec_set_power_to_all - Set the power state to all widgets 27388c2ecf20Sopenharmony_ci * @codec: the HDA codec 27398c2ecf20Sopenharmony_ci * @fg: function group (not used now) 27408c2ecf20Sopenharmony_ci * @power_state: the power state to set (AC_PWRST_*) 27418c2ecf20Sopenharmony_ci * 27428c2ecf20Sopenharmony_ci * Set the given power state to all widgets that have the power control. 27438c2ecf20Sopenharmony_ci * If the codec has power_filter set, it evaluates the power state and 27448c2ecf20Sopenharmony_ci * filter out if it's unchanged as D3. 27458c2ecf20Sopenharmony_ci */ 27468c2ecf20Sopenharmony_civoid snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, 27478c2ecf20Sopenharmony_ci unsigned int power_state) 27488c2ecf20Sopenharmony_ci{ 27498c2ecf20Sopenharmony_ci hda_nid_t nid; 27508c2ecf20Sopenharmony_ci 27518c2ecf20Sopenharmony_ci for_each_hda_codec_node(nid, codec) { 27528c2ecf20Sopenharmony_ci unsigned int wcaps = get_wcaps(codec, nid); 27538c2ecf20Sopenharmony_ci unsigned int state = power_state; 27548c2ecf20Sopenharmony_ci if (!(wcaps & AC_WCAP_POWER)) 27558c2ecf20Sopenharmony_ci continue; 27568c2ecf20Sopenharmony_ci if (codec->power_filter) { 27578c2ecf20Sopenharmony_ci state = codec->power_filter(codec, nid, power_state); 27588c2ecf20Sopenharmony_ci if (state != power_state && power_state == AC_PWRST_D3) 27598c2ecf20Sopenharmony_ci continue; 27608c2ecf20Sopenharmony_ci } 27618c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, 27628c2ecf20Sopenharmony_ci state); 27638c2ecf20Sopenharmony_ci } 27648c2ecf20Sopenharmony_ci} 27658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all); 27668c2ecf20Sopenharmony_ci 27678c2ecf20Sopenharmony_ci/** 27688c2ecf20Sopenharmony_ci * snd_hda_codec_eapd_power_filter - A power filter callback for EAPD 27698c2ecf20Sopenharmony_ci * @codec: the HDA codec 27708c2ecf20Sopenharmony_ci * @nid: widget NID 27718c2ecf20Sopenharmony_ci * @power_state: power state to evalue 27728c2ecf20Sopenharmony_ci * 27738c2ecf20Sopenharmony_ci * Don't power down the widget if it controls eapd and EAPD_BTLENABLE is set. 27748c2ecf20Sopenharmony_ci * This can be used a codec power_filter callback. 27758c2ecf20Sopenharmony_ci */ 27768c2ecf20Sopenharmony_ciunsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, 27778c2ecf20Sopenharmony_ci hda_nid_t nid, 27788c2ecf20Sopenharmony_ci unsigned int power_state) 27798c2ecf20Sopenharmony_ci{ 27808c2ecf20Sopenharmony_ci if (nid == codec->core.afg || nid == codec->core.mfg) 27818c2ecf20Sopenharmony_ci return power_state; 27828c2ecf20Sopenharmony_ci if (power_state == AC_PWRST_D3 && 27838c2ecf20Sopenharmony_ci get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN && 27848c2ecf20Sopenharmony_ci (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { 27858c2ecf20Sopenharmony_ci int eapd = snd_hda_codec_read(codec, nid, 0, 27868c2ecf20Sopenharmony_ci AC_VERB_GET_EAPD_BTLENABLE, 0); 27878c2ecf20Sopenharmony_ci if (eapd & 0x02) 27888c2ecf20Sopenharmony_ci return AC_PWRST_D0; 27898c2ecf20Sopenharmony_ci } 27908c2ecf20Sopenharmony_ci return power_state; 27918c2ecf20Sopenharmony_ci} 27928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter); 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci/* 27958c2ecf20Sopenharmony_ci * set power state of the codec, and return the power state 27968c2ecf20Sopenharmony_ci */ 27978c2ecf20Sopenharmony_cistatic unsigned int hda_set_power_state(struct hda_codec *codec, 27988c2ecf20Sopenharmony_ci unsigned int power_state) 27998c2ecf20Sopenharmony_ci{ 28008c2ecf20Sopenharmony_ci hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg; 28018c2ecf20Sopenharmony_ci int count; 28028c2ecf20Sopenharmony_ci unsigned int state; 28038c2ecf20Sopenharmony_ci int flags = 0; 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci /* this delay seems necessary to avoid click noise at power-down */ 28068c2ecf20Sopenharmony_ci if (power_state == AC_PWRST_D3) { 28078c2ecf20Sopenharmony_ci if (codec->depop_delay < 0) 28088c2ecf20Sopenharmony_ci msleep(codec_has_epss(codec) ? 10 : 100); 28098c2ecf20Sopenharmony_ci else if (codec->depop_delay > 0) 28108c2ecf20Sopenharmony_ci msleep(codec->depop_delay); 28118c2ecf20Sopenharmony_ci flags = HDA_RW_NO_RESPONSE_FALLBACK; 28128c2ecf20Sopenharmony_ci } 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci /* repeat power states setting at most 10 times*/ 28158c2ecf20Sopenharmony_ci for (count = 0; count < 10; count++) { 28168c2ecf20Sopenharmony_ci if (codec->patch_ops.set_power_state) 28178c2ecf20Sopenharmony_ci codec->patch_ops.set_power_state(codec, fg, 28188c2ecf20Sopenharmony_ci power_state); 28198c2ecf20Sopenharmony_ci else { 28208c2ecf20Sopenharmony_ci state = power_state; 28218c2ecf20Sopenharmony_ci if (codec->power_filter) 28228c2ecf20Sopenharmony_ci state = codec->power_filter(codec, fg, state); 28238c2ecf20Sopenharmony_ci if (state == power_state || power_state != AC_PWRST_D3) 28248c2ecf20Sopenharmony_ci snd_hda_codec_read(codec, fg, flags, 28258c2ecf20Sopenharmony_ci AC_VERB_SET_POWER_STATE, 28268c2ecf20Sopenharmony_ci state); 28278c2ecf20Sopenharmony_ci snd_hda_codec_set_power_to_all(codec, fg, power_state); 28288c2ecf20Sopenharmony_ci } 28298c2ecf20Sopenharmony_ci state = snd_hda_sync_power_state(codec, fg, power_state); 28308c2ecf20Sopenharmony_ci if (!(state & AC_PWRST_ERROR)) 28318c2ecf20Sopenharmony_ci break; 28328c2ecf20Sopenharmony_ci } 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci return state; 28358c2ecf20Sopenharmony_ci} 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_ci/* sync power states of all widgets; 28388c2ecf20Sopenharmony_ci * this is called at the end of codec parsing 28398c2ecf20Sopenharmony_ci */ 28408c2ecf20Sopenharmony_cistatic void sync_power_up_states(struct hda_codec *codec) 28418c2ecf20Sopenharmony_ci{ 28428c2ecf20Sopenharmony_ci hda_nid_t nid; 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_ci /* don't care if no filter is used */ 28458c2ecf20Sopenharmony_ci if (!codec->power_filter) 28468c2ecf20Sopenharmony_ci return; 28478c2ecf20Sopenharmony_ci 28488c2ecf20Sopenharmony_ci for_each_hda_codec_node(nid, codec) { 28498c2ecf20Sopenharmony_ci unsigned int wcaps = get_wcaps(codec, nid); 28508c2ecf20Sopenharmony_ci unsigned int target; 28518c2ecf20Sopenharmony_ci if (!(wcaps & AC_WCAP_POWER)) 28528c2ecf20Sopenharmony_ci continue; 28538c2ecf20Sopenharmony_ci target = codec->power_filter(codec, nid, AC_PWRST_D0); 28548c2ecf20Sopenharmony_ci if (target == AC_PWRST_D0) 28558c2ecf20Sopenharmony_ci continue; 28568c2ecf20Sopenharmony_ci if (!snd_hda_check_power_state(codec, nid, target)) 28578c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, nid, 0, 28588c2ecf20Sopenharmony_ci AC_VERB_SET_POWER_STATE, target); 28598c2ecf20Sopenharmony_ci } 28608c2ecf20Sopenharmony_ci} 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_HDA_RECONFIG 28638c2ecf20Sopenharmony_ci/* execute additional init verbs */ 28648c2ecf20Sopenharmony_cistatic void hda_exec_init_verbs(struct hda_codec *codec) 28658c2ecf20Sopenharmony_ci{ 28668c2ecf20Sopenharmony_ci if (codec->init_verbs.list) 28678c2ecf20Sopenharmony_ci snd_hda_sequence_write(codec, codec->init_verbs.list); 28688c2ecf20Sopenharmony_ci} 28698c2ecf20Sopenharmony_ci#else 28708c2ecf20Sopenharmony_cistatic inline void hda_exec_init_verbs(struct hda_codec *codec) {} 28718c2ecf20Sopenharmony_ci#endif 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 28748c2ecf20Sopenharmony_ci/* update the power on/off account with the current jiffies */ 28758c2ecf20Sopenharmony_cistatic void update_power_acct(struct hda_codec *codec, bool on) 28768c2ecf20Sopenharmony_ci{ 28778c2ecf20Sopenharmony_ci unsigned long delta = jiffies - codec->power_jiffies; 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci if (on) 28808c2ecf20Sopenharmony_ci codec->power_on_acct += delta; 28818c2ecf20Sopenharmony_ci else 28828c2ecf20Sopenharmony_ci codec->power_off_acct += delta; 28838c2ecf20Sopenharmony_ci codec->power_jiffies += delta; 28848c2ecf20Sopenharmony_ci} 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_civoid snd_hda_update_power_acct(struct hda_codec *codec) 28878c2ecf20Sopenharmony_ci{ 28888c2ecf20Sopenharmony_ci update_power_acct(codec, hda_codec_is_power_on(codec)); 28898c2ecf20Sopenharmony_ci} 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci/* 28928c2ecf20Sopenharmony_ci * call suspend and power-down; used both from PM and power-save 28938c2ecf20Sopenharmony_ci * this function returns the power state in the end 28948c2ecf20Sopenharmony_ci */ 28958c2ecf20Sopenharmony_cistatic unsigned int hda_call_codec_suspend(struct hda_codec *codec) 28968c2ecf20Sopenharmony_ci{ 28978c2ecf20Sopenharmony_ci unsigned int state; 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci snd_hdac_enter_pm(&codec->core); 29008c2ecf20Sopenharmony_ci if (codec->patch_ops.suspend) 29018c2ecf20Sopenharmony_ci codec->patch_ops.suspend(codec); 29028c2ecf20Sopenharmony_ci hda_cleanup_all_streams(codec); 29038c2ecf20Sopenharmony_ci state = hda_set_power_state(codec, AC_PWRST_D3); 29048c2ecf20Sopenharmony_ci update_power_acct(codec, true); 29058c2ecf20Sopenharmony_ci snd_hdac_leave_pm(&codec->core); 29068c2ecf20Sopenharmony_ci return state; 29078c2ecf20Sopenharmony_ci} 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_ci/* 29108c2ecf20Sopenharmony_ci * kick up codec; used both from PM and power-save 29118c2ecf20Sopenharmony_ci */ 29128c2ecf20Sopenharmony_cistatic void hda_call_codec_resume(struct hda_codec *codec) 29138c2ecf20Sopenharmony_ci{ 29148c2ecf20Sopenharmony_ci snd_hdac_enter_pm(&codec->core); 29158c2ecf20Sopenharmony_ci if (codec->core.regmap) 29168c2ecf20Sopenharmony_ci regcache_mark_dirty(codec->core.regmap); 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci codec->power_jiffies = jiffies; 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci hda_set_power_state(codec, AC_PWRST_D0); 29218c2ecf20Sopenharmony_ci restore_shutup_pins(codec); 29228c2ecf20Sopenharmony_ci hda_exec_init_verbs(codec); 29238c2ecf20Sopenharmony_ci snd_hda_jack_set_dirty_all(codec); 29248c2ecf20Sopenharmony_ci if (codec->patch_ops.resume) 29258c2ecf20Sopenharmony_ci codec->patch_ops.resume(codec); 29268c2ecf20Sopenharmony_ci else { 29278c2ecf20Sopenharmony_ci if (codec->patch_ops.init) 29288c2ecf20Sopenharmony_ci codec->patch_ops.init(codec); 29298c2ecf20Sopenharmony_ci snd_hda_regmap_sync(codec); 29308c2ecf20Sopenharmony_ci } 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_ci if (codec->jackpoll_interval) 29338c2ecf20Sopenharmony_ci hda_jackpoll_work(&codec->jackpoll_work.work); 29348c2ecf20Sopenharmony_ci else 29358c2ecf20Sopenharmony_ci snd_hda_jack_report_sync(codec); 29368c2ecf20Sopenharmony_ci codec->core.dev.power.power_state = PMSG_ON; 29378c2ecf20Sopenharmony_ci snd_hdac_leave_pm(&codec->core); 29388c2ecf20Sopenharmony_ci} 29398c2ecf20Sopenharmony_ci 29408c2ecf20Sopenharmony_cistatic int hda_codec_runtime_suspend(struct device *dev) 29418c2ecf20Sopenharmony_ci{ 29428c2ecf20Sopenharmony_ci struct hda_codec *codec = dev_to_hda_codec(dev); 29438c2ecf20Sopenharmony_ci unsigned int state; 29448c2ecf20Sopenharmony_ci 29458c2ecf20Sopenharmony_ci /* Nothing to do if card registration fails and the component driver never probes */ 29468c2ecf20Sopenharmony_ci if (!codec->card) 29478c2ecf20Sopenharmony_ci return 0; 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&codec->jackpoll_work); 29508c2ecf20Sopenharmony_ci state = hda_call_codec_suspend(codec); 29518c2ecf20Sopenharmony_ci if (codec->link_down_at_suspend || 29528c2ecf20Sopenharmony_ci (codec_has_clkstop(codec) && codec_has_epss(codec) && 29538c2ecf20Sopenharmony_ci (state & AC_PWRST_CLK_STOP_OK))) 29548c2ecf20Sopenharmony_ci snd_hdac_codec_link_down(&codec->core); 29558c2ecf20Sopenharmony_ci codec_display_power(codec, false); 29568c2ecf20Sopenharmony_ci return 0; 29578c2ecf20Sopenharmony_ci} 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_cistatic int hda_codec_runtime_resume(struct device *dev) 29608c2ecf20Sopenharmony_ci{ 29618c2ecf20Sopenharmony_ci struct hda_codec *codec = dev_to_hda_codec(dev); 29628c2ecf20Sopenharmony_ci 29638c2ecf20Sopenharmony_ci /* Nothing to do if card registration fails and the component driver never probes */ 29648c2ecf20Sopenharmony_ci if (!codec->card) 29658c2ecf20Sopenharmony_ci return 0; 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_ci codec_display_power(codec, true); 29688c2ecf20Sopenharmony_ci snd_hdac_codec_link_up(&codec->core); 29698c2ecf20Sopenharmony_ci hda_call_codec_resume(codec); 29708c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 29718c2ecf20Sopenharmony_ci return 0; 29728c2ecf20Sopenharmony_ci} 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 29778c2ecf20Sopenharmony_cistatic int hda_codec_pm_prepare(struct device *dev) 29788c2ecf20Sopenharmony_ci{ 29798c2ecf20Sopenharmony_ci dev->power.power_state = PMSG_SUSPEND; 29808c2ecf20Sopenharmony_ci return pm_runtime_suspended(dev); 29818c2ecf20Sopenharmony_ci} 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_cistatic void hda_codec_pm_complete(struct device *dev) 29848c2ecf20Sopenharmony_ci{ 29858c2ecf20Sopenharmony_ci struct hda_codec *codec = dev_to_hda_codec(dev); 29868c2ecf20Sopenharmony_ci 29878c2ecf20Sopenharmony_ci /* If no other pm-functions are called between prepare() and complete() */ 29888c2ecf20Sopenharmony_ci if (dev->power.power_state.event == PM_EVENT_SUSPEND) 29898c2ecf20Sopenharmony_ci dev->power.power_state = PMSG_RESUME; 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci if (pm_runtime_suspended(dev) && (codec->jackpoll_interval || 29928c2ecf20Sopenharmony_ci hda_codec_need_resume(codec) || codec->forced_resume)) 29938c2ecf20Sopenharmony_ci pm_request_resume(dev); 29948c2ecf20Sopenharmony_ci} 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_cistatic int hda_codec_pm_suspend(struct device *dev) 29978c2ecf20Sopenharmony_ci{ 29988c2ecf20Sopenharmony_ci dev->power.power_state = PMSG_SUSPEND; 29998c2ecf20Sopenharmony_ci return pm_runtime_force_suspend(dev); 30008c2ecf20Sopenharmony_ci} 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_cistatic int hda_codec_pm_resume(struct device *dev) 30038c2ecf20Sopenharmony_ci{ 30048c2ecf20Sopenharmony_ci dev->power.power_state = PMSG_RESUME; 30058c2ecf20Sopenharmony_ci return pm_runtime_force_resume(dev); 30068c2ecf20Sopenharmony_ci} 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_cistatic int hda_codec_pm_freeze(struct device *dev) 30098c2ecf20Sopenharmony_ci{ 30108c2ecf20Sopenharmony_ci dev->power.power_state = PMSG_FREEZE; 30118c2ecf20Sopenharmony_ci return pm_runtime_force_suspend(dev); 30128c2ecf20Sopenharmony_ci} 30138c2ecf20Sopenharmony_ci 30148c2ecf20Sopenharmony_cistatic int hda_codec_pm_thaw(struct device *dev) 30158c2ecf20Sopenharmony_ci{ 30168c2ecf20Sopenharmony_ci dev->power.power_state = PMSG_THAW; 30178c2ecf20Sopenharmony_ci return pm_runtime_force_resume(dev); 30188c2ecf20Sopenharmony_ci} 30198c2ecf20Sopenharmony_ci 30208c2ecf20Sopenharmony_cistatic int hda_codec_pm_restore(struct device *dev) 30218c2ecf20Sopenharmony_ci{ 30228c2ecf20Sopenharmony_ci dev->power.power_state = PMSG_RESTORE; 30238c2ecf20Sopenharmony_ci return pm_runtime_force_resume(dev); 30248c2ecf20Sopenharmony_ci} 30258c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 30268c2ecf20Sopenharmony_ci 30278c2ecf20Sopenharmony_ci/* referred in hda_bind.c */ 30288c2ecf20Sopenharmony_ciconst struct dev_pm_ops hda_codec_driver_pm = { 30298c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 30308c2ecf20Sopenharmony_ci .prepare = hda_codec_pm_prepare, 30318c2ecf20Sopenharmony_ci .complete = hda_codec_pm_complete, 30328c2ecf20Sopenharmony_ci .suspend = hda_codec_pm_suspend, 30338c2ecf20Sopenharmony_ci .resume = hda_codec_pm_resume, 30348c2ecf20Sopenharmony_ci .freeze = hda_codec_pm_freeze, 30358c2ecf20Sopenharmony_ci .thaw = hda_codec_pm_thaw, 30368c2ecf20Sopenharmony_ci .poweroff = hda_codec_pm_suspend, 30378c2ecf20Sopenharmony_ci .restore = hda_codec_pm_restore, 30388c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 30398c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, 30408c2ecf20Sopenharmony_ci NULL) 30418c2ecf20Sopenharmony_ci}; 30428c2ecf20Sopenharmony_ci 30438c2ecf20Sopenharmony_ci/* 30448c2ecf20Sopenharmony_ci * add standard channel maps if not specified 30458c2ecf20Sopenharmony_ci */ 30468c2ecf20Sopenharmony_cistatic int add_std_chmaps(struct hda_codec *codec) 30478c2ecf20Sopenharmony_ci{ 30488c2ecf20Sopenharmony_ci struct hda_pcm *pcm; 30498c2ecf20Sopenharmony_ci int str, err; 30508c2ecf20Sopenharmony_ci 30518c2ecf20Sopenharmony_ci list_for_each_entry(pcm, &codec->pcm_list_head, list) { 30528c2ecf20Sopenharmony_ci for (str = 0; str < 2; str++) { 30538c2ecf20Sopenharmony_ci struct hda_pcm_stream *hinfo = &pcm->stream[str]; 30548c2ecf20Sopenharmony_ci struct snd_pcm_chmap *chmap; 30558c2ecf20Sopenharmony_ci const struct snd_pcm_chmap_elem *elem; 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci if (!pcm->pcm || pcm->own_chmap || !hinfo->substreams) 30588c2ecf20Sopenharmony_ci continue; 30598c2ecf20Sopenharmony_ci elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps; 30608c2ecf20Sopenharmony_ci err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem, 30618c2ecf20Sopenharmony_ci hinfo->channels_max, 30628c2ecf20Sopenharmony_ci 0, &chmap); 30638c2ecf20Sopenharmony_ci if (err < 0) 30648c2ecf20Sopenharmony_ci return err; 30658c2ecf20Sopenharmony_ci chmap->channel_mask = SND_PCM_CHMAP_MASK_2468; 30668c2ecf20Sopenharmony_ci } 30678c2ecf20Sopenharmony_ci } 30688c2ecf20Sopenharmony_ci return 0; 30698c2ecf20Sopenharmony_ci} 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_ci/* default channel maps for 2.1 speakers; 30728c2ecf20Sopenharmony_ci * since HD-audio supports only stereo, odd number channels are omitted 30738c2ecf20Sopenharmony_ci */ 30748c2ecf20Sopenharmony_ciconst struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[] = { 30758c2ecf20Sopenharmony_ci { .channels = 2, 30768c2ecf20Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, 30778c2ecf20Sopenharmony_ci { .channels = 4, 30788c2ecf20Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 30798c2ecf20Sopenharmony_ci SNDRV_CHMAP_LFE, SNDRV_CHMAP_LFE } }, 30808c2ecf20Sopenharmony_ci { } 30818c2ecf20Sopenharmony_ci}; 30828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps); 30838c2ecf20Sopenharmony_ci 30848c2ecf20Sopenharmony_ciint snd_hda_codec_build_controls(struct hda_codec *codec) 30858c2ecf20Sopenharmony_ci{ 30868c2ecf20Sopenharmony_ci int err = 0; 30878c2ecf20Sopenharmony_ci hda_exec_init_verbs(codec); 30888c2ecf20Sopenharmony_ci /* continue to initialize... */ 30898c2ecf20Sopenharmony_ci if (codec->patch_ops.init) 30908c2ecf20Sopenharmony_ci err = codec->patch_ops.init(codec); 30918c2ecf20Sopenharmony_ci if (!err && codec->patch_ops.build_controls) 30928c2ecf20Sopenharmony_ci err = codec->patch_ops.build_controls(codec); 30938c2ecf20Sopenharmony_ci if (err < 0) 30948c2ecf20Sopenharmony_ci return err; 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci /* we create chmaps here instead of build_pcms */ 30978c2ecf20Sopenharmony_ci err = add_std_chmaps(codec); 30988c2ecf20Sopenharmony_ci if (err < 0) 30998c2ecf20Sopenharmony_ci return err; 31008c2ecf20Sopenharmony_ci 31018c2ecf20Sopenharmony_ci if (codec->jackpoll_interval) 31028c2ecf20Sopenharmony_ci hda_jackpoll_work(&codec->jackpoll_work.work); 31038c2ecf20Sopenharmony_ci else 31048c2ecf20Sopenharmony_ci snd_hda_jack_report_sync(codec); /* call at the last init point */ 31058c2ecf20Sopenharmony_ci sync_power_up_states(codec); 31068c2ecf20Sopenharmony_ci return 0; 31078c2ecf20Sopenharmony_ci} 31088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_build_controls); 31098c2ecf20Sopenharmony_ci 31108c2ecf20Sopenharmony_ci/* 31118c2ecf20Sopenharmony_ci * PCM stuff 31128c2ecf20Sopenharmony_ci */ 31138c2ecf20Sopenharmony_cistatic int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo, 31148c2ecf20Sopenharmony_ci struct hda_codec *codec, 31158c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 31168c2ecf20Sopenharmony_ci{ 31178c2ecf20Sopenharmony_ci return 0; 31188c2ecf20Sopenharmony_ci} 31198c2ecf20Sopenharmony_ci 31208c2ecf20Sopenharmony_cistatic int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo, 31218c2ecf20Sopenharmony_ci struct hda_codec *codec, 31228c2ecf20Sopenharmony_ci unsigned int stream_tag, 31238c2ecf20Sopenharmony_ci unsigned int format, 31248c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 31258c2ecf20Sopenharmony_ci{ 31268c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); 31278c2ecf20Sopenharmony_ci return 0; 31288c2ecf20Sopenharmony_ci} 31298c2ecf20Sopenharmony_ci 31308c2ecf20Sopenharmony_cistatic int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo, 31318c2ecf20Sopenharmony_ci struct hda_codec *codec, 31328c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 31338c2ecf20Sopenharmony_ci{ 31348c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, hinfo->nid); 31358c2ecf20Sopenharmony_ci return 0; 31368c2ecf20Sopenharmony_ci} 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_cistatic int set_pcm_default_values(struct hda_codec *codec, 31398c2ecf20Sopenharmony_ci struct hda_pcm_stream *info) 31408c2ecf20Sopenharmony_ci{ 31418c2ecf20Sopenharmony_ci int err; 31428c2ecf20Sopenharmony_ci 31438c2ecf20Sopenharmony_ci /* query support PCM information from the given NID */ 31448c2ecf20Sopenharmony_ci if (info->nid && (!info->rates || !info->formats)) { 31458c2ecf20Sopenharmony_ci err = snd_hda_query_supported_pcm(codec, info->nid, 31468c2ecf20Sopenharmony_ci info->rates ? NULL : &info->rates, 31478c2ecf20Sopenharmony_ci info->formats ? NULL : &info->formats, 31488c2ecf20Sopenharmony_ci info->maxbps ? NULL : &info->maxbps); 31498c2ecf20Sopenharmony_ci if (err < 0) 31508c2ecf20Sopenharmony_ci return err; 31518c2ecf20Sopenharmony_ci } 31528c2ecf20Sopenharmony_ci if (info->ops.open == NULL) 31538c2ecf20Sopenharmony_ci info->ops.open = hda_pcm_default_open_close; 31548c2ecf20Sopenharmony_ci if (info->ops.close == NULL) 31558c2ecf20Sopenharmony_ci info->ops.close = hda_pcm_default_open_close; 31568c2ecf20Sopenharmony_ci if (info->ops.prepare == NULL) { 31578c2ecf20Sopenharmony_ci if (snd_BUG_ON(!info->nid)) 31588c2ecf20Sopenharmony_ci return -EINVAL; 31598c2ecf20Sopenharmony_ci info->ops.prepare = hda_pcm_default_prepare; 31608c2ecf20Sopenharmony_ci } 31618c2ecf20Sopenharmony_ci if (info->ops.cleanup == NULL) { 31628c2ecf20Sopenharmony_ci if (snd_BUG_ON(!info->nid)) 31638c2ecf20Sopenharmony_ci return -EINVAL; 31648c2ecf20Sopenharmony_ci info->ops.cleanup = hda_pcm_default_cleanup; 31658c2ecf20Sopenharmony_ci } 31668c2ecf20Sopenharmony_ci return 0; 31678c2ecf20Sopenharmony_ci} 31688c2ecf20Sopenharmony_ci 31698c2ecf20Sopenharmony_ci/* 31708c2ecf20Sopenharmony_ci * codec prepare/cleanup entries 31718c2ecf20Sopenharmony_ci */ 31728c2ecf20Sopenharmony_ci/** 31738c2ecf20Sopenharmony_ci * snd_hda_codec_prepare - Prepare a stream 31748c2ecf20Sopenharmony_ci * @codec: the HDA codec 31758c2ecf20Sopenharmony_ci * @hinfo: PCM information 31768c2ecf20Sopenharmony_ci * @stream: stream tag to assign 31778c2ecf20Sopenharmony_ci * @format: format id to assign 31788c2ecf20Sopenharmony_ci * @substream: PCM substream to assign 31798c2ecf20Sopenharmony_ci * 31808c2ecf20Sopenharmony_ci * Calls the prepare callback set by the codec with the given arguments. 31818c2ecf20Sopenharmony_ci * Clean up the inactive streams when successful. 31828c2ecf20Sopenharmony_ci */ 31838c2ecf20Sopenharmony_ciint snd_hda_codec_prepare(struct hda_codec *codec, 31848c2ecf20Sopenharmony_ci struct hda_pcm_stream *hinfo, 31858c2ecf20Sopenharmony_ci unsigned int stream, 31868c2ecf20Sopenharmony_ci unsigned int format, 31878c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 31888c2ecf20Sopenharmony_ci{ 31898c2ecf20Sopenharmony_ci int ret; 31908c2ecf20Sopenharmony_ci mutex_lock(&codec->bus->prepare_mutex); 31918c2ecf20Sopenharmony_ci if (hinfo->ops.prepare) 31928c2ecf20Sopenharmony_ci ret = hinfo->ops.prepare(hinfo, codec, stream, format, 31938c2ecf20Sopenharmony_ci substream); 31948c2ecf20Sopenharmony_ci else 31958c2ecf20Sopenharmony_ci ret = -ENODEV; 31968c2ecf20Sopenharmony_ci if (ret >= 0) 31978c2ecf20Sopenharmony_ci purify_inactive_streams(codec); 31988c2ecf20Sopenharmony_ci mutex_unlock(&codec->bus->prepare_mutex); 31998c2ecf20Sopenharmony_ci return ret; 32008c2ecf20Sopenharmony_ci} 32018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_prepare); 32028c2ecf20Sopenharmony_ci 32038c2ecf20Sopenharmony_ci/** 32048c2ecf20Sopenharmony_ci * snd_hda_codec_cleanup - Clean up stream resources 32058c2ecf20Sopenharmony_ci * @codec: the HDA codec 32068c2ecf20Sopenharmony_ci * @hinfo: PCM information 32078c2ecf20Sopenharmony_ci * @substream: PCM substream 32088c2ecf20Sopenharmony_ci * 32098c2ecf20Sopenharmony_ci * Calls the cleanup callback set by the codec with the given arguments. 32108c2ecf20Sopenharmony_ci */ 32118c2ecf20Sopenharmony_civoid snd_hda_codec_cleanup(struct hda_codec *codec, 32128c2ecf20Sopenharmony_ci struct hda_pcm_stream *hinfo, 32138c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 32148c2ecf20Sopenharmony_ci{ 32158c2ecf20Sopenharmony_ci mutex_lock(&codec->bus->prepare_mutex); 32168c2ecf20Sopenharmony_ci if (hinfo->ops.cleanup) 32178c2ecf20Sopenharmony_ci hinfo->ops.cleanup(hinfo, codec, substream); 32188c2ecf20Sopenharmony_ci mutex_unlock(&codec->bus->prepare_mutex); 32198c2ecf20Sopenharmony_ci} 32208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_cleanup); 32218c2ecf20Sopenharmony_ci 32228c2ecf20Sopenharmony_ci/* global */ 32238c2ecf20Sopenharmony_ciconst char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { 32248c2ecf20Sopenharmony_ci "Audio", "SPDIF", "HDMI", "Modem" 32258c2ecf20Sopenharmony_ci}; 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci/* 32288c2ecf20Sopenharmony_ci * get the empty PCM device number to assign 32298c2ecf20Sopenharmony_ci */ 32308c2ecf20Sopenharmony_cistatic int get_empty_pcm_device(struct hda_bus *bus, unsigned int type) 32318c2ecf20Sopenharmony_ci{ 32328c2ecf20Sopenharmony_ci /* audio device indices; not linear to keep compatibility */ 32338c2ecf20Sopenharmony_ci /* assigned to static slots up to dev#10; if more needed, assign 32348c2ecf20Sopenharmony_ci * the later slot dynamically (when CONFIG_SND_DYNAMIC_MINORS=y) 32358c2ecf20Sopenharmony_ci */ 32368c2ecf20Sopenharmony_ci static const int audio_idx[HDA_PCM_NTYPES][5] = { 32378c2ecf20Sopenharmony_ci [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, 32388c2ecf20Sopenharmony_ci [HDA_PCM_TYPE_SPDIF] = { 1, -1 }, 32398c2ecf20Sopenharmony_ci [HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 }, 32408c2ecf20Sopenharmony_ci [HDA_PCM_TYPE_MODEM] = { 6, -1 }, 32418c2ecf20Sopenharmony_ci }; 32428c2ecf20Sopenharmony_ci int i; 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ci if (type >= HDA_PCM_NTYPES) { 32458c2ecf20Sopenharmony_ci dev_err(bus->card->dev, "Invalid PCM type %d\n", type); 32468c2ecf20Sopenharmony_ci return -EINVAL; 32478c2ecf20Sopenharmony_ci } 32488c2ecf20Sopenharmony_ci 32498c2ecf20Sopenharmony_ci for (i = 0; audio_idx[type][i] >= 0; i++) { 32508c2ecf20Sopenharmony_ci#ifndef CONFIG_SND_DYNAMIC_MINORS 32518c2ecf20Sopenharmony_ci if (audio_idx[type][i] >= 8) 32528c2ecf20Sopenharmony_ci break; 32538c2ecf20Sopenharmony_ci#endif 32548c2ecf20Sopenharmony_ci if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits)) 32558c2ecf20Sopenharmony_ci return audio_idx[type][i]; 32568c2ecf20Sopenharmony_ci } 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DYNAMIC_MINORS 32598c2ecf20Sopenharmony_ci /* non-fixed slots starting from 10 */ 32608c2ecf20Sopenharmony_ci for (i = 10; i < 32; i++) { 32618c2ecf20Sopenharmony_ci if (!test_and_set_bit(i, bus->pcm_dev_bits)) 32628c2ecf20Sopenharmony_ci return i; 32638c2ecf20Sopenharmony_ci } 32648c2ecf20Sopenharmony_ci#endif 32658c2ecf20Sopenharmony_ci 32668c2ecf20Sopenharmony_ci dev_warn(bus->card->dev, "Too many %s devices\n", 32678c2ecf20Sopenharmony_ci snd_hda_pcm_type_name[type]); 32688c2ecf20Sopenharmony_ci#ifndef CONFIG_SND_DYNAMIC_MINORS 32698c2ecf20Sopenharmony_ci dev_warn(bus->card->dev, 32708c2ecf20Sopenharmony_ci "Consider building the kernel with CONFIG_SND_DYNAMIC_MINORS=y\n"); 32718c2ecf20Sopenharmony_ci#endif 32728c2ecf20Sopenharmony_ci return -EAGAIN; 32738c2ecf20Sopenharmony_ci} 32748c2ecf20Sopenharmony_ci 32758c2ecf20Sopenharmony_ci/* call build_pcms ops of the given codec and set up the default parameters */ 32768c2ecf20Sopenharmony_ciint snd_hda_codec_parse_pcms(struct hda_codec *codec) 32778c2ecf20Sopenharmony_ci{ 32788c2ecf20Sopenharmony_ci struct hda_pcm *cpcm; 32798c2ecf20Sopenharmony_ci int err; 32808c2ecf20Sopenharmony_ci 32818c2ecf20Sopenharmony_ci if (!list_empty(&codec->pcm_list_head)) 32828c2ecf20Sopenharmony_ci return 0; /* already parsed */ 32838c2ecf20Sopenharmony_ci 32848c2ecf20Sopenharmony_ci if (!codec->patch_ops.build_pcms) 32858c2ecf20Sopenharmony_ci return 0; 32868c2ecf20Sopenharmony_ci 32878c2ecf20Sopenharmony_ci err = codec->patch_ops.build_pcms(codec); 32888c2ecf20Sopenharmony_ci if (err < 0) { 32898c2ecf20Sopenharmony_ci codec_err(codec, "cannot build PCMs for #%d (error %d)\n", 32908c2ecf20Sopenharmony_ci codec->core.addr, err); 32918c2ecf20Sopenharmony_ci return err; 32928c2ecf20Sopenharmony_ci } 32938c2ecf20Sopenharmony_ci 32948c2ecf20Sopenharmony_ci list_for_each_entry(cpcm, &codec->pcm_list_head, list) { 32958c2ecf20Sopenharmony_ci int stream; 32968c2ecf20Sopenharmony_ci 32978c2ecf20Sopenharmony_ci for (stream = 0; stream < 2; stream++) { 32988c2ecf20Sopenharmony_ci struct hda_pcm_stream *info = &cpcm->stream[stream]; 32998c2ecf20Sopenharmony_ci 33008c2ecf20Sopenharmony_ci if (!info->substreams) 33018c2ecf20Sopenharmony_ci continue; 33028c2ecf20Sopenharmony_ci err = set_pcm_default_values(codec, info); 33038c2ecf20Sopenharmony_ci if (err < 0) { 33048c2ecf20Sopenharmony_ci codec_warn(codec, 33058c2ecf20Sopenharmony_ci "fail to setup default for PCM %s\n", 33068c2ecf20Sopenharmony_ci cpcm->name); 33078c2ecf20Sopenharmony_ci return err; 33088c2ecf20Sopenharmony_ci } 33098c2ecf20Sopenharmony_ci } 33108c2ecf20Sopenharmony_ci } 33118c2ecf20Sopenharmony_ci 33128c2ecf20Sopenharmony_ci return 0; 33138c2ecf20Sopenharmony_ci} 33148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_parse_pcms); 33158c2ecf20Sopenharmony_ci 33168c2ecf20Sopenharmony_ci/* assign all PCMs of the given codec */ 33178c2ecf20Sopenharmony_ciint snd_hda_codec_build_pcms(struct hda_codec *codec) 33188c2ecf20Sopenharmony_ci{ 33198c2ecf20Sopenharmony_ci struct hda_bus *bus = codec->bus; 33208c2ecf20Sopenharmony_ci struct hda_pcm *cpcm; 33218c2ecf20Sopenharmony_ci int dev, err; 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_ci err = snd_hda_codec_parse_pcms(codec); 33248c2ecf20Sopenharmony_ci if (err < 0) 33258c2ecf20Sopenharmony_ci return err; 33268c2ecf20Sopenharmony_ci 33278c2ecf20Sopenharmony_ci /* attach a new PCM streams */ 33288c2ecf20Sopenharmony_ci list_for_each_entry(cpcm, &codec->pcm_list_head, list) { 33298c2ecf20Sopenharmony_ci if (cpcm->pcm) 33308c2ecf20Sopenharmony_ci continue; /* already attached */ 33318c2ecf20Sopenharmony_ci if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) 33328c2ecf20Sopenharmony_ci continue; /* no substreams assigned */ 33338c2ecf20Sopenharmony_ci 33348c2ecf20Sopenharmony_ci dev = get_empty_pcm_device(bus, cpcm->pcm_type); 33358c2ecf20Sopenharmony_ci if (dev < 0) { 33368c2ecf20Sopenharmony_ci cpcm->device = SNDRV_PCM_INVALID_DEVICE; 33378c2ecf20Sopenharmony_ci continue; /* no fatal error */ 33388c2ecf20Sopenharmony_ci } 33398c2ecf20Sopenharmony_ci cpcm->device = dev; 33408c2ecf20Sopenharmony_ci err = snd_hda_attach_pcm_stream(bus, codec, cpcm); 33418c2ecf20Sopenharmony_ci if (err < 0) { 33428c2ecf20Sopenharmony_ci codec_err(codec, 33438c2ecf20Sopenharmony_ci "cannot attach PCM stream %d for codec #%d\n", 33448c2ecf20Sopenharmony_ci dev, codec->core.addr); 33458c2ecf20Sopenharmony_ci continue; /* no fatal error */ 33468c2ecf20Sopenharmony_ci } 33478c2ecf20Sopenharmony_ci } 33488c2ecf20Sopenharmony_ci 33498c2ecf20Sopenharmony_ci return 0; 33508c2ecf20Sopenharmony_ci} 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci/** 33538c2ecf20Sopenharmony_ci * snd_hda_add_new_ctls - create controls from the array 33548c2ecf20Sopenharmony_ci * @codec: the HDA codec 33558c2ecf20Sopenharmony_ci * @knew: the array of struct snd_kcontrol_new 33568c2ecf20Sopenharmony_ci * 33578c2ecf20Sopenharmony_ci * This helper function creates and add new controls in the given array. 33588c2ecf20Sopenharmony_ci * The array must be terminated with an empty entry as terminator. 33598c2ecf20Sopenharmony_ci * 33608c2ecf20Sopenharmony_ci * Returns 0 if successful, or a negative error code. 33618c2ecf20Sopenharmony_ci */ 33628c2ecf20Sopenharmony_ciint snd_hda_add_new_ctls(struct hda_codec *codec, 33638c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *knew) 33648c2ecf20Sopenharmony_ci{ 33658c2ecf20Sopenharmony_ci int err; 33668c2ecf20Sopenharmony_ci 33678c2ecf20Sopenharmony_ci for (; knew->name; knew++) { 33688c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 33698c2ecf20Sopenharmony_ci int addr = 0, idx = 0; 33708c2ecf20Sopenharmony_ci if (knew->iface == (__force snd_ctl_elem_iface_t)-1) 33718c2ecf20Sopenharmony_ci continue; /* skip this codec private value */ 33728c2ecf20Sopenharmony_ci for (;;) { 33738c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(knew, codec); 33748c2ecf20Sopenharmony_ci if (!kctl) 33758c2ecf20Sopenharmony_ci return -ENOMEM; 33768c2ecf20Sopenharmony_ci if (addr > 0) 33778c2ecf20Sopenharmony_ci kctl->id.device = addr; 33788c2ecf20Sopenharmony_ci if (idx > 0) 33798c2ecf20Sopenharmony_ci kctl->id.index = idx; 33808c2ecf20Sopenharmony_ci err = snd_hda_ctl_add(codec, 0, kctl); 33818c2ecf20Sopenharmony_ci if (!err) 33828c2ecf20Sopenharmony_ci break; 33838c2ecf20Sopenharmony_ci /* try first with another device index corresponding to 33848c2ecf20Sopenharmony_ci * the codec addr; if it still fails (or it's the 33858c2ecf20Sopenharmony_ci * primary codec), then try another control index 33868c2ecf20Sopenharmony_ci */ 33878c2ecf20Sopenharmony_ci if (!addr && codec->core.addr) 33888c2ecf20Sopenharmony_ci addr = codec->core.addr; 33898c2ecf20Sopenharmony_ci else if (!idx && !knew->index) { 33908c2ecf20Sopenharmony_ci idx = find_empty_mixer_ctl_idx(codec, 33918c2ecf20Sopenharmony_ci knew->name, 0); 33928c2ecf20Sopenharmony_ci if (idx <= 0) 33938c2ecf20Sopenharmony_ci return err; 33948c2ecf20Sopenharmony_ci } else 33958c2ecf20Sopenharmony_ci return err; 33968c2ecf20Sopenharmony_ci } 33978c2ecf20Sopenharmony_ci } 33988c2ecf20Sopenharmony_ci return 0; 33998c2ecf20Sopenharmony_ci} 34008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_add_new_ctls); 34018c2ecf20Sopenharmony_ci 34028c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 34038c2ecf20Sopenharmony_cistatic void codec_set_power_save(struct hda_codec *codec, int delay) 34048c2ecf20Sopenharmony_ci{ 34058c2ecf20Sopenharmony_ci struct device *dev = hda_codec_dev(codec); 34068c2ecf20Sopenharmony_ci 34078c2ecf20Sopenharmony_ci if (delay == 0 && codec->auto_runtime_pm) 34088c2ecf20Sopenharmony_ci delay = 3000; 34098c2ecf20Sopenharmony_ci 34108c2ecf20Sopenharmony_ci if (delay > 0) { 34118c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, delay); 34128c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(dev); 34138c2ecf20Sopenharmony_ci pm_runtime_allow(dev); 34148c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(dev)) 34158c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 34168c2ecf20Sopenharmony_ci } else { 34178c2ecf20Sopenharmony_ci pm_runtime_dont_use_autosuspend(dev); 34188c2ecf20Sopenharmony_ci pm_runtime_forbid(dev); 34198c2ecf20Sopenharmony_ci } 34208c2ecf20Sopenharmony_ci} 34218c2ecf20Sopenharmony_ci 34228c2ecf20Sopenharmony_ci/** 34238c2ecf20Sopenharmony_ci * snd_hda_set_power_save - reprogram autosuspend for the given delay 34248c2ecf20Sopenharmony_ci * @bus: HD-audio bus 34258c2ecf20Sopenharmony_ci * @delay: autosuspend delay in msec, 0 = off 34268c2ecf20Sopenharmony_ci * 34278c2ecf20Sopenharmony_ci * Synchronize the runtime PM autosuspend state from the power_save option. 34288c2ecf20Sopenharmony_ci */ 34298c2ecf20Sopenharmony_civoid snd_hda_set_power_save(struct hda_bus *bus, int delay) 34308c2ecf20Sopenharmony_ci{ 34318c2ecf20Sopenharmony_ci struct hda_codec *c; 34328c2ecf20Sopenharmony_ci 34338c2ecf20Sopenharmony_ci list_for_each_codec(c, bus) 34348c2ecf20Sopenharmony_ci codec_set_power_save(c, delay); 34358c2ecf20Sopenharmony_ci} 34368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_set_power_save); 34378c2ecf20Sopenharmony_ci 34388c2ecf20Sopenharmony_ci/** 34398c2ecf20Sopenharmony_ci * snd_hda_check_amp_list_power - Check the amp list and update the power 34408c2ecf20Sopenharmony_ci * @codec: HD-audio codec 34418c2ecf20Sopenharmony_ci * @check: the object containing an AMP list and the status 34428c2ecf20Sopenharmony_ci * @nid: NID to check / update 34438c2ecf20Sopenharmony_ci * 34448c2ecf20Sopenharmony_ci * Check whether the given NID is in the amp list. If it's in the list, 34458c2ecf20Sopenharmony_ci * check the current AMP status, and update the power-status according 34468c2ecf20Sopenharmony_ci * to the mute status. 34478c2ecf20Sopenharmony_ci * 34488c2ecf20Sopenharmony_ci * This function is supposed to be set or called from the check_power_status 34498c2ecf20Sopenharmony_ci * patch ops. 34508c2ecf20Sopenharmony_ci */ 34518c2ecf20Sopenharmony_ciint snd_hda_check_amp_list_power(struct hda_codec *codec, 34528c2ecf20Sopenharmony_ci struct hda_loopback_check *check, 34538c2ecf20Sopenharmony_ci hda_nid_t nid) 34548c2ecf20Sopenharmony_ci{ 34558c2ecf20Sopenharmony_ci const struct hda_amp_list *p; 34568c2ecf20Sopenharmony_ci int ch, v; 34578c2ecf20Sopenharmony_ci 34588c2ecf20Sopenharmony_ci if (!check->amplist) 34598c2ecf20Sopenharmony_ci return 0; 34608c2ecf20Sopenharmony_ci for (p = check->amplist; p->nid; p++) { 34618c2ecf20Sopenharmony_ci if (p->nid == nid) 34628c2ecf20Sopenharmony_ci break; 34638c2ecf20Sopenharmony_ci } 34648c2ecf20Sopenharmony_ci if (!p->nid) 34658c2ecf20Sopenharmony_ci return 0; /* nothing changed */ 34668c2ecf20Sopenharmony_ci 34678c2ecf20Sopenharmony_ci for (p = check->amplist; p->nid; p++) { 34688c2ecf20Sopenharmony_ci for (ch = 0; ch < 2; ch++) { 34698c2ecf20Sopenharmony_ci v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, 34708c2ecf20Sopenharmony_ci p->idx); 34718c2ecf20Sopenharmony_ci if (!(v & HDA_AMP_MUTE) && v > 0) { 34728c2ecf20Sopenharmony_ci if (!check->power_on) { 34738c2ecf20Sopenharmony_ci check->power_on = 1; 34748c2ecf20Sopenharmony_ci snd_hda_power_up_pm(codec); 34758c2ecf20Sopenharmony_ci } 34768c2ecf20Sopenharmony_ci return 1; 34778c2ecf20Sopenharmony_ci } 34788c2ecf20Sopenharmony_ci } 34798c2ecf20Sopenharmony_ci } 34808c2ecf20Sopenharmony_ci if (check->power_on) { 34818c2ecf20Sopenharmony_ci check->power_on = 0; 34828c2ecf20Sopenharmony_ci snd_hda_power_down_pm(codec); 34838c2ecf20Sopenharmony_ci } 34848c2ecf20Sopenharmony_ci return 0; 34858c2ecf20Sopenharmony_ci} 34868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power); 34878c2ecf20Sopenharmony_ci#endif 34888c2ecf20Sopenharmony_ci 34898c2ecf20Sopenharmony_ci/* 34908c2ecf20Sopenharmony_ci * input MUX helper 34918c2ecf20Sopenharmony_ci */ 34928c2ecf20Sopenharmony_ci 34938c2ecf20Sopenharmony_ci/** 34948c2ecf20Sopenharmony_ci * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum 34958c2ecf20Sopenharmony_ci * @imux: imux helper object 34968c2ecf20Sopenharmony_ci * @uinfo: pointer to get/store the data 34978c2ecf20Sopenharmony_ci */ 34988c2ecf20Sopenharmony_ciint snd_hda_input_mux_info(const struct hda_input_mux *imux, 34998c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 35008c2ecf20Sopenharmony_ci{ 35018c2ecf20Sopenharmony_ci unsigned int index; 35028c2ecf20Sopenharmony_ci 35038c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 35048c2ecf20Sopenharmony_ci uinfo->count = 1; 35058c2ecf20Sopenharmony_ci uinfo->value.enumerated.items = imux->num_items; 35068c2ecf20Sopenharmony_ci if (!imux->num_items) 35078c2ecf20Sopenharmony_ci return 0; 35088c2ecf20Sopenharmony_ci index = uinfo->value.enumerated.item; 35098c2ecf20Sopenharmony_ci if (index >= imux->num_items) 35108c2ecf20Sopenharmony_ci index = imux->num_items - 1; 35118c2ecf20Sopenharmony_ci strcpy(uinfo->value.enumerated.name, imux->items[index].label); 35128c2ecf20Sopenharmony_ci return 0; 35138c2ecf20Sopenharmony_ci} 35148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_input_mux_info); 35158c2ecf20Sopenharmony_ci 35168c2ecf20Sopenharmony_ci/** 35178c2ecf20Sopenharmony_ci * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum 35188c2ecf20Sopenharmony_ci * @codec: the HDA codec 35198c2ecf20Sopenharmony_ci * @imux: imux helper object 35208c2ecf20Sopenharmony_ci * @ucontrol: pointer to get/store the data 35218c2ecf20Sopenharmony_ci * @nid: input mux NID 35228c2ecf20Sopenharmony_ci * @cur_val: pointer to get/store the current imux value 35238c2ecf20Sopenharmony_ci */ 35248c2ecf20Sopenharmony_ciint snd_hda_input_mux_put(struct hda_codec *codec, 35258c2ecf20Sopenharmony_ci const struct hda_input_mux *imux, 35268c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol, 35278c2ecf20Sopenharmony_ci hda_nid_t nid, 35288c2ecf20Sopenharmony_ci unsigned int *cur_val) 35298c2ecf20Sopenharmony_ci{ 35308c2ecf20Sopenharmony_ci unsigned int idx; 35318c2ecf20Sopenharmony_ci 35328c2ecf20Sopenharmony_ci if (!imux->num_items) 35338c2ecf20Sopenharmony_ci return 0; 35348c2ecf20Sopenharmony_ci idx = ucontrol->value.enumerated.item[0]; 35358c2ecf20Sopenharmony_ci if (idx >= imux->num_items) 35368c2ecf20Sopenharmony_ci idx = imux->num_items - 1; 35378c2ecf20Sopenharmony_ci if (*cur_val == idx) 35388c2ecf20Sopenharmony_ci return 0; 35398c2ecf20Sopenharmony_ci snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, 35408c2ecf20Sopenharmony_ci imux->items[idx].index); 35418c2ecf20Sopenharmony_ci *cur_val = idx; 35428c2ecf20Sopenharmony_ci return 1; 35438c2ecf20Sopenharmony_ci} 35448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_input_mux_put); 35458c2ecf20Sopenharmony_ci 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_ci/** 35488c2ecf20Sopenharmony_ci * snd_hda_enum_helper_info - Helper for simple enum ctls 35498c2ecf20Sopenharmony_ci * @kcontrol: ctl element 35508c2ecf20Sopenharmony_ci * @uinfo: pointer to get/store the data 35518c2ecf20Sopenharmony_ci * @num_items: number of enum items 35528c2ecf20Sopenharmony_ci * @texts: enum item string array 35538c2ecf20Sopenharmony_ci * 35548c2ecf20Sopenharmony_ci * process kcontrol info callback of a simple string enum array 35558c2ecf20Sopenharmony_ci * when @num_items is 0 or @texts is NULL, assume a boolean enum array 35568c2ecf20Sopenharmony_ci */ 35578c2ecf20Sopenharmony_ciint snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol, 35588c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo, 35598c2ecf20Sopenharmony_ci int num_items, const char * const *texts) 35608c2ecf20Sopenharmony_ci{ 35618c2ecf20Sopenharmony_ci static const char * const texts_default[] = { 35628c2ecf20Sopenharmony_ci "Disabled", "Enabled" 35638c2ecf20Sopenharmony_ci }; 35648c2ecf20Sopenharmony_ci 35658c2ecf20Sopenharmony_ci if (!texts || !num_items) { 35668c2ecf20Sopenharmony_ci num_items = 2; 35678c2ecf20Sopenharmony_ci texts = texts_default; 35688c2ecf20Sopenharmony_ci } 35698c2ecf20Sopenharmony_ci 35708c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, num_items, texts); 35718c2ecf20Sopenharmony_ci} 35728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_enum_helper_info); 35738c2ecf20Sopenharmony_ci 35748c2ecf20Sopenharmony_ci/* 35758c2ecf20Sopenharmony_ci * Multi-channel / digital-out PCM helper functions 35768c2ecf20Sopenharmony_ci */ 35778c2ecf20Sopenharmony_ci 35788c2ecf20Sopenharmony_ci/* setup SPDIF output stream */ 35798c2ecf20Sopenharmony_cistatic void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, 35808c2ecf20Sopenharmony_ci unsigned int stream_tag, unsigned int format) 35818c2ecf20Sopenharmony_ci{ 35828c2ecf20Sopenharmony_ci struct hda_spdif_out *spdif; 35838c2ecf20Sopenharmony_ci unsigned int curr_fmt; 35848c2ecf20Sopenharmony_ci bool reset; 35858c2ecf20Sopenharmony_ci 35868c2ecf20Sopenharmony_ci spdif = snd_hda_spdif_out_of_nid(codec, nid); 35878c2ecf20Sopenharmony_ci /* Add sanity check to pass klockwork check. 35888c2ecf20Sopenharmony_ci * This should never happen. 35898c2ecf20Sopenharmony_ci */ 35908c2ecf20Sopenharmony_ci if (WARN_ON(spdif == NULL)) 35918c2ecf20Sopenharmony_ci return; 35928c2ecf20Sopenharmony_ci 35938c2ecf20Sopenharmony_ci curr_fmt = snd_hda_codec_read(codec, nid, 0, 35948c2ecf20Sopenharmony_ci AC_VERB_GET_STREAM_FORMAT, 0); 35958c2ecf20Sopenharmony_ci reset = codec->spdif_status_reset && 35968c2ecf20Sopenharmony_ci (spdif->ctls & AC_DIG1_ENABLE) && 35978c2ecf20Sopenharmony_ci curr_fmt != format; 35988c2ecf20Sopenharmony_ci 35998c2ecf20Sopenharmony_ci /* turn off SPDIF if needed; otherwise the IEC958 bits won't be 36008c2ecf20Sopenharmony_ci updated */ 36018c2ecf20Sopenharmony_ci if (reset) 36028c2ecf20Sopenharmony_ci set_dig_out_convert(codec, nid, 36038c2ecf20Sopenharmony_ci spdif->ctls & ~AC_DIG1_ENABLE & 0xff, 36048c2ecf20Sopenharmony_ci -1); 36058c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); 36068c2ecf20Sopenharmony_ci if (codec->follower_dig_outs) { 36078c2ecf20Sopenharmony_ci const hda_nid_t *d; 36088c2ecf20Sopenharmony_ci for (d = codec->follower_dig_outs; *d; d++) 36098c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, *d, stream_tag, 0, 36108c2ecf20Sopenharmony_ci format); 36118c2ecf20Sopenharmony_ci } 36128c2ecf20Sopenharmony_ci /* turn on again (if needed) */ 36138c2ecf20Sopenharmony_ci if (reset) 36148c2ecf20Sopenharmony_ci set_dig_out_convert(codec, nid, 36158c2ecf20Sopenharmony_ci spdif->ctls & 0xff, -1); 36168c2ecf20Sopenharmony_ci} 36178c2ecf20Sopenharmony_ci 36188c2ecf20Sopenharmony_cistatic void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) 36198c2ecf20Sopenharmony_ci{ 36208c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, nid); 36218c2ecf20Sopenharmony_ci if (codec->follower_dig_outs) { 36228c2ecf20Sopenharmony_ci const hda_nid_t *d; 36238c2ecf20Sopenharmony_ci for (d = codec->follower_dig_outs; *d; d++) 36248c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, *d); 36258c2ecf20Sopenharmony_ci } 36268c2ecf20Sopenharmony_ci} 36278c2ecf20Sopenharmony_ci 36288c2ecf20Sopenharmony_ci/** 36298c2ecf20Sopenharmony_ci * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode 36308c2ecf20Sopenharmony_ci * @codec: the HDA codec 36318c2ecf20Sopenharmony_ci * @mout: hda_multi_out object 36328c2ecf20Sopenharmony_ci */ 36338c2ecf20Sopenharmony_ciint snd_hda_multi_out_dig_open(struct hda_codec *codec, 36348c2ecf20Sopenharmony_ci struct hda_multi_out *mout) 36358c2ecf20Sopenharmony_ci{ 36368c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 36378c2ecf20Sopenharmony_ci if (mout->dig_out_used == HDA_DIG_ANALOG_DUP) 36388c2ecf20Sopenharmony_ci /* already opened as analog dup; reset it once */ 36398c2ecf20Sopenharmony_ci cleanup_dig_out_stream(codec, mout->dig_out_nid); 36408c2ecf20Sopenharmony_ci mout->dig_out_used = HDA_DIG_EXCLUSIVE; 36418c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 36428c2ecf20Sopenharmony_ci return 0; 36438c2ecf20Sopenharmony_ci} 36448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_open); 36458c2ecf20Sopenharmony_ci 36468c2ecf20Sopenharmony_ci/** 36478c2ecf20Sopenharmony_ci * snd_hda_multi_out_dig_prepare - prepare the digital out stream 36488c2ecf20Sopenharmony_ci * @codec: the HDA codec 36498c2ecf20Sopenharmony_ci * @mout: hda_multi_out object 36508c2ecf20Sopenharmony_ci * @stream_tag: stream tag to assign 36518c2ecf20Sopenharmony_ci * @format: format id to assign 36528c2ecf20Sopenharmony_ci * @substream: PCM substream to assign 36538c2ecf20Sopenharmony_ci */ 36548c2ecf20Sopenharmony_ciint snd_hda_multi_out_dig_prepare(struct hda_codec *codec, 36558c2ecf20Sopenharmony_ci struct hda_multi_out *mout, 36568c2ecf20Sopenharmony_ci unsigned int stream_tag, 36578c2ecf20Sopenharmony_ci unsigned int format, 36588c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 36598c2ecf20Sopenharmony_ci{ 36608c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 36618c2ecf20Sopenharmony_ci setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format); 36628c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 36638c2ecf20Sopenharmony_ci return 0; 36648c2ecf20Sopenharmony_ci} 36658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare); 36668c2ecf20Sopenharmony_ci 36678c2ecf20Sopenharmony_ci/** 36688c2ecf20Sopenharmony_ci * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream 36698c2ecf20Sopenharmony_ci * @codec: the HDA codec 36708c2ecf20Sopenharmony_ci * @mout: hda_multi_out object 36718c2ecf20Sopenharmony_ci */ 36728c2ecf20Sopenharmony_ciint snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, 36738c2ecf20Sopenharmony_ci struct hda_multi_out *mout) 36748c2ecf20Sopenharmony_ci{ 36758c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 36768c2ecf20Sopenharmony_ci cleanup_dig_out_stream(codec, mout->dig_out_nid); 36778c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 36788c2ecf20Sopenharmony_ci return 0; 36798c2ecf20Sopenharmony_ci} 36808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_cleanup); 36818c2ecf20Sopenharmony_ci 36828c2ecf20Sopenharmony_ci/** 36838c2ecf20Sopenharmony_ci * snd_hda_multi_out_dig_close - release the digital out stream 36848c2ecf20Sopenharmony_ci * @codec: the HDA codec 36858c2ecf20Sopenharmony_ci * @mout: hda_multi_out object 36868c2ecf20Sopenharmony_ci */ 36878c2ecf20Sopenharmony_ciint snd_hda_multi_out_dig_close(struct hda_codec *codec, 36888c2ecf20Sopenharmony_ci struct hda_multi_out *mout) 36898c2ecf20Sopenharmony_ci{ 36908c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 36918c2ecf20Sopenharmony_ci mout->dig_out_used = 0; 36928c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 36938c2ecf20Sopenharmony_ci return 0; 36948c2ecf20Sopenharmony_ci} 36958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close); 36968c2ecf20Sopenharmony_ci 36978c2ecf20Sopenharmony_ci/** 36988c2ecf20Sopenharmony_ci * snd_hda_multi_out_analog_open - open analog outputs 36998c2ecf20Sopenharmony_ci * @codec: the HDA codec 37008c2ecf20Sopenharmony_ci * @mout: hda_multi_out object 37018c2ecf20Sopenharmony_ci * @substream: PCM substream to assign 37028c2ecf20Sopenharmony_ci * @hinfo: PCM information to assign 37038c2ecf20Sopenharmony_ci * 37048c2ecf20Sopenharmony_ci * Open analog outputs and set up the hw-constraints. 37058c2ecf20Sopenharmony_ci * If the digital outputs can be opened as follower, open the digital 37068c2ecf20Sopenharmony_ci * outputs, too. 37078c2ecf20Sopenharmony_ci */ 37088c2ecf20Sopenharmony_ciint snd_hda_multi_out_analog_open(struct hda_codec *codec, 37098c2ecf20Sopenharmony_ci struct hda_multi_out *mout, 37108c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 37118c2ecf20Sopenharmony_ci struct hda_pcm_stream *hinfo) 37128c2ecf20Sopenharmony_ci{ 37138c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 37148c2ecf20Sopenharmony_ci runtime->hw.channels_max = mout->max_channels; 37158c2ecf20Sopenharmony_ci if (mout->dig_out_nid) { 37168c2ecf20Sopenharmony_ci if (!mout->analog_rates) { 37178c2ecf20Sopenharmony_ci mout->analog_rates = hinfo->rates; 37188c2ecf20Sopenharmony_ci mout->analog_formats = hinfo->formats; 37198c2ecf20Sopenharmony_ci mout->analog_maxbps = hinfo->maxbps; 37208c2ecf20Sopenharmony_ci } else { 37218c2ecf20Sopenharmony_ci runtime->hw.rates = mout->analog_rates; 37228c2ecf20Sopenharmony_ci runtime->hw.formats = mout->analog_formats; 37238c2ecf20Sopenharmony_ci hinfo->maxbps = mout->analog_maxbps; 37248c2ecf20Sopenharmony_ci } 37258c2ecf20Sopenharmony_ci if (!mout->spdif_rates) { 37268c2ecf20Sopenharmony_ci snd_hda_query_supported_pcm(codec, mout->dig_out_nid, 37278c2ecf20Sopenharmony_ci &mout->spdif_rates, 37288c2ecf20Sopenharmony_ci &mout->spdif_formats, 37298c2ecf20Sopenharmony_ci &mout->spdif_maxbps); 37308c2ecf20Sopenharmony_ci } 37318c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 37328c2ecf20Sopenharmony_ci if (mout->share_spdif) { 37338c2ecf20Sopenharmony_ci if ((runtime->hw.rates & mout->spdif_rates) && 37348c2ecf20Sopenharmony_ci (runtime->hw.formats & mout->spdif_formats)) { 37358c2ecf20Sopenharmony_ci runtime->hw.rates &= mout->spdif_rates; 37368c2ecf20Sopenharmony_ci runtime->hw.formats &= mout->spdif_formats; 37378c2ecf20Sopenharmony_ci if (mout->spdif_maxbps < hinfo->maxbps) 37388c2ecf20Sopenharmony_ci hinfo->maxbps = mout->spdif_maxbps; 37398c2ecf20Sopenharmony_ci } else { 37408c2ecf20Sopenharmony_ci mout->share_spdif = 0; 37418c2ecf20Sopenharmony_ci /* FIXME: need notify? */ 37428c2ecf20Sopenharmony_ci } 37438c2ecf20Sopenharmony_ci } 37448c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 37458c2ecf20Sopenharmony_ci } 37468c2ecf20Sopenharmony_ci return snd_pcm_hw_constraint_step(substream->runtime, 0, 37478c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 2); 37488c2ecf20Sopenharmony_ci} 37498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_open); 37508c2ecf20Sopenharmony_ci 37518c2ecf20Sopenharmony_ci/** 37528c2ecf20Sopenharmony_ci * snd_hda_multi_out_analog_prepare - Preapre the analog outputs. 37538c2ecf20Sopenharmony_ci * @codec: the HDA codec 37548c2ecf20Sopenharmony_ci * @mout: hda_multi_out object 37558c2ecf20Sopenharmony_ci * @stream_tag: stream tag to assign 37568c2ecf20Sopenharmony_ci * @format: format id to assign 37578c2ecf20Sopenharmony_ci * @substream: PCM substream to assign 37588c2ecf20Sopenharmony_ci * 37598c2ecf20Sopenharmony_ci * Set up the i/o for analog out. 37608c2ecf20Sopenharmony_ci * When the digital out is available, copy the front out to digital out, too. 37618c2ecf20Sopenharmony_ci */ 37628c2ecf20Sopenharmony_ciint snd_hda_multi_out_analog_prepare(struct hda_codec *codec, 37638c2ecf20Sopenharmony_ci struct hda_multi_out *mout, 37648c2ecf20Sopenharmony_ci unsigned int stream_tag, 37658c2ecf20Sopenharmony_ci unsigned int format, 37668c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 37678c2ecf20Sopenharmony_ci{ 37688c2ecf20Sopenharmony_ci const hda_nid_t *nids = mout->dac_nids; 37698c2ecf20Sopenharmony_ci int chs = substream->runtime->channels; 37708c2ecf20Sopenharmony_ci struct hda_spdif_out *spdif; 37718c2ecf20Sopenharmony_ci int i; 37728c2ecf20Sopenharmony_ci 37738c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 37748c2ecf20Sopenharmony_ci spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid); 37758c2ecf20Sopenharmony_ci if (mout->dig_out_nid && mout->share_spdif && 37768c2ecf20Sopenharmony_ci mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 37778c2ecf20Sopenharmony_ci if (chs == 2 && spdif != NULL && 37788c2ecf20Sopenharmony_ci snd_hda_is_supported_format(codec, mout->dig_out_nid, 37798c2ecf20Sopenharmony_ci format) && 37808c2ecf20Sopenharmony_ci !(spdif->status & IEC958_AES0_NONAUDIO)) { 37818c2ecf20Sopenharmony_ci mout->dig_out_used = HDA_DIG_ANALOG_DUP; 37828c2ecf20Sopenharmony_ci setup_dig_out_stream(codec, mout->dig_out_nid, 37838c2ecf20Sopenharmony_ci stream_tag, format); 37848c2ecf20Sopenharmony_ci } else { 37858c2ecf20Sopenharmony_ci mout->dig_out_used = 0; 37868c2ecf20Sopenharmony_ci cleanup_dig_out_stream(codec, mout->dig_out_nid); 37878c2ecf20Sopenharmony_ci } 37888c2ecf20Sopenharmony_ci } 37898c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 37908c2ecf20Sopenharmony_ci 37918c2ecf20Sopenharmony_ci /* front */ 37928c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 37938c2ecf20Sopenharmony_ci 0, format); 37948c2ecf20Sopenharmony_ci if (!mout->no_share_stream && 37958c2ecf20Sopenharmony_ci mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]) 37968c2ecf20Sopenharmony_ci /* headphone out will just decode front left/right (stereo) */ 37978c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 37988c2ecf20Sopenharmony_ci 0, format); 37998c2ecf20Sopenharmony_ci /* extra outputs copied from front */ 38008c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++) 38018c2ecf20Sopenharmony_ci if (!mout->no_share_stream && mout->hp_out_nid[i]) 38028c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, 38038c2ecf20Sopenharmony_ci mout->hp_out_nid[i], 38048c2ecf20Sopenharmony_ci stream_tag, 0, format); 38058c2ecf20Sopenharmony_ci 38068c2ecf20Sopenharmony_ci /* surrounds */ 38078c2ecf20Sopenharmony_ci for (i = 1; i < mout->num_dacs; i++) { 38088c2ecf20Sopenharmony_ci if (chs >= (i + 1) * 2) /* independent out */ 38098c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 38108c2ecf20Sopenharmony_ci i * 2, format); 38118c2ecf20Sopenharmony_ci else if (!mout->no_share_stream) /* copy front */ 38128c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 38138c2ecf20Sopenharmony_ci 0, format); 38148c2ecf20Sopenharmony_ci } 38158c2ecf20Sopenharmony_ci 38168c2ecf20Sopenharmony_ci /* extra surrounds */ 38178c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) { 38188c2ecf20Sopenharmony_ci int ch = 0; 38198c2ecf20Sopenharmony_ci if (!mout->extra_out_nid[i]) 38208c2ecf20Sopenharmony_ci break; 38218c2ecf20Sopenharmony_ci if (chs >= (i + 1) * 2) 38228c2ecf20Sopenharmony_ci ch = i * 2; 38238c2ecf20Sopenharmony_ci else if (!mout->no_share_stream) 38248c2ecf20Sopenharmony_ci break; 38258c2ecf20Sopenharmony_ci snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i], 38268c2ecf20Sopenharmony_ci stream_tag, ch, format); 38278c2ecf20Sopenharmony_ci } 38288c2ecf20Sopenharmony_ci 38298c2ecf20Sopenharmony_ci return 0; 38308c2ecf20Sopenharmony_ci} 38318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_prepare); 38328c2ecf20Sopenharmony_ci 38338c2ecf20Sopenharmony_ci/** 38348c2ecf20Sopenharmony_ci * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out 38358c2ecf20Sopenharmony_ci * @codec: the HDA codec 38368c2ecf20Sopenharmony_ci * @mout: hda_multi_out object 38378c2ecf20Sopenharmony_ci */ 38388c2ecf20Sopenharmony_ciint snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, 38398c2ecf20Sopenharmony_ci struct hda_multi_out *mout) 38408c2ecf20Sopenharmony_ci{ 38418c2ecf20Sopenharmony_ci const hda_nid_t *nids = mout->dac_nids; 38428c2ecf20Sopenharmony_ci int i; 38438c2ecf20Sopenharmony_ci 38448c2ecf20Sopenharmony_ci for (i = 0; i < mout->num_dacs; i++) 38458c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, nids[i]); 38468c2ecf20Sopenharmony_ci if (mout->hp_nid) 38478c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, mout->hp_nid); 38488c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++) 38498c2ecf20Sopenharmony_ci if (mout->hp_out_nid[i]) 38508c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, 38518c2ecf20Sopenharmony_ci mout->hp_out_nid[i]); 38528c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 38538c2ecf20Sopenharmony_ci if (mout->extra_out_nid[i]) 38548c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_stream(codec, 38558c2ecf20Sopenharmony_ci mout->extra_out_nid[i]); 38568c2ecf20Sopenharmony_ci mutex_lock(&codec->spdif_mutex); 38578c2ecf20Sopenharmony_ci if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 38588c2ecf20Sopenharmony_ci cleanup_dig_out_stream(codec, mout->dig_out_nid); 38598c2ecf20Sopenharmony_ci mout->dig_out_used = 0; 38608c2ecf20Sopenharmony_ci } 38618c2ecf20Sopenharmony_ci mutex_unlock(&codec->spdif_mutex); 38628c2ecf20Sopenharmony_ci return 0; 38638c2ecf20Sopenharmony_ci} 38648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup); 38658c2ecf20Sopenharmony_ci 38668c2ecf20Sopenharmony_ci/** 38678c2ecf20Sopenharmony_ci * snd_hda_get_default_vref - Get the default (mic) VREF pin bits 38688c2ecf20Sopenharmony_ci * @codec: the HDA codec 38698c2ecf20Sopenharmony_ci * @pin: referred pin NID 38708c2ecf20Sopenharmony_ci * 38718c2ecf20Sopenharmony_ci * Guess the suitable VREF pin bits to be set as the pin-control value. 38728c2ecf20Sopenharmony_ci * Note: the function doesn't set the AC_PINCTL_IN_EN bit. 38738c2ecf20Sopenharmony_ci */ 38748c2ecf20Sopenharmony_ciunsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin) 38758c2ecf20Sopenharmony_ci{ 38768c2ecf20Sopenharmony_ci unsigned int pincap; 38778c2ecf20Sopenharmony_ci unsigned int oldval; 38788c2ecf20Sopenharmony_ci oldval = snd_hda_codec_read(codec, pin, 0, 38798c2ecf20Sopenharmony_ci AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 38808c2ecf20Sopenharmony_ci pincap = snd_hda_query_pin_caps(codec, pin); 38818c2ecf20Sopenharmony_ci pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; 38828c2ecf20Sopenharmony_ci /* Exception: if the default pin setup is vref50, we give it priority */ 38838c2ecf20Sopenharmony_ci if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50) 38848c2ecf20Sopenharmony_ci return AC_PINCTL_VREF_80; 38858c2ecf20Sopenharmony_ci else if (pincap & AC_PINCAP_VREF_50) 38868c2ecf20Sopenharmony_ci return AC_PINCTL_VREF_50; 38878c2ecf20Sopenharmony_ci else if (pincap & AC_PINCAP_VREF_100) 38888c2ecf20Sopenharmony_ci return AC_PINCTL_VREF_100; 38898c2ecf20Sopenharmony_ci else if (pincap & AC_PINCAP_VREF_GRD) 38908c2ecf20Sopenharmony_ci return AC_PINCTL_VREF_GRD; 38918c2ecf20Sopenharmony_ci return AC_PINCTL_VREF_HIZ; 38928c2ecf20Sopenharmony_ci} 38938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_get_default_vref); 38948c2ecf20Sopenharmony_ci 38958c2ecf20Sopenharmony_ci/** 38968c2ecf20Sopenharmony_ci * snd_hda_correct_pin_ctl - correct the pin ctl value for matching with the pin cap 38978c2ecf20Sopenharmony_ci * @codec: the HDA codec 38988c2ecf20Sopenharmony_ci * @pin: referred pin NID 38998c2ecf20Sopenharmony_ci * @val: pin ctl value to audit 39008c2ecf20Sopenharmony_ci */ 39018c2ecf20Sopenharmony_ciunsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, 39028c2ecf20Sopenharmony_ci hda_nid_t pin, unsigned int val) 39038c2ecf20Sopenharmony_ci{ 39048c2ecf20Sopenharmony_ci static const unsigned int cap_lists[][2] = { 39058c2ecf20Sopenharmony_ci { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 }, 39068c2ecf20Sopenharmony_ci { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 }, 39078c2ecf20Sopenharmony_ci { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 }, 39088c2ecf20Sopenharmony_ci { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD }, 39098c2ecf20Sopenharmony_ci }; 39108c2ecf20Sopenharmony_ci unsigned int cap; 39118c2ecf20Sopenharmony_ci 39128c2ecf20Sopenharmony_ci if (!val) 39138c2ecf20Sopenharmony_ci return 0; 39148c2ecf20Sopenharmony_ci cap = snd_hda_query_pin_caps(codec, pin); 39158c2ecf20Sopenharmony_ci if (!cap) 39168c2ecf20Sopenharmony_ci return val; /* don't know what to do... */ 39178c2ecf20Sopenharmony_ci 39188c2ecf20Sopenharmony_ci if (val & AC_PINCTL_OUT_EN) { 39198c2ecf20Sopenharmony_ci if (!(cap & AC_PINCAP_OUT)) 39208c2ecf20Sopenharmony_ci val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); 39218c2ecf20Sopenharmony_ci else if ((val & AC_PINCTL_HP_EN) && !(cap & AC_PINCAP_HP_DRV)) 39228c2ecf20Sopenharmony_ci val &= ~AC_PINCTL_HP_EN; 39238c2ecf20Sopenharmony_ci } 39248c2ecf20Sopenharmony_ci 39258c2ecf20Sopenharmony_ci if (val & AC_PINCTL_IN_EN) { 39268c2ecf20Sopenharmony_ci if (!(cap & AC_PINCAP_IN)) 39278c2ecf20Sopenharmony_ci val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN); 39288c2ecf20Sopenharmony_ci else { 39298c2ecf20Sopenharmony_ci unsigned int vcap, vref; 39308c2ecf20Sopenharmony_ci int i; 39318c2ecf20Sopenharmony_ci vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; 39328c2ecf20Sopenharmony_ci vref = val & AC_PINCTL_VREFEN; 39338c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cap_lists); i++) { 39348c2ecf20Sopenharmony_ci if (vref == cap_lists[i][0] && 39358c2ecf20Sopenharmony_ci !(vcap & cap_lists[i][1])) { 39368c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(cap_lists) - 1) 39378c2ecf20Sopenharmony_ci vref = AC_PINCTL_VREF_HIZ; 39388c2ecf20Sopenharmony_ci else 39398c2ecf20Sopenharmony_ci vref = cap_lists[i + 1][0]; 39408c2ecf20Sopenharmony_ci } 39418c2ecf20Sopenharmony_ci } 39428c2ecf20Sopenharmony_ci val &= ~AC_PINCTL_VREFEN; 39438c2ecf20Sopenharmony_ci val |= vref; 39448c2ecf20Sopenharmony_ci } 39458c2ecf20Sopenharmony_ci } 39468c2ecf20Sopenharmony_ci 39478c2ecf20Sopenharmony_ci return val; 39488c2ecf20Sopenharmony_ci} 39498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl); 39508c2ecf20Sopenharmony_ci 39518c2ecf20Sopenharmony_ci/** 39528c2ecf20Sopenharmony_ci * _snd_hda_pin_ctl - Helper to set pin ctl value 39538c2ecf20Sopenharmony_ci * @codec: the HDA codec 39548c2ecf20Sopenharmony_ci * @pin: referred pin NID 39558c2ecf20Sopenharmony_ci * @val: pin control value to set 39568c2ecf20Sopenharmony_ci * @cached: access over codec pinctl cache or direct write 39578c2ecf20Sopenharmony_ci * 39588c2ecf20Sopenharmony_ci * This function is a helper to set a pin ctl value more safely. 39598c2ecf20Sopenharmony_ci * It corrects the pin ctl value via snd_hda_correct_pin_ctl(), stores the 39608c2ecf20Sopenharmony_ci * value in pin target array via snd_hda_codec_set_pin_target(), then 39618c2ecf20Sopenharmony_ci * actually writes the value via either snd_hda_codec_write_cache() or 39628c2ecf20Sopenharmony_ci * snd_hda_codec_write() depending on @cached flag. 39638c2ecf20Sopenharmony_ci */ 39648c2ecf20Sopenharmony_ciint _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, 39658c2ecf20Sopenharmony_ci unsigned int val, bool cached) 39668c2ecf20Sopenharmony_ci{ 39678c2ecf20Sopenharmony_ci val = snd_hda_correct_pin_ctl(codec, pin, val); 39688c2ecf20Sopenharmony_ci snd_hda_codec_set_pin_target(codec, pin, val); 39698c2ecf20Sopenharmony_ci if (cached) 39708c2ecf20Sopenharmony_ci return snd_hda_codec_write_cache(codec, pin, 0, 39718c2ecf20Sopenharmony_ci AC_VERB_SET_PIN_WIDGET_CONTROL, val); 39728c2ecf20Sopenharmony_ci else 39738c2ecf20Sopenharmony_ci return snd_hda_codec_write(codec, pin, 0, 39748c2ecf20Sopenharmony_ci AC_VERB_SET_PIN_WIDGET_CONTROL, val); 39758c2ecf20Sopenharmony_ci} 39768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(_snd_hda_set_pin_ctl); 39778c2ecf20Sopenharmony_ci 39788c2ecf20Sopenharmony_ci/** 39798c2ecf20Sopenharmony_ci * snd_hda_add_imux_item - Add an item to input_mux 39808c2ecf20Sopenharmony_ci * @codec: the HDA codec 39818c2ecf20Sopenharmony_ci * @imux: imux helper object 39828c2ecf20Sopenharmony_ci * @label: the name of imux item to assign 39838c2ecf20Sopenharmony_ci * @index: index number of imux item to assign 39848c2ecf20Sopenharmony_ci * @type_idx: pointer to store the resultant label index 39858c2ecf20Sopenharmony_ci * 39868c2ecf20Sopenharmony_ci * When the same label is used already in the existing items, the number 39878c2ecf20Sopenharmony_ci * suffix is appended to the label. This label index number is stored 39888c2ecf20Sopenharmony_ci * to type_idx when non-NULL pointer is given. 39898c2ecf20Sopenharmony_ci */ 39908c2ecf20Sopenharmony_ciint snd_hda_add_imux_item(struct hda_codec *codec, 39918c2ecf20Sopenharmony_ci struct hda_input_mux *imux, const char *label, 39928c2ecf20Sopenharmony_ci int index, int *type_idx) 39938c2ecf20Sopenharmony_ci{ 39948c2ecf20Sopenharmony_ci int i, label_idx = 0; 39958c2ecf20Sopenharmony_ci if (imux->num_items >= HDA_MAX_NUM_INPUTS) { 39968c2ecf20Sopenharmony_ci codec_err(codec, "hda_codec: Too many imux items!\n"); 39978c2ecf20Sopenharmony_ci return -EINVAL; 39988c2ecf20Sopenharmony_ci } 39998c2ecf20Sopenharmony_ci for (i = 0; i < imux->num_items; i++) { 40008c2ecf20Sopenharmony_ci if (!strncmp(label, imux->items[i].label, strlen(label))) 40018c2ecf20Sopenharmony_ci label_idx++; 40028c2ecf20Sopenharmony_ci } 40038c2ecf20Sopenharmony_ci if (type_idx) 40048c2ecf20Sopenharmony_ci *type_idx = label_idx; 40058c2ecf20Sopenharmony_ci if (label_idx > 0) 40068c2ecf20Sopenharmony_ci snprintf(imux->items[imux->num_items].label, 40078c2ecf20Sopenharmony_ci sizeof(imux->items[imux->num_items].label), 40088c2ecf20Sopenharmony_ci "%s %d", label, label_idx); 40098c2ecf20Sopenharmony_ci else 40108c2ecf20Sopenharmony_ci strlcpy(imux->items[imux->num_items].label, label, 40118c2ecf20Sopenharmony_ci sizeof(imux->items[imux->num_items].label)); 40128c2ecf20Sopenharmony_ci imux->items[imux->num_items].index = index; 40138c2ecf20Sopenharmony_ci imux->num_items++; 40148c2ecf20Sopenharmony_ci return 0; 40158c2ecf20Sopenharmony_ci} 40168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_add_imux_item); 40178c2ecf20Sopenharmony_ci 40188c2ecf20Sopenharmony_ci/** 40198c2ecf20Sopenharmony_ci * snd_hda_bus_reset_codecs - Reset the bus 40208c2ecf20Sopenharmony_ci * @bus: HD-audio bus 40218c2ecf20Sopenharmony_ci */ 40228c2ecf20Sopenharmony_civoid snd_hda_bus_reset_codecs(struct hda_bus *bus) 40238c2ecf20Sopenharmony_ci{ 40248c2ecf20Sopenharmony_ci struct hda_codec *codec; 40258c2ecf20Sopenharmony_ci 40268c2ecf20Sopenharmony_ci list_for_each_codec(codec, bus) { 40278c2ecf20Sopenharmony_ci /* FIXME: maybe a better way needed for forced reset */ 40288c2ecf20Sopenharmony_ci if (current_work() != &codec->jackpoll_work.work) 40298c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&codec->jackpoll_work); 40308c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 40318c2ecf20Sopenharmony_ci if (hda_codec_is_power_on(codec)) { 40328c2ecf20Sopenharmony_ci hda_call_codec_suspend(codec); 40338c2ecf20Sopenharmony_ci hda_call_codec_resume(codec); 40348c2ecf20Sopenharmony_ci } 40358c2ecf20Sopenharmony_ci#endif 40368c2ecf20Sopenharmony_ci } 40378c2ecf20Sopenharmony_ci} 40388c2ecf20Sopenharmony_ci 40398c2ecf20Sopenharmony_ci/** 40408c2ecf20Sopenharmony_ci * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer 40418c2ecf20Sopenharmony_ci * @pcm: PCM caps bits 40428c2ecf20Sopenharmony_ci * @buf: the string buffer to write 40438c2ecf20Sopenharmony_ci * @buflen: the max buffer length 40448c2ecf20Sopenharmony_ci * 40458c2ecf20Sopenharmony_ci * used by hda_proc.c and hda_eld.c 40468c2ecf20Sopenharmony_ci */ 40478c2ecf20Sopenharmony_civoid snd_print_pcm_bits(int pcm, char *buf, int buflen) 40488c2ecf20Sopenharmony_ci{ 40498c2ecf20Sopenharmony_ci static const unsigned int bits[] = { 8, 16, 20, 24, 32 }; 40508c2ecf20Sopenharmony_ci int i, j; 40518c2ecf20Sopenharmony_ci 40528c2ecf20Sopenharmony_ci for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++) 40538c2ecf20Sopenharmony_ci if (pcm & (AC_SUPPCM_BITS_8 << i)) 40548c2ecf20Sopenharmony_ci j += scnprintf(buf + j, buflen - j, " %d", bits[i]); 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_ci buf[j] = '\0'; /* necessary when j == 0 */ 40578c2ecf20Sopenharmony_ci} 40588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_print_pcm_bits); 40598c2ecf20Sopenharmony_ci 40608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("HDA codec core"); 40618c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4062