162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 - 2021 Red Hat, Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci * Hans de Goede <hdegoede@redhat.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/list.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/mutex.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <drm/drm_privacy_screen_machine.h>
1662306a36Sopenharmony_ci#include <drm/drm_privacy_screen_consumer.h>
1762306a36Sopenharmony_ci#include <drm/drm_privacy_screen_driver.h>
1862306a36Sopenharmony_ci#include "drm_internal.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/**
2162306a36Sopenharmony_ci * DOC: overview
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * This class allows non KMS drivers, from e.g. drivers/platform/x86 to
2462306a36Sopenharmony_ci * register a privacy-screen device, which the KMS drivers can then use
2562306a36Sopenharmony_ci * to implement the standard privacy-screen properties, see
2662306a36Sopenharmony_ci * :ref:`Standard Connector Properties<standard_connector_properties>`.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * KMS drivers using a privacy-screen class device are advised to use the
2962306a36Sopenharmony_ci * drm_connector_attach_privacy_screen_provider() and
3062306a36Sopenharmony_ci * drm_connector_update_privacy_screen() helpers for dealing with this.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define to_drm_privacy_screen(dev) \
3462306a36Sopenharmony_ci	container_of(dev, struct drm_privacy_screen, dev)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic DEFINE_MUTEX(drm_privacy_screen_lookup_lock);
3762306a36Sopenharmony_cistatic LIST_HEAD(drm_privacy_screen_lookup_list);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic DEFINE_MUTEX(drm_privacy_screen_devs_lock);
4062306a36Sopenharmony_cistatic LIST_HEAD(drm_privacy_screen_devs);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*** drm_privacy_screen_machine.h functions ***/
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/**
4562306a36Sopenharmony_ci * drm_privacy_screen_lookup_add - add an entry to the static privacy-screen
4662306a36Sopenharmony_ci *    lookup list
4762306a36Sopenharmony_ci * @lookup: lookup list entry to add
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * Add an entry to the static privacy-screen lookup list. Note the
5062306a36Sopenharmony_ci * &struct list_head which is part of the &struct drm_privacy_screen_lookup
5162306a36Sopenharmony_ci * gets added to a list owned by the privacy-screen core. So the passed in
5262306a36Sopenharmony_ci * &struct drm_privacy_screen_lookup must not be free-ed until it is removed
5362306a36Sopenharmony_ci * from the lookup list by calling drm_privacy_screen_lookup_remove().
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_civoid drm_privacy_screen_lookup_add(struct drm_privacy_screen_lookup *lookup)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	mutex_lock(&drm_privacy_screen_lookup_lock);
5862306a36Sopenharmony_ci	list_add(&lookup->list, &drm_privacy_screen_lookup_list);
5962306a36Sopenharmony_ci	mutex_unlock(&drm_privacy_screen_lookup_lock);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_privacy_screen_lookup_add);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/**
6462306a36Sopenharmony_ci * drm_privacy_screen_lookup_remove - remove an entry to the static
6562306a36Sopenharmony_ci *    privacy-screen lookup list
6662306a36Sopenharmony_ci * @lookup: lookup list entry to remove
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * Remove an entry previously added with drm_privacy_screen_lookup_add()
6962306a36Sopenharmony_ci * from the static privacy-screen lookup list.
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_civoid drm_privacy_screen_lookup_remove(struct drm_privacy_screen_lookup *lookup)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	mutex_lock(&drm_privacy_screen_lookup_lock);
7462306a36Sopenharmony_ci	list_del(&lookup->list);
7562306a36Sopenharmony_ci	mutex_unlock(&drm_privacy_screen_lookup_lock);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_privacy_screen_lookup_remove);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*** drm_privacy_screen_consumer.h functions ***/
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic struct drm_privacy_screen *drm_privacy_screen_get_by_name(
8262306a36Sopenharmony_ci	const char *name)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct drm_privacy_screen *priv;
8562306a36Sopenharmony_ci	struct device *dev = NULL;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	mutex_lock(&drm_privacy_screen_devs_lock);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	list_for_each_entry(priv, &drm_privacy_screen_devs, list) {
9062306a36Sopenharmony_ci		if (strcmp(dev_name(&priv->dev), name) == 0) {
9162306a36Sopenharmony_ci			dev = get_device(&priv->dev);
9262306a36Sopenharmony_ci			break;
9362306a36Sopenharmony_ci		}
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	mutex_unlock(&drm_privacy_screen_devs_lock);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return dev ? to_drm_privacy_screen(dev) : NULL;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/**
10262306a36Sopenharmony_ci * drm_privacy_screen_get - get a privacy-screen provider
10362306a36Sopenharmony_ci * @dev: consumer-device for which to get a privacy-screen provider
10462306a36Sopenharmony_ci * @con_id: (video)connector name for which to get a privacy-screen provider
10562306a36Sopenharmony_ci *
10662306a36Sopenharmony_ci * Get a privacy-screen provider for a privacy-screen attached to the
10762306a36Sopenharmony_ci * display described by the @dev and @con_id parameters.
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * Return:
11062306a36Sopenharmony_ci * * A pointer to a &struct drm_privacy_screen on success.
11162306a36Sopenharmony_ci * * ERR_PTR(-ENODEV) if no matching privacy-screen is found
11262306a36Sopenharmony_ci * * ERR_PTR(-EPROBE_DEFER) if there is a matching privacy-screen,
11362306a36Sopenharmony_ci *                          but it has not been registered yet.
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_cistruct drm_privacy_screen *drm_privacy_screen_get(struct device *dev,
11662306a36Sopenharmony_ci						  const char *con_id)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	const char *dev_id = dev ? dev_name(dev) : NULL;
11962306a36Sopenharmony_ci	struct drm_privacy_screen_lookup *l;
12062306a36Sopenharmony_ci	struct drm_privacy_screen *priv;
12162306a36Sopenharmony_ci	const char *provider = NULL;
12262306a36Sopenharmony_ci	int match, best = -1;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/*
12562306a36Sopenharmony_ci	 * For now we only support using a static lookup table, which is
12662306a36Sopenharmony_ci	 * populated by the drm_privacy_screen_arch_init() call. This should
12762306a36Sopenharmony_ci	 * be extended with device-tree / fw_node lookup when support is added
12862306a36Sopenharmony_ci	 * for device-tree using hardware with a privacy-screen.
12962306a36Sopenharmony_ci	 *
13062306a36Sopenharmony_ci	 * The lookup algorithm was shamelessly taken from the clock
13162306a36Sopenharmony_ci	 * framework:
13262306a36Sopenharmony_ci	 *
13362306a36Sopenharmony_ci	 * We do slightly fuzzy matching here:
13462306a36Sopenharmony_ci	 *  An entry with a NULL ID is assumed to be a wildcard.
13562306a36Sopenharmony_ci	 *  If an entry has a device ID, it must match
13662306a36Sopenharmony_ci	 *  If an entry has a connection ID, it must match
13762306a36Sopenharmony_ci	 * Then we take the most specific entry - with the following order
13862306a36Sopenharmony_ci	 * of precedence: dev+con > dev only > con only.
13962306a36Sopenharmony_ci	 */
14062306a36Sopenharmony_ci	mutex_lock(&drm_privacy_screen_lookup_lock);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	list_for_each_entry(l, &drm_privacy_screen_lookup_list, list) {
14362306a36Sopenharmony_ci		match = 0;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		if (l->dev_id) {
14662306a36Sopenharmony_ci			if (!dev_id || strcmp(l->dev_id, dev_id))
14762306a36Sopenharmony_ci				continue;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci			match += 2;
15062306a36Sopenharmony_ci		}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		if (l->con_id) {
15362306a36Sopenharmony_ci			if (!con_id || strcmp(l->con_id, con_id))
15462306a36Sopenharmony_ci				continue;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci			match += 1;
15762306a36Sopenharmony_ci		}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci		if (match > best) {
16062306a36Sopenharmony_ci			provider = l->provider;
16162306a36Sopenharmony_ci			best = match;
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	mutex_unlock(&drm_privacy_screen_lookup_lock);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (!provider)
16862306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	priv = drm_privacy_screen_get_by_name(provider);
17162306a36Sopenharmony_ci	if (!priv)
17262306a36Sopenharmony_ci		return ERR_PTR(-EPROBE_DEFER);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return priv;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_privacy_screen_get);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/**
17962306a36Sopenharmony_ci * drm_privacy_screen_put - release a privacy-screen reference
18062306a36Sopenharmony_ci * @priv: privacy screen reference to release
18162306a36Sopenharmony_ci *
18262306a36Sopenharmony_ci * Release a privacy-screen provider reference gotten through
18362306a36Sopenharmony_ci * drm_privacy_screen_get(). May be called with a NULL or ERR_PTR,
18462306a36Sopenharmony_ci * in which case it is a no-op.
18562306a36Sopenharmony_ci */
18662306a36Sopenharmony_civoid drm_privacy_screen_put(struct drm_privacy_screen *priv)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(priv))
18962306a36Sopenharmony_ci		return;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	put_device(&priv->dev);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_privacy_screen_put);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/**
19662306a36Sopenharmony_ci * drm_privacy_screen_set_sw_state - set a privacy-screen's sw-state
19762306a36Sopenharmony_ci * @priv: privacy screen to set the sw-state for
19862306a36Sopenharmony_ci * @sw_state: new sw-state value to set
19962306a36Sopenharmony_ci *
20062306a36Sopenharmony_ci * Set the sw-state of a privacy screen. If the privacy-screen is not
20162306a36Sopenharmony_ci * in a locked hw-state, then the actual and hw-state of the privacy-screen
20262306a36Sopenharmony_ci * will be immediately updated to the new value. If the privacy-screen is
20362306a36Sopenharmony_ci * in a locked hw-state, then the new sw-state will be remembered as the
20462306a36Sopenharmony_ci * requested state to put the privacy-screen in when it becomes unlocked.
20562306a36Sopenharmony_ci *
20662306a36Sopenharmony_ci * Return: 0 on success, negative error code on failure.
20762306a36Sopenharmony_ci */
20862306a36Sopenharmony_ciint drm_privacy_screen_set_sw_state(struct drm_privacy_screen *priv,
20962306a36Sopenharmony_ci				    enum drm_privacy_screen_status sw_state)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	int ret = 0;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	mutex_lock(&priv->lock);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (!priv->ops) {
21662306a36Sopenharmony_ci		ret = -ENODEV;
21762306a36Sopenharmony_ci		goto out;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/*
22162306a36Sopenharmony_ci	 * As per the DRM connector properties documentation, setting the
22262306a36Sopenharmony_ci	 * sw_state while the hw_state is locked is allowed. In this case
22362306a36Sopenharmony_ci	 * it is a no-op other then storing the new sw_state so that it
22462306a36Sopenharmony_ci	 * can be honored when the state gets unlocked.
22562306a36Sopenharmony_ci	 * Also skip the set if the hw already is in the desired state.
22662306a36Sopenharmony_ci	 */
22762306a36Sopenharmony_ci	if (priv->hw_state >= PRIVACY_SCREEN_DISABLED_LOCKED ||
22862306a36Sopenharmony_ci	    priv->hw_state == sw_state) {
22962306a36Sopenharmony_ci		priv->sw_state = sw_state;
23062306a36Sopenharmony_ci		goto out;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	ret = priv->ops->set_sw_state(priv, sw_state);
23462306a36Sopenharmony_ciout:
23562306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
23662306a36Sopenharmony_ci	return ret;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_privacy_screen_set_sw_state);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/**
24162306a36Sopenharmony_ci * drm_privacy_screen_get_state - get privacy-screen's current state
24262306a36Sopenharmony_ci * @priv: privacy screen to get the state for
24362306a36Sopenharmony_ci * @sw_state_ret: address where to store the privacy-screens current sw-state
24462306a36Sopenharmony_ci * @hw_state_ret: address where to store the privacy-screens current hw-state
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * Get the current state of a privacy-screen, both the sw-state and the
24762306a36Sopenharmony_ci * hw-state.
24862306a36Sopenharmony_ci */
24962306a36Sopenharmony_civoid drm_privacy_screen_get_state(struct drm_privacy_screen *priv,
25062306a36Sopenharmony_ci				  enum drm_privacy_screen_status *sw_state_ret,
25162306a36Sopenharmony_ci				  enum drm_privacy_screen_status *hw_state_ret)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	mutex_lock(&priv->lock);
25462306a36Sopenharmony_ci	*sw_state_ret = priv->sw_state;
25562306a36Sopenharmony_ci	*hw_state_ret = priv->hw_state;
25662306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_privacy_screen_get_state);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/**
26162306a36Sopenharmony_ci * drm_privacy_screen_register_notifier - register a notifier
26262306a36Sopenharmony_ci * @priv: Privacy screen to register the notifier with
26362306a36Sopenharmony_ci * @nb: Notifier-block for the notifier to register
26462306a36Sopenharmony_ci *
26562306a36Sopenharmony_ci * Register a notifier with the privacy-screen to be notified of changes made
26662306a36Sopenharmony_ci * to the privacy-screen state from outside of the privacy-screen class.
26762306a36Sopenharmony_ci * E.g. the state may be changed by the hardware itself in response to a
26862306a36Sopenharmony_ci * hotkey press.
26962306a36Sopenharmony_ci *
27062306a36Sopenharmony_ci * The notifier is called with no locks held. The new hw_state and sw_state
27162306a36Sopenharmony_ci * can be retrieved using the drm_privacy_screen_get_state() function.
27262306a36Sopenharmony_ci * A pointer to the drm_privacy_screen's struct is passed as the ``void *data``
27362306a36Sopenharmony_ci * argument of the notifier_block's notifier_call.
27462306a36Sopenharmony_ci *
27562306a36Sopenharmony_ci * The notifier will NOT be called when changes are made through
27662306a36Sopenharmony_ci * drm_privacy_screen_set_sw_state(). It is only called for external changes.
27762306a36Sopenharmony_ci *
27862306a36Sopenharmony_ci * Return: 0 on success, negative error code on failure.
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_ciint drm_privacy_screen_register_notifier(struct drm_privacy_screen *priv,
28162306a36Sopenharmony_ci					 struct notifier_block *nb)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	return blocking_notifier_chain_register(&priv->notifier_head, nb);
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_privacy_screen_register_notifier);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci/**
28862306a36Sopenharmony_ci * drm_privacy_screen_unregister_notifier - unregister a notifier
28962306a36Sopenharmony_ci * @priv: Privacy screen to register the notifier with
29062306a36Sopenharmony_ci * @nb: Notifier-block for the notifier to register
29162306a36Sopenharmony_ci *
29262306a36Sopenharmony_ci * Unregister a notifier registered with drm_privacy_screen_register_notifier().
29362306a36Sopenharmony_ci *
29462306a36Sopenharmony_ci * Return: 0 on success, negative error code on failure.
29562306a36Sopenharmony_ci */
29662306a36Sopenharmony_ciint drm_privacy_screen_unregister_notifier(struct drm_privacy_screen *priv,
29762306a36Sopenharmony_ci					   struct notifier_block *nb)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	return blocking_notifier_chain_unregister(&priv->notifier_head, nb);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_privacy_screen_unregister_notifier);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/*** drm_privacy_screen_driver.h functions ***/
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic ssize_t sw_state_show(struct device *dev,
30662306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
30962306a36Sopenharmony_ci	const char * const sw_state_names[] = {
31062306a36Sopenharmony_ci		"Disabled",
31162306a36Sopenharmony_ci		"Enabled",
31262306a36Sopenharmony_ci	};
31362306a36Sopenharmony_ci	ssize_t ret;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	mutex_lock(&priv->lock);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (!priv->ops)
31862306a36Sopenharmony_ci		ret = -ENODEV;
31962306a36Sopenharmony_ci	else if (WARN_ON(priv->sw_state >= ARRAY_SIZE(sw_state_names)))
32062306a36Sopenharmony_ci		ret = -ENXIO;
32162306a36Sopenharmony_ci	else
32262306a36Sopenharmony_ci		ret = sprintf(buf, "%s\n", sw_state_names[priv->sw_state]);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
32562306a36Sopenharmony_ci	return ret;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci/*
32862306a36Sopenharmony_ci * RO: Do not allow setting the sw_state through sysfs, this MUST be done
32962306a36Sopenharmony_ci * through the drm_properties on the drm_connector.
33062306a36Sopenharmony_ci */
33162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sw_state);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic ssize_t hw_state_show(struct device *dev,
33462306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
33762306a36Sopenharmony_ci	const char * const hw_state_names[] = {
33862306a36Sopenharmony_ci		"Disabled",
33962306a36Sopenharmony_ci		"Enabled",
34062306a36Sopenharmony_ci		"Disabled, locked",
34162306a36Sopenharmony_ci		"Enabled, locked",
34262306a36Sopenharmony_ci	};
34362306a36Sopenharmony_ci	ssize_t ret;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	mutex_lock(&priv->lock);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (!priv->ops)
34862306a36Sopenharmony_ci		ret = -ENODEV;
34962306a36Sopenharmony_ci	else if (WARN_ON(priv->hw_state >= ARRAY_SIZE(hw_state_names)))
35062306a36Sopenharmony_ci		ret = -ENXIO;
35162306a36Sopenharmony_ci	else
35262306a36Sopenharmony_ci		ret = sprintf(buf, "%s\n", hw_state_names[priv->hw_state]);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
35562306a36Sopenharmony_ci	return ret;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(hw_state);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic struct attribute *drm_privacy_screen_attrs[] = {
36062306a36Sopenharmony_ci	&dev_attr_sw_state.attr,
36162306a36Sopenharmony_ci	&dev_attr_hw_state.attr,
36262306a36Sopenharmony_ci	NULL
36362306a36Sopenharmony_ci};
36462306a36Sopenharmony_ciATTRIBUTE_GROUPS(drm_privacy_screen);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic struct device_type drm_privacy_screen_type = {
36762306a36Sopenharmony_ci	.name = "privacy_screen",
36862306a36Sopenharmony_ci	.groups = drm_privacy_screen_groups,
36962306a36Sopenharmony_ci};
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic void drm_privacy_screen_device_release(struct device *dev)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	kfree(priv);
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/**
37962306a36Sopenharmony_ci * drm_privacy_screen_register - register a privacy-screen
38062306a36Sopenharmony_ci * @parent: parent-device for the privacy-screen
38162306a36Sopenharmony_ci * @ops: &struct drm_privacy_screen_ops pointer with ops for the privacy-screen
38262306a36Sopenharmony_ci * @data: Private data owned by the privacy screen provider
38362306a36Sopenharmony_ci *
38462306a36Sopenharmony_ci * Create and register a privacy-screen.
38562306a36Sopenharmony_ci *
38662306a36Sopenharmony_ci * Return:
38762306a36Sopenharmony_ci * * A pointer to the created privacy-screen on success.
38862306a36Sopenharmony_ci * * An ERR_PTR(errno) on failure.
38962306a36Sopenharmony_ci */
39062306a36Sopenharmony_cistruct drm_privacy_screen *drm_privacy_screen_register(
39162306a36Sopenharmony_ci	struct device *parent, const struct drm_privacy_screen_ops *ops,
39262306a36Sopenharmony_ci	void *data)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct drm_privacy_screen *priv;
39562306a36Sopenharmony_ci	int ret;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
39862306a36Sopenharmony_ci	if (!priv)
39962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	mutex_init(&priv->lock);
40262306a36Sopenharmony_ci	BLOCKING_INIT_NOTIFIER_HEAD(&priv->notifier_head);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	priv->dev.class = drm_class;
40562306a36Sopenharmony_ci	priv->dev.type = &drm_privacy_screen_type;
40662306a36Sopenharmony_ci	priv->dev.parent = parent;
40762306a36Sopenharmony_ci	priv->dev.release = drm_privacy_screen_device_release;
40862306a36Sopenharmony_ci	dev_set_name(&priv->dev, "privacy_screen-%s", dev_name(parent));
40962306a36Sopenharmony_ci	priv->drvdata = data;
41062306a36Sopenharmony_ci	priv->ops = ops;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	priv->ops->get_hw_state(priv);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	ret = device_register(&priv->dev);
41562306a36Sopenharmony_ci	if (ret) {
41662306a36Sopenharmony_ci		put_device(&priv->dev);
41762306a36Sopenharmony_ci		return ERR_PTR(ret);
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	mutex_lock(&drm_privacy_screen_devs_lock);
42162306a36Sopenharmony_ci	list_add(&priv->list, &drm_privacy_screen_devs);
42262306a36Sopenharmony_ci	mutex_unlock(&drm_privacy_screen_devs_lock);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return priv;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_privacy_screen_register);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci/**
42962306a36Sopenharmony_ci * drm_privacy_screen_unregister - unregister privacy-screen
43062306a36Sopenharmony_ci * @priv: privacy-screen to unregister
43162306a36Sopenharmony_ci *
43262306a36Sopenharmony_ci * Unregister a privacy-screen registered with drm_privacy_screen_register().
43362306a36Sopenharmony_ci * May be called with a NULL or ERR_PTR, in which case it is a no-op.
43462306a36Sopenharmony_ci */
43562306a36Sopenharmony_civoid drm_privacy_screen_unregister(struct drm_privacy_screen *priv)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(priv))
43862306a36Sopenharmony_ci		return;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	mutex_lock(&drm_privacy_screen_devs_lock);
44162306a36Sopenharmony_ci	list_del(&priv->list);
44262306a36Sopenharmony_ci	mutex_unlock(&drm_privacy_screen_devs_lock);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	mutex_lock(&priv->lock);
44562306a36Sopenharmony_ci	priv->drvdata = NULL;
44662306a36Sopenharmony_ci	priv->ops = NULL;
44762306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	device_unregister(&priv->dev);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_privacy_screen_unregister);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/**
45462306a36Sopenharmony_ci * drm_privacy_screen_call_notifier_chain - notify consumers of state change
45562306a36Sopenharmony_ci * @priv: Privacy screen to register the notifier with
45662306a36Sopenharmony_ci *
45762306a36Sopenharmony_ci * A privacy-screen provider driver can call this functions upon external
45862306a36Sopenharmony_ci * changes to the privacy-screen state. E.g. the state may be changed by the
45962306a36Sopenharmony_ci * hardware itself in response to a hotkey press.
46062306a36Sopenharmony_ci * This function must be called without holding the privacy-screen lock.
46162306a36Sopenharmony_ci * the driver must update sw_state and hw_state to reflect the new state before
46262306a36Sopenharmony_ci * calling this function.
46362306a36Sopenharmony_ci * The expected behavior from the driver upon receiving an external state
46462306a36Sopenharmony_ci * change event is: 1. Take the lock; 2. Update sw_state and hw_state;
46562306a36Sopenharmony_ci * 3. Release the lock. 4. Call drm_privacy_screen_call_notifier_chain().
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_civoid drm_privacy_screen_call_notifier_chain(struct drm_privacy_screen *priv)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	blocking_notifier_call_chain(&priv->notifier_head, 0, priv);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_privacy_screen_call_notifier_chain);
472