ifitzpat / linux-lenovo-X1-tablet-gen3

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

README

Getting Lenovo X1 Tablet Gen 3 hardware working on linux

DistroManjaro
Kernel5.4

Working out of the box:

  • Front camera
  • Touchscreen
  • Stylus
  • Keyboard
  • Wifi
  • Bluetooth
  • Speakers
  • Volume Buttons
  • USB-C ports
  • Mobile Broadband
  • Trackpoint and trackpad buttons
  • SD card reader
  • Accelerometers (with iio-sensor-proxy)

Not working:

  • Back camera
  • Microphone
  • S3 sleep

Not tested:

CANCELLED Buttons and trackpoint:

  • State “CANCELLED” from [2019-11-30 Sat 13:13]

First fork or clone https://github.com/torvalds/linux

Then apply this patch by u/jakeday.

From f1592214e7b2d2f04c0ac6940aca265342fb9f5a Mon Sep 17 00:00:00 2001
From: Jake Day <jake@ninebysix.com>
Date: Thu, 27 Jun 2019 11:59:37 -0400
Subject: [PATCH 03/11] buttons

---
 drivers/input/misc/soc_button_array.c     | 133 ++++++++++++++++++++--
 drivers/platform/x86/surfacepro3_button.c |  38 +++++++
 2 files changed, 159 insertions(+), 12 deletions(-)

diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
index bb458beecb43..e35580655cd0 100644
--- a/drivers/input/misc/soc_button_array.c
+++ b/drivers/input/misc/soc_button_array.c
@@ -29,6 +29,17 @@ struct soc_button_info {
 	bool wakeup;
 };

+/**
+ * struct soc_device_data - driver data for different device types
+ * @button_info: specifications of buttons, if NULL specification is assumed to
+ *               be present in _DSD
+ * @check: device-specific check (NULL means all will be accepted)
+ */
+struct soc_device_data {
+	struct soc_button_info *button_info;
+	int (*check)(struct device *dev);
+};
+
 /*
  * Some of the buttons like volume up/down are auto repeat, while others
  * are not. To support both, we register two platform devices, and put
@@ -314,6 +325,7 @@ static int soc_button_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	const struct acpi_device_id *id;
+	struct soc_device_data *device_data;
 	struct soc_button_info *button_info;
 	struct soc_button_data *priv;
 	struct platform_device *pd;
@@ -324,18 +336,19 @@ static int soc_button_probe(struct platform_device *pdev)
 	if (!id)
 		return -ENODEV;

-	if (!id->driver_data) {
+	device_data = (struct soc_device_data *)id->driver_data;
+	if (device_data && device_data->check) {
+		error = device_data->check(dev);
+		if (error)
+			return error;
+	}
+
+	if (device_data && device_data->button_info) {
+		button_info = (struct soc_button_info *)device_data->button_info;
+	} else {
 		button_info = soc_button_get_button_info(dev);
 		if (IS_ERR(button_info))
 			return PTR_ERR(button_info);
-	} else {
-		button_info = (struct soc_button_info *)id->driver_data;
-	}
-
-	error = gpiod_count(dev, NULL);
-	if (error < 0) {
-		dev_dbg(dev, "no GPIO attached, ignoring...\n");
-		return -ENODEV;
 	}

 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -361,12 +374,32 @@ static int soc_button_probe(struct platform_device *pdev)
 	if (!priv->children[0] && !priv->children[1])
 		return -ENODEV;

-	if (!id->driver_data)
+	if (!device_data || !device_data->button_info)
 		devm_kfree(dev, button_info);

 	return 0;
 }

+
+static int soc_device_check_generic(struct device *dev)
+{
+	int gpios;
+
+	gpios = gpiod_count(dev, NULL);
+	if (gpios < 0) {
+		dev_dbg(dev, "no GPIO attached, ignoring...\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static struct soc_device_data soc_device_ACPI0011 = {
+	.button_info = NULL,
+	.check = soc_device_check_generic,
+};
+
+
 /*
  * Definition of buttons on the tablet. The ACPI index of each button
  * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
@@ -381,9 +414,85 @@ static struct soc_button_info soc_button_PNP0C40[] = {
 	{ }
 };

+static struct soc_device_data soc_device_PNP0C40 = {
+	.button_info = soc_button_PNP0C40,
+	.check = soc_device_check_generic,
+};
+
+
+/*
+ * Special device check for Surface Book 2 and Surface Pro (2017).
+ * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned
+ * devices use MSHW0040 for power and volume buttons, however the way they
+ * have to be addressed differs. Make sure that we only load this drivers
+ * for the correct devices by checking the OEM Platform Revision provided by
+ * the _DSM method.
+ */
+#define MSHW0040_DSM_REVISION		0x01
+#define MSHW0040_DSM_GET_OMPR		0x02	// get OEM Platform Revision
+static const guid_t MSHW0040_DSM_UUID =
+	GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65,
+	          0x49, 0x80, 0x35);
+
+static int soc_device_check_MSHW0040(struct device *dev)
+{
+	acpi_handle handle = ACPI_HANDLE(dev);
+	union acpi_object *result;
+	u64 oem_platform_rev = 0;
+	int gpios;
+
+	// get OEM platform revision
+	result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID,
+					 MSHW0040_DSM_REVISION,
+	                                 MSHW0040_DSM_GET_OMPR, NULL,
+					 ACPI_TYPE_INTEGER);
+
+	if (result) {
+		oem_platform_rev = result->integer.value;
+		ACPI_FREE(result);
+	}
+
+	if (oem_platform_rev == 0)
+		return -ENODEV;
+
+	dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev);
+
+	/*
+	 * We are _really_ expecting GPIOs here. If we do not get any, this
+	 * means the GPIO driver has not been loaded yet (which can happen).
+	 * Try again later.
+	 */
+	gpios = gpiod_count(dev, NULL);
+	if (gpios < 0)
+		return -EAGAIN;
+
+	return 0;
+}
+
+/*
+ * Button infos for Microsoft Surface Book 2 and Surface Pro (2017).
+ * Obtained from DSDT/testing.
+ */
+static struct soc_button_info soc_button_MSHW0040[] = {
+	{ "power", 0, EV_KEY, KEY_POWER, false, true },
+	{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
+	{ "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false },
+	{ }
+};
+
+static struct soc_device_data soc_device_MSHW0040 = {
+	.button_info = soc_button_MSHW0040,
+	.check = soc_device_check_MSHW0040,
+};
+
+
 static const struct acpi_device_id soc_button_acpi_match[] = {
-	{ "PNP0C40", (unsigned long)soc_button_PNP0C40 },
-	{ "ACPI0011", 0 },
+	{ "PNP0C40", (unsigned long)&soc_device_PNP0C40 },
+	{ "ACPI0011", (unsigned long)&soc_device_ACPI0011 },
+
+	/* Microsoft Surface Devices (5th and 6th generation) */
+	{ "MSHW0040", (unsigned long)&soc_device_MSHW0040 },
+
 	{ }
 };

diff --git a/drivers/platform/x86/surfacepro3_button.c b/drivers/platform/x86/surfacepro3_button.c
index 1b491690ce07..eaec30380b11 100644
--- a/drivers/platform/x86/surfacepro3_button.c
+++ b/drivers/platform/x86/surfacepro3_button.c
@@ -24,6 +24,12 @@
 #define SURFACE_BUTTON_OBJ_NAME		"VGBI"
 #define SURFACE_BUTTON_DEVICE_NAME	"Surface Pro 3/4 Buttons"

+#define MSHW0040_DSM_REVISION		0x01
+#define MSHW0040_DSM_GET_OMPR		0x02	// get OEM Platform Revision
+static const guid_t MSHW0040_DSM_UUID =
+	GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65,
+		  0x49, 0x80, 0x35);
+
 #define SURFACE_BUTTON_NOTIFY_TABLET_MODE	0xc8

 #define SURFACE_BUTTON_NOTIFY_PRESS_POWER	0xc6
@@ -146,6 +152,34 @@ static int surface_button_resume(struct device *dev)
 }
 #endif

