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#include <linux/export.h>
  20
  21#include "drm_sysfs.h"
  22#include "drm_core.h"
  23#include "drmP.h"
  24
  25#define to_drm_minor(d) container_of(d, struct drm_minor, kdev)
  26#define to_drm_connector(d) container_of(d, struct drm_connector, kdev)
  27
  28static struct device_type drm_sysfs_device_minor = {
  29        .name = "drm_minor"
  30};
  31
  32/**
  33 * drm_class_suspend - DRM class suspend hook
  34 * @dev: Linux device to suspend
  35 * @state: power state to enter
  36 *
  37 * Just figures out what the actual struct drm_device associated with
  38 * @dev is and calls its suspend hook, if present.
  39 */
  40static int drm_class_suspend(struct device *dev, pm_message_t state)
  41{
  42        if (dev->type == &drm_sysfs_device_minor) {
  43                struct drm_minor *drm_minor = to_drm_minor(dev);
  44                struct drm_device *drm_dev = drm_minor->dev;
  45
  46                if (drm_minor->type == DRM_MINOR_LEGACY &&
  47                    !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
  48                    drm_dev->driver->suspend)
  49                        return drm_dev->driver->suspend(drm_dev, state);
  50        }
  51        return 0;
  52}
  53
  54/**
  55 * drm_class_resume - DRM class resume hook
  56 * @dev: Linux device to resume
  57 *
  58 * Just figures out what the actual struct drm_device associated with
  59 * @dev is and calls its resume hook, if present.
  60 */
  61static int drm_class_resume(struct device *dev)
  62{
  63        if (dev->type == &drm_sysfs_device_minor) {
  64                struct drm_minor *drm_minor = to_drm_minor(dev);
  65                struct drm_device *drm_dev = drm_minor->dev;
  66
  67                if (drm_minor->type == DRM_MINOR_LEGACY &&
  68                    !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
  69                    drm_dev->driver->resume)
  70                        return drm_dev->driver->resume(drm_dev);
  71        }
  72        return 0;
  73}
  74
  75static char *drm_devnode(struct device *dev, umode_t *mode)
  76{
  77        return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
  78}
  79
  80static CLASS_ATTR_STRING(version, S_IRUGO,
  81                CORE_NAME " "
  82                __stringify(CORE_MAJOR) "."
  83                __stringify(CORE_MINOR) "."
  84                __stringify(CORE_PATCHLEVEL) " "
  85                CORE_DATE);
  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.attr);
 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.attr);
 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        int ret;
 163
 164        ret = mutex_lock_interruptible(&connector->dev->mode_config.mutex);
 165        if (ret)
 166                return ret;
 167
 168        status = connector->funcs->detect(connector, true);
 169        mutex_unlock(&connector->dev->mode_config.mutex);
 170
 171        return snprintf(buf, PAGE_SIZE, "%s\n",
 172                        drm_get_connector_status_name(status));
 173}
 174
 175static ssize_t dpms_show(struct device *device,
 176                           struct device_attribute *attr,
 177                           char *buf)
 178{
 179        struct drm_connector *connector = to_drm_connector(device);
 180        struct drm_device *dev = connector->dev;
 181        uint64_t dpms_status;
 182        int ret;
 183
 184        ret = drm_connector_property_get_value(connector,
 185                                            dev->mode_config.dpms_property,
 186                                            &dpms_status);
 187        if (ret)
 188                return 0;
 189
 190        return snprintf(buf, PAGE_SIZE, "%s\n",
 191                        drm_get_dpms_name((int)dpms_status));
 192}
 193
 194static ssize_t enabled_show(struct device *device,
 195                            struct device_attribute *attr,
 196                           char *buf)
 197{
 198        struct drm_connector *connector = to_drm_connector(device);
 199
 200        return snprintf(buf, PAGE_SIZE, "%s\n", connector->encoder ? "enabled" :
 201                        "disabled");
 202}
 203
 204static ssize_t edid_show(struct file *filp, struct kobject *kobj,
 205                         struct bin_attribute *attr, char *buf, loff_t off,
 206                         size_t count)
 207{
 208        struct device *connector_dev = container_of(kobj, struct device, kobj);
 209        struct drm_connector *connector = to_drm_connector(connector_dev);
 210        unsigned char *edid;
 211        size_t size;
 212
 213        if (!connector->edid_blob_ptr)
 214                return 0;
 215
 216        edid = connector->edid_blob_ptr->data;
 217        size = connector->edid_blob_ptr->length;
 218        if (!edid)
 219                return 0;
 220
 221        if (off >= size)
 222                return 0;
 223
 224        if (off + count > size)
 225                count = size - off;
 226        memcpy(buf, edid + off, count);
 227
 228        return count;
 229}
 230
 231static ssize_t modes_show(struct device *device,
 232                           struct device_attribute *attr,
 233                           char *buf)
 234{
 235        struct drm_connector *connector = to_drm_connector(device);
 236        struct drm_display_mode *mode;
 237        int written = 0;
 238
 239        list_for_each_entry(mode, &connector->modes, head) {
 240                written += snprintf(buf + written, PAGE_SIZE - written, "%s\n",
 241                                    mode->name);
 242        }
 243
 244        return written;
 245}
 246
 247static ssize_t subconnector_show(struct device *device,
 248                           struct device_attribute *attr,
 249                           char *buf)
 250{
 251        struct drm_connector *connector = to_drm_connector(device);
 252        struct drm_device *dev = connector->dev;
 253        struct drm_property *prop = NULL;
 254        uint64_t subconnector;
 255        int is_tv = 0;
 256        int ret;
 257
 258        switch (connector->connector_type) {
 259                case DRM_MODE_CONNECTOR_DVII:
 260                        prop = dev->mode_config.dvi_i_subconnector_property;
 261                        break;
 262                case DRM_MODE_CONNECTOR_Composite:
 263                case DRM_MODE_CONNECTOR_SVIDEO:
 264                case DRM_MODE_CONNECTOR_Component:
 265                case DRM_MODE_CONNECTOR_TV:
 266                        prop = dev->mode_config.tv_subconnector_property;
 267                        is_tv = 1;
 268                        break;
 269                default:
 270                        DRM_ERROR("Wrong connector type for this property\n");
 271                        return 0;
 272        }
 273
 274        if (!prop) {
 275                DRM_ERROR("Unable to find subconnector property\n");
 276                return 0;
 277        }
 278
 279        ret = drm_connector_property_get_value(connector, prop, &subconnector);
 280        if (ret)
 281                return 0;
 282
 283        return snprintf(buf, PAGE_SIZE, "%s", is_tv ?
 284                        drm_get_tv_subconnector_name((int)subconnector) :
 285                        drm_get_dvi_i_subconnector_name((int)subconnector));
 286}
 287
 288static ssize_t select_subconnector_show(struct device *device,
 289                           struct device_attribute *attr,
 290                           char *buf)
 291{
 292        struct drm_connector *connector = to_drm_connector(device);
 293        struct drm_device *dev = connector->dev;
 294        struct drm_property *prop = NULL;
 295        uint64_t subconnector;
 296        int is_tv = 0;
 297        int ret;
 298
 299        switch (connector->connector_type) {
 300                case DRM_MODE_CONNECTOR_DVII:
 301                        prop = dev->mode_config.dvi_i_select_subconnector_property;
 302                        break;
 303                case DRM_MODE_CONNECTOR_Composite:
 304                case DRM_MODE_CONNECTOR_SVIDEO:
 305                case DRM_MODE_CONNECTOR_Component:
 306                case DRM_MODE_CONNECTOR_TV:
 307                        prop = dev->mode_config.tv_select_subconnector_property;
 308                        is_tv = 1;
 309                        break;
 310                default:
 311                        DRM_ERROR("Wrong connector type for this property\n");
 312                        return 0;
 313        }
 314
 315        if (!prop) {
 316                DRM_ERROR("Unable to find select subconnector property\n");
 317                return 0;
 318        }
 319
 320        ret = drm_connector_property_get_value(connector, prop, &subconnector);
 321        if (ret)
 322                return 0;
 323
 324        return snprintf(buf, PAGE_SIZE, "%s", is_tv ?
 325                        drm_get_tv_select_name((int)subconnector) :
 326                        drm_get_dvi_i_select_name((int)subconnector));
 327}
 328
 329static struct device_attribute connector_attrs[] = {
 330        __ATTR_RO(status),
 331        __ATTR_RO(enabled),
 332        __ATTR_RO(dpms),
 333        __ATTR_RO(modes),
 334};
 335
 336/* These attributes are for both DVI-I connectors and all types of tv-out. */
 337static struct device_attribute connector_attrs_opt1[] = {
 338        __ATTR_RO(subconnector),
 339        __ATTR_RO(select_subconnector),
 340};
 341
 342static struct bin_attribute edid_attr = {
 343        .attr.name = "edid",
 344        .attr.mode = 0444,
 345        .size = 0,
 346        .read = edid_show,
 347};
 348
 349/**
 350 * drm_sysfs_connector_add - add a connector to sysfs
 351 * @connector: connector to add
 352 *
 353 * Create a connector device in sysfs, along with its associated connector
 354 * properties (so far, connection status, dpms, mode list & edid) and
 355 * generate a hotplug event so userspace knows there's a new connector
 356 * available.
 357 *
 358 * Note:
 359 * This routine should only be called *once* for each registered connector.
 360 * A second call for an already registered connector will trigger the BUG_ON
 361 * below.
 362 */
 363int drm_sysfs_connector_add(struct drm_connector *connector)
 364{
 365        struct drm_device *dev = connector->dev;
 366        int attr_cnt = 0;
 367        int opt_cnt = 0;
 368        int i;
 369        int ret;
 370
 371        /* We shouldn't get called more than once for the same connector */
 372        BUG_ON(device_is_registered(&connector->kdev));
 373
 374        connector->kdev.parent = &dev->primary->kdev;
 375        connector->kdev.class = drm_class;
 376        connector->kdev.release = drm_sysfs_device_release;
 377
 378        DRM_DEBUG("adding \"%s\" to sysfs\n",
 379                  drm_get_connector_name(connector));
 380
 381        dev_set_name(&connector->kdev, "card%d-%s",
 382                     dev->primary->index, drm_get_connector_name(connector));
 383        ret = device_register(&connector->kdev);
 384
 385        if (ret) {
 386                DRM_ERROR("failed to register connector device: %d\n", ret);
 387                goto out;
 388        }
 389
 390        /* Standard attributes */
 391
 392        for (attr_cnt = 0; attr_cnt < ARRAY_SIZE(connector_attrs); attr_cnt++) {
 393                ret = device_create_file(&connector->kdev, &connector_attrs[attr_cnt]);
 394                if (ret)
 395                        goto err_out_files;
 396        }
 397
 398        /* Optional attributes */
 399        /*
 400         * In the long run it maybe a good idea to make one set of
 401         * optionals per connector type.
 402         */
 403        switch (connector->connector_type) {
 404                case DRM_MODE_CONNECTOR_DVII:
 405                case DRM_MODE_CONNECTOR_Composite:
 406                case DRM_MODE_CONNECTOR_SVIDEO:
 407                case DRM_MODE_CONNECTOR_Component:
 408                case DRM_MODE_CONNECTOR_TV:
 409                        for (opt_cnt = 0; opt_cnt < ARRAY_SIZE(connector_attrs_opt1); opt_cnt++) {
 410                                ret = device_create_file(&connector->kdev, &connector_attrs_opt1[opt_cnt]);
 411                                if (ret)
 412                                        goto err_out_files;
 413                        }
 414                        break;
 415                default:
 416                        break;
 417        }
 418
 419        ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr);
 420        if (ret)
 421                goto err_out_files;
 422
 423        /* Let userspace know we have a new connector */
 424        drm_sysfs_hotplug_event(dev);
 425
 426        return 0;
 427
 428err_out_files:
 429        for (i = 0; i < opt_cnt; i++)
 430                device_remove_file(&connector->kdev, &connector_attrs_opt1[i]);
 431        for (i = 0; i < attr_cnt; i++)
 432                device_remove_file(&connector->kdev, &connector_attrs[i]);
 433        device_unregister(&connector->kdev);
 434
 435out:
 436        return ret;
 437}
 438EXPORT_SYMBOL(drm_sysfs_connector_add);
 439
 440/**
 441 * drm_sysfs_connector_remove - remove an connector device from sysfs
 442 * @connector: connector to remove
 443 *
 444 * Remove @connector and its associated attributes from sysfs.  Note that
 445 * the device model core will take care of sending the "remove" uevent
 446 * at this time, so we don't need to do it.
 447 *
 448 * Note:
 449 * This routine should only be called if the connector was previously
 450 * successfully registered.  If @connector hasn't been registered yet,
 451 * you'll likely see a panic somewhere deep in sysfs code when called.
 452 */
 453void drm_sysfs_connector_remove(struct drm_connector *connector)
 454{
 455        int i;
 456
 457        if (!connector->kdev.parent)
 458                return;
 459        DRM_DEBUG("removing \"%s\" from sysfs\n",
 460                  drm_get_connector_name(connector));
 461
 462        for (i = 0; i < ARRAY_SIZE(connector_attrs); i++)
 463                device_remove_file(&connector->kdev, &connector_attrs[i]);
 464        sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr);
 465        device_unregister(&connector->kdev);
 466        connector->kdev.parent = NULL;
 467}
 468EXPORT_SYMBOL(drm_sysfs_connector_remove);
 469
 470/**
 471 * drm_sysfs_hotplug_event - generate a DRM uevent
 472 * @dev: DRM device
 473 *
 474 * Send a uevent for the DRM device specified by @dev.  Currently we only
 475 * set HOTPLUG=1 in the uevent environment, but this could be expanded to
 476 * deal with other types of events.
 477 */
 478void drm_sysfs_hotplug_event(struct drm_device *dev)
 479{
 480        char *event_string = "HOTPLUG=1";
 481        char *envp[] = { event_string, NULL };
 482
 483        DRM_DEBUG("generating hotplug event\n");
 484
 485        kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
 486}
 487EXPORT_SYMBOL(drm_sysfs_hotplug_event);
 488
 489/**
 490 * drm_sysfs_device_add - adds a class device to sysfs for a character driver
 491 * @dev: DRM device to be added
 492 * @head: DRM head in question
 493 *
 494 * Add a DRM device to the DRM's device model class.  We use @dev's PCI device
 495 * as the parent for the Linux device, and make sure it has a file containing
 496 * the driver we're using (for userspace compatibility).
 497 */
 498int drm_sysfs_device_add(struct drm_minor *minor)
 499{
 500        int err;
 501        char *minor_str;
 502
 503        minor->kdev.parent = minor->dev->dev;
 504
 505        minor->kdev.class = drm_class;
 506        minor->kdev.release = drm_sysfs_device_release;
 507        minor->kdev.devt = minor->device;
 508        minor->kdev.type = &drm_sysfs_device_minor;
 509        if (minor->type == DRM_MINOR_CONTROL)
 510                minor_str = "controlD%d";
 511        else if (minor->type == DRM_MINOR_RENDER)
 512                minor_str = "renderD%d";
 513        else
 514                minor_str = "card%d";
 515
 516        dev_set_name(&minor->kdev, minor_str, minor->index);
 517
 518        err = device_register(&minor->kdev);
 519        if (err) {
 520                DRM_ERROR("device add failed: %d\n", err);
 521                goto err_out;
 522        }
 523
 524        return 0;
 525
 526err_out:
 527        return err;
 528}
 529
 530/**
 531 * drm_sysfs_device_remove - remove DRM device
 532 * @dev: DRM device to remove
 533 *
 534 * This call unregisters and cleans up a class device that was created with a
 535 * call to drm_sysfs_device_add()
 536 */
 537void drm_sysfs_device_remove(struct drm_minor *minor)
 538{
 539        if (minor->kdev.parent)
 540                device_unregister(&minor->kdev);
 541        minor->kdev.parent = NULL;
 542}
 543
 544
 545/**
 546 * drm_class_device_register - Register a struct device in the drm class.
 547 *
 548 * @dev: pointer to struct device to register.
 549 *
 550 * @dev should have all relevant members pre-filled with the exception
 551 * of the class member. In particular, the device_type member must
 552 * be set.
 553 */
 554
 555int drm_class_device_register(struct device *dev)
 556{
 557        dev->class = drm_class;
 558        return device_register(dev);
 559}
 560EXPORT_SYMBOL_GPL(drm_class_device_register);
 561
 562void drm_class_device_unregister(struct device *dev)
 563{
 564        return device_unregister(dev);
 565}
 566EXPORT_SYMBOL_GPL(drm_class_device_unregister);
 567