Reviewed-by: Lyude Paul <ly...@redhat.com>

On Fri, 2017-09-15 at 15:20 +0200, Benjamin Berg wrote:
> Many thinkpad laptops and convertibles provide the GMMS method to
> resolve how far the laptop has been opened and whether it has been
> converted into tablet mode. This allows reporting a more precise tablet
> mode state to userspace.
> 
> The current implementation only reports a summarized tablet mode state
> which is triggered as soon as the input devices become unusable as they
> are folded away from the display.
> 
> This will work on all models where the CMMD method was used previously and
> it may also work in other cases.
> 
> Thanks to Peter Zhang of Lenovo for providing information on how to use the
> GMMS method to query the tablet mode.
> 
> Signed-off-by: Benjamin Berg <bb...@redhat.com>
> Cc: Peter FP1 Zhang <zhang...@lenovo.com>
> Cc: Lyude Paul <ly...@redhat.com>
> ---
>  drivers/platform/x86/thinkpad_acpi.c | 132 +++++++++++++++++++++++++++++++-
> ---
>  1 file changed, 119 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/platform/x86/thinkpad_acpi.c
> b/drivers/platform/x86/thinkpad_acpi.c
> index 2242d6035d9e..91fab1a13a6d 100644
> --- a/drivers/platform/x86/thinkpad_acpi.c
> +++ b/drivers/platform/x86/thinkpad_acpi.c
> @@ -310,8 +310,7 @@ static struct {
>       enum {
>               TP_HOTKEY_TABLET_NONE = 0,
>               TP_HOTKEY_TABLET_USES_MHKG,
> -             /* X1 Yoga 2016, seen on BIOS N1FET44W */
> -             TP_HOTKEY_TABLET_USES_CMMD,
> +             TP_HOTKEY_TABLET_USES_GMMS,
>       } hotkey_tablet;
>       u32 kbdlight:1;
>       u32 light:1;
> @@ -2044,8 +2043,28 @@ static void hotkey_poll_setup(const bool may_warn);
>  
>  /* HKEY.MHKG() return bits */
>  #define TP_HOTKEY_TABLET_MASK (1 << 3)
> -/* ThinkPad X1 Yoga (2016) */
> -#define TP_EC_CMMD_TABLET_MODE 0x6
> +enum {
> +     TP_ACPI_MULTI_MODE_INVALID      = 0,
> +     TP_ACPI_MULTI_MODE_UNKNOWN      = 1 << 0,
> +     TP_ACPI_MULTI_MODE_LAPTOP       = 1 << 1,
> +     TP_ACPI_MULTI_MODE_TABLET       = 1 << 2,
> +     TP_ACPI_MULTI_MODE_FLAT         = 1 << 3,
> +     TP_ACPI_MULTI_MODE_STAND        = 1 << 4,
> +     TP_ACPI_MULTI_MODE_TENT         = 1 << 5,
> +     TP_ACPI_MULTI_MODE_STAND_TENT   = 1 << 6,
> +};
> +
> +enum {
> +     /* The following modes are considered tablet mode for the purpose
> of
> +      * reporting the status to userspace. i.e. in all these modes it
> makes
> +      * sense to disable the laptop input devices such as touchpad and
> +      * keyboard.
> +      */
> +     TP_ACPI_MULTI_MODE_TABLET_LIKE  = TP_ACPI_MULTI_MODE_TABLET |
> +                                       TP_ACPI_MULTI_MODE_STAND |
> +                                       TP_ACPI_MULTI_MODE_TENT |
> +                                       TP_ACPI_MULTI_MODE_STAND_TENT,
> +};
>  
>  static int hotkey_get_wlsw(void)
>  {
> @@ -2066,6 +2085,90 @@ static int hotkey_get_wlsw(void)
>       return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;
>  }
>  
> +static int hotkey_gmms_get_tablet_mode(int s, int *has_tablet_mode)
> +{
> +     int type = (s >> 16) & 0xffff;
> +     int value = s & 0xffff;
> +     int mode = TP_ACPI_MULTI_MODE_INVALID;
> +     int valid_modes = 0;
> +
> +     if (has_tablet_mode)
> +             *has_tablet_mode = 0;
> +
> +     switch (type) {
> +     case 1:
> +             valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
> +                           TP_ACPI_MULTI_MODE_TABLET |
> +                           TP_ACPI_MULTI_MODE_STAND_TENT;
> +             break;
> +     case 2:
> +             valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
> +                           TP_ACPI_MULTI_MODE_FLAT |
> +                           TP_ACPI_MULTI_MODE_TABLET |
> +                           TP_ACPI_MULTI_MODE_STAND |
> +                           TP_ACPI_MULTI_MODE_TENT;
> +             break;
> +     case 3:
> +             valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
> +                           TP_ACPI_MULTI_MODE_FLAT;
> +             break;
> +     case 4:
> +             valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
> +                           TP_ACPI_MULTI_MODE_TABLET |
> +                           TP_ACPI_MULTI_MODE_STAND |
> +                           TP_ACPI_MULTI_MODE_TENT;
> +             break;
> +     case 5:
> +             valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
> +                           TP_ACPI_MULTI_MODE_FLAT |
> +                           TP_ACPI_MULTI_MODE_TABLET |
> +                           TP_ACPI_MULTI_MODE_STAND |
> +                           TP_ACPI_MULTI_MODE_TENT;
> +             break;
> +     default:
> +             pr_err("Unknown multi mode status type %d with value
> 0x%04X, please report this to %s\n",
> +                    type, value, TPACPI_MAIL);
> +             return 0;
> +     }
> +
> +     if (has_tablet_mode && (valid_modes &
> TP_ACPI_MULTI_MODE_TABLET_LIKE))
> +             *has_tablet_mode = 1;
> +
> +     switch (value) {
> +     case 1:
> +             mode = TP_ACPI_MULTI_MODE_LAPTOP;
> +             break;
> +     case 2:
> +             mode = TP_ACPI_MULTI_MODE_FLAT;
> +             break;
> +     case 3:
> +             mode = TP_ACPI_MULTI_MODE_TABLET;
> +             break;
> +     case 4:
> +             if (type == 1)
> +                     mode = TP_ACPI_MULTI_MODE_STAND_TENT;
> +             else
> +                     mode = TP_ACPI_MULTI_MODE_STAND;
> +             break;
> +     case 5:
> +             mode = TP_ACPI_MULTI_MODE_TENT;
> +             break;
> +     default:
> +             if (type == 5 && value == 0xffff) {
> +                     pr_warn("Multi mode status is undetected, assuming
> laptop\n");
> +                     return 0;
> +             }
> +     }
> +
> +     if (!(mode & valid_modes)) {
> +             pr_err("Unknown/reserved multi mode value 0x%04X for type
> %d, please report this to %s\n",
> +                    value, type, TPACPI_MAIL);
> +             return 0;
> +     }
> +
> +     return !!(mode & TP_ACPI_MULTI_MODE_TABLET_LIKE);
> +}
> +
>  static int hotkey_get_tablet_mode(int *status)
>  {
>       int s;
> @@ -2077,11 +2180,11 @@ static int hotkey_get_tablet_mode(int *status)
>  
>               *status = ((s & TP_HOTKEY_TABLET_MASK) != 0);
>               break;
> -     case TP_HOTKEY_TABLET_USES_CMMD:
> -             if (!acpi_evalf(ec_handle, &s, "CMMD", "d"))
> +     case TP_HOTKEY_TABLET_USES_GMMS:
> +             if (!acpi_evalf(hkey_handle, &s, "GMMS", "dd", 0))
>                       return -EIO;
>  
> -             *status = (s == TP_EC_CMMD_TABLET_MODE);
> +             *status = hotkey_gmms_get_tablet_mode(s, NULL);
>               break;
>       default:
>               break;
> @@ -3113,16 +3216,19 @@ static int hotkey_init_tablet_mode(void)
>       int in_tablet_mode = 0, res;
>       char *type = NULL;
>  
> -     if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) {
> +     if (acpi_evalf(hkey_handle, &res, "GMMS", "qdd", 0)) {
> +             int has_tablet_mode;
> +
> +             in_tablet_mode = hotkey_gmms_get_tablet_mode(res,
> +                                                          &has_tablet_mo
> de);
> +             if (has_tablet_mode)
> +                     tp_features.hotkey_tablet =
> TP_HOTKEY_TABLET_USES_GMMS;
> +             type = "GMMS";
> +     } else if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) {
>               /* For X41t, X60t, X61t Tablets... */
>               tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_MHKG;
>               in_tablet_mode = !!(res & TP_HOTKEY_TABLET_MASK);
>               type = "MHKG";
> -     } else if (acpi_evalf(ec_handle, &res, "CMMD", "qd")) {
> -             /* For X1 Yoga (2016) */
> -             tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_CMMD;
> -             in_tablet_mode = res == TP_EC_CMMD_TABLET_MODE;
> -             type = "CMMD";
>       }
>  
>       if (!tp_features.hotkey_tablet)

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
ibm-acpi-devel mailing list
ibm-acpi-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ibm-acpi-devel

Reply via email to