+/*
+ * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device
+ * ID (MSHW0040) for the power/volume buttons. Make sure this is the right
+ * device by checking for the _DSM method and OEM Platform Revision.
+ */
+static int surface_button_check_MSHW0040(struct acpi_device *dev)
+{
+	acpi_handle handle = dev->handle;
+	union acpi_object *result;
+	u64 oem_platform_rev = 0;
+
+	// get OEM platform revision
+	result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID,
+					 MSHW0040_DSM_REVISION,
+					 MSHW0040_DSM_GET_OMPR,
+					 NULL, ACPI_TYPE_INTEGER);
+
+	if (result) {
+		oem_platform_rev = result->integer.value;
+		ACPI_FREE(result);
+	}
+
+	dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev);
+
+	return oem_platform_rev == 0 ? 0 : -ENODEV;
+}
+
+
 static int surface_button_add(struct acpi_device *device)
 {
 	struct surface_button *button;
@@ -158,6 +192,10 @@ static int surface_button_add(struct acpi_device *device)
 	    strlen(SURFACE_BUTTON_OBJ_NAME)))
 		return -ENODEV;

+	error = surface_button_check_MSHW0040(device);
+	if (error)
+		return error;
+
 	button = kzalloc(sizeof(struct surface_button), GFP_KERNEL);
 	if (!button)
 		return -ENOMEM;
--
2.19.1

Build and make the kernel

Get the config of the current kernel

cat /proc/config.gz | gunzip > .config

Build

make -k

Install modules

sudo make modules_install

Kernel install script for kernel 5.2

Copy `/etc/mkinitcpio.d/linuxXX.preset` and adapt for current kernel version

# mkinitcpio preset file for the 'linux52' package

ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-5.2-x86_64"

PRESETS=('default' 'fallback')

#default_config="/etc/mkinitcpio.conf"
default_image="/boot/initramfs-5.2-x86_64.img"
#default_options=""

