linux/drivers/gpu/drm/drm_sysfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2
   3/*
   4 * drm_sysfs.c - Modifications to drm_sysfs_class.c to support
   5 *               extra sysfs attribute from DRM. Normal drm_sysfs_class
   6 *               does not allow adding attributes.
   7 *
   8 * Copyright (c) 2004 Jon Smirl <jonsmirl@gmail.com>
   9 * Copyright (c) 2003-2004 Greg Kroah-Hartman <greg@kroah.com>
  10 * Copyright (c) 2003-2004 IBM Corp.
  11 */
  12
  13#include <linux/device.h>
  14#include <linux/kdev_t.h>
  15#include <linux/gfp.h>
  16#include <linux/err.h>
  17#include <linux/export.h>
  18
  19#include <drm/drm_sysfs.h>
  20#include <drm/drmP.h>
  21#include "drm_internal.h"
  22
  23#define to_drm_minor(d) dev_get_drvdata(d)
  24#define to_drm_connector(d) dev_get_drvdata(d)
  25
  26/**
  27 * DOC: overview
  28 *
  29 * DRM provides very little additional support to drivers for sysfs
  30 * interactions, beyond just all the standard stuff. Drivers who want to expose
  31 * additional sysfs properties and property groups can attach them at either
  32 * &drm_device.dev or &drm_connector.kdev.
  33 *
  34 * Registration is automatically handled when calling drm_dev_register(), or
  35 * drm_connector_register() in case of hot-plugged connectors. Unregistration is
  36 * also automatically handled by drm_dev_unregister() and
  37 * drm_connector_unregister().
  38 */
  39
  40static struct device_type drm_sysfs_device_minor = {
  41        .name = "drm_minor"
  42};
  43
  44struct class *drm_class;
  45
  46static char *drm_devnode(struct device *dev, umode_t *mode)
  47{
  48        return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
  49}
  50
  51static CLASS_ATTR_STRING(version, S_IRUGO, "drm 1.1.0 20060810");
  52
  53/**
  54 * drm_sysfs_init - initialize sysfs helpers
  55 *
  56 * This is used to create the DRM class, which is the implicit parent of any
  57 * other top-level DRM sysfs objects.
  58 *
  59 * You must call drm_sysfs_destroy() to release the allocated resources.
  60 *
  61 * Return: 0 on success, negative error code on failure.
  62 */
  63int drm_sysfs_init(void)
  64{
  65        int err;
  66
  67        drm_class = class_create(THIS_MODULE, "drm");
  68        if (IS_ERR(drm_class))
  69                return PTR_ERR(drm_class);
  70
  71        err = class_create_file(drm_class, &class_attr_version.attr);
  72        if (err) {
  73                class_destroy(drm_class);
  74                drm_class = NULL;
  75                return err;
  76        }
  77
  78        drm_class->devnode = drm_devnode;
  79        return 0;
  80}
  81
  82/**
  83 * drm_sysfs_destroy - destroys DRM class
  84 *
  85 * Destroy the DRM device class.
  86 */
  87void drm_sysfs_destroy(void)
  88{
  89        if (IS_ERR_OR_NULL(drm_class))
  90                return;
  91        class_remove_file(drm_class, &class_attr_version.attr);
  92        class_destroy(drm_class);
  93        drm_class = NULL;
  94}
  95
  96/*
  97 * Connector properties
  98 */
  99static ssize_t status_store(struct device *device,
 100                           struct device_attribute *attr,
 101                           const char *buf, size_t count)
 102{
 103        struct drm_connector *connector = to_drm_connector(device);
 104        struct drm_device *dev = connector->dev;
 105        enum drm_connector_force old_force;
 106        int ret;
 107
 108        ret = mutex_lock_interruptible(&dev->mode_config.mutex);
 109        if (ret)
 110                return ret;
 111
 112        old_force = connector->force;
 113
 114        if (sysfs_streq(buf, "detect"))
 115                connector->force = 0;
 116        else if (sysfs_streq(buf, "on"))
 117                connector->force = DRM_FORCE_ON;
 118        else if (sysfs_streq(buf, "on-digital"))
 119                connector->force = DRM_FORCE_ON_DIGITAL;
 120        else if (sysfs_streq(buf, "off"))
 121                connector->force = DRM_FORCE_OFF;
 122        else
 123                ret = -EINVAL;
 124
 125        if (old_force != connector->force || !connector->force) {
 126                DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force updated from %d to %d or reprobing\n",
 127                              connector->base.id,
 128                              connector->name,
 129                              old_force, connector->force);
 130
 131                connector->funcs->fill_modes(connector,
 132                                             dev->mode_config.max_width,
 133                                             dev->mode_config.max_height);
 134        }
 135
 136        mutex_unlock(&dev->mode_config.mutex);
 137
 138        return ret ? ret : count;
 139}
 140
 141static ssize_t status_show(struct device *device,
 142                           struct device_attribute *attr,
 143                           char *buf)
 144{
 145        struct drm_connector *connector = to_drm_connector(device);
 146        enum drm_connector_status status;
 147
 148        status = READ_ONCE(connector->status);
 149
 150        return snprintf(buf, PAGE_SIZE, "%s\n",
 151                        drm_get_connector_status_name(status));
 152}
 153
 154static ssize_t dpms_show(struct device *device,
 155                           struct device_attribute *attr,
 156                           char *buf)
 157{
 158        struct drm_connector *connector = to_drm_connector(device);
 159        int dpms;
 160
 161        dpms = READ_ONCE(connector->dpms);
 162
 163        return snprintf(buf, PAGE_SIZE, "%s\n",
 164                        drm_get_dpms_name(dpms));
 165}
 166
 167static ssize_t enabled_show(struct device *device,
 168                            struct device_attribute *attr,
 169                           char *buf)
 170{
 171        struct drm_connector *connector = to_drm_connector(device);
 172        bool enabled;
 173
 174        enabled = READ_ONCE(connector->encoder);
 175
 176        return snprintf(buf, PAGE_SIZE, enabled ? "enabled\n" : "disabled\n");
 177}
 178
 179static ssize_t edid_show(struct file *filp, struct kobject *kobj,
 180                         struct bin_attribute *attr, char *buf, loff_t off,
 181                         size_t count)
 182{
 183        struct device *connector_dev = kobj_to_dev(kobj);
 184        struct drm_connector *connector = to_drm_connector(connector_dev);
 185        unsigned char *edid;
 186        size_t size;
 187        ssize_t ret = 0;
 188
 189        mutex_lock(&connector->dev->mode_config.mutex);
 190        if (!connector->edid_blob_ptr)
 191                goto unlock;
 192
 193        edid = connector->edid_blob_ptr->data;
 194        size = connector->edid_blob_ptr->length;
 195        if (!edid)
 196                goto unlock;
 197
 198        if (off >= size)
 199                goto unlock;
 200
 201        if (off + count > size)
 202                count = size - off;
 203        memcpy(buf, edid + off, count);
 204
 205        ret = count;
 206unlock:
 207        mutex_unlock(&connector->dev->mode_config.mutex);
 208
 209        return ret;
 210}
 211
 212static ssize_t modes_show(struct device *device,
 213                           struct device_attribute *attr,
 214                           char *buf)
 215{
 216        struct drm_connector *connector = to_drm_connector(device);
 217        struct drm_display_mode *mode;
 218        int written = 0;
 219
 220        mutex_lock(&connector->dev->mode_config.mutex);
 221        list_for_each_entry(mode, &connector->modes, head) {
 222                written += snprintf(buf + written, PAGE_SIZE - written, "%s\n",
 223                                    mode->name);
 224        }
 225        mutex_unlock(&connector->dev->mode_config.mutex);
 226
 227        return written;
 228}
 229
 230static DEVICE_ATTR_RW(status);
 231static DEVICE_ATTR_RO(enabled);
 232static DEVICE_ATTR_RO(dpms);
 233static DEVICE_ATTR_RO(modes);
 234
 235static struct attribute *connector_dev_attrs[] = {
 236        &dev_attr_status.attr,
 237        &dev_attr_enabled.attr,
 238        &dev_attr_dpms.attr,
 239        &dev_attr_modes.attr,
 240        NULL
 241};
 242
 243static struct bin_attribute edid_attr = {
 244        .attr.name = "edid",
 245        .attr.mode = 0444,
 246        .size = 0,
 247        .read = edid_show,
 248};
 249
 250static struct bin_attribute *connector_bin_attrs[] = {
 251        &edid_attr,
 252        NULL
 253};
 254
 255static const struct attribute_group connector_dev_group = {
 256        .attrs = connector_dev_attrs,
 257        .bin_attrs = connector_bin_attrs,
 258};
 259
 260static const struct attribute_group *connector_dev_groups[] = {
 261        &connector_dev_group,
 262        NULL
 263};
 264
 265int drm_sysfs_connector_add(struct drm_connector *connector)
 266{
 267        struct drm_device *dev = connector->dev;
 268
 269        if (connector->kdev)
 270                return 0;
 271
 272        connector->kdev =
 273                device_create_with_groups(drm_class, dev->primary->kdev, 0,
 274                                          connector, connector_dev_groups,
 275                                          "card%d-%s", dev->primary->index,
 276                                          connector->name);
 277        DRM_DEBUG("adding \"%s\" to sysfs\n",
 278                  connector->name);
 279
 280        if (IS_ERR(connector->kdev)) {
 281                DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev));
 282                return PTR_ERR(connector->kdev);
 283        }
 284
 285        /* Let userspace know we have a new connector */
 286        drm_sysfs_hotplug_event(dev);
 287
 288        return 0;
 289}
 290
 291void drm_sysfs_connector_remove(struct drm_connector *connector)
 292{
 293        if (!connector->kdev)
 294                return;
 295        DRM_DEBUG("removing \"%s\" from sysfs\n",
 296                  connector->name);
 297
 298        device_unregister(connector->kdev);
 299        connector->kdev = NULL;
 300}
 301
 302void drm_sysfs_lease_event(struct drm_device *dev)
 303{
 304        char *event_string = "LEASE=1";
 305        char *envp[] = { event_string, NULL };
 306
 307        DRM_DEBUG("generating lease event\n");
 308
 309        kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
 310}
 311
 312/**
 313 * drm_sysfs_hotplug_event - generate a DRM uevent
 314 * @dev: DRM device
 315 *
 316 * Send a uevent for the DRM device specified by @dev.  Currently we only
 317 * set HOTPLUG=1 in the uevent environment, but this could be expanded to
 318 * deal with other types of events.
 319 */
 320void drm_sysfs_hotplug_event(struct drm_device *dev)
 321{
 322        char *event_string = "HOTPLUG=1";
 323        char *envp[] = { event_string, NULL };
 324
 325        DRM_DEBUG("generating hotplug event\n");
 326
 327        kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
 328}
 329EXPORT_SYMBOL(drm_sysfs_hotplug_event);
 330
 331static void drm_sysfs_release(struct device *dev)
 332{
 333        kfree(dev);
 334}
 335
 336struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
 337{
 338        const char *minor_str;
 339        struct device *kdev;
 340        int r;
 341
 342        if (minor->type == DRM_MINOR_RENDER)
 343                minor_str = "renderD%d";
 344        else
 345                minor_str = "card%d";
 346
 347        kdev = kzalloc(sizeof(*kdev), GFP_KERNEL);
 348        if (!kdev)
 349                return ERR_PTR(-ENOMEM);
 350
 351        device_initialize(kdev);
 352        kdev->devt = MKDEV(DRM_MAJOR, minor->index);
 353        kdev->class = drm_class;
 354        kdev->type = &drm_sysfs_device_minor;
 355        kdev->parent = minor->dev->dev;
 356        kdev->release = drm_sysfs_release;
 357        dev_set_drvdata(kdev, minor);
 358
 359        r = dev_set_name(kdev, minor_str, minor->index);
 360        if (r < 0)
 361                goto err_free;
 362
 363        return kdev;
 364
 365err_free:
 366        put_device(kdev);
 367        return ERR_PTR(r);
 368}
 369
 370/**
 371 * drm_class_device_register - register new device with the DRM sysfs class
 372 * @dev: device to register
 373 *
 374 * Registers a new &struct device within the DRM sysfs class. Essentially only
 375 * used by ttm to have a place for its global settings. Drivers should never use
 376 * this.
 377 */
 378int drm_class_device_register(struct device *dev)
 379{
 380        if (!drm_class || IS_ERR(drm_class))
 381                return -ENOENT;
 382
 383        dev->class = drm_class;
 384        return device_register(dev);
 385}
 386EXPORT_SYMBOL_GPL(drm_class_device_register);
 387
 388/**
 389 * drm_class_device_unregister - unregister device with the DRM sysfs class
 390 * @dev: device to unregister
 391 *
 392 * Unregisters a &struct device from the DRM sysfs class. Essentially only used
 393 * by ttm to have a place for its global settings. Drivers should never use
 394 * this.
 395 */
 396void drm_class_device_unregister(struct device *dev)
 397{
 398        return device_unregister(dev);
 399}
 400EXPORT_SYMBOL_GPL(drm_class_device_unregister);
 401