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