xref: /openbmc/linux/drivers/gpu/drm/drm_sysfs.c (revision 0cf8d292)
155716d26SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c0e09200SDave Airlie 
3c0e09200SDave Airlie /*
4c0e09200SDave Airlie  * drm_sysfs.c - Modifications to drm_sysfs_class.c to support
5c0e09200SDave Airlie  *               extra sysfs attribute from DRM. Normal drm_sysfs_class
6c0e09200SDave Airlie  *               does not allow adding attributes.
7c0e09200SDave Airlie  *
8c0e09200SDave Airlie  * Copyright (c) 2004 Jon Smirl <jonsmirl@gmail.com>
9c0e09200SDave Airlie  * Copyright (c) 2003-2004 Greg Kroah-Hartman <greg@kroah.com>
10c0e09200SDave Airlie  * Copyright (c) 2003-2004 IBM Corp.
11c0e09200SDave Airlie  */
12c0e09200SDave Airlie 
1348c429c6SHans de Goede #include <linux/acpi.h>
14c5c51b24SWon Chung #include <linux/component.h>
15c0e09200SDave Airlie #include <linux/device.h>
16c0e09200SDave Airlie #include <linux/err.h>
172d1a8a48SPaul Gortmaker #include <linux/export.h>
180500c04eSSam Ravnborg #include <linux/gfp.h>
19e1a29c6cSAndrzej Pietrasiewicz #include <linux/i2c.h>
200500c04eSSam Ravnborg #include <linux/kdev_t.h>
21c5c51b24SWon Chung #include <linux/property.h>
220500c04eSSam Ravnborg #include <linux/slab.h>
23c0e09200SDave Airlie 
247428ff70SOded Gabbay #include <drm/drm_accel.h>
250500c04eSSam Ravnborg #include <drm/drm_connector.h>
260500c04eSSam Ravnborg #include <drm/drm_device.h>
270500c04eSSam Ravnborg #include <drm/drm_file.h>
280500c04eSSam Ravnborg #include <drm/drm_modes.h>
290500c04eSSam Ravnborg #include <drm/drm_print.h>
300500c04eSSam Ravnborg #include <drm/drm_property.h>
31760285e7SDavid Howells #include <drm/drm_sysfs.h>
320500c04eSSam Ravnborg 
3367d0ec4eSDaniel Vetter #include "drm_internal.h"
346fe2ce06SRamalingam C #include "drm_crtc_internal.h"
35c0e09200SDave Airlie 
365bdebb18SDave Airlie #define to_drm_minor(d) dev_get_drvdata(d)
375bdebb18SDave Airlie #define to_drm_connector(d) dev_get_drvdata(d)
38c0e09200SDave Airlie 
39e2271704SDaniel Vetter /**
40e2271704SDaniel Vetter  * DOC: overview
41e2271704SDaniel Vetter  *
42e2271704SDaniel Vetter  * DRM provides very little additional support to drivers for sysfs
43e2271704SDaniel Vetter  * interactions, beyond just all the standard stuff. Drivers who want to expose
44e2271704SDaniel Vetter  * additional sysfs properties and property groups can attach them at either
45e2271704SDaniel Vetter  * &drm_device.dev or &drm_connector.kdev.
46e2271704SDaniel Vetter  *
47e2271704SDaniel Vetter  * Registration is automatically handled when calling drm_dev_register(), or
48e2271704SDaniel Vetter  * drm_connector_register() in case of hot-plugged connectors. Unregistration is
49e2271704SDaniel Vetter  * also automatically handled by drm_dev_unregister() and
50e2271704SDaniel Vetter  * drm_connector_unregister().
51e2271704SDaniel Vetter  */
52e2271704SDaniel Vetter 
5308e4d534SThomas Hellstrom static struct device_type drm_sysfs_device_minor = {
5408e4d534SThomas Hellstrom 	.name = "drm_minor"
5508e4d534SThomas Hellstrom };
5608e4d534SThomas Hellstrom 
57331de7dbSHans de Goede static struct device_type drm_sysfs_device_connector = {
58331de7dbSHans de Goede 	.name = "drm_connector",
59331de7dbSHans de Goede };
60331de7dbSHans de Goede 
61fcc90213SDavid Herrmann struct class *drm_class;
62fcc90213SDavid Herrmann 
6348c429c6SHans de Goede #ifdef CONFIG_ACPI
drm_connector_acpi_bus_match(struct device * dev)6448c429c6SHans de Goede static bool drm_connector_acpi_bus_match(struct device *dev)
6548c429c6SHans de Goede {
6648c429c6SHans de Goede 	return dev->type == &drm_sysfs_device_connector;
6748c429c6SHans de Goede }
6848c429c6SHans de Goede 
drm_connector_acpi_find_companion(struct device * dev)6948c429c6SHans de Goede static struct acpi_device *drm_connector_acpi_find_companion(struct device *dev)
7048c429c6SHans de Goede {
7148c429c6SHans de Goede 	struct drm_connector *connector = to_drm_connector(dev);
7248c429c6SHans de Goede 
7348c429c6SHans de Goede 	return to_acpi_device_node(connector->fwnode);
7448c429c6SHans de Goede }
7548c429c6SHans de Goede 
7648c429c6SHans de Goede static struct acpi_bus_type drm_connector_acpi_bus = {
7748c429c6SHans de Goede 	.name = "drm_connector",
7848c429c6SHans de Goede 	.match = drm_connector_acpi_bus_match,
7948c429c6SHans de Goede 	.find_companion = drm_connector_acpi_find_companion,
8048c429c6SHans de Goede };
8148c429c6SHans de Goede 
drm_sysfs_acpi_register(void)8248c429c6SHans de Goede static void drm_sysfs_acpi_register(void)
8348c429c6SHans de Goede {
8448c429c6SHans de Goede 	register_acpi_bus_type(&drm_connector_acpi_bus);
8548c429c6SHans de Goede }
8648c429c6SHans de Goede 
drm_sysfs_acpi_unregister(void)8748c429c6SHans de Goede static void drm_sysfs_acpi_unregister(void)
8848c429c6SHans de Goede {
8948c429c6SHans de Goede 	unregister_acpi_bus_type(&drm_connector_acpi_bus);
9048c429c6SHans de Goede }
9148c429c6SHans de Goede #else
drm_sysfs_acpi_register(void)9248c429c6SHans de Goede static void drm_sysfs_acpi_register(void) { }
drm_sysfs_acpi_unregister(void)9348c429c6SHans de Goede static void drm_sysfs_acpi_unregister(void) { }
9448c429c6SHans de Goede #endif
9548c429c6SHans de Goede 
drm_devnode(const struct device * dev,umode_t * mode)96ff62b8e6SGreg Kroah-Hartman static char *drm_devnode(const struct device *dev, umode_t *mode)
9702200d06SKay Sievers {
9802200d06SKay Sievers 	return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
9902200d06SKay Sievers }
10002200d06SKay Sievers 
typec_connector_bind(struct device * dev,struct device * typec_connector,void * data)101c5c51b24SWon Chung static int typec_connector_bind(struct device *dev,
102c5c51b24SWon Chung 				struct device *typec_connector, void *data)
103c5c51b24SWon Chung {
104c5c51b24SWon Chung 	int ret;
105c5c51b24SWon Chung 
106c5c51b24SWon Chung 	ret = sysfs_create_link(&dev->kobj, &typec_connector->kobj, "typec_connector");
107c5c51b24SWon Chung 	if (ret)
108c5c51b24SWon Chung 		return ret;
109c5c51b24SWon Chung 
110c5c51b24SWon Chung 	ret = sysfs_create_link(&typec_connector->kobj, &dev->kobj, "drm_connector");
111c5c51b24SWon Chung 	if (ret)
112c5c51b24SWon Chung 		sysfs_remove_link(&dev->kobj, "typec_connector");
113c5c51b24SWon Chung 
114c5c51b24SWon Chung 	return ret;
115c5c51b24SWon Chung }
116c5c51b24SWon Chung 
typec_connector_unbind(struct device * dev,struct device * typec_connector,void * data)117c5c51b24SWon Chung static void typec_connector_unbind(struct device *dev,
118c5c51b24SWon Chung 				   struct device *typec_connector, void *data)
119c5c51b24SWon Chung {
120c5c51b24SWon Chung 	sysfs_remove_link(&typec_connector->kobj, "drm_connector");
121c5c51b24SWon Chung 	sysfs_remove_link(&dev->kobj, "typec_connector");
122c5c51b24SWon Chung }
123c5c51b24SWon Chung 
124c5c51b24SWon Chung static const struct component_ops typec_connector_ops = {
125c5c51b24SWon Chung 	.bind = typec_connector_bind,
126c5c51b24SWon Chung 	.unbind = typec_connector_unbind,
127c5c51b24SWon Chung };
128c5c51b24SWon Chung 
12982d5e73fSDavid Herrmann static CLASS_ATTR_STRING(version, S_IRUGO, "drm 1.1.0 20060810");
130c0e09200SDave Airlie 
131c0e09200SDave Airlie /**
132fcc90213SDavid Herrmann  * drm_sysfs_init - initialize sysfs helpers
133c0e09200SDave Airlie  *
134fcc90213SDavid Herrmann  * This is used to create the DRM class, which is the implicit parent of any
135fcc90213SDavid Herrmann  * other top-level DRM sysfs objects.
136c0e09200SDave Airlie  *
137fcc90213SDavid Herrmann  * You must call drm_sysfs_destroy() to release the allocated resources.
138fcc90213SDavid Herrmann  *
139fcc90213SDavid Herrmann  * Return: 0 on success, negative error code on failure.
140c0e09200SDave Airlie  */
drm_sysfs_init(void)141fcc90213SDavid Herrmann int drm_sysfs_init(void)
142c0e09200SDave Airlie {
143c0e09200SDave Airlie 	int err;
144c0e09200SDave Airlie 
1451aaba11dSGreg Kroah-Hartman 	drm_class = class_create("drm");
146fcc90213SDavid Herrmann 	if (IS_ERR(drm_class))
147fcc90213SDavid Herrmann 		return PTR_ERR(drm_class);
148fcc90213SDavid Herrmann 
149fcc90213SDavid Herrmann 	err = class_create_file(drm_class, &class_attr_version.attr);
150fcc90213SDavid Herrmann 	if (err) {
151fcc90213SDavid Herrmann 		class_destroy(drm_class);
152fcc90213SDavid Herrmann 		drm_class = NULL;
153fcc90213SDavid Herrmann 		return err;
154c0e09200SDave Airlie 	}
155c0e09200SDave Airlie 
156fcc90213SDavid Herrmann 	drm_class->devnode = drm_devnode;
15748c429c6SHans de Goede 
15848c429c6SHans de Goede 	drm_sysfs_acpi_register();
159fcc90213SDavid Herrmann 	return 0;
160c0e09200SDave Airlie }
161c0e09200SDave Airlie 
162c0e09200SDave Airlie /**
163c0e09200SDave Airlie  * drm_sysfs_destroy - destroys DRM class
164c0e09200SDave Airlie  *
165c0e09200SDave Airlie  * Destroy the DRM device class.
166c0e09200SDave Airlie  */
drm_sysfs_destroy(void)167c0e09200SDave Airlie void drm_sysfs_destroy(void)
168c0e09200SDave Airlie {
16926b91ae4SDavid Herrmann 	if (IS_ERR_OR_NULL(drm_class))
170c0e09200SDave Airlie 		return;
17148c429c6SHans de Goede 	drm_sysfs_acpi_unregister();
1720933e2d9SAndi Kleen 	class_remove_file(drm_class, &class_attr_version.attr);
173c0e09200SDave Airlie 	class_destroy(drm_class);
17449099c49SDave Airlie 	drm_class = NULL;
175c0e09200SDave Airlie }
176c0e09200SDave Airlie 
drm_sysfs_release(struct device * dev)177331de7dbSHans de Goede static void drm_sysfs_release(struct device *dev)
178331de7dbSHans de Goede {
179331de7dbSHans de Goede 	kfree(dev);
180331de7dbSHans de Goede }
181331de7dbSHans de Goede 
182f453ba04SDave Airlie /*
183f453ba04SDave Airlie  * Connector properties
184f453ba04SDave Airlie  */
status_store(struct device * device,struct device_attribute * attr,const char * buf,size_t count)185c484f02dSChris Wilson static ssize_t status_store(struct device *device,
186c484f02dSChris Wilson 			   struct device_attribute *attr,
187c484f02dSChris Wilson 			   const char *buf, size_t count)
188c484f02dSChris Wilson {
189c484f02dSChris Wilson 	struct drm_connector *connector = to_drm_connector(device);
190c484f02dSChris Wilson 	struct drm_device *dev = connector->dev;
191ed293f77SDaniel Vetter 	enum drm_connector_force old_force;
192c484f02dSChris Wilson 	int ret;
193c484f02dSChris Wilson 
194c484f02dSChris Wilson 	ret = mutex_lock_interruptible(&dev->mode_config.mutex);
195c484f02dSChris Wilson 	if (ret)
196c484f02dSChris Wilson 		return ret;
197c484f02dSChris Wilson 
198ed293f77SDaniel Vetter 	old_force = connector->force;
199c484f02dSChris Wilson 
200ed293f77SDaniel Vetter 	if (sysfs_streq(buf, "detect"))
201c484f02dSChris Wilson 		connector->force = 0;
202ed293f77SDaniel Vetter 	else if (sysfs_streq(buf, "on"))
203c484f02dSChris Wilson 		connector->force = DRM_FORCE_ON;
204ed293f77SDaniel Vetter 	else if (sysfs_streq(buf, "on-digital"))
205c484f02dSChris Wilson 		connector->force = DRM_FORCE_ON_DIGITAL;
206ed293f77SDaniel Vetter 	else if (sysfs_streq(buf, "off"))
207c484f02dSChris Wilson 		connector->force = DRM_FORCE_OFF;
208ed293f77SDaniel Vetter 	else
209c484f02dSChris Wilson 		ret = -EINVAL;
210c484f02dSChris Wilson 
211ed293f77SDaniel Vetter 	if (old_force != connector->force || !connector->force) {
212ed293f77SDaniel Vetter 		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force updated from %d to %d or reprobing\n",
213c484f02dSChris Wilson 			      connector->base.id,
214c484f02dSChris Wilson 			      connector->name,
215ed293f77SDaniel Vetter 			      old_force, connector->force);
216c484f02dSChris Wilson 
217ed293f77SDaniel Vetter 		connector->funcs->fill_modes(connector,
218ed293f77SDaniel Vetter 					     dev->mode_config.max_width,
219ed293f77SDaniel Vetter 					     dev->mode_config.max_height);
220c484f02dSChris Wilson 	}
221c484f02dSChris Wilson 
222c484f02dSChris Wilson 	mutex_unlock(&dev->mode_config.mutex);
223c484f02dSChris Wilson 
22438d8571dSRussell King 	return ret ? ret : count;
225c484f02dSChris Wilson }
226c484f02dSChris Wilson 
status_show(struct device * device,struct device_attribute * attr,char * buf)227f453ba04SDave Airlie static ssize_t status_show(struct device *device,
228f453ba04SDave Airlie 			   struct device_attribute *attr,
229f453ba04SDave Airlie 			   char *buf)
230f453ba04SDave Airlie {
231f453ba04SDave Airlie 	struct drm_connector *connector = to_drm_connector(device);
2324eb9b945SDaniel Vetter 	enum drm_connector_status status;
2334eb9b945SDaniel Vetter 
2344eb9b945SDaniel Vetter 	status = READ_ONCE(connector->status);
235007c80a5SChris Wilson 
236835bc483STian Tao 	return sysfs_emit(buf, "%s\n",
2374eb9b945SDaniel Vetter 			  drm_get_connector_status_name(status));
238f453ba04SDave Airlie }
239f453ba04SDave Airlie 
dpms_show(struct device * device,struct device_attribute * attr,char * buf)240f453ba04SDave Airlie static ssize_t dpms_show(struct device *device,
241f453ba04SDave Airlie 			   struct device_attribute *attr,
242f453ba04SDave Airlie 			   char *buf)
243f453ba04SDave Airlie {
244f453ba04SDave Airlie 	struct drm_connector *connector = to_drm_connector(device);
245621bd0f6SDaniel Vetter 	int dpms;
246f453ba04SDave Airlie 
247621bd0f6SDaniel Vetter 	dpms = READ_ONCE(connector->dpms);
248f453ba04SDave Airlie 
249835bc483STian Tao 	return sysfs_emit(buf, "%s\n", drm_get_dpms_name(dpms));
250f453ba04SDave Airlie }
251f453ba04SDave Airlie 
enabled_show(struct device * device,struct device_attribute * attr,char * buf)252f453ba04SDave Airlie static ssize_t enabled_show(struct device *device,
253f453ba04SDave Airlie 			    struct device_attribute *attr,
254f453ba04SDave Airlie 			   char *buf)
255f453ba04SDave Airlie {
256f453ba04SDave Airlie 	struct drm_connector *connector = to_drm_connector(device);
2574eb9b945SDaniel Vetter 	bool enabled;
258f453ba04SDave Airlie 
2594eb9b945SDaniel Vetter 	enabled = READ_ONCE(connector->encoder);
2604eb9b945SDaniel Vetter 
261835bc483STian Tao 	return sysfs_emit(buf, enabled ? "enabled\n" : "disabled\n");
262f453ba04SDave Airlie }
263f453ba04SDave Airlie 
edid_show(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)2642c3c8beaSChris Wright static ssize_t edid_show(struct file *filp, struct kobject *kobj,
2652c3c8beaSChris Wright 			 struct bin_attribute *attr, char *buf, loff_t off,
2662c3c8beaSChris Wright 			 size_t count)
267f453ba04SDave Airlie {
268d122cbf1SGeliang Tang 	struct device *connector_dev = kobj_to_dev(kobj);
269f453ba04SDave Airlie 	struct drm_connector *connector = to_drm_connector(connector_dev);
270f453ba04SDave Airlie 	unsigned char *edid;
271f453ba04SDave Airlie 	size_t size;
272a48a62bcSDaniel Vetter 	ssize_t ret = 0;
273f453ba04SDave Airlie 
274a48a62bcSDaniel Vetter 	mutex_lock(&connector->dev->mode_config.mutex);
275f453ba04SDave Airlie 	if (!connector->edid_blob_ptr)
276a48a62bcSDaniel Vetter 		goto unlock;
277f453ba04SDave Airlie 
278f453ba04SDave Airlie 	edid = connector->edid_blob_ptr->data;
279f453ba04SDave Airlie 	size = connector->edid_blob_ptr->length;
280f453ba04SDave Airlie 	if (!edid)
281a48a62bcSDaniel Vetter 		goto unlock;
282f453ba04SDave Airlie 
283f453ba04SDave Airlie 	if (off >= size)
284a48a62bcSDaniel Vetter 		goto unlock;
285f453ba04SDave Airlie 
286f453ba04SDave Airlie 	if (off + count > size)
287f453ba04SDave Airlie 		count = size - off;
288f453ba04SDave Airlie 	memcpy(buf, edid + off, count);
289f453ba04SDave Airlie 
290a48a62bcSDaniel Vetter 	ret = count;
291a48a62bcSDaniel Vetter unlock:
292a48a62bcSDaniel Vetter 	mutex_unlock(&connector->dev->mode_config.mutex);
293a48a62bcSDaniel Vetter 
294a48a62bcSDaniel Vetter 	return ret;
295f453ba04SDave Airlie }
296f453ba04SDave Airlie 
modes_show(struct device * device,struct device_attribute * attr,char * buf)297f453ba04SDave Airlie static ssize_t modes_show(struct device *device,
298f453ba04SDave Airlie 			   struct device_attribute *attr,
299f453ba04SDave Airlie 			   char *buf)
300f453ba04SDave Airlie {
301f453ba04SDave Airlie 	struct drm_connector *connector = to_drm_connector(device);
302f453ba04SDave Airlie 	struct drm_display_mode *mode;
303f453ba04SDave Airlie 	int written = 0;
304f453ba04SDave Airlie 
305a48a62bcSDaniel Vetter 	mutex_lock(&connector->dev->mode_config.mutex);
306f453ba04SDave Airlie 	list_for_each_entry(mode, &connector->modes, head) {
3079b9f2219STakashi Iwai 		written += scnprintf(buf + written, PAGE_SIZE - written, "%s\n",
308f453ba04SDave Airlie 				    mode->name);
309f453ba04SDave Airlie 	}
310a48a62bcSDaniel Vetter 	mutex_unlock(&connector->dev->mode_config.mutex);
311f453ba04SDave Airlie 
312f453ba04SDave Airlie 	return written;
313f453ba04SDave Airlie }
314f453ba04SDave Airlie 
connector_id_show(struct device * device,struct device_attribute * attr,char * buf)315f0038cffSWon Chung static ssize_t connector_id_show(struct device *device,
316f0038cffSWon Chung 				 struct device_attribute *attr,
317f0038cffSWon Chung 				 char *buf)
318f0038cffSWon Chung {
319f0038cffSWon Chung 	struct drm_connector *connector = to_drm_connector(device);
320f0038cffSWon Chung 
321f0038cffSWon Chung 	return sysfs_emit(buf, "%d\n", connector->base.id);
322f0038cffSWon Chung }
323f0038cffSWon Chung 
324c484f02dSChris Wilson static DEVICE_ATTR_RW(status);
325335f1a62STakashi Iwai static DEVICE_ATTR_RO(enabled);
326335f1a62STakashi Iwai static DEVICE_ATTR_RO(dpms);
327335f1a62STakashi Iwai static DEVICE_ATTR_RO(modes);
328f0038cffSWon Chung static DEVICE_ATTR_RO(connector_id);
329335f1a62STakashi Iwai 
330335f1a62STakashi Iwai static struct attribute *connector_dev_attrs[] = {
331335f1a62STakashi Iwai 	&dev_attr_status.attr,
332335f1a62STakashi Iwai 	&dev_attr_enabled.attr,
333335f1a62STakashi Iwai 	&dev_attr_dpms.attr,
334335f1a62STakashi Iwai 	&dev_attr_modes.attr,
335f0038cffSWon Chung 	&dev_attr_connector_id.attr,
336335f1a62STakashi Iwai 	NULL
337f453ba04SDave Airlie };
338f453ba04SDave Airlie 
339f453ba04SDave Airlie static struct bin_attribute edid_attr = {
340f453ba04SDave Airlie 	.attr.name = "edid",
341e36ebaf4SKeith Packard 	.attr.mode = 0444,
3427466f4ccSAdam Jackson 	.size = 0,
343f453ba04SDave Airlie 	.read = edid_show,
344f453ba04SDave Airlie };
345f453ba04SDave Airlie 
346335f1a62STakashi Iwai static struct bin_attribute *connector_bin_attrs[] = {
347335f1a62STakashi Iwai 	&edid_attr,
348335f1a62STakashi Iwai 	NULL
349335f1a62STakashi Iwai };
350335f1a62STakashi Iwai 
351335f1a62STakashi Iwai static const struct attribute_group connector_dev_group = {
352335f1a62STakashi Iwai 	.attrs = connector_dev_attrs,
353335f1a62STakashi Iwai 	.bin_attrs = connector_bin_attrs,
354335f1a62STakashi Iwai };
355335f1a62STakashi Iwai 
356335f1a62STakashi Iwai static const struct attribute_group *connector_dev_groups[] = {
357335f1a62STakashi Iwai 	&connector_dev_group,
358335f1a62STakashi Iwai 	NULL
359335f1a62STakashi Iwai };
360335f1a62STakashi Iwai 
drm_sysfs_connector_add(struct drm_connector * connector)361f453ba04SDave Airlie int drm_sysfs_connector_add(struct drm_connector *connector)
362f453ba04SDave Airlie {
363f453ba04SDave Airlie 	struct drm_device *dev = connector->dev;
364331de7dbSHans de Goede 	struct device *kdev;
365331de7dbSHans de Goede 	int r;
366f453ba04SDave Airlie 
3675bdebb18SDave Airlie 	if (connector->kdev)
3685bdebb18SDave Airlie 		return 0;
3695bdebb18SDave Airlie 
370331de7dbSHans de Goede 	kdev = kzalloc(sizeof(*kdev), GFP_KERNEL);
371331de7dbSHans de Goede 	if (!kdev)
372331de7dbSHans de Goede 		return -ENOMEM;
373331de7dbSHans de Goede 
374331de7dbSHans de Goede 	device_initialize(kdev);
375331de7dbSHans de Goede 	kdev->class = drm_class;
376331de7dbSHans de Goede 	kdev->type = &drm_sysfs_device_connector;
377331de7dbSHans de Goede 	kdev->parent = dev->primary->kdev;
378331de7dbSHans de Goede 	kdev->groups = connector_dev_groups;
379331de7dbSHans de Goede 	kdev->release = drm_sysfs_release;
380331de7dbSHans de Goede 	dev_set_drvdata(kdev, connector);
381331de7dbSHans de Goede 
382331de7dbSHans de Goede 	r = dev_set_name(kdev, "card%d-%s", dev->primary->index, connector->name);
383331de7dbSHans de Goede 	if (r)
384331de7dbSHans de Goede 		goto err_free;
385331de7dbSHans de Goede 
386f453ba04SDave Airlie 	DRM_DEBUG("adding \"%s\" to sysfs\n",
38725933820SJani Nikula 		  connector->name);
388f453ba04SDave Airlie 
389331de7dbSHans de Goede 	r = device_add(kdev);
390331de7dbSHans de Goede 	if (r) {
391331de7dbSHans de Goede 		drm_err(dev, "failed to register connector device: %d\n", r);
392331de7dbSHans de Goede 		goto err_free;
393f453ba04SDave Airlie 	}
394f453ba04SDave Airlie 
395331de7dbSHans de Goede 	connector->kdev = kdev;
396331de7dbSHans de Goede 
397c5c51b24SWon Chung 	if (dev_fwnode(kdev)) {
398c5c51b24SWon Chung 		r = component_add(kdev, &typec_connector_ops);
399c5c51b24SWon Chung 		if (r)
400c5c51b24SWon Chung 			drm_err(dev, "failed to add component to create link to typec connector\n");
401c5c51b24SWon Chung 	}
402c5c51b24SWon Chung 
403e1a29c6cSAndrzej Pietrasiewicz 	if (connector->ddc)
404e1a29c6cSAndrzej Pietrasiewicz 		return sysfs_create_link(&connector->kdev->kobj,
405e1a29c6cSAndrzej Pietrasiewicz 				 &connector->ddc->dev.kobj, "ddc");
406c5c51b24SWon Chung 
407f453ba04SDave Airlie 	return 0;
408331de7dbSHans de Goede 
409331de7dbSHans de Goede err_free:
410331de7dbSHans de Goede 	put_device(kdev);
411331de7dbSHans de Goede 	return r;
412f453ba04SDave Airlie }
413f453ba04SDave Airlie 
drm_sysfs_connector_remove(struct drm_connector * connector)414f453ba04SDave Airlie void drm_sysfs_connector_remove(struct drm_connector *connector)
415f453ba04SDave Airlie {
4165bdebb18SDave Airlie 	if (!connector->kdev)
4171828fe6cSDave Airlie 		return;
418e1a29c6cSAndrzej Pietrasiewicz 
419e1a29c6cSAndrzej Pietrasiewicz 	if (connector->ddc)
420e1a29c6cSAndrzej Pietrasiewicz 		sysfs_remove_link(&connector->kdev->kobj, "ddc");
421e1a29c6cSAndrzej Pietrasiewicz 
422c5c51b24SWon Chung 	if (dev_fwnode(connector->kdev))
423c5c51b24SWon Chung 		component_del(connector->kdev, &typec_connector_ops);
424c5c51b24SWon Chung 
425f453ba04SDave Airlie 	DRM_DEBUG("removing \"%s\" from sysfs\n",
42625933820SJani Nikula 		  connector->name);
427f453ba04SDave Airlie 
4285bdebb18SDave Airlie 	device_unregister(connector->kdev);
4295bdebb18SDave Airlie 	connector->kdev = NULL;
430f453ba04SDave Airlie }
431f453ba04SDave Airlie 
drm_sysfs_lease_event(struct drm_device * dev)432ce858828SDaniel Vetter void drm_sysfs_lease_event(struct drm_device *dev)
433ce858828SDaniel Vetter {
434ce858828SDaniel Vetter 	char *event_string = "LEASE=1";
435ce858828SDaniel Vetter 	char *envp[] = { event_string, NULL };
436ce858828SDaniel Vetter 
437ce858828SDaniel Vetter 	DRM_DEBUG("generating lease event\n");
438ce858828SDaniel Vetter 
439ce858828SDaniel Vetter 	kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
440ce858828SDaniel Vetter }
441ce858828SDaniel Vetter 
442f453ba04SDave Airlie /**
443f453ba04SDave Airlie  * drm_sysfs_hotplug_event - generate a DRM uevent
444f453ba04SDave Airlie  * @dev: DRM device
445f453ba04SDave Airlie  *
446f453ba04SDave Airlie  * Send a uevent for the DRM device specified by @dev.  Currently we only
447f453ba04SDave Airlie  * set HOTPLUG=1 in the uevent environment, but this could be expanded to
448f453ba04SDave Airlie  * deal with other types of events.
4496fe2ce06SRamalingam C  *
4506fe2ce06SRamalingam C  * Any new uapi should be using the drm_sysfs_connector_status_event()
4516fe2ce06SRamalingam C  * for uevents on connector status change.
452f453ba04SDave Airlie  */
drm_sysfs_hotplug_event(struct drm_device * dev)453f453ba04SDave Airlie void drm_sysfs_hotplug_event(struct drm_device *dev)
454f453ba04SDave Airlie {
455f453ba04SDave Airlie 	char *event_string = "HOTPLUG=1";
456f453ba04SDave Airlie 	char *envp[] = { event_string, NULL };
457f453ba04SDave Airlie 
458f453ba04SDave Airlie 	DRM_DEBUG("generating hotplug event\n");
459f453ba04SDave Airlie 
4605bdebb18SDave Airlie 	kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
461f453ba04SDave Airlie }
4625ca58282SJesse Barnes EXPORT_SYMBOL(drm_sysfs_hotplug_event);
463f453ba04SDave Airlie 
4646fe2ce06SRamalingam C /**
4650d6a8c5eSSimon Ser  * drm_sysfs_connector_hotplug_event - generate a DRM uevent for any connector
4660d6a8c5eSSimon Ser  * change
4670d6a8c5eSSimon Ser  * @connector: connector which has changed
4680d6a8c5eSSimon Ser  *
4690d6a8c5eSSimon Ser  * Send a uevent for the DRM connector specified by @connector. This will send
4700d6a8c5eSSimon Ser  * a uevent with the properties HOTPLUG=1 and CONNECTOR.
4710d6a8c5eSSimon Ser  */
drm_sysfs_connector_hotplug_event(struct drm_connector * connector)4720d6a8c5eSSimon Ser void drm_sysfs_connector_hotplug_event(struct drm_connector *connector)
4730d6a8c5eSSimon Ser {
4740d6a8c5eSSimon Ser 	struct drm_device *dev = connector->dev;
4750d6a8c5eSSimon Ser 	char hotplug_str[] = "HOTPLUG=1", conn_id[21];
4760d6a8c5eSSimon Ser 	char *envp[] = { hotplug_str, conn_id, NULL };
4770d6a8c5eSSimon Ser 
4780d6a8c5eSSimon Ser 	snprintf(conn_id, sizeof(conn_id),
4790d6a8c5eSSimon Ser 		 "CONNECTOR=%u", connector->base.id);
4800d6a8c5eSSimon Ser 
4810d6a8c5eSSimon Ser 	drm_dbg_kms(connector->dev,
4820d6a8c5eSSimon Ser 		    "[CONNECTOR:%d:%s] generating connector hotplug event\n",
4830d6a8c5eSSimon Ser 		    connector->base.id, connector->name);
4840d6a8c5eSSimon Ser 
4850d6a8c5eSSimon Ser 	kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
4860d6a8c5eSSimon Ser }
4870d6a8c5eSSimon Ser EXPORT_SYMBOL(drm_sysfs_connector_hotplug_event);
4880d6a8c5eSSimon Ser 
4890d6a8c5eSSimon Ser /**
490*0cf8d292SSimon Ser  * drm_sysfs_connector_property_event - generate a DRM uevent for connector
491*0cf8d292SSimon Ser  * property change
492*0cf8d292SSimon Ser  * @connector: connector on which property changed
493*0cf8d292SSimon Ser  * @property: connector property which has changed.
4946fe2ce06SRamalingam C  *
495*0cf8d292SSimon Ser  * Send a uevent for the specified DRM connector and property.  Currently we
4966fe2ce06SRamalingam C  * set HOTPLUG=1 and connector id along with the attached property id
497*0cf8d292SSimon Ser  * related to the change.
4986fe2ce06SRamalingam C  */
drm_sysfs_connector_property_event(struct drm_connector * connector,struct drm_property * property)499*0cf8d292SSimon Ser void drm_sysfs_connector_property_event(struct drm_connector *connector,
5006fe2ce06SRamalingam C 					struct drm_property *property)
5016fe2ce06SRamalingam C {
5026fe2ce06SRamalingam C 	struct drm_device *dev = connector->dev;
5036fe2ce06SRamalingam C 	char hotplug_str[] = "HOTPLUG=1", conn_id[21], prop_id[21];
5046fe2ce06SRamalingam C 	char *envp[4] = { hotplug_str, conn_id, prop_id, NULL };
5056fe2ce06SRamalingam C 
5066fe2ce06SRamalingam C 	WARN_ON(!drm_mode_obj_find_prop_id(&connector->base,
5076fe2ce06SRamalingam C 					   property->base.id));
5086fe2ce06SRamalingam C 
5096fe2ce06SRamalingam C 	snprintf(conn_id, ARRAY_SIZE(conn_id),
5106fe2ce06SRamalingam C 		 "CONNECTOR=%u", connector->base.id);
5116fe2ce06SRamalingam C 	snprintf(prop_id, ARRAY_SIZE(prop_id),
5126fe2ce06SRamalingam C 		 "PROPERTY=%u", property->base.id);
5136fe2ce06SRamalingam C 
514*0cf8d292SSimon Ser 	drm_dbg_kms(connector->dev,
515*0cf8d292SSimon Ser 		    "[CONNECTOR:%d:%s] generating connector property event for [PROP:%d:%s]\n",
516*0cf8d292SSimon Ser 		    connector->base.id, connector->name,
517*0cf8d292SSimon Ser 		    property->base.id, property->name);
5186fe2ce06SRamalingam C 
5196fe2ce06SRamalingam C 	kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
5206fe2ce06SRamalingam C }
521*0cf8d292SSimon Ser EXPORT_SYMBOL(drm_sysfs_connector_property_event);
5226fe2ce06SRamalingam C 
drm_sysfs_minor_alloc(struct drm_minor * minor)523e1728075SDavid Herrmann struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
524c0e09200SDave Airlie {
525e1728075SDavid Herrmann 	const char *minor_str;
526e1728075SDavid Herrmann 	struct device *kdev;
527760c960bSDavid Herrmann 	int r;
528c0e09200SDave Airlie 
529e1728075SDavid Herrmann 	kdev = kzalloc(sizeof(*kdev), GFP_KERNEL);
530e1728075SDavid Herrmann 	if (!kdev)
531e1728075SDavid Herrmann 		return ERR_PTR(-ENOMEM);
532760c960bSDavid Herrmann 
533e1728075SDavid Herrmann 	device_initialize(kdev);
5347428ff70SOded Gabbay 
5357428ff70SOded Gabbay 	if (minor->type == DRM_MINOR_ACCEL) {
5367428ff70SOded Gabbay 		minor_str = "accel%d";
5377428ff70SOded Gabbay 		accel_set_device_instance_params(kdev, minor->index);
5387428ff70SOded Gabbay 	} else {
5397428ff70SOded Gabbay 		if (minor->type == DRM_MINOR_RENDER)
5407428ff70SOded Gabbay 			minor_str = "renderD%d";
5417428ff70SOded Gabbay 		else
5427428ff70SOded Gabbay 			minor_str = "card%d";
5437428ff70SOded Gabbay 
544e1728075SDavid Herrmann 		kdev->devt = MKDEV(DRM_MAJOR, minor->index);
545e1728075SDavid Herrmann 		kdev->class = drm_class;
546e1728075SDavid Herrmann 		kdev->type = &drm_sysfs_device_minor;
5477428ff70SOded Gabbay 	}
5487428ff70SOded Gabbay 
549e1728075SDavid Herrmann 	kdev->parent = minor->dev->dev;
550e1728075SDavid Herrmann 	kdev->release = drm_sysfs_release;
551e1728075SDavid Herrmann 	dev_set_drvdata(kdev, minor);
552760c960bSDavid Herrmann 
553e1728075SDavid Herrmann 	r = dev_set_name(kdev, minor_str, minor->index);
554760c960bSDavid Herrmann 	if (r < 0)
555e1728075SDavid Herrmann 		goto err_free;
556760c960bSDavid Herrmann 
557e1728075SDavid Herrmann 	return kdev;
558760c960bSDavid Herrmann 
559e1728075SDavid Herrmann err_free:
560e1728075SDavid Herrmann 	put_device(kdev);
561e1728075SDavid Herrmann 	return ERR_PTR(r);
562c0e09200SDave Airlie }
563c0e09200SDave Airlie 
564c0e09200SDave Airlie /**
565e2271704SDaniel Vetter  * drm_class_device_register - register new device with the DRM sysfs class
566e2271704SDaniel Vetter  * @dev: device to register
567327c225bSThomas Hellstrom  *
568e2271704SDaniel Vetter  * Registers a new &struct device within the DRM sysfs class. Essentially only
569e2271704SDaniel Vetter  * used by ttm to have a place for its global settings. Drivers should never use
570e2271704SDaniel Vetter  * this.
571327c225bSThomas Hellstrom  */
drm_class_device_register(struct device * dev)572327c225bSThomas Hellstrom int drm_class_device_register(struct device *dev)
573327c225bSThomas Hellstrom {
57449099c49SDave Airlie 	if (!drm_class || IS_ERR(drm_class))
57549099c49SDave Airlie 		return -ENOENT;
57649099c49SDave Airlie 
577327c225bSThomas Hellstrom 	dev->class = drm_class;
578327c225bSThomas Hellstrom 	return device_register(dev);
579327c225bSThomas Hellstrom }
580327c225bSThomas Hellstrom EXPORT_SYMBOL_GPL(drm_class_device_register);
581327c225bSThomas Hellstrom 
582e2271704SDaniel Vetter /**
583e2271704SDaniel Vetter  * drm_class_device_unregister - unregister device with the DRM sysfs class
584e2271704SDaniel Vetter  * @dev: device to unregister
585e2271704SDaniel Vetter  *
586e2271704SDaniel Vetter  * Unregisters a &struct device from the DRM sysfs class. Essentially only used
587e2271704SDaniel Vetter  * by ttm to have a place for its global settings. Drivers should never use
588e2271704SDaniel Vetter  * this.
589e2271704SDaniel Vetter  */
drm_class_device_unregister(struct device * dev)590327c225bSThomas Hellstrom void drm_class_device_unregister(struct device *dev)
591327c225bSThomas Hellstrom {
592327c225bSThomas Hellstrom 	return device_unregister(dev);
593327c225bSThomas Hellstrom }
594327c225bSThomas Hellstrom EXPORT_SYMBOL_GPL(drm_class_device_unregister);
595