Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * sysfs support for HD-audio core device
0004  */
0005 
0006 #include <linux/slab.h>
0007 #include <linux/sysfs.h>
0008 #include <linux/device.h>
0009 #include <sound/core.h>
0010 #include <sound/hdaudio.h>
0011 #include "local.h"
0012 
0013 struct hdac_widget_tree {
0014     struct kobject *root;
0015     struct kobject *afg;
0016     struct kobject **nodes;
0017 };
0018 
0019 #define CODEC_ATTR(type)                    \
0020 static ssize_t type##_show(struct device *dev,          \
0021                struct device_attribute *attr,   \
0022                char *buf)               \
0023 {                               \
0024     struct hdac_device *codec = dev_to_hdac_dev(dev);   \
0025     return sysfs_emit(buf, "0x%x\n", codec->type);      \
0026 } \
0027 static DEVICE_ATTR_RO(type)
0028 
0029 #define CODEC_ATTR_STR(type)                    \
0030 static ssize_t type##_show(struct device *dev,          \
0031                  struct device_attribute *attr, \
0032                     char *buf)      \
0033 {                               \
0034     struct hdac_device *codec = dev_to_hdac_dev(dev);   \
0035     return sysfs_emit(buf, "%s\n",              \
0036               codec->type ? codec->type : "");  \
0037 } \
0038 static DEVICE_ATTR_RO(type)
0039 
0040 CODEC_ATTR(type);
0041 CODEC_ATTR(vendor_id);
0042 CODEC_ATTR(subsystem_id);
0043 CODEC_ATTR(revision_id);
0044 CODEC_ATTR(afg);
0045 CODEC_ATTR(mfg);
0046 CODEC_ATTR_STR(vendor_name);
0047 CODEC_ATTR_STR(chip_name);
0048 
0049 static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
0050                  char *buf)
0051 {
0052     return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
0053 }
0054 static DEVICE_ATTR_RO(modalias);
0055 
0056 static struct attribute *hdac_dev_attrs[] = {
0057     &dev_attr_type.attr,
0058     &dev_attr_vendor_id.attr,
0059     &dev_attr_subsystem_id.attr,
0060     &dev_attr_revision_id.attr,
0061     &dev_attr_afg.attr,
0062     &dev_attr_mfg.attr,
0063     &dev_attr_vendor_name.attr,
0064     &dev_attr_chip_name.attr,
0065     &dev_attr_modalias.attr,
0066     NULL
0067 };
0068 
0069 static const struct attribute_group hdac_dev_attr_group = {
0070     .attrs  = hdac_dev_attrs,
0071 };
0072 
0073 const struct attribute_group *hdac_dev_attr_groups[] = {
0074     &hdac_dev_attr_group,
0075     NULL
0076 };
0077 
0078 /*
0079  * Widget tree sysfs
0080  *
0081  * This is a tree showing the attributes of each widget.  It appears like
0082  * /sys/bus/hdaudioC0D0/widgets/04/caps
0083  */
0084 
0085 struct widget_attribute;
0086 
0087 struct widget_attribute {
0088     struct attribute    attr;
0089     ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid,
0090             struct widget_attribute *attr, char *buf);
0091     ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid,
0092              struct widget_attribute *attr,
0093              const char *buf, size_t count);
0094 };
0095 
0096 static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp)
0097 {
0098     struct device *dev = kobj_to_dev(kobj->parent->parent);
0099     int nid;
0100     ssize_t ret;
0101 
0102     ret = kstrtoint(kobj->name, 16, &nid);
0103     if (ret < 0)
0104         return ret;
0105     *codecp = dev_to_hdac_dev(dev);
0106     return nid;
0107 }
0108 
0109 static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr,
0110                 char *buf)
0111 {
0112     struct widget_attribute *wid_attr =
0113         container_of(attr, struct widget_attribute, attr);
0114     struct hdac_device *codec;
0115     int nid;
0116 
0117     if (!wid_attr->show)
0118         return -EIO;
0119     nid = get_codec_nid(kobj, &codec);
0120     if (nid < 0)
0121         return nid;
0122     return wid_attr->show(codec, nid, wid_attr, buf);
0123 }
0124 
0125 static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr,
0126                  const char *buf, size_t count)
0127 {
0128     struct widget_attribute *wid_attr =
0129         container_of(attr, struct widget_attribute, attr);
0130     struct hdac_device *codec;
0131     int nid;
0132 
0133     if (!wid_attr->store)
0134         return -EIO;
0135     nid = get_codec_nid(kobj, &codec);
0136     if (nid < 0)
0137         return nid;
0138     return wid_attr->store(codec, nid, wid_attr, buf, count);
0139 }
0140 
0141 static const struct sysfs_ops widget_sysfs_ops = {
0142     .show   = widget_attr_show,
0143     .store  = widget_attr_store,
0144 };
0145 
0146 static void widget_release(struct kobject *kobj)
0147 {
0148     kfree(kobj);
0149 }
0150 
0151 static struct kobj_type widget_ktype = {
0152     .release    = widget_release,
0153     .sysfs_ops  = &widget_sysfs_ops,
0154 };
0155 
0156 #define WIDGET_ATTR_RO(_name) \
0157     struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
0158 #define WIDGET_ATTR_RW(_name) \
0159     struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
0160 
0161 static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid,
0162             struct widget_attribute *attr, char *buf)
0163 {
0164     return sysfs_emit(buf, "0x%08x\n", get_wcaps(codec, nid));
0165 }
0166 
0167 static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid,
0168                  struct widget_attribute *attr, char *buf)
0169 {
0170     if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
0171         return 0;
0172     return sysfs_emit(buf, "0x%08x\n",
0173               snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP));
0174 }
0175 
0176 static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid,
0177                 struct widget_attribute *attr, char *buf)
0178 {
0179     unsigned int val;
0180 
0181     if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
0182         return 0;
0183     if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
0184         return 0;
0185     return sysfs_emit(buf, "0x%08x\n", val);
0186 }
0187 
0188 static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid)
0189 {
0190     if (nid == codec->afg || nid == codec->mfg)
0191         return true;
0192     switch (get_wcaps_type(get_wcaps(codec, nid))) {
0193     case AC_WID_AUD_OUT:
0194     case AC_WID_AUD_IN:
0195         return true;
0196     default:
0197         return false;
0198     }
0199 }
0200 
0201 static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid,
0202                  struct widget_attribute *attr, char *buf)
0203 {
0204     if (!has_pcm_cap(codec, nid))
0205         return 0;
0206     return sysfs_emit(buf, "0x%08x\n",
0207               snd_hdac_read_parm(codec, nid, AC_PAR_PCM));
0208 }
0209 
0210 static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid,
0211                 struct widget_attribute *attr, char *buf)
0212 {
0213     if (!has_pcm_cap(codec, nid))
0214         return 0;
0215     return sysfs_emit(buf, "0x%08x\n",
0216               snd_hdac_read_parm(codec, nid, AC_PAR_STREAM));
0217 }
0218 
0219 static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid,
0220                 struct widget_attribute *attr, char *buf)
0221 {
0222     if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
0223         return 0;
0224     return sysfs_emit(buf, "0x%08x\n",
0225               snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP));
0226 }
0227 
0228 static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid,
0229                  struct widget_attribute *attr, char *buf)
0230 {
0231     if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
0232         return 0;
0233     return sysfs_emit(buf, "0x%08x\n",
0234               snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP));
0235 }
0236 
0237 static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid,
0238                    struct widget_attribute *attr, char *buf)
0239 {
0240     if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER))
0241         return 0;
0242     return sysfs_emit(buf, "0x%08x\n",
0243               snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE));
0244 }
0245 
0246 static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid,
0247                   struct widget_attribute *attr, char *buf)
0248 {
0249     return sysfs_emit(buf, "0x%08x\n",
0250               snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP));
0251 }
0252 
0253 static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid,
0254                 struct widget_attribute *attr, char *buf)
0255 {
0256     hda_nid_t list[32];
0257     int i, nconns;
0258     ssize_t ret = 0;
0259 
0260     nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list));
0261     if (nconns <= 0)
0262         return nconns;
0263     for (i = 0; i < nconns; i++)
0264         ret += sysfs_emit_at(buf,  ret, "%s0x%02x", i ? " " : "", list[i]);
0265     ret += sysfs_emit_at(buf, ret, "\n");
0266     return ret;
0267 }
0268 
0269 static WIDGET_ATTR_RO(caps);
0270 static WIDGET_ATTR_RO(pin_caps);
0271 static WIDGET_ATTR_RO(pin_cfg);
0272 static WIDGET_ATTR_RO(pcm_caps);
0273 static WIDGET_ATTR_RO(pcm_formats);
0274 static WIDGET_ATTR_RO(amp_in_caps);
0275 static WIDGET_ATTR_RO(amp_out_caps);
0276 static WIDGET_ATTR_RO(power_caps);
0277 static WIDGET_ATTR_RO(gpio_caps);
0278 static WIDGET_ATTR_RO(connections);
0279 
0280 static struct attribute *widget_node_attrs[] = {
0281     &wid_attr_caps.attr,
0282     &wid_attr_pin_caps.attr,
0283     &wid_attr_pin_cfg.attr,
0284     &wid_attr_pcm_caps.attr,
0285     &wid_attr_pcm_formats.attr,
0286     &wid_attr_amp_in_caps.attr,
0287     &wid_attr_amp_out_caps.attr,
0288     &wid_attr_power_caps.attr,
0289     &wid_attr_connections.attr,
0290     NULL,
0291 };
0292 
0293 static struct attribute *widget_afg_attrs[] = {
0294     &wid_attr_pcm_caps.attr,
0295     &wid_attr_pcm_formats.attr,
0296     &wid_attr_amp_in_caps.attr,
0297     &wid_attr_amp_out_caps.attr,
0298     &wid_attr_power_caps.attr,
0299     &wid_attr_gpio_caps.attr,
0300     NULL,
0301 };
0302 
0303 static const struct attribute_group widget_node_group = {
0304     .attrs = widget_node_attrs,
0305 };
0306 
0307 static const struct attribute_group widget_afg_group = {
0308     .attrs = widget_afg_attrs,
0309 };
0310 
0311 static void free_widget_node(struct kobject *kobj,
0312                  const struct attribute_group *group)
0313 {
0314     if (kobj) {
0315         sysfs_remove_group(kobj, group);
0316         kobject_put(kobj);
0317     }
0318 }
0319 
0320 static void widget_tree_free(struct hdac_device *codec)
0321 {
0322     struct hdac_widget_tree *tree = codec->widgets;
0323     struct kobject **p;
0324 
0325     if (!tree)
0326         return;
0327     free_widget_node(tree->afg, &widget_afg_group);
0328     if (tree->nodes) {
0329         for (p = tree->nodes; *p; p++)
0330             free_widget_node(*p, &widget_node_group);
0331         kfree(tree->nodes);
0332     }
0333     kobject_put(tree->root);
0334     kfree(tree);
0335     codec->widgets = NULL;
0336 }
0337 
0338 static int add_widget_node(struct kobject *parent, hda_nid_t nid,
0339                const struct attribute_group *group,
0340                struct kobject **res)
0341 {
0342     struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
0343     int err;
0344 
0345     if (!kobj)
0346         return -ENOMEM;
0347     kobject_init(kobj, &widget_ktype);
0348     err = kobject_add(kobj, parent, "%02x", nid);
0349     if (err < 0)
0350         return err;
0351     err = sysfs_create_group(kobj, group);
0352     if (err < 0) {
0353         kobject_put(kobj);
0354         return err;
0355     }
0356 
0357     *res = kobj;
0358     return 0;
0359 }
0360 
0361 static int widget_tree_create(struct hdac_device *codec)
0362 {
0363     struct hdac_widget_tree *tree;
0364     int i, err;
0365     hda_nid_t nid;
0366 
0367     tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL);
0368     if (!tree)
0369         return -ENOMEM;
0370 
0371     tree->root = kobject_create_and_add("widgets", &codec->dev.kobj);
0372     if (!tree->root)
0373         return -ENOMEM;
0374 
0375     tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes),
0376                   GFP_KERNEL);
0377     if (!tree->nodes)
0378         return -ENOMEM;
0379 
0380     for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
0381         err = add_widget_node(tree->root, nid, &widget_node_group,
0382                       &tree->nodes[i]);
0383         if (err < 0)
0384             return err;
0385     }
0386 
0387     if (codec->afg) {
0388         err = add_widget_node(tree->root, codec->afg,
0389                       &widget_afg_group, &tree->afg);
0390         if (err < 0)
0391             return err;
0392     }
0393 
0394     kobject_uevent(tree->root, KOBJ_CHANGE);
0395     return 0;
0396 }
0397 
0398 /* call with codec->widget_lock held */
0399 int hda_widget_sysfs_init(struct hdac_device *codec)
0400 {
0401     int err;
0402 
0403     if (codec->widgets)
0404         return 0; /* already created */
0405 
0406     err = widget_tree_create(codec);
0407     if (err < 0) {
0408         widget_tree_free(codec);
0409         return err;
0410     }
0411 
0412     return 0;
0413 }
0414 
0415 /* call with codec->widget_lock held */
0416 void hda_widget_sysfs_exit(struct hdac_device *codec)
0417 {
0418     widget_tree_free(codec);
0419 }
0420 
0421 /* call with codec->widget_lock held */
0422 int hda_widget_sysfs_reinit(struct hdac_device *codec,
0423                 hda_nid_t start_nid, int num_nodes)
0424 {
0425     struct hdac_widget_tree *tree;
0426     hda_nid_t end_nid = start_nid + num_nodes;
0427     hda_nid_t nid;
0428     int i;
0429 
0430     if (!codec->widgets)
0431         return 0;
0432 
0433     tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL);
0434     if (!tree)
0435         return -ENOMEM;
0436 
0437     tree->nodes = kcalloc(num_nodes + 1, sizeof(*tree->nodes), GFP_KERNEL);
0438     if (!tree->nodes) {
0439         kfree(tree);
0440         return -ENOMEM;
0441     }
0442 
0443     /* prune non-existing nodes */
0444     for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
0445         if (nid < start_nid || nid >= end_nid)
0446             free_widget_node(codec->widgets->nodes[i],
0447                      &widget_node_group);
0448     }
0449 
0450     /* add new nodes */
0451     for (i = 0, nid = start_nid; i < num_nodes; i++, nid++) {
0452         if (nid < codec->start_nid || nid >= codec->end_nid)
0453             add_widget_node(tree->root, nid, &widget_node_group,
0454                     &tree->nodes[i]);
0455         else
0456             tree->nodes[i] =
0457                 codec->widgets->nodes[nid - codec->start_nid];
0458     }
0459 
0460     /* replace with the new tree */
0461     kfree(codec->widgets->nodes);
0462     kfree(codec->widgets);
0463     codec->widgets = tree;
0464 
0465     kobject_uevent(tree->root, KOBJ_CHANGE);
0466     return 0;
0467 }