xref: /openbmc/linux/sound/hda/hdac_sysfs.c (revision aacdac35)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
23256be65STakashi Iwai /*
33256be65STakashi Iwai  * sysfs support for HD-audio core device
43256be65STakashi Iwai  */
53256be65STakashi Iwai 
63256be65STakashi Iwai #include <linux/slab.h>
73256be65STakashi Iwai #include <linux/sysfs.h>
83256be65STakashi Iwai #include <linux/device.h>
93256be65STakashi Iwai #include <sound/core.h>
103256be65STakashi Iwai #include <sound/hdaudio.h>
113256be65STakashi Iwai #include "local.h"
123256be65STakashi Iwai 
133256be65STakashi Iwai struct hdac_widget_tree {
143256be65STakashi Iwai 	struct kobject *root;
153256be65STakashi Iwai 	struct kobject *afg;
163256be65STakashi Iwai 	struct kobject **nodes;
173256be65STakashi Iwai };
183256be65STakashi Iwai 
193256be65STakashi Iwai #define CODEC_ATTR(type)					\
203256be65STakashi Iwai static ssize_t type##_show(struct device *dev,			\
213256be65STakashi Iwai 			   struct device_attribute *attr,	\
223256be65STakashi Iwai 			   char *buf)				\
233256be65STakashi Iwai {								\
243256be65STakashi Iwai 	struct hdac_device *codec = dev_to_hdac_dev(dev);	\
2517daae7aSTakashi Iwai 	return sysfs_emit(buf, "0x%x\n", codec->type);		\
263256be65STakashi Iwai } \
273256be65STakashi Iwai static DEVICE_ATTR_RO(type)
283256be65STakashi Iwai 
293256be65STakashi Iwai #define CODEC_ATTR_STR(type)					\
303256be65STakashi Iwai static ssize_t type##_show(struct device *dev,			\
313256be65STakashi Iwai 			     struct device_attribute *attr,	\
323256be65STakashi Iwai 					char *buf)		\
333256be65STakashi Iwai {								\
343256be65STakashi Iwai 	struct hdac_device *codec = dev_to_hdac_dev(dev);	\
3517daae7aSTakashi Iwai 	return sysfs_emit(buf, "%s\n",				\
363256be65STakashi Iwai 			  codec->type ? codec->type : "");	\
373256be65STakashi Iwai } \
383256be65STakashi Iwai static DEVICE_ATTR_RO(type)
393256be65STakashi Iwai 
40eacf6e0aSTakashi Iwai CODEC_ATTR(type);
413256be65STakashi Iwai CODEC_ATTR(vendor_id);
423256be65STakashi Iwai CODEC_ATTR(subsystem_id);
433256be65STakashi Iwai CODEC_ATTR(revision_id);
443256be65STakashi Iwai CODEC_ATTR(afg);
453256be65STakashi Iwai CODEC_ATTR(mfg);
463256be65STakashi Iwai CODEC_ATTR_STR(vendor_name);
473256be65STakashi Iwai CODEC_ATTR_STR(chip_name);
483256be65STakashi Iwai 
modalias_show(struct device * dev,struct device_attribute * attr,char * buf)4978abb2afSSubhransu S. Prusty static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
5078abb2afSSubhransu S. Prusty 			     char *buf)
5178abb2afSSubhransu S. Prusty {
5278abb2afSSubhransu S. Prusty 	return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
5378abb2afSSubhransu S. Prusty }
5478abb2afSSubhransu S. Prusty static DEVICE_ATTR_RO(modalias);
5578abb2afSSubhransu S. Prusty 
563256be65STakashi Iwai static struct attribute *hdac_dev_attrs[] = {
57eacf6e0aSTakashi Iwai 	&dev_attr_type.attr,
583256be65STakashi Iwai 	&dev_attr_vendor_id.attr,
593256be65STakashi Iwai 	&dev_attr_subsystem_id.attr,
603256be65STakashi Iwai 	&dev_attr_revision_id.attr,
613256be65STakashi Iwai 	&dev_attr_afg.attr,
623256be65STakashi Iwai 	&dev_attr_mfg.attr,
633256be65STakashi Iwai 	&dev_attr_vendor_name.attr,
643256be65STakashi Iwai 	&dev_attr_chip_name.attr,
6578abb2afSSubhransu S. Prusty 	&dev_attr_modalias.attr,
663256be65STakashi Iwai 	NULL
673256be65STakashi Iwai };
683256be65STakashi Iwai 
690417fadaSRikard Falkeborn static const struct attribute_group hdac_dev_attr_group = {
703256be65STakashi Iwai 	.attrs	= hdac_dev_attrs,
713256be65STakashi Iwai };
723256be65STakashi Iwai 
733256be65STakashi Iwai const struct attribute_group *hdac_dev_attr_groups[] = {
743256be65STakashi Iwai 	&hdac_dev_attr_group,
753256be65STakashi Iwai 	NULL
763256be65STakashi Iwai };
773256be65STakashi Iwai 
783256be65STakashi Iwai /*
793256be65STakashi Iwai  * Widget tree sysfs
803256be65STakashi Iwai  *
813256be65STakashi Iwai  * This is a tree showing the attributes of each widget.  It appears like
823256be65STakashi Iwai  * /sys/bus/hdaudioC0D0/widgets/04/caps
833256be65STakashi Iwai  */
843256be65STakashi Iwai 
853256be65STakashi Iwai struct widget_attribute;
863256be65STakashi Iwai 
873256be65STakashi Iwai struct widget_attribute {
883256be65STakashi Iwai 	struct attribute	attr;
893256be65STakashi Iwai 	ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid,
903256be65STakashi Iwai 			struct widget_attribute *attr, char *buf);
913256be65STakashi Iwai 	ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid,
923256be65STakashi Iwai 			 struct widget_attribute *attr,
933256be65STakashi Iwai 			 const char *buf, size_t count);
943256be65STakashi Iwai };
953256be65STakashi Iwai 
get_codec_nid(struct kobject * kobj,struct hdac_device ** codecp)963256be65STakashi Iwai static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp)
973256be65STakashi Iwai {
983256be65STakashi Iwai 	struct device *dev = kobj_to_dev(kobj->parent->parent);
993256be65STakashi Iwai 	int nid;
1003256be65STakashi Iwai 	ssize_t ret;
1013256be65STakashi Iwai 
1023256be65STakashi Iwai 	ret = kstrtoint(kobj->name, 16, &nid);
1033256be65STakashi Iwai 	if (ret < 0)
1043256be65STakashi Iwai 		return ret;
1053256be65STakashi Iwai 	*codecp = dev_to_hdac_dev(dev);
1063256be65STakashi Iwai 	return nid;
1073256be65STakashi Iwai }
1083256be65STakashi Iwai 
widget_attr_show(struct kobject * kobj,struct attribute * attr,char * buf)1093256be65STakashi Iwai static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr,
1103256be65STakashi Iwai 				char *buf)
1113256be65STakashi Iwai {
1123256be65STakashi Iwai 	struct widget_attribute *wid_attr =
1133256be65STakashi Iwai 		container_of(attr, struct widget_attribute, attr);
1143256be65STakashi Iwai 	struct hdac_device *codec;
1153256be65STakashi Iwai 	int nid;
1163256be65STakashi Iwai 
1173256be65STakashi Iwai 	if (!wid_attr->show)
1183256be65STakashi Iwai 		return -EIO;
1193256be65STakashi Iwai 	nid = get_codec_nid(kobj, &codec);
1203256be65STakashi Iwai 	if (nid < 0)
1213256be65STakashi Iwai 		return nid;
1223256be65STakashi Iwai 	return wid_attr->show(codec, nid, wid_attr, buf);
1233256be65STakashi Iwai }
1243256be65STakashi Iwai 
widget_attr_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t count)1253256be65STakashi Iwai static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr,
1263256be65STakashi Iwai 				 const char *buf, size_t count)
1273256be65STakashi Iwai {
1283256be65STakashi Iwai 	struct widget_attribute *wid_attr =
1293256be65STakashi Iwai 		container_of(attr, struct widget_attribute, attr);
1303256be65STakashi Iwai 	struct hdac_device *codec;
1313256be65STakashi Iwai 	int nid;
1323256be65STakashi Iwai 
1333256be65STakashi Iwai 	if (!wid_attr->store)
1343256be65STakashi Iwai 		return -EIO;
1353256be65STakashi Iwai 	nid = get_codec_nid(kobj, &codec);
1363256be65STakashi Iwai 	if (nid < 0)
1373256be65STakashi Iwai 		return nid;
1383256be65STakashi Iwai 	return wid_attr->store(codec, nid, wid_attr, buf, count);
1393256be65STakashi Iwai }
1403256be65STakashi Iwai 
1413256be65STakashi Iwai static const struct sysfs_ops widget_sysfs_ops = {
1423256be65STakashi Iwai 	.show	= widget_attr_show,
1433256be65STakashi Iwai 	.store	= widget_attr_store,
1443256be65STakashi Iwai };
1453256be65STakashi Iwai 
widget_release(struct kobject * kobj)1463256be65STakashi Iwai static void widget_release(struct kobject *kobj)
1473256be65STakashi Iwai {
1483256be65STakashi Iwai 	kfree(kobj);
1493256be65STakashi Iwai }
1503256be65STakashi Iwai 
151*aacdac35SThomas Weißschuh static const struct kobj_type widget_ktype = {
1523256be65STakashi Iwai 	.release	= widget_release,
1533256be65STakashi Iwai 	.sysfs_ops	= &widget_sysfs_ops,
1543256be65STakashi Iwai };
1553256be65STakashi Iwai 
1563256be65STakashi Iwai #define WIDGET_ATTR_RO(_name) \
1573256be65STakashi Iwai 	struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
1583256be65STakashi Iwai #define WIDGET_ATTR_RW(_name) \
1593256be65STakashi Iwai 	struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
1603256be65STakashi Iwai 
caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)1613256be65STakashi Iwai static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid,
1623256be65STakashi Iwai 			struct widget_attribute *attr, char *buf)
1633256be65STakashi Iwai {
16417daae7aSTakashi Iwai 	return sysfs_emit(buf, "0x%08x\n", get_wcaps(codec, nid));
1653256be65STakashi Iwai }
1663256be65STakashi Iwai 
pin_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)1673256be65STakashi Iwai static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid,
1683256be65STakashi Iwai 			     struct widget_attribute *attr, char *buf)
1693256be65STakashi Iwai {
1703256be65STakashi Iwai 	if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
1713256be65STakashi Iwai 		return 0;
17217daae7aSTakashi Iwai 	return sysfs_emit(buf, "0x%08x\n",
1733256be65STakashi Iwai 			  snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP));
1743256be65STakashi Iwai }
1753256be65STakashi Iwai 
pin_cfg_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)1763256be65STakashi Iwai static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid,
1773256be65STakashi Iwai 			    struct widget_attribute *attr, char *buf)
1783256be65STakashi Iwai {
1793256be65STakashi Iwai 	unsigned int val;
1803256be65STakashi Iwai 
1813256be65STakashi Iwai 	if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
1823256be65STakashi Iwai 		return 0;
1833256be65STakashi Iwai 	if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
1843256be65STakashi Iwai 		return 0;
18517daae7aSTakashi Iwai 	return sysfs_emit(buf, "0x%08x\n", val);
1863256be65STakashi Iwai }
1873256be65STakashi Iwai 
has_pcm_cap(struct hdac_device * codec,hda_nid_t nid)1883256be65STakashi Iwai static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid)
1893256be65STakashi Iwai {
1903256be65STakashi Iwai 	if (nid == codec->afg || nid == codec->mfg)
1913256be65STakashi Iwai 		return true;
1923256be65STakashi Iwai 	switch (get_wcaps_type(get_wcaps(codec, nid))) {
1933256be65STakashi Iwai 	case AC_WID_AUD_OUT:
1943256be65STakashi Iwai 	case AC_WID_AUD_IN:
1953256be65STakashi Iwai 		return true;
1963256be65STakashi Iwai 	default:
1973256be65STakashi Iwai 		return false;
1983256be65STakashi Iwai 	}
1993256be65STakashi Iwai }
2003256be65STakashi Iwai 
pcm_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)2013256be65STakashi Iwai static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid,
2023256be65STakashi Iwai 			     struct widget_attribute *attr, char *buf)
2033256be65STakashi Iwai {
2043256be65STakashi Iwai 	if (!has_pcm_cap(codec, nid))
2053256be65STakashi Iwai 		return 0;
20617daae7aSTakashi Iwai 	return sysfs_emit(buf, "0x%08x\n",
2073256be65STakashi Iwai 			  snd_hdac_read_parm(codec, nid, AC_PAR_PCM));
2083256be65STakashi Iwai }
2093256be65STakashi Iwai 
pcm_formats_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)2103256be65STakashi Iwai static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid,
2113256be65STakashi Iwai 				struct widget_attribute *attr, char *buf)
2123256be65STakashi Iwai {
2133256be65STakashi Iwai 	if (!has_pcm_cap(codec, nid))
2143256be65STakashi Iwai 		return 0;
21517daae7aSTakashi Iwai 	return sysfs_emit(buf, "0x%08x\n",
2163256be65STakashi Iwai 			  snd_hdac_read_parm(codec, nid, AC_PAR_STREAM));
2173256be65STakashi Iwai }
2183256be65STakashi Iwai 
amp_in_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)2193256be65STakashi Iwai static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid,
2203256be65STakashi Iwai 				struct widget_attribute *attr, char *buf)
2213256be65STakashi Iwai {
2223256be65STakashi Iwai 	if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
2233256be65STakashi Iwai 		return 0;
22417daae7aSTakashi Iwai 	return sysfs_emit(buf, "0x%08x\n",
2253256be65STakashi Iwai 			  snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP));
2263256be65STakashi Iwai }
2273256be65STakashi Iwai 
amp_out_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)2283256be65STakashi Iwai static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid,
2293256be65STakashi Iwai 				 struct widget_attribute *attr, char *buf)
2303256be65STakashi Iwai {
2313256be65STakashi Iwai 	if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
2323256be65STakashi Iwai 		return 0;
23317daae7aSTakashi Iwai 	return sysfs_emit(buf, "0x%08x\n",
2343256be65STakashi Iwai 			  snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP));
2353256be65STakashi Iwai }
2363256be65STakashi Iwai 
power_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)2373256be65STakashi Iwai static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid,
2383256be65STakashi Iwai 			       struct widget_attribute *attr, char *buf)
2393256be65STakashi Iwai {
2403256be65STakashi Iwai 	if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER))
2413256be65STakashi Iwai 		return 0;
24217daae7aSTakashi Iwai 	return sysfs_emit(buf, "0x%08x\n",
2433256be65STakashi Iwai 			  snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE));
2443256be65STakashi Iwai }
2453256be65STakashi Iwai 
gpio_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)2463256be65STakashi Iwai static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid,
2473256be65STakashi Iwai 			      struct widget_attribute *attr, char *buf)
2483256be65STakashi Iwai {
24917daae7aSTakashi Iwai 	return sysfs_emit(buf, "0x%08x\n",
2503256be65STakashi Iwai 			  snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP));
2513256be65STakashi Iwai }
2523256be65STakashi Iwai 
connections_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)2533256be65STakashi Iwai static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid,
2543256be65STakashi Iwai 				struct widget_attribute *attr, char *buf)
2553256be65STakashi Iwai {
2563256be65STakashi Iwai 	hda_nid_t list[32];
2573256be65STakashi Iwai 	int i, nconns;
2583256be65STakashi Iwai 	ssize_t ret = 0;
2593256be65STakashi Iwai 
2603256be65STakashi Iwai 	nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list));
2613256be65STakashi Iwai 	if (nconns <= 0)
2623256be65STakashi Iwai 		return nconns;
2633256be65STakashi Iwai 	for (i = 0; i < nconns; i++)
26417daae7aSTakashi Iwai 		ret += sysfs_emit_at(buf,  ret, "%s0x%02x", i ? " " : "", list[i]);
26517daae7aSTakashi Iwai 	ret += sysfs_emit_at(buf, ret, "\n");
2663256be65STakashi Iwai 	return ret;
2673256be65STakashi Iwai }
2683256be65STakashi Iwai 
2693256be65STakashi Iwai static WIDGET_ATTR_RO(caps);
2703256be65STakashi Iwai static WIDGET_ATTR_RO(pin_caps);
2713256be65STakashi Iwai static WIDGET_ATTR_RO(pin_cfg);
2723256be65STakashi Iwai static WIDGET_ATTR_RO(pcm_caps);
2733256be65STakashi Iwai static WIDGET_ATTR_RO(pcm_formats);
2743256be65STakashi Iwai static WIDGET_ATTR_RO(amp_in_caps);
2753256be65STakashi Iwai static WIDGET_ATTR_RO(amp_out_caps);
2763256be65STakashi Iwai static WIDGET_ATTR_RO(power_caps);
2773256be65STakashi Iwai static WIDGET_ATTR_RO(gpio_caps);
2783256be65STakashi Iwai static WIDGET_ATTR_RO(connections);
2793256be65STakashi Iwai 
2803256be65STakashi Iwai static struct attribute *widget_node_attrs[] = {
2813256be65STakashi Iwai 	&wid_attr_caps.attr,
2823256be65STakashi Iwai 	&wid_attr_pin_caps.attr,
2833256be65STakashi Iwai 	&wid_attr_pin_cfg.attr,
2843256be65STakashi Iwai 	&wid_attr_pcm_caps.attr,
2853256be65STakashi Iwai 	&wid_attr_pcm_formats.attr,
2863256be65STakashi Iwai 	&wid_attr_amp_in_caps.attr,
2873256be65STakashi Iwai 	&wid_attr_amp_out_caps.attr,
2883256be65STakashi Iwai 	&wid_attr_power_caps.attr,
2893256be65STakashi Iwai 	&wid_attr_connections.attr,
2903256be65STakashi Iwai 	NULL,
2913256be65STakashi Iwai };
2923256be65STakashi Iwai 
2933256be65STakashi Iwai static struct attribute *widget_afg_attrs[] = {
2943256be65STakashi Iwai 	&wid_attr_pcm_caps.attr,
2953256be65STakashi Iwai 	&wid_attr_pcm_formats.attr,
2963256be65STakashi Iwai 	&wid_attr_amp_in_caps.attr,
2973256be65STakashi Iwai 	&wid_attr_amp_out_caps.attr,
2983256be65STakashi Iwai 	&wid_attr_power_caps.attr,
2993256be65STakashi Iwai 	&wid_attr_gpio_caps.attr,
3003256be65STakashi Iwai 	NULL,
3013256be65STakashi Iwai };
3023256be65STakashi Iwai 
3033256be65STakashi Iwai static const struct attribute_group widget_node_group = {
3043256be65STakashi Iwai 	.attrs = widget_node_attrs,
3053256be65STakashi Iwai };
3063256be65STakashi Iwai 
3073256be65STakashi Iwai static const struct attribute_group widget_afg_group = {
3083256be65STakashi Iwai 	.attrs = widget_afg_attrs,
3093256be65STakashi Iwai };
3103256be65STakashi Iwai 
free_widget_node(struct kobject * kobj,const struct attribute_group * group)3113256be65STakashi Iwai static void free_widget_node(struct kobject *kobj,
3123256be65STakashi Iwai 			     const struct attribute_group *group)
3133256be65STakashi Iwai {
3143256be65STakashi Iwai 	if (kobj) {
3153256be65STakashi Iwai 		sysfs_remove_group(kobj, group);
3163256be65STakashi Iwai 		kobject_put(kobj);
3173256be65STakashi Iwai 	}
3183256be65STakashi Iwai }
3193256be65STakashi Iwai 
widget_tree_free(struct hdac_device * codec)3203256be65STakashi Iwai static void widget_tree_free(struct hdac_device *codec)
3213256be65STakashi Iwai {
3223256be65STakashi Iwai 	struct hdac_widget_tree *tree = codec->widgets;
3233256be65STakashi Iwai 	struct kobject **p;
3243256be65STakashi Iwai 
3253256be65STakashi Iwai 	if (!tree)
3263256be65STakashi Iwai 		return;
327142267c9STakashi Iwai 	free_widget_node(tree->afg, &widget_afg_group);
3283256be65STakashi Iwai 	if (tree->nodes) {
3293256be65STakashi Iwai 		for (p = tree->nodes; *p; p++)
3303256be65STakashi Iwai 			free_widget_node(*p, &widget_node_group);
3313256be65STakashi Iwai 		kfree(tree->nodes);
3323256be65STakashi Iwai 	}
3333256be65STakashi Iwai 	kobject_put(tree->root);
3343256be65STakashi Iwai 	kfree(tree);
3353256be65STakashi Iwai 	codec->widgets = NULL;
3363256be65STakashi Iwai }
3373256be65STakashi Iwai 
add_widget_node(struct kobject * parent,hda_nid_t nid,const struct attribute_group * group,struct kobject ** res)3383256be65STakashi Iwai static int add_widget_node(struct kobject *parent, hda_nid_t nid,
3393256be65STakashi Iwai 			   const struct attribute_group *group,
3403256be65STakashi Iwai 			   struct kobject **res)
3413256be65STakashi Iwai {
3423256be65STakashi Iwai 	struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
3433256be65STakashi Iwai 	int err;
3443256be65STakashi Iwai 
3453256be65STakashi Iwai 	if (!kobj)
3463256be65STakashi Iwai 		return -ENOMEM;
3473256be65STakashi Iwai 	kobject_init(kobj, &widget_ktype);
3483256be65STakashi Iwai 	err = kobject_add(kobj, parent, "%02x", nid);
3499a5523f7SYe Bin 	if (err < 0) {
3509a5523f7SYe Bin 		kobject_put(kobj);
3513256be65STakashi Iwai 		return err;
3529a5523f7SYe Bin 	}
3533256be65STakashi Iwai 	err = sysfs_create_group(kobj, group);
3543256be65STakashi Iwai 	if (err < 0) {
3553256be65STakashi Iwai 		kobject_put(kobj);
3563256be65STakashi Iwai 		return err;
3573256be65STakashi Iwai 	}
3583256be65STakashi Iwai 
3593256be65STakashi Iwai 	*res = kobj;
3603256be65STakashi Iwai 	return 0;
3613256be65STakashi Iwai }
3623256be65STakashi Iwai 
widget_tree_create(struct hdac_device * codec)3633256be65STakashi Iwai static int widget_tree_create(struct hdac_device *codec)
3643256be65STakashi Iwai {
3653256be65STakashi Iwai 	struct hdac_widget_tree *tree;
3663256be65STakashi Iwai 	int i, err;
3673256be65STakashi Iwai 	hda_nid_t nid;
3683256be65STakashi Iwai 
3693256be65STakashi Iwai 	tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL);
3703256be65STakashi Iwai 	if (!tree)
3713256be65STakashi Iwai 		return -ENOMEM;
3723256be65STakashi Iwai 
3733256be65STakashi Iwai 	tree->root = kobject_create_and_add("widgets", &codec->dev.kobj);
3743256be65STakashi Iwai 	if (!tree->root)
3753256be65STakashi Iwai 		return -ENOMEM;
3763256be65STakashi Iwai 
3773256be65STakashi Iwai 	tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes),
3783256be65STakashi Iwai 			      GFP_KERNEL);
3793256be65STakashi Iwai 	if (!tree->nodes)
3803256be65STakashi Iwai 		return -ENOMEM;
3813256be65STakashi Iwai 
3823256be65STakashi Iwai 	for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
3833256be65STakashi Iwai 		err = add_widget_node(tree->root, nid, &widget_node_group,
3843256be65STakashi Iwai 				      &tree->nodes[i]);
3853256be65STakashi Iwai 		if (err < 0)
3863256be65STakashi Iwai 			return err;
3873256be65STakashi Iwai 	}
3883256be65STakashi Iwai 
389142267c9STakashi Iwai 	if (codec->afg) {
390142267c9STakashi Iwai 		err = add_widget_node(tree->root, codec->afg,
391142267c9STakashi Iwai 				      &widget_afg_group, &tree->afg);
392142267c9STakashi Iwai 		if (err < 0)
393142267c9STakashi Iwai 			return err;
394142267c9STakashi Iwai 	}
395142267c9STakashi Iwai 
3963256be65STakashi Iwai 	kobject_uevent(tree->root, KOBJ_CHANGE);
3973256be65STakashi Iwai 	return 0;
3983256be65STakashi Iwai }
3993256be65STakashi Iwai 
400ed180abbSAmadeusz Sławiński /* call with codec->widget_lock held */
hda_widget_sysfs_init(struct hdac_device * codec)4013256be65STakashi Iwai int hda_widget_sysfs_init(struct hdac_device *codec)
4023256be65STakashi Iwai {
4033256be65STakashi Iwai 	int err;
4043256be65STakashi Iwai 
405a92d5ee8STakashi Iwai 	if (codec->widgets)
406a92d5ee8STakashi Iwai 		return 0; /* already created */
407a92d5ee8STakashi Iwai 
4083256be65STakashi Iwai 	err = widget_tree_create(codec);
4093256be65STakashi Iwai 	if (err < 0) {
4103256be65STakashi Iwai 		widget_tree_free(codec);
4113256be65STakashi Iwai 		return err;
4123256be65STakashi Iwai 	}
4133256be65STakashi Iwai 
4143256be65STakashi Iwai 	return 0;
4153256be65STakashi Iwai }
4163256be65STakashi Iwai 
417ed180abbSAmadeusz Sławiński /* call with codec->widget_lock held */
hda_widget_sysfs_exit(struct hdac_device * codec)4183256be65STakashi Iwai void hda_widget_sysfs_exit(struct hdac_device *codec)
4193256be65STakashi Iwai {
4203256be65STakashi Iwai 	widget_tree_free(codec);
4213256be65STakashi Iwai }
4229780ded3STakashi Iwai 
423ed180abbSAmadeusz Sławiński /* call with codec->widget_lock held */
hda_widget_sysfs_reinit(struct hdac_device * codec,hda_nid_t start_nid,int num_nodes)4249780ded3STakashi Iwai int hda_widget_sysfs_reinit(struct hdac_device *codec,
4259780ded3STakashi Iwai 			    hda_nid_t start_nid, int num_nodes)
4269780ded3STakashi Iwai {
4279780ded3STakashi Iwai 	struct hdac_widget_tree *tree;
4289780ded3STakashi Iwai 	hda_nid_t end_nid = start_nid + num_nodes;
4299780ded3STakashi Iwai 	hda_nid_t nid;
4309780ded3STakashi Iwai 	int i;
4319780ded3STakashi Iwai 
4329780ded3STakashi Iwai 	if (!codec->widgets)
433774a075aSTakashi Iwai 		return 0;
4349780ded3STakashi Iwai 
4359780ded3STakashi Iwai 	tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL);
4369780ded3STakashi Iwai 	if (!tree)
4379780ded3STakashi Iwai 		return -ENOMEM;
4389780ded3STakashi Iwai 
4399780ded3STakashi Iwai 	tree->nodes = kcalloc(num_nodes + 1, sizeof(*tree->nodes), GFP_KERNEL);
4409780ded3STakashi Iwai 	if (!tree->nodes) {
4419780ded3STakashi Iwai 		kfree(tree);
4429780ded3STakashi Iwai 		return -ENOMEM;
4439780ded3STakashi Iwai 	}
4449780ded3STakashi Iwai 
4459780ded3STakashi Iwai 	/* prune non-existing nodes */
4469780ded3STakashi Iwai 	for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
4479780ded3STakashi Iwai 		if (nid < start_nid || nid >= end_nid)
4489780ded3STakashi Iwai 			free_widget_node(codec->widgets->nodes[i],
4499780ded3STakashi Iwai 					 &widget_node_group);
4509780ded3STakashi Iwai 	}
4519780ded3STakashi Iwai 
4529780ded3STakashi Iwai 	/* add new nodes */
4539780ded3STakashi Iwai 	for (i = 0, nid = start_nid; i < num_nodes; i++, nid++) {
4549780ded3STakashi Iwai 		if (nid < codec->start_nid || nid >= codec->end_nid)
4559780ded3STakashi Iwai 			add_widget_node(tree->root, nid, &widget_node_group,
4569780ded3STakashi Iwai 					&tree->nodes[i]);
4579780ded3STakashi Iwai 		else
4589780ded3STakashi Iwai 			tree->nodes[i] =
4599780ded3STakashi Iwai 				codec->widgets->nodes[nid - codec->start_nid];
4609780ded3STakashi Iwai 	}
4619780ded3STakashi Iwai 
4629780ded3STakashi Iwai 	/* replace with the new tree */
4639780ded3STakashi Iwai 	kfree(codec->widgets->nodes);
4649780ded3STakashi Iwai 	kfree(codec->widgets);
4659780ded3STakashi Iwai 	codec->widgets = tree;
4669780ded3STakashi Iwai 
4679780ded3STakashi Iwai 	kobject_uevent(tree->root, KOBJ_CHANGE);
4689780ded3STakashi Iwai 	return 0;
4699780ded3STakashi Iwai }
470