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