mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-03 18:20:25 +02:00 
			
		
		
		
	UAPI Changes: Cross-subsystem Changes: - Split out panel-lvds and lvds dt bindings . - Put yes/no on/off disabled/enabled strings in linux/string_helpers.h and use it in drivers and tomoyo. - Clarify dma_fence_chain and dma_fence_array should never include eachother. - Flatten chains in syncobj's. - Don't double add in fbdev/defio when page is already enlisted. - Don't sort deferred-I/O pages by default in fbdev. Core Changes: - Fix missing pm_runtime_put_sync in bridge. - Set modifier support to only linear fb modifier if drivers don't advertise support. - As a result, we remove allow_fb_modifiers. - Add missing clear for EDID Deep Color Modes in drm_reset_display_info. - Assorted documentation updates. - Warn once in drm_clflush if there is no arch support. - Add missing select for dp helper in drm_panel_edp. - Assorted small fixes. - Improve fb-helper's clipping handling. - Don't dump shmem mmaps in a core dump. - Add accounting to ttm resource manager, and use it in amdgpu. - Allow querying the detected eDP panel through debugfs. - Add helpers for xrgb8888 to 8 and 1 bits gray. - Improve drm's buddy allocator. - Add selftests for the buddy allocator. Driver Changes: - Add support for nomodeset to a lot of drm drivers. - Use drm_module_*_driver in a lot of drm drivers. - Assorted small fixes to bridge/lt9611, v3d, vc4, vmwgfx, mxsfb, nouveau, bridge/dw-hdmi, panfrost, lima, ingenic, sprd, bridge/anx7625, ti-sn65dsi86. - Add bridge/it6505. - Create DP and DVI-I connectors in ast. - Assorted nouveau backlight fixes. - Rework amdgpu reset handling. - Add dt bindings for ingenic,jz4780-dw-hdmi. - Support reading edid through aux channel in ingenic. - Add a drm driver for Solomon SSD130x OLED displays. - Add simple support for sharp LQ140M1JW46. - Add more panels to nt35560. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEuXvWqAysSYEJGuVH/lWMcqZwE8MFAmIWLSEACgkQ/lWMcqZw E8OP7hAAjix94EX5fhFa7OAdqUbFtsiKhK/4zNtV9FWpFiEsDBz+dlbfDQWIx5an FIiiiQtSfWjpDv6pcMhoNf80w+dDbc/Cuauz6nNGO7Pkaerh2D/EPG74FD7f7nE3 EIScVs1heYtzM9usKrFKupNYgIdgZxmeovClWuE0OTjLOes2PGvvxXK6iJqNqJMX VlDO5SR7GRqsDUDV6vmwl63uKL77xJXAahAXIx+BQ/1xrtEhlu6NwsgHIsmPmMSN YluX34zc1xD/6/uUqvEdp7u46/5/He1c5Q/ia1WV3wRxsO/eMZ+axXqCZP3XGZdt rMdGNtj1MWKkudYiowStWkCVSG/0fXJCFIAhvRmeZy+YqPdVlqZ2W7g4H1l9iJoo UVfT9cHrKoxHsukvIEckC5Ov9v1yr39Bd4wUuqaUTUSxY8VID5vjY63TsXl9Zke1 SluTFe9qybbnRNz/hYRvwIS1eT8HvUauAfAhypGTLI5DYHTD7PawcfMJkNzCtJm4 Ta4SC3rTpkpN+7oc8SoNgqRHQ8U9KL5oksP0wVa8vwHsMptSd3X4pUljc6TcfjLv GEo41D5AuJz3HRVcn9yqPbLoPE2FFB7bfwIMH77yNnoos4Izy/LGhKpN0YdImmI5 W5XVFB0jltGSIhkzLe1mFpLrdJwdUTSUVeCK4H5PhZZQEHLkVtg= =HuwD -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2022-02-23' of git://anongit.freedesktop.org/drm/drm-misc into drm-next drm-misc-next for v5.18: UAPI Changes: Cross-subsystem Changes: - Split out panel-lvds and lvds dt bindings . - Put yes/no on/off disabled/enabled strings in linux/string_helpers.h and use it in drivers and tomoyo. - Clarify dma_fence_chain and dma_fence_array should never include eachother. - Flatten chains in syncobj's. - Don't double add in fbdev/defio when page is already enlisted. - Don't sort deferred-I/O pages by default in fbdev. Core Changes: - Fix missing pm_runtime_put_sync in bridge. - Set modifier support to only linear fb modifier if drivers don't advertise support. - As a result, we remove allow_fb_modifiers. - Add missing clear for EDID Deep Color Modes in drm_reset_display_info. - Assorted documentation updates. - Warn once in drm_clflush if there is no arch support. - Add missing select for dp helper in drm_panel_edp. - Assorted small fixes. - Improve fb-helper's clipping handling. - Don't dump shmem mmaps in a core dump. - Add accounting to ttm resource manager, and use it in amdgpu. - Allow querying the detected eDP panel through debugfs. - Add helpers for xrgb8888 to 8 and 1 bits gray. - Improve drm's buddy allocator. - Add selftests for the buddy allocator. Driver Changes: - Add support for nomodeset to a lot of drm drivers. - Use drm_module_*_driver in a lot of drm drivers. - Assorted small fixes to bridge/lt9611, v3d, vc4, vmwgfx, mxsfb, nouveau, bridge/dw-hdmi, panfrost, lima, ingenic, sprd, bridge/anx7625, ti-sn65dsi86. - Add bridge/it6505. - Create DP and DVI-I connectors in ast. - Assorted nouveau backlight fixes. - Rework amdgpu reset handling. - Add dt bindings for ingenic,jz4780-dw-hdmi. - Support reading edid through aux channel in ingenic. - Add a drm driver for Solomon SSD130x OLED displays. - Add simple support for sharp LQ140M1JW46. - Add more panels to nt35560. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/686ec871-e77f-c230-22e5-9e3bb80f064a@linux.intel.com
		
			
				
	
	
		
			471 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			471 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: MIT
 | 
						|
