File Index
Files and directories
Sign in
FILE

drm_privacy_screen.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/drm_privacy_screen.c C
  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
 37static DEFINE_MUTEX(drm_privacy_screen_lookup_lock);
 38static LIST_HEAD(drm_privacy_screen_lookup_list);
 39
 40static DEFINE_MUTEX(drm_privacy_screen_devs_lock);
 41static 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 */
 56void 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}
 62EXPORT_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 */
 72void 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}
 78EXPORT_SYMBOL(drm_privacy_screen_lookup_remove);
 79
 80/*** drm_privacy_screen_consumer.h functions ***/
 81
 82static 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 */
116struct 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}
177EXPORT_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 */
187void 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}
194EXPORT_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 */
209int 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);
235out:
236	mutex_unlock(&priv->lock);
237	return ret;
238}
239EXPORT_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 */
250void 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}
259EXPORT_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 */
281int 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}
286EXPORT_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 */
297int 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}
302EXPORT_SYMBOL(drm_privacy_screen_unregister_notifier);
303
304/*** drm_privacy_screen_driver.h functions ***/
305
306static 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 */
332static DEVICE_ATTR_RO(sw_state);
333
334static 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}
358static DEVICE_ATTR_RO(hw_state);
359
360static struct attribute *drm_privacy_screen_attrs[] = {
361	&dev_attr_sw_state.attr,
362	&dev_attr_hw_state.attr,
363	NULL
364};
365ATTRIBUTE_GROUPS(drm_privacy_screen);
366
367static struct device_type drm_privacy_screen_type = {
368	.name = "privacy_screen",
369	.groups = drm_privacy_screen_groups,
370};
371
372static 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 */
391struct 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}
427EXPORT_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 */
436void 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}
452EXPORT_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 */
468void drm_privacy_screen_call_notifier_chain(struct drm_privacy_screen *priv)
469{
470	blocking_notifier_call_chain(&priv->notifier_head, 0, priv);
471}
472EXPORT_SYMBOL(drm_privacy_screen_call_notifier_chain);
~/ Read-only file preview.