lede/target/linux/silicon/patches-5.19/0123-HID-magicmouse-add-support-for-Macbook-trackpads.patch

359 lines
12 KiB
Diff

From 89d8ef52b73399098877813502dc8320f7f07a69 Mon Sep 17 00:00:00 2001
From: Janne Grunau <j@jannau.net>
Date: Thu, 16 Dec 2021 01:17:48 +0100
Subject: [PATCH 123/171] HID: magicmouse: add support for Macbook trackpads
The trackpads in Macbooks beginning in 2015 are HID devices connected
over SPI. On Intel Macbooks they are currently supported by applespi.c.
This chang adds support for the trackpads on Apple Silicon Macbooks
starting in late 2020. They use a new HID over SPI transport driver.
The touch report format differs from USB/BT Magic Trackpads. It is the
same format as the type 4 format supported by bcm5974.c.
Signed-off-by: Janne Grunau <j@jannau.net>
---
drivers/hid/Kconfig | 4 +-
drivers/hid/hid-magicmouse.c | 259 ++++++++++++++++++++++++++++++++++-
2 files changed, 260 insertions(+), 3 deletions(-)
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 77c3c00c877c..abaf704d5a81 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -676,11 +676,13 @@ config LOGIWHEELS_FF
config HID_MAGICMOUSE
tristate "Apple Magic Mouse/Trackpad multi-touch support"
depends on HID
+ default SPI_HID_APPLE
help
Support for the Apple Magic Mouse/Trackpad multi-touch.
Say Y here if you want support for the multi-touch features of the
- Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad.
+ Apple Wireless "Magic" Mouse, the Apple Wireless "Magic" Trackpad and
+ fource touch Trackpads in Macbooks starting from 2015.
config HID_MALTRON
tristate "Maltron L90 keyboard"
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 734e97234890..65c4b6307ce9 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -113,6 +113,18 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
#define TRACKPAD2_RES_Y \
((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100))
+#define J314_TP_DIMENSION_X (float)13000
+#define J314_TP_MIN_X -5900
+#define J314_TP_MAX_X 6500
+#define J314_TP_RES_X \
+ ((J314_TP_MAX_X - J314_TP_MIN_X) / (J314_TP_DIMENSION_X / 100))
+#define J314_TP_DIMENSION_Y (float)8100
+#define J314_TP_MIN_Y -200
+#define J314_TP_MAX_Y 7400
+#define J314_TP_RES_Y \
+ ((J314_TP_MAX_Y - J314_TP_MIN_Y) / (J314_TP_DIMENSION_Y / 100))
+
+#define J314_TP_MAX_FINGER_ORIENTATION 16384
struct magicmouse_input_ops {
int (*raw_event)(struct hid_device *hdev,
@@ -519,6 +531,157 @@ static int magicmouse_raw_event_usb(struct hid_device *hdev,
return 1;
}
+/**
+ * struct tp_finger - single trackpad finger structure, le16-aligned
+ *
+ * @unknown1: unknown
+ * @unknown2: unknown
+ * @abs_x: absolute x coordinate
+ * @abs_y: absolute y coordinate
+ * @rel_x: relative x coordinate
+ * @rel_y: relative y coordinate
+ * @tool_major: tool area, major axis
+ * @tool_minor: tool area, minor axis
+ * @orientation: 16384 when point, else 15 bit angle
+ * @touch_major: touch area, major axis
+ * @touch_minor: touch area, minor axis
+ * @unused: zeros
+ * @pressure: pressure on forcetouch touchpad
+ * @multi: one finger: varies, more fingers: constant
+ * @crc16: on last finger: crc over the whole message struct
+ * (i.e. message header + this struct) minus the last
+ * @crc16 field; unknown on all other fingers.
+ */
+struct tp_finger {
+ __le16 unknown1;
+ __le16 unknown2;
+ __le16 abs_x;
+ __le16 abs_y;
+ __le16 rel_x;
+ __le16 rel_y;
+ __le16 tool_major;
+ __le16 tool_minor;
+ __le16 orientation;
+ __le16 touch_major;
+ __le16 touch_minor;
+ __le16 unused[2];
+ __le16 pressure;
+ __le16 multi;
+} __attribute__((packed, aligned(2)));
+
+/**
+ * struct trackpad report
+ *
+ * @report_id: reportid
+ * @buttons: HID Usage Buttons 3 1-bit reports
+ * @num_fingers: the number of fingers being reported in @fingers
+ * @clicked: same as @buttons
+ */
+struct tp_header {
+ // HID mouse report
+ u8 report_id;
+ u8 buttons;
+ u8 rel_x;
+ u8 rel_y;
+ u8 padding[4];
+ // HID vendor part, up to 1751 bytes
+ u8 unknown[22];
+ u8 num_fingers;
+ u8 clicked;
+ u8 unknown3[14];
+};
+
+static inline int le16_to_int(__le16 x)
+{
+ return (signed short)le16_to_cpu(x);
+}
+
+static void report_finger_data(struct input_dev *input, int slot,
+ const struct input_mt_pos *pos,
+ const struct tp_finger *f)
+{
+ input_mt_slot(input, slot);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR,
+ le16_to_int(f->touch_major) << 1);
+ input_report_abs(input, ABS_MT_TOUCH_MINOR,
+ le16_to_int(f->touch_minor) << 1);
+ input_report_abs(input, ABS_MT_WIDTH_MAJOR,
+ le16_to_int(f->tool_major) << 1);
+ input_report_abs(input, ABS_MT_WIDTH_MINOR,
+ le16_to_int(f->tool_minor) << 1);
+ input_report_abs(input, ABS_MT_ORIENTATION,
+ J314_TP_MAX_FINGER_ORIENTATION - le16_to_int(f->orientation));
+ input_report_abs(input, ABS_MT_PRESSURE, le16_to_int(f->pressure));
+ input_report_abs(input, ABS_MT_POSITION_X, pos->x);
+ input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
+}
+
+static int magicmouse_raw_event_spi(struct hid_device *hdev,
+ struct hid_report *report, u8 *data, int size)
+{
+ struct magicmouse_sc *msc = hid_get_drvdata(hdev);
+ struct input_dev *input = msc->input;
+ struct tp_header *tp_hdr;
+ struct tp_finger *f;
+ int i, n;
+ u32 npoints;
+ const size_t hdr_sz = sizeof(struct tp_header);
+ const size_t touch_sz = sizeof(struct tp_finger);
+ u8 map_contacs[MAX_CONTACTS];
+
+ // hid_warn(hdev, "%s\n", __func__);
+ // print_hex_dump_debug("appleft ev: ", DUMP_PREFIX_OFFSET, 16, 1, data,
+ // size, false);
+
+ if (data[0] != TRACKPAD2_USB_REPORT_ID)
+ return 0;
+
+ /* Expect 46 bytes of prefix, and N * 30 bytes of touch data. */
+ if (size < hdr_sz || ((size - hdr_sz) % touch_sz) != 0)
+ return 0;
+
+ tp_hdr = (struct tp_header *)data;
+
+ npoints = (size - hdr_sz) / touch_sz;
+ if (npoints < tp_hdr->num_fingers || npoints > MAX_CONTACTS) {
+ hid_warn(hdev,
+ "unexpected number of touches (%u) for "
+ "report\n",
+ npoints);
+ return 0;
+ }
+
+ n = 0;
+ for (i = 0; i < tp_hdr->num_fingers; i++) {
+ f = (struct tp_finger *)(data + hdr_sz + i * touch_sz);
+ if (le16_to_int(f->touch_major) == 0)
+ continue;
+
+ hid_dbg(hdev, "ev x:%04hx y:%04hx\n", le16_to_int(f->abs_x),
+ le16_to_int(f->abs_y));
+ msc->pos[n].x = le16_to_int(f->abs_x);
+ msc->pos[n].y = -le16_to_int(f->abs_y);
+ map_contacs[n] = i;
+ n++;
+ }
+
+ input_mt_assign_slots(input, msc->tracking_ids, msc->pos, n, 0);
+
+ for (i = 0; i < n; i++) {
+ int idx = map_contacs[i];
+ f = (struct tp_finger *)(data + hdr_sz + idx * touch_sz);
+ report_finger_data(input, msc->tracking_ids[i], &msc->pos[i], f);
+ }
+
+ input_mt_sync_frame(input);
+ input_report_key(input, BTN_MOUSE, data[1] & 1);
+
+ input_sync(input);
+ return 1;
+}
+
static int magicmouse_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
@@ -698,6 +861,79 @@ static int magicmouse_setup_input_usb(struct input_dev *input,
return 0;
}
+static int magicmouse_setup_input_spi(struct input_dev *input,
+ struct hid_device *hdev)
+{
+ int error;
+ int mt_flags = 0;
+
+ __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+ __clear_bit(BTN_0, input->keybit);
+ __clear_bit(BTN_RIGHT, input->keybit);
+ __clear_bit(BTN_MIDDLE, input->keybit);
+ __clear_bit(EV_REL, input->evbit);
+ __clear_bit(REL_X, input->relbit);
+ __clear_bit(REL_Y, input->relbit);
+
+ mt_flags = INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK;
+
+ /* finger touch area */
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 5000, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 5000, 0, 0);
+
+ /* finger approach area */
+ input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 5000, 0, 0);
+ input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 5000, 0, 0);
+
+ /* Note: Touch Y position from the device is inverted relative
+ * to how pointer motion is reported (and relative to how USB
+ * HID recommends the coordinates work). This driver keeps
+ * the origin at the same position, and just uses the additive
+ * inverse of the reported Y.
+ */
+
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0, 6000, 0, 0);
+
+ /*
+ * This makes libinput recognize this as a PressurePad and
+ * stop trying to use pressure for touch size. Pressure unit
+ * seems to be ~grams on these touchpads.
+ */
+ input_abs_set_res(input, ABS_MT_PRESSURE, 1);
+
+ /* finger orientation */
+ input_set_abs_params(input, ABS_MT_ORIENTATION, -J314_TP_MAX_FINGER_ORIENTATION,
+ J314_TP_MAX_FINGER_ORIENTATION, 0, 0);
+
+ /* finger position */
+ input_set_abs_params(input, ABS_MT_POSITION_X, J314_TP_MIN_X, J314_TP_MAX_X,
+ 0, 0);
+ /* Y axis is inverted */
+ input_set_abs_params(input, ABS_MT_POSITION_Y, -J314_TP_MAX_Y, -J314_TP_MIN_Y,
+ 0, 0);
+
+ /* X/Y resolution */
+ input_abs_set_res(input, ABS_MT_POSITION_X, J314_TP_RES_X);
+ input_abs_set_res(input, ABS_MT_POSITION_Y, J314_TP_RES_Y);
+
+ input_set_events_per_packet(input, 60);
+
+ /* touchpad button */
+ input_set_capability(input, EV_KEY, BTN_MOUSE);
+
+ /*
+ * hid-input may mark device as using autorepeat, but the trackpad does
+ * not actually want it.
+ */
+ __clear_bit(EV_REP, input->evbit);
+
+ error = input_mt_init_slots(input, MAX_CONTACTS, mt_flags);
+ if (error)
+ return error;
+
+ return 0;
+}
+
static int magicmouse_input_mapping(struct hid_device *hdev,
struct hid_input *hi, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max)
@@ -753,6 +989,9 @@ static int magicmouse_enable_multitouch(struct hid_device *hdev)
feature_size = sizeof(feature_mt_trackpad2_usb);
feature = feature_mt_trackpad2_usb;
}
+ } else if (hdev->vendor == SPI_VENDOR_ID_APPLE) {
+ feature_size = sizeof(feature_mt_trackpad2_usb);
+ feature = feature_mt_trackpad2_usb;
} else if (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
feature_size = sizeof(feature_mt_mouse2);
feature = feature_mt_mouse2;
@@ -827,14 +1066,26 @@ static int magicmouse_probe(struct hid_device *hdev,
struct hid_report *report;
int ret;
+ if (id->bus == BUS_SPI && id->vendor == SPI_VENDOR_ID_APPLE &&
+ hdev->type != HID_TYPE_SPI_MOUSE)
+ return -ENODEV;
+
msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
if (msc == NULL) {
hid_err(hdev, "can't alloc magicmouse descriptor\n");
return -ENOMEM;
}
- msc->input_ops.raw_event = magicmouse_raw_event_usb;
- msc->input_ops.setup_input = magicmouse_setup_input_usb;
+ // internal trackpad use a data format use input ops to avoid
+ // conflicts with the report ID.
+ if (id->vendor == SPI_VENDOR_ID_APPLE) {
+ msc->input_ops.raw_event = magicmouse_raw_event_spi;
+ msc->input_ops.setup_input = magicmouse_setup_input_spi;
+
+ } else {
+ msc->input_ops.raw_event = magicmouse_raw_event_usb;
+ msc->input_ops.setup_input = magicmouse_setup_input_usb;
+ }
msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
msc->hdev = hdev;
@@ -884,6 +1135,8 @@ static int magicmouse_probe(struct hid_device *hdev,
else /* USB_VENDOR_ID_APPLE */
report = hid_register_report(hdev, HID_INPUT_REPORT,
TRACKPAD2_USB_REPORT_ID, 0);
+ } else if (id->vendor == SPI_VENDOR_ID_APPLE) {
+ report = hid_register_report(hdev, HID_INPUT_REPORT, 2, 0);
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
report = hid_register_report(hdev, HID_INPUT_REPORT,
TRACKPAD_REPORT_ID, 0);
@@ -978,6 +1231,8 @@ static const struct hid_device_id magic_mice[] = {
USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
+ { HID_SPI_DEVICE(SPI_VENDOR_ID_APPLE, HID_ANY_ID),
+ .driver_data = 0 },
{ }
};
MODULE_DEVICE_TABLE(hid, magic_mice);
--
2.34.1