brintos

brintos / linux-shallow public Read only

0
0
Text · 13.9 KiB · 6cc39e3 Raw
472 lines · c
1// SPDX-License-Identifier: MIT2/*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: overview22 *23 * This class allows non KMS drivers, from e.g. drivers/platform/x86 to24 * register a privacy-screen device, which the KMS drivers can then use25 * to implement the standard privacy-screen properties, see26 * :ref:`Standard Connector Properties<standard_connector_properties>`.27 *28 * KMS drivers using a privacy-screen class device are advised to use the29 * drm_connector_attach_privacy_screen_provider() and30 * 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-screen46 *    lookup list47 * @lookup: lookup list entry to add48 *49 * Add an entry to the static privacy-screen lookup list. Note the50 * &struct list_head which is part of the &struct drm_privacy_screen_lookup51 * gets added to a list owned by the privacy-screen core. So the passed in52 * &struct drm_privacy_screen_lookup must not be free-ed until it is removed53 * 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(&lookup->list, &drm_privacy_screen_lookup_list);59	mutex_unlock(&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 static65 *    privacy-screen lookup list66 * @lookup: lookup list entry to remove67 *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(&lookup->list);75	mutex_unlock(&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(&priv->dev), name) == 0) {91			dev = get_device(&priv->dev);92			break;93		}94	}95 96	mutex_unlock(&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 provider103 * @dev: consumer-device for which to get a privacy-screen provider104 * @con_id: (video)connector name for which to get a privacy-screen provider105 *106 * Get a privacy-screen provider for a privacy-screen attached to the107 * 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 found112 * * 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 is126	 * populated by the drm_privacy_screen_arch_init() call. This should127	 * be extended with device-tree / fw_node lookup when support is added128	 * for device-tree using hardware with a privacy-screen.129	 *130	 * The lookup algorithm was shamelessly taken from the clock131	 * 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 match136	 *  If an entry has a connection ID, it must match137	 * Then we take the most specific entry - with the following order138	 * 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(&drm_privacy_screen_lookup_lock);166 167	if (!provider)168		return ERR_PTR(-ENODEV);169 170	priv = drm_privacy_screen_get_by_name(provider);171	if (!priv)172		return ERR_PTR(-EPROBE_DEFER);173 174	return priv;175}176EXPORT_SYMBOL(drm_privacy_screen_get);177 178/**179 * drm_privacy_screen_put - release a privacy-screen reference180 * @priv: privacy screen reference to release181 *182 * Release a privacy-screen provider reference gotten through183 * 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(priv))189		return;190 191	put_device(&priv->dev);192}193EXPORT_SYMBOL(drm_privacy_screen_put);194 195/**196 * drm_privacy_screen_set_sw_state - set a privacy-screen's sw-state197 * @priv: privacy screen to set the sw-state for198 * @sw_state: new sw-state value to set199 *200 * Set the sw-state of a privacy screen. If the privacy-screen is not201 * in a locked hw-state, then the actual and hw-state of the privacy-screen202 * will be immediately updated to the new value. If the privacy-screen is203 * in a locked hw-state, then the new sw-state will be remembered as the204 * 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 the222	 * sw_state while the hw_state is locked is allowed. In this case223	 * it is a no-op other then storing the new sw_state so that it224	 * 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(&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 state242 * @priv: privacy screen to get the state for243 * @sw_state_ret: address where to store the privacy-screens current sw-state244 * @hw_state_ret: address where to store the privacy-screens current hw-state245 *246 * Get the current state of a privacy-screen, both the sw-state and the247 * 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(&priv->lock);257}258EXPORT_SYMBOL(drm_privacy_screen_get_state);259 260/**261 * drm_privacy_screen_register_notifier - register a notifier262 * @priv: Privacy screen to register the notifier with263 * @nb: Notifier-block for the notifier to register264 *265 * Register a notifier with the privacy-screen to be notified of changes made266 * 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 a268 * hotkey press.269 *270 * The notifier is called with no locks held. The new hw_state and sw_state271 * 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 through276 * 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(&priv->notifier_head, nb);284}285EXPORT_SYMBOL(drm_privacy_screen_register_notifier);286 287/**288 * drm_privacy_screen_unregister_notifier - unregister a notifier289 * @priv: Privacy screen to register the notifier with290 * @nb: Notifier-block for the notifier to register291 *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(&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	else322		ret = sprintf(buf, "%s\n", sw_state_names[priv->sw_state]);323 324	mutex_unlock(&priv->lock);325	return ret;326}327/*328 * RO: Do not allow setting the sw_state through sysfs, this MUST be done329 * 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	else352		ret = sprintf(buf, "%s\n", hw_state_names[priv->hw_state]);353 354	mutex_unlock(&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	NULL363};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(priv);376}377 378/**379 * drm_privacy_screen_register - register a privacy-screen380 * @parent: parent-device for the privacy-screen381 * @ops: &struct drm_privacy_screen_ops pointer with ops for the privacy-screen382 * @data: Private data owned by the privacy screen provider383 *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(sizeof(*priv), GFP_KERNEL);398	if (!priv)399		return ERR_PTR(-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(&priv->dev, "privacy_screen-%s", dev_name(parent));409	priv->drvdata = data;410	priv->ops = ops;411 412	priv->ops->get_hw_state(priv);413 414	ret = device_register(&priv->dev);415	if (ret) {416		put_device(&priv->dev);417		return ERR_PTR(ret);418	}419 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);423 424	return priv;425}426EXPORT_SYMBOL(drm_privacy_screen_register);427 428/**429 * drm_privacy_screen_unregister - unregister privacy-screen430 * @priv: privacy-screen to unregister431 *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(priv))438		return;439 440	mutex_lock(&drm_privacy_screen_devs_lock);441	list_del(&priv->list);442	mutex_unlock(&drm_privacy_screen_devs_lock);443 444	mutex_lock(&priv->lock);445	priv->drvdata = NULL;446	priv->ops = NULL;447	mutex_unlock(&priv->lock);448 449	device_unregister(&priv->dev);450}451EXPORT_SYMBOL(drm_privacy_screen_unregister);452 453/**454 * drm_privacy_screen_call_notifier_chain - notify consumers of state change455 * @priv: Privacy screen to register the notifier with456 *457 * A privacy-screen provider driver can call this functions upon external458 * changes to the privacy-screen state. E.g. the state may be changed by the459 * 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 before462 * calling this function.463 * The expected behavior from the driver upon receiving an external state464 * 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(&priv->notifier_head, 0, priv);470}471EXPORT_SYMBOL(drm_privacy_screen_call_notifier_chain);472