lkml.org 
[lkml]   [2020]   [Dec]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH 1/3] intel-hid: add support for SW_TABLET_MODE
Date
Add support for SW_TABLET_MODE for convertibles notebook.

Exactly as intel-vbtn driver, the event code 0xcc is emitted by
convertibles when entering tablet mode and 0xcd when return to
laptop mode.

Signed-off-by: Elia Devito <eliadevito@gmail.com>
---
more info: https://bugzilla.kernel.org/show_bug.cgi?id=207433

drivers/platform/x86/intel-hid.c | 84 ++++++++++++++++++++++++++++++--
1 file changed, 80 insertions(+), 4 deletions(-)

diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c
index 86261970bd8f..5093c57102cf 100644
--- a/drivers/platform/x86/intel-hid.c
+++ b/drivers/platform/x86/intel-hid.c
@@ -15,6 +15,9 @@
#include <linux/platform_device.h>
#include <linux/suspend.h>

+/* When NOT in tablet mode, VGBS returns with the flag 0x40 */
+#define TABLET_MODE_FLAG 0x40
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex Hung");

@@ -61,7 +64,11 @@ static const struct key_entry intel_array_keymap[] = {
{ KE_IGNORE, 0xC9, { KEY_ROTATE_LOCK_TOGGLE } }, /* Release */
{ KE_KEY, 0xCE, { KEY_POWER } }, /* Press */
{ KE_IGNORE, 0xCF, { KEY_POWER } }, /* Release */
- { KE_END },
+};
+
+static const struct key_entry intel_array_switches[] = {
+ { KE_SW, 0xCC, { .sw = { SW_TABLET_MODE, 1 } } }, /* Tablet */
+ { KE_SW, 0xCD, { .sw = { SW_TABLET_MODE, 0 } } }, /* Laptop */
};

static const struct dmi_system_id button_array_table[] = {
@@ -89,9 +96,23 @@ static const struct dmi_system_id button_array_table[] = {
{ }
};

+static const struct dmi_system_id button_array_switches_table[] = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_CHASSIS_TYPE, "31" /* Convertible */),
+ },
+ },
+ { }
+};
+
+#define KEYMAP_LEN \
+ (ARRAY_SIZE(intel_array_keymap) + ARRAY_SIZE(intel_array_switches) + 1)
+
struct intel_hid_priv {
+ struct key_entry keymap[KEYMAP_LEN];
struct input_dev *input_dev;
struct input_dev *array;
+ bool has_switches;
bool wakeup_mode;
};

@@ -327,23 +348,54 @@ static int intel_hid_input_setup(struct platform_device *device)
return input_register_device(priv->input_dev);
}

+static void detect_tablet_mode(struct platform_device *device)
+{
+ struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
+ acpi_handle handle = ACPI_HANDLE(&device->dev);
+ unsigned long long vgbs;
+ int m;
+
+ if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_VGBS_FN, &vgbs))
+ return;
+
+ m = !(vgbs & TABLET_MODE_FLAG);
+ input_report_switch(priv->array, SW_TABLET_MODE, m);
+}
+
static int intel_button_array_input_setup(struct platform_device *device)
{
struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
- int ret;
+ int ret, keymap_len = 0;

/* Setup input device for 5 button array */
priv->array = devm_input_allocate_device(&device->dev);
if (!priv->array)
return -ENOMEM;

- ret = sparse_keymap_setup(priv->array, intel_array_keymap, NULL);
+ memcpy(&priv->keymap[keymap_len], intel_array_keymap,
+ ARRAY_SIZE(intel_array_keymap) *
+ sizeof(struct key_entry));
+ keymap_len += ARRAY_SIZE(intel_array_keymap);
+
+ if (priv->has_switches) {
+ memcpy(&priv->keymap[keymap_len], intel_array_switches,
+ ARRAY_SIZE(intel_array_switches) *
+ sizeof(struct key_entry));
+ keymap_len += ARRAY_SIZE(intel_array_switches);
+ }
+
+ priv->keymap[keymap_len].type = KE_END;
+
+ ret = sparse_keymap_setup(priv->array, priv->keymap, NULL);
if (ret)
return ret;

priv->array->name = "Intel HID 5 button array";
priv->array->id.bustype = BUS_HOST;

+ if (priv->has_switches)
+ detect_tablet_mode(device);
+
return input_register_device(priv->array);
}

@@ -352,7 +404,10 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
struct platform_device *device = context;
struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
unsigned long long ev_index;
+ unsigned int val = !(event & 1); /* Even=press, Odd=release */
+ const struct key_entry *ke;

+ dev_info(&device->dev, "event 0x%x\n", event);
if (priv->wakeup_mode) {
/*
* Needed for wakeup from suspend-to-idle to work on some
@@ -367,13 +422,19 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
if (event == 0xc0 || !priv->array)
return;

- if (!sparse_keymap_entry_from_scancode(priv->array, event)) {
+ ke = sparse_keymap_entry_from_scancode(priv->array, event);
+ if (!ke) {
dev_info(&device->dev, "unknown event 0x%x\n", event);
return;
}

wakeup:
pm_wakeup_hard_event(&device->dev);
+
+ /* report the new switch position to the input subsystem. */
+ if (ke && ke->type == KE_SW)
+ sparse_keymap_report_event(priv->array, event, val, 0);
+
return;
}

@@ -441,6 +502,20 @@ static bool button_array_present(struct platform_device *device)
return false;
}

+static bool intel_button_array_has_switches(struct platform_device *device)
+{
+ acpi_handle handle = ACPI_HANDLE(&device->dev);
+ unsigned long long vgbs;
+
+ if (!dmi_check_system(button_array_switches_table))
+ return false;
+
+ if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_VGBS_FN, &vgbs))
+ return false;
+
+ return true;
+}
+
static int intel_hid_probe(struct platform_device *device)
{
acpi_handle handle = ACPI_HANDLE(&device->dev);
@@ -479,6 +554,7 @@ static int intel_hid_probe(struct platform_device *device)

/* Setup 5 button array */
if (button_array_present(device)) {
+ priv->has_switches = intel_button_array_has_switches(device);
dev_info(&device->dev, "platform supports 5 button array\n");
err = intel_button_array_input_setup(device);
if (err)
--
2.28.0
\
 
 \ /
  Last update: 2020-12-01 21:00    [W:0.127 / U:1.892 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site