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