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