1303 lines · 36.0 KB · C
RawDownload
/* * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org * * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * * Author Rickard E. (Rik) Faith <faith@valinux.com> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include <linux/bitops.h>#include <linux/cgroup_dmem.h>#include <linux/debugfs.h>#include <linux/export.h>#include <linux/fs.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/mount.h>#include <linux/pseudo_fs.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/sprintf.h>#include <linux/srcu.h>#include <linux/xarray.h> #include <drm/drm_accel.h>#include <drm/drm_bridge.h>#include <drm/drm_cache.h>#include <drm/drm_client_event.h>#include <drm/drm_color_mgmt.h>#include <drm/drm_drv.h>#include <drm/drm_file.h>#include <drm/drm_managed.h>#include <drm/drm_mode_object.h>#include <drm/drm_panic.h>#include <drm/drm_print.h>#include <drm/drm_privacy_screen_machine.h>#include <drm/drm_ras_genl_family.h> #include "drm_crtc_internal.h"#include "drm_internal.h" MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl");MODULE_DESCRIPTION("DRM shared core routines");MODULE_LICENSE("GPL and additional rights"); DEFINE_XARRAY_ALLOC(drm_minors_xa); /* * If the drm core fails to init for whatever reason, * we should prevent any drivers from registering with it. * It's best to check this at drm_dev_init(), as some drivers * prefer to embed struct drm_device into their own device * structure and call drm_dev_init() themselves. */static bool drm_core_init_complete; DEFINE_STATIC_SRCU(drm_unplug_srcu); /* * DRM Minors * A DRM device can provide several char-dev interfaces on the DRM-Major. Each * of them is represented by a drm_minor object. Depending on the capabilities * of the device-driver, different interfaces are registered. * * Minors can be accessed via dev->$minor_name. This pointer is either * NULL or a valid drm_minor pointer and stays valid as long as the device is * valid. This means, DRM minors have the same life-time as the underlying * device. However, this doesn't mean that the minor is active. Minors are * registered and unregistered dynamically according to device-state. */ static struct xarray *drm_minor_get_xa(enum drm_minor_type type){	if (type == DRM_MINOR_PRIMARY || type == DRM_MINOR_RENDER)		return &drm_minors_xa;#if IS_ENABLED(CONFIG_DRM_ACCEL)	else if (type == DRM_MINOR_ACCEL)		return &accel_minors_xa;#endif	else		return ERR_PTR(-EOPNOTSUPP);} static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,					     enum drm_minor_type type){	switch (type) {	case DRM_MINOR_PRIMARY:		return &dev->primary;	case DRM_MINOR_RENDER:		return &dev->render;	case DRM_MINOR_ACCEL:		return &dev->accel;	default:		BUG();	}} static void drm_minor_alloc_release(struct drm_device *dev, void *data){	struct drm_minor *minor = data; 	WARN_ON(dev != minor->dev); 	put_device(minor->kdev); 	xa_erase(drm_minor_get_xa(minor->type), minor->index);} /* * DRM used to support 64 devices, for backwards compatibility we need to maintain the * minor allocation scheme where minors 0-63 are primary nodes, 64-127 are control nodes, * and 128-191 are render nodes. * After reaching the limit, we're allocating minors dynamically - first-come, first-serve. * Accel nodes are using a distinct major, so the minors are allocated in continuous 0-MAX * range. */#define DRM_MINOR_LIMIT(t) ({ \	typeof(t) _t = (t); \	_t == DRM_MINOR_ACCEL ? XA_LIMIT(0, ACCEL_MAX_MINORS) : XA_LIMIT(64 * _t, 64 * _t + 63); \})#define DRM_EXTENDED_MINOR_LIMIT XA_LIMIT(192, (1 << MINORBITS) - 1) static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type){	struct drm_minor *minor;	int r; 	minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);	if (!minor)		return -ENOMEM; 	minor->type = type;	minor->dev = dev; 	r = xa_alloc(drm_minor_get_xa(type), &minor->index,		     NULL, DRM_MINOR_LIMIT(type), GFP_KERNEL);	if (r == -EBUSY && (type == DRM_MINOR_PRIMARY || type == DRM_MINOR_RENDER))		r = xa_alloc(&drm_minors_xa, &minor->index,			     NULL, DRM_EXTENDED_MINOR_LIMIT, GFP_KERNEL);	if (r < 0)		return r; 	r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);	if (r)		return r; 	minor->kdev = drm_sysfs_minor_alloc(minor);	if (IS_ERR(minor->kdev))		return PTR_ERR(minor->kdev); 	*drm_minor_get_slot(dev, type) = minor;	return 0;} static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type){	struct drm_minor *minor;	void *entry;	int ret; 	DRM_DEBUG("\n"); 	minor = *drm_minor_get_slot(dev, type);	if (!minor)		return 0; 	if (minor->type != DRM_MINOR_ACCEL) {		ret = drm_debugfs_register(minor, minor->index);		if (ret) {			DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");			goto err_debugfs;		}	} 	ret = device_add(minor->kdev);	if (ret)		goto err_debugfs; 	/* replace NULL with @minor so lookups will succeed from now on */	entry = xa_store(drm_minor_get_xa(type), minor->index, minor, GFP_KERNEL);	if (xa_is_err(entry)) {		ret = xa_err(entry);		goto err_debugfs;	}	WARN_ON(entry); 	DRM_DEBUG("new minor registered %d\n", minor->index);	return 0; err_debugfs:	drm_debugfs_unregister(minor);	return ret;} static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type type){	struct drm_minor *minor; 	minor = *drm_minor_get_slot(dev, type);	if (!minor || !device_is_registered(minor->kdev))		return; 	/* replace @minor with NULL so lookups will fail from now on */	xa_store(drm_minor_get_xa(type), minor->index, NULL, GFP_KERNEL); 	device_del(minor->kdev);	dev_set_drvdata(minor->kdev, NULL); /* safety belt */	drm_debugfs_unregister(minor);} /* * Looks up the given minor-ID and returns the respective DRM-minor object. The * refence-count of the underlying device is increased so you must release this * object with drm_minor_release(). * * As long as you hold this minor, it is guaranteed that the object and the * minor->dev pointer will stay valid! However, the device may get unplugged and * unregistered while you hold the minor. */struct drm_minor *drm_minor_acquire(struct xarray *minor_xa, unsigned int minor_id){	struct drm_minor *minor; 	xa_lock(minor_xa);	minor = xa_load(minor_xa, minor_id);	if (minor)		drm_dev_get(minor->dev);	xa_unlock(minor_xa); 	if (!minor) {		return ERR_PTR(-ENODEV);	} else if (drm_dev_is_unplugged(minor->dev)) {		drm_dev_put(minor->dev);		return ERR_PTR(-ENODEV);	} 	return minor;} void drm_minor_release(struct drm_minor *minor){	drm_dev_put(minor->dev);} /** * DOC: driver instance overview * * A device instance for a drm driver is represented by &struct drm_device. This * is allocated and initialized with devm_drm_dev_alloc(), usually from * bus-specific ->probe() callbacks implemented by the driver. The driver then * needs to initialize all the various subsystems for the drm device like memory * management, vblank handling, modesetting support and initial output * configuration plus obviously initialize all the corresponding hardware bits. * Finally when everything is up and running and ready for userspace the device * instance can be published using drm_dev_register(). * * There is also deprecated support for initializing device instances using * bus-specific helpers and the &drm_driver.load callback. But due to * backwards-compatibility needs the device instance have to be published too * early, which requires unpretty global locking to make safe and is therefore * only support for existing drivers not yet converted to the new scheme. * * When cleaning up a device instance everything needs to be done in reverse: * First unpublish the device instance with drm_dev_unregister(). Then clean up * any other resources allocated at device initialization and drop the driver's * reference to &drm_device using drm_dev_put(). * * Note that any allocation or resource which is visible to userspace must be * released only when the final drm_dev_put() is called, and not when the * driver is unbound from the underlying physical struct &device. Best to use * &drm_device managed resources with drmm_add_action(), drmm_kmalloc() and * related functions. * * devres managed resources like devm_kmalloc() can only be used for resources * directly related to the underlying hardware device, and only used in code * paths fully protected by drm_dev_enter() and drm_dev_exit(). * * Display driver example * ~~~~~~~~~~~~~~~~~~~~~~ * * The following example shows a typical structure of a DRM display driver. * The example focus on the probe() function and the other functions that is * almost always present and serves as a demonstration of devm_drm_dev_alloc(). * * .. code-block:: c * *	struct driver_device { *		struct drm_device drm; *		void *userspace_facing; *		struct clk *pclk; *	}; * *	static const struct drm_driver driver_drm_driver = { *		[...] *	}; * *	static int driver_probe(struct platform_device *pdev) *	{ *		struct driver_device *priv; *		struct drm_device *drm; *		int ret; * *		priv = devm_drm_dev_alloc(&pdev->dev, &driver_drm_driver, *					  struct driver_device, drm); *		if (IS_ERR(priv)) *			return PTR_ERR(priv); *		drm = &priv->drm; * *		ret = drmm_mode_config_init(drm); *		if (ret) *			return ret; * *		priv->userspace_facing = drmm_kzalloc(..., GFP_KERNEL); *		if (!priv->userspace_facing) *			return -ENOMEM; * *		priv->pclk = devm_clk_get(dev, "PCLK"); *		if (IS_ERR(priv->pclk)) *			return PTR_ERR(priv->pclk); * *		// Further setup, display pipeline etc * *		platform_set_drvdata(pdev, drm); * *		ret = drm_mode_config_create_initial_state(drm); *		if (ret) *			return ret; * *		ret = drm_dev_register(drm); *		if (ret) *			return ret; * *		drm_fbdev_{...}_setup(drm, 32); * *		return 0; *	} * *	// This function is called before the devm_ resources are released *	static int driver_remove(struct platform_device *pdev) *	{ *		struct drm_device *drm = platform_get_drvdata(pdev); * *		drm_dev_unregister(drm); *		drm_atomic_helper_shutdown(drm) * *		return 0; *	} * *	// This function is called on kernel restart and shutdown *	static void driver_shutdown(struct platform_device *pdev) *	{ *		drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); *	} * *	static int __maybe_unused driver_pm_suspend(struct device *dev) *	{ *		return drm_mode_config_helper_suspend(dev_get_drvdata(dev)); *	} * *	static int __maybe_unused driver_pm_resume(struct device *dev) *	{ *		drm_mode_config_helper_resume(dev_get_drvdata(dev)); * *		return 0; *	} * *	static const struct dev_pm_ops driver_pm_ops = { *		SET_SYSTEM_SLEEP_PM_OPS(driver_pm_suspend, driver_pm_resume) *	}; * *	static struct platform_driver driver_driver = { *		.driver = { *			[...] *			.pm = &driver_pm_ops, *		}, *		.probe = driver_probe, *		.remove = driver_remove, *		.shutdown = driver_shutdown, *	}; *	module_platform_driver(driver_driver); * * Drivers that want to support device unplugging (USB, DT overlay unload) should * use drm_dev_unplug() instead of drm_dev_unregister(). The driver must protect * regions that is accessing device resources to prevent use after they're * released. This is done using drm_dev_enter() and drm_dev_exit(). There is one * shortcoming however, drm_dev_unplug() marks the drm_device as unplugged before * drm_atomic_helper_shutdown() is called. This means that if the disable code * paths are protected, they will not run on regular driver module unload, * possibly leaving the hardware enabled. */ /** * drm_put_dev - Unregister and release a DRM device * @dev: DRM device * * Called at module unload time or when a PCI device is unplugged. * * Cleans up all DRM device, calling drm_lastclose(). * * Note: Use of this function is deprecated. It will eventually go away * completely.  Please use drm_dev_unregister() and drm_dev_put() explicitly * instead to make sure that the device isn't userspace accessible any more * while teardown is in progress, ensuring that userspace can't access an * inconsistent state. */void drm_put_dev(struct drm_device *dev){	DRM_DEBUG("\n"); 	if (!dev) {		DRM_ERROR("cleanup called no dev\n");		return;	} 	drm_dev_unregister(dev);	drm_dev_put(dev);}EXPORT_SYMBOL(drm_put_dev); /** * drm_dev_enter - Enter device critical section * @dev: DRM device * @idx: Pointer to index that will be passed to the matching drm_dev_exit() * * This function marks and protects the beginning of a section that should not * be entered after the device has been unplugged. The section end is marked * with drm_dev_exit(). Calls to this function can be nested. * * Returns: * True if it is OK to enter the section, false otherwise. */bool drm_dev_enter(struct drm_device *dev, int *idx){	*idx = srcu_read_lock(&drm_unplug_srcu); 	if (dev->unplugged) {		srcu_read_unlock(&drm_unplug_srcu, *idx);		return false;	} 	return true;}EXPORT_SYMBOL(drm_dev_enter); /** * drm_dev_exit - Exit device critical section * @idx: index returned from drm_dev_enter() * * This function marks the end of a section that should not be entered after * the device has been unplugged. */void drm_dev_exit(int idx){	srcu_read_unlock(&drm_unplug_srcu, idx);}EXPORT_SYMBOL(drm_dev_exit); /* * Mark the device as unplugged and wait for any in-flight drm_dev_enter() * critical sections to complete. */static void drm_dev_synchronize_unplug(struct drm_device *dev){	/*	 * After synchronizing any critical read section is guaranteed to see	 * the new value of ->unplugged, and any critical section which might	 * still have seen the old value of ->unplugged is guaranteed to have	 * finished.	 */	dev->unplugged = true;	synchronize_srcu(&drm_unplug_srcu);} /** * drm_dev_unplug - unplug a DRM device * @dev: DRM device * * This unplugs a hotpluggable DRM device, which makes it inaccessible to * userspace operations. Entry-points can use drm_dev_enter() and * drm_dev_exit() to protect device resources in a race free manner. This

Lines 1–500 of 1303.