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