Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.7 416 lines 10 kB view raw
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}