root/sound/hda/hdac_sysfs.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. modalias_show
  2. get_codec_nid
  3. widget_attr_show
  4. widget_attr_store
  5. widget_release
  6. caps_show
  7. pin_caps_show
  8. pin_cfg_show
  9. has_pcm_cap
  10. pcm_caps_show
  11. pcm_formats_show
  12. amp_in_caps_show
  13. amp_out_caps_show
  14. power_caps_show
  15. gpio_caps_show
  16. connections_show
  17. free_widget_node
  18. widget_tree_free
  19. add_widget_node
  20. widget_tree_create
  21. hda_widget_sysfs_init
  22. hda_widget_sysfs_exit
  23. hda_widget_sysfs_reinit

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

/* [<][>][^][v][top][bottom][index][help] */