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