/*
 | 
						|
 * Copyright (C) 2020 - 2021 Red Hat, Inc.
 | 
						|
 *
 | 
						|
 * Authors:
 | 
						|
 * Hans de Goede <hdegoede@redhat.com>
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/device.h>
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/list.h>
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/mutex.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
#include <drm/drm_privacy_screen_machine.h>
 | 
						|
#include <drm/drm_privacy_screen_consumer.h>
 | 
						|
#include <drm/drm_privacy_screen_driver.h>
 | 
						|
#include "drm_internal.h"
 | 
						|
 | 
						|
/**
 | 
						|
 * DOC: overview
 | 
						|
 *
 | 
						|
 * This class allows non KMS drivers, from e.g. drivers/platform/x86 to
 | 
						|
 * register a privacy-screen device, which the KMS drivers can then use
 | 
						|
 * to implement the standard privacy-screen properties, see
 | 
						|
 * :ref:`Standard Connector Properties<standard_connector_properties>`.
 | 
						|
 *
 | 
						|
 * KMS drivers using a privacy-screen class device are advised to use the
 | 
						|
 * drm_connector_attach_privacy_screen_provider() and
 | 
						|
 * drm_connector_update_privacy_screen() helpers for dealing with this.
 | 
						|
 */
 | 
						|
 | 
						|
#define to_drm_privacy_screen(dev) \
 | 
						|
	container_of(dev, struct drm_privacy_screen, dev)
 | 
						|
 | 
						|
static DEFINE_MUTEX(drm_privacy_screen_lookup_lock);
 | 
						|
static LIST_HEAD(drm_privacy_screen_lookup_list);
 | 
						|
 | 
						|
static DEFINE_MUTEX(drm_privacy_screen_devs_lock);
 | 
						|
static LIST_HEAD(drm_privacy_screen_devs);
 | 
						|
 | 
						|
/*** drm_privacy_screen_machine.h functions ***/
 | 
						|
 | 
						|