#fallback_config="/etc/mkinitcpio.conf"
fallback_image="/boot/initramfs-5.2-x86_64-fallback.img"
fallback_options="-S autodetect"

!#/bin/sh

cp arch/x86_64/boot/bzImage /boot/vmlinuz-5.2-x86_64
mkinitcpio -p linux52
update-grub

To get S3 sleep working

Following this gist: https://gist.github.com/e6e4f462dff2334aad84b6edd5181c09.git

sudo pacman -S iasl
sudo cat /sys/firmware/acpi/tables/DSDT > dsdt.aml
iasl -d dsdt.aml

apply this patch

--- dsdt.dsl~   2018-04-26 09:35:29.501055509 -0600
+++ dsdt.dsl    2018-04-26 09:36:23.769729028 -0600
@@ -18,7 +18,7 @@
  *     Compiler ID      "INTL"
  *     Compiler Version 0x20160527 (538314023)
  */
-DefinitionBlock ("", "DSDT", 2, "LENOVO", "SKL     ", 0x00000000)
+DefinitionBlock ("", "DSDT", 2, "LENOVO", "SKL     ", 0x00000001)
 {
     External (_GPE.TBNF, MethodObj)    // 0 Arguments
     External (_PR_.BGIA, UnknownObj)
@@ -415,9 +415,7 @@
     Name (SS1, 0x00)
     Name (SS2, 0x00)
     Name (SS3, One)
-    One
     Name (SS4, One)
-    One
     OperationRegion (GNVS, SystemMemory, 0x9FF4E000, 0x0771)
     Field (GNVS, AnyAcc, Lock, Preserve)
     {
@@ -27580,6 +27578,13 @@
         0x00,
         0x00
     })
+    Name (\_S3, Package (0x04)  // _S3_: S3 System State
+    {
+        0x05,
+        0x05,
+        0x00,
+        0x00
+    })
     Name (\_S4, Package (0x04)  // _S4_: S4 System State
     {
         0x06,

Ideally this would work:

patch --verbose < x1_dsdt.patch

However, for me it didn’t so I had to manually apply the edits.

iasl -ve -tc dsdt.dsl
cp dsdt.aml /boot/

fix this script for Manjaro

#! /bin/sh -e

# Uncomment to load custom ACPI table
GRUB_CUSTOM_ACPI="/boot/dsdt.aml"

# DON'T MODIFY ANYTHING BELOW THIS LINE!

prefix=/usr
exec_prefix=\${prefix}
datadir=\${exec_prefix}/share

. \${datadir}/grub/grub-mkconfig_lib

# Load custom ACPI table
if [ x\${GRUB_CUSTOM_ACPI} != x ] && [ -f \${GRUB_CUSTOM_ACPI} ] \\
        && is_path_readable_by_grub \${GRUB_CUSTOM_ACPI}; then
    echo "Found custom ACPI table: \${GRUB_CUSTOM_ACPI}" >&2
    prepare_grub_to_access_device \`\${grub_probe} --target=device \${GRUB_CUSTOM_ACPI}\` | sed -e "s/^/ /"
    cat << EOF
acpi (\\\$root)\`make_system_path_relative_to_its_root \${GRUB_CUSTOM_ACPI}\`
EOF
fi
sudo chmod 755 /etc/grub.d/01_acpi

S3 sleep

[2019-07-21 Sun 13:37] https://delta-xi.net/blog/?utm_source=share&utm_medium=ios_app#056 https://bbs.archlinux.org/viewtopic.php?id=234913 https://forums.lenovo.com/t5/Ubuntu/X1-Gen3-Tablet-Linux-support/td-p/4451259

Overriding a DSDT | 01.org

[2019-07-10 Wed 23:20] https://01.org/linux-acpi/documentation/overriding-dsdt

Switching tty based on keyboard attachment

For development I prefer a minimal DE with a tiling WM, however whenever I switch to tablet-mode (i.e., when the keyboard is disconnected). I’d like to use the touch interface in a full DE (e.g., gnome-shell). I trigger the following snippet on the tablet-mode acpi event to accomplish this:

#!/bin/sh

case "$1" in
    tablet)
	chvt "$(((ps hotty -C gnome-shell | grep "tty") || echo 'tty1') | sed s/tty//)"
	;;
    *)
	chvt 3
	;;
esac

This changes the tty to whichever one is running gnome-shell whenever the keyboard is detached and changes it back to tty3 where I run my tiliing WM otherwise.

Add the following to /etc/acpi/handler.sh to accomplish the triggering

   video/tabletmode)
	case "$2" in
	    TBLT)
		logger 'tablet mode toggled'
		case "$4" in
		    00000001)
			logger 'tablet mode'
			/path/to/script.sh tablet
			;;
		    *)
			logger 'normal mode'
			/path/to/script.sh normal
			;;
		esac
		;;
	    *)
		logger "ACPI action undefined: $3"
		;;
       esac
       ;;

Change touchscreen driver to libinput

/usr/share/X11/xorg.conf.d/70-wacom.conf

About