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/err.h> 18 19 #include "drm_sysfs.h" 20 #include "drm_core.h" 21 #include "drmP.h" 22 23 #define to_drm_minor(d) container_of(d, struct drm_minor, kdev) 24 #define to_drm_connector(d) container_of(d, struct drm_connector, kdev) 25 26 static struct device_type drm_sysfs_device_minor = { 27 .name = "drm_minor" 28 }; 29 30 /** 31 * drm_class_suspend - DRM class suspend hook 32 * @dev: Linux device to suspend 33 * @state: power state to enter 34 * 35 * Just figures out what the actual struct drm_device associated with 36 * @dev is and calls its suspend hook, if present. 37 */ 38 static int drm_class_suspend(struct device *dev, pm_message_t state) 39 { 40 if (dev->type == &drm_sysfs_device_minor) { 41 struct drm_minor *drm_minor = to_drm_minor(dev); 42 struct drm_device *drm_dev = drm_minor->dev; 43 44 if (drm_minor->type == DRM_MINOR_LEGACY && 45 !drm_core_check_feature(drm_dev, DRIVER_MODESET) && 46 drm_dev->driver->suspend) 47 return drm_dev->driver->suspend(drm_dev, state); 48 } 49 return 0; 50 } 51 52 /** 53 * drm_class_resume - DRM class resume hook 54 * @dev: Linux device to resume 55 * 56 * Just figures out what the actual struct drm_device associated with 57 * @dev is and calls its resume hook, if present. 58 */ 59 static int drm_class_resume(struct device *dev) 60 { 61 if (dev->type == &drm_sysfs_device_minor) { 62 struct drm_minor *drm_minor = to_drm_minor(dev); 63 struct drm_device *drm_dev = drm_minor->dev; 64 65 if (drm_minor->type == DRM_MINOR_LEGACY && 66 !drm_core_check_feature(drm_dev, DRIVER_MODESET) && 67 drm_dev->driver->resume) 68 return drm_dev->driver->resume(drm_dev); 69 } 70 return 0; 71 } 72 73 /* Display the version of drm_core. This doesn't work right in current design */ 74 static ssize_t version_show(struct class *dev, char *buf) 75 { 76 return sprintf(buf, "%s %d.%d.%d %s\n", CORE_NAME, CORE_MAJOR, 77 CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); 78 } 79 80 static char *drm_devnode(struct device *dev, mode_t *mode) 81 { 82 return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); 83 } 84 85 static CLASS_ATTR(version, S_IRUGO, version_show, NULL); 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); 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); 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 163 status = connector->funcs->detect(connector); 164 return snprintf(buf, PAGE_SIZE, "%s\n", 165 drm_get_connector_status_name(status)); 166 } 167 168 static ssize_t dpms_show(struct device *device, 169 struct device_attribute *attr, 170 char *buf) 171 { 172 struct drm_connector *connector = to_drm_connector(device); 173 struct drm_device *dev = connector->dev; 174 uint64_t dpms_status; 175 int ret; 176 177 ret = drm_connector_property_get_value(connector, 178 dev->mode_config.dpms_property, 179 &dpms_status); 180 if (ret) 181 return 0; 182 183 return snprintf(buf, PAGE_SIZE, "%s\n", 184 drm_get_dpms_name((int)dpms_status)); 185 } 186 187 static ssize_t enabled_show(struct device *device, 188 struct device_attribute *attr, 189 char *buf) 190 { 191 struct drm_connector *connector = to_drm_connector(device); 192 193 return snprintf(buf, PAGE_SIZE, "%s\n", connector->encoder ? "enabled" : 194 "disabled"); 195 } 196 197 static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr, 198 char *buf, loff_t off, 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 = 128, 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 ret = 0, i, j; 359 360 /* We shouldn't get called more than once for the same connector */ 361 BUG_ON(device_is_registered(&connector->kdev)); 362 363 connector->kdev.parent = &dev->primary->kdev; 364 connector->kdev.class = drm_class; 365 connector->kdev.release = drm_sysfs_device_release; 366 367 DRM_DEBUG("adding \"%s\" to sysfs\n", 368 drm_get_connector_name(connector)); 369 370 dev_set_name(&connector->kdev, "card%d-%s", 371 dev->primary->index, drm_get_connector_name(connector)); 372 ret = device_register(&connector->kdev); 373 374 if (ret) { 375 DRM_ERROR("failed to register connector device: %d\n", ret); 376 goto out; 377 } 378 379 /* Standard attributes */ 380 381 for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) { 382 ret = device_create_file(&connector->kdev, &connector_attrs[i]); 383 if (ret) 384 goto err_out_files; 385 } 386 387 /* Optional attributes */ 388 /* 389 * In the long run it maybe a good idea to make one set of 390 * optionals per connector type. 391 */ 392 switch (connector->connector_type) { 393 case DRM_MODE_CONNECTOR_DVII: 394 case DRM_MODE_CONNECTOR_Composite: 395 case DRM_MODE_CONNECTOR_SVIDEO: 396 case DRM_MODE_CONNECTOR_Component: 397 case DRM_MODE_CONNECTOR_TV: 398 for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) { 399 ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]); 400 if (ret) 401 goto err_out_files; 402 } 403 break; 404 default: 405 break; 406 } 407 408 ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr); 409 if (ret) 410 goto err_out_files; 411 412 /* Let userspace know we have a new connector */ 413 drm_sysfs_hotplug_event(dev); 414 415 return 0; 416 417 err_out_files: 418 if (i > 0) 419 for (j = 0; j < i; j++) 420 device_remove_file(&connector->kdev, 421 &connector_attrs[i]); 422 device_unregister(&connector->kdev); 423 424 out: 425 return ret; 426 } 427 EXPORT_SYMBOL(drm_sysfs_connector_add); 428 429 /** 430 * drm_sysfs_connector_remove - remove an connector device from sysfs 431 * @connector: connector to remove 432 * 433 * Remove @connector and its associated attributes from sysfs. Note that 434 * the device model core will take care of sending the "remove" uevent 435 * at this time, so we don't need to do it. 436 * 437 * Note: 438 * This routine should only be called if the connector was previously 439 * successfully registered. If @connector hasn't been registered yet, 440 * you'll likely see a panic somewhere deep in sysfs code when called. 441 */ 442 void drm_sysfs_connector_remove(struct drm_connector *connector) 443 { 444 int i; 445 446 DRM_DEBUG("removing \"%s\" from sysfs\n", 447 drm_get_connector_name(connector)); 448 449 for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) 450 device_remove_file(&connector->kdev, &connector_attrs[i]); 451 sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr); 452 device_unregister(&connector->kdev); 453 } 454 EXPORT_SYMBOL(drm_sysfs_connector_remove); 455 456 /** 457 * drm_sysfs_hotplug_event - generate a DRM uevent 458 * @dev: DRM device 459 * 460 * Send a uevent for the DRM device specified by @dev. Currently we only 461 * set HOTPLUG=1 in the uevent environment, but this could be expanded to 462 * deal with other types of events. 463 */ 464 void drm_sysfs_hotplug_event(struct drm_device *dev) 465 { 466 char *event_string = "HOTPLUG=1"; 467 char *envp[] = { event_string, NULL }; 468 469 DRM_DEBUG("generating hotplug event\n"); 470 471 kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); 472 } 473 EXPORT_SYMBOL(drm_sysfs_hotplug_event); 474 475 /** 476 * drm_sysfs_device_add - adds a class device to sysfs for a character driver 477 * @dev: DRM device to be added 478 * @head: DRM head in question 479 * 480 * Add a DRM device to the DRM's device model class. We use @dev's PCI device 481 * as the parent for the Linux device, and make sure it has a file containing 482 * the driver we're using (for userspace compatibility). 483 */ 484 int drm_sysfs_device_add(struct drm_minor *minor) 485 { 486 int err; 487 char *minor_str; 488 489 minor->kdev.parent = &minor->dev->pdev->dev; 490 minor->kdev.class = drm_class; 491 minor->kdev.release = drm_sysfs_device_release; 492 minor->kdev.devt = minor->device; 493 minor->kdev.type = &drm_sysfs_device_minor; 494 if (minor->type == DRM_MINOR_CONTROL) 495 minor_str = "controlD%d"; 496 else if (minor->type == DRM_MINOR_RENDER) 497 minor_str = "renderD%d"; 498 else 499 minor_str = "card%d"; 500 501 dev_set_name(&minor->kdev, minor_str, minor->index); 502 503 err = device_register(&minor->kdev); 504 if (err) { 505 DRM_ERROR("device add failed: %d\n", err); 506 goto err_out; 507 } 508 509 return 0; 510 511 err_out: 512 return err; 513 } 514 515 /** 516 * drm_sysfs_device_remove - remove DRM device 517 * @dev: DRM device to remove 518 * 519 * This call unregisters and cleans up a class device that was created with a 520 * call to drm_sysfs_device_add() 521 */ 522 void drm_sysfs_device_remove(struct drm_minor *minor) 523 { 524 device_unregister(&minor->kdev); 525 } 526 527 528 /** 529 * drm_class_device_register - Register a struct device in the drm class. 530 * 531 * @dev: pointer to struct device to register. 532 * 533 * @dev should have all relevant members pre-filled with the exception 534 * of the class member. In particular, the device_type member must 535 * be set. 536 */ 537 538 int drm_class_device_register(struct device *dev) 539 { 540 dev->class = drm_class; 541 return device_register(dev); 542 } 543 EXPORT_SYMBOL_GPL(drm_class_device_register); 544 545 void drm_class_device_unregister(struct device *dev) 546 { 547 return device_unregister(dev); 548 } 549 EXPORT_SYMBOL_GPL(drm_class_device_unregister); 550