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/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"
19
20/**
21 * DOC: overview
22 *
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>`.
27 *
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.
31 */
32
33#define to_drm_privacy_screen(dev) \
34 container_of(dev, struct drm_privacy_screen, dev)
35
36static DEFINE_MUTEX(drm_privacy_screen_lookup_lock);
37static LIST_HEAD(drm_privacy_screen_lookup_list);
38
39static DEFINE_MUTEX(drm_privacy_screen_devs_lock);
40static LIST_HEAD(drm_privacy_screen_devs);
41
42/*** drm_privacy_screen_machine.h functions ***/
43
44/**
45 * drm_privacy_screen_lookup_add - add an entry to the static privacy-screen
46 * lookup list
47 * @lookup: lookup list entry to add
48 *
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().
54 */
55void drm_privacy_screen_lookup_add(struct drm_privacy_screen_lookup *lookup)
56{
57 mutex_lock(&drm_privacy_screen_lookup_lock);
58 list_add(new: &lookup->list, head: &drm_privacy_screen_lookup_list);
59 mutex_unlock(lock: &drm_privacy_screen_lookup_lock);
60}
61EXPORT_SYMBOL(drm_privacy_screen_lookup_add);
62
63/**
64 * drm_privacy_screen_lookup_remove - remove an entry to the static
65 * privacy-screen lookup list
66 * @lookup: lookup list entry to remove
67 *
68 * Remove an entry previously added with drm_privacy_screen_lookup_add()
69 * from the static privacy-screen lookup list.
70 */
71void drm_privacy_screen_lookup_remove(struct drm_privacy_screen_lookup *lookup)
72{
73 mutex_lock(&drm_privacy_screen_lookup_lock);
74 list_del(entry: &lookup->list);
75 mutex_unlock(lock: &drm_privacy_screen_lookup_lock);
76}
77EXPORT_SYMBOL(drm_privacy_screen_lookup_remove);
78
79/*** drm_privacy_screen_consumer.h functions ***/
80
81static struct drm_privacy_screen *drm_privacy_screen_get_by_name(
82 const char *name)
83{
84 struct drm_privacy_screen *priv;
85 struct device *dev = NULL;
86
87 mutex_lock(&drm_privacy_screen_devs_lock);
88
89 list_for_each_entry(priv, &drm_privacy_screen_devs, list) {
90 if (strcmp(dev_name(dev: &priv->dev), name) == 0) {
91 dev = get_device(dev: &priv->dev);
92 break;
93 }
94 }
95
96 mutex_unlock(lock: &drm_privacy_screen_devs_lock);
97
98 return dev ? to_drm_privacy_screen(dev) : NULL;
99}
100
101/**
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
105 *
106 * Get a privacy-screen provider for a privacy-screen attached to the
107 * display described by the @dev and @con_id parameters.
108 *
109 * Return:
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.
114 */
115struct drm_privacy_screen *drm_privacy_screen_get(struct device *dev,
116 const char *con_id)
117{
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;
123
124 /*
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.
129 *
130 * The lookup algorithm was shamelessly taken from the clock
131 * framework:
132 *
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.
139 */
140 mutex_lock(&drm_privacy_screen_lookup_lock);
141
142 list_for_each_entry(l, &drm_privacy_screen_lookup_list, list) {
143 match = 0;
144
145 if (l->dev_id) {
146 if (!dev_id || strcmp(l->dev_id, dev_id))
147 continue;
148
149 match += 2;
150 }
151
152 if (l->con_id) {
153 if (!con_id || strcmp(l->con_id, con_id))
154 continue;
155
156 match += 1;
157 }
158
159 if (match > best) {
160 provider = l->provider;
161 best = match;
162 }
163 }
164
165 mutex_unlock(lock: &drm_privacy_screen_lookup_lock);
166
167 if (!provider)
168 return ERR_PTR(error: -ENODEV);
169
170 priv = drm_privacy_screen_get_by_name(name: provider);
171 if (!priv)
172 return ERR_PTR(error: -EPROBE_DEFER);
173
174 return priv;
175}
176EXPORT_SYMBOL(drm_privacy_screen_get);
177
178/**
179 * drm_privacy_screen_put - release a privacy-screen reference
180 * @priv: privacy screen reference to release
181 *
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.
185 */
186void drm_privacy_screen_put(struct drm_privacy_screen *priv)
187{
188 if (IS_ERR_OR_NULL(ptr: priv))
189 return;
190
191 put_device(dev: &priv->dev);
192}
193EXPORT_SYMBOL(drm_privacy_screen_put);
194
195/**
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
199 *
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.
205 *
206 * Return: 0 on success, negative error code on failure.
207 */
208int drm_privacy_screen_set_sw_state(struct drm_privacy_screen *priv,
209 enum drm_privacy_screen_status sw_state)
210{
211 int ret = 0;
212
213 mutex_lock(&priv->lock);
214
215 if (!priv->ops) {
216 ret = -ENODEV;
217 goto out;
218 }
219
220 /*
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.
226 */
227 if (priv->hw_state >= PRIVACY_SCREEN_DISABLED_LOCKED ||
228 priv->hw_state == sw_state) {
229 priv->sw_state = sw_state;
230 goto out;
231 }
232
233 ret = priv->ops->set_sw_state(priv, sw_state);
234out:
235 mutex_unlock(lock: &priv->lock);
236 return ret;
237}
238EXPORT_SYMBOL(drm_privacy_screen_set_sw_state);
239
240/**
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
245 *
246 * Get the current state of a privacy-screen, both the sw-state and the
247 * hw-state.
248 */
249void 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)
252{
253 mutex_lock(&priv->lock);
254 *sw_state_ret = priv->sw_state;
255 *hw_state_ret = priv->hw_state;
256 mutex_unlock(lock: &priv->lock);
257}
258EXPORT_SYMBOL(drm_privacy_screen_get_state);
259
260/**
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
264 *
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
268 * hotkey press.
269 *
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.
274 *
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.
277 *
278 * Return: 0 on success, negative error code on failure.
279 */
280int drm_privacy_screen_register_notifier(struct drm_privacy_screen *priv,
281 struct notifier_block *nb)
282{
283 return blocking_notifier_chain_register(nh: &priv->notifier_head, nb);
284}
285EXPORT_SYMBOL(drm_privacy_screen_register_notifier);
286
287/**
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
291 *
292 * Unregister a notifier registered with drm_privacy_screen_register_notifier().
293 *
294 * Return: 0 on success, negative error code on failure.
295 */
296int drm_privacy_screen_unregister_notifier(struct drm_privacy_screen *priv,
297 struct notifier_block *nb)
298{
299 return blocking_notifier_chain_unregister(nh: &priv->notifier_head, nb);
300}
301EXPORT_SYMBOL(drm_privacy_screen_unregister_notifier);
302
303/*** drm_privacy_screen_driver.h functions ***/
304
305static ssize_t sw_state_show(struct device *dev,
306 struct device_attribute *attr, char *buf)
307{
308 struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
309 const char * const sw_state_names[] = {
310 "Disabled",
311 "Enabled",
312 };
313 ssize_t ret;
314
315 mutex_lock(&priv->lock);
316
317 if (!priv->ops)
318 ret = -ENODEV;
319 else if (WARN_ON(priv->sw_state >= ARRAY_SIZE(sw_state_names)))
320 ret = -ENXIO;
321 else
322 ret = sprintf(buf, fmt: "%s\n", sw_state_names[priv->sw_state]);
323
324 mutex_unlock(lock: &priv->lock);
325 return ret;
326}
327/*
328 * RO: Do not allow setting the sw_state through sysfs, this MUST be done
329 * through the drm_properties on the drm_connector.
330 */
331static DEVICE_ATTR_RO(sw_state);
332
333static ssize_t hw_state_show(struct device *dev,
334 struct device_attribute *attr, char *buf)
335{
336 struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
337 const char * const hw_state_names[] = {
338 "Disabled",
339 "Enabled",
340 "Disabled, locked",
341 "Enabled, locked",
342 };
343 ssize_t ret;
344
345 mutex_lock(&priv->lock);
346
347 if (!priv->ops)
348 ret = -ENODEV;
349 else if (WARN_ON(priv->hw_state >= ARRAY_SIZE(hw_state_names)))
350 ret = -ENXIO;
351 else
352 ret = sprintf(buf, fmt: "%s\n", hw_state_names[priv->hw_state]);
353
354 mutex_unlock(lock: &priv->lock);
355 return ret;
356}
357static DEVICE_ATTR_RO(hw_state);
358
359static struct attribute *drm_privacy_screen_attrs[] = {
360 &dev_attr_sw_state.attr,
361 &dev_attr_hw_state.attr,
362 NULL
363};
364ATTRIBUTE_GROUPS(drm_privacy_screen);
365
366static struct device_type drm_privacy_screen_type = {
367 .name = "privacy_screen",
368 .groups = drm_privacy_screen_groups,
369};
370
371static void drm_privacy_screen_device_release(struct device *dev)
372{
373 struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
374
375 kfree(objp: priv);
376}
377
378/**
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
383 *
384 * Create and register a privacy-screen.
385 *
386 * Return:
387 * * A pointer to the created privacy-screen on success.
388 * * An ERR_PTR(errno) on failure.
389 */
390struct drm_privacy_screen *drm_privacy_screen_register(
391 struct device *parent, const struct drm_privacy_screen_ops *ops,
392 void *data)
393{
394 struct drm_privacy_screen *priv;
395 int ret;
396
397 priv = kzalloc(size: sizeof(*priv), GFP_KERNEL);
398 if (!priv)
399 return ERR_PTR(error: -ENOMEM);
400
401 mutex_init(&priv->lock);
402 BLOCKING_INIT_NOTIFIER_HEAD(&priv->notifier_head);
403
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(dev: &priv->dev, name: "privacy_screen-%s", dev_name(dev: parent));
409 priv->drvdata = data;
410 priv->ops = ops;
411
412 priv->ops->get_hw_state(priv);
413
414 ret = device_register(dev: &priv->dev);
415 if (ret) {
416 put_device(dev: &priv->dev);
417 return ERR_PTR(error: ret);
418 }
419
420 mutex_lock(&drm_privacy_screen_devs_lock);
421 list_add(new: &priv->list, head: &drm_privacy_screen_devs);
422 mutex_unlock(lock: &drm_privacy_screen_devs_lock);
423
424 return priv;
425}
426EXPORT_SYMBOL(drm_privacy_screen_register);
427
428/**
429 * drm_privacy_screen_unregister - unregister privacy-screen
430 * @priv: privacy-screen to unregister
431 *
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.
434 */
435void drm_privacy_screen_unregister(struct drm_privacy_screen *priv)
436{
437 if (IS_ERR_OR_NULL(ptr: priv))
438 return;
439
440 mutex_lock(&drm_privacy_screen_devs_lock);
441 list_del(entry: &priv->list);
442 mutex_unlock(lock: &drm_privacy_screen_devs_lock);
443
444 mutex_lock(&priv->lock);
445 priv->drvdata = NULL;
446 priv->ops = NULL;
447 mutex_unlock(lock: &priv->lock);
448
449 device_unregister(dev: &priv->dev);
450}
451EXPORT_SYMBOL(drm_privacy_screen_unregister);
452
453/**
454 * drm_privacy_screen_call_notifier_chain - notify consumers of state change
455 * @priv: Privacy screen to register the notifier with
456 *
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().
466 */
467void drm_privacy_screen_call_notifier_chain(struct drm_privacy_screen *priv)
468{
469 blocking_notifier_call_chain(nh: &priv->notifier_head, val: 0, v: priv);
470}
471EXPORT_SYMBOL(drm_privacy_screen_call_notifier_chain);
472

source code of linux/drivers/gpu/drm/drm_privacy_screen.c