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