xref: /openbmc/linux/drivers/gpu/drm/drm_privacy_screen.c (revision a1a98689301b9af0313e4c1ba44558e8b67ff76e)
1*a1a98689SHans de Goede // SPDX-License-Identifier: MIT
2*a1a98689SHans de Goede /*
3*a1a98689SHans de Goede  * Copyright (C) 2020 - 2021 Red Hat, Inc.
4*a1a98689SHans de Goede  *
5*a1a98689SHans de Goede  * Authors:
6*a1a98689SHans de Goede  * Hans de Goede <hdegoede@redhat.com>
7*a1a98689SHans de Goede  */
8*a1a98689SHans de Goede 
9*a1a98689SHans de Goede #include <linux/device.h>
10*a1a98689SHans de Goede #include <linux/kernel.h>
11*a1a98689SHans de Goede #include <linux/list.h>
12*a1a98689SHans de Goede #include <linux/module.h>
13*a1a98689SHans de Goede #include <linux/mutex.h>
14*a1a98689SHans de Goede #include <linux/slab.h>
15*a1a98689SHans de Goede #include <drm/drm_privacy_screen_machine.h>
16*a1a98689SHans de Goede #include <drm/drm_privacy_screen_consumer.h>
17*a1a98689SHans de Goede #include <drm/drm_privacy_screen_driver.h>
18*a1a98689SHans de Goede #include "drm_internal.h"
19*a1a98689SHans de Goede 
20*a1a98689SHans de Goede /**
21*a1a98689SHans de Goede  * DOC: overview
22*a1a98689SHans de Goede  *
23*a1a98689SHans de Goede  * This class allows non KMS drivers, from e.g. drivers/platform/x86 to
24*a1a98689SHans de Goede  * register a privacy-screen device, which the KMS drivers can then use
25*a1a98689SHans de Goede  * to implement the standard privacy-screen properties, see
26*a1a98689SHans de Goede  * :ref:`Standard Connector Properties<standard_connector_properties>`.
27*a1a98689SHans de Goede  *
28*a1a98689SHans de Goede  * KMS drivers using a privacy-screen class device are advised to use the
29*a1a98689SHans de Goede  * drm_connector_attach_privacy_screen_provider() and
30*a1a98689SHans de Goede  * drm_connector_update_privacy_screen() helpers for dealing with this.
31*a1a98689SHans de Goede  */
32*a1a98689SHans de Goede 
33*a1a98689SHans de Goede #define to_drm_privacy_screen(dev) \
34*a1a98689SHans de Goede 	container_of(dev, struct drm_privacy_screen, dev)
35*a1a98689SHans de Goede 
36*a1a98689SHans de Goede static DEFINE_MUTEX(drm_privacy_screen_lookup_lock);
37*a1a98689SHans de Goede static LIST_HEAD(drm_privacy_screen_lookup_list);
38*a1a98689SHans de Goede 
39*a1a98689SHans de Goede static DEFINE_MUTEX(drm_privacy_screen_devs_lock);
40*a1a98689SHans de Goede static LIST_HEAD(drm_privacy_screen_devs);
41*a1a98689SHans de Goede 
42*a1a98689SHans de Goede /*** drm_privacy_screen_machine.h functions ***/
43*a1a98689SHans de Goede 
44*a1a98689SHans de Goede /**
45*a1a98689SHans de Goede  * drm_privacy_screen_lookup_add - add an entry to the static privacy-screen
46*a1a98689SHans de Goede  *    lookup list
47*a1a98689SHans de Goede  * @lookup: lookup list entry to add
48*a1a98689SHans de Goede  *
49*a1a98689SHans de Goede  * Add an entry to the static privacy-screen lookup list. Note the
50*a1a98689SHans de Goede  * &struct list_head which is part of the &struct drm_privacy_screen_lookup
51*a1a98689SHans de Goede  * gets added to a list owned by the privacy-screen core. So the passed in
52*a1a98689SHans de Goede  * &struct drm_privacy_screen_lookup must not be free-ed until it is removed
53*a1a98689SHans de Goede  * from the lookup list by calling drm_privacy_screen_lookup_remove().
54*a1a98689SHans de Goede  */
55*a1a98689SHans de Goede void drm_privacy_screen_lookup_add(struct drm_privacy_screen_lookup *lookup)
56*a1a98689SHans de Goede {
57*a1a98689SHans de Goede 	mutex_lock(&drm_privacy_screen_lookup_lock);
58*a1a98689SHans de Goede 	list_add(&lookup->list, &drm_privacy_screen_lookup_list);
59*a1a98689SHans de Goede 	mutex_unlock(&drm_privacy_screen_lookup_lock);
60*a1a98689SHans de Goede }
61*a1a98689SHans de Goede EXPORT_SYMBOL(drm_privacy_screen_lookup_add);
62*a1a98689SHans de Goede 
63*a1a98689SHans de Goede /**
64*a1a98689SHans de Goede  * drm_privacy_screen_lookup_remove - remove an entry to the static
65*a1a98689SHans de Goede  *    privacy-screen lookup list
66*a1a98689SHans de Goede  * @lookup: lookup list entry to remove
67*a1a98689SHans de Goede  *
68*a1a98689SHans de Goede  * Remove an entry previously added with drm_privacy_screen_lookup_add()
69*a1a98689SHans de Goede  * from the static privacy-screen lookup list.
70*a1a98689SHans de Goede  */
71*a1a98689SHans de Goede void drm_privacy_screen_lookup_remove(struct drm_privacy_screen_lookup *lookup)
72*a1a98689SHans de Goede {
73*a1a98689SHans de Goede 	mutex_lock(&drm_privacy_screen_lookup_lock);
74*a1a98689SHans de Goede 	list_del(&lookup->list);
75*a1a98689SHans de Goede 	mutex_unlock(&drm_privacy_screen_lookup_lock);
76*a1a98689SHans de Goede }
77*a1a98689SHans de Goede EXPORT_SYMBOL(drm_privacy_screen_lookup_remove);
78*a1a98689SHans de Goede 
79*a1a98689SHans de Goede /*** drm_privacy_screen_consumer.h functions ***/
80*a1a98689SHans de Goede 
81*a1a98689SHans de Goede static struct drm_privacy_screen *drm_privacy_screen_get_by_name(
82*a1a98689SHans de Goede 	const char *name)
83*a1a98689SHans de Goede {
84*a1a98689SHans de Goede 	struct drm_privacy_screen *priv;
85*a1a98689SHans de Goede 	struct device *dev = NULL;
86*a1a98689SHans de Goede 
87*a1a98689SHans de Goede 	mutex_lock(&drm_privacy_screen_devs_lock);
88*a1a98689SHans de Goede 
89*a1a98689SHans de Goede 	list_for_each_entry(priv, &drm_privacy_screen_devs, list) {
90*a1a98689SHans de Goede 		if (strcmp(dev_name(&priv->dev), name) == 0) {
91*a1a98689SHans de Goede 			dev = get_device(&priv->dev);
92*a1a98689SHans de Goede 			break;
93*a1a98689SHans de Goede 		}
94*a1a98689SHans de Goede 	}
95*a1a98689SHans de Goede 
96*a1a98689SHans de Goede 	mutex_unlock(&drm_privacy_screen_devs_lock);
97*a1a98689SHans de Goede 
98*a1a98689SHans de Goede 	return dev ? to_drm_privacy_screen(dev) : NULL;
99*a1a98689SHans de Goede }
100*a1a98689SHans de Goede 
101*a1a98689SHans de Goede /**
102*a1a98689SHans de Goede  * drm_privacy_screen_get - get a privacy-screen provider
103*a1a98689SHans de Goede  * @dev: consumer-device for which to get a privacy-screen provider
104*a1a98689SHans de Goede  * @con_id: (video)connector name for which to get a privacy-screen provider
105*a1a98689SHans de Goede  *
106*a1a98689SHans de Goede  * Get a privacy-screen provider for a privacy-screen attached to the
107*a1a98689SHans de Goede  * display described by the @dev and @con_id parameters.
108*a1a98689SHans de Goede  *
109*a1a98689SHans de Goede  * Return:
110*a1a98689SHans de Goede  * * A pointer to a &struct drm_privacy_screen on success.
111*a1a98689SHans de Goede  * * ERR_PTR(-ENODEV) if no matching privacy-screen is found
112*a1a98689SHans de Goede  * * ERR_PTR(-EPROBE_DEFER) if there is a matching privacy-screen,
113*a1a98689SHans de Goede  *                          but it has not been registered yet.
114*a1a98689SHans de Goede  */
115*a1a98689SHans de Goede struct drm_privacy_screen *drm_privacy_screen_get(struct device *dev,
116*a1a98689SHans de Goede 						  const char *con_id)
117*a1a98689SHans de Goede {
118*a1a98689SHans de Goede 	const char *dev_id = dev ? dev_name(dev) : NULL;
119*a1a98689SHans de Goede 	struct drm_privacy_screen_lookup *l;
120*a1a98689SHans de Goede 	struct drm_privacy_screen *priv;
121*a1a98689SHans de Goede 	const char *provider = NULL;
122*a1a98689SHans de Goede 	int match, best = -1;
123*a1a98689SHans de Goede 
124*a1a98689SHans de Goede 	/*
125*a1a98689SHans de Goede 	 * For now we only support using a static lookup table, which is
126*a1a98689SHans de Goede 	 * populated by the drm_privacy_screen_arch_init() call. This should
127*a1a98689SHans de Goede 	 * be extended with device-tree / fw_node lookup when support is added
128*a1a98689SHans de Goede 	 * for device-tree using hardware with a privacy-screen.
129*a1a98689SHans de Goede 	 *
130*a1a98689SHans de Goede 	 * The lookup algorithm was shamelessly taken from the clock
131*a1a98689SHans de Goede 	 * framework:
132*a1a98689SHans de Goede 	 *
133*a1a98689SHans de Goede 	 * We do slightly fuzzy matching here:
134*a1a98689SHans de Goede 	 *  An entry with a NULL ID is assumed to be a wildcard.
135*a1a98689SHans de Goede 	 *  If an entry has a device ID, it must match
136*a1a98689SHans de Goede 	 *  If an entry has a connection ID, it must match
137*a1a98689SHans de Goede 	 * Then we take the most specific entry - with the following order
138*a1a98689SHans de Goede 	 * of precedence: dev+con > dev only > con only.
139*a1a98689SHans de Goede 	 */
140*a1a98689SHans de Goede 	mutex_lock(&drm_privacy_screen_lookup_lock);
141*a1a98689SHans de Goede 
142*a1a98689SHans de Goede 	list_for_each_entry(l, &drm_privacy_screen_lookup_list, list) {
143*a1a98689SHans de Goede 		match = 0;
144*a1a98689SHans de Goede 
145*a1a98689SHans de Goede 		if (l->dev_id) {
146*a1a98689SHans de Goede 			if (!dev_id || strcmp(l->dev_id, dev_id))
147*a1a98689SHans de Goede 				continue;
148*a1a98689SHans de Goede 
149*a1a98689SHans de Goede 			match += 2;
150*a1a98689SHans de Goede 		}
151*a1a98689SHans de Goede 
152*a1a98689SHans de Goede 		if (l->con_id) {
153*a1a98689SHans de Goede 			if (!con_id || strcmp(l->con_id, con_id))
154*a1a98689SHans de Goede 				continue;
155*a1a98689SHans de Goede 
156*a1a98689SHans de Goede 			match += 1;
157*a1a98689SHans de Goede 		}
158*a1a98689SHans de Goede 
159*a1a98689SHans de Goede 		if (match > best) {
160*a1a98689SHans de Goede 			provider = l->provider;
161*a1a98689SHans de Goede 			best = match;
162*a1a98689SHans de Goede 		}
163*a1a98689SHans de Goede 	}
164*a1a98689SHans de Goede 
165*a1a98689SHans de Goede 	mutex_unlock(&drm_privacy_screen_lookup_lock);
166*a1a98689SHans de Goede 
167*a1a98689SHans de Goede 	if (!provider)
168*a1a98689SHans de Goede 		return ERR_PTR(-ENODEV);
169*a1a98689SHans de Goede 
170*a1a98689SHans de Goede 	priv = drm_privacy_screen_get_by_name(provider);
171*a1a98689SHans de Goede 	if (!priv)
172*a1a98689SHans de Goede 		return ERR_PTR(-EPROBE_DEFER);
173*a1a98689SHans de Goede 
174*a1a98689SHans de Goede 	return priv;
175*a1a98689SHans de Goede }
176*a1a98689SHans de Goede EXPORT_SYMBOL(drm_privacy_screen_get);
177*a1a98689SHans de Goede 
178*a1a98689SHans de Goede /**
179*a1a98689SHans de Goede  * drm_privacy_screen_put - release a privacy-screen reference
180*a1a98689SHans de Goede  * @priv: privacy screen reference to release
181*a1a98689SHans de Goede  *
182*a1a98689SHans de Goede  * Release a privacy-screen provider reference gotten through
183*a1a98689SHans de Goede  * drm_privacy_screen_get(). May be called with a NULL or ERR_PTR,
184*a1a98689SHans de Goede  * in which case it is a no-op.
185*a1a98689SHans de Goede  */
186*a1a98689SHans de Goede void drm_privacy_screen_put(struct drm_privacy_screen *priv)
187*a1a98689SHans de Goede {
188*a1a98689SHans de Goede 	if (IS_ERR_OR_NULL(priv))
189*a1a98689SHans de Goede 		return;
190*a1a98689SHans de Goede 
191*a1a98689SHans de Goede 	put_device(&priv->dev);
192*a1a98689SHans de Goede }
193*a1a98689SHans de Goede EXPORT_SYMBOL(drm_privacy_screen_put);
194*a1a98689SHans de Goede 
195*a1a98689SHans de Goede /**
196*a1a98689SHans de Goede  * drm_privacy_screen_set_sw_state - set a privacy-screen's sw-state
197*a1a98689SHans de Goede  * @priv: privacy screen to set the sw-state for
198*a1a98689SHans de Goede  * @sw_state: new sw-state value to set
199*a1a98689SHans de Goede  *
200*a1a98689SHans de Goede  * Set the sw-state of a privacy screen. If the privacy-screen is not
201*a1a98689SHans de Goede  * in a locked hw-state, then the actual and hw-state of the privacy-screen
202*a1a98689SHans de Goede  * will be immediately updated to the new value. If the privacy-screen is
203*a1a98689SHans de Goede  * in a locked hw-state, then the new sw-state will be remembered as the
204*a1a98689SHans de Goede  * requested state to put the privacy-screen in when it becomes unlocked.
205*a1a98689SHans de Goede  *
206*a1a98689SHans de Goede  * Return: 0 on success, negative error code on failure.
207*a1a98689SHans de Goede  */
208*a1a98689SHans de Goede int drm_privacy_screen_set_sw_state(struct drm_privacy_screen *priv,
209*a1a98689SHans de Goede 				    enum drm_privacy_screen_status sw_state)
210*a1a98689SHans de Goede {
211*a1a98689SHans de Goede 	int ret = 0;
212*a1a98689SHans de Goede 
213*a1a98689SHans de Goede 	mutex_lock(&priv->lock);
214*a1a98689SHans de Goede 
215*a1a98689SHans de Goede 	if (!priv->ops) {
216*a1a98689SHans de Goede 		ret = -ENODEV;
217*a1a98689SHans de Goede 		goto out;
218*a1a98689SHans de Goede 	}
219*a1a98689SHans de Goede 
220*a1a98689SHans de Goede 	/*
221*a1a98689SHans de Goede 	 * As per the DRM connector properties documentation, setting the
222*a1a98689SHans de Goede 	 * sw_state while the hw_state is locked is allowed. In this case
223*a1a98689SHans de Goede 	 * it is a no-op other then storing the new sw_state so that it
224*a1a98689SHans de Goede 	 * can be honored when the state gets unlocked.
225*a1a98689SHans de Goede 	 * Also skip the set if the hw already is in the desired state.
226*a1a98689SHans de Goede 	 */
227*a1a98689SHans de Goede 	if (priv->hw_state >= PRIVACY_SCREEN_DISABLED_LOCKED ||
228*a1a98689SHans de Goede 	    priv->hw_state == sw_state) {
229*a1a98689SHans de Goede 		priv->sw_state = sw_state;
230*a1a98689SHans de Goede 		goto out;
231*a1a98689SHans de Goede 	}
232*a1a98689SHans de Goede 
233*a1a98689SHans de Goede 	ret = priv->ops->set_sw_state(priv, sw_state);
234*a1a98689SHans de Goede out:
235*a1a98689SHans de Goede 	mutex_unlock(&priv->lock);
236*a1a98689SHans de Goede 	return ret;
237*a1a98689SHans de Goede }
238*a1a98689SHans de Goede EXPORT_SYMBOL(drm_privacy_screen_set_sw_state);
239*a1a98689SHans de Goede 
240*a1a98689SHans de Goede /**
241*a1a98689SHans de Goede  * drm_privacy_screen_get_state - get privacy-screen's current state
242*a1a98689SHans de Goede  * @priv: privacy screen to get the state for
243*a1a98689SHans de Goede  * @sw_state_ret: address where to store the privacy-screens current sw-state
244*a1a98689SHans de Goede  * @hw_state_ret: address where to store the privacy-screens current hw-state
245*a1a98689SHans de Goede  *
246*a1a98689SHans de Goede  * Get the current state of a privacy-screen, both the sw-state and the
247*a1a98689SHans de Goede  * hw-state.
248*a1a98689SHans de Goede  */
249*a1a98689SHans de Goede void drm_privacy_screen_get_state(struct drm_privacy_screen *priv,
250*a1a98689SHans de Goede 				  enum drm_privacy_screen_status *sw_state_ret,
251*a1a98689SHans de Goede 				  enum drm_privacy_screen_status *hw_state_ret)
252*a1a98689SHans de Goede {
253*a1a98689SHans de Goede 	mutex_lock(&priv->lock);
254*a1a98689SHans de Goede 	*sw_state_ret = priv->sw_state;
255*a1a98689SHans de Goede 	*hw_state_ret = priv->hw_state;
256*a1a98689SHans de Goede 	mutex_unlock(&priv->lock);
257*a1a98689SHans de Goede }
258*a1a98689SHans de Goede EXPORT_SYMBOL(drm_privacy_screen_get_state);
259*a1a98689SHans de Goede 
260*a1a98689SHans de Goede /*** drm_privacy_screen_driver.h functions ***/
261*a1a98689SHans de Goede 
262*a1a98689SHans de Goede static ssize_t sw_state_show(struct device *dev,
263*a1a98689SHans de Goede 			     struct device_attribute *attr, char *buf)
264*a1a98689SHans de Goede {
265*a1a98689SHans de Goede 	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
266*a1a98689SHans de Goede 	const char * const sw_state_names[] = {
267*a1a98689SHans de Goede 		"Disabled",
268*a1a98689SHans de Goede 		"Enabled",
269*a1a98689SHans de Goede 	};
270*a1a98689SHans de Goede 	ssize_t ret;
271*a1a98689SHans de Goede 
272*a1a98689SHans de Goede 	mutex_lock(&priv->lock);
273*a1a98689SHans de Goede 
274*a1a98689SHans de Goede 	if (!priv->ops)
275*a1a98689SHans de Goede 		ret = -ENODEV;
276*a1a98689SHans de Goede 	else if (WARN_ON(priv->sw_state >= ARRAY_SIZE(sw_state_names)))
277*a1a98689SHans de Goede 		ret = -ENXIO;
278*a1a98689SHans de Goede 	else
279*a1a98689SHans de Goede 		ret = sprintf(buf, "%s\n", sw_state_names[priv->sw_state]);
280*a1a98689SHans de Goede 
281*a1a98689SHans de Goede 	mutex_unlock(&priv->lock);
282*a1a98689SHans de Goede 	return ret;
283*a1a98689SHans de Goede }
284*a1a98689SHans de Goede /*
285*a1a98689SHans de Goede  * RO: Do not allow setting the sw_state through sysfs, this MUST be done
286*a1a98689SHans de Goede  * through the drm_properties on the drm_connector.
287*a1a98689SHans de Goede  */
288*a1a98689SHans de Goede static DEVICE_ATTR_RO(sw_state);
289*a1a98689SHans de Goede 
290*a1a98689SHans de Goede static ssize_t hw_state_show(struct device *dev,
291*a1a98689SHans de Goede 			     struct device_attribute *attr, char *buf)
292*a1a98689SHans de Goede {
293*a1a98689SHans de Goede 	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
294*a1a98689SHans de Goede 	const char * const hw_state_names[] = {
295*a1a98689SHans de Goede 		"Disabled",
296*a1a98689SHans de Goede 		"Enabled",
297*a1a98689SHans de Goede 		"Disabled, locked",
298*a1a98689SHans de Goede 		"Enabled, locked",
299*a1a98689SHans de Goede 	};
300*a1a98689SHans de Goede 	ssize_t ret;
301*a1a98689SHans de Goede 
302*a1a98689SHans de Goede 	mutex_lock(&priv->lock);
303*a1a98689SHans de Goede 
304*a1a98689SHans de Goede 	if (!priv->ops)
305*a1a98689SHans de Goede 		ret = -ENODEV;
306*a1a98689SHans de Goede 	else if (WARN_ON(priv->hw_state >= ARRAY_SIZE(hw_state_names)))
307*a1a98689SHans de Goede 		ret = -ENXIO;
308*a1a98689SHans de Goede 	else
309*a1a98689SHans de Goede 		ret = sprintf(buf, "%s\n", hw_state_names[priv->hw_state]);
310*a1a98689SHans de Goede 
311*a1a98689SHans de Goede 	mutex_unlock(&priv->lock);
312*a1a98689SHans de Goede 	return ret;
313*a1a98689SHans de Goede }
314*a1a98689SHans de Goede static DEVICE_ATTR_RO(hw_state);
315*a1a98689SHans de Goede 
316*a1a98689SHans de Goede static struct attribute *drm_privacy_screen_attrs[] = {
317*a1a98689SHans de Goede 	&dev_attr_sw_state.attr,
318*a1a98689SHans de Goede 	&dev_attr_hw_state.attr,
319*a1a98689SHans de Goede 	NULL
320*a1a98689SHans de Goede };
321*a1a98689SHans de Goede ATTRIBUTE_GROUPS(drm_privacy_screen);
322*a1a98689SHans de Goede 
323*a1a98689SHans de Goede static struct device_type drm_privacy_screen_type = {
324*a1a98689SHans de Goede 	.name = "privacy_screen",
325*a1a98689SHans de Goede 	.groups = drm_privacy_screen_groups,
326*a1a98689SHans de Goede };
327*a1a98689SHans de Goede 
328*a1a98689SHans de Goede static void drm_privacy_screen_device_release(struct device *dev)
329*a1a98689SHans de Goede {
330*a1a98689SHans de Goede 	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
331*a1a98689SHans de Goede 
332*a1a98689SHans de Goede 	kfree(priv);
333*a1a98689SHans de Goede }
334*a1a98689SHans de Goede 
335*a1a98689SHans de Goede /**
336*a1a98689SHans de Goede  * drm_privacy_screen_register - register a privacy-screen
337*a1a98689SHans de Goede  * @parent: parent-device for the privacy-screen
338*a1a98689SHans de Goede  * @ops: &struct drm_privacy_screen_ops pointer with ops for the privacy-screen
339*a1a98689SHans de Goede  *
340*a1a98689SHans de Goede  * Create and register a privacy-screen.
341*a1a98689SHans de Goede  *
342*a1a98689SHans de Goede  * Return:
343*a1a98689SHans de Goede  * * A pointer to the created privacy-screen on success.
344*a1a98689SHans de Goede  * * An ERR_PTR(errno) on failure.
345*a1a98689SHans de Goede  */
346*a1a98689SHans de Goede struct drm_privacy_screen *drm_privacy_screen_register(
347*a1a98689SHans de Goede 	struct device *parent, const struct drm_privacy_screen_ops *ops)
348*a1a98689SHans de Goede {
349*a1a98689SHans de Goede 	struct drm_privacy_screen *priv;
350*a1a98689SHans de Goede 	int ret;
351*a1a98689SHans de Goede 
352*a1a98689SHans de Goede 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
353*a1a98689SHans de Goede 	if (!priv)
354*a1a98689SHans de Goede 		return ERR_PTR(-ENOMEM);
355*a1a98689SHans de Goede 
356*a1a98689SHans de Goede 	mutex_init(&priv->lock);
357*a1a98689SHans de Goede 
358*a1a98689SHans de Goede 	priv->dev.class = drm_class;
359*a1a98689SHans de Goede 	priv->dev.type = &drm_privacy_screen_type;
360*a1a98689SHans de Goede 	priv->dev.parent = parent;
361*a1a98689SHans de Goede 	priv->dev.release = drm_privacy_screen_device_release;
362*a1a98689SHans de Goede 	dev_set_name(&priv->dev, "privacy_screen-%s", dev_name(parent));
363*a1a98689SHans de Goede 	priv->ops = ops;
364*a1a98689SHans de Goede 
365*a1a98689SHans de Goede 	priv->ops->get_hw_state(priv);
366*a1a98689SHans de Goede 
367*a1a98689SHans de Goede 	ret = device_register(&priv->dev);
368*a1a98689SHans de Goede 	if (ret) {
369*a1a98689SHans de Goede 		put_device(&priv->dev);
370*a1a98689SHans de Goede 		return ERR_PTR(ret);
371*a1a98689SHans de Goede 	}
372*a1a98689SHans de Goede 
373*a1a98689SHans de Goede 	mutex_lock(&drm_privacy_screen_devs_lock);
374*a1a98689SHans de Goede 	list_add(&priv->list, &drm_privacy_screen_devs);
375*a1a98689SHans de Goede 	mutex_unlock(&drm_privacy_screen_devs_lock);
376*a1a98689SHans de Goede 
377*a1a98689SHans de Goede 	return priv;
378*a1a98689SHans de Goede }
379*a1a98689SHans de Goede EXPORT_SYMBOL(drm_privacy_screen_register);
380*a1a98689SHans de Goede 
381*a1a98689SHans de Goede /**
382*a1a98689SHans de Goede  * drm_privacy_screen_unregister - unregister privacy-screen
383*a1a98689SHans de Goede  * @priv: privacy-screen to unregister
384*a1a98689SHans de Goede  *
385*a1a98689SHans de Goede  * Unregister a privacy-screen registered with drm_privacy_screen_register().
386*a1a98689SHans de Goede  * May be called with a NULL or ERR_PTR, in which case it is a no-op.
387*a1a98689SHans de Goede  */
388*a1a98689SHans de Goede void drm_privacy_screen_unregister(struct drm_privacy_screen *priv)
389*a1a98689SHans de Goede {
390*a1a98689SHans de Goede 	if (IS_ERR_OR_NULL(priv))
391*a1a98689SHans de Goede 		return;
392*a1a98689SHans de Goede 
393*a1a98689SHans de Goede 	mutex_lock(&drm_privacy_screen_devs_lock);
394*a1a98689SHans de Goede 	list_del(&priv->list);
395*a1a98689SHans de Goede 	mutex_unlock(&drm_privacy_screen_devs_lock);
396*a1a98689SHans de Goede 
397*a1a98689SHans de Goede 	mutex_lock(&priv->lock);
398*a1a98689SHans de Goede 	priv->ops = NULL;
399*a1a98689SHans de Goede 	mutex_unlock(&priv->lock);
400*a1a98689SHans de Goede 
401*a1a98689SHans de Goede 	device_unregister(&priv->dev);
402*a1a98689SHans de Goede }
403*a1a98689SHans de Goede EXPORT_SYMBOL(drm_privacy_screen_unregister);
404