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