LCOV - code coverage report
Current view: top level - drivers/gpu/drm - drm_sysfs.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 27 164 16.5 %
Date: 2023-03-27 20:00:47 Functions: 4 19 21.1 %

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

Generated by: LCOV version 1.14