linux/drivers/gpu/drm/drm_sysfs.c
<<
>>
Prefs
   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
  26static 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 */
  38static 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 */
  59static 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 */
  74static 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
  80static char *drm_devnode(struct device *dev, mode_t *mode)
  81{
  82        return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
  83}
  84
  85static 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 */
  98struct 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
 120err_out_class:
 121        class_destroy(class);
 122err_out:
 123        return ERR_PTR(err);
 124}
 125
 126/**
 127 * drm_sysfs_destroy - destroys DRM class
 128 *
 129 * Destroy the DRM device class.
 130 */
 131void 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 */
 147static 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 */
 156static 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
 168static 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
 187static 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
 197static 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
 223static 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
 239static 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
 280static 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
 321static 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. */
 329static struct device_attribute connector_attrs_opt1[] = {
 330        __ATTR_RO(subconnector),
 331        __ATTR_RO(select_subconnector),
 332};
 333
 334static 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 */
 355int 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
 417err_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
 424out:
 425        return ret;
 426}
 427EXPORT_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 */
 442void 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}
 454EXPORT_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 */
 464void 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}
 473EXPORT_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 */
 484int 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
 511err_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 */
 522void 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
 538int drm_class_device_register(struct device *dev)
 539{
 540        dev->class = drm_class;
 541        return device_register(dev);
 542}
 543EXPORT_SYMBOL_GPL(drm_class_device_register);
 544
 545void drm_class_device_unregister(struct device *dev)
 546{
 547        return device_unregister(dev);
 548}
 549EXPORT_SYMBOL_GPL(drm_class_device_unregister);
 550