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