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 attr_cnt = 0; 358 int opt_cnt = 0; 359 int i; 360 int ret = 0; 361 362 /* We shouldn't get called more than once for the same connector */ 363 BUG_ON(device_is_registered(&connector->kdev)); 364 365 connector->kdev.parent = &dev->primary->kdev; 366 connector->kdev.class = drm_class; 367 connector->kdev.release = drm_sysfs_device_release; 368 369 DRM_DEBUG("adding \"%s\" to sysfs\n", 370 drm_get_connector_name(connector)); 371 372 dev_set_name(&connector->kdev, "card%d-%s", 373 dev->primary->index, drm_get_connector_name(connector)); 374 ret = device_register(&connector->kdev); 375 376 if (ret) { 377 DRM_ERROR("failed to register connector device: %d\n", ret); 378 goto out; 379 } 380 381 /* Standard attributes */ 382 383 for (attr_cnt = 0; attr_cnt < ARRAY_SIZE(connector_attrs); attr_cnt++) { 384 ret = device_create_file(&connector->kdev, &connector_attrs[attr_cnt]); 385 if (ret) 386 goto err_out_files; 387 } 388 389 /* Optional attributes */ 390 /* 391 * In the long run it maybe a good idea to make one set of 392 * optionals per connector type. 393 */ 394 switch (connector->connector_type) { 395 case DRM_MODE_CONNECTOR_DVII: 396 case DRM_MODE_CONNECTOR_Composite: 397 case DRM_MODE_CONNECTOR_SVIDEO: 398 case DRM_MODE_CONNECTOR_Component: 399 case DRM_MODE_CONNECTOR_TV: 400 for (opt_cnt = 0; opt_cnt < ARRAY_SIZE(connector_attrs_opt1); opt_cnt++) { 401 ret = device_create_file(&connector->kdev, &connector_attrs_opt1[opt_cnt]); 402 if (ret) 403 goto err_out_files; 404 } 405 break; 406 default: 407 break; 408 } 409 410 ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr); 411 if (ret) 412 goto err_out_files; 413 414 /* Let userspace know we have a new connector */ 415 drm_sysfs_hotplug_event(dev); 416 417 return 0; 418 419 err_out_files: 420 for (i = 0; i < opt_cnt; i++) 421 device_remove_file(&connector->kdev, &connector_attrs_opt1[i]); 422 for (i = 0; i < attr_cnt; i++) 423 device_remove_file(&connector->kdev, &connector_attrs[i]); 424 device_unregister(&connector->kdev); 425 426 out: 427 return ret; 428 } 429 EXPORT_SYMBOL(drm_sysfs_connector_add); 430 431 /** 432 * drm_sysfs_connector_remove - remove an connector device from sysfs 433 * @connector: connector to remove 434 * 435 * Remove @connector and its associated attributes from sysfs. Note that 436 * the device model core will take care of sending the "remove" uevent 437 * at this time, so we don't need to do it. 438 * 439 * Note: 440 * This routine should only be called if the connector was previously 441 * successfully registered. If @connector hasn't been registered yet, 442 * you'll likely see a panic somewhere deep in sysfs code when called. 443 */ 444 void drm_sysfs_connector_remove(struct drm_connector *connector) 445 { 446 int i; 447 448 DRM_DEBUG("removing \"%s\" from sysfs\n", 449 drm_get_connector_name(connector)); 450 451 for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) 452 device_remove_file(&connector->kdev, &connector_attrs[i]); 453 sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr); 454 device_unregister(&connector->kdev); 455 } 456 EXPORT_SYMBOL(drm_sysfs_connector_remove); 457 458 /** 459 * drm_sysfs_hotplug_event - generate a DRM uevent 460 * @dev: DRM device 461 * 462 * Send a uevent for the DRM device specified by @dev. Currently we only 463 * set HOTPLUG=1 in the uevent environment, but this could be expanded to 464 * deal with other types of events. 465 */ 466 void drm_sysfs_hotplug_event(struct drm_device *dev) 467 { 468 char *event_string = "HOTPLUG=1"; 469 char *envp[] = { event_string, NULL }; 470 471 DRM_DEBUG("generating hotplug event\n"); 472 473 kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); 474 } 475 EXPORT_SYMBOL(drm_sysfs_hotplug_event); 476 477 /** 478 * drm_sysfs_device_add - adds a class device to sysfs for a character driver 479 * @dev: DRM device to be added 480 * @head: DRM head in question 481 * 482 * Add a DRM device to the DRM's device model class. We use @dev's PCI device 483 * as the parent for the Linux device, and make sure it has a file containing 484 * the driver we're using (for userspace compatibility). 485 */ 486 int drm_sysfs_device_add(struct drm_minor *minor) 487 { 488 int err; 489 char *minor_str; 490 491 minor->kdev.parent = &minor->dev->pdev->dev; 492 minor->kdev.class = drm_class; 493 minor->kdev.release = drm_sysfs_device_release; 494 minor->kdev.devt = minor->device; 495 minor->kdev.type = &drm_sysfs_device_minor; 496 if (minor->type == DRM_MINOR_CONTROL) 497 minor_str = "controlD%d"; 498 else if (minor->type == DRM_MINOR_RENDER) 499 minor_str = "renderD%d"; 500 else 501 minor_str = "card%d"; 502 503 dev_set_name(&minor->kdev, minor_str, minor->index); 504 505 err = device_register(&minor->kdev); 506 if (err) { 507 DRM_ERROR("device add failed: %d\n", err); 508 goto err_out; 509 } 510 511 return 0; 512 513 err_out: 514 return err; 515 } 516 517 /** 518 * drm_sysfs_device_remove - remove DRM device 519 * @dev: DRM device to remove 520 * 521 * This call unregisters and cleans up a class device that was created with a 522 * call to drm_sysfs_device_add() 523 */ 524 void drm_sysfs_device_remove(struct drm_minor *minor) 525 { 526 device_unregister(&minor->kdev); 527 } 528 529 530 /** 531 * drm_class_device_register - Register a struct device in the drm class. 532 * 533 * @dev: pointer to struct device to register. 534 * 535 * @dev should have all relevant members pre-filled with the exception 536 * of the class member. In particular, the device_type member must 537 * be set. 538 */ 539 540 int drm_class_device_register(struct device *dev) 541 { 542 dev->class = drm_class; 543 return device_register(dev); 544 } 545 EXPORT_SYMBOL_GPL(drm_class_device_register); 546 547 void drm_class_device_unregister(struct device *dev) 548 { 549 return device_unregister(dev); 550 } 551 EXPORT_SYMBOL_GPL(drm_class_device_unregister); 552