brintos

brintos / linux-shallow public Read only

0
0
Text · 14.5 KiB · fb3bbb6 Raw
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