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