xref: /OK3568_Linux_fs/kernel/sound/hda/hdac_sysfs.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * sysfs support for HD-audio core device
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/slab.h>
7*4882a593Smuzhiyun #include <linux/sysfs.h>
8*4882a593Smuzhiyun #include <linux/device.h>
9*4882a593Smuzhiyun #include <sound/core.h>
10*4882a593Smuzhiyun #include <sound/hdaudio.h>
11*4882a593Smuzhiyun #include "local.h"
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun struct hdac_widget_tree {
14*4882a593Smuzhiyun 	struct kobject *root;
15*4882a593Smuzhiyun 	struct kobject *afg;
16*4882a593Smuzhiyun 	struct kobject **nodes;
17*4882a593Smuzhiyun };
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #define CODEC_ATTR(type)					\
20*4882a593Smuzhiyun static ssize_t type##_show(struct device *dev,			\
21*4882a593Smuzhiyun 			   struct device_attribute *attr,	\
22*4882a593Smuzhiyun 			   char *buf)				\
23*4882a593Smuzhiyun {								\
24*4882a593Smuzhiyun 	struct hdac_device *codec = dev_to_hdac_dev(dev);	\
25*4882a593Smuzhiyun 	return sprintf(buf, "0x%x\n", codec->type);		\
26*4882a593Smuzhiyun } \
27*4882a593Smuzhiyun static DEVICE_ATTR_RO(type)
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #define CODEC_ATTR_STR(type)					\
30*4882a593Smuzhiyun static ssize_t type##_show(struct device *dev,			\
31*4882a593Smuzhiyun 			     struct device_attribute *attr,	\
32*4882a593Smuzhiyun 					char *buf)		\
33*4882a593Smuzhiyun {								\
34*4882a593Smuzhiyun 	struct hdac_device *codec = dev_to_hdac_dev(dev);	\
35*4882a593Smuzhiyun 	return sprintf(buf, "%s\n",				\
36*4882a593Smuzhiyun 		       codec->type ? codec->type : "");		\
37*4882a593Smuzhiyun } \
38*4882a593Smuzhiyun static DEVICE_ATTR_RO(type)
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun CODEC_ATTR(type);
41*4882a593Smuzhiyun CODEC_ATTR(vendor_id);
42*4882a593Smuzhiyun CODEC_ATTR(subsystem_id);
43*4882a593Smuzhiyun CODEC_ATTR(revision_id);
44*4882a593Smuzhiyun CODEC_ATTR(afg);
45*4882a593Smuzhiyun CODEC_ATTR(mfg);
46*4882a593Smuzhiyun CODEC_ATTR_STR(vendor_name);
47*4882a593Smuzhiyun CODEC_ATTR_STR(chip_name);
48*4882a593Smuzhiyun 
modalias_show(struct device * dev,struct device_attribute * attr,char * buf)49*4882a593Smuzhiyun static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
50*4882a593Smuzhiyun 			     char *buf)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun 	return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun static DEVICE_ATTR_RO(modalias);
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun static struct attribute *hdac_dev_attrs[] = {
57*4882a593Smuzhiyun 	&dev_attr_type.attr,
58*4882a593Smuzhiyun 	&dev_attr_vendor_id.attr,
59*4882a593Smuzhiyun 	&dev_attr_subsystem_id.attr,
60*4882a593Smuzhiyun 	&dev_attr_revision_id.attr,
61*4882a593Smuzhiyun 	&dev_attr_afg.attr,
62*4882a593Smuzhiyun 	&dev_attr_mfg.attr,
63*4882a593Smuzhiyun 	&dev_attr_vendor_name.attr,
64*4882a593Smuzhiyun 	&dev_attr_chip_name.attr,
65*4882a593Smuzhiyun 	&dev_attr_modalias.attr,
66*4882a593Smuzhiyun 	NULL
67*4882a593Smuzhiyun };
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun static struct attribute_group hdac_dev_attr_group = {
70*4882a593Smuzhiyun 	.attrs	= hdac_dev_attrs,
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun const struct attribute_group *hdac_dev_attr_groups[] = {
74*4882a593Smuzhiyun 	&hdac_dev_attr_group,
75*4882a593Smuzhiyun 	NULL
76*4882a593Smuzhiyun };
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun /*
79*4882a593Smuzhiyun  * Widget tree sysfs
80*4882a593Smuzhiyun  *
81*4882a593Smuzhiyun  * This is a tree showing the attributes of each widget.  It appears like
82*4882a593Smuzhiyun  * /sys/bus/hdaudioC0D0/widgets/04/caps
83*4882a593Smuzhiyun  */
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun struct widget_attribute;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun struct widget_attribute {
88*4882a593Smuzhiyun 	struct attribute	attr;
89*4882a593Smuzhiyun 	ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid,
90*4882a593Smuzhiyun 			struct widget_attribute *attr, char *buf);
91*4882a593Smuzhiyun 	ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid,
92*4882a593Smuzhiyun 			 struct widget_attribute *attr,
93*4882a593Smuzhiyun 			 const char *buf, size_t count);
94*4882a593Smuzhiyun };
95*4882a593Smuzhiyun 
get_codec_nid(struct kobject * kobj,struct hdac_device ** codecp)96*4882a593Smuzhiyun static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun 	struct device *dev = kobj_to_dev(kobj->parent->parent);
99*4882a593Smuzhiyun 	int nid;
100*4882a593Smuzhiyun 	ssize_t ret;
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	ret = kstrtoint(kobj->name, 16, &nid);
103*4882a593Smuzhiyun 	if (ret < 0)
104*4882a593Smuzhiyun 		return ret;
105*4882a593Smuzhiyun 	*codecp = dev_to_hdac_dev(dev);
106*4882a593Smuzhiyun 	return nid;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun 
widget_attr_show(struct kobject * kobj,struct attribute * attr,char * buf)109*4882a593Smuzhiyun static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr,
110*4882a593Smuzhiyun 				char *buf)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	struct widget_attribute *wid_attr =
113*4882a593Smuzhiyun 		container_of(attr, struct widget_attribute, attr);
114*4882a593Smuzhiyun 	struct hdac_device *codec;
115*4882a593Smuzhiyun 	int nid;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	if (!wid_attr->show)
118*4882a593Smuzhiyun 		return -EIO;
119*4882a593Smuzhiyun 	nid = get_codec_nid(kobj, &codec);
120*4882a593Smuzhiyun 	if (nid < 0)
121*4882a593Smuzhiyun 		return nid;
122*4882a593Smuzhiyun 	return wid_attr->show(codec, nid, wid_attr, buf);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
widget_attr_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t count)125*4882a593Smuzhiyun static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr,
126*4882a593Smuzhiyun 				 const char *buf, size_t count)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	struct widget_attribute *wid_attr =
129*4882a593Smuzhiyun 		container_of(attr, struct widget_attribute, attr);
130*4882a593Smuzhiyun 	struct hdac_device *codec;
131*4882a593Smuzhiyun 	int nid;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	if (!wid_attr->store)
134*4882a593Smuzhiyun 		return -EIO;
135*4882a593Smuzhiyun 	nid = get_codec_nid(kobj, &codec);
136*4882a593Smuzhiyun 	if (nid < 0)
137*4882a593Smuzhiyun 		return nid;
138*4882a593Smuzhiyun 	return wid_attr->store(codec, nid, wid_attr, buf, count);
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun static const struct sysfs_ops widget_sysfs_ops = {
142*4882a593Smuzhiyun 	.show	= widget_attr_show,
143*4882a593Smuzhiyun 	.store	= widget_attr_store,
144*4882a593Smuzhiyun };
145*4882a593Smuzhiyun 
widget_release(struct kobject * kobj)146*4882a593Smuzhiyun static void widget_release(struct kobject *kobj)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	kfree(kobj);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun static struct kobj_type widget_ktype = {
152*4882a593Smuzhiyun 	.release	= widget_release,
153*4882a593Smuzhiyun 	.sysfs_ops	= &widget_sysfs_ops,
154*4882a593Smuzhiyun };
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun #define WIDGET_ATTR_RO(_name) \
157*4882a593Smuzhiyun 	struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
158*4882a593Smuzhiyun #define WIDGET_ATTR_RW(_name) \
159*4882a593Smuzhiyun 	struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
160*4882a593Smuzhiyun 
caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)161*4882a593Smuzhiyun static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid,
162*4882a593Smuzhiyun 			struct widget_attribute *attr, char *buf)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun 	return sprintf(buf, "0x%08x\n", get_wcaps(codec, nid));
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun 
pin_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)167*4882a593Smuzhiyun static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid,
168*4882a593Smuzhiyun 			     struct widget_attribute *attr, char *buf)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun 	if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
171*4882a593Smuzhiyun 		return 0;
172*4882a593Smuzhiyun 	return sprintf(buf, "0x%08x\n",
173*4882a593Smuzhiyun 		       snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP));
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun 
pin_cfg_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)176*4882a593Smuzhiyun static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid,
177*4882a593Smuzhiyun 			    struct widget_attribute *attr, char *buf)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun 	unsigned int val;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
182*4882a593Smuzhiyun 		return 0;
183*4882a593Smuzhiyun 	if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
184*4882a593Smuzhiyun 		return 0;
185*4882a593Smuzhiyun 	return sprintf(buf, "0x%08x\n", val);
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun 
has_pcm_cap(struct hdac_device * codec,hda_nid_t nid)188*4882a593Smuzhiyun static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun 	if (nid == codec->afg || nid == codec->mfg)
191*4882a593Smuzhiyun 		return true;
192*4882a593Smuzhiyun 	switch (get_wcaps_type(get_wcaps(codec, nid))) {
193*4882a593Smuzhiyun 	case AC_WID_AUD_OUT:
194*4882a593Smuzhiyun 	case AC_WID_AUD_IN:
195*4882a593Smuzhiyun 		return true;
196*4882a593Smuzhiyun 	default:
197*4882a593Smuzhiyun 		return false;
198*4882a593Smuzhiyun 	}
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun 
pcm_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)201*4882a593Smuzhiyun static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid,
202*4882a593Smuzhiyun 			     struct widget_attribute *attr, char *buf)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun 	if (!has_pcm_cap(codec, nid))
205*4882a593Smuzhiyun 		return 0;
206*4882a593Smuzhiyun 	return sprintf(buf, "0x%08x\n",
207*4882a593Smuzhiyun 		       snd_hdac_read_parm(codec, nid, AC_PAR_PCM));
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun 
pcm_formats_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)210*4882a593Smuzhiyun static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid,
211*4882a593Smuzhiyun 				struct widget_attribute *attr, char *buf)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun 	if (!has_pcm_cap(codec, nid))
214*4882a593Smuzhiyun 		return 0;
215*4882a593Smuzhiyun 	return sprintf(buf, "0x%08x\n",
216*4882a593Smuzhiyun 		       snd_hdac_read_parm(codec, nid, AC_PAR_STREAM));
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun 
amp_in_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)219*4882a593Smuzhiyun static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid,
220*4882a593Smuzhiyun 				struct widget_attribute *attr, char *buf)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
223*4882a593Smuzhiyun 		return 0;
224*4882a593Smuzhiyun 	return sprintf(buf, "0x%08x\n",
225*4882a593Smuzhiyun 		       snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP));
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun 
amp_out_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)228*4882a593Smuzhiyun static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid,
229*4882a593Smuzhiyun 				 struct widget_attribute *attr, char *buf)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun 	if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
232*4882a593Smuzhiyun 		return 0;
233*4882a593Smuzhiyun 	return sprintf(buf, "0x%08x\n",
234*4882a593Smuzhiyun 		       snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP));
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun 
power_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)237*4882a593Smuzhiyun static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid,
238*4882a593Smuzhiyun 			       struct widget_attribute *attr, char *buf)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun 	if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER))
241*4882a593Smuzhiyun 		return 0;
242*4882a593Smuzhiyun 	return sprintf(buf, "0x%08x\n",
243*4882a593Smuzhiyun 		       snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE));
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun 
gpio_caps_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)246*4882a593Smuzhiyun static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid,
247*4882a593Smuzhiyun 			      struct widget_attribute *attr, char *buf)
248*4882a593Smuzhiyun {
249*4882a593Smuzhiyun 	return sprintf(buf, "0x%08x\n",
250*4882a593Smuzhiyun 		       snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP));
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun 
connections_show(struct hdac_device * codec,hda_nid_t nid,struct widget_attribute * attr,char * buf)253*4882a593Smuzhiyun static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid,
254*4882a593Smuzhiyun 				struct widget_attribute *attr, char *buf)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun 	hda_nid_t list[32];
257*4882a593Smuzhiyun 	int i, nconns;
258*4882a593Smuzhiyun 	ssize_t ret = 0;
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list));
261*4882a593Smuzhiyun 	if (nconns <= 0)
262*4882a593Smuzhiyun 		return nconns;
263*4882a593Smuzhiyun 	for (i = 0; i < nconns; i++)
264*4882a593Smuzhiyun 		ret += sprintf(buf + ret, "%s0x%02x", i ? " " : "", list[i]);
265*4882a593Smuzhiyun 	ret += sprintf(buf + ret, "\n");
266*4882a593Smuzhiyun 	return ret;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun static WIDGET_ATTR_RO(caps);
270*4882a593Smuzhiyun static WIDGET_ATTR_RO(pin_caps);
271*4882a593Smuzhiyun static WIDGET_ATTR_RO(pin_cfg);
272*4882a593Smuzhiyun static WIDGET_ATTR_RO(pcm_caps);
273*4882a593Smuzhiyun static WIDGET_ATTR_RO(pcm_formats);
274*4882a593Smuzhiyun static WIDGET_ATTR_RO(amp_in_caps);
275*4882a593Smuzhiyun static WIDGET_ATTR_RO(amp_out_caps);
276*4882a593Smuzhiyun static WIDGET_ATTR_RO(power_caps);
277*4882a593Smuzhiyun static WIDGET_ATTR_RO(gpio_caps);
278*4882a593Smuzhiyun static WIDGET_ATTR_RO(connections);
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun static struct attribute *widget_node_attrs[] = {
281*4882a593Smuzhiyun 	&wid_attr_caps.attr,
282*4882a593Smuzhiyun 	&wid_attr_pin_caps.attr,
283*4882a593Smuzhiyun 	&wid_attr_pin_cfg.attr,
284*4882a593Smuzhiyun 	&wid_attr_pcm_caps.attr,
285*4882a593Smuzhiyun 	&wid_attr_pcm_formats.attr,
286*4882a593Smuzhiyun 	&wid_attr_amp_in_caps.attr,
287*4882a593Smuzhiyun 	&wid_attr_amp_out_caps.attr,
288*4882a593Smuzhiyun 	&wid_attr_power_caps.attr,
289*4882a593Smuzhiyun 	&wid_attr_connections.attr,
290*4882a593Smuzhiyun 	NULL,
291*4882a593Smuzhiyun };
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun static struct attribute *widget_afg_attrs[] = {
294*4882a593Smuzhiyun 	&wid_attr_pcm_caps.attr,
295*4882a593Smuzhiyun 	&wid_attr_pcm_formats.attr,
296*4882a593Smuzhiyun 	&wid_attr_amp_in_caps.attr,
297*4882a593Smuzhiyun 	&wid_attr_amp_out_caps.attr,
298*4882a593Smuzhiyun 	&wid_attr_power_caps.attr,
299*4882a593Smuzhiyun 	&wid_attr_gpio_caps.attr,
300*4882a593Smuzhiyun 	NULL,
301*4882a593Smuzhiyun };
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun static const struct attribute_group widget_node_group = {
304*4882a593Smuzhiyun 	.attrs = widget_node_attrs,
305*4882a593Smuzhiyun };
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun static const struct attribute_group widget_afg_group = {
308*4882a593Smuzhiyun 	.attrs = widget_afg_attrs,
309*4882a593Smuzhiyun };
310*4882a593Smuzhiyun 
free_widget_node(struct kobject * kobj,const struct attribute_group * group)311*4882a593Smuzhiyun static void free_widget_node(struct kobject *kobj,
312*4882a593Smuzhiyun 			     const struct attribute_group *group)
313*4882a593Smuzhiyun {
314*4882a593Smuzhiyun 	if (kobj) {
315*4882a593Smuzhiyun 		sysfs_remove_group(kobj, group);
316*4882a593Smuzhiyun 		kobject_put(kobj);
317*4882a593Smuzhiyun 	}
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun 
widget_tree_free(struct hdac_device * codec)320*4882a593Smuzhiyun static void widget_tree_free(struct hdac_device *codec)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun 	struct hdac_widget_tree *tree = codec->widgets;
323*4882a593Smuzhiyun 	struct kobject **p;
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 	if (!tree)
326*4882a593Smuzhiyun 		return;
327*4882a593Smuzhiyun 	free_widget_node(tree->afg, &widget_afg_group);
328*4882a593Smuzhiyun 	if (tree->nodes) {
329*4882a593Smuzhiyun 		for (p = tree->nodes; *p; p++)
330*4882a593Smuzhiyun 			free_widget_node(*p, &widget_node_group);
331*4882a593Smuzhiyun 		kfree(tree->nodes);
332*4882a593Smuzhiyun 	}
333*4882a593Smuzhiyun 	kobject_put(tree->root);
334*4882a593Smuzhiyun 	kfree(tree);
335*4882a593Smuzhiyun 	codec->widgets = NULL;
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun 
add_widget_node(struct kobject * parent,hda_nid_t nid,const struct attribute_group * group,struct kobject ** res)338*4882a593Smuzhiyun static int add_widget_node(struct kobject *parent, hda_nid_t nid,
339*4882a593Smuzhiyun 			   const struct attribute_group *group,
340*4882a593Smuzhiyun 			   struct kobject **res)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun 	struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
343*4882a593Smuzhiyun 	int err;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	if (!kobj)
346*4882a593Smuzhiyun 		return -ENOMEM;
347*4882a593Smuzhiyun 	kobject_init(kobj, &widget_ktype);
348*4882a593Smuzhiyun 	err = kobject_add(kobj, parent, "%02x", nid);
349*4882a593Smuzhiyun 	if (err < 0) {
350*4882a593Smuzhiyun 		kobject_put(kobj);
351*4882a593Smuzhiyun 		return err;
352*4882a593Smuzhiyun 	}
353*4882a593Smuzhiyun 	err = sysfs_create_group(kobj, group);
354*4882a593Smuzhiyun 	if (err < 0) {
355*4882a593Smuzhiyun 		kobject_put(kobj);
356*4882a593Smuzhiyun 		return err;
357*4882a593Smuzhiyun 	}
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	*res = kobj;
360*4882a593Smuzhiyun 	return 0;
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun 
widget_tree_create(struct hdac_device * codec)363*4882a593Smuzhiyun static int widget_tree_create(struct hdac_device *codec)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun 	struct hdac_widget_tree *tree;
366*4882a593Smuzhiyun 	int i, err;
367*4882a593Smuzhiyun 	hda_nid_t nid;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL);
370*4882a593Smuzhiyun 	if (!tree)
371*4882a593Smuzhiyun 		return -ENOMEM;
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	tree->root = kobject_create_and_add("widgets", &codec->dev.kobj);
374*4882a593Smuzhiyun 	if (!tree->root)
375*4882a593Smuzhiyun 		return -ENOMEM;
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes),
378*4882a593Smuzhiyun 			      GFP_KERNEL);
379*4882a593Smuzhiyun 	if (!tree->nodes)
380*4882a593Smuzhiyun 		return -ENOMEM;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
383*4882a593Smuzhiyun 		err = add_widget_node(tree->root, nid, &widget_node_group,
384*4882a593Smuzhiyun 				      &tree->nodes[i]);
385*4882a593Smuzhiyun 		if (err < 0)
386*4882a593Smuzhiyun 			return err;
387*4882a593Smuzhiyun 	}
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	if (codec->afg) {
390*4882a593Smuzhiyun 		err = add_widget_node(tree->root, codec->afg,
391*4882a593Smuzhiyun 				      &widget_afg_group, &tree->afg);
392*4882a593Smuzhiyun 		if (err < 0)
393*4882a593Smuzhiyun 			return err;
394*4882a593Smuzhiyun 	}
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	kobject_uevent(tree->root, KOBJ_CHANGE);
397*4882a593Smuzhiyun 	return 0;
398*4882a593Smuzhiyun }
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun /* call with codec->widget_lock held */
hda_widget_sysfs_init(struct hdac_device * codec)401*4882a593Smuzhiyun int hda_widget_sysfs_init(struct hdac_device *codec)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun 	int err;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	if (codec->widgets)
406*4882a593Smuzhiyun 		return 0; /* already created */
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	err = widget_tree_create(codec);
409*4882a593Smuzhiyun 	if (err < 0) {
410*4882a593Smuzhiyun 		widget_tree_free(codec);
411*4882a593Smuzhiyun 		return err;
412*4882a593Smuzhiyun 	}
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	return 0;
415*4882a593Smuzhiyun }
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun /* call with codec->widget_lock held */
hda_widget_sysfs_exit(struct hdac_device * codec)418*4882a593Smuzhiyun void hda_widget_sysfs_exit(struct hdac_device *codec)
419*4882a593Smuzhiyun {
420*4882a593Smuzhiyun 	widget_tree_free(codec);
421*4882a593Smuzhiyun }
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun /* call with codec->widget_lock held */
hda_widget_sysfs_reinit(struct hdac_device * codec,hda_nid_t start_nid,int num_nodes)424*4882a593Smuzhiyun int hda_widget_sysfs_reinit(struct hdac_device *codec,
425*4882a593Smuzhiyun 			    hda_nid_t start_nid, int num_nodes)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun 	struct hdac_widget_tree *tree;
428*4882a593Smuzhiyun 	hda_nid_t end_nid = start_nid + num_nodes;
429*4882a593Smuzhiyun 	hda_nid_t nid;
430*4882a593Smuzhiyun 	int i;
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	if (!codec->widgets)
433*4882a593Smuzhiyun 		return 0;
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL);
436*4882a593Smuzhiyun 	if (!tree)
437*4882a593Smuzhiyun 		return -ENOMEM;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	tree->nodes = kcalloc(num_nodes + 1, sizeof(*tree->nodes), GFP_KERNEL);
440*4882a593Smuzhiyun 	if (!tree->nodes) {
441*4882a593Smuzhiyun 		kfree(tree);
442*4882a593Smuzhiyun 		return -ENOMEM;
443*4882a593Smuzhiyun 	}
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	/* prune non-existing nodes */
446*4882a593Smuzhiyun 	for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
447*4882a593Smuzhiyun 		if (nid < start_nid || nid >= end_nid)
448*4882a593Smuzhiyun 			free_widget_node(codec->widgets->nodes[i],
449*4882a593Smuzhiyun 					 &widget_node_group);
450*4882a593Smuzhiyun 	}
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	/* add new nodes */
453*4882a593Smuzhiyun 	for (i = 0, nid = start_nid; i < num_nodes; i++, nid++) {
454*4882a593Smuzhiyun 		if (nid < codec->start_nid || nid >= codec->end_nid)
455*4882a593Smuzhiyun 			add_widget_node(tree->root, nid, &widget_node_group,
456*4882a593Smuzhiyun 					&tree->nodes[i]);
457*4882a593Smuzhiyun 		else
458*4882a593Smuzhiyun 			tree->nodes[i] =
459*4882a593Smuzhiyun 				codec->widgets->nodes[nid - codec->start_nid];
460*4882a593Smuzhiyun 	}
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	/* replace with the new tree */
463*4882a593Smuzhiyun 	kfree(codec->widgets->nodes);
464*4882a593Smuzhiyun 	kfree(codec->widgets);
465*4882a593Smuzhiyun 	codec->widgets = tree;
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	kobject_uevent(tree->root, KOBJ_CHANGE);
468*4882a593Smuzhiyun 	return 0;
469*4882a593Smuzhiyun }
470