Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

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