18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * HD-audio codec core device
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/init.h>
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/device.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/export.h>
128c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
138c2ecf20Sopenharmony_ci#include <sound/hdaudio.h>
148c2ecf20Sopenharmony_ci#include <sound/hda_regmap.h>
158c2ecf20Sopenharmony_ci#include <sound/pcm.h>
168c2ecf20Sopenharmony_ci#include "local.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic void setup_fg_nodes(struct hdac_device *codec);
198c2ecf20Sopenharmony_cistatic int get_codec_vendor_name(struct hdac_device *codec);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic void default_release(struct device *dev)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	snd_hdac_device_exit(dev_to_hdac_dev(dev));
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/**
278c2ecf20Sopenharmony_ci * snd_hdac_device_init - initialize the HD-audio codec base device
288c2ecf20Sopenharmony_ci * @codec: device to initialize
298c2ecf20Sopenharmony_ci * @bus: but to attach
308c2ecf20Sopenharmony_ci * @name: device name string
318c2ecf20Sopenharmony_ci * @addr: codec address
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * Returns zero for success or a negative error code.
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * This function increments the runtime PM counter and marks it active.
368c2ecf20Sopenharmony_ci * The caller needs to turn it off appropriately later.
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * The caller needs to set the device's release op properly by itself.
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_ciint snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
418c2ecf20Sopenharmony_ci			 const char *name, unsigned int addr)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct device *dev;
448c2ecf20Sopenharmony_ci	hda_nid_t fg;
458c2ecf20Sopenharmony_ci	int err;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	dev = &codec->dev;
488c2ecf20Sopenharmony_ci	device_initialize(dev);
498c2ecf20Sopenharmony_ci	dev->parent = bus->dev;
508c2ecf20Sopenharmony_ci	dev->bus = &snd_hda_bus_type;
518c2ecf20Sopenharmony_ci	dev->release = default_release;
528c2ecf20Sopenharmony_ci	dev->groups = hdac_dev_attr_groups;
538c2ecf20Sopenharmony_ci	dev_set_name(dev, "%s", name);
548c2ecf20Sopenharmony_ci	device_enable_async_suspend(dev);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	codec->bus = bus;
578c2ecf20Sopenharmony_ci	codec->addr = addr;
588c2ecf20Sopenharmony_ci	codec->type = HDA_DEV_CORE;
598c2ecf20Sopenharmony_ci	mutex_init(&codec->widget_lock);
608c2ecf20Sopenharmony_ci	mutex_init(&codec->regmap_lock);
618c2ecf20Sopenharmony_ci	pm_runtime_set_active(&codec->dev);
628c2ecf20Sopenharmony_ci	pm_runtime_get_noresume(&codec->dev);
638c2ecf20Sopenharmony_ci	atomic_set(&codec->in_pm, 0);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	err = snd_hdac_bus_add_device(bus, codec);
668c2ecf20Sopenharmony_ci	if (err < 0)
678c2ecf20Sopenharmony_ci		goto error;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* fill parameters */
708c2ecf20Sopenharmony_ci	codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
718c2ecf20Sopenharmony_ci					      AC_PAR_VENDOR_ID);
728c2ecf20Sopenharmony_ci	if (codec->vendor_id == -1) {
738c2ecf20Sopenharmony_ci		/* read again, hopefully the access method was corrected
748c2ecf20Sopenharmony_ci		 * in the last read...
758c2ecf20Sopenharmony_ci		 */
768c2ecf20Sopenharmony_ci		codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
778c2ecf20Sopenharmony_ci						      AC_PAR_VENDOR_ID);
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
818c2ecf20Sopenharmony_ci						 AC_PAR_SUBSYSTEM_ID);
828c2ecf20Sopenharmony_ci	codec->revision_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
838c2ecf20Sopenharmony_ci						AC_PAR_REV_ID);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	setup_fg_nodes(codec);
868c2ecf20Sopenharmony_ci	if (!codec->afg && !codec->mfg) {
878c2ecf20Sopenharmony_ci		dev_err(dev, "no AFG or MFG node found\n");
888c2ecf20Sopenharmony_ci		err = -ENODEV;
898c2ecf20Sopenharmony_ci		goto error;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	fg = codec->afg ? codec->afg : codec->mfg;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	err = snd_hdac_refresh_widgets(codec);
958c2ecf20Sopenharmony_ci	if (err < 0)
968c2ecf20Sopenharmony_ci		goto error;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE);
998c2ecf20Sopenharmony_ci	/* reread ssid if not set by parameter */
1008c2ecf20Sopenharmony_ci	if (codec->subsystem_id == -1 || codec->subsystem_id == 0)
1018c2ecf20Sopenharmony_ci		snd_hdac_read(codec, fg, AC_VERB_GET_SUBSYSTEM_ID, 0,
1028c2ecf20Sopenharmony_ci			      &codec->subsystem_id);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	err = get_codec_vendor_name(codec);
1058c2ecf20Sopenharmony_ci	if (err < 0)
1068c2ecf20Sopenharmony_ci		goto error;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	codec->chip_name = kasprintf(GFP_KERNEL, "ID %x",
1098c2ecf20Sopenharmony_ci				     codec->vendor_id & 0xffff);
1108c2ecf20Sopenharmony_ci	if (!codec->chip_name) {
1118c2ecf20Sopenharmony_ci		err = -ENOMEM;
1128c2ecf20Sopenharmony_ci		goto error;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci error:
1188c2ecf20Sopenharmony_ci	put_device(&codec->dev);
1198c2ecf20Sopenharmony_ci	return err;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_device_init);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/**
1248c2ecf20Sopenharmony_ci * snd_hdac_device_exit - clean up the HD-audio codec base device
1258c2ecf20Sopenharmony_ci * @codec: device to clean up
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_civoid snd_hdac_device_exit(struct hdac_device *codec)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	pm_runtime_put_noidle(&codec->dev);
1308c2ecf20Sopenharmony_ci	/* keep balance of runtime PM child_count in parent device */
1318c2ecf20Sopenharmony_ci	pm_runtime_set_suspended(&codec->dev);
1328c2ecf20Sopenharmony_ci	snd_hdac_bus_remove_device(codec->bus, codec);
1338c2ecf20Sopenharmony_ci	kfree(codec->vendor_name);
1348c2ecf20Sopenharmony_ci	kfree(codec->chip_name);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_device_exit);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/**
1398c2ecf20Sopenharmony_ci * snd_hdac_device_register - register the hd-audio codec base device
1408c2ecf20Sopenharmony_ci * @codec: the device to register
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_ciint snd_hdac_device_register(struct hdac_device *codec)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	int err;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	err = device_add(&codec->dev);
1478c2ecf20Sopenharmony_ci	if (err < 0)
1488c2ecf20Sopenharmony_ci		return err;
1498c2ecf20Sopenharmony_ci	mutex_lock(&codec->widget_lock);
1508c2ecf20Sopenharmony_ci	err = hda_widget_sysfs_init(codec);
1518c2ecf20Sopenharmony_ci	mutex_unlock(&codec->widget_lock);
1528c2ecf20Sopenharmony_ci	if (err < 0) {
1538c2ecf20Sopenharmony_ci		device_del(&codec->dev);
1548c2ecf20Sopenharmony_ci		return err;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return 0;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_device_register);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci/**
1628c2ecf20Sopenharmony_ci * snd_hdac_device_unregister - unregister the hd-audio codec base device
1638c2ecf20Sopenharmony_ci * @codec: the device to unregister
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_civoid snd_hdac_device_unregister(struct hdac_device *codec)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	if (device_is_registered(&codec->dev)) {
1688c2ecf20Sopenharmony_ci		mutex_lock(&codec->widget_lock);
1698c2ecf20Sopenharmony_ci		hda_widget_sysfs_exit(codec);
1708c2ecf20Sopenharmony_ci		mutex_unlock(&codec->widget_lock);
1718c2ecf20Sopenharmony_ci		device_del(&codec->dev);
1728c2ecf20Sopenharmony_ci		snd_hdac_bus_remove_device(codec->bus, codec);
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/**
1788c2ecf20Sopenharmony_ci * snd_hdac_device_set_chip_name - set/update the codec name
1798c2ecf20Sopenharmony_ci * @codec: the HDAC device
1808c2ecf20Sopenharmony_ci * @name: name string to set
1818c2ecf20Sopenharmony_ci *
1828c2ecf20Sopenharmony_ci * Returns 0 if the name is set or updated, or a negative error code.
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_ciint snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	char *newname;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (!name)
1898c2ecf20Sopenharmony_ci		return 0;
1908c2ecf20Sopenharmony_ci	newname = kstrdup(name, GFP_KERNEL);
1918c2ecf20Sopenharmony_ci	if (!newname)
1928c2ecf20Sopenharmony_ci		return -ENOMEM;
1938c2ecf20Sopenharmony_ci	kfree(codec->chip_name);
1948c2ecf20Sopenharmony_ci	codec->chip_name = newname;
1958c2ecf20Sopenharmony_ci	return 0;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/**
2008c2ecf20Sopenharmony_ci * snd_hdac_codec_modalias - give the module alias name
2018c2ecf20Sopenharmony_ci * @codec: HDAC device
2028c2ecf20Sopenharmony_ci * @buf: string buffer to store
2038c2ecf20Sopenharmony_ci * @size: string buffer size
2048c2ecf20Sopenharmony_ci *
2058c2ecf20Sopenharmony_ci * Returns the size of string, like snprintf(), or a negative error code.
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_ciint snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	return scnprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
2108c2ecf20Sopenharmony_ci			codec->vendor_id, codec->revision_id, codec->type);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/**
2158c2ecf20Sopenharmony_ci * snd_hdac_make_cmd - compose a 32bit command word to be sent to the
2168c2ecf20Sopenharmony_ci *	HD-audio controller
2178c2ecf20Sopenharmony_ci * @codec: the codec object
2188c2ecf20Sopenharmony_ci * @nid: NID to encode
2198c2ecf20Sopenharmony_ci * @verb: verb to encode
2208c2ecf20Sopenharmony_ci * @parm: parameter to encode
2218c2ecf20Sopenharmony_ci *
2228c2ecf20Sopenharmony_ci * Return an encoded command verb or -1 for error.
2238c2ecf20Sopenharmony_ci */
2248c2ecf20Sopenharmony_cistatic unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
2258c2ecf20Sopenharmony_ci				      unsigned int verb, unsigned int parm)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	u32 val, addr;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	addr = codec->addr;
2308c2ecf20Sopenharmony_ci	if ((addr & ~0xf) || (nid & ~0x7f) ||
2318c2ecf20Sopenharmony_ci	    (verb & ~0xfff) || (parm & ~0xffff)) {
2328c2ecf20Sopenharmony_ci		dev_err(&codec->dev, "out of range cmd %x:%x:%x:%x\n",
2338c2ecf20Sopenharmony_ci			addr, nid, verb, parm);
2348c2ecf20Sopenharmony_ci		return -1;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	val = addr << 28;
2388c2ecf20Sopenharmony_ci	val |= (u32)nid << 20;
2398c2ecf20Sopenharmony_ci	val |= verb << 8;
2408c2ecf20Sopenharmony_ci	val |= parm;
2418c2ecf20Sopenharmony_ci	return val;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci/**
2458c2ecf20Sopenharmony_ci * snd_hdac_exec_verb - execute an encoded verb
2468c2ecf20Sopenharmony_ci * @codec: the codec object
2478c2ecf20Sopenharmony_ci * @cmd: encoded verb to execute
2488c2ecf20Sopenharmony_ci * @flags: optional flags, pass zero for default
2498c2ecf20Sopenharmony_ci * @res: the pointer to store the result, NULL if running async
2508c2ecf20Sopenharmony_ci *
2518c2ecf20Sopenharmony_ci * Returns zero if successful, or a negative error code.
2528c2ecf20Sopenharmony_ci *
2538c2ecf20Sopenharmony_ci * This calls the exec_verb op when set in hdac_codec.  If not,
2548c2ecf20Sopenharmony_ci * call the default snd_hdac_bus_exec_verb().
2558c2ecf20Sopenharmony_ci */
2568c2ecf20Sopenharmony_ciint snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
2578c2ecf20Sopenharmony_ci		       unsigned int flags, unsigned int *res)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	if (codec->exec_verb)
2608c2ecf20Sopenharmony_ci		return codec->exec_verb(codec, cmd, flags, res);
2618c2ecf20Sopenharmony_ci	return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci/**
2668c2ecf20Sopenharmony_ci * snd_hdac_read - execute a verb
2678c2ecf20Sopenharmony_ci * @codec: the codec object
2688c2ecf20Sopenharmony_ci * @nid: NID to execute a verb
2698c2ecf20Sopenharmony_ci * @verb: verb to execute
2708c2ecf20Sopenharmony_ci * @parm: parameter for a verb
2718c2ecf20Sopenharmony_ci * @res: the pointer to store the result, NULL if running async
2728c2ecf20Sopenharmony_ci *
2738c2ecf20Sopenharmony_ci * Returns zero if successful, or a negative error code.
2748c2ecf20Sopenharmony_ci */
2758c2ecf20Sopenharmony_ciint snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
2768c2ecf20Sopenharmony_ci		  unsigned int verb, unsigned int parm, unsigned int *res)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	unsigned int cmd = snd_hdac_make_cmd(codec, nid, verb, parm);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return snd_hdac_exec_verb(codec, cmd, 0, res);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_read);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci/**
2858c2ecf20Sopenharmony_ci * _snd_hdac_read_parm - read a parmeter
2868c2ecf20Sopenharmony_ci * @codec: the codec object
2878c2ecf20Sopenharmony_ci * @nid: NID to read a parameter
2888c2ecf20Sopenharmony_ci * @parm: parameter to read
2898c2ecf20Sopenharmony_ci * @res: pointer to store the read value
2908c2ecf20Sopenharmony_ci *
2918c2ecf20Sopenharmony_ci * This function returns zero or an error unlike snd_hdac_read_parm().
2928c2ecf20Sopenharmony_ci */
2938c2ecf20Sopenharmony_ciint _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
2948c2ecf20Sopenharmony_ci			unsigned int *res)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	unsigned int cmd;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
2998c2ecf20Sopenharmony_ci	return snd_hdac_regmap_read_raw(codec, cmd, res);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(_snd_hdac_read_parm);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/**
3048c2ecf20Sopenharmony_ci * snd_hdac_read_parm_uncached - read a codec parameter without caching
3058c2ecf20Sopenharmony_ci * @codec: the codec object
3068c2ecf20Sopenharmony_ci * @nid: NID to read a parameter
3078c2ecf20Sopenharmony_ci * @parm: parameter to read
3088c2ecf20Sopenharmony_ci *
3098c2ecf20Sopenharmony_ci * Returns -1 for error.  If you need to distinguish the error more
3108c2ecf20Sopenharmony_ci * strictly, use snd_hdac_read() directly.
3118c2ecf20Sopenharmony_ci */
3128c2ecf20Sopenharmony_ciint snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
3138c2ecf20Sopenharmony_ci				int parm)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	unsigned int cmd, val;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
3188c2ecf20Sopenharmony_ci	if (snd_hdac_regmap_read_raw_uncached(codec, cmd, &val) < 0)
3198c2ecf20Sopenharmony_ci		return -1;
3208c2ecf20Sopenharmony_ci	return val;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/**
3258c2ecf20Sopenharmony_ci * snd_hdac_override_parm - override read-only parameters
3268c2ecf20Sopenharmony_ci * @codec: the codec object
3278c2ecf20Sopenharmony_ci * @nid: NID for the parameter
3288c2ecf20Sopenharmony_ci * @parm: the parameter to change
3298c2ecf20Sopenharmony_ci * @val: the parameter value to overwrite
3308c2ecf20Sopenharmony_ci */
3318c2ecf20Sopenharmony_ciint snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
3328c2ecf20Sopenharmony_ci			   unsigned int parm, unsigned int val)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm;
3358c2ecf20Sopenharmony_ci	int err;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (!codec->regmap)
3388c2ecf20Sopenharmony_ci		return -EINVAL;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	codec->caps_overwriting = true;
3418c2ecf20Sopenharmony_ci	err = snd_hdac_regmap_write_raw(codec, verb, val);
3428c2ecf20Sopenharmony_ci	codec->caps_overwriting = false;
3438c2ecf20Sopenharmony_ci	return err;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_override_parm);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci/**
3488c2ecf20Sopenharmony_ci * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes
3498c2ecf20Sopenharmony_ci * @codec: the codec object
3508c2ecf20Sopenharmony_ci * @nid: NID to inspect
3518c2ecf20Sopenharmony_ci * @start_id: the pointer to store the starting NID
3528c2ecf20Sopenharmony_ci *
3538c2ecf20Sopenharmony_ci * Returns the number of subtree nodes or zero if not found.
3548c2ecf20Sopenharmony_ci * This function reads parameters always without caching.
3558c2ecf20Sopenharmony_ci */
3568c2ecf20Sopenharmony_ciint snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
3578c2ecf20Sopenharmony_ci			   hda_nid_t *start_id)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	unsigned int parm;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT);
3628c2ecf20Sopenharmony_ci	if (parm == -1) {
3638c2ecf20Sopenharmony_ci		*start_id = 0;
3648c2ecf20Sopenharmony_ci		return 0;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci	*start_id = (parm >> 16) & 0x7fff;
3678c2ecf20Sopenharmony_ci	return (int)(parm & 0x7fff);
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_get_sub_nodes);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci/*
3728c2ecf20Sopenharmony_ci * look for an AFG and MFG nodes
3738c2ecf20Sopenharmony_ci */
3748c2ecf20Sopenharmony_cistatic void setup_fg_nodes(struct hdac_device *codec)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	int i, total_nodes, function_id;
3778c2ecf20Sopenharmony_ci	hda_nid_t nid;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	total_nodes = snd_hdac_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
3808c2ecf20Sopenharmony_ci	for (i = 0; i < total_nodes; i++, nid++) {
3818c2ecf20Sopenharmony_ci		function_id = snd_hdac_read_parm(codec, nid,
3828c2ecf20Sopenharmony_ci						 AC_PAR_FUNCTION_TYPE);
3838c2ecf20Sopenharmony_ci		switch (function_id & 0xff) {
3848c2ecf20Sopenharmony_ci		case AC_GRP_AUDIO_FUNCTION:
3858c2ecf20Sopenharmony_ci			codec->afg = nid;
3868c2ecf20Sopenharmony_ci			codec->afg_function_id = function_id & 0xff;
3878c2ecf20Sopenharmony_ci			codec->afg_unsol = (function_id >> 8) & 1;
3888c2ecf20Sopenharmony_ci			break;
3898c2ecf20Sopenharmony_ci		case AC_GRP_MODEM_FUNCTION:
3908c2ecf20Sopenharmony_ci			codec->mfg = nid;
3918c2ecf20Sopenharmony_ci			codec->mfg_function_id = function_id & 0xff;
3928c2ecf20Sopenharmony_ci			codec->mfg_unsol = (function_id >> 8) & 1;
3938c2ecf20Sopenharmony_ci			break;
3948c2ecf20Sopenharmony_ci		default:
3958c2ecf20Sopenharmony_ci			break;
3968c2ecf20Sopenharmony_ci		}
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci/**
4018c2ecf20Sopenharmony_ci * snd_hdac_refresh_widgets - Reset the widget start/end nodes
4028c2ecf20Sopenharmony_ci * @codec: the codec object
4038c2ecf20Sopenharmony_ci */
4048c2ecf20Sopenharmony_ciint snd_hdac_refresh_widgets(struct hdac_device *codec)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	hda_nid_t start_nid;
4078c2ecf20Sopenharmony_ci	int nums, err = 0;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/*
4108c2ecf20Sopenharmony_ci	 * Serialize against multiple threads trying to update the sysfs
4118c2ecf20Sopenharmony_ci	 * widgets array.
4128c2ecf20Sopenharmony_ci	 */
4138c2ecf20Sopenharmony_ci	mutex_lock(&codec->widget_lock);
4148c2ecf20Sopenharmony_ci	nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid);
4158c2ecf20Sopenharmony_ci	if (!start_nid || nums <= 0 || nums >= 0xff) {
4168c2ecf20Sopenharmony_ci		dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n",
4178c2ecf20Sopenharmony_ci			codec->afg);
4188c2ecf20Sopenharmony_ci		err = -EINVAL;
4198c2ecf20Sopenharmony_ci		goto unlock;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	err = hda_widget_sysfs_reinit(codec, start_nid, nums);
4238c2ecf20Sopenharmony_ci	if (err < 0)
4248c2ecf20Sopenharmony_ci		goto unlock;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	codec->num_nodes = nums;
4278c2ecf20Sopenharmony_ci	codec->start_nid = start_nid;
4288c2ecf20Sopenharmony_ci	codec->end_nid = start_nid + nums;
4298c2ecf20Sopenharmony_ciunlock:
4308c2ecf20Sopenharmony_ci	mutex_unlock(&codec->widget_lock);
4318c2ecf20Sopenharmony_ci	return err;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci/* return CONNLIST_LEN parameter of the given widget */
4368c2ecf20Sopenharmony_cistatic unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	unsigned int wcaps = get_wcaps(codec, nid);
4398c2ecf20Sopenharmony_ci	unsigned int parm;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (!(wcaps & AC_WCAP_CONN_LIST) &&
4428c2ecf20Sopenharmony_ci	    get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
4438c2ecf20Sopenharmony_ci		return 0;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN);
4468c2ecf20Sopenharmony_ci	if (parm == -1)
4478c2ecf20Sopenharmony_ci		parm = 0;
4488c2ecf20Sopenharmony_ci	return parm;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci/**
4528c2ecf20Sopenharmony_ci * snd_hdac_get_connections - get a widget connection list
4538c2ecf20Sopenharmony_ci * @codec: the codec object
4548c2ecf20Sopenharmony_ci * @nid: NID
4558c2ecf20Sopenharmony_ci * @conn_list: the array to store the results, can be NULL
4568c2ecf20Sopenharmony_ci * @max_conns: the max size of the given array
4578c2ecf20Sopenharmony_ci *
4588c2ecf20Sopenharmony_ci * Returns the number of connected widgets, zero for no connection, or a
4598c2ecf20Sopenharmony_ci * negative error code.  When the number of elements don't fit with the
4608c2ecf20Sopenharmony_ci * given array size, it returns -ENOSPC.
4618c2ecf20Sopenharmony_ci *
4628c2ecf20Sopenharmony_ci * When @conn_list is NULL, it just checks the number of connections.
4638c2ecf20Sopenharmony_ci */
4648c2ecf20Sopenharmony_ciint snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
4658c2ecf20Sopenharmony_ci			     hda_nid_t *conn_list, int max_conns)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	unsigned int parm;
4688c2ecf20Sopenharmony_ci	int i, conn_len, conns, err;
4698c2ecf20Sopenharmony_ci	unsigned int shift, num_elems, mask;
4708c2ecf20Sopenharmony_ci	hda_nid_t prev_nid;
4718c2ecf20Sopenharmony_ci	int null_count = 0;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	parm = get_num_conns(codec, nid);
4748c2ecf20Sopenharmony_ci	if (!parm)
4758c2ecf20Sopenharmony_ci		return 0;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	if (parm & AC_CLIST_LONG) {
4788c2ecf20Sopenharmony_ci		/* long form */
4798c2ecf20Sopenharmony_ci		shift = 16;
4808c2ecf20Sopenharmony_ci		num_elems = 2;
4818c2ecf20Sopenharmony_ci	} else {
4828c2ecf20Sopenharmony_ci		/* short form */
4838c2ecf20Sopenharmony_ci		shift = 8;
4848c2ecf20Sopenharmony_ci		num_elems = 4;
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci	conn_len = parm & AC_CLIST_LENGTH;
4878c2ecf20Sopenharmony_ci	mask = (1 << (shift-1)) - 1;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (!conn_len)
4908c2ecf20Sopenharmony_ci		return 0; /* no connection */
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (conn_len == 1) {
4938c2ecf20Sopenharmony_ci		/* single connection */
4948c2ecf20Sopenharmony_ci		err = snd_hdac_read(codec, nid, AC_VERB_GET_CONNECT_LIST, 0,
4958c2ecf20Sopenharmony_ci				    &parm);
4968c2ecf20Sopenharmony_ci		if (err < 0)
4978c2ecf20Sopenharmony_ci			return err;
4988c2ecf20Sopenharmony_ci		if (conn_list)
4998c2ecf20Sopenharmony_ci			conn_list[0] = parm & mask;
5008c2ecf20Sopenharmony_ci		return 1;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	/* multi connection */
5048c2ecf20Sopenharmony_ci	conns = 0;
5058c2ecf20Sopenharmony_ci	prev_nid = 0;
5068c2ecf20Sopenharmony_ci	for (i = 0; i < conn_len; i++) {
5078c2ecf20Sopenharmony_ci		int range_val;
5088c2ecf20Sopenharmony_ci		hda_nid_t val, n;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci		if (i % num_elems == 0) {
5118c2ecf20Sopenharmony_ci			err = snd_hdac_read(codec, nid,
5128c2ecf20Sopenharmony_ci					    AC_VERB_GET_CONNECT_LIST, i,
5138c2ecf20Sopenharmony_ci					    &parm);
5148c2ecf20Sopenharmony_ci			if (err < 0)
5158c2ecf20Sopenharmony_ci				return -EIO;
5168c2ecf20Sopenharmony_ci		}
5178c2ecf20Sopenharmony_ci		range_val = !!(parm & (1 << (shift-1))); /* ranges */
5188c2ecf20Sopenharmony_ci		val = parm & mask;
5198c2ecf20Sopenharmony_ci		if (val == 0 && null_count++) {  /* no second chance */
5208c2ecf20Sopenharmony_ci			dev_dbg(&codec->dev,
5218c2ecf20Sopenharmony_ci				"invalid CONNECT_LIST verb %x[%i]:%x\n",
5228c2ecf20Sopenharmony_ci				nid, i, parm);
5238c2ecf20Sopenharmony_ci			return 0;
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci		parm >>= shift;
5268c2ecf20Sopenharmony_ci		if (range_val) {
5278c2ecf20Sopenharmony_ci			/* ranges between the previous and this one */
5288c2ecf20Sopenharmony_ci			if (!prev_nid || prev_nid >= val) {
5298c2ecf20Sopenharmony_ci				dev_warn(&codec->dev,
5308c2ecf20Sopenharmony_ci					 "invalid dep_range_val %x:%x\n",
5318c2ecf20Sopenharmony_ci					 prev_nid, val);
5328c2ecf20Sopenharmony_ci				continue;
5338c2ecf20Sopenharmony_ci			}
5348c2ecf20Sopenharmony_ci			for (n = prev_nid + 1; n <= val; n++) {
5358c2ecf20Sopenharmony_ci				if (conn_list) {
5368c2ecf20Sopenharmony_ci					if (conns >= max_conns)
5378c2ecf20Sopenharmony_ci						return -ENOSPC;
5388c2ecf20Sopenharmony_ci					conn_list[conns] = n;
5398c2ecf20Sopenharmony_ci				}
5408c2ecf20Sopenharmony_ci				conns++;
5418c2ecf20Sopenharmony_ci			}
5428c2ecf20Sopenharmony_ci		} else {
5438c2ecf20Sopenharmony_ci			if (conn_list) {
5448c2ecf20Sopenharmony_ci				if (conns >= max_conns)
5458c2ecf20Sopenharmony_ci					return -ENOSPC;
5468c2ecf20Sopenharmony_ci				conn_list[conns] = val;
5478c2ecf20Sopenharmony_ci			}
5488c2ecf20Sopenharmony_ci			conns++;
5498c2ecf20Sopenharmony_ci		}
5508c2ecf20Sopenharmony_ci		prev_nid = val;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci	return conns;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_get_connections);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
5578c2ecf20Sopenharmony_ci/**
5588c2ecf20Sopenharmony_ci * snd_hdac_power_up - power up the codec
5598c2ecf20Sopenharmony_ci * @codec: the codec object
5608c2ecf20Sopenharmony_ci *
5618c2ecf20Sopenharmony_ci * This function calls the runtime PM helper to power up the given codec.
5628c2ecf20Sopenharmony_ci * Unlike snd_hdac_power_up_pm(), you should call this only for the code
5638c2ecf20Sopenharmony_ci * path that isn't included in PM path.  Otherwise it gets stuck.
5648c2ecf20Sopenharmony_ci *
5658c2ecf20Sopenharmony_ci * Returns zero if successful, or a negative error code.
5668c2ecf20Sopenharmony_ci */
5678c2ecf20Sopenharmony_ciint snd_hdac_power_up(struct hdac_device *codec)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	return pm_runtime_get_sync(&codec->dev);
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_power_up);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci/**
5748c2ecf20Sopenharmony_ci * snd_hdac_power_down - power down the codec
5758c2ecf20Sopenharmony_ci * @codec: the codec object
5768c2ecf20Sopenharmony_ci *
5778c2ecf20Sopenharmony_ci * Returns zero if successful, or a negative error code.
5788c2ecf20Sopenharmony_ci */
5798c2ecf20Sopenharmony_ciint snd_hdac_power_down(struct hdac_device *codec)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct device *dev = &codec->dev;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
5848c2ecf20Sopenharmony_ci	return pm_runtime_put_autosuspend(dev);
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_power_down);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci/**
5898c2ecf20Sopenharmony_ci * snd_hdac_power_up_pm - power up the codec
5908c2ecf20Sopenharmony_ci * @codec: the codec object
5918c2ecf20Sopenharmony_ci *
5928c2ecf20Sopenharmony_ci * This function can be called in a recursive code path like init code
5938c2ecf20Sopenharmony_ci * which may be called by PM suspend/resume again.  OTOH, if a power-up
5948c2ecf20Sopenharmony_ci * call must wake up the sleeper (e.g. in a kctl callback), use
5958c2ecf20Sopenharmony_ci * snd_hdac_power_up() instead.
5968c2ecf20Sopenharmony_ci *
5978c2ecf20Sopenharmony_ci * Returns zero if successful, or a negative error code.
5988c2ecf20Sopenharmony_ci */
5998c2ecf20Sopenharmony_ciint snd_hdac_power_up_pm(struct hdac_device *codec)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	if (!atomic_inc_not_zero(&codec->in_pm))
6028c2ecf20Sopenharmony_ci		return snd_hdac_power_up(codec);
6038c2ecf20Sopenharmony_ci	return 0;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci/* like snd_hdac_power_up_pm(), but only increment the pm count when
6088c2ecf20Sopenharmony_ci * already powered up.  Returns -1 if not powered up, 1 if incremented
6098c2ecf20Sopenharmony_ci * or 0 if unchanged.  Only used in hdac_regmap.c
6108c2ecf20Sopenharmony_ci */
6118c2ecf20Sopenharmony_ciint snd_hdac_keep_power_up(struct hdac_device *codec)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	if (!atomic_inc_not_zero(&codec->in_pm)) {
6148c2ecf20Sopenharmony_ci		int ret = pm_runtime_get_if_active(&codec->dev, true);
6158c2ecf20Sopenharmony_ci		if (!ret)
6168c2ecf20Sopenharmony_ci			return -1;
6178c2ecf20Sopenharmony_ci		if (ret < 0)
6188c2ecf20Sopenharmony_ci			return 0;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci	return 1;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci/**
6248c2ecf20Sopenharmony_ci * snd_hdac_power_down_pm - power down the codec
6258c2ecf20Sopenharmony_ci * @codec: the codec object
6268c2ecf20Sopenharmony_ci *
6278c2ecf20Sopenharmony_ci * Like snd_hdac_power_up_pm(), this function is used in a recursive
6288c2ecf20Sopenharmony_ci * code path like init code which may be called by PM suspend/resume again.
6298c2ecf20Sopenharmony_ci *
6308c2ecf20Sopenharmony_ci * Returns zero if successful, or a negative error code.
6318c2ecf20Sopenharmony_ci */
6328c2ecf20Sopenharmony_ciint snd_hdac_power_down_pm(struct hdac_device *codec)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	if (atomic_dec_if_positive(&codec->in_pm) < 0)
6358c2ecf20Sopenharmony_ci		return snd_hdac_power_down(codec);
6368c2ecf20Sopenharmony_ci	return 0;
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
6398c2ecf20Sopenharmony_ci#endif
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci/* codec vendor labels */
6428c2ecf20Sopenharmony_cistruct hda_vendor_id {
6438c2ecf20Sopenharmony_ci	unsigned int id;
6448c2ecf20Sopenharmony_ci	const char *name;
6458c2ecf20Sopenharmony_ci};
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cistatic const struct hda_vendor_id hda_vendor_ids[] = {
6488c2ecf20Sopenharmony_ci	{ 0x0014, "Loongson" },
6498c2ecf20Sopenharmony_ci	{ 0x1002, "ATI" },
6508c2ecf20Sopenharmony_ci	{ 0x1013, "Cirrus Logic" },
6518c2ecf20Sopenharmony_ci	{ 0x1057, "Motorola" },
6528c2ecf20Sopenharmony_ci	{ 0x1095, "Silicon Image" },
6538c2ecf20Sopenharmony_ci	{ 0x10de, "Nvidia" },
6548c2ecf20Sopenharmony_ci	{ 0x10ec, "Realtek" },
6558c2ecf20Sopenharmony_ci	{ 0x1102, "Creative" },
6568c2ecf20Sopenharmony_ci	{ 0x1106, "VIA" },
6578c2ecf20Sopenharmony_ci	{ 0x111d, "IDT" },
6588c2ecf20Sopenharmony_ci	{ 0x11c1, "LSI" },
6598c2ecf20Sopenharmony_ci	{ 0x11d4, "Analog Devices" },
6608c2ecf20Sopenharmony_ci	{ 0x13f6, "C-Media" },
6618c2ecf20Sopenharmony_ci	{ 0x14f1, "Conexant" },
6628c2ecf20Sopenharmony_ci	{ 0x17e8, "Chrontel" },
6638c2ecf20Sopenharmony_ci	{ 0x1854, "LG" },
6648c2ecf20Sopenharmony_ci	{ 0x19e5, "Huawei" },
6658c2ecf20Sopenharmony_ci	{ 0x1aec, "Wolfson Microelectronics" },
6668c2ecf20Sopenharmony_ci	{ 0x1af4, "QEMU" },
6678c2ecf20Sopenharmony_ci	{ 0x434d, "C-Media" },
6688c2ecf20Sopenharmony_ci	{ 0x8086, "Intel" },
6698c2ecf20Sopenharmony_ci	{ 0x8384, "SigmaTel" },
6708c2ecf20Sopenharmony_ci	{} /* terminator */
6718c2ecf20Sopenharmony_ci};
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci/* store the codec vendor name */
6748c2ecf20Sopenharmony_cistatic int get_codec_vendor_name(struct hdac_device *codec)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	const struct hda_vendor_id *c;
6778c2ecf20Sopenharmony_ci	u16 vendor_id = codec->vendor_id >> 16;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	for (c = hda_vendor_ids; c->id; c++) {
6808c2ecf20Sopenharmony_ci		if (c->id == vendor_id) {
6818c2ecf20Sopenharmony_ci			codec->vendor_name = kstrdup(c->name, GFP_KERNEL);
6828c2ecf20Sopenharmony_ci			return codec->vendor_name ? 0 : -ENOMEM;
6838c2ecf20Sopenharmony_ci		}
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id);
6878c2ecf20Sopenharmony_ci	return codec->vendor_name ? 0 : -ENOMEM;
6888c2ecf20Sopenharmony_ci}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci/*
6918c2ecf20Sopenharmony_ci * stream formats
6928c2ecf20Sopenharmony_ci */
6938c2ecf20Sopenharmony_cistruct hda_rate_tbl {
6948c2ecf20Sopenharmony_ci	unsigned int hz;
6958c2ecf20Sopenharmony_ci	unsigned int alsa_bits;
6968c2ecf20Sopenharmony_ci	unsigned int hda_fmt;
6978c2ecf20Sopenharmony_ci};
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci/* rate = base * mult / div */
7008c2ecf20Sopenharmony_ci#define HDA_RATE(base, mult, div) \
7018c2ecf20Sopenharmony_ci	(AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
7028c2ecf20Sopenharmony_ci	 (((div) - 1) << AC_FMT_DIV_SHIFT))
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cistatic const struct hda_rate_tbl rate_bits[] = {
7058c2ecf20Sopenharmony_ci	/* rate in Hz, ALSA rate bitmask, HDA format value */
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	/* autodetected value used in snd_hda_query_supported_pcm */
7088c2ecf20Sopenharmony_ci	{ 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
7098c2ecf20Sopenharmony_ci	{ 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
7108c2ecf20Sopenharmony_ci	{ 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
7118c2ecf20Sopenharmony_ci	{ 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
7128c2ecf20Sopenharmony_ci	{ 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
7138c2ecf20Sopenharmony_ci	{ 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
7148c2ecf20Sopenharmony_ci	{ 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
7158c2ecf20Sopenharmony_ci	{ 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
7168c2ecf20Sopenharmony_ci	{ 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
7178c2ecf20Sopenharmony_ci	{ 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
7188c2ecf20Sopenharmony_ci	{ 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
7198c2ecf20Sopenharmony_ci#define AC_PAR_PCM_RATE_BITS	11
7208c2ecf20Sopenharmony_ci	/* up to bits 10, 384kHZ isn't supported properly */
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* not autodetected value */
7238c2ecf20Sopenharmony_ci	{ 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	{ 0 } /* terminator */
7268c2ecf20Sopenharmony_ci};
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci/**
7298c2ecf20Sopenharmony_ci * snd_hdac_calc_stream_format - calculate the format bitset
7308c2ecf20Sopenharmony_ci * @rate: the sample rate
7318c2ecf20Sopenharmony_ci * @channels: the number of channels
7328c2ecf20Sopenharmony_ci * @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
7338c2ecf20Sopenharmony_ci * @maxbps: the max. bps
7348c2ecf20Sopenharmony_ci * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant)
7358c2ecf20Sopenharmony_ci *
7368c2ecf20Sopenharmony_ci * Calculate the format bitset from the given rate, channels and th PCM format.
7378c2ecf20Sopenharmony_ci *
7388c2ecf20Sopenharmony_ci * Return zero if invalid.
7398c2ecf20Sopenharmony_ci */
7408c2ecf20Sopenharmony_ciunsigned int snd_hdac_calc_stream_format(unsigned int rate,
7418c2ecf20Sopenharmony_ci					 unsigned int channels,
7428c2ecf20Sopenharmony_ci					 snd_pcm_format_t format,
7438c2ecf20Sopenharmony_ci					 unsigned int maxbps,
7448c2ecf20Sopenharmony_ci					 unsigned short spdif_ctls)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	int i;
7478c2ecf20Sopenharmony_ci	unsigned int val = 0;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	for (i = 0; rate_bits[i].hz; i++)
7508c2ecf20Sopenharmony_ci		if (rate_bits[i].hz == rate) {
7518c2ecf20Sopenharmony_ci			val = rate_bits[i].hda_fmt;
7528c2ecf20Sopenharmony_ci			break;
7538c2ecf20Sopenharmony_ci		}
7548c2ecf20Sopenharmony_ci	if (!rate_bits[i].hz)
7558c2ecf20Sopenharmony_ci		return 0;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (channels == 0 || channels > 8)
7588c2ecf20Sopenharmony_ci		return 0;
7598c2ecf20Sopenharmony_ci	val |= channels - 1;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	switch (snd_pcm_format_width(format)) {
7628c2ecf20Sopenharmony_ci	case 8:
7638c2ecf20Sopenharmony_ci		val |= AC_FMT_BITS_8;
7648c2ecf20Sopenharmony_ci		break;
7658c2ecf20Sopenharmony_ci	case 16:
7668c2ecf20Sopenharmony_ci		val |= AC_FMT_BITS_16;
7678c2ecf20Sopenharmony_ci		break;
7688c2ecf20Sopenharmony_ci	case 20:
7698c2ecf20Sopenharmony_ci	case 24:
7708c2ecf20Sopenharmony_ci	case 32:
7718c2ecf20Sopenharmony_ci		if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
7728c2ecf20Sopenharmony_ci			val |= AC_FMT_BITS_32;
7738c2ecf20Sopenharmony_ci		else if (maxbps >= 24)
7748c2ecf20Sopenharmony_ci			val |= AC_FMT_BITS_24;
7758c2ecf20Sopenharmony_ci		else
7768c2ecf20Sopenharmony_ci			val |= AC_FMT_BITS_20;
7778c2ecf20Sopenharmony_ci		break;
7788c2ecf20Sopenharmony_ci	default:
7798c2ecf20Sopenharmony_ci		return 0;
7808c2ecf20Sopenharmony_ci	}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (spdif_ctls & AC_DIG1_NONAUDIO)
7838c2ecf20Sopenharmony_ci		val |= AC_FMT_TYPE_NON_PCM;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	return val;
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_cistatic unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	unsigned int val = 0;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	if (nid != codec->afg &&
7948c2ecf20Sopenharmony_ci	    (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
7958c2ecf20Sopenharmony_ci		val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM);
7968c2ecf20Sopenharmony_ci	if (!val || val == -1)
7978c2ecf20Sopenharmony_ci		val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM);
7988c2ecf20Sopenharmony_ci	if (!val || val == -1)
7998c2ecf20Sopenharmony_ci		return 0;
8008c2ecf20Sopenharmony_ci	return val;
8018c2ecf20Sopenharmony_ci}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_cistatic unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid)
8048c2ecf20Sopenharmony_ci{
8058c2ecf20Sopenharmony_ci	unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (!streams || streams == -1)
8088c2ecf20Sopenharmony_ci		streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM);
8098c2ecf20Sopenharmony_ci	if (!streams || streams == -1)
8108c2ecf20Sopenharmony_ci		return 0;
8118c2ecf20Sopenharmony_ci	return streams;
8128c2ecf20Sopenharmony_ci}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci/**
8158c2ecf20Sopenharmony_ci * snd_hdac_query_supported_pcm - query the supported PCM rates and formats
8168c2ecf20Sopenharmony_ci * @codec: the codec object
8178c2ecf20Sopenharmony_ci * @nid: NID to query
8188c2ecf20Sopenharmony_ci * @ratesp: the pointer to store the detected rate bitflags
8198c2ecf20Sopenharmony_ci * @formatsp: the pointer to store the detected formats
8208c2ecf20Sopenharmony_ci * @bpsp: the pointer to store the detected format widths
8218c2ecf20Sopenharmony_ci *
8228c2ecf20Sopenharmony_ci * Queries the supported PCM rates and formats.  The NULL @ratesp, @formatsp
8238c2ecf20Sopenharmony_ci * or @bsps argument is ignored.
8248c2ecf20Sopenharmony_ci *
8258c2ecf20Sopenharmony_ci * Returns 0 if successful, otherwise a negative error code.
8268c2ecf20Sopenharmony_ci */
8278c2ecf20Sopenharmony_ciint snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
8288c2ecf20Sopenharmony_ci				 u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	unsigned int i, val, wcaps;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	wcaps = get_wcaps(codec, nid);
8338c2ecf20Sopenharmony_ci	val = query_pcm_param(codec, nid);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	if (ratesp) {
8368c2ecf20Sopenharmony_ci		u32 rates = 0;
8378c2ecf20Sopenharmony_ci		for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) {
8388c2ecf20Sopenharmony_ci			if (val & (1 << i))
8398c2ecf20Sopenharmony_ci				rates |= rate_bits[i].alsa_bits;
8408c2ecf20Sopenharmony_ci		}
8418c2ecf20Sopenharmony_ci		if (rates == 0) {
8428c2ecf20Sopenharmony_ci			dev_err(&codec->dev,
8438c2ecf20Sopenharmony_ci				"rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n",
8448c2ecf20Sopenharmony_ci				nid, val,
8458c2ecf20Sopenharmony_ci				(wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
8468c2ecf20Sopenharmony_ci			return -EIO;
8478c2ecf20Sopenharmony_ci		}
8488c2ecf20Sopenharmony_ci		*ratesp = rates;
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	if (formatsp || bpsp) {
8528c2ecf20Sopenharmony_ci		u64 formats = 0;
8538c2ecf20Sopenharmony_ci		unsigned int streams, bps;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci		streams = query_stream_param(codec, nid);
8568c2ecf20Sopenharmony_ci		if (!streams)
8578c2ecf20Sopenharmony_ci			return -EIO;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci		bps = 0;
8608c2ecf20Sopenharmony_ci		if (streams & AC_SUPFMT_PCM) {
8618c2ecf20Sopenharmony_ci			if (val & AC_SUPPCM_BITS_8) {
8628c2ecf20Sopenharmony_ci				formats |= SNDRV_PCM_FMTBIT_U8;
8638c2ecf20Sopenharmony_ci				bps = 8;
8648c2ecf20Sopenharmony_ci			}
8658c2ecf20Sopenharmony_ci			if (val & AC_SUPPCM_BITS_16) {
8668c2ecf20Sopenharmony_ci				formats |= SNDRV_PCM_FMTBIT_S16_LE;
8678c2ecf20Sopenharmony_ci				bps = 16;
8688c2ecf20Sopenharmony_ci			}
8698c2ecf20Sopenharmony_ci			if (wcaps & AC_WCAP_DIGITAL) {
8708c2ecf20Sopenharmony_ci				if (val & AC_SUPPCM_BITS_32)
8718c2ecf20Sopenharmony_ci					formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
8728c2ecf20Sopenharmony_ci				if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
8738c2ecf20Sopenharmony_ci					formats |= SNDRV_PCM_FMTBIT_S32_LE;
8748c2ecf20Sopenharmony_ci				if (val & AC_SUPPCM_BITS_24)
8758c2ecf20Sopenharmony_ci					bps = 24;
8768c2ecf20Sopenharmony_ci				else if (val & AC_SUPPCM_BITS_20)
8778c2ecf20Sopenharmony_ci					bps = 20;
8788c2ecf20Sopenharmony_ci			} else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|
8798c2ecf20Sopenharmony_ci					  AC_SUPPCM_BITS_32)) {
8808c2ecf20Sopenharmony_ci				formats |= SNDRV_PCM_FMTBIT_S32_LE;
8818c2ecf20Sopenharmony_ci				if (val & AC_SUPPCM_BITS_32)
8828c2ecf20Sopenharmony_ci					bps = 32;
8838c2ecf20Sopenharmony_ci				else if (val & AC_SUPPCM_BITS_24)
8848c2ecf20Sopenharmony_ci					bps = 24;
8858c2ecf20Sopenharmony_ci				else if (val & AC_SUPPCM_BITS_20)
8868c2ecf20Sopenharmony_ci					bps = 20;
8878c2ecf20Sopenharmony_ci			}
8888c2ecf20Sopenharmony_ci		}
8898c2ecf20Sopenharmony_ci#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
8908c2ecf20Sopenharmony_ci		if (streams & AC_SUPFMT_FLOAT32) {
8918c2ecf20Sopenharmony_ci			formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
8928c2ecf20Sopenharmony_ci			if (!bps)
8938c2ecf20Sopenharmony_ci				bps = 32;
8948c2ecf20Sopenharmony_ci		}
8958c2ecf20Sopenharmony_ci#endif
8968c2ecf20Sopenharmony_ci		if (streams == AC_SUPFMT_AC3) {
8978c2ecf20Sopenharmony_ci			/* should be exclusive */
8988c2ecf20Sopenharmony_ci			/* temporary hack: we have still no proper support
8998c2ecf20Sopenharmony_ci			 * for the direct AC3 stream...
9008c2ecf20Sopenharmony_ci			 */
9018c2ecf20Sopenharmony_ci			formats |= SNDRV_PCM_FMTBIT_U8;
9028c2ecf20Sopenharmony_ci			bps = 8;
9038c2ecf20Sopenharmony_ci		}
9048c2ecf20Sopenharmony_ci		if (formats == 0) {
9058c2ecf20Sopenharmony_ci			dev_err(&codec->dev,
9068c2ecf20Sopenharmony_ci				"formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n",
9078c2ecf20Sopenharmony_ci				nid, val,
9088c2ecf20Sopenharmony_ci				(wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
9098c2ecf20Sopenharmony_ci				streams);
9108c2ecf20Sopenharmony_ci			return -EIO;
9118c2ecf20Sopenharmony_ci		}
9128c2ecf20Sopenharmony_ci		if (formatsp)
9138c2ecf20Sopenharmony_ci			*formatsp = formats;
9148c2ecf20Sopenharmony_ci		if (bpsp)
9158c2ecf20Sopenharmony_ci			*bpsp = bps;
9168c2ecf20Sopenharmony_ci	}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	return 0;
9198c2ecf20Sopenharmony_ci}
9208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci/**
9238c2ecf20Sopenharmony_ci * snd_hdac_is_supported_format - Check the validity of the format
9248c2ecf20Sopenharmony_ci * @codec: the codec object
9258c2ecf20Sopenharmony_ci * @nid: NID to check
9268c2ecf20Sopenharmony_ci * @format: the HD-audio format value to check
9278c2ecf20Sopenharmony_ci *
9288c2ecf20Sopenharmony_ci * Check whether the given node supports the format value.
9298c2ecf20Sopenharmony_ci *
9308c2ecf20Sopenharmony_ci * Returns true if supported, false if not.
9318c2ecf20Sopenharmony_ci */
9328c2ecf20Sopenharmony_cibool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
9338c2ecf20Sopenharmony_ci				  unsigned int format)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	int i;
9368c2ecf20Sopenharmony_ci	unsigned int val = 0, rate, stream;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	val = query_pcm_param(codec, nid);
9398c2ecf20Sopenharmony_ci	if (!val)
9408c2ecf20Sopenharmony_ci		return false;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	rate = format & 0xff00;
9438c2ecf20Sopenharmony_ci	for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
9448c2ecf20Sopenharmony_ci		if (rate_bits[i].hda_fmt == rate) {
9458c2ecf20Sopenharmony_ci			if (val & (1 << i))
9468c2ecf20Sopenharmony_ci				break;
9478c2ecf20Sopenharmony_ci			return false;
9488c2ecf20Sopenharmony_ci		}
9498c2ecf20Sopenharmony_ci	if (i >= AC_PAR_PCM_RATE_BITS)
9508c2ecf20Sopenharmony_ci		return false;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	stream = query_stream_param(codec, nid);
9538c2ecf20Sopenharmony_ci	if (!stream)
9548c2ecf20Sopenharmony_ci		return false;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	if (stream & AC_SUPFMT_PCM) {
9578c2ecf20Sopenharmony_ci		switch (format & 0xf0) {
9588c2ecf20Sopenharmony_ci		case 0x00:
9598c2ecf20Sopenharmony_ci			if (!(val & AC_SUPPCM_BITS_8))
9608c2ecf20Sopenharmony_ci				return false;
9618c2ecf20Sopenharmony_ci			break;
9628c2ecf20Sopenharmony_ci		case 0x10:
9638c2ecf20Sopenharmony_ci			if (!(val & AC_SUPPCM_BITS_16))
9648c2ecf20Sopenharmony_ci				return false;
9658c2ecf20Sopenharmony_ci			break;
9668c2ecf20Sopenharmony_ci		case 0x20:
9678c2ecf20Sopenharmony_ci			if (!(val & AC_SUPPCM_BITS_20))
9688c2ecf20Sopenharmony_ci				return false;
9698c2ecf20Sopenharmony_ci			break;
9708c2ecf20Sopenharmony_ci		case 0x30:
9718c2ecf20Sopenharmony_ci			if (!(val & AC_SUPPCM_BITS_24))
9728c2ecf20Sopenharmony_ci				return false;
9738c2ecf20Sopenharmony_ci			break;
9748c2ecf20Sopenharmony_ci		case 0x40:
9758c2ecf20Sopenharmony_ci			if (!(val & AC_SUPPCM_BITS_32))
9768c2ecf20Sopenharmony_ci				return false;
9778c2ecf20Sopenharmony_ci			break;
9788c2ecf20Sopenharmony_ci		default:
9798c2ecf20Sopenharmony_ci			return false;
9808c2ecf20Sopenharmony_ci		}
9818c2ecf20Sopenharmony_ci	} else {
9828c2ecf20Sopenharmony_ci		/* FIXME: check for float32 and AC3? */
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	return true;
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_cistatic unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
9908c2ecf20Sopenharmony_ci			int flags, unsigned int verb, unsigned int parm)
9918c2ecf20Sopenharmony_ci{
9928c2ecf20Sopenharmony_ci	unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
9938c2ecf20Sopenharmony_ci	unsigned int res;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
9968c2ecf20Sopenharmony_ci		return -1;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	return res;
9998c2ecf20Sopenharmony_ci}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_cistatic int codec_write(struct hdac_device *hdac, hda_nid_t nid,
10028c2ecf20Sopenharmony_ci			int flags, unsigned int verb, unsigned int parm)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
10078c2ecf20Sopenharmony_ci}
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci/**
10108c2ecf20Sopenharmony_ci * snd_hdac_codec_read - send a command and get the response
10118c2ecf20Sopenharmony_ci * @hdac: the HDAC device
10128c2ecf20Sopenharmony_ci * @nid: NID to send the command
10138c2ecf20Sopenharmony_ci * @flags: optional bit flags
10148c2ecf20Sopenharmony_ci * @verb: the verb to send
10158c2ecf20Sopenharmony_ci * @parm: the parameter for the verb
10168c2ecf20Sopenharmony_ci *
10178c2ecf20Sopenharmony_ci * Send a single command and read the corresponding response.
10188c2ecf20Sopenharmony_ci *
10198c2ecf20Sopenharmony_ci * Returns the obtained response value, or -1 for an error.
10208c2ecf20Sopenharmony_ci */
10218c2ecf20Sopenharmony_ciint snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
10228c2ecf20Sopenharmony_ci			int flags, unsigned int verb, unsigned int parm)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	return codec_read(hdac, nid, flags, verb, parm);
10258c2ecf20Sopenharmony_ci}
10268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_codec_read);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci/**
10298c2ecf20Sopenharmony_ci * snd_hdac_codec_write - send a single command without waiting for response
10308c2ecf20Sopenharmony_ci * @hdac: the HDAC device
10318c2ecf20Sopenharmony_ci * @nid: NID to send the command
10328c2ecf20Sopenharmony_ci * @flags: optional bit flags
10338c2ecf20Sopenharmony_ci * @verb: the verb to send
10348c2ecf20Sopenharmony_ci * @parm: the parameter for the verb
10358c2ecf20Sopenharmony_ci *
10368c2ecf20Sopenharmony_ci * Send a single command without waiting for response.
10378c2ecf20Sopenharmony_ci *
10388c2ecf20Sopenharmony_ci * Returns 0 if successful, or a negative error code.
10398c2ecf20Sopenharmony_ci */
10408c2ecf20Sopenharmony_ciint snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
10418c2ecf20Sopenharmony_ci			int flags, unsigned int verb, unsigned int parm)
10428c2ecf20Sopenharmony_ci{
10438c2ecf20Sopenharmony_ci	return codec_write(hdac, nid, flags, verb, parm);
10448c2ecf20Sopenharmony_ci}
10458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_codec_write);
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci/**
10488c2ecf20Sopenharmony_ci * snd_hdac_check_power_state - check whether the actual power state matches
10498c2ecf20Sopenharmony_ci * with the target state
10508c2ecf20Sopenharmony_ci *
10518c2ecf20Sopenharmony_ci * @hdac: the HDAC device
10528c2ecf20Sopenharmony_ci * @nid: NID to send the command
10538c2ecf20Sopenharmony_ci * @target_state: target state to check for
10548c2ecf20Sopenharmony_ci *
10558c2ecf20Sopenharmony_ci * Return true if state matches, false if not
10568c2ecf20Sopenharmony_ci */
10578c2ecf20Sopenharmony_cibool snd_hdac_check_power_state(struct hdac_device *hdac,
10588c2ecf20Sopenharmony_ci		hda_nid_t nid, unsigned int target_state)
10598c2ecf20Sopenharmony_ci{
10608c2ecf20Sopenharmony_ci	unsigned int state = codec_read(hdac, nid, 0,
10618c2ecf20Sopenharmony_ci				AC_VERB_GET_POWER_STATE, 0);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	if (state & AC_PWRST_ERROR)
10648c2ecf20Sopenharmony_ci		return true;
10658c2ecf20Sopenharmony_ci	state = (state >> 4) & 0x0f;
10668c2ecf20Sopenharmony_ci	return (state == target_state);
10678c2ecf20Sopenharmony_ci}
10688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
10698c2ecf20Sopenharmony_ci/**
10708c2ecf20Sopenharmony_ci * snd_hdac_sync_power_state - wait until actual power state matches
10718c2ecf20Sopenharmony_ci * with the target state
10728c2ecf20Sopenharmony_ci *
10738c2ecf20Sopenharmony_ci * @codec: the HDAC device
10748c2ecf20Sopenharmony_ci * @nid: NID to send the command
10758c2ecf20Sopenharmony_ci * @power_state: target power state to wait for
10768c2ecf20Sopenharmony_ci *
10778c2ecf20Sopenharmony_ci * Return power state or PS_ERROR if codec rejects GET verb.
10788c2ecf20Sopenharmony_ci */
10798c2ecf20Sopenharmony_ciunsigned int snd_hdac_sync_power_state(struct hdac_device *codec,
10808c2ecf20Sopenharmony_ci			hda_nid_t nid, unsigned int power_state)
10818c2ecf20Sopenharmony_ci{
10828c2ecf20Sopenharmony_ci	unsigned long end_time = jiffies + msecs_to_jiffies(500);
10838c2ecf20Sopenharmony_ci	unsigned int state, actual_state, count;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	for (count = 0; count < 500; count++) {
10868c2ecf20Sopenharmony_ci		state = snd_hdac_codec_read(codec, nid, 0,
10878c2ecf20Sopenharmony_ci				AC_VERB_GET_POWER_STATE, 0);
10888c2ecf20Sopenharmony_ci		if (state & AC_PWRST_ERROR) {
10898c2ecf20Sopenharmony_ci			msleep(20);
10908c2ecf20Sopenharmony_ci			break;
10918c2ecf20Sopenharmony_ci		}
10928c2ecf20Sopenharmony_ci		actual_state = (state >> 4) & 0x0f;
10938c2ecf20Sopenharmony_ci		if (actual_state == power_state)
10948c2ecf20Sopenharmony_ci			break;
10958c2ecf20Sopenharmony_ci		if (time_after_eq(jiffies, end_time))
10968c2ecf20Sopenharmony_ci			break;
10978c2ecf20Sopenharmony_ci		/* wait until the codec reachs to the target state */
10988c2ecf20Sopenharmony_ci		msleep(1);
10998c2ecf20Sopenharmony_ci	}
11008c2ecf20Sopenharmony_ci	return state;
11018c2ecf20Sopenharmony_ci}
11028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_sync_power_state);
1103