Commit cf6473b9 authored by Jiri Kosina's avatar Jiri Kosina
Browse files

Merge branch 'for-6.14/lenovo' into for-linus

- improved support for Thinkpad-X12-TAB-1/2 (Vishnu Sankar)
parents 2ec37df7 52e7d1f7
Loading
Loading
Loading
Loading
+108 −1
Original line number Diff line number Diff line
@@ -32,11 +32,22 @@
#include <linux/leds.h>
#include <linux/workqueue.h>

#if IS_ENABLED(CONFIG_ACPI_PLATFORM_PROFILE)
#include <linux/platform_profile.h>
#endif /* CONFIG_ACPI_PLATFORM_PROFILE */

#include "hid-ids.h"

/* Userspace expects F20 for mic-mute KEY_MICMUTE does not work */
#define LENOVO_KEY_MICMUTE KEY_F20

/* HID raw events for ThinkPad X12 Tabs*/
#define TP_X12_RAW_HOTKEY_FN_F4		0x00020003
#define TP_X12_RAW_HOTKEY_FN_F8		0x38001003
#define TP_X12_RAW_HOTKEY_FN_F10	0x00000803
#define TP_X12_RAW_HOTKEY_FN_F12	0x00000403
#define TP_X12_RAW_HOTKEY_FN_SPACE	0x18001003