/**
 | 
						|
 * drm_privacy_screen_lookup_add - add an entry to the static privacy-screen
 | 
						|
 *    lookup list
 | 
						|
 * @lookup: lookup list entry to add
 | 
						|
 *
 | 
						|
 * Add an entry to the static privacy-screen lookup list. Note the
 | 
						|
 * &struct list_head which is part of the &struct drm_privacy_screen_lookup
 | 
						|
 * gets added to a list owned by the privacy-screen core. So the passed in
 | 
						|
 * &struct drm_privacy_screen_lookup must not be free-ed until it is removed
 | 
						|
 * from the lookup list by calling drm_privacy_screen_lookup_remove().
 | 
						|
 */
 | 
						|
void drm_privacy_screen_lookup_add(struct drm_privacy_screen_lookup *lookup)
 | 
						|
{
 | 
						|
	mutex_lock(&drm_privacy_screen_lookup_lock);
 | 
						|
	list_add(&lookup->list, &drm_privacy_screen_lookup_list);
 | 
						|
	mutex_unlock(&drm_privacy_screen_lookup_lock);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(drm_privacy_screen_lookup_add);
 | 
						|
 | 
						|
/**
 | 
						|
 * drm_privacy_screen_lookup_remove - remove an entry to the static
 | 
						|
 *    privacy-screen lookup list
 | 
						|
 * @lookup: lookup list entry to remove
 | 
						|
 *
 | 
						|
 * Remove an entry previously added with drm_privacy_screen_lookup_add()
 | 
						|
 * from the static privacy-screen lookup list.
 | 
						|
 */
 | 
						|
void drm_privacy_screen_lookup_remove(struct drm_privacy_screen_lookup *lookup)
 | 
						|
{
 | 
						|
	mutex_lock(&drm_privacy_screen_lookup_lock);
 | 
						|
	list_del(&lookup->list);
 | 
						|
	mutex_unlock(&drm_privacy_screen_lookup_lock);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(drm_privacy_screen_lookup_remove);
 | 
						|
 | 
						|
/*** drm_privacy_screen_consumer.h functions ***/
 | 
						|
 | 
						|
static struct drm_privacy_screen *drm_privacy_screen_get_by_name(
 | 
						|
	const char *name)
 | 
						|
{
 | 
						|
	struct drm_privacy_screen *priv;
 | 
						|
	struct device *dev = NULL;
 | 
						|
 | 
						|
	mutex_lock(&drm_privacy_screen_devs_lock);
 | 
						|
 | 
						|
	list_for_each_entry(priv, &drm_privacy_screen_devs, list) {
 | 
						|
		if (strcmp(dev_name(&priv->dev), name) == 0) {
 | 
						|
			dev = get_device(&priv->dev);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_unlock(&drm_privacy_screen_devs_lock);
 | 
						|
 | 
						|
	return dev ? to_drm_privacy_screen(dev) : NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * drm_privacy_screen_get - get a privacy-screen provider
 | 
						|
 * @dev: consumer-device for which to get a privacy-screen provider
 | 
						|
 * @con_id: (video)connector name for which to get a privacy-screen provider
 | 
						|
 *
 | 
						|
 * Get a privacy-screen provider for a privacy-screen attached to the
 | 
						|
 * display described by the @dev and @con_id parameters.
 | 
						|
 *
 | 
						|
 * Return:
 | 
						|
 * * A pointer to a &struct drm_privacy_screen on success.
 | 
						|
 * * ERR_PTR(-ENODEV) if no matching privacy-screen is found
 | 
						|
 * * ERR_PTR(-EPROBE_DEFER) if there is a matching privacy-screen,
 | 
						|
 *                          but it has not been registered yet.
 | 
						|
 */
 | 
						|
struct drm_privacy_screen *drm_privacy_screen_get(struct device *dev,
 | 
						|
						  const char *con_id)
 | 
						|
{
 | 
						|
	const char *dev_id = dev ? dev_name(dev) : NULL;
 | 
						|
	struct drm_privacy_screen_lookup *l;
 | 
						|
	struct drm_privacy_screen *priv;
 | 
						|
	const char *provider = NULL;
 | 
						|
	int match, best = -1;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * For now we only support using a static lookup table, which is
 | 
						|
	 * populated by the drm_privacy_screen_arch_init() call. This should
 | 
						|
	 * be extended with device-tree / fw_node lookup when support is added
 | 
						|
	 * for device-tree using hardware with a privacy-screen.
 | 
						|
	 *
 | 
						|
	 * The lookup algorithm was shamelessly taken from the clock
 | 
						|
	 * framework:
 | 
						|
	 *
 | 
						|
	 * We do slightly fuzzy matching here:
 | 
						|
	 *  An entry with a NULL ID is assumed to be a wildcard.
 | 
						|
	 *  If an entry has a device ID, it must match
 | 
						|
	 *  If an entry has a connection ID, it must match
 | 
						|
	 * Then we take the most specific entry - with the following order
 | 
						|
	 * of precedence: dev+con > dev only > con only.
 | 
						|
	 */
 | 
						|
	mutex_lock(&drm_privacy_screen_lookup_lock);
 | 
						|
 | 
						|
	list_for_each_entry(l, &drm_privacy_screen_lookup_list, list) {
 | 
						|
		match = 0;
 | 
						|
 | 
						|
		if (l->dev_id) {
 | 
						|
			if (!dev_id || strcmp(l->dev_id, dev_id))
 | 
						|
				continue;
 | 
						|
 | 
						|
			match += 2;
 | 
						|
		}
 | 
						|
 | 
						|
		if (l->con_id) {
 | 
						|
			if (!con_id || strcmp(l->con_id, con_id))
 | 
						|
				continue;
 | 
						|
 | 
						|
			match += 1;
 | 
						|
		}
 | 
						|
 | 
						|
		if (match > best) {
 | 
						|
			provider = l->provider;
 | 
						|
			best = match;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_unlock(&drm_privacy_screen_lookup_lock);
 | 
						|
 | 
						|
	if (!provider)
 | 
						|
		return ERR_PTR(-ENODEV);
 | 
						|
 | 
						|
	priv = drm_privacy_screen_get_by_name(provider);
 | 
						|
	if (!priv)
 | 
						|
		return ERR_PTR(-EPROBE_DEFER);
 | 
						|
 | 
						|
	return priv;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(drm_privacy_screen_get);
 | 
						|
 | 
						|
/**
 | 
						|
 * drm_privacy_screen_put - release a privacy-screen reference
 | 
						|
 * @priv: privacy screen reference to release
 | 
						|
 *
 | 
						|
 * Release a privacy-screen provider reference gotten through
 | 
						|
 * drm_privacy_screen_get(). May be called with a NULL or ERR_PTR,
 | 
						|
 * in which case it is a no-op.
 | 
						|
 */
 | 
						|
void drm_privacy_screen_put(struct drm_privacy_screen *priv)
 | 
						|
{
 | 
						|
	if (IS_ERR_OR_NULL(priv))
 | 
						|
		return;
 | 
						|
 | 
						|
	put_device(&priv->dev);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(drm_privacy_screen_put);
 | 
						|
 | 
						|
/**
 | 
						|
 * drm_privacy_screen_set_sw_state - set a privacy-screen's sw-state
 | 
						|
 * @priv: privacy screen to set the sw-state for
 | 
						|
 * @sw_state: new sw-state value to set
 | 
						|
 *
 | 
						|
 * Set the sw-state of a privacy screen. If the privacy-screen is not
 | 
						|
 * in a locked hw-state, then the actual and hw-state of the privacy-screen
 | 
						|
 * will be immediately updated to the new value. If the privacy-screen is
 | 
						|
 * in a locked hw-state, then the new sw-state will be remembered as the
 | 
						|
 * requested state to put the privacy-screen in when it becomes unlocked.
 | 
						|
 *
 | 
						|
 * Return: 0 on success, negative error code on failure.
 | 
						|
 */
 | 
						|
int drm_privacy_screen_set_sw_state(struct drm_privacy_screen *priv,
 | 
						|
				    enum drm_privacy_screen_status sw_state)
 | 
						|
{
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	mutex_lock(&priv->lock);
 | 
						|
 | 
						|
	if (!priv->ops) {
 | 
						|
		ret = -ENODEV;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * As per the DRM connector properties documentation, setting the
 | 
						|
	 * sw_state while the hw_state is locked is allowed. In this case
 | 
						|
	 * it is a no-op other then storing the new sw_state so that it
 | 
						|
	 * can be honored when the state gets unlocked.
 | 
						|
	 * Also skip the set if the hw already is in the desired state.
 | 
						|
	 */
 | 
						|
	if (priv->hw_state >= PRIVACY_SCREEN_DISABLED_LOCKED ||
 | 
						|
	    priv->hw_state == sw_state) {
 | 
						|
		priv->sw_state = sw_state;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = priv->ops->set_sw_state(priv, sw_state);
 | 
						|
out:
 | 
						|
	mutex_unlock(&priv->lock);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(drm_privacy_screen_set_sw_state);
 | 
						|
 | 
						|
/**
 | 
						|
 * drm_privacy_screen_get_state - get privacy-screen's current state
 | 
						|
 * @priv: privacy screen to get the state for
 | 
						|
 * @sw_state_ret: address where to store the privacy-screens current sw-state
 | 
						|
 * @hw_state_ret: address where to store the privacy-screens current hw-state
 | 
						|
 *
 | 
						|
 * Get the current state of a privacy-screen, both the sw-state and the
 | 
						|
 * hw-state.
 | 
						|
 */
 | 
						|
void drm_privacy_screen_get_state(struct drm_privacy_screen *priv,
 | 
						|
				  enum drm_privacy_screen_status *sw_state_ret,
 | 
						|
				  enum drm_privacy_screen_status *hw_state_ret)
 | 
						|
{
 | 
						|
	mutex_lock(&priv->lock);
 | 
						|
	*sw_state_ret = priv->sw_state;
 | 
						|
	*hw_state_ret = priv->hw_state;
 | 
						|
	mutex_unlock(&priv->lock);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(drm_privacy_screen_get_state);
 | 
						|
 | 
						|
/**
 | 
						|
 * drm_privacy_screen_register_notifier - register a notifier
 | 
						|
 * @priv: Privacy screen to register the notifier with
 | 
						|
 * @nb: Notifier-block for the notifier to register
 | 
						|
 *
 | 
						|
 * Register a notifier with the privacy-screen to be notified of changes made
 | 
						|
 * to the privacy-screen state from outside of the privacy-screen class.
 | 
						|
 * E.g. the state may be changed by the hardware itself in response to a
 | 
						|
 * hotkey press.
 | 
						|
 *
 | 
						|
 * The notifier is called with no locks held. The new hw_state and sw_state
 | 
						|
 * can be retrieved using the drm_privacy_screen_get_state() function.
 | 
						|
 * A pointer to the drm_privacy_screen's struct is passed as the ``void *data``
 | 
						|
 * argument of the notifier_block's notifier_call.
 | 
						|
 *
 | 
						|
 * The notifier will NOT be called when changes are made through
 | 
						|
 * drm_privacy_screen_set_sw_state(). It is only called for external changes.
 | 
						|
 *
 | 
						|
 * Return: 0 on success, negative error code on failure.
 | 
						|
 */
 | 
						|
int drm_privacy_screen_register_notifier(struct drm_privacy_screen *priv,
 | 
						|
					 struct notifier_block *nb)
 | 
						|
{
 | 
						|
	return blocking_notifier_chain_register(&priv->notifier_head, nb);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(drm_privacy_screen_register_notifier);
 | 
						|
 | 
						|
/**
 | 
						|
 * drm_privacy_screen_unregister_notifier - unregister a notifier
 | 
						|
 * @priv: Privacy screen to register the notifier with
 | 
						|
 * @nb: Notifier-block for the notifier to register
 | 
						|
 *
 | 
						|
 * Unregister a notifier registered with drm_privacy_screen_register_notifier().
 | 
						|
 *
 | 
						|
 * Return: 0 on success, negative error code on failure.
 | 
						|
 */
 | 
						|
int drm_privacy_screen_unregister_notifier(struct drm_privacy_screen *priv,
 | 
						|
					   struct notifier_block *nb)
 | 
						|
{
 | 
						|
	return blocking_notifier_chain_unregister(&priv->notifier_head, nb);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(drm_privacy_screen_unregister_notifier);
 | 
						|
 | 
						|
/*** drm_privacy_screen_driver.h functions ***/
 | 
						|
 | 
						|
static ssize_t sw_state_show(struct device *dev,
 | 
						|
			     struct device_attribute *attr, char *buf)
 | 
						|
{
 | 
						|
	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
 | 
						|
	const char * const sw_state_names[] = {
 | 
						|
		"Disabled",
 | 
						|
		"Enabled",
 | 
						|
	};
 | 
						|
	ssize_t ret;
 | 
						|
 | 
						|
	mutex_lock(&priv->lock);
 | 
						|
 | 
						|
	if (!priv->ops)
 | 
						|
		ret = -ENODEV;
 | 
						|
	else if (WARN_ON(priv->sw_state >= ARRAY_SIZE(sw_state_names)))
 | 
						|
		ret = -ENXIO;
 | 
						|
	else
 | 
						|
		ret = sprintf(buf, "%s\n", sw_state_names[priv->sw_state]);
 | 
						|
 | 
						|
	mutex_unlock(&priv->lock);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * RO: Do not allow setting the sw_state through sysfs, this MUST be done
 | 
						|
 * through the drm_properties on the drm_connector.
 | 
						|
 */
 | 
						|
static DEVICE_ATTR_RO(sw_state);
 | 
						|
 | 
						|
static ssize_t hw_state_show(struct device *dev,
 | 
						|
			     struct device_attribute *attr, char *buf)
 | 
						|
{
 | 
						|
	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
 | 
						|
	const char * const hw_state_names[] = {
 | 
						|
		"Disabled",
 | 
						|
		"Enabled",
 | 
						|
		"Disabled, locked",
 | 
						|
		"Enabled, locked",
 | 
						|
	};
 | 
						|
	ssize_t ret;
 | 
						|
 | 
						|
	mutex_lock(&priv->lock);
 | 
						|
 | 
						|
	if (!priv->ops)
 | 
						|
		ret = -ENODEV;
 | 
						|
	else if (WARN_ON(priv->hw_state >= ARRAY_SIZE(hw_state_names)))
 | 
						|
		ret = -ENXIO;
 | 
						|
	else
 | 
						|
		ret = sprintf(buf, "%s\n", hw_state_names[priv->hw_state]);
 | 
						|
 | 
						|
	mutex_unlock(&priv->lock);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
static DEVICE_ATTR_RO(hw_state);
 | 
						|
 | 
						|
static struct attribute *drm_privacy_screen_attrs[] = {
 | 
						|
	&dev_attr_sw_state.attr,
 | 
						|
	&dev_attr_hw_state.attr,
 | 
						|
	NULL
 | 
						|
};
 | 
						|
ATTRIBUTE_GROUPS(drm_privacy_screen);
 | 
						|
 | 
						|
static struct device_type drm_privacy_screen_type = {
 | 
						|
	.name = "privacy_screen",
 | 
						|
	.groups = drm_privacy_screen_groups,
 | 
						|
};
 | 
						|
 | 
						|
static void drm_privacy_screen_device_release(struct device *dev)
 | 
						|
{
 | 
						|
	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
 | 
						|
 | 
						|
	kfree(priv);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * drm_privacy_screen_register - register a privacy-screen
 | 
						|
 * @parent: parent-device for the privacy-screen
 | 
						|
 * @ops: &struct drm_privacy_screen_ops pointer with ops for the privacy-screen
 | 
						|
 * @data: Private data owned by the privacy screen provider
 | 
						|
 *
 | 
						|
 * Create and register a privacy-screen.
 | 
						|
 *
 | 
						|
 * Return:
 | 
						|
 * * A pointer to the created privacy-screen on success.
 | 
						|
 * * An ERR_PTR(errno) on failure.
 | 
						|
 */
 | 
						|
struct drm_privacy_screen *drm_privacy_screen_register(
 | 
						|
	struct device *parent, const struct drm_privacy_screen_ops *ops,
 | 
						|
	void *data)
 | 
						|
{
 | 
						|
	struct drm_privacy_screen *priv;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 | 
						|
	if (!priv)
 | 
						|
		return ERR_PTR(-ENOMEM);
 | 
						|
 | 
						|
	mutex_init(&priv->lock);
 | 
						|
	BLOCKING_INIT_NOTIFIER_HEAD(&priv->notifier_head);
 | 
						|
 | 
						|
	priv->dev.class = drm_class;
 | 
						|
	priv->dev.type = &drm_privacy_screen_type;
 | 
						|
	priv->dev.parent = parent;
 | 
						|
	priv->dev.release = drm_privacy_screen_device_release;
 | 
						|
	dev_set_name(&priv->dev, "privacy_screen-%s", dev_name(parent));
 | 
						|
	priv->drvdata = data;
 | 
						|
	priv->ops = ops;
 | 
						|
 | 
						|
	priv->ops->get_hw_state(priv);
 | 
						|
 | 
						|
	ret = device_register(&priv->dev);
 | 
						|
	if (ret) {
 | 
						|
		put_device(&priv->dev);
 | 
						|
		return ERR_PTR(ret);
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_lock(&drm_privacy_screen_devs_lock);
 | 
						|
	list_add(&priv->list, &drm_privacy_screen_devs);
 | 
						|
	mutex_unlock(&drm_privacy_screen_devs_lock);
 | 
						|
 | 
						|
	return priv;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(drm_privacy_screen_register);
 | 
						|
 | 
						|
/**
 | 
						|
 * drm_privacy_screen_unregister - unregister privacy-screen
 | 
						|
 * @priv: privacy-screen to unregister
 | 
						|
 *
 | 
						|
 * Unregister a privacy-screen registered with drm_privacy_screen_register().
 | 
						|
 * May be called with a NULL or ERR_PTR, in which case it is a no-op.
 | 
						|
 */
 | 
						|
void drm_privacy_screen_unregister(struct drm_privacy_screen *priv)
 | 
						|
{
 | 
						|
	if (IS_ERR_OR_NULL(priv))
 | 
						|
		return;
 | 
						|
 | 
						|
	mutex_lock(&drm_privacy_screen_devs_lock);
 | 
						|
	list_del(&priv->list);
 | 
						|
	mutex_unlock(&drm_privacy_screen_devs_lock);
 | 
						|
 | 
						|
	mutex_lock(&priv->lock);
 | 
						|
	priv->drvdata = NULL;
 | 
						|
	priv->ops = NULL;
 | 
						|
	mutex_unlock(&priv->lock);
 | 
						|
 | 
						|
	device_unregister(&priv->dev);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(drm_privacy_screen_unregister);
 | 
						|
 | 
						|
/**
 | 
						|
 * drm_privacy_screen_call_notifier_chain - notify consumers of state change
 | 
						|
 * @priv: Privacy screen to register the notifier with
 | 
						|
 *
 | 
						|
 * A privacy-screen provider driver can call this functions upon external
 | 
						|
 * changes to the privacy-screen state. E.g. the state may be changed by the
 | 
						|
 * hardware itself in response to a hotkey press.
 | 
						|
 * This function must be called without holding the privacy-screen lock.
 | 
						|
 * the driver must update sw_state and hw_state to reflect the new state before
 | 
						|
 * calling this function.
 | 
						|
 * The expected behavior from the driver upon receiving an external state
 | 
						|
 * change event is: 1. Take the lock; 2. Update sw_state and hw_state;
 | 
						|
 * 3. Release the lock. 4. Call drm_privacy_screen_call_notifier_chain().
 | 
						|
 */
 | 
						|
void drm_privacy_screen_call_notifier_chain(struct drm_privacy_screen *priv)
 | 
						|
{
 | 
						|
	blocking_notifier_call_chain(&priv->notifier_head, 0, priv);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(drm_privacy_screen_call_notifier_chain);
 |