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