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