1 // SPDX-License-Identifier: MIT
3 * Copyright (C) 2020 - 2021 Red Hat, Inc.
6 * Hans de Goede <hdegoede@redhat.com>
9 #include <linux/device.h>
10 #include <linux/kernel.h>
11 #include <linux/list.h>
12 #include <linux/module.h>
13 #include <linux/mutex.h>
14 #include <linux/slab.h>
15 #include <drm/drm_privacy_screen_machine.h>
16 #include <drm/drm_privacy_screen_consumer.h>
17 #include <drm/drm_privacy_screen_driver.h>
18 #include "drm_internal.h"
23 * This class allows non KMS drivers, from e.g. drivers/platform/x86 to
24 * register a privacy-screen device, which the KMS drivers can then use
25 * to implement the standard privacy-screen properties, see
26 * :ref:`Standard Connector Properties<standard_connector_properties>`.
28 * KMS drivers using a privacy-screen class device are advised to use the
29 * drm_connector_attach_privacy_screen_provider() and
30 * drm_connector_update_privacy_screen() helpers for dealing with this.
33 #define to_drm_privacy_screen(dev) \
34 container_of(dev, struct drm_privacy_screen, dev)
36 static DEFINE_MUTEX(drm_privacy_screen_lookup_lock
);
37 static LIST_HEAD(drm_privacy_screen_lookup_list
);
39 static DEFINE_MUTEX(drm_privacy_screen_devs_lock
);
40 static LIST_HEAD(drm_privacy_screen_devs
);
42 /*** drm_privacy_screen_machine.h functions ***/
45 * drm_privacy_screen_lookup_add - add an entry to the static privacy-screen
47 * @lookup: lookup list entry to add
49 * Add an entry to the static privacy-screen lookup list. Note the
50 * &struct list_head which is part of the &struct drm_privacy_screen_lookup
51 * gets added to a list owned by the privacy-screen core. So the passed in
52 * &struct drm_privacy_screen_lookup must not be free-ed until it is removed
53 * from the lookup list by calling drm_privacy_screen_lookup_remove().
55 void drm_privacy_screen_lookup_add(struct drm_privacy_screen_lookup
*lookup
)
57 mutex_lock(&drm_privacy_screen_lookup_lock
);
58 list_add(&lookup
->list
, &drm_privacy_screen_lookup_list
);
59 mutex_unlock(&drm_privacy_screen_lookup_lock
);
61 EXPORT_SYMBOL(drm_privacy_screen_lookup_add
);
64 * drm_privacy_screen_lookup_remove - remove an entry to the static
65 * privacy-screen lookup list
66 * @lookup: lookup list entry to remove
68 * Remove an entry previously added with drm_privacy_screen_lookup_add()
69 * from the static privacy-screen lookup list.
71 void drm_privacy_screen_lookup_remove(struct drm_privacy_screen_lookup
*lookup
)
73 mutex_lock(&drm_privacy_screen_lookup_lock
);
74 list_del(&lookup
->list
);
75 mutex_unlock(&drm_privacy_screen_lookup_lock
);
77 EXPORT_SYMBOL(drm_privacy_screen_lookup_remove
);
79 /*** drm_privacy_screen_consumer.h functions ***/
81 static struct drm_privacy_screen
*drm_privacy_screen_get_by_name(
84 struct drm_privacy_screen
*priv
;
85 struct device
*dev
= NULL
;
87 mutex_lock(&drm_privacy_screen_devs_lock
);
89 list_for_each_entry(priv
, &drm_privacy_screen_devs
, list
) {
90 if (strcmp(dev_name(&priv
->dev
), name
) == 0) {
91 dev
= get_device(&priv
->dev
);
96 mutex_unlock(&drm_privacy_screen_devs_lock
);
98 return dev
? to_drm_privacy_screen(dev
) : NULL
;
102 * drm_privacy_screen_get - get a privacy-screen provider
103 * @dev: consumer-device for which to get a privacy-screen provider
104 * @con_id: (video)connector name for which to get a privacy-screen provider
106 * Get a privacy-screen provider for a privacy-screen attached to the
107 * display described by the @dev and @con_id parameters.
110 * * A pointer to a &struct drm_privacy_screen on success.
111 * * ERR_PTR(-ENODEV) if no matching privacy-screen is found
112 * * ERR_PTR(-EPROBE_DEFER) if there is a matching privacy-screen,
113 * but it has not been registered yet.
115 struct drm_privacy_screen
*drm_privacy_screen_get(struct device
*dev
,
118 const char *dev_id
= dev
? dev_name(dev
) : NULL
;
119 struct drm_privacy_screen_lookup
*l
;
120 struct drm_privacy_screen
*priv
;
121 const char *provider
= NULL
;
122 int match
, best
= -1;
125 * For now we only support using a static lookup table, which is
126 * populated by the drm_privacy_screen_arch_init() call. This should
127 * be extended with device-tree / fw_node lookup when support is added
128 * for device-tree using hardware with a privacy-screen.
130 * The lookup algorithm was shamelessly taken from the clock
133 * We do slightly fuzzy matching here:
134 * An entry with a NULL ID is assumed to be a wildcard.
135 * If an entry has a device ID, it must match
136 * If an entry has a connection ID, it must match
137 * Then we take the most specific entry - with the following order
138 * of precedence: dev+con > dev only > con only.
140 mutex_lock(&drm_privacy_screen_lookup_lock
);
142 list_for_each_entry(l
, &drm_privacy_screen_lookup_list
, list
) {
146 if (!dev_id
|| strcmp(l
->dev_id
, dev_id
))
153 if (!con_id
|| strcmp(l
->con_id
, con_id
))
160 provider
= l
->provider
;
165 mutex_unlock(&drm_privacy_screen_lookup_lock
);
168 return ERR_PTR(-ENODEV
);
170 priv
= drm_privacy_screen_get_by_name(provider
);
172 return ERR_PTR(-EPROBE_DEFER
);
176 EXPORT_SYMBOL(drm_privacy_screen_get
);
179 * drm_privacy_screen_put - release a privacy-screen reference
180 * @priv: privacy screen reference to release
182 * Release a privacy-screen provider reference gotten through
183 * drm_privacy_screen_get(). May be called with a NULL or ERR_PTR,
184 * in which case it is a no-op.
186 void drm_privacy_screen_put(struct drm_privacy_screen
*priv
)
188 if (IS_ERR_OR_NULL(priv
))
191 put_device(&priv
->dev
);
193 EXPORT_SYMBOL(drm_privacy_screen_put
);
196 * drm_privacy_screen_set_sw_state - set a privacy-screen's sw-state
197 * @priv: privacy screen to set the sw-state for
198 * @sw_state: new sw-state value to set
200 * Set the sw-state of a privacy screen. If the privacy-screen is not
201 * in a locked hw-state, then the actual and hw-state of the privacy-screen
202 * will be immediately updated to the new value. If the privacy-screen is
203 * in a locked hw-state, then the new sw-state will be remembered as the
204 * requested state to put the privacy-screen in when it becomes unlocked.
206 * Return: 0 on success, negative error code on failure.
208 int drm_privacy_screen_set_sw_state(struct drm_privacy_screen
*priv
,
209 enum drm_privacy_screen_status sw_state
)
213 mutex_lock(&priv
->lock
);
221 * As per the DRM connector properties documentation, setting the
222 * sw_state while the hw_state is locked is allowed. In this case
223 * it is a no-op other then storing the new sw_state so that it
224 * can be honored when the state gets unlocked.
225 * Also skip the set if the hw already is in the desired state.
227 if (priv
->hw_state
>= PRIVACY_SCREEN_DISABLED_LOCKED
||
228 priv
->hw_state
== sw_state
) {
229 priv
->sw_state
= sw_state
;
233 ret
= priv
->ops
->set_sw_state(priv
, sw_state
);
235 mutex_unlock(&priv
->lock
);
238 EXPORT_SYMBOL(drm_privacy_screen_set_sw_state
);
241 * drm_privacy_screen_get_state - get privacy-screen's current state
242 * @priv: privacy screen to get the state for
243 * @sw_state_ret: address where to store the privacy-screens current sw-state
244 * @hw_state_ret: address where to store the privacy-screens current hw-state
246 * Get the current state of a privacy-screen, both the sw-state and the
249 void drm_privacy_screen_get_state(struct drm_privacy_screen
*priv
,
250 enum drm_privacy_screen_status
*sw_state_ret
,
251 enum drm_privacy_screen_status
*hw_state_ret
)
253 mutex_lock(&priv
->lock
);
254 *sw_state_ret
= priv
->sw_state
;
255 *hw_state_ret
= priv
->hw_state
;
256 mutex_unlock(&priv
->lock
);
258 EXPORT_SYMBOL(drm_privacy_screen_get_state
);
261 * drm_privacy_screen_register_notifier - register a notifier
262 * @priv: Privacy screen to register the notifier with
263 * @nb: Notifier-block for the notifier to register
265 * Register a notifier with the privacy-screen to be notified of changes made
266 * to the privacy-screen state from outside of the privacy-screen class.
267 * E.g. the state may be changed by the hardware itself in response to a
270 * The notifier is called with no locks held. The new hw_state and sw_state
271 * can be retrieved using the drm_privacy_screen_get_state() function.
272 * A pointer to the drm_privacy_screen's struct is passed as the ``void *data``
273 * argument of the notifier_block's notifier_call.
275 * The notifier will NOT be called when changes are made through
276 * drm_privacy_screen_set_sw_state(). It is only called for external changes.
278 * Return: 0 on success, negative error code on failure.
280 int drm_privacy_screen_register_notifier(struct drm_privacy_screen
*priv
,
281 struct notifier_block
*nb
)
283 return blocking_notifier_chain_register(&priv
->notifier_head
, nb
);
285 EXPORT_SYMBOL(drm_privacy_screen_register_notifier
);
288 * drm_privacy_screen_unregister_notifier - unregister a notifier
289 * @priv: Privacy screen to register the notifier with
290 * @nb: Notifier-block for the notifier to register
292 * Unregister a notifier registered with drm_privacy_screen_register_notifier().
294 * Return: 0 on success, negative error code on failure.
296 int drm_privacy_screen_unregister_notifier(struct drm_privacy_screen
*priv
,
297 struct notifier_block
*nb
)
299 return blocking_notifier_chain_unregister(&priv
->notifier_head
, nb
);
301 EXPORT_SYMBOL(drm_privacy_screen_unregister_notifier
);
303 /*** drm_privacy_screen_driver.h functions ***/
305 static ssize_t
sw_state_show(struct device
*dev
,
306 struct device_attribute
*attr
, char *buf
)
308 struct drm_privacy_screen
*priv
= to_drm_privacy_screen(dev
);
309 const char * const sw_state_names
[] = {
315 mutex_lock(&priv
->lock
);
319 else if (WARN_ON(priv
->sw_state
>= ARRAY_SIZE(sw_state_names
)))
322 ret
= sprintf(buf
, "%s\n", sw_state_names
[priv
->sw_state
]);
324 mutex_unlock(&priv
->lock
);
328 * RO: Do not allow setting the sw_state through sysfs, this MUST be done
329 * through the drm_properties on the drm_connector.
331 static DEVICE_ATTR_RO(sw_state
);
333 static ssize_t
hw_state_show(struct device
*dev
,
334 struct device_attribute
*attr
, char *buf
)
336 struct drm_privacy_screen
*priv
= to_drm_privacy_screen(dev
);
337 const char * const hw_state_names
[] = {
345 mutex_lock(&priv
->lock
);
349 else if (WARN_ON(priv
->hw_state
>= ARRAY_SIZE(hw_state_names
)))
352 ret
= sprintf(buf
, "%s\n", hw_state_names
[priv
->hw_state
]);
354 mutex_unlock(&priv
->lock
);
357 static DEVICE_ATTR_RO(hw_state
);
359 static struct attribute
*drm_privacy_screen_attrs
[] = {
360 &dev_attr_sw_state
.attr
,
361 &dev_attr_hw_state
.attr
,
364 ATTRIBUTE_GROUPS(drm_privacy_screen
);
366 static struct device_type drm_privacy_screen_type
= {
367 .name
= "privacy_screen",
368 .groups
= drm_privacy_screen_groups
,
371 static void drm_privacy_screen_device_release(struct device
*dev
)
373 struct drm_privacy_screen
*priv
= to_drm_privacy_screen(dev
);
379 * drm_privacy_screen_register - register a privacy-screen
380 * @parent: parent-device for the privacy-screen
381 * @ops: &struct drm_privacy_screen_ops pointer with ops for the privacy-screen
382 * @data: Private data owned by the privacy screen provider
384 * Create and register a privacy-screen.
387 * * A pointer to the created privacy-screen on success.
388 * * An ERR_PTR(errno) on failure.
390 struct drm_privacy_screen
*drm_privacy_screen_register(
391 struct device
*parent
, const struct drm_privacy_screen_ops
*ops
,
394 struct drm_privacy_screen
*priv
;
397 priv
= kzalloc(sizeof(*priv
), GFP_KERNEL
);
399 return ERR_PTR(-ENOMEM
);
401 mutex_init(&priv
->lock
);
402 BLOCKING_INIT_NOTIFIER_HEAD(&priv
->notifier_head
);
404 priv
->dev
.class = drm_class
;
405 priv
->dev
.type
= &drm_privacy_screen_type
;
406 priv
->dev
.parent
= parent
;
407 priv
->dev
.release
= drm_privacy_screen_device_release
;
408 dev_set_name(&priv
->dev
, "privacy_screen-%s", dev_name(parent
));
409 priv
->drvdata
= data
;
412 priv
->ops
->get_hw_state(priv
);
414 ret
= device_register(&priv
->dev
);
416 put_device(&priv
->dev
);
420 mutex_lock(&drm_privacy_screen_devs_lock
);
421 list_add(&priv
->list
, &drm_privacy_screen_devs
);
422 mutex_unlock(&drm_privacy_screen_devs_lock
);
426 EXPORT_SYMBOL(drm_privacy_screen_register
);
429 * drm_privacy_screen_unregister - unregister privacy-screen
430 * @priv: privacy-screen to unregister
432 * Unregister a privacy-screen registered with drm_privacy_screen_register().
433 * May be called with a NULL or ERR_PTR, in which case it is a no-op.
435 void drm_privacy_screen_unregister(struct drm_privacy_screen
*priv
)
437 if (IS_ERR_OR_NULL(priv
))
440 mutex_lock(&drm_privacy_screen_devs_lock
);
441 list_del(&priv
->list
);
442 mutex_unlock(&drm_privacy_screen_devs_lock
);
444 mutex_lock(&priv
->lock
);
445 priv
->drvdata
= NULL
;
447 mutex_unlock(&priv
->lock
);
449 device_unregister(&priv
->dev
);
451 EXPORT_SYMBOL(drm_privacy_screen_unregister
);
454 * drm_privacy_screen_call_notifier_chain - notify consumers of state change
455 * @priv: Privacy screen to register the notifier with
457 * A privacy-screen provider driver can call this functions upon external
458 * changes to the privacy-screen state. E.g. the state may be changed by the
459 * hardware itself in response to a hotkey press.
460 * This function must be called without holding the privacy-screen lock.
461 * the driver must update sw_state and hw_state to reflect the new state before
462 * calling this function.
463 * The expected behavior from the driver upon receiving an external state
464 * change event is: 1. Take the lock; 2. Update sw_state and hw_state;
465 * 3. Release the lock. 4. Call drm_privacy_screen_call_notifier_chain().
467 void drm_privacy_screen_call_notifier_chain(struct drm_privacy_screen
*priv
)
469 blocking_notifier_call_chain(&priv
->notifier_head
, 0, priv
);
471 EXPORT_SYMBOL(drm_privacy_screen_call_notifier_chain
);