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