13256be65STakashi Iwai /* 23256be65STakashi Iwai * sysfs support for HD-audio core device 33256be65STakashi Iwai */ 43256be65STakashi Iwai 53256be65STakashi Iwai #include <linux/slab.h> 63256be65STakashi Iwai #include <linux/sysfs.h> 73256be65STakashi Iwai #include <linux/device.h> 83256be65STakashi Iwai #include <sound/core.h> 93256be65STakashi Iwai #include <sound/hdaudio.h> 103256be65STakashi Iwai #include "local.h" 113256be65STakashi Iwai 123256be65STakashi Iwai struct hdac_widget_tree { 133256be65STakashi Iwai struct kobject *root; 143256be65STakashi Iwai struct kobject *afg; 153256be65STakashi Iwai struct kobject **nodes; 163256be65STakashi Iwai }; 173256be65STakashi Iwai 183256be65STakashi Iwai #define CODEC_ATTR(type) \ 193256be65STakashi Iwai static ssize_t type##_show(struct device *dev, \ 203256be65STakashi Iwai struct device_attribute *attr, \ 213256be65STakashi Iwai char *buf) \ 223256be65STakashi Iwai { \ 233256be65STakashi Iwai struct hdac_device *codec = dev_to_hdac_dev(dev); \ 243256be65STakashi Iwai return sprintf(buf, "0x%x\n", codec->type); \ 253256be65STakashi Iwai } \ 263256be65STakashi Iwai static DEVICE_ATTR_RO(type) 273256be65STakashi Iwai 283256be65STakashi Iwai #define CODEC_ATTR_STR(type) \ 293256be65STakashi Iwai static ssize_t type##_show(struct device *dev, \ 303256be65STakashi Iwai struct device_attribute *attr, \ 313256be65STakashi Iwai char *buf) \ 323256be65STakashi Iwai { \ 333256be65STakashi Iwai struct hdac_device *codec = dev_to_hdac_dev(dev); \ 343256be65STakashi Iwai return sprintf(buf, "%s\n", \ 353256be65STakashi Iwai codec->type ? codec->type : ""); \ 363256be65STakashi Iwai } \ 373256be65STakashi Iwai static DEVICE_ATTR_RO(type) 383256be65STakashi Iwai 393256be65STakashi Iwai CODEC_ATTR(vendor_id); 403256be65STakashi Iwai CODEC_ATTR(subsystem_id); 413256be65STakashi Iwai CODEC_ATTR(revision_id); 423256be65STakashi Iwai CODEC_ATTR(afg); 433256be65STakashi Iwai CODEC_ATTR(mfg); 443256be65STakashi Iwai CODEC_ATTR_STR(vendor_name); 453256be65STakashi Iwai CODEC_ATTR_STR(chip_name); 463256be65STakashi Iwai 473256be65STakashi Iwai static struct attribute *hdac_dev_attrs[] = { 483256be65STakashi Iwai &dev_attr_vendor_id.attr, 493256be65STakashi Iwai &dev_attr_subsystem_id.attr, 503256be65STakashi Iwai &dev_attr_revision_id.attr, 513256be65STakashi Iwai &dev_attr_afg.attr, 523256be65STakashi Iwai &dev_attr_mfg.attr, 533256be65STakashi Iwai &dev_attr_vendor_name.attr, 543256be65STakashi Iwai &dev_attr_chip_name.attr, 553256be65STakashi Iwai NULL 563256be65STakashi Iwai }; 573256be65STakashi Iwai 583256be65STakashi Iwai static struct attribute_group hdac_dev_attr_group = { 593256be65STakashi Iwai .attrs = hdac_dev_attrs, 603256be65STakashi Iwai }; 613256be65STakashi Iwai 623256be65STakashi Iwai const struct attribute_group *hdac_dev_attr_groups[] = { 633256be65STakashi Iwai &hdac_dev_attr_group, 643256be65STakashi Iwai NULL 653256be65STakashi Iwai }; 663256be65STakashi Iwai 673256be65STakashi Iwai /* 683256be65STakashi Iwai * Widget tree sysfs 693256be65STakashi Iwai * 703256be65STakashi Iwai * This is a tree showing the attributes of each widget. It appears like 713256be65STakashi Iwai * /sys/bus/hdaudioC0D0/widgets/04/caps 723256be65STakashi Iwai */ 733256be65STakashi Iwai 743256be65STakashi Iwai struct widget_attribute; 753256be65STakashi Iwai 763256be65STakashi Iwai struct widget_attribute { 773256be65STakashi Iwai struct attribute attr; 783256be65STakashi Iwai ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid, 793256be65STakashi Iwai struct widget_attribute *attr, char *buf); 803256be65STakashi Iwai ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid, 813256be65STakashi Iwai struct widget_attribute *attr, 823256be65STakashi Iwai const char *buf, size_t count); 833256be65STakashi Iwai }; 843256be65STakashi Iwai 853256be65STakashi Iwai static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp) 863256be65STakashi Iwai { 873256be65STakashi Iwai struct device *dev = kobj_to_dev(kobj->parent->parent); 883256be65STakashi Iwai int nid; 893256be65STakashi Iwai ssize_t ret; 903256be65STakashi Iwai 913256be65STakashi Iwai ret = kstrtoint(kobj->name, 16, &nid); 923256be65STakashi Iwai if (ret < 0) 933256be65STakashi Iwai return ret; 943256be65STakashi Iwai *codecp = dev_to_hdac_dev(dev); 953256be65STakashi Iwai return nid; 963256be65STakashi Iwai } 973256be65STakashi Iwai 983256be65STakashi Iwai static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr, 993256be65STakashi Iwai char *buf) 1003256be65STakashi Iwai { 1013256be65STakashi Iwai struct widget_attribute *wid_attr = 1023256be65STakashi Iwai container_of(attr, struct widget_attribute, attr); 1033256be65STakashi Iwai struct hdac_device *codec; 1043256be65STakashi Iwai int nid; 1053256be65STakashi Iwai 1063256be65STakashi Iwai if (!wid_attr->show) 1073256be65STakashi Iwai return -EIO; 1083256be65STakashi Iwai nid = get_codec_nid(kobj, &codec); 1093256be65STakashi Iwai if (nid < 0) 1103256be65STakashi Iwai return nid; 1113256be65STakashi Iwai return wid_attr->show(codec, nid, wid_attr, buf); 1123256be65STakashi Iwai } 1133256be65STakashi Iwai 1143256be65STakashi Iwai static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr, 1153256be65STakashi Iwai const char *buf, size_t count) 1163256be65STakashi Iwai { 1173256be65STakashi Iwai struct widget_attribute *wid_attr = 1183256be65STakashi Iwai container_of(attr, struct widget_attribute, attr); 1193256be65STakashi Iwai struct hdac_device *codec; 1203256be65STakashi Iwai int nid; 1213256be65STakashi Iwai 1223256be65STakashi Iwai if (!wid_attr->store) 1233256be65STakashi Iwai return -EIO; 1243256be65STakashi Iwai nid = get_codec_nid(kobj, &codec); 1253256be65STakashi Iwai if (nid < 0) 1263256be65STakashi Iwai return nid; 1273256be65STakashi Iwai return wid_attr->store(codec, nid, wid_attr, buf, count); 1283256be65STakashi Iwai } 1293256be65STakashi Iwai 1303256be65STakashi Iwai static const struct sysfs_ops widget_sysfs_ops = { 1313256be65STakashi Iwai .show = widget_attr_show, 1323256be65STakashi Iwai .store = widget_attr_store, 1333256be65STakashi Iwai }; 1343256be65STakashi Iwai 1353256be65STakashi Iwai static void widget_release(struct kobject *kobj) 1363256be65STakashi Iwai { 1373256be65STakashi Iwai kfree(kobj); 1383256be65STakashi Iwai } 1393256be65STakashi Iwai 1403256be65STakashi Iwai static struct kobj_type widget_ktype = { 1413256be65STakashi Iwai .release = widget_release, 1423256be65STakashi Iwai .sysfs_ops = &widget_sysfs_ops, 1433256be65STakashi Iwai }; 1443256be65STakashi Iwai 1453256be65STakashi Iwai #define WIDGET_ATTR_RO(_name) \ 1463256be65STakashi Iwai struct widget_attribute wid_attr_##_name = __ATTR_RO(_name) 1473256be65STakashi Iwai #define WIDGET_ATTR_RW(_name) \ 1483256be65STakashi Iwai struct widget_attribute wid_attr_##_name = __ATTR_RW(_name) 1493256be65STakashi Iwai 1503256be65STakashi Iwai static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid, 1513256be65STakashi Iwai struct widget_attribute *attr, char *buf) 1523256be65STakashi Iwai { 1533256be65STakashi Iwai return sprintf(buf, "0x%08x\n", get_wcaps(codec, nid)); 1543256be65STakashi Iwai } 1553256be65STakashi Iwai 1563256be65STakashi Iwai static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid, 1573256be65STakashi Iwai struct widget_attribute *attr, char *buf) 1583256be65STakashi Iwai { 1593256be65STakashi Iwai if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) 1603256be65STakashi Iwai return 0; 1613256be65STakashi Iwai return sprintf(buf, "0x%08x\n", 1623256be65STakashi Iwai snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP)); 1633256be65STakashi Iwai } 1643256be65STakashi Iwai 1653256be65STakashi Iwai static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid, 1663256be65STakashi Iwai struct widget_attribute *attr, char *buf) 1673256be65STakashi Iwai { 1683256be65STakashi Iwai unsigned int val; 1693256be65STakashi Iwai 1703256be65STakashi Iwai if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) 1713256be65STakashi Iwai return 0; 1723256be65STakashi Iwai if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val)) 1733256be65STakashi Iwai return 0; 1743256be65STakashi Iwai return sprintf(buf, "0x%08x\n", val); 1753256be65STakashi Iwai } 1763256be65STakashi Iwai 1773256be65STakashi Iwai static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid) 1783256be65STakashi Iwai { 1793256be65STakashi Iwai if (nid == codec->afg || nid == codec->mfg) 1803256be65STakashi Iwai return true; 1813256be65STakashi Iwai switch (get_wcaps_type(get_wcaps(codec, nid))) { 1823256be65STakashi Iwai case AC_WID_AUD_OUT: 1833256be65STakashi Iwai case AC_WID_AUD_IN: 1843256be65STakashi Iwai return true; 1853256be65STakashi Iwai default: 1863256be65STakashi Iwai return false; 1873256be65STakashi Iwai } 1883256be65STakashi Iwai } 1893256be65STakashi Iwai 1903256be65STakashi Iwai static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid, 1913256be65STakashi Iwai struct widget_attribute *attr, char *buf) 1923256be65STakashi Iwai { 1933256be65STakashi Iwai if (!has_pcm_cap(codec, nid)) 1943256be65STakashi Iwai return 0; 1953256be65STakashi Iwai return sprintf(buf, "0x%08x\n", 1963256be65STakashi Iwai snd_hdac_read_parm(codec, nid, AC_PAR_PCM)); 1973256be65STakashi Iwai } 1983256be65STakashi Iwai 1993256be65STakashi Iwai static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid, 2003256be65STakashi Iwai struct widget_attribute *attr, char *buf) 2013256be65STakashi Iwai { 2023256be65STakashi Iwai if (!has_pcm_cap(codec, nid)) 2033256be65STakashi Iwai return 0; 2043256be65STakashi Iwai return sprintf(buf, "0x%08x\n", 2053256be65STakashi Iwai snd_hdac_read_parm(codec, nid, AC_PAR_STREAM)); 2063256be65STakashi Iwai } 2073256be65STakashi Iwai 2083256be65STakashi Iwai static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid, 2093256be65STakashi Iwai struct widget_attribute *attr, char *buf) 2103256be65STakashi Iwai { 2113256be65STakashi Iwai if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) 2123256be65STakashi Iwai return 0; 2133256be65STakashi Iwai return sprintf(buf, "0x%08x\n", 2143256be65STakashi Iwai snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP)); 2153256be65STakashi Iwai } 2163256be65STakashi Iwai 2173256be65STakashi Iwai static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid, 2183256be65STakashi Iwai struct widget_attribute *attr, char *buf) 2193256be65STakashi Iwai { 2203256be65STakashi Iwai if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) 2213256be65STakashi Iwai return 0; 2223256be65STakashi Iwai return sprintf(buf, "0x%08x\n", 2233256be65STakashi Iwai snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP)); 2243256be65STakashi Iwai } 2253256be65STakashi Iwai 2263256be65STakashi Iwai static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid, 2273256be65STakashi Iwai struct widget_attribute *attr, char *buf) 2283256be65STakashi Iwai { 2293256be65STakashi Iwai if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER)) 2303256be65STakashi Iwai return 0; 2313256be65STakashi Iwai return sprintf(buf, "0x%08x\n", 2323256be65STakashi Iwai snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE)); 2333256be65STakashi Iwai } 2343256be65STakashi Iwai 2353256be65STakashi Iwai static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid, 2363256be65STakashi Iwai struct widget_attribute *attr, char *buf) 2373256be65STakashi Iwai { 2383256be65STakashi Iwai return sprintf(buf, "0x%08x\n", 2393256be65STakashi Iwai snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP)); 2403256be65STakashi Iwai } 2413256be65STakashi Iwai 2423256be65STakashi Iwai static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid, 2433256be65STakashi Iwai struct widget_attribute *attr, char *buf) 2443256be65STakashi Iwai { 2453256be65STakashi Iwai hda_nid_t list[32]; 2463256be65STakashi Iwai int i, nconns; 2473256be65STakashi Iwai ssize_t ret = 0; 2483256be65STakashi Iwai 2493256be65STakashi Iwai nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list)); 2503256be65STakashi Iwai if (nconns <= 0) 2513256be65STakashi Iwai return nconns; 2523256be65STakashi Iwai for (i = 0; i < nconns; i++) 2533256be65STakashi Iwai ret += sprintf(buf + ret, "%s0x%02x", i ? " " : "", list[i]); 2543256be65STakashi Iwai ret += sprintf(buf + ret, "\n"); 2553256be65STakashi Iwai return ret; 2563256be65STakashi Iwai } 2573256be65STakashi Iwai 2583256be65STakashi Iwai static WIDGET_ATTR_RO(caps); 2593256be65STakashi Iwai static WIDGET_ATTR_RO(pin_caps); 2603256be65STakashi Iwai static WIDGET_ATTR_RO(pin_cfg); 2613256be65STakashi Iwai static WIDGET_ATTR_RO(pcm_caps); 2623256be65STakashi Iwai static WIDGET_ATTR_RO(pcm_formats); 2633256be65STakashi Iwai static WIDGET_ATTR_RO(amp_in_caps); 2643256be65STakashi Iwai static WIDGET_ATTR_RO(amp_out_caps); 2653256be65STakashi Iwai static WIDGET_ATTR_RO(power_caps); 2663256be65STakashi Iwai static WIDGET_ATTR_RO(gpio_caps); 2673256be65STakashi Iwai static WIDGET_ATTR_RO(connections); 2683256be65STakashi Iwai 2693256be65STakashi Iwai static struct attribute *widget_node_attrs[] = { 2703256be65STakashi Iwai &wid_attr_caps.attr, 2713256be65STakashi Iwai &wid_attr_pin_caps.attr, 2723256be65STakashi Iwai &wid_attr_pin_cfg.attr, 2733256be65STakashi Iwai &wid_attr_pcm_caps.attr, 2743256be65STakashi Iwai &wid_attr_pcm_formats.attr, 2753256be65STakashi Iwai &wid_attr_amp_in_caps.attr, 2763256be65STakashi Iwai &wid_attr_amp_out_caps.attr, 2773256be65STakashi Iwai &wid_attr_power_caps.attr, 2783256be65STakashi Iwai &wid_attr_connections.attr, 2793256be65STakashi Iwai NULL, 2803256be65STakashi Iwai }; 2813256be65STakashi Iwai 2823256be65STakashi Iwai static struct attribute *widget_afg_attrs[] = { 2833256be65STakashi Iwai &wid_attr_pcm_caps.attr, 2843256be65STakashi Iwai &wid_attr_pcm_formats.attr, 2853256be65STakashi Iwai &wid_attr_amp_in_caps.attr, 2863256be65STakashi Iwai &wid_attr_amp_out_caps.attr, 2873256be65STakashi Iwai &wid_attr_power_caps.attr, 2883256be65STakashi Iwai &wid_attr_gpio_caps.attr, 2893256be65STakashi Iwai NULL, 2903256be65STakashi Iwai }; 2913256be65STakashi Iwai 2923256be65STakashi Iwai static const struct attribute_group widget_node_group = { 2933256be65STakashi Iwai .attrs = widget_node_attrs, 2943256be65STakashi Iwai }; 2953256be65STakashi Iwai 2963256be65STakashi Iwai static const struct attribute_group widget_afg_group = { 2973256be65STakashi Iwai .attrs = widget_afg_attrs, 2983256be65STakashi Iwai }; 2993256be65STakashi Iwai 3003256be65STakashi Iwai static void free_widget_node(struct kobject *kobj, 3013256be65STakashi Iwai const struct attribute_group *group) 3023256be65STakashi Iwai { 3033256be65STakashi Iwai if (kobj) { 3043256be65STakashi Iwai sysfs_remove_group(kobj, group); 3053256be65STakashi Iwai kobject_put(kobj); 3063256be65STakashi Iwai } 3073256be65STakashi Iwai } 3083256be65STakashi Iwai 3093256be65STakashi Iwai static void widget_tree_free(struct hdac_device *codec) 3103256be65STakashi Iwai { 3113256be65STakashi Iwai struct hdac_widget_tree *tree = codec->widgets; 3123256be65STakashi Iwai struct kobject **p; 3133256be65STakashi Iwai 3143256be65STakashi Iwai if (!tree) 3153256be65STakashi Iwai return; 3163256be65STakashi Iwai if (tree->nodes) { 3173256be65STakashi Iwai for (p = tree->nodes; *p; p++) 3183256be65STakashi Iwai free_widget_node(*p, &widget_node_group); 3193256be65STakashi Iwai kfree(tree->nodes); 3203256be65STakashi Iwai } 3213256be65STakashi Iwai free_widget_node(tree->afg, &widget_afg_group); 3223256be65STakashi Iwai if (tree->root) 3233256be65STakashi Iwai kobject_put(tree->root); 3243256be65STakashi Iwai kfree(tree); 3253256be65STakashi Iwai codec->widgets = NULL; 3263256be65STakashi Iwai } 3273256be65STakashi Iwai 3283256be65STakashi Iwai static int add_widget_node(struct kobject *parent, hda_nid_t nid, 3293256be65STakashi Iwai const struct attribute_group *group, 3303256be65STakashi Iwai struct kobject **res) 3313256be65STakashi Iwai { 3323256be65STakashi Iwai struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); 3333256be65STakashi Iwai int err; 3343256be65STakashi Iwai 3353256be65STakashi Iwai if (!kobj) 3363256be65STakashi Iwai return -ENOMEM; 3373256be65STakashi Iwai kobject_init(kobj, &widget_ktype); 3383256be65STakashi Iwai err = kobject_add(kobj, parent, "%02x", nid); 3393256be65STakashi Iwai if (err < 0) 3403256be65STakashi Iwai return err; 3413256be65STakashi Iwai err = sysfs_create_group(kobj, group); 3423256be65STakashi Iwai if (err < 0) { 3433256be65STakashi Iwai kobject_put(kobj); 3443256be65STakashi Iwai return err; 3453256be65STakashi Iwai } 3463256be65STakashi Iwai 3473256be65STakashi Iwai *res = kobj; 3483256be65STakashi Iwai return 0; 3493256be65STakashi Iwai } 3503256be65STakashi Iwai 3513256be65STakashi Iwai static int widget_tree_create(struct hdac_device *codec) 3523256be65STakashi Iwai { 3533256be65STakashi Iwai struct hdac_widget_tree *tree; 3543256be65STakashi Iwai int i, err; 3553256be65STakashi Iwai hda_nid_t nid; 3563256be65STakashi Iwai 3573256be65STakashi Iwai tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL); 3583256be65STakashi Iwai if (!tree) 3593256be65STakashi Iwai return -ENOMEM; 3603256be65STakashi Iwai 3613256be65STakashi Iwai tree->root = kobject_create_and_add("widgets", &codec->dev.kobj); 3623256be65STakashi Iwai if (!tree->root) 3633256be65STakashi Iwai return -ENOMEM; 3643256be65STakashi Iwai 3653256be65STakashi Iwai if (codec->afg) { 3663256be65STakashi Iwai err = add_widget_node(tree->root, codec->afg, 3673256be65STakashi Iwai &widget_afg_group, &tree->afg); 3683256be65STakashi Iwai if (err < 0) 3693256be65STakashi Iwai return err; 3703256be65STakashi Iwai } 3713256be65STakashi Iwai 3723256be65STakashi Iwai tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes), 3733256be65STakashi Iwai GFP_KERNEL); 3743256be65STakashi Iwai if (!tree->nodes) 3753256be65STakashi Iwai return -ENOMEM; 3763256be65STakashi Iwai 3773256be65STakashi Iwai for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) { 3783256be65STakashi Iwai err = add_widget_node(tree->root, nid, &widget_node_group, 3793256be65STakashi Iwai &tree->nodes[i]); 3803256be65STakashi Iwai if (err < 0) 3813256be65STakashi Iwai return err; 3823256be65STakashi Iwai } 3833256be65STakashi Iwai 3843256be65STakashi Iwai kobject_uevent(tree->root, KOBJ_CHANGE); 3853256be65STakashi Iwai return 0; 3863256be65STakashi Iwai } 3873256be65STakashi Iwai 3883256be65STakashi Iwai int hda_widget_sysfs_init(struct hdac_device *codec) 3893256be65STakashi Iwai { 3903256be65STakashi Iwai int err; 3913256be65STakashi Iwai 3923256be65STakashi Iwai err = widget_tree_create(codec); 3933256be65STakashi Iwai if (err < 0) { 3943256be65STakashi Iwai widget_tree_free(codec); 3953256be65STakashi Iwai return err; 3963256be65STakashi Iwai } 3973256be65STakashi Iwai 3983256be65STakashi Iwai return 0; 3993256be65STakashi Iwai } 4003256be65STakashi Iwai 4013256be65STakashi Iwai void hda_widget_sysfs_exit(struct hdac_device *codec) 4023256be65STakashi Iwai { 4033256be65STakashi Iwai widget_tree_free(codec); 4043256be65STakashi Iwai } 405