1 2 /* 3 * drm_sysfs.c - Modifications to drm_sysfs_class.c to support 4 * extra sysfs attribute from DRM. Normal drm_sysfs_class 5 * does not allow adding attributes. 6 * 7 * Copyright (c) 2004 Jon Smirl <jonsmirl@gmail.com> 8 * Copyright (c) 2003-2004 Greg Kroah-Hartman <greg@kroah.com> 9 * Copyright (c) 2003-2004 IBM Corp. 10 * 11 * This file is released under the GPLv2 12 * 13 */ 14 15 #include <linux/device.h> 16 #include <linux/kdev_t.h> 17 #include <linux/gfp.h> 18 #include <linux/err.h> 19 20 #include "drm_sysfs.h" 21 #include "drm_core.h" 22 #include "drmP.h" 23 24 #define to_drm_minor(d) container_of(d, struct drm_minor, kdev) 25 #define to_drm_connector(d) container_of(d, struct drm_connector, kdev) 26 27 static struct device_type drm_sysfs_device_minor = { 28 .name = "drm_minor" 29 }; 30 31 /** 32 * drm_class_suspend - DRM class suspend hook 33 * @dev: Linux device to suspend 34 * @state: power state to enter 35 * 36 * Just figures out what the actual struct drm_device associated with 37 * @dev is and calls its suspend hook, if present. 38 */ 39 static int drm_class_suspend(struct device *dev, pm_message_t state) 40 { 41 if (dev->type == &drm_sysfs_device_minor) { 42 struct drm_minor *drm_minor = to_drm_minor(dev); 43 struct drm_device *drm_dev = drm_minor->dev; 44 45 if (drm_minor->type == DRM_MINOR_LEGACY && 46 !drm_core_check_feature(drm_dev, DRIVER_MODESET) && 47 drm_dev->driver->suspend) 48 return drm_dev->driver->suspend(drm_dev, state); 49 } 50 return 0; 51 } 52 53 /** 54 * drm_class_resume - DRM class resume hook 55 * @dev: Linux device to resume 56 * 57 * Just figures out what the actual struct drm_device associated with 58 * @dev is and calls its resume hook, if present. 59 */ 60 static int drm_class_resume(struct device *dev) 61 { 62 if (dev->type == &drm_sysfs_device_minor) { 63 struct drm_minor *drm_minor = to_drm_minor(dev); 64 struct drm_device *drm_dev = drm_minor->dev; 65 66 if (drm_minor->type == DRM_MINOR_LEGACY && 67 !drm_core_check_feature(drm_dev, DRIVER_MODESET) && 68 drm_dev->driver->resume) 69 return drm_dev->driver->resume(drm_dev); 70 } 71 return 0; 72 } 73 74 static char *drm_devnode(struct device *dev, mode_t *mode) 75 { 76 return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); 77 } 78 79 static CLASS_ATTR_STRING(version, S_IRUGO, 80 CORE_NAME " " 81 __stringify(CORE_MAJOR) "." 82 __stringify(CORE_MINOR) "." 83 __stringify(CORE_PATCHLEVEL) " " 84 CORE_DATE); 85 86 /** 87 * drm_sysfs_create - create a struct drm_sysfs_class structure 88 * @owner: pointer to the module that is to "own" this struct drm_sysfs_class 89 * @name: pointer to a string for the name of this class. 90 * 91 * This is used to create DRM class pointer that can then be used 92 * in calls to drm_sysfs_device_add(). 93 * 94 * Note, the pointer created here is to be destroyed when finished by making a 95 * call to drm_sysfs_destroy(). 96 */ 97 struct class *drm_sysfs_create(struct module *owner, char *name) 98 { 99 struct class *class; 100 int err; 101 102 class = class_create(owner, name); 103 if (IS_ERR(class)) { 104 err = PTR_ERR(class); 105 goto err_out; 106 } 107 108 class->suspend = drm_class_suspend; 109 class->resume = drm_class_resume; 110 111 err = class_create_file(class, &class_attr_version.attr); 112 if (err) 113 goto err_out_class; 114 115 class->devnode = drm_devnode; 116 117 return class; 118 119 err_out_class: 120 class_destroy(class); 121 err_out: 122 return ERR_PTR(err); 123 } 124 125 /** 126 * drm_sysfs_destroy - destroys DRM class 127 * 128 * Destroy the DRM device class. 129 */ 130 void drm_sysfs_destroy(void) 131 { 132 if ((drm_class == NULL) || (IS_ERR(drm_class))) 133 return; 134 class_remove_file(drm_class, &class_attr_version.attr); 135 class_destroy(drm_class); 136 } 137 138 /** 139 * drm_sysfs_device_release - do nothing 140 * @dev: Linux device 141 * 142 * Normally, this would free the DRM device associated with @dev, along 143 * with cleaning up any other stuff. But we do that in the DRM core, so 144 * this function can just return and hope that the core does its job. 145 */ 146 static void drm_sysfs_device_release(struct device *dev) 147 { 148 memset(dev, 0, sizeof(struct device)); 149 return; 150 } 151 152 /* 153 * Connector properties 154 */ 155 static ssize_t status_show(struct device *device, 156 struct device_attribute *attr, 157 char *buf) 158 { 159 struct drm_connector *connector = to_drm_connector(device); 160 enum drm_connector_status status; 161 162 status = connector->funcs->detect(connector, true); 163 return snprintf(buf, PAGE_SIZE, "%s\n", 164 drm_get_connector_status_name(status)); 165 } 166 167 static ssize_t dpms_show(struct device *device, 168 struct device_attribute *attr, 169 char *buf) 170 { 171 struct drm_connector *connector = to_drm_connector(device); 172 struct drm_device *dev = connector->dev; 173 uint64_t dpms_status; 174 int ret; 175 176 ret = drm_connector_property_get_value(connector, 177 dev->mode_config.dpms_property, 178 &dpms_status); 179 if (ret) 180 return 0; 181 182 return snprintf(buf, PAGE_SIZE, "%s\n", 183 drm_get_dpms_name((int)dpms_status)); 184 } 185 186 static ssize_t enabled_show(struct device *device, 187 struct device_attribute *attr, 188 char *buf) 189 { 190 struct drm_connector *connector = to_drm_connector(device); 191 192 return snprintf(buf, PAGE_SIZE, "%s\n", connector->encoder ? "enabled" : 193 "disabled"); 194 } 195 196 static ssize_t edid_show(struct file *filp, struct kobject *kobj, 197 struct bin_attribute *attr, char *buf, loff_t off, 198 size_t count) 199 { 200 struct device *connector_dev = container_of(kobj, struct device, kobj); 201 struct drm_connector *connector = to_drm_connector(connector_dev); 202 unsigned char *edid; 203 size_t size; 204 205 if (!connector->edid_blob_ptr) 206 return 0; 207 208 edid = connector->edid_blob_ptr->data; 209 size = connector->edid_blob_ptr->length; 210 if (!edid) 211 return 0; 212 213 if (off >= size) 214 return 0; 215 216 if (off + count > size) 217 count = size - off; 218 memcpy(buf, edid + off, count); 219 220 return count; 221 } 222 223 static ssize_t modes_show(struct device *device, 224 struct device_attribute *attr, 225 char *buf) 226 { 227 struct drm_connector *connector = to_drm_connector(device); 228 struct drm_display_mode *mode; 229 int written = 0; 230 231 list_for_each_entry(mode, &connector->modes, head) { 232 written += snprintf(buf + written, PAGE_SIZE - written, "%s\n", 233 mode->name); 234 } 235 236 return written; 237 } 238 239 static ssize_t subconnector_show(struct device *device, 240 struct device_attribute *attr, 241 char *buf) 242 { 243 struct drm_connector *connector = to_drm_connector(device); 244 struct drm_device *dev = connector->dev; 245 struct drm_property *prop = NULL; 246 uint64_t subconnector; 247 int is_tv = 0; 248 int ret; 249 250 switch (connector->connector_type) { 251 case DRM_MODE_CONNECTOR_DVII: 252 prop = dev->mode_config.dvi_i_subconnector_property; 253 break; 254 case DRM_MODE_CONNECTOR_Composite: 255 case DRM_MODE_CONNECTOR_SVIDEO: 256 case DRM_MODE_CONNECTOR_Component: 257 case DRM_MODE_CONNECTOR_TV: 258 prop = dev->mode_config.tv_subconnector_property; 259 is_tv = 1; 260 break; 261 default: 262 DRM_ERROR("Wrong connector type for this property\n"); 263 return 0; 264 } 265 266 if (!prop) { 267 DRM_ERROR("Unable to find subconnector property\n"); 268 return 0; 269 } 270 271 ret = drm_connector_property_get_value(connector, prop, &subconnector); 272 if (ret) 273 return 0; 274 275 return snprintf(buf, PAGE_SIZE, "%s", is_tv ? 276 drm_get_tv_subconnector_name((int)subconnector) : 277 drm_get_dvi_i_subconnector_name((int)subconnector)); 278 } 279 280 static ssize_t select_subconnector_show(struct device *device, 281 struct device_attribute *attr, 282 char *buf) 283 { 284 struct drm_connector *connector = to_drm_connector(device); 285 struct drm_device *dev = connector->dev; 286 struct drm_property *prop = NULL; 287 uint64_t subconnector; 288 int is_tv = 0; 289 int ret; 290 291 switch (connector->connector_type) { 292 case DRM_MODE_CONNECTOR_DVII: 293 prop = dev->mode_config.dvi_i_select_subconnector_property; 294 break; 295 case DRM_MODE_CONNECTOR_Composite: 296 case DRM_MODE_CONNECTOR_SVIDEO: 297 case DRM_MODE_CONNECTOR_Component: 298 case DRM_MODE_CONNECTOR_TV: 299 prop = dev->mode_config.tv_select_subconnector_property; 300 is_tv = 1; 301 break; 302 default: 303 DRM_ERROR("Wrong connector type for this property\n"); 304 return 0; 305 } 306 307 if (!prop) { 308 DRM_ERROR("Unable to find select subconnector property\n"); 309 return 0; 310 } 311 312 ret = drm_connector_property_get_value(connector, prop, &subconnector); 313 if (ret) 314 return 0; 315 316 return snprintf(buf, PAGE_SIZE, "%s", is_tv ? 317 drm_get_tv_select_name((int)subconnector) : 318 drm_get_dvi_i_select_name((int)subconnector)); 319 } 320 321 static struct device_attribute connector_attrs[] = { 322 __ATTR_RO(status), 323 __ATTR_RO(enabled), 324 __ATTR_RO(dpms), 325 __ATTR_RO(modes), 326 }; 327 328 /* These attributes are for both DVI-I connectors and all types of tv-out. */ 329 static struct device_attribute connector_attrs_opt1[] = { 330 __ATTR_RO(subconnector), 331 __ATTR_RO(select_subconnector), 332 }; 333 334 static struct bin_attribute edid_attr = { 335 .attr.name = "edid", 336 .attr.mode = 0444, 337 .size = 0, 338 .read = edid_show, 339 }; 340 341 /** 342 * drm_sysfs_connector_add - add an connector to sysfs 343 * @connector: connector to add 344 * 345 * Create an connector device in sysfs, along with its associated connector 346 * properties (so far, connection status, dpms, mode list & edid) and 347 * generate a hotplug event so userspace knows there's a new connector 348 * available. 349 * 350 * Note: 351 * This routine should only be called *once* for each DRM minor registered. 352 * A second call for an already registered device will trigger the BUG_ON 353 * below. 354 */ 355 int drm_sysfs_connector_add(struct drm_connector *connector) 356 { 357 struct drm_device *dev = connector->dev; 358 int attr_cnt = 0; 359 int opt_cnt = 0; 360 int i; 361 int ret = 0; 362 363 /* We shouldn't get called more than once for the same connector */ 364 BUG_ON(device_is_registered(&connector->kdev)); 365 366 connector->kdev.parent = &dev->primary->kdev; 367 connector->kdev.class = drm_class; 368 connector->kdev.release = drm_sysfs_device_release; 369 370 DRM_DEBUG("adding \"%s\" to sysfs\n", 371 drm_get_connector_name(connector)); 372 373 dev_set_name(&connector->kdev, "card%d-%s", 374 dev->primary->index, drm_get_connector_name(connector)); 375 ret = device_register(&connector->kdev); 376 377 if (ret) { 378 DRM_ERROR("failed to register connector device: %d\n", ret); 379 goto out; 380 } 381 382 /* Standard attributes */ 383 384 for (attr_cnt = 0; attr_cnt < ARRAY_SIZE(connector_attrs); attr_cnt++) { 385 ret = device_create_file(&connector->kdev, &connector_attrs[attr_cnt]); 386 if (ret) 387 goto err_out_files; 388 } 389 390 /* Optional attributes */ 391 /* 392 * In the long run it maybe a good idea to make one set of 393 * optionals per connector type. 394 */ 395 switch (connector->connector_type) { 396 case DRM_MODE_CONNECTOR_DVII: 397 case DRM_MODE_CONNECTOR_Composite: 398 case DRM_MODE_CONNECTOR_SVIDEO: 399 case DRM_MODE_CONNECTOR_Component: 400 case DRM_MODE_CONNECTOR_TV: 401 for (opt_cnt = 0; opt_cnt < ARRAY_SIZE(connector_attrs_opt1); opt_cnt++) { 402 ret = device_create_file(&connector->kdev, &connector_attrs_opt1[opt_cnt]); 403 if (ret) 404 goto err_out_files; 405 } 406 break; 407 default: 408 break; 409 } 410 411 ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr); 412 if (ret) 413 goto err_out_files; 414 415 /* Let userspace know we have a new connector */ 416 drm_sysfs_hotplug_event(dev); 417 418 return 0; 419 420 err_out_files: 421 for (i = 0; i < opt_cnt; i++) 422 device_remove_file(&connector->kdev, &connector_attrs_opt1[i]); 423 for (i = 0; i < attr_cnt; i++) 424 device_remove_file(&connector->kdev, &connector_attrs[i]); 425 device_unregister(&connector->kdev); 426 427 out: 428 return ret; 429 } 430 EXPORT_SYMBOL(drm_sysfs_connector_add); 431 432 /** 433 * drm_sysfs_connector_remove - remove an connector device from sysfs 434 * @connector: connector to remove 435 * 436 * Remove @connector and its associated attributes from sysfs. Note that 437 * the device model core will take care of sending the "remove" uevent 438 * at this time, so we don't need to do it. 439 * 440 * Note: 441 * This routine should only be called if the connector was previously 442 * successfully registered. If @connector hasn't been registered yet, 443 * you'll likely see a panic somewhere deep in sysfs code when called. 444 */ 445 void drm_sysfs_connector_remove(struct drm_connector *connector) 446 { 447 int i; 448 449 DRM_DEBUG("removing \"%s\" from sysfs\n", 450 drm_get_connector_name(connector)); 451 452 for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) 453 device_remove_file(&connector->kdev, &connector_attrs[i]); 454 sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr); 455 device_unregister(&connector->kdev); 456 } 457 EXPORT_SYMBOL(drm_sysfs_connector_remove); 458 459 /** 460 * drm_sysfs_hotplug_event - generate a DRM uevent 461 * @dev: DRM device 462 * 463 * Send a uevent for the DRM device specified by @dev. Currently we only 464 * set HOTPLUG=1 in the uevent environment, but this could be expanded to 465 * deal with other types of events. 466 */ 467 void drm_sysfs_hotplug_event(struct drm_device *dev) 468 { 469 char *event_string = "HOTPLUG=1"; 470 char *envp[] = { event_string, NULL }; 471 472 DRM_DEBUG("generating hotplug event\n"); 473 474 kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); 475 } 476 EXPORT_SYMBOL(drm_sysfs_hotplug_event); 477 478 /** 479 * drm_sysfs_device_add - adds a class device to sysfs for a character driver 480 * @dev: DRM device to be added 481 * @head: DRM head in question 482 * 483 * Add a DRM device to the DRM's device model class. We use @dev's PCI device 484 * as the parent for the Linux device, and make sure it has a file containing 485 * the driver we're using (for userspace compatibility). 486 */ 487 int drm_sysfs_device_add(struct drm_minor *minor) 488 { 489 int err; 490 char *minor_str; 491 492 minor->kdev.parent = minor->dev->dev; 493 494 minor->kdev.class = drm_class; 495 minor->kdev.release = drm_sysfs_device_release; 496 minor->kdev.devt = minor->device; 497 minor->kdev.type = &drm_sysfs_device_minor; 498 if (minor->type == DRM_MINOR_CONTROL) 499 minor_str = "controlD%d"; 500 else if (minor->type == DRM_MINOR_RENDER) 501 minor_str = "renderD%d"; 502 else 503 minor_str = "card%d"; 504 505 dev_set_name(&minor->kdev, minor_str, minor->index); 506 507 err = device_register(&minor->kdev); 508 if (err) { 509 DRM_ERROR("device add failed: %d\n", err); 510 goto err_out; 511 } 512 513 return 0; 514 515 err_out: 516 return err; 517 } 518 519 /** 520 * drm_sysfs_device_remove - remove DRM device 521 * @dev: DRM device to remove 522 * 523 * This call unregisters and cleans up a class device that was created with a 524 * call to drm_sysfs_device_add() 525 */ 526 void drm_sysfs_device_remove(struct drm_minor *minor) 527 { 528 device_unregister(&minor->kdev); 529 } 530 531 532 /** 533 * drm_class_device_register - Register a struct device in the drm class. 534 * 535 * @dev: pointer to struct device to register. 536 * 537 * @dev should have all relevant members pre-filled with the exception 538 * of the class member. In particular, the device_type member must 539 * be set. 540 */ 541 542 int drm_class_device_register(struct device *dev) 543 { 544 dev->class = drm_class; 545 return device_register(dev); 546 } 547 EXPORT_SYMBOL_GPL(drm_class_device_register); 548 549 void drm_class_device_unregister(struct device *dev) 550 { 551 return device_unregister(dev); 552 } 553 EXPORT_SYMBOL_GPL(drm_class_device_unregister); 554