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); 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 kobject *kobj, struct bin_attribute *attr, 197 char *buf, loff_t off, size_t count) 198 { 199 struct device *connector_dev = container_of(kobj, struct device, kobj); 200 struct drm_connector *connector = to_drm_connector(connector_dev); 201 unsigned char *edid; 202 size_t size; 203 204 if (!connector->edid_blob_ptr) 205 return 0; 206 207 edid = connector->edid_blob_ptr->data; 208 size = connector->edid_blob_ptr->length; 209 if (!edid) 210 return 0; 211 212 if (off >= size) 213 return 0; 214 215 if (off + count > size) 216 count = size - off; 217 memcpy(buf, edid + off, count); 218 219 return count; 220 } 221 222 static ssize_t modes_show(struct device *device, 223 struct device_attribute *attr, 224 char *buf) 225 { 226 struct drm_connector *connector = to_drm_connector(device); 227 struct drm_display_mode *mode; 228 int written = 0; 229 230 list_for_each_entry(mode, &connector->modes, head) { 231 written += snprintf(buf + written, PAGE_SIZE - written, "%s\n", 232 mode->name); 233 } 234 235 return written; 236 } 237 238 static ssize_t subconnector_show(struct device *device, 239 struct device_attribute *attr, 240 char *buf) 241 { 242 struct drm_connector *connector = to_drm_connector(device); 243 struct drm_device *dev = connector->dev; 244 struct drm_property *prop = NULL; 245 uint64_t subconnector; 246 int is_tv = 0; 247 int ret; 248 249 switch (connector->connector_type) { 250 case DRM_MODE_CONNECTOR_DVII: 251 prop = dev->mode_config.dvi_i_subconnector_property; 252 break; 253 case DRM_MODE_CONNECTOR_Composite: 254 case DRM_MODE_CONNECTOR_SVIDEO: 255 case DRM_MODE_CONNECTOR_Component: 256 case DRM_MODE_CONNECTOR_TV: 257 prop = dev->mode_config.tv_subconnector_property; 258 is_tv = 1; 259 break; 260 default: 261 DRM_ERROR("Wrong connector type for this property\n"); 262 return 0; 263 } 264 265 if (!prop) { 266 DRM_ERROR("Unable to find subconnector property\n"); 267 return 0; 268 } 269 270 ret = drm_connector_property_get_value(connector, prop, &subconnector); 271 if (ret) 272 return 0; 273 274 return snprintf(buf, PAGE_SIZE, "%s", is_tv ? 275 drm_get_tv_subconnector_name((int)subconnector) : 276 drm_get_dvi_i_subconnector_name((int)subconnector)); 277 } 278 279 static ssize_t select_subconnector_show(struct device *device, 280 struct device_attribute *attr, 281 char *buf) 282 { 283 struct drm_connector *connector = to_drm_connector(device); 284 struct drm_device *dev = connector->dev; 285 struct drm_property *prop = NULL; 286 uint64_t subconnector; 287 int is_tv = 0; 288 int ret; 289 290 switch (connector->connector_type) { 291 case DRM_MODE_CONNECTOR_DVII: 292 prop = dev->mode_config.dvi_i_select_subconnector_property; 293 break; 294 case DRM_MODE_CONNECTOR_Composite: 295 case DRM_MODE_CONNECTOR_SVIDEO: 296 case DRM_MODE_CONNECTOR_Component: 297 case DRM_MODE_CONNECTOR_TV: 298 prop = dev->mode_config.tv_select_subconnector_property; 299 is_tv = 1; 300 break; 301 default: 302 DRM_ERROR("Wrong connector type for this property\n"); 303 return 0; 304 } 305 306 if (!prop) { 307 DRM_ERROR("Unable to find select subconnector property\n"); 308 return 0; 309 } 310 311 ret = drm_connector_property_get_value(connector, prop, &subconnector); 312 if (ret) 313 return 0; 314 315 return snprintf(buf, PAGE_SIZE, "%s", is_tv ? 316 drm_get_tv_select_name((int)subconnector) : 317 drm_get_dvi_i_select_name((int)subconnector)); 318 } 319 320 static struct device_attribute connector_attrs[] = { 321 __ATTR_RO(status), 322 __ATTR_RO(enabled), 323 __ATTR_RO(dpms), 324 __ATTR_RO(modes), 325 }; 326 327 /* These attributes are for both DVI-I connectors and all types of tv-out. */ 328 static struct device_attribute connector_attrs_opt1[] = { 329 __ATTR_RO(subconnector), 330 __ATTR_RO(select_subconnector), 331 }; 332 333 static struct bin_attribute edid_attr = { 334 .attr.name = "edid", 335 .attr.mode = 0444, 336 .size = 128, 337 .read = edid_show, 338 }; 339 340 /** 341 * drm_sysfs_connector_add - add an connector to sysfs 342 * @connector: connector to add 343 * 344 * Create an connector device in sysfs, along with its associated connector 345 * properties (so far, connection status, dpms, mode list & edid) and 346 * generate a hotplug event so userspace knows there's a new connector 347 * available. 348 * 349 * Note: 350 * This routine should only be called *once* for each DRM minor registered. 351 * A second call for an already registered device will trigger the BUG_ON 352 * below. 353 */ 354 int drm_sysfs_connector_add(struct drm_connector *connector) 355 { 356 struct drm_device *dev = connector->dev; 357 int ret = 0, i, j; 358 359 /* We shouldn't get called more than once for the same connector */ 360 BUG_ON(device_is_registered(&connector->kdev)); 361 362 connector->kdev.parent = &dev->primary->kdev; 363 connector->kdev.class = drm_class; 364 connector->kdev.release = drm_sysfs_device_release; 365 366 DRM_DEBUG("adding \"%s\" to sysfs\n", 367 drm_get_connector_name(connector)); 368 369 dev_set_name(&connector->kdev, "card%d-%s", 370 dev->primary->index, drm_get_connector_name(connector)); 371 ret = device_register(&connector->kdev); 372 373 if (ret) { 374 DRM_ERROR("failed to register connector device: %d\n", ret); 375 goto out; 376 } 377 378 /* Standard attributes */ 379 380 for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) { 381 ret = device_create_file(&connector->kdev, &connector_attrs[i]); 382 if (ret) 383 goto err_out_files; 384 } 385 386 /* Optional attributes */ 387 /* 388 * In the long run it maybe a good idea to make one set of 389 * optionals per connector type. 390 */ 391 switch (connector->connector_type) { 392 case DRM_MODE_CONNECTOR_DVII: 393 case DRM_MODE_CONNECTOR_Composite: 394 case DRM_MODE_CONNECTOR_SVIDEO: 395 case DRM_MODE_CONNECTOR_Component: 396 case DRM_MODE_CONNECTOR_TV: 397 for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) { 398 ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]); 399 if (ret) 400 goto err_out_files; 401 } 402 break; 403 default: 404 break; 405 } 406 407 ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr); 408 if (ret) 409 goto err_out_files; 410 411 /* Let userspace know we have a new connector */ 412 drm_sysfs_hotplug_event(dev); 413 414 return 0; 415 416 err_out_files: 417 if (i > 0) 418 for (j = 0; j < i; j++) 419 device_remove_file(&connector->kdev, 420 &connector_attrs[i]); 421 device_unregister(&connector->kdev); 422 423 out: 424 return ret; 425 } 426 EXPORT_SYMBOL(drm_sysfs_connector_add); 427 428 /** 429 * drm_sysfs_connector_remove - remove an connector device from sysfs 430 * @connector: connector to remove 431 * 432 * Remove @connector and its associated attributes from sysfs. Note that 433 * the device model core will take care of sending the "remove" uevent 434 * at this time, so we don't need to do it. 435 * 436 * Note: 437 * This routine should only be called if the connector was previously 438 * successfully registered. If @connector hasn't been registered yet, 439 * you'll likely see a panic somewhere deep in sysfs code when called. 440 */ 441 void drm_sysfs_connector_remove(struct drm_connector *connector) 442 { 443 int i; 444 445 DRM_DEBUG("removing \"%s\" from sysfs\n", 446 drm_get_connector_name(connector)); 447 448 for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) 449 device_remove_file(&connector->kdev, &connector_attrs[i]); 450 sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr); 451 device_unregister(&connector->kdev); 452 } 453 EXPORT_SYMBOL(drm_sysfs_connector_remove); 454 455 /** 456 * drm_sysfs_hotplug_event - generate a DRM uevent 457 * @dev: DRM device 458 * 459 * Send a uevent for the DRM device specified by @dev. Currently we only 460 * set HOTPLUG=1 in the uevent environment, but this could be expanded to 461 * deal with other types of events. 462 */ 463 void drm_sysfs_hotplug_event(struct drm_device *dev) 464 { 465 char *event_string = "HOTPLUG=1"; 466 char *envp[] = { event_string, NULL }; 467 468 DRM_DEBUG("generating hotplug event\n"); 469 470 kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); 471 } 472 EXPORT_SYMBOL(drm_sysfs_hotplug_event); 473 474 /** 475 * drm_sysfs_device_add - adds a class device to sysfs for a character driver 476 * @dev: DRM device to be added 477 * @head: DRM head in question 478 * 479 * Add a DRM device to the DRM's device model class. We use @dev's PCI device 480 * as the parent for the Linux device, and make sure it has a file containing 481 * the driver we're using (for userspace compatibility). 482 */ 483 int drm_sysfs_device_add(struct drm_minor *minor) 484 { 485 int err; 486 char *minor_str; 487 488 minor->kdev.parent = &minor->dev->pdev->dev; 489 minor->kdev.class = drm_class; 490 minor->kdev.release = drm_sysfs_device_release; 491 minor->kdev.devt = minor->device; 492 minor->kdev.type = &drm_sysfs_device_minor; 493 if (minor->type == DRM_MINOR_CONTROL) 494 minor_str = "controlD%d"; 495 else if (minor->type == DRM_MINOR_RENDER) 496 minor_str = "renderD%d"; 497 else 498 minor_str = "card%d"; 499 500 dev_set_name(&minor->kdev, minor_str, minor->index); 501 502 err = device_register(&minor->kdev); 503 if (err) { 504 DRM_ERROR("device add failed: %d\n", err); 505 goto err_out; 506 } 507 508 return 0; 509 510 err_out: 511 return err; 512 } 513 514 /** 515 * drm_sysfs_device_remove - remove DRM device 516 * @dev: DRM device to remove 517 * 518 * This call unregisters and cleans up a class device that was created with a 519 * call to drm_sysfs_device_add() 520 */ 521 void drm_sysfs_device_remove(struct drm_minor *minor) 522 { 523 device_unregister(&minor->kdev); 524 } 525 526 527 /** 528 * drm_class_device_register - Register a struct device in the drm class. 529 * 530 * @dev: pointer to struct device to register. 531 * 532 * @dev should have all relevant members pre-filled with the exception 533 * of the class member. In particular, the device_type member must 534 * be set. 535 */ 536 537 int drm_class_device_register(struct device *dev) 538 { 539 dev->class = drm_class; 540 return device_register(dev); 541 } 542 EXPORT_SYMBOL_GPL(drm_class_device_register); 543 544 void drm_class_device_unregister(struct device *dev) 545 { 546 return device_unregister(dev); 547 } 548 EXPORT_SYMBOL_GPL(drm_class_device_unregister); 549