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 12 struct hdac_widget_tree { 13 struct kobject *root; 14 struct kobject *afg; 15 struct kobject **nodes; 16 }; 17 18 #define CODEC_ATTR(type) \ 19 static 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 } \ 26 static DEVICE_ATTR_RO(type) 27 28 #define CODEC_ATTR_STR(type) \ 29 static 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 } \ 37 static DEVICE_ATTR_RO(type) 38 39 CODEC_ATTR(type); 40 CODEC_ATTR(vendor_id); 41 CODEC_ATTR(subsystem_id); 42 CODEC_ATTR(revision_id); 43 CODEC_ATTR(afg); 44 CODEC_ATTR(mfg); 45 CODEC_ATTR_STR(vendor_name); 46 CODEC_ATTR_STR(chip_name); 47 48 static 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 } 53 static DEVICE_ATTR_RO(modalias); 54 55 static 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 68 static struct attribute_group hdac_dev_attr_group = { 69 .attrs = hdac_dev_attrs, 70 }; 71 72 const 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 84 struct widget_attribute; 85 86 struct 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 95 static 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 108 static 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 124 static 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 140 static const struct sysfs_ops widget_sysfs_ops = { 141 .show = widget_attr_show, 142 .store = widget_attr_store, 143 }; 144 145 static void widget_release(struct kobject *kobj) 146 { 147 kfree(kobj); 148 } 149 150 static 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 160 static 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 166 static 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 175 static 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 187 static 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 200 static 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 209 static 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 218 static 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 227 static 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 236 static 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 245 static 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 252 static 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 268 static WIDGET_ATTR_RO(caps); 269 static WIDGET_ATTR_RO(pin_caps); 270 static WIDGET_ATTR_RO(pin_cfg); 271 static WIDGET_ATTR_RO(pcm_caps); 272 static WIDGET_ATTR_RO(pcm_formats); 273 static WIDGET_ATTR_RO(amp_in_caps); 274 static WIDGET_ATTR_RO(amp_out_caps); 275 static WIDGET_ATTR_RO(power_caps); 276 static WIDGET_ATTR_RO(gpio_caps); 277 static WIDGET_ATTR_RO(connections); 278 279 static 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 292 static 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 302 static const struct attribute_group widget_node_group = { 303 .attrs = widget_node_attrs, 304 }; 305 306 static const struct attribute_group widget_afg_group = { 307 .attrs = widget_afg_attrs, 308 }; 309 310 static 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 319 static 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 337 static 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 360 static 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 397 int 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 413 void hda_widget_sysfs_exit(struct hdac_device *codec) 414 { 415 widget_tree_free(codec); 416 } 417 418 int hda_widget_sysfs_reinit(struct hdac_device *codec, 419 hda_nid_t start_nid, int num_nodes) 420 { 421 struct hdac_widget_tree *tree; 422 hda_nid_t end_nid = start_nid + num_nodes; 423 hda_nid_t nid; 424 int i; 425 426 if (!codec->widgets) 427 return hda_widget_sysfs_init(codec); 428 429 tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL); 430 if (!tree) 431 return -ENOMEM; 432 433 tree->nodes = kcalloc(num_nodes + 1, sizeof(*tree->nodes), GFP_KERNEL); 434 if (!tree->nodes) { 435 kfree(tree); 436 return -ENOMEM; 437 } 438 439 /* prune non-existing nodes */ 440 for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) { 441 if (nid < start_nid || nid >= end_nid) 442 free_widget_node(codec->widgets->nodes[i], 443 &widget_node_group); 444 } 445 446 /* add new nodes */ 447 for (i = 0, nid = start_nid; i < num_nodes; i++, nid++) { 448 if (nid < codec->start_nid || nid >= codec->end_nid) 449 add_widget_node(tree->root, nid, &widget_node_group, 450 &tree->nodes[i]); 451 else 452 tree->nodes[i] = 453 codec->widgets->nodes[nid - codec->start_nid]; 454 } 455 456 /* replace with the new tree */ 457 kfree(codec->widgets->nodes); 458 kfree(codec->widgets); 459 codec->widgets = tree; 460 461 kobject_uevent(tree->root, KOBJ_CHANGE); 462 return 0; 463 } 464