[PATCH 8/9] platform/x86: thinkpad_acpi: Register a privacy-screen device

Hans de Goede hdegoede at redhat.com
Thu Sep 16 09:09:17 UTC 2021


Hi,

On 9/15/21 10:55 PM, Lyude Paul wrote:
> On Mon, 2021-09-06 at 09:35 +0200, Hans de Goede wrote:
>> Register a privacy-screen device on laptops with a privacy-screen,
>> this exports the PrivacyGuard features to user-space using a
>> standardized vendor-agnostic sysfs interface. Note the sysfs interface
>> is read-only.
>>
>> Registering a privacy-screen device with the new privacy-screen class
>> code will also allow the GPU driver to get a handle to it and export
>> the privacy-screen setting as a property on the DRM connector object
>> for the LCD panel. This DRM connector property is news standardized
> 
> Looks like a typo here ------------------------------^

Ack I will fix this before pushing this out.

> 
>> interface which all user-space code should use to query and control
>> the privacy-screen.
>>
>> Reviewed-by: Emil Velikov <emil.l.velikov at gmail.com>
>> Signed-off-by: Hans de Goede <hdegoede at redhat.com>
>> ---
>> Changes in v2:
>> - Make the new lcdshadow_set_sw_state, lcdshadow_get_hw_state and
>>   lcdshadow_ops symbols static
>> - Update state and call drm_privacy_screen_call_notifier_chain()
>>   when the state is changed by pressing the Fn + D hotkey combo
>> ---
>>  drivers/platform/x86/Kconfig         |  2 +
>>  drivers/platform/x86/thinkpad_acpi.c | 91 ++++++++++++++++++++--------
>>  2 files changed, 68 insertions(+), 25 deletions(-)
>>
>> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
>> index d12db6c316ea..ae00a27f9f95 100644
>> --- a/drivers/platform/x86/Kconfig
>> +++ b/drivers/platform/x86/Kconfig
>> @@ -509,7 +509,9 @@ config THINKPAD_ACPI
>>         depends on ACPI_VIDEO || ACPI_VIDEO = n
>>         depends on BACKLIGHT_CLASS_DEVICE
>>         depends on I2C
>> +       depends on DRM
>>         select ACPI_PLATFORM_PROFILE
>> +       select DRM_PRIVACY_SCREEN
>>         select HWMON
>>         select NVRAM
>>         select NEW_LEDS
>> diff --git a/drivers/platform/x86/thinkpad_acpi.c
>> b/drivers/platform/x86/thinkpad_acpi.c
>> index b8f2556c4797..044b238730ba 100644
>> --- a/drivers/platform/x86/thinkpad_acpi.c
>> +++ b/drivers/platform/x86/thinkpad_acpi.c
>> @@ -73,6 +73,7 @@
>>  #include <linux/uaccess.h>
>>  #include <acpi/battery.h>
>>  #include <acpi/video.h>
>> +#include <drm/drm_privacy_screen_driver.h>
>>  #include "dual_accel_detect.h"
>>  
>>  /* ThinkPad CMOS commands */
>> @@ -157,6 +158,7 @@ enum tpacpi_hkey_event_t {
>>         TP_HKEY_EV_VOL_UP               = 0x1015, /* Volume up or unmute */
>>         TP_HKEY_EV_VOL_DOWN             = 0x1016, /* Volume down or unmute
>> */
>>         TP_HKEY_EV_VOL_MUTE             = 0x1017, /* Mixer output mute */
>> +       TP_HKEY_EV_PRIVACYGUARD_TOGGLE  = 0x130f, /* Toggle priv.guard
>> on/off */
>>  
>>         /* Reasons for waking up from S3/S4 */
>>         TP_HKEY_EV_WKUP_S3_UNDOCK       = 0x2304, /* undock requested, S3 */
>> @@ -3889,6 +3891,12 @@ static bool hotkey_notify_extended_hotkey(const u32
>> hkey)
>>  {
>>         unsigned int scancode;
>>  
>> +       switch (hkey) {
>> +       case TP_HKEY_EV_PRIVACYGUARD_TOGGLE:
>> +               tpacpi_driver_event(hkey);
>> +               return true;
>> +       }
>> +
>>         /* Extended keycodes start at 0x300 and our offset into the map
>>          * TP_ACPI_HOTKEYSCAN_EXTENDED_START. The calculated scancode
>>          * will be positive, but might not be in the correct range.
>> @@ -9819,30 +9827,40 @@ static struct ibm_struct battery_driver_data = {
>>   * LCD Shadow subdriver, for the Lenovo PrivacyGuard feature
>>   */
>>  
>> +static struct drm_privacy_screen *lcdshadow_dev;
>>  static acpi_handle lcdshadow_get_handle;
>>  static acpi_handle lcdshadow_set_handle;
>> -static int lcdshadow_state;
>>  
>> -static int lcdshadow_on_off(bool state)
>> +static int lcdshadow_set_sw_state(struct drm_privacy_screen *priv,
>> +                                 enum drm_privacy_screen_status state)
>>  {
>>         int output;
>>  
>> +       if (WARN_ON(!mutex_is_locked(&priv->lock)))
>> +               return -EIO;
>> +
>>         if (!acpi_evalf(lcdshadow_set_handle, &output, NULL, "dd",
>> (int)state))
>>                 return -EIO;
>>  
>> -       lcdshadow_state = state;
>> +       priv->hw_state = priv->sw_state = state;
>>         return 0;
>>  }
>>  
>> -static int lcdshadow_set(bool on)
>> +static void lcdshadow_get_hw_state(struct drm_privacy_screen *priv)
>>  {
>> -       if (lcdshadow_state < 0)
>> -               return lcdshadow_state;
>> -       if (lcdshadow_state == on)
>> -               return 0;
>> -       return lcdshadow_on_off(on);
>> +       int output;
>> +
>> +       if (!acpi_evalf(lcdshadow_get_handle, &output, NULL, "dd", 0))
>> +               return;
>> +
>> +       priv->hw_state = priv->sw_state = output & 0x1;
>>  }
>>  
>> +static const struct drm_privacy_screen_ops lcdshadow_ops = {
>> +       .set_sw_state = lcdshadow_set_sw_state,
>> +       .get_hw_state = lcdshadow_get_hw_state,
>> +};
>> +
>>  static int tpacpi_lcdshadow_init(struct ibm_init_struct *iibm)
>>  {
>>         acpi_status status1, status2;
>> @@ -9850,36 +9868,44 @@ static int tpacpi_lcdshadow_init(struct
>> ibm_init_struct *iibm)
>>  
>>         status1 = acpi_get_handle(hkey_handle, "GSSS",
>> &lcdshadow_get_handle);
>>         status2 = acpi_get_handle(hkey_handle, "SSSS",
>> &lcdshadow_set_handle);
>> -       if (ACPI_FAILURE(status1) || ACPI_FAILURE(status2)) {
>> -               lcdshadow_state = -ENODEV;
>> +       if (ACPI_FAILURE(status1) || ACPI_FAILURE(status2))
>>                 return 0;
>> -       }
>>  
>> -       if (!acpi_evalf(lcdshadow_get_handle, &output, NULL, "dd", 0)) {
>> -               lcdshadow_state = -EIO;
>> +       if (!acpi_evalf(lcdshadow_get_handle, &output, NULL, "dd", 0))
>>                 return -EIO;
>> -       }
>> -       if (!(output & 0x10000)) {
>> -               lcdshadow_state = -ENODEV;
>> +
>> +       if (!(output & 0x10000))
>>                 return 0;
>> -       }
>> -       lcdshadow_state = output & 0x1;
>> +
>> +       lcdshadow_dev = drm_privacy_screen_register(&tpacpi_pdev->dev,
>> +                                                   &lcdshadow_ops);
>> +       if (IS_ERR(lcdshadow_dev))
>> +               return PTR_ERR(lcdshadow_dev);
>>  
>>         return 0;
>>  }
>>  
>> +static void lcdshadow_exit(void)
>> +{
>> +       drm_privacy_screen_unregister(lcdshadow_dev);
>> +}
>> +
>>  static void lcdshadow_resume(void)
>>  {
>> -       if (lcdshadow_state >= 0)
>> -               lcdshadow_on_off(lcdshadow_state);
>> +       if (!lcdshadow_dev)
>> +               return;
>> +
>> +       mutex_lock(&lcdshadow_dev->lock);
>> +       lcdshadow_set_sw_state(lcdshadow_dev, lcdshadow_dev->sw_state);
>> +       mutex_unlock(&lcdshadow_dev->lock);
>>  }
>>  
> 
> For privacy screens provided by x86 platform drivers this is -probably-
> correct, but only so long as we're confident that the privacy screen is always
> going to be controllable regardless of the power state of the actual LCD
> panel.

Right, in this case the privacy-screen control is entirely independent
of the actual LCD state. Also notice that this code does not introduce
the re-storing of the privacy-screen state, that was already there, it
merely changes it to go through the new drm_privacy_screen API.


> I'd think we would need to handle suspend/resume in the atomic commit though
> if we ever have to support systems where the two are dependent on one another,
> but, that's a simple enough change to do later if it arises that I think we
> can ignore it for now.

Ack.

Regards,

Hans



> 
>>  static int lcdshadow_read(struct seq_file *m)
>>  {
>> -       if (lcdshadow_state < 0) {
>> +       if (!lcdshadow_dev) {
>>                 seq_puts(m, "status:\t\tnot supported\n");
>>         } else {
>> -               seq_printf(m, "status:\t\t%d\n", lcdshadow_state);
>> +               seq_printf(m, "status:\t\t%d\n", lcdshadow_dev->hw_state);
>>                 seq_puts(m, "commands:\t0, 1\n");
>>         }
>>  
>> @@ -9891,7 +9917,7 @@ static int lcdshadow_write(char *buf)
>>         char *cmd;
>>         int res, state = -EINVAL;
>>  
>> -       if (lcdshadow_state < 0)
>> +       if (!lcdshadow_dev)
>>                 return -ENODEV;
>>  
>>         while ((cmd = strsep(&buf, ","))) {
>> @@ -9903,11 +9929,18 @@ static int lcdshadow_write(char *buf)
>>         if (state >= 2 || state < 0)
>>                 return -EINVAL;
>>  
>> -       return lcdshadow_set(state);
>> +       mutex_lock(&lcdshadow_dev->lock);
>> +       res = lcdshadow_set_sw_state(lcdshadow_dev, state);
>> +       mutex_unlock(&lcdshadow_dev->lock);
>> +
>> +       drm_privacy_screen_call_notifier_chain(lcdshadow_dev);
>> +
>> +       return res;
>>  }
>>  
>>  static struct ibm_struct lcdshadow_driver_data = {
>>         .name = "lcdshadow",
>> +       .exit = lcdshadow_exit,
>>         .resume = lcdshadow_resume,
>>         .read = lcdshadow_read,
>>         .write = lcdshadow_write,
>> @@ -10717,6 +10750,14 @@ static void tpacpi_driver_event(const unsigned int
>> hkey_event)
>>                 if (!atomic_add_unless(&dytc_ignore_event, -1, 0))
>>                         dytc_profile_refresh();
>>         }
>> +
>> +       if (lcdshadow_dev && hkey_event == TP_HKEY_EV_PRIVACYGUARD_TOGGLE) {
>> +               mutex_lock(&lcdshadow_dev->lock);
>> +               lcdshadow_get_hw_state(lcdshadow_dev);
>> +               mutex_unlock(&lcdshadow_dev->lock);
>> +
>> +               drm_privacy_screen_call_notifier_chain(lcdshadow_dev);
>> +       }
>>  }
>>  
>>  static void hotkey_driver_event(const unsigned int scancode)
> 



More information about the dri-devel mailing list