root/drivers/gpu/drm/drm_sysfs.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. drm_devnode
  2. drm_sysfs_init
  3. drm_sysfs_destroy
  4. status_store
  5. status_show
  6. dpms_show
  7. enabled_show
  8. edid_show
  9. modes_show
  10. drm_sysfs_connector_add
  11. drm_sysfs_connector_remove
  12. drm_sysfs_lease_event
  13. drm_sysfs_hotplug_event
  14. drm_sysfs_connector_status_event
  15. drm_sysfs_release
  16. drm_sysfs_minor_alloc
  17. drm_class_device_register
  18. drm_class_device_unregister

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

/* [<][>][^][v][top][bottom][index][help] */