18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * drm_sysfs.c - Modifications to drm_sysfs_class.c to support 58c2ecf20Sopenharmony_ci * extra sysfs attribute from DRM. Normal drm_sysfs_class 68c2ecf20Sopenharmony_ci * does not allow adding attributes. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (c) 2004 Jon Smirl <jonsmirl@gmail.com> 98c2ecf20Sopenharmony_ci * Copyright (c) 2003-2004 Greg Kroah-Hartman <greg@kroah.com> 108c2ecf20Sopenharmony_ci * Copyright (c) 2003-2004 IBM Corp. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/acpi.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/export.h> 178c2ecf20Sopenharmony_ci#include <linux/gfp.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci#include <linux/kdev_t.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <drm/drm_connector.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_file.h> 258c2ecf20Sopenharmony_ci#include <drm/drm_modes.h> 268c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 278c2ecf20Sopenharmony_ci#include <drm/drm_property.h> 288c2ecf20Sopenharmony_ci#include <drm/drm_sysfs.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "drm_internal.h" 318c2ecf20Sopenharmony_ci#include "drm_crtc_internal.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define to_drm_minor(d) dev_get_drvdata(d) 348c2ecf20Sopenharmony_ci#define to_drm_connector(d) dev_get_drvdata(d) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/** 378c2ecf20Sopenharmony_ci * DOC: overview 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * DRM provides very little additional support to drivers for sysfs 408c2ecf20Sopenharmony_ci * interactions, beyond just all the standard stuff. Drivers who want to expose 418c2ecf20Sopenharmony_ci * additional sysfs properties and property groups can attach them at either 428c2ecf20Sopenharmony_ci * &drm_device.dev or &drm_connector.kdev. 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * Registration is automatically handled when calling drm_dev_register(), or 458c2ecf20Sopenharmony_ci * drm_connector_register() in case of hot-plugged connectors. Unregistration is 468c2ecf20Sopenharmony_ci * also automatically handled by drm_dev_unregister() and 478c2ecf20Sopenharmony_ci * drm_connector_unregister(). 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct device_type drm_sysfs_device_minor = { 518c2ecf20Sopenharmony_ci .name = "drm_minor" 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic struct device_type drm_sysfs_device_connector = { 558c2ecf20Sopenharmony_ci .name = "drm_connector", 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct class *drm_class; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 618c2ecf20Sopenharmony_cistatic bool drm_connector_acpi_bus_match(struct device *dev) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return dev->type == &drm_sysfs_device_connector; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic struct acpi_device *drm_connector_acpi_find_companion(struct device *dev) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct drm_connector *connector = to_drm_connector(dev); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return to_acpi_device_node(connector->fwnode); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic struct acpi_bus_type drm_connector_acpi_bus = { 748c2ecf20Sopenharmony_ci .name = "drm_connector", 758c2ecf20Sopenharmony_ci .match = drm_connector_acpi_bus_match, 768c2ecf20Sopenharmony_ci .find_companion = drm_connector_acpi_find_companion, 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void drm_sysfs_acpi_register(void) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci register_acpi_bus_type(&drm_connector_acpi_bus); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void drm_sysfs_acpi_unregister(void) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci unregister_acpi_bus_type(&drm_connector_acpi_bus); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci#else 898c2ecf20Sopenharmony_cistatic void drm_sysfs_acpi_register(void) { } 908c2ecf20Sopenharmony_cistatic void drm_sysfs_acpi_unregister(void) { } 918c2ecf20Sopenharmony_ci#endif 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic char *drm_devnode(struct device *dev, umode_t *mode) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic CLASS_ATTR_STRING(version, S_IRUGO, "drm 1.1.0 20060810"); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/** 1018c2ecf20Sopenharmony_ci * drm_sysfs_init - initialize sysfs helpers 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * This is used to create the DRM class, which is the implicit parent of any 1048c2ecf20Sopenharmony_ci * other top-level DRM sysfs objects. 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci * You must call drm_sysfs_destroy() to release the allocated resources. 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * Return: 0 on success, negative error code on failure. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ciint drm_sysfs_init(void) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci int err; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci drm_class = class_create(THIS_MODULE, "drm"); 1158c2ecf20Sopenharmony_ci if (IS_ERR(drm_class)) 1168c2ecf20Sopenharmony_ci return PTR_ERR(drm_class); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci err = class_create_file(drm_class, &class_attr_version.attr); 1198c2ecf20Sopenharmony_ci if (err) { 1208c2ecf20Sopenharmony_ci class_destroy(drm_class); 1218c2ecf20Sopenharmony_ci drm_class = NULL; 1228c2ecf20Sopenharmony_ci return err; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci drm_class->devnode = drm_devnode; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci drm_sysfs_acpi_register(); 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/** 1328c2ecf20Sopenharmony_ci * drm_sysfs_destroy - destroys DRM class 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * Destroy the DRM device class. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_civoid drm_sysfs_destroy(void) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(drm_class)) 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci drm_sysfs_acpi_unregister(); 1418c2ecf20Sopenharmony_ci class_remove_file(drm_class, &class_attr_version.attr); 1428c2ecf20Sopenharmony_ci class_destroy(drm_class); 1438c2ecf20Sopenharmony_ci drm_class = NULL; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void drm_sysfs_release(struct device *dev) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci kfree(dev); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* 1528c2ecf20Sopenharmony_ci * Connector properties 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistatic ssize_t status_store(struct device *device, 1558c2ecf20Sopenharmony_ci struct device_attribute *attr, 1568c2ecf20Sopenharmony_ci const char *buf, size_t count) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct drm_connector *connector = to_drm_connector(device); 1598c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 1608c2ecf20Sopenharmony_ci enum drm_connector_force old_force; 1618c2ecf20Sopenharmony_ci int ret; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&dev->mode_config.mutex); 1648c2ecf20Sopenharmony_ci if (ret) 1658c2ecf20Sopenharmony_ci return ret; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci old_force = connector->force; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (sysfs_streq(buf, "detect")) 1708c2ecf20Sopenharmony_ci connector->force = 0; 1718c2ecf20Sopenharmony_ci else if (sysfs_streq(buf, "on")) 1728c2ecf20Sopenharmony_ci connector->force = DRM_FORCE_ON; 1738c2ecf20Sopenharmony_ci else if (sysfs_streq(buf, "on-digital")) 1748c2ecf20Sopenharmony_ci connector->force = DRM_FORCE_ON_DIGITAL; 1758c2ecf20Sopenharmony_ci else if (sysfs_streq(buf, "off")) 1768c2ecf20Sopenharmony_ci connector->force = DRM_FORCE_OFF; 1778c2ecf20Sopenharmony_ci else 1788c2ecf20Sopenharmony_ci ret = -EINVAL; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (old_force != connector->force || !connector->force) { 1818c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force updated from %d to %d or reprobing\n", 1828c2ecf20Sopenharmony_ci connector->base.id, 1838c2ecf20Sopenharmony_ci connector->name, 1848c2ecf20Sopenharmony_ci old_force, connector->force); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci connector->funcs->fill_modes(connector, 1878c2ecf20Sopenharmony_ci dev->mode_config.max_width, 1888c2ecf20Sopenharmony_ci dev->mode_config.max_height); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return ret ? ret : count; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic ssize_t status_show(struct device *device, 1978c2ecf20Sopenharmony_ci struct device_attribute *attr, 1988c2ecf20Sopenharmony_ci char *buf) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct drm_connector *connector = to_drm_connector(device); 2018c2ecf20Sopenharmony_ci enum drm_connector_status status; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci status = READ_ONCE(connector->status); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 2068c2ecf20Sopenharmony_ci drm_get_connector_status_name(status)); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic ssize_t dpms_show(struct device *device, 2108c2ecf20Sopenharmony_ci struct device_attribute *attr, 2118c2ecf20Sopenharmony_ci char *buf) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct drm_connector *connector = to_drm_connector(device); 2148c2ecf20Sopenharmony_ci int dpms; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci dpms = READ_ONCE(connector->dpms); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 2198c2ecf20Sopenharmony_ci drm_get_dpms_name(dpms)); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic ssize_t enabled_show(struct device *device, 2238c2ecf20Sopenharmony_ci struct device_attribute *attr, 2248c2ecf20Sopenharmony_ci char *buf) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct drm_connector *connector = to_drm_connector(device); 2278c2ecf20Sopenharmony_ci bool enabled; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci enabled = READ_ONCE(connector->encoder); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, enabled ? "enabled\n" : "disabled\n"); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic ssize_t edid_show(struct file *filp, struct kobject *kobj, 2358c2ecf20Sopenharmony_ci struct bin_attribute *attr, char *buf, loff_t off, 2368c2ecf20Sopenharmony_ci size_t count) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct device *connector_dev = kobj_to_dev(kobj); 2398c2ecf20Sopenharmony_ci struct drm_connector *connector = to_drm_connector(connector_dev); 2408c2ecf20Sopenharmony_ci unsigned char *edid; 2418c2ecf20Sopenharmony_ci size_t size; 2428c2ecf20Sopenharmony_ci ssize_t ret = 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci mutex_lock(&connector->dev->mode_config.mutex); 2458c2ecf20Sopenharmony_ci if (!connector->edid_blob_ptr) 2468c2ecf20Sopenharmony_ci goto unlock; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci edid = connector->edid_blob_ptr->data; 2498c2ecf20Sopenharmony_ci size = connector->edid_blob_ptr->length; 2508c2ecf20Sopenharmony_ci if (!edid) 2518c2ecf20Sopenharmony_ci goto unlock; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (off >= size) 2548c2ecf20Sopenharmony_ci goto unlock; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (off + count > size) 2578c2ecf20Sopenharmony_ci count = size - off; 2588c2ecf20Sopenharmony_ci memcpy(buf, edid + off, count); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ret = count; 2618c2ecf20Sopenharmony_ciunlock: 2628c2ecf20Sopenharmony_ci mutex_unlock(&connector->dev->mode_config.mutex); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return ret; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic ssize_t modes_show(struct device *device, 2688c2ecf20Sopenharmony_ci struct device_attribute *attr, 2698c2ecf20Sopenharmony_ci char *buf) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct drm_connector *connector = to_drm_connector(device); 2728c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 2738c2ecf20Sopenharmony_ci int written = 0; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci mutex_lock(&connector->dev->mode_config.mutex); 2768c2ecf20Sopenharmony_ci list_for_each_entry(mode, &connector->modes, head) { 2778c2ecf20Sopenharmony_ci written += scnprintf(buf + written, PAGE_SIZE - written, "%s\n", 2788c2ecf20Sopenharmony_ci mode->name); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci mutex_unlock(&connector->dev->mode_config.mutex); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return written; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(status); 2868c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(enabled); 2878c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(dpms); 2888c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modes); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic struct attribute *connector_dev_attrs[] = { 2918c2ecf20Sopenharmony_ci &dev_attr_status.attr, 2928c2ecf20Sopenharmony_ci &dev_attr_enabled.attr, 2938c2ecf20Sopenharmony_ci &dev_attr_dpms.attr, 2948c2ecf20Sopenharmony_ci &dev_attr_modes.attr, 2958c2ecf20Sopenharmony_ci NULL 2968c2ecf20Sopenharmony_ci}; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic struct bin_attribute edid_attr = { 2998c2ecf20Sopenharmony_ci .attr.name = "edid", 3008c2ecf20Sopenharmony_ci .attr.mode = 0444, 3018c2ecf20Sopenharmony_ci .size = 0, 3028c2ecf20Sopenharmony_ci .read = edid_show, 3038c2ecf20Sopenharmony_ci}; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic struct bin_attribute *connector_bin_attrs[] = { 3068c2ecf20Sopenharmony_ci &edid_attr, 3078c2ecf20Sopenharmony_ci NULL 3088c2ecf20Sopenharmony_ci}; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic const struct attribute_group connector_dev_group = { 3118c2ecf20Sopenharmony_ci .attrs = connector_dev_attrs, 3128c2ecf20Sopenharmony_ci .bin_attrs = connector_bin_attrs, 3138c2ecf20Sopenharmony_ci}; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic const struct attribute_group *connector_dev_groups[] = { 3168c2ecf20Sopenharmony_ci &connector_dev_group, 3178c2ecf20Sopenharmony_ci NULL 3188c2ecf20Sopenharmony_ci}; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ciint drm_sysfs_connector_add(struct drm_connector *connector) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 3238c2ecf20Sopenharmony_ci struct device *kdev; 3248c2ecf20Sopenharmony_ci int r; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (connector->kdev) 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); 3308c2ecf20Sopenharmony_ci if (!kdev) 3318c2ecf20Sopenharmony_ci return -ENOMEM; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci device_initialize(kdev); 3348c2ecf20Sopenharmony_ci kdev->class = drm_class; 3358c2ecf20Sopenharmony_ci kdev->type = &drm_sysfs_device_connector; 3368c2ecf20Sopenharmony_ci kdev->parent = dev->primary->kdev; 3378c2ecf20Sopenharmony_ci kdev->groups = connector_dev_groups; 3388c2ecf20Sopenharmony_ci kdev->release = drm_sysfs_release; 3398c2ecf20Sopenharmony_ci dev_set_drvdata(kdev, connector); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci r = dev_set_name(kdev, "card%d-%s", dev->primary->index, connector->name); 3428c2ecf20Sopenharmony_ci if (r) 3438c2ecf20Sopenharmony_ci goto err_free; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci DRM_DEBUG("adding \"%s\" to sysfs\n", 3468c2ecf20Sopenharmony_ci connector->name); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci r = device_add(kdev); 3498c2ecf20Sopenharmony_ci if (r) { 3508c2ecf20Sopenharmony_ci drm_err(dev, "failed to register connector device: %d\n", r); 3518c2ecf20Sopenharmony_ci goto err_free; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci connector->kdev = kdev; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (connector->ddc) 3578c2ecf20Sopenharmony_ci return sysfs_create_link(&connector->kdev->kobj, 3588c2ecf20Sopenharmony_ci &connector->ddc->dev.kobj, "ddc"); 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cierr_free: 3628c2ecf20Sopenharmony_ci put_device(kdev); 3638c2ecf20Sopenharmony_ci return r; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_civoid drm_sysfs_connector_remove(struct drm_connector *connector) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci if (!connector->kdev) 3698c2ecf20Sopenharmony_ci return; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (connector->ddc) 3728c2ecf20Sopenharmony_ci sysfs_remove_link(&connector->kdev->kobj, "ddc"); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci DRM_DEBUG("removing \"%s\" from sysfs\n", 3758c2ecf20Sopenharmony_ci connector->name); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci device_unregister(connector->kdev); 3788c2ecf20Sopenharmony_ci connector->kdev = NULL; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_civoid drm_sysfs_lease_event(struct drm_device *dev) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci char *event_string = "LEASE=1"; 3848c2ecf20Sopenharmony_ci char *envp[] = { event_string, NULL }; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci DRM_DEBUG("generating lease event\n"); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/** 3928c2ecf20Sopenharmony_ci * drm_sysfs_hotplug_event - generate a DRM uevent 3938c2ecf20Sopenharmony_ci * @dev: DRM device 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci * Send a uevent for the DRM device specified by @dev. Currently we only 3968c2ecf20Sopenharmony_ci * set HOTPLUG=1 in the uevent environment, but this could be expanded to 3978c2ecf20Sopenharmony_ci * deal with other types of events. 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * Any new uapi should be using the drm_sysfs_connector_status_event() 4008c2ecf20Sopenharmony_ci * for uevents on connector status change. 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_civoid drm_sysfs_hotplug_event(struct drm_device *dev) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci char *event_string = "HOTPLUG=1"; 4058c2ecf20Sopenharmony_ci char *envp[] = { event_string, NULL }; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci DRM_DEBUG("generating hotplug event\n"); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_sysfs_hotplug_event); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/** 4148c2ecf20Sopenharmony_ci * drm_sysfs_connector_status_event - generate a DRM uevent for connector 4158c2ecf20Sopenharmony_ci * property status change 4168c2ecf20Sopenharmony_ci * @connector: connector on which property status changed 4178c2ecf20Sopenharmony_ci * @property: connector property whose status changed. 4188c2ecf20Sopenharmony_ci * 4198c2ecf20Sopenharmony_ci * Send a uevent for the DRM device specified by @dev. Currently we 4208c2ecf20Sopenharmony_ci * set HOTPLUG=1 and connector id along with the attached property id 4218c2ecf20Sopenharmony_ci * related to the status change. 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_civoid drm_sysfs_connector_status_event(struct drm_connector *connector, 4248c2ecf20Sopenharmony_ci struct drm_property *property) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 4278c2ecf20Sopenharmony_ci char hotplug_str[] = "HOTPLUG=1", conn_id[21], prop_id[21]; 4288c2ecf20Sopenharmony_ci char *envp[4] = { hotplug_str, conn_id, prop_id, NULL }; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci WARN_ON(!drm_mode_obj_find_prop_id(&connector->base, 4318c2ecf20Sopenharmony_ci property->base.id)); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci snprintf(conn_id, ARRAY_SIZE(conn_id), 4348c2ecf20Sopenharmony_ci "CONNECTOR=%u", connector->base.id); 4358c2ecf20Sopenharmony_ci snprintf(prop_id, ARRAY_SIZE(prop_id), 4368c2ecf20Sopenharmony_ci "PROPERTY=%u", property->base.id); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci DRM_DEBUG("generating connector status event\n"); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp); 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_sysfs_connector_status_event); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistruct device *drm_sysfs_minor_alloc(struct drm_minor *minor) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci const char *minor_str; 4478c2ecf20Sopenharmony_ci struct device *kdev; 4488c2ecf20Sopenharmony_ci int r; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (minor->type == DRM_MINOR_RENDER) 4518c2ecf20Sopenharmony_ci minor_str = "renderD%d"; 4528c2ecf20Sopenharmony_ci else 4538c2ecf20Sopenharmony_ci minor_str = "card%d"; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); 4568c2ecf20Sopenharmony_ci if (!kdev) 4578c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci device_initialize(kdev); 4608c2ecf20Sopenharmony_ci kdev->devt = MKDEV(DRM_MAJOR, minor->index); 4618c2ecf20Sopenharmony_ci kdev->class = drm_class; 4628c2ecf20Sopenharmony_ci kdev->type = &drm_sysfs_device_minor; 4638c2ecf20Sopenharmony_ci kdev->parent = minor->dev->dev; 4648c2ecf20Sopenharmony_ci kdev->release = drm_sysfs_release; 4658c2ecf20Sopenharmony_ci dev_set_drvdata(kdev, minor); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci r = dev_set_name(kdev, minor_str, minor->index); 4688c2ecf20Sopenharmony_ci if (r < 0) 4698c2ecf20Sopenharmony_ci goto err_free; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return kdev; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cierr_free: 4748c2ecf20Sopenharmony_ci put_device(kdev); 4758c2ecf20Sopenharmony_ci return ERR_PTR(r); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/** 4798c2ecf20Sopenharmony_ci * drm_class_device_register - register new device with the DRM sysfs class 4808c2ecf20Sopenharmony_ci * @dev: device to register 4818c2ecf20Sopenharmony_ci * 4828c2ecf20Sopenharmony_ci * Registers a new &struct device within the DRM sysfs class. Essentially only 4838c2ecf20Sopenharmony_ci * used by ttm to have a place for its global settings. Drivers should never use 4848c2ecf20Sopenharmony_ci * this. 4858c2ecf20Sopenharmony_ci */ 4868c2ecf20Sopenharmony_ciint drm_class_device_register(struct device *dev) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci if (!drm_class || IS_ERR(drm_class)) 4898c2ecf20Sopenharmony_ci return -ENOENT; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci dev->class = drm_class; 4928c2ecf20Sopenharmony_ci return device_register(dev); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_class_device_register); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/** 4978c2ecf20Sopenharmony_ci * drm_class_device_unregister - unregister device with the DRM sysfs class 4988c2ecf20Sopenharmony_ci * @dev: device to unregister 4998c2ecf20Sopenharmony_ci * 5008c2ecf20Sopenharmony_ci * Unregisters a &struct device from the DRM sysfs class. Essentially only used 5018c2ecf20Sopenharmony_ci * by ttm to have a place for its global settings. Drivers should never use 5028c2ecf20Sopenharmony_ci * this. 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_civoid drm_class_device_unregister(struct device *dev) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci return device_unregister(dev); 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_class_device_unregister); 509