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