struct lenovo_drvdata {
	u8 led_report[3]; /* Must be first for proper alignment */
	int led_state;
@@ -71,6 +82,14 @@ struct lenovo_drvdata {
#define TP10UBKBD_LED_OFF		1
#define TP10UBKBD_LED_ON		2

/* Function to report raw_events as key events*/
static inline void report_key_event(struct input_dev *input, int keycode)
{
	input_report_key(input, keycode, 1);
	input_report_key(input, keycode, 0);
	input_sync(input);
}

static int lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code,
				    enum led_brightness value)
{
@@ -472,6 +491,8 @@ static int lenovo_input_mapping(struct hid_device *hdev,
	case USB_DEVICE_ID_LENOVO_TP10UBKBD:
		return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field,
							       usage, bit, max);
	case USB_DEVICE_ID_LENOVO_X12_TAB:
	case USB_DEVICE_ID_LENOVO_X12_TAB2:
	case USB_DEVICE_ID_LENOVO_X1_TAB:
	case USB_DEVICE_ID_LENOVO_X1_TAB3:
		return lenovo_input_mapping_x1_tab_kbd(hdev, hi, field, usage, bit, max);
@@ -582,6 +603,8 @@ static ssize_t attr_fn_lock_store(struct device *dev,
	case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
		lenovo_features_set_cptkbd(hdev);
		break;
	case USB_DEVICE_ID_LENOVO_X12_TAB:
	case USB_DEVICE_ID_LENOVO_X12_TAB2:
	case USB_DEVICE_ID_LENOVO_TP10UBKBD:
	case USB_DEVICE_ID_LENOVO_X1_TAB:
	case USB_DEVICE_ID_LENOVO_X1_TAB3:
@@ -680,6 +703,62 @@ static const struct attribute_group lenovo_attr_group_cptkbd = {
	.attrs = lenovo_attributes_cptkbd,
};

/* Function to handle Lenovo Thinkpad TAB X12's HID raw inputs for fn keys*/
static int lenovo_raw_event_TP_X12_tab(struct hid_device *hdev, u32 raw_data)
{
	struct hid_input *hidinput;
	struct input_dev *input = NULL;

	/* Iterate through all associated input devices */
	list_for_each_entry(hidinput, &hdev->inputs, list) {
		input = hidinput->input;
		if (!input)
			continue;

		switch (raw_data) {
			/* fn-F20 being used here for MIC mute*/
		case TP_X12_RAW_HOTKEY_FN_F4:
			report_key_event(input, LENOVO_KEY_MICMUTE);
			return 1;
		/* Power-mode or Airplane mode will be called based on the device*/
		case TP_X12_RAW_HOTKEY_FN_F8:
			/*
			 * TP X12 TAB uses Fn-F8 calls Airplanemode
			 * Whereas TP X12 TAB2 uses Fn-F8 for toggling
			 * Power modes
			 */
			if (hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB) {
				report_key_event(input, KEY_RFKILL);
				return 1;
			}
#if IS_ENABLED(CONFIG_ACPI_PLATFORM_PROFILE)
			else {
				platform_profile_cycle();
				return 1;
			}
#endif /* CONFIG_ACPI_PLATFORM_PROFILE */
			return 0;
		case TP_X12_RAW_HOTKEY_FN_F10:
			/* TAB1 has PICKUP Phone and TAB2 use Snipping tool*/
			(hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB) ?
			report_key_event(input, KEY_PICKUP_PHONE) :
			report_key_event(input, KEY_SELECTIVE_SCREENSHOT);
			return 1;
		case TP_X12_RAW_HOTKEY_FN_F12:
			/* BookMarks/STAR key*/
			report_key_event(input, KEY_BOOKMARKS);
			return 1;
		case TP_X12_RAW_HOTKEY_FN_SPACE:
			/* Keyboard LED backlight toggle*/
			report_key_event(input, KEY_KBDILLUMTOGGLE);
			return 1;
		default:
			break;
		}
	}
	return 0;
}

static int lenovo_raw_event(struct hid_device *hdev,
			struct hid_report *report, u8 *data, int size)
{
@@ -697,6 +776,15 @@ static int lenovo_raw_event(struct hid_device *hdev,
		data[2] = 0x01;
	}

	/*
	 * Lenovo TP X12 Tab KBD's Fn+XX is HID raw data defined. Report ID is 0x03
	 * e.g.: Raw data received for MIC mute is 0x00020003.
	 */
	if (unlikely((hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB
			|| hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB2)
			&& size >= 3 && report->id == 0x03))
		return lenovo_raw_event_TP_X12_tab(hdev, le32_to_cpu(*(u32 *)data));

	return 0;
}

@@ -776,6 +864,8 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
	case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
	case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
		return lenovo_event_cptkbd(hdev, field, usage, value);
	case USB_DEVICE_ID_LENOVO_X12_TAB:
	case USB_DEVICE_ID_LENOVO_X12_TAB2:
	case USB_DEVICE_ID_LENOVO_TP10UBKBD:
	case USB_DEVICE_ID_LENOVO_X1_TAB:
	case USB_DEVICE_ID_LENOVO_X1_TAB3:
@@ -1057,6 +1147,8 @@ static int lenovo_led_brightness_set(struct led_classdev *led_cdev,
	case USB_DEVICE_ID_LENOVO_TPKBD:
		lenovo_led_set_tpkbd(hdev);
		break;
	case USB_DEVICE_ID_LENOVO_X12_TAB:
	case USB_DEVICE_ID_LENOVO_X12_TAB2:
	case USB_DEVICE_ID_LENOVO_TP10UBKBD:
	case USB_DEVICE_ID_LENOVO_X1_TAB:
	case USB_DEVICE_ID_LENOVO_X1_TAB3:
@@ -1243,8 +1335,15 @@ static int lenovo_probe_tp10ubkbd(struct hid_device *hdev)
	 * We cannot read the state, only set it, so we force it to on here
	 * (which should be a no-op) to make sure that our state matches the
	 * keyboard's FN-lock state. This is the same as what Windows does.
	 *
	 * For X12 TAB and TAB2, the default windows behaviour Fn-lock Off.
	 * Adding additional check to ensure the behaviour in case of
	 * Thinkpad X12 Tabs.
	 */
	data->fn_lock = true;

	data->fn_lock = !(hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB ||
			hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB2);

	lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, data->fn_lock);

	ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
@@ -1288,6 +1387,8 @@ static int lenovo_probe(struct hid_device *hdev,
	case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
		ret = lenovo_probe_cptkbd(hdev);
		break;
	case USB_DEVICE_ID_LENOVO_X12_TAB:
	case USB_DEVICE_ID_LENOVO_X12_TAB2:
	case USB_DEVICE_ID_LENOVO_TP10UBKBD:
	case USB_DEVICE_ID_LENOVO_X1_TAB:
	case USB_DEVICE_ID_LENOVO_X1_TAB3:
@@ -1375,6 +1476,8 @@ static void lenovo_remove(struct hid_device *hdev)
	case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
		lenovo_remove_cptkbd(hdev);
		break;
	case USB_DEVICE_ID_LENOVO_X12_TAB:
	case USB_DEVICE_ID_LENOVO_X12_TAB2:
	case USB_DEVICE_ID_LENOVO_TP10UBKBD:
	case USB_DEVICE_ID_LENOVO_X1_TAB:
	case USB_DEVICE_ID_LENOVO_X1_TAB3:
@@ -1429,6 +1532,10 @@ static const struct hid_device_id lenovo_devices[] = {
		     USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB) },
	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
		     USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB3) },
	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
		     USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB) },
	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
		     USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB2) },
	{ }
};