From 0fb18a674036760d784646853b00a71aedcc769a Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Thu, 21 Sep 2023 11:25:48 +0530 Subject: [PATCH] Add the new touch bar driver --- ...per-for-finding-a-field-with-a-certa.patch | 115 ++ ...bl-add-driver-for-the-backlight-of-A.patch | 299 +++ ...kbd-add-driver-for-the-keyboard-mode.patch | 404 +++++ ...e-Add-Apple-iBridge-HID-driver-for-T.patch | 750 -------- ...-support-getting-the-contact-ID-from.patch | 40 + ...ar-Add-driver-for-the-Touch-Bar-on-M.patch | 1602 ----------------- ...upport-getting-the-tip-state-from-HI.patch | 58 + ...ake-cls-maxcontacts-into-account-for.patch | 42 + ...llow-specifying-if-a-device-is-direc.patch | 57 + ...h-add-device-ID-for-Apple-Touch-Bars.patch | 94 + ...-support-for-generic-FOURCCs-by-exte.patch | 197 ++ ...-add-shutdown-callback-to-usb_driver.patch | 94 + ...r-add-helper-for-BGR888-to-XRGB8888-.patch | 232 +++ ...ver-for-Apple-Touch-Bars-in-x86-Macs.patch | 710 ++++++++ ...ds-standardise-keyboard-backlight-le.patch | 0 ...gic-backlight-Add-driver-for-keyboar.patch | 38 +- 16 files changed, 2361 insertions(+), 2371 deletions(-) create mode 100644 1004-HID-core-add-helper-for-finding-a-field-with-a-certa.patch create mode 100644 1005-HID-hid-appletb-bl-add-driver-for-the-backlight-of-A.patch create mode 100644 1006-HID-hid-appletb-kbd-add-driver-for-the-keyboard-mode.patch delete mode 100644 1007-HID-apple-ibridge-Add-Apple-iBridge-HID-driver-for-T.patch create mode 100644 1007-HID-multitouch-support-getting-the-contact-ID-from.patch delete mode 100644 1008-HID-apple-touchbar-Add-driver-for-the-Touch-Bar-on-M.patch create mode 100644 1008-HID-multitouch-support-getting-the-tip-state-from-HI.patch create mode 100644 1009-HID-multitouch-take-cls-maxcontacts-into-account-for.patch create mode 100644 1010-HID-multitouch-allow-specifying-if-a-device-is-direc.patch create mode 100644 1011-HID-multitouch-add-device-ID-for-Apple-Touch-Bars.patch create mode 100644 1012-lib-vsprintf-Add-support-for-generic-FOURCCs-by-exte.patch create mode 100644 1013-USB-core-add-shutdown-callback-to-usb_driver.patch create mode 100644 1014-drm-format-helper-add-helper-for-BGR888-to-XRGB8888-.patch create mode 100644 1015-drm-tiny-add-driver-for-Apple-Touch-Bars-in-x86-Macs.patch rename 1005-Documentation-leds-standardise-keyboard-backlight-le.patch => 1016-Documentation-leds-standardise-keyboard-backlight-le.patch (100%) rename 1006-HID-hid-apple-magic-backlight-Add-driver-for-keyboar.patch => 1017-HID-hid-apple-magic-backlight-Add-driver-for-keyboar.patch (89%) diff --git a/1004-HID-core-add-helper-for-finding-a-field-with-a-certa.patch b/1004-HID-core-add-helper-for-finding-a-field-with-a-certa.patch new file mode 100644 index 0000000..aef0af8 --- /dev/null +++ b/1004-HID-core-add-helper-for-finding-a-field-with-a-certa.patch @@ -0,0 +1,115 @@ +From 75ca57b64ce6846622d8aefac5a76fc638a2123d Mon Sep 17 00:00:00 2001 +From: Kerem Karabay +Date: Sun, 5 Mar 2023 19:12:53 +0300 +Subject: [PATCH 01/12] HID: core: add helper for finding a field with a + certain usage + +This helper will allow HID drivers to easily determine if they should +bind to a hid_device by checking for the prescence of a certain field +when its ID is not enough, which can be the case on USB devices with +multiple interfaces and/or configurations. + +Signed-off-by: Kerem Karabay +--- + drivers/hid/hid-core.c | 25 +++++++++++++++++++++++++ + drivers/hid/hid-google-hammer.c | 27 ++------------------------- + include/linux/hid.h | 2 ++ + 3 files changed, 29 insertions(+), 25 deletions(-) + +diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c +index 8992e3c1e..6395bdc2e 100644 +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -1906,6 +1906,31 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) + } + EXPORT_SYMBOL_GPL(hid_set_field); + ++struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_type, ++ unsigned int application, unsigned int usage) ++{ ++ struct list_head *report_list = &hdev->report_enum[report_type].report_list; ++ struct hid_report *report; ++ int i, j; ++ ++ list_for_each_entry(report, report_list, list) { ++ if (report->application != application) ++ continue; ++ ++ for (i = 0; i < report->maxfield; i++) { ++ struct hid_field *field = report->field[i]; ++ ++ for (j = 0; j < field->maxusage; j++) { ++ if (field->usage[j].hid == usage) ++ return field; ++ } ++ } ++ } ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(hid_find_field); ++ + static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, + const u8 *data) + { +diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c +index c6bdb9c4e..fba3652aa 100644 +--- a/drivers/hid/hid-google-hammer.c ++++ b/drivers/hid/hid-google-hammer.c +@@ -419,38 +419,15 @@ static int hammer_event(struct hid_device *hid, struct hid_field *field, + return 0; + } + +-static bool hammer_has_usage(struct hid_device *hdev, unsigned int report_type, +- unsigned application, unsigned usage) +-{ +- struct hid_report_enum *re = &hdev->report_enum[report_type]; +- struct hid_report *report; +- int i, j; +- +- list_for_each_entry(report, &re->report_list, list) { +- if (report->application != application) +- continue; +- +- for (i = 0; i < report->maxfield; i++) { +- struct hid_field *field = report->field[i]; +- +- for (j = 0; j < field->maxusage; j++) +- if (field->usage[j].hid == usage) +- return true; +- } +- } +- +- return false; +-} +- + static bool hammer_has_folded_event(struct hid_device *hdev) + { +- return hammer_has_usage(hdev, HID_INPUT_REPORT, ++ return !!hid_find_field(hdev, HID_INPUT_REPORT, + HID_GD_KEYBOARD, HID_USAGE_KBD_FOLDED); + } + + static bool hammer_has_backlight_control(struct hid_device *hdev) + { +- return hammer_has_usage(hdev, HID_OUTPUT_REPORT, ++ return !!hid_find_field(hdev, HID_OUTPUT_REPORT, + HID_GD_KEYBOARD, HID_AD_BRIGHTNESS); + } + +diff --git a/include/linux/hid.h b/include/linux/hid.h +index 39e21e381..9520fdfdd 100644 +--- a/include/linux/hid.h ++++ b/include/linux/hid.h +@@ -913,6 +913,8 @@ extern void hidinput_report_event(struct hid_device *hid, struct hid_report *rep + extern int hidinput_connect(struct hid_device *hid, unsigned int force); + extern void hidinput_disconnect(struct hid_device *); + ++struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_type, ++ unsigned int application, unsigned int usage); + int hid_set_field(struct hid_field *, unsigned, __s32); + int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt); +-- +2.42.0 + diff --git a/1005-HID-hid-appletb-bl-add-driver-for-the-backlight-of-A.patch b/1005-HID-hid-appletb-bl-add-driver-for-the-backlight-of-A.patch new file mode 100644 index 0000000..3194028 --- /dev/null +++ b/1005-HID-hid-appletb-bl-add-driver-for-the-backlight-of-A.patch @@ -0,0 +1,299 @@ +From 05cd738ce1c0e1a930a1dab02528fd9f1c702c38 Mon Sep 17 00:00:00 2001 +From: Kerem Karabay +Date: Sun, 5 Mar 2023 18:52:43 +0300 +Subject: [PATCH 02/12] HID: hid-appletb-bl: add driver for the backlight of + Apple Touch Bars +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This commit adds a driver for the backlight of Apple Touch Bars on x86 +Macs. Note that currently only T2 Macs are supported. + +This driver is based on previous work done by Ronald Tschalär +. + +Signed-off-by: Kerem Karabay +--- + MAINTAINERS | 6 ++ + drivers/hid/Kconfig | 10 ++ + drivers/hid/Makefile | 1 + + drivers/hid/hid-appletb-bl.c | 193 +++++++++++++++++++++++++++++++++++ + drivers/hid/hid-quirks.c | 4 +- + 5 files changed, 213 insertions(+), 1 deletion(-) + create mode 100644 drivers/hid/hid-appletb-bl.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index 4cc6bf79f..519b3b736 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -9157,6 +9157,12 @@ F: include/linux/pm.h + F: include/linux/suspend.h + F: kernel/power/ + ++HID APPLE TOUCH BAR DRIVERS ++M: Kerem Karabay ++L: linux-input@vger.kernel.org ++S: Maintained ++F: drivers/hid/hid-appletb-* ++ + HID CORE LAYER + M: Jiri Kosina + M: Benjamin Tissoires +diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig +index e11c1c803..cf19a3b33 100644 +--- a/drivers/hid/Kconfig ++++ b/drivers/hid/Kconfig +@@ -148,6 +148,16 @@ config HID_APPLEIR + + Say Y here if you want support for Apple infrared remote control. + ++config HID_APPLETB_BL ++ tristate "Apple Touch Bar Backlight" ++ depends on BACKLIGHT_CLASS_DEVICE ++ help ++ Say Y here if you want support for the backlight of Touch Bars on x86 ++ MacBook Pros. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called hid-appletb-bl. ++ + config HID_ASUS + tristate "Asus" + depends on USB_HID +diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile +index 7a9e16015..bc86e38b2 100644 +--- a/drivers/hid/Makefile ++++ b/drivers/hid/Makefile +@@ -29,6 +29,7 @@ obj-$(CONFIG_HID_ALPS) += hid-alps.o + obj-$(CONFIG_HID_ACRUX) += hid-axff.o + obj-$(CONFIG_HID_APPLE) += hid-apple.o + obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o ++obj-$(CONFIG_HID_APPLETB_BL) += hid-appletb-bl.o + obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o + obj-$(CONFIG_HID_ASUS) += hid-asus.o + obj-$(CONFIG_HID_AUREAL) += hid-aureal.o +diff --git a/drivers/hid/hid-appletb-bl.c b/drivers/hid/hid-appletb-bl.c +new file mode 100644 +index 000000000..0c5e4b776 +--- /dev/null ++++ b/drivers/hid/hid-appletb-bl.c +@@ -0,0 +1,193 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Apple Touch Bar Backlight Driver ++ * ++ * Copyright (c) 2017-2018 Ronald Tschalär ++ * Copyright (c) 2022-2023 Kerem Karabay ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++ ++#include "hid-ids.h" ++ ++#define APPLETB_BL_ON 1 ++#define APPLETB_BL_DIM 3 ++#define APPLETB_BL_OFF 4 ++ ++#define HID_UP_APPLEVENDOR_TB_BL 0xff120000 ++ ++#define HID_VD_APPLE_TB_BRIGHTNESS 0xff120001 ++#define HID_USAGE_AUX1 0xff120020 ++#define HID_USAGE_BRIGHTNESS 0xff120021 ++ ++struct appletb_bl { ++ struct hid_field *aux1_field, *brightness_field; ++ struct backlight_device *bdev; ++ ++ bool full_on; ++}; ++ ++const u8 appletb_bl_brightness_map[] = { ++ APPLETB_BL_OFF, ++ APPLETB_BL_DIM, ++ APPLETB_BL_ON ++}; ++ ++static int appletb_bl_set_brightness(struct appletb_bl *bl, u8 brightness) ++{ ++ struct hid_report *report = bl->brightness_field->report; ++ struct hid_device *hdev = report->device; ++ int ret; ++ ++ ret = hid_set_field(bl->aux1_field, 0, 1); ++ if (ret) { ++ hid_err(hdev, "Failed to set auxiliary field (%pe)\n", ERR_PTR(ret)); ++ return ret; ++ } ++ ++ ret = hid_set_field(bl->brightness_field, 0, brightness); ++ if (ret) { ++ hid_err(hdev, "Failed to set brightness field (%pe)\n", ERR_PTR(ret)); ++ return ret; ++ } ++ ++ if (!bl->full_on) { ++ ret = hid_hw_power(hdev, PM_HINT_FULLON); ++ if (ret < 0) { ++ hid_err(hdev, "Device didn't power on (%pe)\n", ERR_PTR(ret)); ++ return ret; ++ } ++ ++ bl->full_on = true; ++ } ++ ++ hid_hw_request(hdev, report, HID_REQ_SET_REPORT); ++ ++ if (brightness == APPLETB_BL_OFF) { ++ hid_hw_power(hdev, PM_HINT_NORMAL); ++ bl->full_on = false; ++ } ++ ++ return 0; ++} ++ ++static int appletb_bl_update_status(struct backlight_device *bdev) ++{ ++ struct appletb_bl *bl = bl_get_data(bdev); ++ u16 brightness; ++ ++ if (bdev->props.state & BL_CORE_SUSPENDED) ++ brightness = 0; ++ else ++ brightness = backlight_get_brightness(bdev); ++ ++ return appletb_bl_set_brightness(bl, appletb_bl_brightness_map[brightness]); ++} ++ ++static const struct backlight_ops appletb_bl_backlight_ops = { ++ .options = BL_CORE_SUSPENDRESUME, ++ .update_status = appletb_bl_update_status, ++}; ++ ++static int appletb_bl_probe(struct hid_device *hdev, const struct hid_device_id *id) ++{ ++ struct hid_field *aux1_field, *brightness_field; ++ struct backlight_properties bl_props = { 0 }; ++ struct device *dev = &hdev->dev; ++ struct appletb_bl *bl; ++ int ret; ++ ++ ret = hid_parse(hdev); ++ if (ret) ++ return dev_err_probe(dev, ret, "HID parse failed\n"); ++ ++ aux1_field = hid_find_field(hdev, HID_FEATURE_REPORT, ++ HID_VD_APPLE_TB_BRIGHTNESS, HID_USAGE_AUX1); ++ ++ brightness_field = hid_find_field(hdev, HID_FEATURE_REPORT, ++ HID_VD_APPLE_TB_BRIGHTNESS, HID_USAGE_BRIGHTNESS); ++ ++ if (!aux1_field || !brightness_field) ++ return -ENODEV; ++ ++ if (aux1_field->report != brightness_field->report) ++ return dev_err_probe(dev, -ENODEV, "Encountered unexpected report structure\n"); ++ ++ bl = devm_kzalloc(dev, sizeof(*bl), GFP_KERNEL); ++ if (!bl) ++ return -ENOMEM; ++ ++ ret = hid_hw_start(hdev, HID_CONNECT_DRIVER); ++ if (ret) ++ return dev_err_probe(dev, ret, "HID hardware start failed\n"); ++ ++ ret = hid_hw_open(hdev); ++ if (ret) { ++ dev_err_probe(dev, ret, "HID hardware open failed\n"); ++ goto stop_hw; ++ } ++ ++ bl->aux1_field = aux1_field; ++ bl->brightness_field = brightness_field; ++ ++ ret = appletb_bl_set_brightness(bl, APPLETB_BL_OFF); ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to set touch bar brightness to off\n"); ++ goto close_hw; ++ } ++ ++ bl_props.type = BACKLIGHT_RAW; ++ bl_props.max_brightness = ARRAY_SIZE(appletb_bl_brightness_map) - 1; ++ ++ bl->bdev = devm_backlight_device_register(dev, "appletb_backlight", dev, bl, ++ &appletb_bl_backlight_ops, &bl_props); ++ if (IS_ERR(bl->bdev)) { ++ ret = PTR_ERR(bl->bdev); ++ dev_err_probe(dev, ret, "Failed to register backlight device\n"); ++ goto close_hw; ++ } ++ ++ hid_set_drvdata(hdev, bl); ++ ++ return 0; ++ ++close_hw: ++ hid_hw_close(hdev); ++stop_hw: ++ hid_hw_stop(hdev); ++ ++ return ret; ++} ++ ++static void appletb_bl_remove(struct hid_device *hdev) ++{ ++ struct appletb_bl *bl = hid_get_drvdata(hdev); ++ ++ appletb_bl_set_brightness(bl, APPLETB_BL_OFF); ++ ++ hid_hw_close(hdev); ++ hid_hw_stop(hdev); ++} ++ ++static const struct hid_device_id appletb_bl_hid_ids[] = { ++ /* MacBook Pro's 2018, 2019, with T2 chip: iBridge DFR Brightness */ ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(hid, appletb_bl_hid_ids); ++ ++static struct hid_driver appletb_bl_hid_driver = { ++ .name = "hid-appletb-bl", ++ .id_table = appletb_bl_hid_ids, ++ .probe = appletb_bl_probe, ++ .remove = appletb_bl_remove, ++}; ++module_hid_driver(appletb_bl_hid_driver); ++ ++MODULE_AUTHOR("Ronald Tschalär"); ++MODULE_AUTHOR("Kerem Karabay "); ++MODULE_DESCRIPTION("MacBookPro Touch Bar Backlight Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index 3983b4f28..82e7a80c9 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -325,7 +325,6 @@ static const struct hid_device_id hid_have_special_driver[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021) }, +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, + #endif + #if IS_ENABLED(CONFIG_HID_APPLEIR) +@@ -335,6 +334,9 @@ static const struct hid_device_id hid_have_special_driver[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, + #endif ++#if IS_ENABLED(CONFIG_HID_APPLETB_BL) ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, ++#endif + #if IS_ENABLED(CONFIG_HID_ASUS) + { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD) }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD) }, +-- +2.42.0 + diff --git a/1006-HID-hid-appletb-kbd-add-driver-for-the-keyboard-mode.patch b/1006-HID-hid-appletb-kbd-add-driver-for-the-keyboard-mode.patch new file mode 100644 index 0000000..72b62ba --- /dev/null +++ b/1006-HID-hid-appletb-kbd-add-driver-for-the-keyboard-mode.patch @@ -0,0 +1,404 @@ +From 514b4f088b7ed916c634ca6f61de72c5f86268dd Mon Sep 17 00:00:00 2001 +From: Kerem Karabay +Date: Sun, 5 Mar 2023 18:17:23 +0300 +Subject: [PATCH 03/12] HID: hid-appletb-kbd: add driver for the keyboard mode + of Apple Touch Bars +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The Touch Bars found on x86 Macs support two USB configurations: one +where the device presents itself as a HID keyboard and can display +predefined sets of keys, and one where the operating system has full +control over what is displayed. This commit adds a driver for the +display functionality of the first configuration. + +Note that currently only T2 Macs are supported. + +This driver is based on previous work done by Ronald Tschalär +. + +Signed-off-by: Kerem Karabay +--- + .../ABI/testing/sysfs-driver-hid-appletb-kbd | 13 + + drivers/hid/Kconfig | 11 + + drivers/hid/Makefile | 1 + + drivers/hid/hid-appletb-kbd.c | 289 ++++++++++++++++++ + drivers/hid/hid-quirks.c | 4 +- + 5 files changed, 317 insertions(+), 1 deletion(-) + create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd + create mode 100644 drivers/hid/hid-appletb-kbd.c + +diff --git a/Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd b/Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd +new file mode 100644 +index 000000000..2a19584d0 +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd +@@ -0,0 +1,13 @@ ++What: /sys/bus/hid/drivers/hid-appletb-kbd//mode ++Date: September, 2023 ++KernelVersion: 6.5 ++Contact: linux-input@vger.kernel.org ++Description: ++ The set of keys displayed on the Touch Bar. ++ Valid values are: ++ == ================= ++ 0 Escape key only ++ 1 Function keys ++ 2 Media/brightness keys ++ 3 None ++ == ================= +diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig +index cf19a3b33..852de13aa 100644 +--- a/drivers/hid/Kconfig ++++ b/drivers/hid/Kconfig +@@ -158,6 +158,17 @@ config HID_APPLETB_BL + To compile this driver as a module, choose M here: the + module will be called hid-appletb-bl. + ++config HID_APPLETB_KBD ++ tristate "Apple Touch Bar Keyboard Mode" ++ depends on USB_HID ++ help ++ Say Y here if you want support for the keyboard mode (escape, ++ function, media and brightness keys) of Touch Bars on x86 MacBook ++ Pros. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called hid-appletb-kbd. ++ + config HID_ASUS + tristate "Asus" + depends on USB_HID +diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile +index bc86e38b2..5b60015fd 100644 +--- a/drivers/hid/Makefile ++++ b/drivers/hid/Makefile +@@ -30,6 +30,7 @@ obj-$(CONFIG_HID_ACRUX) += hid-axff.o + obj-$(CONFIG_HID_APPLE) += hid-apple.o + obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o + obj-$(CONFIG_HID_APPLETB_BL) += hid-appletb-bl.o ++obj-$(CONFIG_HID_APPLETB_KBD) += hid-appletb-kbd.o + obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o + obj-$(CONFIG_HID_ASUS) += hid-asus.o + obj-$(CONFIG_HID_AUREAL) += hid-aureal.o +diff --git a/drivers/hid/hid-appletb-kbd.c b/drivers/hid/hid-appletb-kbd.c +new file mode 100644 +index 000000000..bc004c408 +--- /dev/null ++++ b/drivers/hid/hid-appletb-kbd.c +@@ -0,0 +1,289 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Apple Touch Bar Keyboard Mode Driver ++ * ++ * Copyright (c) 2017-2018 Ronald Tschalär ++ * Copyright (c) 2022-2023 Kerem Karabay ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "hid-ids.h" ++ ++#define APPLETB_KBD_MODE_ESC 0 ++#define APPLETB_KBD_MODE_FN 1 ++#define APPLETB_KBD_MODE_SPCL 2 ++#define APPLETB_KBD_MODE_OFF 3 ++#define APPLETB_KBD_MODE_MAX APPLETB_KBD_MODE_OFF ++ ++#define HID_USAGE_MODE 0x00ff0004 ++ ++struct appletb_kbd { ++ struct hid_field *mode_field; ++ ++ u8 saved_mode; ++ u8 current_mode; ++}; ++ ++static const struct key_entry appletb_kbd_keymap[] = { ++ { KE_KEY, KEY_ESC, { KEY_ESC } }, ++ { KE_KEY, KEY_F1, { KEY_BRIGHTNESSDOWN } }, ++ { KE_KEY, KEY_F2, { KEY_BRIGHTNESSUP } }, ++ { KE_KEY, KEY_F3, { KEY_RESERVED } }, ++ { KE_KEY, KEY_F4, { KEY_RESERVED } }, ++ { KE_KEY, KEY_F5, { KEY_KBDILLUMDOWN } }, ++ { KE_KEY, KEY_F6, { KEY_KBDILLUMUP } }, ++ { KE_KEY, KEY_F7, { KEY_PREVIOUSSONG } }, ++ { KE_KEY, KEY_F8, { KEY_PLAYPAUSE } }, ++ { KE_KEY, KEY_F9, { KEY_NEXTSONG } }, ++ { KE_KEY, KEY_F10, { KEY_MUTE } }, ++ { KE_KEY, KEY_F11, { KEY_VOLUMEDOWN } }, ++ { KE_KEY, KEY_F12, { KEY_VOLUMEUP } }, ++ { KE_END, 0 } ++}; ++ ++static int appletb_kbd_set_mode(struct appletb_kbd *kbd, u8 mode) ++{ ++ struct hid_report *report = kbd->mode_field->report; ++ struct hid_device *hdev = report->device; ++ int ret; ++ ++ ret = hid_hw_power(hdev, PM_HINT_FULLON); ++ if (ret) { ++ hid_err(hdev, "Device didn't resume (%pe)\n", ERR_PTR(ret)); ++ return ret; ++ } ++ ++ ret = hid_set_field(kbd->mode_field, 0, mode); ++ if (ret) { ++ hid_err(hdev, "Failed to set mode field to %u (%pe)\n", mode, ERR_PTR(ret)); ++ goto power_normal; ++ } ++ ++ hid_hw_request(hdev, report, HID_REQ_SET_REPORT); ++ ++ kbd->current_mode = mode; ++ ++power_normal: ++ hid_hw_power(hdev, PM_HINT_NORMAL); ++ ++ return ret; ++} ++ ++static ssize_t mode_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct appletb_kbd *kbd = dev_get_drvdata(dev); ++ ++ return sysfs_emit(buf, "%d\n", kbd->current_mode); ++} ++ ++static ssize_t mode_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct appletb_kbd *kbd = dev_get_drvdata(dev); ++ u8 mode; ++ int ret; ++ ++ ret = kstrtou8(buf, 0, &mode); ++ if (ret) ++ return ret; ++ ++ if (mode > APPLETB_KBD_MODE_MAX) ++ return -EINVAL; ++ ++ ret = appletb_kbd_set_mode(kbd, mode); ++ ++ return ret < 0 ? ret : size; ++} ++static DEVICE_ATTR_RW(mode); ++ ++struct attribute *appletb_kbd_attrs[] = { ++ &dev_attr_mode.attr, ++ NULL ++}; ++ATTRIBUTE_GROUPS(appletb_kbd); ++ ++static int appletb_tb_key_to_slot(unsigned int code) ++{ ++ switch (code) { ++ case KEY_ESC: ++ return 0; ++ case KEY_F1 ... KEY_F10: ++ return code - KEY_F1 + 1; ++ case KEY_F11 ... KEY_F12: ++ return code - KEY_F11 + 11; ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int appletb_kbd_hid_event(struct hid_device *hdev, struct hid_field *field, ++ struct hid_usage *usage, __s32 value) ++{ ++ struct appletb_kbd *kbd = hid_get_drvdata(hdev); ++ struct key_entry *translation; ++ struct input_dev *input; ++ int slot; ++ ++ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD || usage->type != EV_KEY) ++ return 0; ++ ++ input = field->hidinput->input; ++ ++ /* ++ * Skip non-touch-bar keys. ++ * ++ * Either the touch bar itself or usbhid generate a slew of key-down ++ * events for all the meta keys. None of which we're at all interested ++ * in. ++ */ ++ slot = appletb_tb_key_to_slot(usage->code); ++ if (slot < 0) ++ return 0; ++ ++ translation = sparse_keymap_entry_from_scancode(input, usage->code); ++ ++ if (translation && kbd->current_mode == APPLETB_KBD_MODE_SPCL) { ++ input_event(input, usage->type, translation->keycode, value); ++ ++ return 1; ++ } ++ ++ return kbd->current_mode == APPLETB_KBD_MODE_OFF; ++} ++ ++static int appletb_kbd_input_configured(struct hid_device *hdev, struct hid_input *hidinput) ++{ ++ struct input_dev *input = hidinput->input; ++ ++ /* ++ * Clear various input capabilities that are blindly set by the hid ++ * driver (usbkbd.c) ++ */ ++ memset(input->evbit, 0, sizeof(input->evbit)); ++ memset(input->keybit, 0, sizeof(input->keybit)); ++ memset(input->ledbit, 0, sizeof(input->ledbit)); ++ ++ __set_bit(EV_REP, input->evbit); ++ ++ return sparse_keymap_setup(input, appletb_kbd_keymap, NULL); ++} ++ ++static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id *id) ++{ ++ struct appletb_kbd *kbd; ++ struct device *dev = &hdev->dev; ++ struct hid_field *mode_field; ++ int ret; ++ ++ ret = hid_parse(hdev); ++ if (ret) ++ return dev_err_probe(dev, ret, "HID parse failed\n"); ++ ++ mode_field = hid_find_field(hdev, HID_OUTPUT_REPORT, ++ HID_GD_KEYBOARD, HID_USAGE_MODE); ++ if (!mode_field) ++ return -ENODEV; ++ ++ kbd = devm_kzalloc(dev, sizeof(*kbd), GFP_KERNEL); ++ if (!kbd) ++ return -ENOMEM; ++ ++ kbd->mode_field = mode_field; ++ ++ ret = hid_hw_start(hdev, HID_CONNECT_HIDINPUT); ++ if (ret) ++ return dev_err_probe(dev, ret, "HID hw start failed\n"); ++ ++ ret = hid_hw_open(hdev); ++ if (ret) { ++ dev_err_probe(dev, ret, "HID hw open failed\n"); ++ goto stop_hw; ++ } ++ ++ ret = appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF); ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to set touchbar mode\n"); ++ goto close_hw; ++ } ++ ++ hid_set_drvdata(hdev, kbd); ++ ++ return 0; ++ ++close_hw: ++ hid_hw_close(hdev); ++stop_hw: ++ hid_hw_stop(hdev); ++ return ret; ++} ++ ++static void appletb_kbd_remove(struct hid_device *hdev) ++{ ++ struct appletb_kbd *kbd = hid_get_drvdata(hdev); ++ ++ appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF); ++ ++ hid_hw_close(hdev); ++ hid_hw_stop(hdev); ++} ++ ++#ifdef CONFIG_PM ++static int appletb_kbd_suspend(struct hid_device *hdev, pm_message_t msg) ++{ ++ struct appletb_kbd *kbd = hid_get_drvdata(hdev); ++ ++ kbd->saved_mode = kbd->current_mode; ++ appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF); ++ ++ return 0; ++} ++ ++static int appletb_kbd_reset_resume(struct hid_device *hdev) ++{ ++ struct appletb_kbd *kbd = hid_get_drvdata(hdev); ++ ++ appletb_kbd_set_mode(kbd, kbd->saved_mode); ++ ++ return 0; ++} ++#endif ++ ++static const struct hid_device_id appletb_kbd_hid_ids[] = { ++ /* MacBook Pro's 2018, 2019, with T2 chip: iBridge Display */ ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(hid, appletb_kbd_hid_ids); ++ ++static struct hid_driver appletb_kbd_hid_driver = { ++ .name = "hid-appletb-kbd", ++ .id_table = appletb_kbd_hid_ids, ++ .probe = appletb_kbd_probe, ++ .remove = appletb_kbd_remove, ++ .event = appletb_kbd_hid_event, ++ .input_configured = appletb_kbd_input_configured, ++#ifdef CONFIG_PM ++ .suspend = appletb_kbd_suspend, ++ .reset_resume = appletb_kbd_reset_resume, ++#endif ++ .driver.dev_groups = appletb_kbd_groups, ++}; ++module_hid_driver(appletb_kbd_hid_driver); ++ ++MODULE_AUTHOR("Ronald Tschalär"); ++MODULE_AUTHOR("Kerem Karabay "); ++MODULE_DESCRIPTION("MacBookPro Touch Bar Keyboard Mode Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index 82e7a80c9..82be9dfaf 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -325,7 +325,6 @@ static const struct hid_device_id hid_have_special_driver[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021) }, +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, + #endif + #if IS_ENABLED(CONFIG_HID_APPLEIR) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, +@@ -337,6 +336,9 @@ static const struct hid_device_id hid_have_special_driver[] = { + #if IS_ENABLED(CONFIG_HID_APPLETB_BL) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, + #endif ++#if IS_ENABLED(CONFIG_HID_APPLETB_KBD) ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, ++#endif + #if IS_ENABLED(CONFIG_HID_ASUS) + { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD) }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD) }, +-- +2.42.0 + diff --git a/1007-HID-apple-ibridge-Add-Apple-iBridge-HID-driver-for-T.patch b/1007-HID-apple-ibridge-Add-Apple-iBridge-HID-driver-for-T.patch deleted file mode 100644 index ffe2dc1..0000000 --- a/1007-HID-apple-ibridge-Add-Apple-iBridge-HID-driver-for-T.patch +++ /dev/null @@ -1,750 +0,0 @@ -From 188bd5792a1c337cfa796f983a63c7e372e4bb62 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Ronald=20Tschal=C3=A4r?= -Date: Sat, 27 Feb 2021 17:26:42 -0800 -Subject: [PATCH 7/8] HID: apple-ibridge: Add Apple iBridge HID driver for T1 - chip. -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The iBridge device provides access to several devices, including: -- the Touch Bar -- the iSight webcam -- the light sensor -- the fingerprint sensor - -This driver provides the core support for managing the iBridge device -and the access to the underlying devices. In particular, the -functionality for the touch bar and light sensor is exposed via USB HID -interfaces, and on devices with the T1 chip one of the HID devices is -used for both functions. So this driver creates virtual HID devices, one -per top-level report collection on each HID device (for a total of 3 -virtual HID devices). The sub-drivers then bind to these virtual HID -devices. - -This way the Touch Bar and ALS drivers can be kept in their own modules, -while at the same time making them look very much like as if they were -connected to the real HID devices. And those drivers then work (mostly) -without further changes on MacBooks with the T2 chip that don't need -this driver. - -Signed-off-by: Ronald Tschalär -[Kerem Karabay: convert to a platform driver] -[Kerem Karabay: fix appleib_forward_int_op] -[Kerem Karabay: rely on HID core's parsing in appleib_add_device] -Signed-off-by: Kerem Karabay ---- - drivers/hid/Kconfig | 15 + - drivers/hid/Makefile | 1 + - drivers/hid/apple-ibridge.c | 610 ++++++++++++++++++++++++++++++++++++ - drivers/hid/apple-ibridge.h | 15 + - drivers/hid/hid-ids.h | 1 + - drivers/hid/hid-quirks.c | 3 + - 6 files changed, 645 insertions(+) - create mode 100644 drivers/hid/apple-ibridge.c - create mode 100644 drivers/hid/apple-ibridge.h - -diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig -index fe489632bfd9..072d71a5d354 100644 ---- a/drivers/hid/Kconfig -+++ b/drivers/hid/Kconfig -@@ -130,6 +130,21 @@ config HID_APPLE - Say Y here if you want support for keyboards of Apple iBooks, PowerBooks, - MacBooks, MacBook Pros and Apple Aluminum. - -+config HID_APPLE_IBRIDGE -+ tristate "Apple iBridge" -+ depends on USB_HID -+ depends on (X86 && ACPI) || COMPILE_TEST -+ imply HID_SENSOR_HUB -+ imply HID_SENSOR_ALS -+ help -+ This module provides the core support for the Apple T1 chip found -+ on 2016 and 2017 MacBookPro's, also known as the iBridge. The drivers -+ for the Touch Bar (apple-touchbar) and light sensor (hid-sensor-hub -+ and hid-sensor-als) need to be enabled separately. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called apple-ibridge. -+ - config HID_APPLE_MAGIC_BACKLIGHT - tristate "Apple Magic Keyboard Backlight" - depends on USB_HID -diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile -index dc8df002bc86..cc2a4f4b17ae 100644 ---- a/drivers/hid/Makefile -+++ b/drivers/hid/Makefile -@@ -26,6 +26,7 @@ obj-$(CONFIG_HID_ACCUTOUCH) += hid-accutouch.o - obj-$(CONFIG_HID_ALPS) += hid-alps.o - obj-$(CONFIG_HID_ACRUX) += hid-axff.o - obj-$(CONFIG_HID_APPLE) += hid-apple.o -+obj-$(CONFIG_HID_APPLE_IBRIDGE) += apple-ibridge.o - obj-$(CONFIG_HID_APPLE_MAGIC_BACKLIGHT) += hid-apple-magic-backlight.o - obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o - obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o -diff --git a/drivers/hid/apple-ibridge.c b/drivers/hid/apple-ibridge.c -new file mode 100644 -index 000000000000..4d26f8d66d3f ---- /dev/null -+++ b/drivers/hid/apple-ibridge.c -@@ -0,0 +1,610 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Apple iBridge Driver -+ * -+ * Copyright (c) 2018 Ronald Tschalär -+ */ -+ -+/** -+ * DOC: Overview -+ * -+ * 2016 and 2017 MacBookPro models with a Touch Bar (MacBookPro13,[23] and -+ * MacBookPro14,[23]) have an Apple iBridge chip (also known as T1 chip) which -+ * exposes the touch bar, built-in webcam (iSight), ambient light sensor, and -+ * Secure Enclave Processor (SEP) for TouchID. It shows up in the system as a -+ * USB device with 3 configurations: 'Default iBridge Interfaces', 'Default -+ * iBridge Interfaces(OS X)', and 'Default iBridge Interfaces(Recovery)'. -+ * -+ * In the first (default after boot) configuration, 4 usb interfaces are -+ * exposed: 2 related to the webcam, and 2 USB HID interfaces representing -+ * the touch bar and the ambient light sensor. The webcam interfaces are -+ * already handled by the uvcvideo driver. However, there is a problem with -+ * the other two interfaces: one of them contains functionality (HID reports) -+ * used by both the touch bar and the ALS, which is an issue because the kernel -+ * allows only one driver to be attached to a given device. This driver exists -+ * to solve this issue. -+ * -+ * This driver is implemented as a HID driver that attaches to both HID -+ * interfaces and in turn creates several virtual child HID devices, one for -+ * each top-level collection found in each interfaces report descriptor. The -+ * touch bar and ALS drivers then attach to these virtual HID devices, and this -+ * driver forwards the operations between the real and virtual devices. -+ * -+ * One important aspect of this approach is that resulting (virtual) HID -+ * devices look much like the HID devices found on the later MacBookPro models -+ * which have a T2 chip, where there are separate USB interfaces for the touch -+ * bar and ALS functionality, which means that the touch bar and ALS drivers -+ * work (mostly) the same on both types of models. -+ * -+ * Lastly, this driver also takes care of the power-management for the -+ * iBridge when suspending and resuming. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "hid-ids.h" -+#include "../hid/usbhid/usbhid.h" -+#include "apple-ibridge.h" -+ -+#define APPLEIB_BASIC_CONFIG 1 -+ -+static struct hid_device_id appleib_sub_hid_ids[] = { -+ { HID_USB_DEVICE(USB_VENDOR_ID_LINUX_FOUNDATION, -+ USB_DEVICE_ID_IBRIDGE_TB) }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_LINUX_FOUNDATION, -+ USB_DEVICE_ID_IBRIDGE_ALS) }, -+}; -+ -+static struct { -+ unsigned int usage; -+ struct hid_device_id *dev_id; -+} appleib_usage_map[] = { -+ /* Default iBridge configuration, key inputs and mode settings */ -+ { 0x00010006, &appleib_sub_hid_ids[0] }, -+ /* OS X iBridge configuration, digitizer inputs */ -+ { 0x000D0005, &appleib_sub_hid_ids[0] }, -+ /* All iBridge configurations, display/DFR settings */ -+ { 0xFF120001, &appleib_sub_hid_ids[0] }, -+ /* All iBridge configurations, ALS */ -+ { 0x00200041, &appleib_sub_hid_ids[1] }, -+}; -+ -+struct appleib_device { -+ acpi_handle asoc_socw; -+}; -+ -+struct appleib_hid_dev_info { -+ struct hid_device *hdev; -+ struct hid_device *sub_hdevs[ARRAY_SIZE(appleib_sub_hid_ids)]; -+ bool sub_open[ARRAY_SIZE(appleib_sub_hid_ids)]; -+}; -+ -+static int appleib_hid_raw_event(struct hid_device *hdev, -+ struct hid_report *report, u8 *data, int size) -+{ -+ struct appleib_hid_dev_info *hdev_info = hid_get_drvdata(hdev); -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) { -+ if (READ_ONCE(hdev_info->sub_open[i])) -+ hid_input_report(hdev_info->sub_hdevs[i], report->type, -+ data, size, 0); -+ } -+ -+ return 0; -+} -+ -+static __u8 *appleib_report_fixup(struct hid_device *hdev, __u8 *rdesc, -+ unsigned int *rsize) -+{ -+ /* Some fields have a size of 64 bits, which according to HID 1.11 -+ * Section 8.4 is not valid ("An item field cannot span more than 4 -+ * bytes in a report"). Furthermore, hid_field_extract() complains -+ * when encountering such a field. So turn them into two 32-bit fields -+ * instead. -+ */ -+ -+ if (*rsize == 634 && -+ /* Usage Page 0xff12 (vendor defined) */ -+ rdesc[212] == 0x06 && rdesc[213] == 0x12 && rdesc[214] == 0xff && -+ /* Usage 0x51 */ -+ rdesc[416] == 0x09 && rdesc[417] == 0x51 && -+ /* report size 64 */ -+ rdesc[432] == 0x75 && rdesc[433] == 64 && -+ /* report count 1 */ -+ rdesc[434] == 0x95 && rdesc[435] == 1) { -+ rdesc[433] = 32; -+ rdesc[435] = 2; -+ hid_dbg(hdev, "Fixed up first 64-bit field\n"); -+ } -+ -+ if (*rsize == 634 && -+ /* Usage Page 0xff12 (vendor defined) */ -+ rdesc[212] == 0x06 && rdesc[213] == 0x12 && rdesc[214] == 0xff && -+ /* Usage 0x51 */ -+ rdesc[611] == 0x09 && rdesc[612] == 0x51 && -+ /* report size 64 */ -+ rdesc[627] == 0x75 && rdesc[628] == 64 && -+ /* report count 1 */ -+ rdesc[629] == 0x95 && rdesc[630] == 1) { -+ rdesc[628] = 32; -+ rdesc[630] = 2; -+ hid_dbg(hdev, "Fixed up second 64-bit field\n"); -+ } -+ -+ return rdesc; -+} -+ -+#ifdef CONFIG_PM -+/** -+ * appleib_forward_int_op() - Forward a hid-driver callback to all drivers on -+ * all virtual HID devices attached to the given real HID device. -+ * @hdev the real hid-device -+ * @forward a function that calls the callback on the given driver -+ * @args arguments for the forward function -+ * -+ * This is for callbacks that return a status as an int. -+ * -+ * Returns: 0 on success, or the first error returned by the @forward function. -+ */ -+static int appleib_forward_int_op(struct hid_device *hdev, -+ int (*forward)(struct hid_driver *, -+ struct hid_device *, void *), -+ void *args) -+{ -+ struct appleib_hid_dev_info *hdev_info = hid_get_drvdata(hdev); -+ struct hid_device *sub_hdev; -+ int rc; -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) { -+ sub_hdev = hdev_info->sub_hdevs[i]; -+ if (sub_hdev->driver) { -+ rc = forward(sub_hdev->driver, sub_hdev, args); -+ if (rc) -+ return rc; -+ } -+ } -+ -+ return 0; -+} -+ -+static int appleib_hid_suspend_fwd(struct hid_driver *drv, -+ struct hid_device *hdev, void *args) -+{ -+ int rc = 0; -+ -+ if (drv->suspend) -+ rc = drv->suspend(hdev, *(pm_message_t *)args); -+ -+ return rc; -+} -+ -+static int appleib_hid_suspend(struct hid_device *hdev, pm_message_t message) -+{ -+ return appleib_forward_int_op(hdev, appleib_hid_suspend_fwd, &message); -+} -+ -+static int appleib_hid_resume_fwd(struct hid_driver *drv, -+ struct hid_device *hdev, void *args) -+{ -+ int rc = 0; -+ -+ if (drv->resume) -+ rc = drv->resume(hdev); -+ -+ return rc; -+} -+ -+static int appleib_hid_resume(struct hid_device *hdev) -+{ -+ return appleib_forward_int_op(hdev, appleib_hid_resume_fwd, NULL); -+} -+ -+static int appleib_hid_reset_resume_fwd(struct hid_driver *drv, -+ struct hid_device *hdev, void *args) -+{ -+ int rc = 0; -+ -+ if (drv->reset_resume) -+ rc = drv->reset_resume(hdev); -+ -+ return rc; -+} -+ -+static int appleib_hid_reset_resume(struct hid_device *hdev) -+{ -+ return appleib_forward_int_op(hdev, appleib_hid_reset_resume_fwd, NULL); -+} -+#endif /* CONFIG_PM */ -+ -+static int appleib_ll_start(struct hid_device *hdev) -+{ -+ return 0; -+} -+ -+static void appleib_ll_stop(struct hid_device *hdev) -+{ -+} -+ -+static int appleib_set_open(struct hid_device *hdev, bool open) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) { -+ /* -+ * hid_hw_open(), and hence appleib_ll_open(), is called -+ * from the driver's probe function, which in turn is called -+ * while adding the sub-hdev; but at this point we haven't yet -+ * added the sub-hdev to our list. So if we don't find the -+ * sub-hdev in our list assume it's in the process of being -+ * added and set the flag on the first unset sub-hdev. -+ */ -+ if (hdev_info->sub_hdevs[i] == hdev || -+ !hdev_info->sub_hdevs[i]) { -+ WRITE_ONCE(hdev_info->sub_open[i], open); -+ return 0; -+ } -+ } -+ -+ return -ENODEV; -+} -+ -+static int appleib_ll_open(struct hid_device *hdev) -+{ -+ return appleib_set_open(hdev, true); -+} -+ -+static void appleib_ll_close(struct hid_device *hdev) -+{ -+ appleib_set_open(hdev, false); -+} -+ -+static int appleib_ll_power(struct hid_device *hdev, int level) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ -+ return hid_hw_power(hdev_info->hdev, level); -+} -+ -+static int appleib_ll_parse(struct hid_device *hdev) -+{ -+ /* we've already called hid_parse_report() */ -+ return 0; -+} -+ -+static void appleib_ll_request(struct hid_device *hdev, -+ struct hid_report *report, int reqtype) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ -+ hid_hw_request(hdev_info->hdev, report, reqtype); -+} -+ -+static int appleib_ll_wait(struct hid_device *hdev) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ -+ hid_hw_wait(hdev_info->hdev); -+ return 0; -+} -+ -+static int appleib_ll_raw_request(struct hid_device *hdev, -+ unsigned char reportnum, __u8 *buf, -+ size_t len, unsigned char rtype, int reqtype) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ -+ return hid_hw_raw_request(hdev_info->hdev, reportnum, buf, len, rtype, -+ reqtype); -+} -+ -+static int appleib_ll_output_report(struct hid_device *hdev, __u8 *buf, -+ size_t len) -+{ -+ struct appleib_hid_dev_info *hdev_info = hdev->driver_data; -+ -+ return hid_hw_output_report(hdev_info->hdev, buf, len); -+} -+ -+static struct hid_ll_driver appleib_ll_driver = { -+ .start = appleib_ll_start, -+ .stop = appleib_ll_stop, -+ .open = appleib_ll_open, -+ .close = appleib_ll_close, -+ .power = appleib_ll_power, -+ .parse = appleib_ll_parse, -+ .request = appleib_ll_request, -+ .wait = appleib_ll_wait, -+ .raw_request = appleib_ll_raw_request, -+ .output_report = appleib_ll_output_report, -+}; -+ -+static struct hid_device_id *appleib_find_dev_id_for_usage(unsigned int usage) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(appleib_usage_map); i++) { -+ if (appleib_usage_map[i].usage == usage) -+ return appleib_usage_map[i].dev_id; -+ } -+ -+ return NULL; -+} -+ -+static struct hid_device * -+appleib_add_sub_dev(struct appleib_hid_dev_info *hdev_info, -+ struct hid_device_id *dev_id) -+{ -+ struct hid_device *sub_hdev; -+ int rc; -+ -+ sub_hdev = hid_allocate_device(); -+ if (IS_ERR(sub_hdev)) -+ return sub_hdev; -+ -+ sub_hdev->dev.parent = &hdev_info->hdev->dev; -+ -+ sub_hdev->bus = dev_id->bus; -+ sub_hdev->group = dev_id->group; -+ sub_hdev->vendor = dev_id->vendor; -+ sub_hdev->product = dev_id->product; -+ -+ sub_hdev->ll_driver = &appleib_ll_driver; -+ -+ snprintf(sub_hdev->name, sizeof(sub_hdev->name), -+ "iBridge Virtual HID %s/%04x:%04x", -+ dev_name(sub_hdev->dev.parent), sub_hdev->vendor, -+ sub_hdev->product); -+ -+ sub_hdev->driver_data = hdev_info; -+ -+ rc = hid_add_device(sub_hdev); -+ if (rc) { -+ hid_destroy_device(sub_hdev); -+ return ERR_PTR(rc); -+ } -+ -+ return sub_hdev; -+} -+ -+static struct appleib_hid_dev_info *appleib_add_device(struct hid_device *hdev) -+{ -+ struct appleib_hid_dev_info *hdev_info; -+ struct hid_device_id *dev_id; -+ unsigned int usage; -+ int i; -+ -+ hdev_info = devm_kzalloc(&hdev->dev, sizeof(*hdev_info), GFP_KERNEL); -+ if (!hdev_info) -+ return ERR_PTR(-ENOMEM); -+ -+ hdev_info->hdev = hdev; -+ -+ for (i = 0; i < hdev->maxcollection; i++) { -+ usage = hdev->collection[i].usage; -+ dev_id = appleib_find_dev_id_for_usage(usage); -+ -+ if (!dev_id) { -+ hid_warn(hdev, "Unknown collection encountered with usage %x\n", -+ usage); -+ } else { -+ hdev_info->sub_hdevs[i] = appleib_add_sub_dev(hdev_info, dev_id); -+ -+ if (IS_ERR(hdev_info->sub_hdevs[i])) { -+ while (i-- > 0) -+ hid_destroy_device(hdev_info->sub_hdevs[i]); -+ return (void *)hdev_info->sub_hdevs[i]; -+ } -+ } -+ } -+ -+ return hdev_info; -+} -+ -+static void appleib_remove_device(struct hid_device *hdev) -+{ -+ struct appleib_hid_dev_info *hdev_info = hid_get_drvdata(hdev); -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(hdev_info->sub_hdevs); i++) { -+ if (hdev_info->sub_hdevs[i]) -+ hid_destroy_device(hdev_info->sub_hdevs[i]); -+ } -+ -+ hid_set_drvdata(hdev, NULL); -+} -+ -+static int appleib_hid_probe(struct hid_device *hdev, -+ const struct hid_device_id *id) -+{ -+ struct appleib_hid_dev_info *hdev_info; -+ struct usb_device *udev; -+ int rc; -+ -+ /* check and set usb config first */ -+ udev = hid_to_usb_dev(hdev); -+ -+ if (udev->actconfig->desc.bConfigurationValue != APPLEIB_BASIC_CONFIG) { -+ rc = usb_driver_set_configuration(udev, APPLEIB_BASIC_CONFIG); -+ return rc ? rc : -ENODEV; -+ } -+ -+ rc = hid_parse(hdev); -+ if (rc) { -+ hid_err(hdev, "ib: hid parse failed (%d)\n", rc); -+ goto error; -+ } -+ -+ rc = hid_hw_start(hdev, HID_CONNECT_DRIVER); -+ if (rc) { -+ hid_err(hdev, "ib: hw start failed (%d)\n", rc); -+ goto error; -+ } -+ -+ hdev_info = appleib_add_device(hdev); -+ if (IS_ERR(hdev_info)) { -+ rc = PTR_ERR(hdev_info); -+ goto stop_hw; -+ } -+ -+ hid_set_drvdata(hdev, hdev_info); -+ -+ rc = hid_hw_open(hdev); -+ if (rc) { -+ hid_err(hdev, "ib: failed to open hid: %d\n", rc); -+ goto remove_dev; -+ } -+ -+ return 0; -+ -+remove_dev: -+ appleib_remove_device(hdev); -+stop_hw: -+ hid_hw_stop(hdev); -+error: -+ return rc; -+} -+ -+static void appleib_hid_remove(struct hid_device *hdev) -+{ -+ hid_hw_close(hdev); -+ appleib_remove_device(hdev); -+ hid_hw_stop(hdev); -+} -+ -+static const struct hid_device_id appleib_hid_ids[] = { -+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IBRIDGE) }, -+ { }, -+}; -+ -+static struct hid_driver appleib_hid_driver = { -+ .name = "apple-ibridge-hid", -+ .id_table = appleib_hid_ids, -+ .probe = appleib_hid_probe, -+ .remove = appleib_hid_remove, -+ .raw_event = appleib_hid_raw_event, -+ .report_fixup = appleib_report_fixup, -+#ifdef CONFIG_PM -+ .suspend = appleib_hid_suspend, -+ .resume = appleib_hid_resume, -+ .reset_resume = appleib_hid_reset_resume, -+#endif -+}; -+ -+static struct appleib_device *appleib_alloc_device(struct platform_device *pdev) -+{ -+ struct appleib_device *ib_dev; -+ acpi_status sts; -+ -+ ib_dev = devm_kzalloc(&pdev->dev, sizeof(*ib_dev), GFP_KERNEL); -+ if (!ib_dev) -+ return ERR_PTR(-ENOMEM); -+ -+ /* get iBridge acpi power control method for suspend/resume */ -+ sts = acpi_get_handle(ACPI_HANDLE(&pdev->dev), "SOCW", &ib_dev->asoc_socw); -+ if (ACPI_FAILURE(sts)) { -+ dev_err(&pdev->dev, -+ "Error getting handle for ASOC.SOCW method: %s\n", -+ acpi_format_exception(sts)); -+ return ERR_PTR(-ENXIO); -+ } -+ -+ /* ensure iBridge is powered on */ -+ sts = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 1); -+ if (ACPI_FAILURE(sts)) -+ dev_warn(&pdev->dev, "SOCW(1) failed: %s\n", -+ acpi_format_exception(sts)); -+ -+ return ib_dev; -+} -+ -+static int appleib_probe(struct platform_device *pdev) -+{ -+ struct appleib_device *ib_dev; -+ int ret; -+ -+ ib_dev = appleib_alloc_device(pdev); -+ if (IS_ERR(ib_dev)) -+ return PTR_ERR(ib_dev); -+ -+ ret = hid_register_driver(&appleib_hid_driver); -+ if (ret) { -+ dev_err(&pdev->dev, "Error registering hid driver: %d\n", -+ ret); -+ return ret; -+ } -+ -+ platform_set_drvdata(pdev, ib_dev); -+ -+ return 0; -+} -+ -+static int appleib_remove(struct platform_device *pdev) -+{ -+ hid_unregister_driver(&appleib_hid_driver); -+ -+ return 0; -+} -+ -+static int appleib_suspend(struct platform_device *pdev, pm_message_t message) -+{ -+ struct appleib_device *ib_dev; -+ int rc; -+ -+ ib_dev = platform_get_drvdata(pdev); -+ -+ rc = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 0); -+ if (ACPI_FAILURE(rc)) -+ dev_warn(&pdev->dev, "SOCW(0) failed: %s\n", -+ acpi_format_exception(rc)); -+ -+ return 0; -+} -+ -+static int appleib_resume(struct platform_device *pdev) -+{ -+ struct appleib_device *ib_dev; -+ int rc; -+ -+ ib_dev = platform_get_drvdata(pdev); -+ -+ rc = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 1); -+ if (ACPI_FAILURE(rc)) -+ dev_warn(&pdev->dev, "SOCW(1) failed: %s\n", -+ acpi_format_exception(rc)); -+ -+ return 0; -+} -+ -+static const struct acpi_device_id appleib_acpi_match[] = { -+ { "APP7777", 0 }, -+ { }, -+}; -+ -+MODULE_DEVICE_TABLE(acpi, appleib_acpi_match); -+ -+static struct platform_driver appleib_driver = { -+ .probe = appleib_probe, -+ .remove = appleib_remove, -+ .suspend = appleib_suspend, -+ .resume = appleib_resume, -+ .driver = { -+ .name = "apple-ibridge", -+ .acpi_match_table = appleib_acpi_match, -+ }, -+}; -+ -+module_platform_driver(appleib_driver); -+ -+MODULE_AUTHOR("Ronald Tschalär"); -+MODULE_DESCRIPTION("Apple iBridge driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/hid/apple-ibridge.h b/drivers/hid/apple-ibridge.h -new file mode 100644 -index 000000000000..8aefcf61589a ---- /dev/null -+++ b/drivers/hid/apple-ibridge.h -@@ -0,0 +1,15 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Apple iBridge Driver -+ * -+ * Copyright (c) 2018 Ronald Tschalär -+ */ -+ -+#ifndef __LINUX_APPLE_IBRDIGE_H -+#define __LINUX_APPLE_IBRDIGE_H -+ -+#define USB_VENDOR_ID_LINUX_FOUNDATION 0x1d6b -+#define USB_DEVICE_ID_IBRIDGE_TB 0x0301 -+#define USB_DEVICE_ID_IBRIDGE_ALS 0x0302 -+ -+#endif -diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h -index 9e36b4cd905e..a9f100e6777b 100644 ---- a/drivers/hid/hid-ids.h -+++ b/drivers/hid/hid-ids.h -@@ -187,6 +187,7 @@ - #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021 0x029f - #define USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT 0x8102 - #define USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY 0x8302 -+#define USB_DEVICE_ID_APPLE_IBRIDGE 0x8600 - - #define USB_VENDOR_ID_ASUS 0x0486 - #define USB_DEVICE_ID_ASUS_T91MT 0x0185 -diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c -index 5bc91f68b374..e9b591fdacdc 100644 ---- a/drivers/hid/hid-quirks.c -+++ b/drivers/hid/hid-quirks.c -@@ -319,6 +319,9 @@ static const struct hid_device_id hid_have_special_driver[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, - #endif -+#if IS_ENABLED(CONFIG_HID_APPLE_IBRIDGE) -+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IBRIDGE) }, -+#endif - #if IS_ENABLED(CONFIG_HID_APPLEIR) - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, --- -2.39.1 - diff --git a/1007-HID-multitouch-support-getting-the-contact-ID-from.patch b/1007-HID-multitouch-support-getting-the-contact-ID-from.patch new file mode 100644 index 0000000..23b98c4 --- /dev/null +++ b/1007-HID-multitouch-support-getting-the-contact-ID-from.patch @@ -0,0 +1,40 @@ +From 2f9be28549307b4ac51e8d66bf3b8d5e0621466d Mon Sep 17 00:00:00 2001 +From: Kerem Karabay +Date: Wed, 19 Jul 2023 19:37:14 +0300 +Subject: [PATCH 04/12] HID: multitouch: support getting the contact ID from + HID_DG_TRANSDUCER_INDEX fields + +This is needed to support Apple Touch Bars, where the contact ID is +contained in fields with the HID_DG_TRANSDUCER_INDEX usage. + +Signed-off-by: Kerem Karabay +--- + drivers/hid/hid-multitouch.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index e31be0cb8..902a59928 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -636,7 +636,9 @@ static struct mt_report_data *mt_allocate_report_data(struct mt_device *td, + + if (field->logical == HID_DG_FINGER || td->hdev->group != HID_GROUP_MULTITOUCH_WIN_8) { + for (n = 0; n < field->report_count; n++) { +- if (field->usage[n].hid == HID_DG_CONTACTID) { ++ unsigned int hid = field->usage[n].hid; ++ ++ if (hid == HID_DG_CONTACTID || hid == HID_DG_TRANSDUCER_INDEX) { + rdata->is_mt_collection = true; + break; + } +@@ -815,6 +817,7 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, + MT_STORE_FIELD(tip_state); + return 1; + case HID_DG_CONTACTID: ++ case HID_DG_TRANSDUCER_INDEX: + MT_STORE_FIELD(contactid); + app->touches_by_report++; + return 1; +-- +2.42.0 + diff --git a/1008-HID-apple-touchbar-Add-driver-for-the-Touch-Bar-on-M.patch b/1008-HID-apple-touchbar-Add-driver-for-the-Touch-Bar-on-M.patch deleted file mode 100644 index 18a7953..0000000 --- a/1008-HID-apple-touchbar-Add-driver-for-the-Touch-Bar-on-M.patch +++ /dev/null @@ -1,1602 +0,0 @@ -From 8885f1aca3a6d9d3404e3f6292f7b3760c8f7e80 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Ronald=20Tschal=C3=A4r?= -Date: Sat, 27 Feb 2021 17:26:43 -0800 -Subject: [PATCH 8/8] HID: apple-touchbar: Add driver for the Touch Bar on - MacBook Pro's. -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This driver enables basic touch bar functionality: enabling it, switching -between modes on FN key press, and dimming and turning the display -off/on when idle/active. - -Signed-off-by: Ronald Tschalär -[Kerem Karabay: use USB product IDs from hid-ids.h] -[Kerem Karabay: use hid_hw_raw_request except when setting the touchbar mode on T1 Macs] -[Kerem Karabay: update Kconfig description] -Signed-off-by: Kerem Karabay -[Orlando Chamberlain: add usage check to not bind to keyboard backlight interface] -Signed-off-by: Orlando Chamberlain -[Aditya Garg: check if apple-touchbar is enabled in the special driver list] -[Aditya Garg: fix suspend on T2 Macs] -Signed-off-by: Aditya Garg ---- - drivers/hid/Kconfig | 11 + - drivers/hid/Makefile | 1 + - drivers/hid/apple-touchbar.c | 1500 ++++++++++++++++++++++++++++++++++ - drivers/hid/hid-quirks.c | 6 +- - 4 files changed, 1516 insertions(+), 2 deletions(-) - create mode 100644 drivers/hid/apple-touchbar.c - -diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig -index 072d71a5d354..c13486d62f7c 100644 ---- a/drivers/hid/Kconfig -+++ b/drivers/hid/Kconfig -@@ -134,6 +134,7 @@ config HID_APPLE_IBRIDGE - tristate "Apple iBridge" - depends on USB_HID - depends on (X86 && ACPI) || COMPILE_TEST -+ imply HID_APPLE_TOUCHBAR - imply HID_SENSOR_HUB - imply HID_SENSOR_ALS - help -@@ -145,6 +146,16 @@ config HID_APPLE_IBRIDGE - To compile this driver as a module, choose M here: the - module will be called apple-ibridge. - -+config HID_APPLE_TOUCHBAR -+ tristate "Apple Touch Bar" -+ depends on USB_HID -+ help -+ Say Y here if you want support for the Touch Bar on x86 -+ MacBook Pros. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called apple-touchbar. -+ - config HID_APPLE_MAGIC_BACKLIGHT - tristate "Apple Magic Keyboard Backlight" - depends on USB_HID -diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile -index cc2a4f4b17ae..058b70e588d0 100644 ---- a/drivers/hid/Makefile -+++ b/drivers/hid/Makefile -@@ -27,6 +27,7 @@ obj-$(CONFIG_HID_ALPS) += hid-alps.o - obj-$(CONFIG_HID_ACRUX) += hid-axff.o - obj-$(CONFIG_HID_APPLE) += hid-apple.o - obj-$(CONFIG_HID_APPLE_IBRIDGE) += apple-ibridge.o -+obj-$(CONFIG_HID_APPLE_TOUCHBAR) += apple-touchbar.o - obj-$(CONFIG_HID_APPLE_MAGIC_BACKLIGHT) += hid-apple-magic-backlight.o - obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o - obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o -diff --git a/drivers/hid/apple-touchbar.c b/drivers/hid/apple-touchbar.c -new file mode 100644 -index 000000000000..ff6a8493b7c9 ---- /dev/null -+++ b/drivers/hid/apple-touchbar.c -@@ -0,0 +1,1500 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Apple Touch Bar Driver -+ * -+ * Copyright (c) 2017-2018 Ronald Tschalär -+ */ -+ -+/* -+ * Recent MacBookPro models (MacBookPro 13,[23] and later) have a touch bar, -+ * which is exposed via several USB interfaces. MacOS supports a fancy mode -+ * where arbitrary buttons can be defined; this driver currently only -+ * supports the simple mode that consists of 3 predefined layouts -+ * (escape-only, esc + special keys, and esc + function keys). -+ * -+ * The first USB HID interface supports two reports, an input report that -+ * is used to report the key presses, and an output report which can be -+ * used to set the touch bar "mode": touch bar off (in which case no touches -+ * are reported at all), escape key only, escape + 12 function keys, and -+ * escape + several special keys (including brightness, audio volume, -+ * etc). The second interface supports several, complex reports, most of -+ * which are unknown at this time, but one of which has been determined to -+ * allow for controlling of the touch bar's brightness: off (though touches -+ * are still reported), dimmed, and full brightness. This driver makes -+ * use of these two reports. -+ */ -+ -+#define dev_fmt(fmt) "tb: " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "hid-ids.h" -+#include "apple-ibridge.h" -+ -+#define HID_UP_APPLE 0xff120000 -+#define HID_USAGE_MODE (HID_UP_CUSTOM | 0x0004) -+#define HID_USAGE_APPLE_APP (HID_UP_APPLE | 0x0001) -+#define HID_USAGE_DISP (HID_UP_APPLE | 0x0021) -+#define HID_USAGE_DISP_AUX1 (HID_UP_APPLE | 0x0020) -+ -+#define APPLETB_MAX_TB_KEYS 13 /* ESC, F1-F12 */ -+ -+#define APPLETB_CMD_MODE_ESC 0 -+#define APPLETB_CMD_MODE_FN 1 -+#define APPLETB_CMD_MODE_SPCL 2 -+#define APPLETB_CMD_MODE_OFF 3 -+#define APPLETB_CMD_MODE_UPD 254 -+#define APPLETB_CMD_MODE_NONE 255 -+ -+#define APPLETB_CMD_DISP_ON 1 -+#define APPLETB_CMD_DISP_DIM 2 -+#define APPLETB_CMD_DISP_OFF 4 -+#define APPLETB_CMD_DISP_UPD 254 -+#define APPLETB_CMD_DISP_NONE 255 -+ -+#define APPLETB_FN_MODE_FKEYS 0 -+#define APPLETB_FN_MODE_NORM 1 -+#define APPLETB_FN_MODE_INV 2 -+#define APPLETB_FN_MODE_SPCL 3 -+#define APPLETB_FN_MODE_ESC 4 -+#define APPLETB_FN_MODE_MAX APPLETB_FN_MODE_ESC -+ -+#define APPLETB_DEVID_KEYBOARD 1 -+#define APPLETB_DEVID_TOUCHPAD 2 -+ -+#define APPLETB_MAX_DIM_TIME 30 -+ -+#define APPLETB_FEATURE_IS_T1 BIT(0) -+ -+static int appletb_tb_def_idle_timeout = 5 * 60; -+module_param_named(idle_timeout, appletb_tb_def_idle_timeout, int, 0444); -+MODULE_PARM_DESC(idle_timeout, "Default touch bar idle timeout:\n" -+ " [>0] - turn touch bar display off after no keyboard, trackpad, or touch bar input has been received for this many seconds;\n" -+ " the display will be turned back on as soon as new input is received\n" -+ " 0 - turn touch bar display off (input does not turn it on again)\n" -+ " -1 - turn touch bar display on (does not turn off automatically)\n" -+ " -2 - disable touch bar completely"); -+ -+static int appletb_tb_def_dim_timeout = -2; -+module_param_named(dim_timeout, appletb_tb_def_dim_timeout, int, 0444); -+MODULE_PARM_DESC(dim_timeout, "Default touch bar dim timeout:\n" -+ " >0 - dim touch bar display after no keyboard, trackpad, or touch bar input has been received for this many seconds\n" -+ " the display will be returned to full brightness as soon as new input is received\n" -+ " 0 - dim touch bar display (input does not return it to full brightness)\n" -+ " -1 - disable timeout (touch bar never dimmed)\n" -+ " [-2] - calculate timeout based on idle-timeout"); -+ -+static int appletb_tb_def_fn_mode = APPLETB_FN_MODE_NORM; -+module_param_named(fnmode, appletb_tb_def_fn_mode, int, 0444); -+MODULE_PARM_DESC(fnmode, "Default Fn key mode:\n" -+ " 0 - function-keys only\n" -+ " [1] - fn key switches from special to function-keys\n" -+ " 2 - inverse of 1\n" -+ " 3 - special keys only\n" -+ " 4 - escape key only"); -+ -+static ssize_t idle_timeout_show(struct device *dev, -+ struct device_attribute *attr, char *buf); -+static ssize_t idle_timeout_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t size); -+static DEVICE_ATTR_RW(idle_timeout); -+ -+static ssize_t dim_timeout_show(struct device *dev, -+ struct device_attribute *attr, char *buf); -+static ssize_t dim_timeout_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t size); -+static DEVICE_ATTR_RW(dim_timeout); -+ -+static ssize_t fnmode_show(struct device *dev, struct device_attribute *attr, -+ char *buf); -+static ssize_t fnmode_store(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t size); -+static DEVICE_ATTR_RW(fnmode); -+ -+static struct attribute *appletb_attrs[] = { -+ &dev_attr_idle_timeout.attr, -+ &dev_attr_dim_timeout.attr, -+ &dev_attr_fnmode.attr, -+ NULL, -+}; -+ -+static const struct attribute_group appletb_attr_group = { -+ .attrs = appletb_attrs, -+}; -+ -+struct appletb_device { -+ bool active; -+ struct device *log_dev; -+ -+ struct hid_field *mode_field; -+ struct hid_field *disp_field; -+ struct hid_field *disp_field_aux1; -+ struct appletb_iface_info { -+ struct hid_device *hdev; -+ struct usb_interface *usb_iface; -+ bool suspended; -+ } mode_iface, disp_iface; -+ -+ struct input_handler inp_handler; -+ struct input_handle kbd_handle; -+ struct input_handle tpd_handle; -+ -+ bool last_tb_keys_pressed[APPLETB_MAX_TB_KEYS]; -+ bool last_tb_keys_translated[APPLETB_MAX_TB_KEYS]; -+ bool last_fn_pressed; -+ -+ ktime_t last_event_time; -+ -+ unsigned char cur_tb_mode; -+ unsigned char pnd_tb_mode; -+ unsigned char cur_tb_disp; -+ unsigned char pnd_tb_disp; -+ bool tb_autopm_off; -+ bool restore_autopm; -+ struct delayed_work tb_work; -+ /* protects most of the above */ -+ spinlock_t tb_lock; -+ -+ int dim_timeout; -+ int idle_timeout; -+ bool dim_to_is_calc; -+ int fn_mode; -+ -+ bool is_t1; -+}; -+ -+struct appletb_key_translation { -+ u16 from; -+ u16 to; -+}; -+ -+static const struct appletb_key_translation appletb_fn_codes[] = { -+ { KEY_F1, KEY_BRIGHTNESSDOWN }, -+ { KEY_F2, KEY_BRIGHTNESSUP }, -+ { KEY_F3, KEY_SCALE }, /* not used */ -+ { KEY_F4, KEY_DASHBOARD }, /* not used */ -+ { KEY_F5, KEY_KBDILLUMDOWN }, -+ { KEY_F6, KEY_KBDILLUMUP }, -+ { KEY_F7, KEY_PREVIOUSSONG }, -+ { KEY_F8, KEY_PLAYPAUSE }, -+ { KEY_F9, KEY_NEXTSONG }, -+ { KEY_F10, KEY_MUTE }, -+ { KEY_F11, KEY_VOLUMEDOWN }, -+ { KEY_F12, KEY_VOLUMEUP }, -+}; -+ -+static struct appletb_device *appletb_dev; -+ -+static bool appletb_disable_autopm(struct hid_device *hdev) -+{ -+ int rc; -+ -+ rc = hid_hw_power(hdev, PM_HINT_FULLON); -+ -+ if (rc == 0) -+ return true; -+ -+ hid_err(hdev, -+ "Failed to disable auto-pm on touch bar device (%d)\n", rc); -+ return false; -+} -+ -+/* -+ * While the mode functionality is listed as a valid hid report in the usb -+ * interface descriptor, on a T1 it's not sent that way. Instead it's sent with -+ * different request-type and without a leading report-id in the data. Hence -+ * we need to send it as a custom usb control message rather via any of the -+ * standard hid_hw_*request() functions. The device might return EPIPE for a -+ * while after setting the display mode on T1 models, so retrying should be -+ * done on those models. -+ */ -+static int appletb_set_tb_mode(struct appletb_device *tb_dev, -+ unsigned char mode) -+{ -+ struct hid_report *report; -+ void *buf; -+ bool autopm_off = false; -+ int rc; -+ -+ if (!tb_dev->mode_iface.hdev) -+ return -ENOTCONN; -+ -+ report = tb_dev->mode_field->report; -+ -+ if (tb_dev->is_t1) { -+ buf = kmemdup(&mode, 1, GFP_KERNEL); -+ } else { -+ char data[] = { report->id, mode }; -+ -+ buf = kmemdup(data, sizeof(data), GFP_KERNEL); -+ } -+ if (!buf) -+ return -ENOMEM; -+ -+ autopm_off = appletb_disable_autopm(tb_dev->mode_iface.hdev); -+ -+ if (tb_dev->is_t1) { -+ int tries = 0; -+ struct usb_device *dev = interface_to_usbdev(tb_dev->mode_iface.usb_iface); -+ __u8 ifnum = tb_dev->mode_iface.usb_iface->cur_altsetting->desc.bInterfaceNumber; -+ -+ do { -+ rc = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), HID_REQ_SET_REPORT, -+ USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_VENDOR, -+ (report->type + 1) << 8 | report->id, -+ ifnum, buf, 1, 2000); -+ -+ if (rc != -EPIPE) -+ break; -+ -+ usleep_range(1000 << tries, 3000 << tries); -+ } while (++tries < 5); -+ } else { -+ rc = hid_hw_raw_request(tb_dev->mode_iface.hdev, report->id, -+ (__u8 *) buf, 2, report->type, -+ HID_REQ_SET_REPORT); -+ } -+ -+ if (rc < 0) -+ dev_err(tb_dev->log_dev, -+ "Failed to set touch bar mode to %u (%d)\n", mode, rc); -+ -+ if (autopm_off) -+ hid_hw_power(tb_dev->mode_iface.hdev, PM_HINT_NORMAL); -+ -+ kfree(buf); -+ -+ return rc; -+} -+ -+static int appletb_set_tb_disp(struct appletb_device *tb_dev, -+ unsigned char disp) -+{ -+ struct hid_report *report; -+ int rc; -+ -+ if (!tb_dev->disp_iface.hdev) -+ return -ENOTCONN; -+ -+ report = tb_dev->disp_field->report; -+ -+ rc = hid_set_field(tb_dev->disp_field_aux1, 0, 1); -+ if (rc) { -+ dev_err(tb_dev->log_dev, -+ "Failed to set display report field (%d)\n", rc); -+ return rc; -+ } -+ -+ rc = hid_set_field(tb_dev->disp_field, 0, disp); -+ if (rc) { -+ dev_err(tb_dev->log_dev, -+ "Failed to set display report field (%d)\n", rc); -+ return rc; -+ } -+ -+ /* -+ * Keep the USB interface powered on while the touch bar display is on -+ * for better responsiveness. -+ */ -+ if (disp != APPLETB_CMD_DISP_OFF && !tb_dev->tb_autopm_off) -+ tb_dev->tb_autopm_off = -+ appletb_disable_autopm(report->device); -+ -+ hid_hw_request(tb_dev->disp_iface.hdev, report, HID_REQ_SET_REPORT); -+ -+ if (disp == APPLETB_CMD_DISP_OFF && tb_dev->tb_autopm_off) { -+ hid_hw_power(tb_dev->disp_iface.hdev, PM_HINT_NORMAL); -+ tb_dev->tb_autopm_off = false; -+ } -+ -+ return rc; -+} -+ -+static bool appletb_any_tb_key_pressed(struct appletb_device *tb_dev) -+{ -+ return !!memchr_inv(tb_dev->last_tb_keys_pressed, 0, -+ sizeof(tb_dev->last_tb_keys_pressed)); -+} -+ -+static void appletb_schedule_tb_update(struct appletb_device *tb_dev, s64 secs) -+{ -+ schedule_delayed_work(&tb_dev->tb_work, msecs_to_jiffies(secs * 1000)); -+} -+ -+static void appletb_set_tb_worker(struct work_struct *work) -+{ -+ struct appletb_device *tb_dev = -+ container_of(work, struct appletb_device, tb_work.work); -+ s64 time_left = 0, min_timeout, time_to_off; -+ unsigned char pending_mode; -+ unsigned char pending_disp; -+ unsigned char current_disp; -+ bool restore_autopm; -+ bool any_tb_key_pressed, need_reschedule; -+ int rc1 = 1, rc2 = 1; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ /* handle explicit mode-change request */ -+ pending_mode = tb_dev->pnd_tb_mode; -+ pending_disp = tb_dev->pnd_tb_disp; -+ restore_autopm = tb_dev->restore_autopm; -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ if (pending_mode != APPLETB_CMD_MODE_NONE) -+ rc1 = appletb_set_tb_mode(tb_dev, pending_mode); -+ if (pending_mode != APPLETB_CMD_MODE_NONE && -+ pending_disp != APPLETB_CMD_DISP_NONE) -+ msleep(25); -+ if (pending_disp != APPLETB_CMD_DISP_NONE) -+ rc2 = appletb_set_tb_disp(tb_dev, pending_disp); -+ -+ if (restore_autopm && tb_dev->tb_autopm_off) -+ appletb_disable_autopm(tb_dev->disp_field->report->device); -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ need_reschedule = false; -+ -+ if (rc1 == 0) { -+ tb_dev->cur_tb_mode = pending_mode; -+ -+ if (tb_dev->pnd_tb_mode == pending_mode) -+ tb_dev->pnd_tb_mode = APPLETB_CMD_MODE_NONE; -+ else -+ need_reschedule = true; -+ } -+ -+ if (rc2 == 0) { -+ tb_dev->cur_tb_disp = pending_disp; -+ -+ if (tb_dev->pnd_tb_disp == pending_disp) -+ tb_dev->pnd_tb_disp = APPLETB_CMD_DISP_NONE; -+ else -+ need_reschedule = true; -+ } -+ current_disp = tb_dev->cur_tb_disp; -+ -+ tb_dev->restore_autopm = false; -+ -+ /* calculate time left to next timeout */ -+ if (tb_dev->idle_timeout == -2 || tb_dev->idle_timeout == 0) -+ min_timeout = -1; -+ else if (tb_dev->idle_timeout == -1) -+ min_timeout = tb_dev->dim_timeout; -+ else if (tb_dev->dim_timeout <= 0) -+ min_timeout = tb_dev->idle_timeout; -+ else -+ min_timeout = min(tb_dev->dim_timeout, tb_dev->idle_timeout); -+ -+ if (min_timeout > 0) { -+ s64 idle_time = -+ (ktime_ms_delta(ktime_get(), tb_dev->last_event_time) + -+ 500) / 1000; -+ -+ time_left = max(min_timeout - idle_time, 0LL); -+ if (tb_dev->idle_timeout <= 0) -+ time_to_off = -1; -+ else if (idle_time >= tb_dev->idle_timeout) -+ time_to_off = 0; -+ else -+ time_to_off = tb_dev->idle_timeout - idle_time; -+ } else { -+ /* not used - just to appease the compiler */ -+ time_to_off = 0; -+ } -+ -+ any_tb_key_pressed = appletb_any_tb_key_pressed(tb_dev); -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ dev_dbg(tb_dev->log_dev, "timeout calc: idle_timeout=%d dim_timeout=%d min_timeout=%lld time_left=%lld need_reschedule=%d any_tb_key_pressed=%d\n", -+ tb_dev->idle_timeout, tb_dev->dim_timeout, min_timeout, -+ time_left, need_reschedule, any_tb_key_pressed); -+ -+ /* a new command arrived while we were busy - handle it */ -+ if (need_reschedule) { -+ appletb_schedule_tb_update(tb_dev, 0); -+ return; -+ } -+ -+ /* if no idle/dim timeout, we're done */ -+ if (min_timeout <= 0) -+ return; -+ -+ /* manage idle/dim timeout */ -+ if (time_left > 0) { -+ /* we fired too soon or had a mode-change - re-schedule */ -+ appletb_schedule_tb_update(tb_dev, time_left); -+ } else if (any_tb_key_pressed) { -+ /* keys are still pressed - re-schedule */ -+ appletb_schedule_tb_update(tb_dev, min_timeout); -+ } else { -+ /* dim or idle timeout reached */ -+ int next_disp = (time_to_off == 0) ? APPLETB_CMD_DISP_OFF : -+ APPLETB_CMD_DISP_DIM; -+ if (next_disp != current_disp && -+ appletb_set_tb_disp(tb_dev, next_disp) == 0) { -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ tb_dev->cur_tb_disp = next_disp; -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ } -+ -+ if (time_to_off > 0) -+ appletb_schedule_tb_update(tb_dev, time_to_off); -+ } -+} -+ -+static u16 appletb_fn_to_special(u16 code) -+{ -+ int idx; -+ -+ for (idx = 0; idx < ARRAY_SIZE(appletb_fn_codes); idx++) { -+ if (appletb_fn_codes[idx].from == code) -+ return appletb_fn_codes[idx].to; -+ } -+ -+ return 0; -+} -+ -+static unsigned char appletb_get_cur_tb_mode(struct appletb_device *tb_dev) -+{ -+ return tb_dev->pnd_tb_mode != APPLETB_CMD_MODE_NONE ? -+ tb_dev->pnd_tb_mode : tb_dev->cur_tb_mode; -+} -+ -+static unsigned char appletb_get_cur_tb_disp(struct appletb_device *tb_dev) -+{ -+ return tb_dev->pnd_tb_disp != APPLETB_CMD_DISP_NONE ? -+ tb_dev->pnd_tb_disp : tb_dev->cur_tb_disp; -+} -+ -+static unsigned char appletb_get_fn_tb_mode(struct appletb_device *tb_dev) -+{ -+ switch (tb_dev->fn_mode) { -+ case APPLETB_FN_MODE_ESC: -+ return APPLETB_CMD_MODE_ESC; -+ -+ case APPLETB_FN_MODE_FKEYS: -+ return APPLETB_CMD_MODE_FN; -+ -+ case APPLETB_FN_MODE_SPCL: -+ return APPLETB_CMD_MODE_SPCL; -+ -+ case APPLETB_FN_MODE_INV: -+ return (tb_dev->last_fn_pressed) ? APPLETB_CMD_MODE_SPCL : -+ APPLETB_CMD_MODE_FN; -+ -+ case APPLETB_FN_MODE_NORM: -+ default: -+ return (tb_dev->last_fn_pressed) ? APPLETB_CMD_MODE_FN : -+ APPLETB_CMD_MODE_SPCL; -+ } -+} -+ -+/* -+ * Switch touch bar mode and display when mode or display not the desired ones. -+ */ -+static void appletb_update_touchbar_no_lock(struct appletb_device *tb_dev, -+ bool force) -+{ -+ unsigned char want_mode; -+ unsigned char want_disp; -+ bool need_update = false; -+ -+ /* -+ * Calculate the new modes: -+ * idle_timeout: -+ * -2 mode/disp off -+ * -1 mode on, disp on/dim -+ * 0 mode on, disp off -+ * >0 mode on, disp off after idle_timeout seconds -+ * dim_timeout (only valid if idle_timeout > 0 || idle_timeout == -1): -+ * -1 disp never dimmed -+ * 0 disp always dimmed -+ * >0 disp dim after dim_timeout seconds -+ */ -+ if (tb_dev->idle_timeout == -2) { -+ want_mode = APPLETB_CMD_MODE_OFF; -+ want_disp = APPLETB_CMD_DISP_OFF; -+ } else { -+ want_mode = appletb_get_fn_tb_mode(tb_dev); -+ want_disp = tb_dev->idle_timeout == 0 ? APPLETB_CMD_DISP_OFF : -+ tb_dev->dim_timeout == 0 ? APPLETB_CMD_DISP_DIM : -+ APPLETB_CMD_DISP_ON; -+ } -+ -+ /* -+ * See if we need to update the touch bar, taking into account that we -+ * generally don't want to switch modes while a touch bar key is -+ * pressed. -+ */ -+ if (appletb_get_cur_tb_mode(tb_dev) != want_mode && -+ !appletb_any_tb_key_pressed(tb_dev)) { -+ tb_dev->pnd_tb_mode = want_mode; -+ need_update = true; -+ } -+ -+ if (appletb_get_cur_tb_disp(tb_dev) != want_disp && -+ (!appletb_any_tb_key_pressed(tb_dev) || -+ want_disp != APPLETB_CMD_DISP_OFF)) { -+ tb_dev->pnd_tb_disp = want_disp; -+ need_update = true; -+ } -+ -+ if (force) -+ need_update = true; -+ -+ /* schedule the update if desired */ -+ dev_dbg_ratelimited(tb_dev->log_dev, -+ "update: need_update=%d, want_mode=%d, cur-mode=%d, want_disp=%d, cur-disp=%d\n", -+ need_update, want_mode, tb_dev->cur_tb_mode, -+ want_disp, tb_dev->cur_tb_disp); -+ -+ if (need_update) { -+ cancel_delayed_work(&tb_dev->tb_work); -+ appletb_schedule_tb_update(tb_dev, 0); -+ } -+} -+ -+static void appletb_update_touchbar(struct appletb_device *tb_dev, bool force) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (tb_dev->active) -+ appletb_update_touchbar_no_lock(tb_dev, force); -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+} -+ -+static void appletb_set_idle_timeout(struct appletb_device *tb_dev, int new) -+{ -+ tb_dev->idle_timeout = new; -+ -+ if (tb_dev->dim_to_is_calc && tb_dev->idle_timeout > 0) -+ tb_dev->dim_timeout = new - min(APPLETB_MAX_DIM_TIME, new / 3); -+ else if (tb_dev->dim_to_is_calc) -+ tb_dev->dim_timeout = -1; -+} -+ -+static ssize_t idle_timeout_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ -+ return snprintf(buf, PAGE_SIZE, "%d\n", tb_dev->idle_timeout); -+} -+ -+static ssize_t idle_timeout_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t size) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ long new; -+ int rc; -+ -+ rc = kstrtol(buf, 0, &new); -+ if (rc || new > INT_MAX || new < -2) -+ return -EINVAL; -+ -+ appletb_set_idle_timeout(tb_dev, new); -+ appletb_update_touchbar(tb_dev, true); -+ -+ return size; -+} -+ -+static void appletb_set_dim_timeout(struct appletb_device *tb_dev, int new) -+{ -+ if (new == -2) { -+ tb_dev->dim_to_is_calc = true; -+ appletb_set_idle_timeout(tb_dev, tb_dev->idle_timeout); -+ } else { -+ tb_dev->dim_to_is_calc = false; -+ tb_dev->dim_timeout = new; -+ } -+} -+ -+static ssize_t dim_timeout_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ -+ return snprintf(buf, PAGE_SIZE, "%d\n", -+ tb_dev->dim_to_is_calc ? -2 : tb_dev->dim_timeout); -+} -+ -+static ssize_t dim_timeout_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t size) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ long new; -+ int rc; -+ -+ rc = kstrtol(buf, 0, &new); -+ if (rc || new > INT_MAX || new < -2) -+ return -EINVAL; -+ -+ appletb_set_dim_timeout(tb_dev, new); -+ appletb_update_touchbar(tb_dev, true); -+ -+ return size; -+} -+ -+static ssize_t fnmode_show(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ -+ return snprintf(buf, PAGE_SIZE, "%d\n", tb_dev->fn_mode); -+} -+ -+static ssize_t fnmode_store(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t size) -+{ -+ struct appletb_device *tb_dev = dev_get_drvdata(dev); -+ long new; -+ int rc; -+ -+ rc = kstrtol(buf, 0, &new); -+ if (rc || new > APPLETB_FN_MODE_MAX || new < 0) -+ return -EINVAL; -+ -+ tb_dev->fn_mode = new; -+ appletb_update_touchbar(tb_dev, false); -+ -+ return size; -+} -+ -+static int appletb_tb_key_to_slot(unsigned int code) -+{ -+ switch (code) { -+ case KEY_ESC: -+ return 0; -+ case KEY_F1: -+ case KEY_F2: -+ case KEY_F3: -+ case KEY_F4: -+ case KEY_F5: -+ case KEY_F6: -+ case KEY_F7: -+ case KEY_F8: -+ case KEY_F9: -+ case KEY_F10: -+ return code - KEY_F1 + 1; -+ case KEY_F11: -+ case KEY_F12: -+ return code - KEY_F11 + 11; -+ default: -+ return -1; -+ } -+} -+ -+static int appletb_hid_event(struct hid_device *hdev, struct hid_field *field, -+ struct hid_usage *usage, __s32 value) -+{ -+ struct appletb_device *tb_dev = hid_get_drvdata(hdev); -+ unsigned int new_code = 0; -+ unsigned long flags; -+ bool send_dummy = false; -+ bool send_trnsl = false; -+ int slot; -+ int rc = 0; -+ -+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD || -+ usage->type != EV_KEY) -+ return 0; -+ -+ /* -+ * Skip non-touch-bar keys. -+ * -+ * Either the touch bar itself or usbhid generate a slew of key-down -+ * events for all the meta keys. None of which we're at all interested -+ * in. -+ */ -+ slot = appletb_tb_key_to_slot(usage->code); -+ if (slot < 0) -+ return 0; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (!tb_dev->active) { -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ return 0; -+ } -+ -+ new_code = appletb_fn_to_special(usage->code); -+ -+ if (value != 2) -+ tb_dev->last_tb_keys_pressed[slot] = value; -+ -+ tb_dev->last_event_time = ktime_get(); -+ -+ appletb_update_touchbar_no_lock(tb_dev, false); -+ -+ /* -+ * We want to suppress touch bar keys while the touch bar is off, but -+ * we do want to wake up the screen if it's asleep, so generate a dummy -+ * event in that case. -+ */ -+ if (tb_dev->cur_tb_mode == APPLETB_CMD_MODE_OFF || -+ tb_dev->cur_tb_disp == APPLETB_CMD_DISP_OFF) { -+ send_dummy = true; -+ rc = 1; -+ /* translate special keys */ -+ } else if (new_code && -+ ((value > 0 && -+ appletb_get_cur_tb_mode(tb_dev) == APPLETB_CMD_MODE_SPCL) -+ || -+ (value == 0 && tb_dev->last_tb_keys_translated[slot]))) { -+ tb_dev->last_tb_keys_translated[slot] = true; -+ send_trnsl = true; -+ rc = 1; -+ /* everything else handled normally */ -+ } else { -+ tb_dev->last_tb_keys_translated[slot] = false; -+ } -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ /* -+ * Need to send these input events outside of the lock, as otherwise -+ * we can run into the following deadlock: -+ * Task 1 Task 2 -+ * appletb_hid_event() input_event() -+ * acquire tb_lock acquire dev->event_lock -+ * input_event() appletb_inp_event() -+ * acquire dev->event_lock acquire tb_lock -+ */ -+ if (send_dummy) { -+ input_event(field->hidinput->input, EV_KEY, KEY_UNKNOWN, 1); -+ input_event(field->hidinput->input, EV_KEY, KEY_UNKNOWN, 0); -+ } else if (send_trnsl) { -+ input_event(field->hidinput->input, usage->type, new_code, -+ value); -+ } -+ -+ return rc; -+} -+ -+static void appletb_inp_event(struct input_handle *handle, unsigned int type, -+ unsigned int code, int value) -+{ -+ struct appletb_device *tb_dev = handle->private; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (!tb_dev->active) { -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ return; -+ } -+ -+ if (type == EV_KEY && code == KEY_FN && value != 2) -+ tb_dev->last_fn_pressed = value; -+ -+ tb_dev->last_event_time = ktime_get(); -+ -+ appletb_update_touchbar_no_lock(tb_dev, false); -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+} -+ -+/* Find and save the usb-device associated with the touch bar input device */ -+static struct usb_interface *appletb_get_usb_iface(struct hid_device *hdev) -+{ -+ struct device *dev = &hdev->dev; -+ -+ while (dev && !(dev->type && dev->type->name && -+ !strcmp(dev->type->name, "usb_interface"))) -+ dev = dev->parent; -+ -+ return dev ? to_usb_interface(dev) : NULL; -+} -+ -+static int appletb_inp_connect(struct input_handler *handler, -+ struct input_dev *dev, -+ const struct input_device_id *id) -+{ -+ struct appletb_device *tb_dev = handler->private; -+ struct input_handle *handle; -+ int rc; -+ -+ if (id->driver_info == APPLETB_DEVID_KEYBOARD) { -+ handle = &tb_dev->kbd_handle; -+ handle->name = "tbkbd"; -+ } else if (id->driver_info == APPLETB_DEVID_TOUCHPAD) { -+ handle = &tb_dev->tpd_handle; -+ handle->name = "tbtpad"; -+ } else { -+ dev_err(tb_dev->log_dev, "Unknown device id (%lu)\n", -+ id->driver_info); -+ return -ENOENT; -+ } -+ -+ if (handle->dev) { -+ dev_err(tb_dev->log_dev, -+ "Duplicate connect to %s input device\n", handle->name); -+ return -EEXIST; -+ } -+ -+ handle->open = 0; -+ handle->dev = input_get_device(dev); -+ handle->handler = handler; -+ handle->private = tb_dev; -+ -+ rc = input_register_handle(handle); -+ if (rc) -+ goto err_free_dev; -+ -+ rc = input_open_device(handle); -+ if (rc) -+ goto err_unregister_handle; -+ -+ dev_dbg(tb_dev->log_dev, "Connected to %s input device\n", -+ handle == &tb_dev->kbd_handle ? "keyboard" : "touchpad"); -+ -+ return 0; -+ -+ err_unregister_handle: -+ input_unregister_handle(handle); -+ err_free_dev: -+ input_put_device(handle->dev); -+ handle->dev = NULL; -+ return rc; -+} -+ -+static void appletb_inp_disconnect(struct input_handle *handle) -+{ -+ struct appletb_device *tb_dev = handle->private; -+ -+ input_close_device(handle); -+ input_unregister_handle(handle); -+ -+ dev_dbg(tb_dev->log_dev, "Disconnected from %s input device\n", -+ handle == &tb_dev->kbd_handle ? "keyboard" : "touchpad"); -+ -+ input_put_device(handle->dev); -+ handle->dev = NULL; -+} -+ -+static int appletb_input_configured(struct hid_device *hdev, -+ struct hid_input *hidinput) -+{ -+ int idx; -+ struct input_dev *input = hidinput->input; -+ -+ /* -+ * Clear various input capabilities that are blindly set by the hid -+ * driver (usbkbd.c) -+ */ -+ memset(input->evbit, 0, sizeof(input->evbit)); -+ memset(input->keybit, 0, sizeof(input->keybit)); -+ memset(input->ledbit, 0, sizeof(input->ledbit)); -+ -+ /* set our actual capabilities */ -+ __set_bit(EV_KEY, input->evbit); -+ __set_bit(EV_REP, input->evbit); -+ __set_bit(EV_MSC, input->evbit); /* hid-input generates MSC_SCAN */ -+ -+ for (idx = 0; idx < ARRAY_SIZE(appletb_fn_codes); idx++) { -+ input_set_capability(input, EV_KEY, appletb_fn_codes[idx].from); -+ input_set_capability(input, EV_KEY, appletb_fn_codes[idx].to); -+ } -+ -+ input_set_capability(input, EV_KEY, KEY_ESC); -+ input_set_capability(input, EV_KEY, KEY_UNKNOWN); -+ -+ return 0; -+} -+ -+static struct appletb_iface_info * -+appletb_get_iface_info(struct appletb_device *tb_dev, struct hid_device *hdev) -+{ -+ if (hdev == tb_dev->mode_iface.hdev) -+ return &tb_dev->mode_iface; -+ if (hdev == tb_dev->disp_iface.hdev) -+ return &tb_dev->disp_iface; -+ return NULL; -+} -+ -+/** -+ * appletb_find_report_field() - Find the field in the report with the given -+ * usage. -+ * @report: the report to search -+ * @field_usage: the usage of the field to search for -+ * -+ * Returns: the hid field if found, or NULL if none found. -+ */ -+static struct hid_field *appletb_find_report_field(struct hid_report *report, -+ unsigned int field_usage) -+{ -+ int f, u; -+ -+ for (f = 0; f < report->maxfield; f++) { -+ struct hid_field *field = report->field[f]; -+ -+ if (field->logical == field_usage) -+ return field; -+ -+ for (u = 0; u < field->maxusage; u++) { -+ if (field->usage[u].hid == field_usage) -+ return field; -+ } -+ } -+ -+ return NULL; -+} -+ -+/** -+ * appletb_find_hid_field() - Search all the reports of the device for the -+ * field with the given usage. -+ * @hdev: the device whose reports to search -+ * @application: the usage of application collection that the field must -+ * belong to -+ * @field_usage: the usage of the field to search for -+ * -+ * Returns: the hid field if found, or NULL if none found. -+ */ -+static struct hid_field *appletb_find_hid_field(struct hid_device *hdev, -+ unsigned int application, -+ unsigned int field_usage) -+{ -+ static const int report_types[] = { HID_INPUT_REPORT, HID_OUTPUT_REPORT, -+ HID_FEATURE_REPORT }; -+ struct hid_report *report; -+ struct hid_field *field; -+ int t; -+ -+ for (t = 0; t < ARRAY_SIZE(report_types); t++) { -+ struct list_head *report_list = -+ &hdev->report_enum[report_types[t]].report_list; -+ list_for_each_entry(report, report_list, list) { -+ if (report->application != application) -+ continue; -+ -+ field = appletb_find_report_field(report, field_usage); -+ if (field) -+ return field; -+ } -+ } -+ -+ return NULL; -+} -+ -+static int appletb_extract_report_and_iface_info(struct appletb_device *tb_dev, -+ struct hid_device *hdev, -+ const struct hid_device_id *id) -+{ -+ struct appletb_iface_info *iface_info; -+ struct usb_interface *usb_iface; -+ struct hid_field *field; -+ -+ field = appletb_find_hid_field(hdev, HID_GD_KEYBOARD, HID_USAGE_MODE); -+ if (field) { -+ iface_info = &tb_dev->mode_iface; -+ tb_dev->mode_field = field; -+ tb_dev->is_t1 = !!(id->driver_data & APPLETB_FEATURE_IS_T1); -+ } else { -+ field = appletb_find_hid_field(hdev, HID_USAGE_APPLE_APP, -+ HID_USAGE_DISP); -+ if (!field) -+ return 0; -+ -+ iface_info = &tb_dev->disp_iface; -+ tb_dev->disp_field = field; -+ tb_dev->disp_field_aux1 = -+ appletb_find_hid_field(hdev, HID_USAGE_APPLE_APP, -+ HID_USAGE_DISP_AUX1); -+ -+ if (!tb_dev->disp_field_aux1 || -+ tb_dev->disp_field_aux1->report != -+ tb_dev->disp_field->report) { -+ dev_err(tb_dev->log_dev, -+ "Unexpected report structure for report %u in device %s\n", -+ tb_dev->disp_field->report->id, -+ dev_name(&hdev->dev)); -+ return -ENODEV; -+ } -+ } -+ -+ usb_iface = appletb_get_usb_iface(hdev); -+ if (!usb_iface) { -+ dev_err(tb_dev->log_dev, -+ "Failed to find usb interface for hid device %s\n", -+ dev_name(&hdev->dev)); -+ return -ENODEV; -+ } -+ -+ iface_info->hdev = hdev; -+ iface_info->usb_iface = usb_get_intf(usb_iface); -+ iface_info->suspended = false; -+ -+ return 1; -+} -+ -+static void appletb_clear_iface_info(struct appletb_device *tb_dev, -+ struct hid_device *hdev) -+{ -+ struct appletb_iface_info *iface_info; -+ -+ iface_info = appletb_get_iface_info(tb_dev, hdev); -+ if (iface_info) { -+ usb_put_intf(iface_info->usb_iface); -+ iface_info->usb_iface = NULL; -+ iface_info->hdev = NULL; -+ } -+} -+ -+static bool appletb_test_and_mark_active(struct appletb_device *tb_dev) -+{ -+ unsigned long flags; -+ bool activated = false; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (tb_dev->mode_iface.hdev && tb_dev->disp_iface.hdev && -+ !tb_dev->active) { -+ tb_dev->active = true; -+ activated = true; -+ } -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ return activated; -+} -+ -+static bool appletb_test_and_mark_inactive(struct appletb_device *tb_dev, -+ struct hid_device *hdev) -+{ -+ unsigned long flags; -+ bool deactivated = false; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (tb_dev->mode_iface.hdev && tb_dev->disp_iface.hdev && -+ tb_dev->active && -+ (hdev == tb_dev->mode_iface.hdev || -+ hdev == tb_dev->disp_iface.hdev)) { -+ tb_dev->active = false; -+ deactivated = true; -+ } -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ return deactivated; -+} -+ -+static const struct input_device_id appletb_input_devices[] = { -+ { -+ .flags = INPUT_DEVICE_ID_MATCH_BUS | -+ INPUT_DEVICE_ID_MATCH_KEYBIT, -+ .bustype = BUS_SPI, -+ .keybit = { [BIT_WORD(KEY_FN)] = BIT_MASK(KEY_FN) }, -+ .driver_info = APPLETB_DEVID_KEYBOARD, -+ }, /* Builtin SPI keyboard device */ -+ { -+ .flags = INPUT_DEVICE_ID_MATCH_BUS | -+ INPUT_DEVICE_ID_MATCH_KEYBIT, -+ .bustype = BUS_SPI, -+ .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, -+ .driver_info = APPLETB_DEVID_TOUCHPAD, -+ }, /* Builtin SPI touchpad device */ -+ { -+ .flags = INPUT_DEVICE_ID_MATCH_BUS | -+ INPUT_DEVICE_ID_MATCH_VENDOR | -+ INPUT_DEVICE_ID_MATCH_KEYBIT, -+ .bustype = BUS_USB, -+ .vendor = 0x05ac /* USB_VENDOR_ID_APPLE */, -+ .keybit = { [BIT_WORD(KEY_FN)] = BIT_MASK(KEY_FN) }, -+ .driver_info = APPLETB_DEVID_KEYBOARD, -+ }, /* Builtin USB keyboard device */ -+ { -+ .flags = INPUT_DEVICE_ID_MATCH_BUS | -+ INPUT_DEVICE_ID_MATCH_VENDOR | -+ INPUT_DEVICE_ID_MATCH_KEYBIT, -+ .bustype = BUS_USB, -+ .vendor = 0x05ac /* USB_VENDOR_ID_APPLE */, -+ .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, -+ .driver_info = APPLETB_DEVID_TOUCHPAD, -+ }, /* Builtin USB touchpad device */ -+ { }, /* Terminating zero entry */ -+}; -+ -+static bool appletb_match_internal_device(struct input_handler *handler, -+ struct input_dev *inp_dev) -+{ -+ struct device *dev = &inp_dev->dev; -+ -+ if (inp_dev->id.bustype == BUS_SPI) -+ return true; -+ -+ /* in kernel: dev && !is_usb_device(dev) */ -+ while (dev && !(dev->type && dev->type->name && -+ !strcmp(dev->type->name, "usb_device"))) -+ dev = dev->parent; -+ -+ /* -+ * Apple labels all their internal keyboards and trackpads as such, -+ * instead of maintaining an ever expanding list of product-id's we -+ * just look at the device's product name. -+ */ -+ if (dev) -+ return !!strstr(to_usb_device(dev)->product, "Internal Keyboard"); -+ -+ return false; -+} -+ -+static int appletb_probe(struct hid_device *hdev, -+ const struct hid_device_id *id) -+{ -+ struct appletb_device *tb_dev = appletb_dev; -+ unsigned long flags; -+ int rc; -+ -+ /* initialize the report info */ -+ rc = hid_parse(hdev); -+ if (rc) { -+ dev_err(tb_dev->log_dev, "hid parse failed (%d)\n", rc); -+ goto error; -+ } -+ -+ /* Ensure this usb endpoint is for the touchbar backlight, not keyboard -+ * backlight. -+ */ -+ if ((hdev->product == USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) && -+ !(hdev->collection && hdev->collection[0].usage == -+ HID_USAGE_APPLE_APP)) { -+ return -ENODEV; -+ } -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (!tb_dev->log_dev) -+ tb_dev->log_dev = &hdev->dev; -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ hid_set_drvdata(hdev, tb_dev); -+ -+ rc = appletb_extract_report_and_iface_info(tb_dev, hdev, id); -+ if (rc < 0) -+ goto error; -+ -+ rc = hid_hw_start(hdev, HID_CONNECT_DRIVER | HID_CONNECT_HIDINPUT); -+ if (rc) { -+ dev_err(tb_dev->log_dev, "hw start failed (%d)\n", rc); -+ goto clear_iface_info; -+ } -+ -+ rc = hid_hw_open(hdev); -+ if (rc) { -+ dev_err(tb_dev->log_dev, "hw open failed (%d)\n", rc); -+ goto stop_hid; -+ } -+ -+ /* do setup if we have both interfaces */ -+ if (appletb_test_and_mark_active(tb_dev)) { -+ /* initialize the touch bar */ -+ if (appletb_tb_def_fn_mode >= 0 && -+ appletb_tb_def_fn_mode <= APPLETB_FN_MODE_MAX) -+ tb_dev->fn_mode = appletb_tb_def_fn_mode; -+ else -+ tb_dev->fn_mode = APPLETB_FN_MODE_NORM; -+ appletb_set_idle_timeout(tb_dev, appletb_tb_def_idle_timeout); -+ appletb_set_dim_timeout(tb_dev, appletb_tb_def_dim_timeout); -+ tb_dev->last_event_time = ktime_get(); -+ -+ tb_dev->pnd_tb_mode = APPLETB_CMD_MODE_UPD; -+ tb_dev->pnd_tb_disp = APPLETB_CMD_DISP_UPD; -+ -+ appletb_update_touchbar(tb_dev, false); -+ -+ /* set up the input handler */ -+ tb_dev->inp_handler.event = appletb_inp_event; -+ tb_dev->inp_handler.connect = appletb_inp_connect; -+ tb_dev->inp_handler.disconnect = appletb_inp_disconnect; -+ tb_dev->inp_handler.name = "appletb"; -+ tb_dev->inp_handler.id_table = appletb_input_devices; -+ tb_dev->inp_handler.match = appletb_match_internal_device; -+ tb_dev->inp_handler.private = tb_dev; -+ -+ rc = input_register_handler(&tb_dev->inp_handler); -+ if (rc) { -+ dev_err(tb_dev->log_dev, -+ "Unable to register keyboard handler (%d)\n", -+ rc); -+ goto mark_inactive; -+ } -+ -+ /* initialize sysfs attributes */ -+ rc = sysfs_create_group(&tb_dev->mode_iface.hdev->dev.kobj, -+ &appletb_attr_group); -+ if (rc) { -+ dev_err(tb_dev->log_dev, -+ "Failed to create sysfs attributes (%d)\n", rc); -+ goto unreg_handler; -+ } -+ -+ dev_dbg(tb_dev->log_dev, "Touchbar activated\n"); -+ } -+ -+ return 0; -+ -+unreg_handler: -+ input_unregister_handler(&tb_dev->inp_handler); -+mark_inactive: -+ appletb_test_and_mark_inactive(tb_dev, hdev); -+ cancel_delayed_work_sync(&tb_dev->tb_work); -+ hid_hw_close(hdev); -+stop_hid: -+ hid_hw_stop(hdev); -+clear_iface_info: -+ appletb_clear_iface_info(tb_dev, hdev); -+error: -+ return rc; -+} -+ -+static void appletb_remove(struct hid_device *hdev) -+{ -+ struct appletb_device *tb_dev = hid_get_drvdata(hdev); -+ unsigned long flags; -+ -+ if (appletb_test_and_mark_inactive(tb_dev, hdev)) { -+ sysfs_remove_group(&tb_dev->mode_iface.hdev->dev.kobj, -+ &appletb_attr_group); -+ -+ input_unregister_handler(&tb_dev->inp_handler); -+ -+ cancel_delayed_work_sync(&tb_dev->tb_work); -+ appletb_set_tb_mode(tb_dev, APPLETB_CMD_MODE_OFF); -+ appletb_set_tb_disp(tb_dev, APPLETB_CMD_DISP_ON); -+ -+ if (tb_dev->tb_autopm_off) -+ hid_hw_power(tb_dev->disp_iface.hdev, PM_HINT_NORMAL); -+ -+ dev_info(tb_dev->log_dev, "Touchbar deactivated\n"); -+ } -+ -+ hid_hw_close(hdev); -+ hid_hw_stop(hdev); -+ appletb_clear_iface_info(tb_dev, hdev); -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (tb_dev->log_dev == &hdev->dev) { -+ if (tb_dev->mode_iface.hdev) -+ tb_dev->log_dev = &tb_dev->mode_iface.hdev->dev; -+ else if (tb_dev->disp_iface.hdev) -+ tb_dev->log_dev = &tb_dev->disp_iface.hdev->dev; -+ else -+ tb_dev->log_dev = NULL; -+ } -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+} -+ -+#ifdef CONFIG_PM -+static int appletb_suspend(struct hid_device *hdev, pm_message_t message) -+{ -+ struct appletb_device *tb_dev = hid_get_drvdata(hdev); -+ struct appletb_iface_info *iface_info; -+ unsigned long flags; -+ bool all_suspended = false; -+ -+ if (message.event != PM_EVENT_SUSPEND && -+ message.event != PM_EVENT_FREEZE) -+ return 0; -+ -+ if (tb_dev->is_t1) { -+ -+ /* -+ * Wait for both interfaces to be suspended and no more async work -+ * in progress. -+ */ -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ if (!tb_dev->mode_iface.suspended && !tb_dev->disp_iface.suspended) { -+ tb_dev->active = false; -+ cancel_delayed_work(&tb_dev->tb_work); -+ } -+ -+ iface_info = appletb_get_iface_info(tb_dev, hdev); -+ if (iface_info) -+ iface_info->suspended = true; -+ -+ if ((!tb_dev->mode_iface.hdev || tb_dev->mode_iface.suspended) && -+ (!tb_dev->disp_iface.hdev || tb_dev->disp_iface.suspended)) -+ all_suspended = true; -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ flush_delayed_work(&tb_dev->tb_work); -+ -+ if (!all_suspended) -+ return 0; -+ -+ /* -+ * The touch bar device itself remembers the last state when suspended -+ * in some cases, but in others (e.g. when mode != off and disp == off) -+ * it resumes with a different state; furthermore it may be only -+ * partially responsive in that state. By turning both mode and disp -+ * off we ensure it is in a good state when resuming (and this happens -+ * to be the same state after booting/resuming-from-hibernate, so less -+ * special casing between the two). -+ */ -+ if (message.event == PM_EVENT_SUSPEND) { -+ appletb_set_tb_mode(tb_dev, APPLETB_CMD_MODE_OFF); -+ appletb_set_tb_disp(tb_dev, APPLETB_CMD_DISP_OFF); -+ } -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ tb_dev->cur_tb_mode = APPLETB_CMD_MODE_OFF; -+ tb_dev->cur_tb_disp = APPLETB_CMD_DISP_OFF; -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ dev_info(tb_dev->log_dev, "Touchbar suspended.\n"); -+ } else { -+ dev_info(tb_dev->log_dev, "T2 Mac detected, not handling suspend.\n"); -+ } -+ -+ return 0; -+} -+ -+static int appletb_reset_resume(struct hid_device *hdev) -+{ -+ struct appletb_device *tb_dev = hid_get_drvdata(hdev); -+ struct appletb_iface_info *iface_info; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&tb_dev->tb_lock, flags); -+ -+ iface_info = appletb_get_iface_info(tb_dev, hdev); -+ if (iface_info) -+ iface_info->suspended = false; -+ -+ if ((tb_dev->mode_iface.hdev && !tb_dev->mode_iface.suspended) && -+ (tb_dev->disp_iface.hdev && !tb_dev->disp_iface.suspended)) { -+ /* -+ * Restore touch bar state. Note that autopm state is not -+ * preserved, so need explicitly restore that here. -+ */ -+ tb_dev->active = true; -+ tb_dev->restore_autopm = true; -+ tb_dev->last_event_time = ktime_get(); -+ -+ appletb_update_touchbar_no_lock(tb_dev, true); -+ -+ dev_info(tb_dev->log_dev, "Touchbar resumed.\n"); -+ } -+ -+ spin_unlock_irqrestore(&tb_dev->tb_lock, flags); -+ -+ return 0; -+} -+#endif -+ -+static struct appletb_device *appletb_alloc_device(void) -+{ -+ struct appletb_device *tb_dev; -+ -+ tb_dev = kzalloc(sizeof(*tb_dev), GFP_KERNEL); -+ if (!tb_dev) -+ return NULL; -+ -+ spin_lock_init(&tb_dev->tb_lock); -+ INIT_DELAYED_WORK(&tb_dev->tb_work, appletb_set_tb_worker); -+ -+ return tb_dev; -+} -+ -+static void appletb_free_device(struct appletb_device *tb_dev) -+{ -+ cancel_delayed_work_sync(&tb_dev->tb_work); -+ kfree(tb_dev); -+} -+ -+static const struct hid_device_id appletb_hid_ids[] = { -+ /* MacBook Pro's 2016, 2017, with T1 chip */ -+ { HID_USB_DEVICE(USB_VENDOR_ID_LINUX_FOUNDATION, -+ USB_DEVICE_ID_IBRIDGE_TB), -+ .driver_data = APPLETB_FEATURE_IS_T1 }, -+ /* MacBook Pro's 2018, 2019, with T2 chip: iBridge DFR brightness */ -+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, -+ USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, -+ /* MacBook Pro's 2018, 2019, with T2 chip: iBridge Display */ -+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, -+ USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, -+ { }, -+}; -+ -+MODULE_DEVICE_TABLE(hid, appletb_hid_ids); -+ -+static struct hid_driver appletb_hid_driver = { -+ .name = "apple-touchbar", -+ .id_table = appletb_hid_ids, -+ .probe = appletb_probe, -+ .remove = appletb_remove, -+ .event = appletb_hid_event, -+ .input_configured = appletb_input_configured, -+#ifdef CONFIG_PM -+ .suspend = appletb_suspend, -+ .reset_resume = appletb_reset_resume, -+#endif -+}; -+ -+static int __init appletb_init(void) -+{ -+ struct appletb_device *tb_dev; -+ int rc; -+ -+ tb_dev = appletb_alloc_device(); -+ if (!tb_dev) -+ return -ENOMEM; -+ -+ appletb_dev = tb_dev; -+ -+ rc = hid_register_driver(&appletb_hid_driver); -+ if (rc) -+ goto error; -+ -+ return 0; -+ -+error: -+ appletb_free_device(tb_dev); -+ return rc; -+} -+ -+static void __exit appletb_exit(void) -+{ -+ hid_unregister_driver(&appletb_hid_driver); -+ appletb_free_device(appletb_dev); -+} -+ -+module_init(appletb_init); -+module_exit(appletb_exit); -+ -+MODULE_AUTHOR("Ronald Tschalär"); -+MODULE_DESCRIPTION("MacBookPro Touch Bar driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c -index e9b591fdacdc..b26aadfe5f26 100644 ---- a/drivers/hid/hid-quirks.c -+++ b/drivers/hid/hid-quirks.c -@@ -316,12 +316,14 @@ static const struct hid_device_id hid_have_special_driver[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021) }, -- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, -- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, - #endif - #if IS_ENABLED(CONFIG_HID_APPLE_IBRIDGE) - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IBRIDGE) }, - #endif -+#if IS_ENABLED(CONFIG_HID_APPLE_TOUCHBAR) -+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, -+#endif - #if IS_ENABLED(CONFIG_HID_APPLEIR) - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, --- -2.39.1 - diff --git a/1008-HID-multitouch-support-getting-the-tip-state-from-HI.patch b/1008-HID-multitouch-support-getting-the-tip-state-from-HI.patch new file mode 100644 index 0000000..a0a60e1 --- /dev/null +++ b/1008-HID-multitouch-support-getting-the-tip-state-from-HI.patch @@ -0,0 +1,58 @@ +From 6162d328fe7b2cf5a3ee8c29bdb229e9528c7a6c Mon Sep 17 00:00:00 2001 +From: Kerem Karabay +Date: Wed, 19 Jul 2023 19:44:10 +0300 +Subject: [PATCH 05/12] HID: multitouch: support getting the tip state from + HID_DG_TOUCH fields + +This is necessary on Apple Touch Bars, where the tip state is contained +in fields with the HID_DG_TOUCH usage. This feature is gated by a quirk +in order to prevent breaking other devices, see commit c2ef8f21ea8f +("HID: multitouch: add support for trackpads"). + +Signed-off-by: Kerem Karabay +--- + drivers/hid/hid-multitouch.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index 902a59928..dd5509eeb 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -72,6 +72,7 @@ MODULE_LICENSE("GPL"); + #define MT_QUIRK_FORCE_MULTI_INPUT BIT(20) + #define MT_QUIRK_DISABLE_WAKEUP BIT(21) + #define MT_QUIRK_ORIENTATION_INVERT BIT(22) ++#define MT_QUIRK_TOUCH_IS_TIPSTATE BIT(23) + + #define MT_INPUTMODE_TOUCHSCREEN 0x02 + #define MT_INPUTMODE_TOUCHPAD 0x03 +@@ -810,6 +811,15 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, + + MT_STORE_FIELD(confidence_state); + return 1; ++ case HID_DG_TOUCH: ++ /* ++ * Legacy devices use TIPSWITCH and not TOUCH. ++ * Let's just ignore this field unless the quirk is set. ++ */ ++ if (!(cls->quirks & MT_QUIRK_TOUCH_IS_TIPSTATE)) ++ return -1; ++ ++ fallthrough; + case HID_DG_TIPSWITCH: + if (field->application != HID_GD_SYSTEM_MULTIAXIS) + input_set_capability(hi->input, +@@ -873,10 +883,6 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, + case HID_DG_CONTACTMAX: + /* contact max are global to the report */ + return -1; +- case HID_DG_TOUCH: +- /* Legacy devices use TIPSWITCH and not TOUCH. +- * Let's just ignore this field. */ +- return -1; + } + /* let hid-input decide for the others */ + return 0; +-- +2.42.0 + diff --git a/1009-HID-multitouch-take-cls-maxcontacts-into-account-for.patch b/1009-HID-multitouch-take-cls-maxcontacts-into-account-for.patch new file mode 100644 index 0000000..c750ee8 --- /dev/null +++ b/1009-HID-multitouch-take-cls-maxcontacts-into-account-for.patch @@ -0,0 +1,42 @@ +From e923c6e1a5a508e341851ae020cdb3e7333ccd18 Mon Sep 17 00:00:00 2001 +From: Kerem Karabay +Date: Wed, 19 Jul 2023 19:26:57 +0300 +Subject: [PATCH 06/12] HID: multitouch: take cls->maxcontacts into account for + devices without a HID_DG_CONTACTMAX field too + +This is needed for Apple Touch Bars, where no HID_DG_CONTACTMAX field is +present and the maximum contact count is greater than the default. + +Signed-off-by: Kerem Karabay +--- + drivers/hid/hid-multitouch.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index dd5509eeb..624c1d3cc 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -491,9 +491,6 @@ static void mt_feature_mapping(struct hid_device *hdev, + if (!td->maxcontacts && + field->logical_maximum <= MT_MAX_MAXCONTACT) + td->maxcontacts = field->logical_maximum; +- if (td->mtclass.maxcontacts) +- /* check if the maxcontacts is given by the class */ +- td->maxcontacts = td->mtclass.maxcontacts; + + break; + case HID_DG_BUTTONTYPE: +@@ -1310,6 +1307,10 @@ static int mt_touch_input_configured(struct hid_device *hdev, + struct input_dev *input = hi->input; + int ret; + ++ /* check if the maxcontacts is given by the class */ ++ if (cls->maxcontacts) ++ td->maxcontacts = cls->maxcontacts; ++ + if (!td->maxcontacts) + td->maxcontacts = MT_DEFAULT_MAXCONTACT; + +-- +2.42.0 + diff --git a/1010-HID-multitouch-allow-specifying-if-a-device-is-direc.patch b/1010-HID-multitouch-allow-specifying-if-a-device-is-direc.patch new file mode 100644 index 0000000..f76b5de --- /dev/null +++ b/1010-HID-multitouch-allow-specifying-if-a-device-is-direc.patch @@ -0,0 +1,57 @@ +From b9f7232d2696b91ae98fadd7b14c531aa8edceb5 Mon Sep 17 00:00:00 2001 +From: Kerem Karabay +Date: Wed, 19 Jul 2023 19:39:53 +0300 +Subject: [PATCH 07/12] HID: multitouch: allow specifying if a device is direct + in a class + +Currently the driver determines the device type based on the +application, but this value is not reliable on Apple Touch Bars, where +the application is HID_DG_TOUCHPAD even though the devices are direct, +so allow setting it in classes. + +Signed-off-by: Kerem Karabay +--- + drivers/hid/hid-multitouch.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index 624c1d3cc..f98fb36ff 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -147,6 +147,7 @@ struct mt_class { + __s32 sn_height; /* Signal/noise ratio for height events */ + __s32 sn_pressure; /* Signal/noise ratio for pressure events */ + __u8 maxcontacts; ++ bool is_direct; /* true for touchscreens */ + bool is_indirect; /* true for touchpads */ + bool export_all_inputs; /* do not ignore mouse, keyboards, etc... */ + }; +@@ -564,13 +565,13 @@ static struct mt_application *mt_allocate_application(struct mt_device *td, + mt_application->application = application; + INIT_LIST_HEAD(&mt_application->mt_usages); + +- if (application == HID_DG_TOUCHSCREEN) ++ if (application == HID_DG_TOUCHSCREEN && !td->mtclass.is_indirect) + mt_application->mt_flags |= INPUT_MT_DIRECT; + + /* + * Model touchscreens providing buttons as touchpads. + */ +- if (application == HID_DG_TOUCHPAD) { ++ if (application == HID_DG_TOUCHPAD && !td->mtclass.is_direct) { + mt_application->mt_flags |= INPUT_MT_POINTER; + td->inputmode_value = MT_INPUTMODE_TOUCHPAD; + } +@@ -1318,6 +1319,9 @@ static int mt_touch_input_configured(struct hid_device *hdev, + if (td->serial_maybe) + mt_post_parse_default_settings(td, app); + ++ if (cls->is_direct) ++ app->mt_flags |= INPUT_MT_DIRECT; ++ + if (cls->is_indirect) + app->mt_flags |= INPUT_MT_POINTER; + +-- +2.42.0 + diff --git a/1011-HID-multitouch-add-device-ID-for-Apple-Touch-Bars.patch b/1011-HID-multitouch-add-device-ID-for-Apple-Touch-Bars.patch new file mode 100644 index 0000000..0ef70de --- /dev/null +++ b/1011-HID-multitouch-add-device-ID-for-Apple-Touch-Bars.patch @@ -0,0 +1,94 @@ +From a74de0b6f2e1b79d54e84dbeab1b310232275d6c Mon Sep 17 00:00:00 2001 +From: Kerem Karabay +Date: Wed, 19 Jul 2023 19:46:02 +0300 +Subject: [PATCH 08/12] HID: multitouch: add device ID for Apple Touch Bars + +Note that this is device ID is for T2 Macs. Testing on T1 Macs would be +appreciated. + +Signed-off-by: Kerem Karabay +--- + drivers/hid/Kconfig | 1 + + drivers/hid/hid-multitouch.c | 26 ++++++++++++++++++++++---- + 2 files changed, 23 insertions(+), 4 deletions(-) + +diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig +index 852de13aa..4e238df87 100644 +--- a/drivers/hid/Kconfig ++++ b/drivers/hid/Kconfig +@@ -737,6 +737,7 @@ config HID_MULTITOUCH + Say Y here if you have one of the following devices: + - 3M PCT touch screens + - ActionStar dual touch panels ++ - Touch Bars on x86 MacBook Pros + - Atmel panels + - Cando dual touch panels + - Chunghwa panels +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index f98fb36ff..f881b19db 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -215,6 +215,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app); + #define MT_CLS_GOOGLE 0x0111 + #define MT_CLS_RAZER_BLADE_STEALTH 0x0112 + #define MT_CLS_SMART_TECH 0x0113 ++#define MT_CLS_APPLE_TOUCHBAR 0x0114 + + #define MT_DEFAULT_MAXCONTACT 10 + #define MT_MAX_MAXCONTACT 250 +@@ -399,6 +400,13 @@ static const struct mt_class mt_classes[] = { + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_SEPARATE_APP_REPORT, + }, ++ { .name = MT_CLS_APPLE_TOUCHBAR, ++ .quirks = MT_QUIRK_HOVERING | ++ MT_QUIRK_TOUCH_IS_TIPSTATE | ++ MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE, ++ .is_direct = true, ++ .maxcontacts = 11, ++ }, + { } + }; + +@@ -1755,6 +1763,15 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) + } + } + ++ ret = hid_parse(hdev); ++ if (ret != 0) ++ return ret; ++ ++ if (mtclass->name == MT_CLS_APPLE_TOUCHBAR && ++ !hid_find_field(hdev, HID_INPUT_REPORT, ++ HID_DG_TOUCHPAD, HID_DG_TRANSDUCER_INDEX)) ++ return -ENODEV; ++ + td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL); + if (!td) { + dev_err(&hdev->dev, "cannot allocate multitouch data\n"); +@@ -1802,10 +1819,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) + + timer_setup(&td->release_timer, mt_expired_timeout, 0); + +- ret = hid_parse(hdev); +- if (ret != 0) +- return ret; +- + if (mtclass->quirks & MT_QUIRK_FIX_CONST_CONTACT_ID) + mt_fix_const_fields(hdev, HID_DG_CONTACTID); + +@@ -2240,6 +2253,11 @@ static const struct hid_device_id mt_devices[] = { + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_CSR2) }, + ++ /* Apple Touch Bars */ ++ { .driver_data = MT_CLS_APPLE_TOUCHBAR, ++ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, ++ USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, ++ + /* Google MT devices */ + { .driver_data = MT_CLS_GOOGLE, + HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE, +-- +2.42.0 + diff --git a/1012-lib-vsprintf-Add-support-for-generic-FOURCCs-by-exte.patch b/1012-lib-vsprintf-Add-support-for-generic-FOURCCs-by-exte.patch new file mode 100644 index 0000000..68525f0 --- /dev/null +++ b/1012-lib-vsprintf-Add-support-for-generic-FOURCCs-by-exte.patch @@ -0,0 +1,197 @@ +From f6ab7e4580962c9d82e7dc40dd074d47b2bce034 Mon Sep 17 00:00:00 2001 +From: Hector Martin +Date: Tue, 1 Feb 2022 00:40:51 +0900 +Subject: [PATCH 09/12] lib/vsprintf: Add support for generic FOURCCs by + extending %p4cc + +%p4cc is designed for DRM/V4L2 FOURCCs with their specific quirks, but +it's useful to be able to print generic 4-character codes formatted as +an integer. Extend it to add format specifiers for printing generic +32-bit FOURCCs with various endian semantics: + +%p4ch Host-endian +%p4cl Little-endian +%p4cb Big-endian +%p4cr Reverse-endian + +The endianness determines how bytes are interpreted as a u32, and the +FOURCC is then always printed MSByte-first (this is the opposite of +V4L/DRM FOURCCs). This covers most practical cases, e.g. %p4cr would +allow printing LSByte-first FOURCCs stored in host endian order +(other than the hex form being in character order, not the integer +value). + +Signed-off-by: Hector Martin +Signed-off-by: Kerem Karabay +--- + Documentation/core-api/printk-formats.rst | 32 ++++++++++++++++++++ + lib/test_printf.c | 20 +++++++++---- + lib/vsprintf.c | 36 +++++++++++++++++++---- + scripts/checkpatch.pl | 2 +- + 4 files changed, 77 insertions(+), 13 deletions(-) + +diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst +index dfe7e75a7..0ccef63e6 100644 +--- a/Documentation/core-api/printk-formats.rst ++++ b/Documentation/core-api/printk-formats.rst +@@ -631,6 +631,38 @@ Examples:: + %p4cc Y10 little-endian (0x20303159) + %p4cc NV12 big-endian (0xb231564e) + ++Generic FourCC code ++------------------- ++ ++:: ++ %p4c[hnbl] gP00 (0x67503030) ++ ++Print a generic FourCC code, as both ASCII characters and its numerical ++value as hexadecimal. ++ ++The additional ``h``, ``r``, ``b``, and ``l`` specifiers are used to specify ++host, reversed, big or little endian order data respectively. Host endian ++order means the data is interpreted as a 32-bit integer and the most ++significant byte is printed first; that is, the character code as printed ++matches the byte order stored in memory on big-endian systems, and is reversed ++on little-endian systems. ++ ++Passed by reference. ++ ++Examples for a little-endian machine, given &(u32)0x67503030:: ++ ++ %p4ch gP00 (0x67503030) ++ %p4cl gP00 (0x67503030) ++ %p4cb 00Pg (0x30305067) ++ %p4cr 00Pg (0x30305067) ++ ++Examples for a big-endian machine, given &(u32)0x67503030:: ++ ++ %p4ch gP00 (0x67503030) ++ %p4cl 00Pg (0x30305067) ++ %p4cb gP00 (0x67503030) ++ %p4cr 00Pg (0x30305067) ++ + Rust + ---- + +diff --git a/lib/test_printf.c b/lib/test_printf.c +index 7677ebccf..2355be36f 100644 +--- a/lib/test_printf.c ++++ b/lib/test_printf.c +@@ -746,18 +746,26 @@ static void __init fwnode_pointer(void) + static void __init fourcc_pointer(void) + { + struct { ++ char type; + u32 code; + char *str; + } const try[] = { +- { 0x3231564e, "NV12 little-endian (0x3231564e)", }, +- { 0xb231564e, "NV12 big-endian (0xb231564e)", }, +- { 0x10111213, ".... little-endian (0x10111213)", }, +- { 0x20303159, "Y10 little-endian (0x20303159)", }, ++ { 'c', 0x3231564e, "NV12 little-endian (0x3231564e)", }, ++ { 'c', 0xb231564e, "NV12 big-endian (0xb231564e)", }, ++ { 'c', 0x10111213, ".... little-endian (0x10111213)", }, ++ { 'c', 0x20303159, "Y10 little-endian (0x20303159)", }, ++ { 'h', 0x67503030, "gP00 (0x67503030)", }, ++ { 'r', 0x30305067, "gP00 (0x67503030)", }, ++ { 'l', cpu_to_le32(0x67503030), "gP00 (0x67503030)", }, ++ { 'b', cpu_to_be32(0x67503030), "gP00 (0x67503030)", }, + }; + unsigned int i; + +- for (i = 0; i < ARRAY_SIZE(try); i++) +- test(try[i].str, "%p4cc", &try[i].code); ++ for (i = 0; i < ARRAY_SIZE(try); i++) { ++ char fmt[] = { '%', 'p', '4', 'c', try[i].type, '\0' }; ++ ++ test(try[i].str, fmt, &try[i].code); ++ } + } + + static void __init +diff --git a/lib/vsprintf.c b/lib/vsprintf.c +index 40f560959..bd9af783c 100644 +--- a/lib/vsprintf.c ++++ b/lib/vsprintf.c +@@ -1758,27 +1758,50 @@ char *fourcc_string(char *buf, char *end, const u32 *fourcc, + char output[sizeof("0123 little-endian (0x01234567)")]; + char *p = output; + unsigned int i; ++ bool pix_fmt = false; + u32 orig, val; + +- if (fmt[1] != 'c' || fmt[2] != 'c') ++ if (fmt[1] != 'c') + return error_string(buf, end, "(%p4?)", spec); + + if (check_pointer(&buf, end, fourcc, spec)) + return buf; + + orig = get_unaligned(fourcc); +- val = orig & ~BIT(31); ++ switch (fmt[2]) { ++ case 'h': ++ val = orig; ++ break; ++ case 'r': ++ val = orig = swab32(orig); ++ break; ++ case 'l': ++ val = orig = le32_to_cpu(orig); ++ break; ++ case 'b': ++ val = orig = be32_to_cpu(orig); ++ break; ++ case 'c': ++ /* Pixel formats are printed LSB-first */ ++ val = swab32(orig & ~BIT(31)); ++ pix_fmt = true; ++ break; ++ default: ++ return error_string(buf, end, "(%p4?)", spec); ++ } + + for (i = 0; i < sizeof(u32); i++) { +- unsigned char c = val >> (i * 8); ++ unsigned char c = val >> ((3 - i) * 8); + + /* Print non-control ASCII characters as-is, dot otherwise */ + *p++ = isascii(c) && isprint(c) ? c : '.'; + } + +- *p++ = ' '; +- strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian"); +- p += strlen(p); ++ if (pix_fmt) { ++ *p++ = ' '; ++ strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian"); ++ p += strlen(p); ++ } + + *p++ = ' '; + *p++ = '('; +@@ -2348,6 +2371,7 @@ char *rust_fmt_argument(char *buf, char *end, void *ptr); + * read the documentation (path below) first. + * - 'NF' For a netdev_features_t + * - '4cc' V4L2 or DRM FourCC code, with endianness and raw numerical value. ++ * - '4c[hlbr]' Generic FourCC code. + * - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with + * a certain separator (' ' by default): + * C colon +diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl +index 880fde13d..f080e33a4 100755 +--- a/scripts/checkpatch.pl ++++ b/scripts/checkpatch.pl +@@ -6906,7 +6906,7 @@ sub process { + ($extension eq "f" && + defined $qualifier && $qualifier !~ /^w/) || + ($extension eq "4" && +- defined $qualifier && $qualifier !~ /^cc/)) { ++ defined $qualifier && $qualifier !~ /^c[chlbr]/)) { + $bad_specifier = $specifier; + last; + } +-- +2.42.0 + diff --git a/1013-USB-core-add-shutdown-callback-to-usb_driver.patch b/1013-USB-core-add-shutdown-callback-to-usb_driver.patch new file mode 100644 index 0000000..c95f4be --- /dev/null +++ b/1013-USB-core-add-shutdown-callback-to-usb_driver.patch @@ -0,0 +1,94 @@ +From f893444f7c842f97f3707897ba29f2c8dd77c8df Mon Sep 17 00:00:00 2001 +From: Kerem Karabay +Date: Mon, 7 Aug 2023 20:29:27 +0300 +Subject: [PATCH 10/12] USB: core: add 'shutdown' callback to usb_driver + +This simplifies running code on shutdown for USB drivers. + +Signed-off-by: Kerem Karabay +--- + drivers/usb/core/driver.c | 14 ++++++++++++++ + drivers/usb/storage/uas.c | 5 ++--- + include/linux/usb.h | 3 +++ + 3 files changed, 19 insertions(+), 3 deletions(-) + +diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c +index f58a0299f..dc0f86376 100644 +--- a/drivers/usb/core/driver.c ++++ b/drivers/usb/core/driver.c +@@ -514,6 +514,19 @@ static int usb_unbind_interface(struct device *dev) + return 0; + } + ++static void usb_shutdown_interface(struct device *dev) ++{ ++ struct usb_interface *intf = to_usb_interface(dev); ++ struct usb_driver *driver; ++ ++ if (!dev->driver) ++ return; ++ ++ driver = to_usb_driver(dev->driver); ++ if (driver->shutdown) ++ driver->shutdown(intf); ++} ++ + /** + * usb_driver_claim_interface - bind a driver to an interface + * @driver: the driver to be bound +@@ -1053,6 +1066,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, + new_driver->drvwrap.driver.bus = &usb_bus_type; + new_driver->drvwrap.driver.probe = usb_probe_interface; + new_driver->drvwrap.driver.remove = usb_unbind_interface; ++ new_driver->drvwrap.driver.shutdown = usb_shutdown_interface; + new_driver->drvwrap.driver.owner = owner; + new_driver->drvwrap.driver.mod_name = mod_name; + new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups; +diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c +index 2583ee981..591fa0379 100644 +--- a/drivers/usb/storage/uas.c ++++ b/drivers/usb/storage/uas.c +@@ -1221,9 +1221,8 @@ static void uas_disconnect(struct usb_interface *intf) + * hang on reboot when the device is still in uas mode. Note the reset is + * necessary as some devices won't revert to usb-storage mode without it. + */ +-static void uas_shutdown(struct device *dev) ++static void uas_shutdown(struct usb_interface *intf) + { +- struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *udev = interface_to_usbdev(intf); + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; +@@ -1246,7 +1245,7 @@ static struct usb_driver uas_driver = { + .suspend = uas_suspend, + .resume = uas_resume, + .reset_resume = uas_reset_resume, +- .drvwrap.driver.shutdown = uas_shutdown, ++ .shutdown = uas_shutdown, + .id_table = uas_usb_ids, + }; + +diff --git a/include/linux/usb.h b/include/linux/usb.h +index 25f8e62a3..5f3ae2186 100644 +--- a/include/linux/usb.h ++++ b/include/linux/usb.h +@@ -1194,6 +1194,7 @@ struct usbdrv_wrap { + * post_reset method is called. + * @post_reset: Called by usb_reset_device() after the device + * has been reset ++ * @shutdown: Called at shut-down time to quiesce the device. + * @id_table: USB drivers use ID table to support hotplugging. + * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set + * or your driver's probe function will never get called. +@@ -1245,6 +1246,8 @@ struct usb_driver { + int (*pre_reset)(struct usb_interface *intf); + int (*post_reset)(struct usb_interface *intf); + ++ void (*shutdown)(struct usb_interface *intf); ++ + const struct usb_device_id *id_table; + const struct attribute_group **dev_groups; + +-- +2.42.0 + diff --git a/1014-drm-format-helper-add-helper-for-BGR888-to-XRGB8888-.patch b/1014-drm-format-helper-add-helper-for-BGR888-to-XRGB8888-.patch new file mode 100644 index 0000000..26938e3 --- /dev/null +++ b/1014-drm-format-helper-add-helper-for-BGR888-to-XRGB8888-.patch @@ -0,0 +1,232 @@ +From 337d6f6e34daaa786a0fb70d0dbd553288cd5ecd Mon Sep 17 00:00:00 2001 +From: Kerem Karabay +Date: Fri, 4 Aug 2023 17:49:25 +0300 +Subject: [PATCH 11/12] drm/format-helper: add helper for BGR888 to XRGB8888 + conversion + +Add XRGB8888 emulation helper for devices that only support BGR888. + +Signed-off-by: Kerem Karabay +--- + drivers/gpu/drm/drm_format_helper.c | 53 ++++++++++++++ + .../gpu/drm/tests/drm_format_helper_test.c | 69 +++++++++++++++++++ + include/drm/drm_format_helper.h | 3 + + 3 files changed, 125 insertions(+) + +diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c +index f93a4efce..5508fbde1 100644 +--- a/drivers/gpu/drm/drm_format_helper.c ++++ b/drivers/gpu/drm/drm_format_helper.c +@@ -601,6 +601,56 @@ void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pi + } + EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888); + ++static void drm_fb_xrgb8888_to_bgr888_line(void *dbuf, const void *sbuf, unsigned int pixels) ++{ ++ u8 *dbuf8 = dbuf; ++ const __le32 *sbuf32 = sbuf; ++ unsigned int x; ++ u32 pix; ++ ++ for (x = 0; x < pixels; x++) { ++ pix = le32_to_cpu(sbuf32[x]); ++ /* write red-green-blue to output in little endianness */ ++ *dbuf8++ = (pix & 0x00FF0000) >> 16; ++ *dbuf8++ = (pix & 0x0000FF00) >> 8; ++ *dbuf8++ = (pix & 0x000000FF) >> 0; ++ } ++} ++ ++/** ++ * drm_fb_xrgb8888_to_bgr888 - Convert XRGB8888 to BGR888 clip buffer ++ * @dst: Array of BGR888 destination buffers ++ * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines ++ * within @dst; can be NULL if scanlines are stored next to each other. ++ * @src: Array of XRGB8888 source buffers ++ * @fb: DRM framebuffer ++ * @clip: Clip rectangle area to copy ++ * ++ * This function copies parts of a framebuffer to display memory and converts the ++ * color format during the process. Destination and framebuffer formats must match. The ++ * parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at ++ * least as many entries as there are planes in @fb's format. Each entry stores the ++ * value for the format's respective color plane at the same index. ++ * ++ * This function does not apply clipping on @dst (i.e. the destination is at the ++ * top-left corner). ++ * ++ * Drivers can use this function for BGR888 devices that don't natively ++ * support XRGB8888. ++ */ ++void drm_fb_xrgb8888_to_bgr888(struct iosys_map *dst, const unsigned int *dst_pitch, ++ const struct iosys_map *src, const struct drm_framebuffer *fb, ++ const struct drm_rect *clip) ++{ ++ static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { ++ 3, ++ }; ++ ++ drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, ++ drm_fb_xrgb8888_to_bgr888_line); ++} ++EXPORT_SYMBOL(drm_fb_xrgb8888_to_bgr888); ++ + static void drm_fb_xrgb8888_to_argb8888_line(void *dbuf, const void *sbuf, unsigned int pixels) + { + __le32 *dbuf32 = dbuf; +@@ -925,6 +975,9 @@ int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t d + } else if (dst_format == DRM_FORMAT_RGB888) { + drm_fb_xrgb8888_to_rgb888(dst, dst_pitch, src, fb, clip); + return 0; ++ } else if (dst_format == DRM_FORMAT_BGR888) { ++ drm_fb_xrgb8888_to_bgr888(dst, dst_pitch, src, fb, clip); ++ return 0; + } else if (dst_format == DRM_FORMAT_ARGB8888) { + drm_fb_xrgb8888_to_argb8888(dst, dst_pitch, src, fb, clip); + return 0; +diff --git a/drivers/gpu/drm/tests/drm_format_helper_test.c b/drivers/gpu/drm/tests/drm_format_helper_test.c +index 474bb7a1c..dff7fabd9 100644 +--- a/drivers/gpu/drm/tests/drm_format_helper_test.c ++++ b/drivers/gpu/drm/tests/drm_format_helper_test.c +@@ -52,6 +52,11 @@ struct convert_to_rgb888_result { + const u8 expected[TEST_BUF_SIZE]; + }; + ++struct convert_to_bgr888_result { ++ unsigned int dst_pitch; ++ const u8 expected[TEST_BUF_SIZE]; ++}; ++ + struct convert_to_argb8888_result { + unsigned int dst_pitch; + const u32 expected[TEST_BUF_SIZE]; +@@ -84,6 +89,7 @@ struct convert_xrgb8888_case { + struct convert_to_argb1555_result argb1555_result; + struct convert_to_rgba5551_result rgba5551_result; + struct convert_to_rgb888_result rgb888_result; ++ struct convert_to_bgr888_result bgr888_result; + struct convert_to_argb8888_result argb8888_result; + struct convert_to_xrgb2101010_result xrgb2101010_result; + struct convert_to_argb2101010_result argb2101010_result; +@@ -125,6 +131,10 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { + .dst_pitch = 0, + .expected = { 0x00, 0x00, 0xFF }, + }, ++ .bgr888_result = { ++ .dst_pitch = 0, ++ .expected = { 0xFF, 0x00, 0x00 }, ++ }, + .argb8888_result = { + .dst_pitch = 0, + .expected = { 0xFFFF0000 }, +@@ -179,6 +189,10 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { + .dst_pitch = 0, + .expected = { 0x00, 0x00, 0xFF }, + }, ++ .bgr888_result = { ++ .dst_pitch = 0, ++ .expected = { 0xFF, 0x00, 0x00 }, ++ }, + .argb8888_result = { + .dst_pitch = 0, + .expected = { 0xFFFF0000 }, +@@ -280,6 +294,15 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + }, + }, ++ .bgr888_result = { ++ .dst_pitch = 0, ++ .expected = { ++ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, ++ 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, ++ 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, ++ 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, ++ }, ++ }, + .argb8888_result = { + .dst_pitch = 0, + .expected = { +@@ -391,6 +414,17 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, ++ .bgr888_result = { ++ .dst_pitch = 15, ++ .expected = { ++ 0x0E, 0x44, 0x9C, 0x11, 0x4D, 0x05, 0xA8, 0xF3, 0x03, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x6C, 0xF0, 0x73, 0x0E, 0x44, 0x9C, 0x11, 0x4D, 0x05, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0xA8, 0x03, 0x03, 0x6C, 0xF0, 0x73, 0x0E, 0x44, 0x9C, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ }, ++ }, + .argb8888_result = { + .dst_pitch = 20, + .expected = { +@@ -727,6 +761,40 @@ static void drm_test_fb_xrgb8888_to_rgb888(struct kunit *test) + KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); + } + ++static void drm_test_fb_xrgb8888_to_bgr888(struct kunit *test) ++{ ++ const struct convert_xrgb8888_case *params = test->param_value; ++ const struct convert_to_bgr888_result *result = ¶ms->bgr888_result; ++ size_t dst_size; ++ u8 *buf = NULL; ++ __le32 *xrgb8888 = NULL; ++ struct iosys_map dst, src; ++ ++ struct drm_framebuffer fb = { ++ .format = drm_format_info(DRM_FORMAT_XRGB8888), ++ .pitches = { params->pitch, 0, 0 }, ++ }; ++ ++ dst_size = conversion_buf_size(DRM_FORMAT_BGR888, result->dst_pitch, ++ ¶ms->clip); ++ KUNIT_ASSERT_GT(test, dst_size, 0); ++ ++ buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); ++ iosys_map_set_vaddr(&dst, buf); ++ ++ xrgb8888 = cpubuf_to_le32(test, params->xrgb8888, TEST_BUF_SIZE); ++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); ++ iosys_map_set_vaddr(&src, xrgb8888); ++ ++ /* ++ * BGR888 expected results are already in little-endian ++ * order, so there's no need to convert the test output. ++ */ ++ drm_fb_xrgb8888_to_bgr888(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip); ++ KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); ++} ++ + static void drm_test_fb_xrgb8888_to_argb8888(struct kunit *test) + { + const struct convert_xrgb8888_case *params = test->param_value; +@@ -858,6 +926,7 @@ static struct kunit_case drm_format_helper_test_cases[] = { + KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb1555, convert_xrgb8888_gen_params), + KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgba5551, convert_xrgb8888_gen_params), + KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgb888, convert_xrgb8888_gen_params), ++ KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_bgr888, convert_xrgb8888_gen_params), + KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb8888, convert_xrgb8888_gen_params), + KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_xrgb2101010, convert_xrgb8888_gen_params), + KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb2101010, convert_xrgb8888_gen_params), +diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h +index 291deb094..7fc553318 100644 +--- a/include/drm/drm_format_helper.h ++++ b/include/drm/drm_format_helper.h +@@ -42,6 +42,9 @@ void drm_fb_xrgb8888_to_rgba5551(struct iosys_map *dst, const unsigned int *dst_ + void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip); ++void drm_fb_xrgb8888_to_bgr888(struct iosys_map *dst, const unsigned int *dst_pitch, ++ const struct iosys_map *src, const struct drm_framebuffer *fb, ++ const struct drm_rect *clip); + void drm_fb_xrgb8888_to_argb8888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip); +-- +2.42.0 + diff --git a/1015-drm-tiny-add-driver-for-Apple-Touch-Bars-in-x86-Macs.patch b/1015-drm-tiny-add-driver-for-Apple-Touch-Bars-in-x86-Macs.patch new file mode 100644 index 0000000..df437b7 --- /dev/null +++ b/1015-drm-tiny-add-driver-for-Apple-Touch-Bars-in-x86-Macs.patch @@ -0,0 +1,710 @@ +From 1f0b6c21c4d56f5be74c4d7d0665525862e307c3 Mon Sep 17 00:00:00 2001 +From: Kerem Karabay +Date: Sat, 6 May 2023 17:30:09 +0300 +Subject: [PATCH 12/12] drm/tiny: add driver for Apple Touch Bars in x86 Macs + +The Touch Bars found on x86 Macs support two USB configurations: one +where the device presents itself as a HID keyboard and can display +predefined sets of keys, and one where the operating system has full +control over what is displayed. This commit adds support for the display +functionality of the second configuration. + +Note that this driver has only been tested on T2 Macs, and only includes +the USB device ID for these devices. Testing on T1 Macs would be +appreciated. + +Credit goes to @imbushuo on GitHub for reverse engineering most of the +protocol. + +Signed-off-by: Kerem Karabay +--- + MAINTAINERS | 6 + + drivers/gpu/drm/tiny/Kconfig | 12 + + drivers/gpu/drm/tiny/Makefile | 1 + + drivers/gpu/drm/tiny/appletbdrm.c | 624 ++++++++++++++++++++++++++++++ + 4 files changed, 643 insertions(+) + create mode 100644 drivers/gpu/drm/tiny/appletbdrm.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index 519b3b736..dfc63d257 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -6372,6 +6372,12 @@ S: Supported + T: git git://anongit.freedesktop.org/drm/drm-misc + F: drivers/gpu/drm/sun4i/sun8i* + ++DRM DRIVER FOR APPLE TOUCH BARS ++M: Kerem Karabay ++L: dri-devel@lists.freedesktop.org ++S: Maintained ++F: drivers/gpu/drm/tiny/appletbdrm.c ++ + DRM DRIVER FOR ARM PL111 CLCD + M: Emma Anholt + S: Supported +diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig +index f6889f649..559a97bce 100644 +--- a/drivers/gpu/drm/tiny/Kconfig ++++ b/drivers/gpu/drm/tiny/Kconfig +@@ -1,5 +1,17 @@ + # SPDX-License-Identifier: GPL-2.0-only + ++config DRM_APPLETBDRM ++ tristate "DRM support for Apple Touch Bars" ++ depends on DRM && USB && MMU ++ select DRM_KMS_HELPER ++ select DRM_GEM_SHMEM_HELPER ++ help ++ Say Y here if you want support for the display of Touch Bars on x86 ++ MacBook Pros. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called appletbdrm. ++ + config DRM_ARCPGU + tristate "ARC PGU" + depends on DRM && OF +diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile +index 76dde89a0..9a1b412e7 100644 +--- a/drivers/gpu/drm/tiny/Makefile ++++ b/drivers/gpu/drm/tiny/Makefile +@@ -1,5 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0-only + ++obj-$(CONFIG_DRM_APPLETBDRM) += appletbdrm.o + obj-$(CONFIG_DRM_ARCPGU) += arcpgu.o + obj-$(CONFIG_DRM_BOCHS) += bochs.o + obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o +diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c +new file mode 100644 +index 000000000..33a99436b +--- /dev/null ++++ b/drivers/gpu/drm/tiny/appletbdrm.c +@@ -0,0 +1,624 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Apple Touch Bar DRM Driver ++ * ++ * Copyright (c) 2023 Kerem Karabay ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define _APPLETBDRM_FOURCC(s) (((s)[0] << 24) | ((s)[1] << 16) | ((s)[2] << 8) | (s)[3]) ++#define APPLETBDRM_FOURCC(s) _APPLETBDRM_FOURCC(#s) ++ ++#define APPLETBDRM_PIXEL_FORMAT APPLETBDRM_FOURCC(RGBA) /* The actual format is BGR888 */ ++#define APPLETBDRM_BITS_PER_PIXEL 24 ++ ++#define APPLETBDRM_MSG_CLEAR_DISPLAY APPLETBDRM_FOURCC(CLRD) ++#define APPLETBDRM_MSG_GET_INFORMATION APPLETBDRM_FOURCC(GINF) ++#define APPLETBDRM_MSG_UPDATE_COMPLETE APPLETBDRM_FOURCC(UDCL) ++#define APPLETBDRM_MSG_SIGNAL_READINESS APPLETBDRM_FOURCC(REDY) ++ ++#define APPLETBDRM_BULK_MSG_TIMEOUT 1000 ++ ++#define drm_to_adev(_drm) container_of(_drm, struct appletbdrm_device, drm) ++#define adev_to_udev(adev) interface_to_usbdev(to_usb_interface(adev->dev)) ++ ++struct appletbdrm_device { ++ struct device *dev; ++ ++ u8 in_ep; ++ u8 out_ep; ++ ++ u32 width; ++ u32 height; ++ ++ struct drm_device drm; ++ struct drm_display_mode mode; ++ struct drm_connector connector; ++ struct drm_simple_display_pipe pipe; ++ ++ bool readiness_signal_received; ++}; ++ ++struct appletbdrm_request_header { ++ __le16 unk_00; ++ __le16 unk_02; ++ __le32 unk_04; ++ __le32 unk_08; ++ __le32 size; ++} __packed; ++ ++struct appletbdrm_response_header { ++ u8 unk_00[16]; ++ u32 msg; ++} __packed; ++ ++struct appletbdrm_simple_request { ++ struct appletbdrm_request_header header; ++ u32 msg; ++ u8 unk_14[8]; ++ __le32 size; ++} __packed; ++ ++struct appletbdrm_information { ++ struct appletbdrm_response_header header; ++ u8 unk_14[12]; ++ __le32 width; ++ __le32 height; ++ u8 bits_per_pixel; ++ __le32 bytes_per_row; ++ __le32 orientation; ++ __le32 bitmap_info; ++ u32 pixel_format; ++ __le32 width_inches; /* floating point */ ++ __le32 height_inches; /* floating point */ ++} __packed; ++ ++struct appletbdrm_frame { ++ __le16 begin_x; ++ __le16 begin_y; ++ __le16 width; ++ __le16 height; ++ __le32 buf_size; ++ u8 buf[]; ++} __packed; ++ ++struct appletbdrm_fb_request_footer { ++ u8 unk_00[12]; ++ __le32 unk_0c; ++ u8 unk_10[12]; ++ __le32 unk_1c; ++ __le64 timestamp; ++ u8 unk_28[12]; ++ __le32 unk_34; ++ u8 unk_38[20]; ++ __le32 unk_4c; ++} __packed; ++ ++struct appletbdrm_fb_request { ++ struct appletbdrm_request_header header; ++ __le16 unk_10; ++ u8 msg_id; ++ u8 unk_13[29]; ++ /* ++ * Contents of `data`: ++ * - struct appletbdrm_frame frames[]; ++ * - struct appletbdrm_fb_request_footer footer; ++ * - padding to make the total size a multiple of 16 ++ */ ++ u8 data[]; ++} __packed; ++ ++struct appletbdrm_fb_request_response { ++ struct appletbdrm_response_header header; ++ u8 unk_14[12]; ++ __le64 timestamp; ++} __packed; ++ ++static int appletbdrm_send_request(struct appletbdrm_device *adev, ++ struct appletbdrm_request_header *request, size_t size) ++{ ++ struct usb_device *udev = adev_to_udev(adev); ++ struct drm_device *drm = &adev->drm; ++ int ret, actual_size; ++ ++ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, adev->out_ep), ++ request, size, &actual_size, APPLETBDRM_BULK_MSG_TIMEOUT); ++ if (ret) { ++ drm_err(drm, "Failed to send message (%pe)\n", ERR_PTR(ret)); ++ return ret; ++ } ++ ++ if (actual_size != size) { ++ drm_err(drm, "Actual size (%d) doesn't match expected size (%lu)\n", ++ actual_size, size); ++ return -EIO; ++ } ++ ++ return ret; ++} ++ ++static int appletbdrm_read_response(struct appletbdrm_device *adev, ++ struct appletbdrm_response_header *response, ++ size_t size, u32 expected_response) ++{ ++ struct usb_device *udev = adev_to_udev(adev); ++ struct drm_device *drm = &adev->drm; ++ int ret, actual_size; ++ ++retry: ++ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, adev->in_ep), ++ response, size, &actual_size, APPLETBDRM_BULK_MSG_TIMEOUT); ++ if (ret) { ++ drm_err(drm, "Failed to read response (%pe)\n", ERR_PTR(ret)); ++ return ret; ++ } ++ ++ /* ++ * The device responds to the first request sent in a particular ++ * timeframe after the USB device configuration is set with a readiness ++ * signal, in which case the response should be read again ++ */ ++ if (response->msg == APPLETBDRM_MSG_SIGNAL_READINESS) { ++ if (!adev->readiness_signal_received) { ++ adev->readiness_signal_received = true; ++ goto retry; ++ } ++ ++ drm_err(drm, "Encountered unexpected readiness signal\n"); ++ return -EIO; ++ } ++ ++ if (actual_size != size) { ++ drm_err(drm, "Actual size (%d) doesn't match expected size (%lu)\n", ++ actual_size, size); ++ return -EIO; ++ } ++ ++ if (response->msg != expected_response) { ++ drm_err(drm, "Unexpected response from device (expected %p4ch found %p4ch)\n", ++ &expected_response, &response->msg); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int appletbdrm_send_msg(struct appletbdrm_device *adev, u32 msg) ++{ ++ struct appletbdrm_simple_request *request; ++ int ret; ++ ++ request = kzalloc(sizeof(*request), GFP_KERNEL); ++ if (!request) ++ return -ENOMEM; ++ ++ request->header.unk_00 = cpu_to_le16(2); ++ request->header.unk_02 = cpu_to_le16(0x1512); ++ request->header.size = cpu_to_le32(sizeof(*request) - sizeof(request->header)); ++ request->msg = msg; ++ request->size = request->header.size; ++ ++ ret = appletbdrm_send_request(adev, &request->header, sizeof(*request)); ++ ++ kfree(request); ++ ++ return ret; ++} ++ ++static int appletbdrm_clear_display(struct appletbdrm_device *adev) ++{ ++ return appletbdrm_send_msg(adev, APPLETBDRM_MSG_CLEAR_DISPLAY); ++} ++ ++static int appletbdrm_signal_readiness(struct appletbdrm_device *adev) ++{ ++ return appletbdrm_send_msg(adev, APPLETBDRM_MSG_SIGNAL_READINESS); ++} ++ ++static int appletbdrm_get_information(struct appletbdrm_device *adev) ++{ ++ struct appletbdrm_information *info; ++ struct drm_device *drm = &adev->drm; ++ u8 bits_per_pixel; ++ u32 pixel_format; ++ int ret; ++ ++ info = kzalloc(sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ ret = appletbdrm_send_msg(adev, APPLETBDRM_MSG_GET_INFORMATION); ++ if (ret) ++ return ret; ++ ++ ret = appletbdrm_read_response(adev, &info->header, sizeof(*info), ++ APPLETBDRM_MSG_GET_INFORMATION); ++ if (ret) ++ goto free_info; ++ ++ bits_per_pixel = info->bits_per_pixel; ++ pixel_format = get_unaligned(&info->pixel_format); ++ ++ adev->width = get_unaligned_le32(&info->width); ++ adev->height = get_unaligned_le32(&info->height); ++ ++ if (bits_per_pixel != APPLETBDRM_BITS_PER_PIXEL) { ++ drm_err(drm, "Encountered unexpected bits per pixel value (%d)\n", bits_per_pixel); ++ ret = -EINVAL; ++ goto free_info; ++ } ++ ++ if (pixel_format != APPLETBDRM_PIXEL_FORMAT) { ++ drm_err(drm, "Encountered unknown pixel format (%p4ch)\n", &pixel_format); ++ ret = -EINVAL; ++ goto free_info; ++ } ++ ++free_info: ++ kfree(info); ++ ++ return ret; ++} ++ ++static u32 rect_size(struct drm_rect *rect) ++{ ++ return drm_rect_width(rect) * drm_rect_height(rect) * (APPLETBDRM_BITS_PER_PIXEL / 8); ++} ++ ++static int appletbdrm_flush_damage(struct appletbdrm_device *adev, ++ struct drm_plane_state *old_state, ++ struct drm_plane_state *state) ++{ ++ struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); ++ struct appletbdrm_fb_request_response *response; ++ struct appletbdrm_fb_request_footer *footer; ++ struct drm_atomic_helper_damage_iter iter; ++ struct drm_framebuffer *fb = state->fb; ++ struct appletbdrm_fb_request *request; ++ struct drm_device *drm = &adev->drm; ++ struct appletbdrm_frame *frame; ++ u64 timestamp = ktime_get_ns(); ++ struct drm_rect damage; ++ size_t frames_size = 0; ++ size_t request_size; ++ int ret; ++ ++ drm_atomic_helper_damage_iter_init(&iter, old_state, state); ++ drm_atomic_for_each_plane_damage(&iter, &damage) { ++ frames_size += struct_size(frame, buf, rect_size(&damage)); ++ } ++ ++ if (!frames_size) ++ return 0; ++ ++ request_size = ALIGN(sizeof(*request) + frames_size + sizeof(*footer), 16); ++ ++ request = kzalloc(request_size, GFP_KERNEL); ++ if (!request) ++ return -ENOMEM; ++ ++ response = kzalloc(sizeof(*response), GFP_KERNEL); ++ if (!response) { ++ ret = -ENOMEM; ++ goto free_request; ++ } ++ ++ ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); ++ if (ret) { ++ drm_err(drm, "Failed to start CPU framebuffer access (%pe)\n", ERR_PTR(ret)); ++ goto free_response; ++ } ++ ++ request->header.unk_00 = cpu_to_le16(2); ++ request->header.unk_02 = cpu_to_le16(0x12); ++ request->header.unk_04 = cpu_to_le32(9); ++ request->header.size = cpu_to_le32(request_size - sizeof(request->header)); ++ request->unk_10 = cpu_to_le16(1); ++ request->msg_id = timestamp & 0xff; ++ ++ frame = (struct appletbdrm_frame *)request->data; ++ ++ drm_atomic_helper_damage_iter_init(&iter, old_state, state); ++ drm_atomic_for_each_plane_damage(&iter, &damage) { ++ struct iosys_map dst = IOSYS_MAP_INIT_VADDR(frame->buf); ++ u32 buf_size = rect_size(&damage); ++ ++ /* ++ * The coordinates need to be translated to the coordinate ++ * system the device expects, see the comment in ++ * appletbdrm_setup_mode_config ++ */ ++ frame->begin_x = cpu_to_le16(damage.y1); ++ frame->begin_y = cpu_to_le16(adev->height - damage.x2); ++ frame->width = cpu_to_le16(drm_rect_height(&damage)); ++ frame->height = cpu_to_le16(drm_rect_width(&damage)); ++ frame->buf_size = cpu_to_le32(buf_size); ++ ++ ret = drm_fb_blit(&dst, NULL, DRM_FORMAT_BGR888, ++ &shadow_plane_state->data[0], fb, &damage); ++ if (ret) { ++ drm_err(drm, "Failed to copy damage clip (%pe)\n", ERR_PTR(ret)); ++ goto end_fb_cpu_access; ++ } ++ ++ frame = (void *)frame + struct_size(frame, buf, buf_size); ++ } ++ ++ footer = (struct appletbdrm_fb_request_footer *)&request->data[frames_size]; ++ ++ footer->unk_0c = cpu_to_le32(0xfffe); ++ footer->unk_1c = cpu_to_le32(0x80001); ++ footer->unk_34 = cpu_to_le32(0x80002); ++ footer->unk_4c = cpu_to_le32(0xffff); ++ footer->timestamp = cpu_to_le64(timestamp); ++ ++ ret = appletbdrm_send_request(adev, &request->header, request_size); ++ if (ret) ++ goto end_fb_cpu_access; ++ ++ ret = appletbdrm_read_response(adev, &response->header, sizeof(*response), ++ APPLETBDRM_MSG_UPDATE_COMPLETE); ++ if (ret) ++ goto end_fb_cpu_access; ++ ++ if (response->timestamp != footer->timestamp) { ++ drm_err(drm, "Response timestamp (%llu) doesn't match request timestamp (%llu)\n", ++ le64_to_cpu(response->timestamp), timestamp); ++ goto end_fb_cpu_access; ++ } ++ ++end_fb_cpu_access: ++ drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); ++free_response: ++ kfree(response); ++free_request: ++ kfree(request); ++ ++ return ret; ++} ++ ++static int appletbdrm_connector_helper_get_modes(struct drm_connector *connector) ++{ ++ struct appletbdrm_device *adev = drm_to_adev(connector->dev); ++ ++ return drm_connector_helper_get_modes_fixed(connector, &adev->mode); ++} ++ ++static enum drm_mode_status appletbdrm_pipe_mode_valid(struct drm_simple_display_pipe *pipe, ++ const struct drm_display_mode *mode) ++{ ++ struct drm_crtc *crtc = &pipe->crtc; ++ struct appletbdrm_device *adev = drm_to_adev(crtc->dev); ++ ++ return drm_crtc_helper_mode_valid_fixed(crtc, mode, &adev->mode); ++} ++ ++static void appletbdrm_pipe_disable(struct drm_simple_display_pipe *pipe) ++{ ++ struct appletbdrm_device *adev = drm_to_adev(pipe->crtc.dev); ++ int idx; ++ ++ if (!drm_dev_enter(&adev->drm, &idx)) ++ return; ++ ++ appletbdrm_clear_display(adev); ++ ++ drm_dev_exit(idx); ++} ++ ++static void appletbdrm_pipe_update(struct drm_simple_display_pipe *pipe, ++ struct drm_plane_state *old_state) ++{ ++ struct drm_crtc *crtc = &pipe->crtc; ++ struct appletbdrm_device *adev = drm_to_adev(crtc->dev); ++ int idx; ++ ++ if (!crtc->state->active || !drm_dev_enter(&adev->drm, &idx)) ++ return; ++ ++ appletbdrm_flush_damage(adev, old_state, pipe->plane.state); ++ ++ drm_dev_exit(idx); ++} ++ ++static const u32 appletbdrm_formats[] = { ++ DRM_FORMAT_BGR888, ++ DRM_FORMAT_XRGB8888, /* emulated */ ++}; ++ ++static const struct drm_mode_config_funcs appletbdrm_mode_config_funcs = { ++ .fb_create = drm_gem_fb_create_with_dirty, ++ .atomic_check = drm_atomic_helper_check, ++ .atomic_commit = drm_atomic_helper_commit, ++}; ++ ++static const struct drm_connector_funcs appletbdrm_connector_funcs = { ++ .reset = drm_atomic_helper_connector_reset, ++ .destroy = drm_connector_cleanup, ++ .fill_modes = drm_helper_probe_single_connector_modes, ++ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, ++ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, ++}; ++ ++static const struct drm_connector_helper_funcs appletbdrm_connector_helper_funcs = { ++ .get_modes = appletbdrm_connector_helper_get_modes, ++}; ++ ++static const struct drm_simple_display_pipe_funcs appletbdrm_pipe_funcs = { ++ DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS, ++ .update = appletbdrm_pipe_update, ++ .disable = appletbdrm_pipe_disable, ++ .mode_valid = appletbdrm_pipe_mode_valid, ++}; ++ ++DEFINE_DRM_GEM_FOPS(appletbdrm_drm_fops); ++ ++static const struct drm_driver appletbdrm_drm_driver = { ++ DRM_GEM_SHMEM_DRIVER_OPS, ++ .name = "appletbdrm", ++ .desc = "Apple Touch Bar DRM Driver", ++ .date = "20230910", ++ .major = 1, ++ .minor = 0, ++ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, ++ .fops = &appletbdrm_drm_fops, ++}; ++ ++static int appletbdrm_setup_mode_config(struct appletbdrm_device *adev) ++{ ++ struct drm_connector *connector = &adev->connector; ++ struct drm_device *drm = &adev->drm; ++ struct device *dev = adev->dev; ++ int ret; ++ ++ ret = drmm_mode_config_init(drm); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to initialize mode configuration\n"); ++ ++ /* ++ * The coordinate system used by the device is different from the ++ * coordinate system of the framebuffer in that the x and y axes are ++ * swapped, and that the y axis is inverted; so what the device reports ++ * as the height is actually the width of the framebuffer and vice ++ * versa ++ */ ++ drm->mode_config.min_width = 0; ++ drm->mode_config.min_height = 0; ++ drm->mode_config.max_width = max(adev->height, DRM_SHADOW_PLANE_MAX_WIDTH); ++ drm->mode_config.max_height = max(adev->width, DRM_SHADOW_PLANE_MAX_HEIGHT); ++ drm->mode_config.preferred_depth = APPLETBDRM_BITS_PER_PIXEL; ++ drm->mode_config.funcs = &appletbdrm_mode_config_funcs; ++ ++ adev->mode = (struct drm_display_mode) { ++ DRM_MODE_INIT(60, adev->height, adev->width, ++ DRM_MODE_RES_MM(adev->height, 218), ++ DRM_MODE_RES_MM(adev->width, 218)) ++ }; ++ ++ ret = drm_connector_init(drm, connector, ++ &appletbdrm_connector_funcs, DRM_MODE_CONNECTOR_USB); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to initialize connector\n"); ++ ++ drm_connector_helper_add(connector, &appletbdrm_connector_helper_funcs); ++ ++ ret = drm_connector_set_panel_orientation(connector, ++ DRM_MODE_PANEL_ORIENTATION_RIGHT_UP); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to set panel orientation\n"); ++ ++ connector->display_info.non_desktop = true; ++ ret = drm_object_property_set_value(&connector->base, ++ drm->mode_config.non_desktop_property, true); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to set non-desktop property\n"); ++ ++ ret = drm_simple_display_pipe_init(drm, &adev->pipe, &appletbdrm_pipe_funcs, ++ appletbdrm_formats, ARRAY_SIZE(appletbdrm_formats), ++ NULL, &adev->connector); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to initialize simple display pipe\n"); ++ ++ drm_plane_enable_fb_damage_clips(&adev->pipe.plane); ++ ++ drm_mode_config_reset(drm); ++ ++ ret = drm_dev_register(drm, 0); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to register DRM device\n"); ++ ++ return 0; ++} ++ ++static int appletbdrm_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++ struct usb_endpoint_descriptor *bulk_in, *bulk_out; ++ struct device *dev = &intf->dev; ++ struct appletbdrm_device *adev; ++ int ret; ++ ++ ret = usb_find_common_endpoints(intf->cur_altsetting, &bulk_in, &bulk_out, NULL, NULL); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to find bulk endpoints\n"); ++ ++ adev = devm_drm_dev_alloc(dev, &appletbdrm_drm_driver, struct appletbdrm_device, drm); ++ if (IS_ERR(adev)) ++ return PTR_ERR(adev); ++ ++ adev->dev = dev; ++ adev->in_ep = bulk_in->bEndpointAddress; ++ adev->out_ep = bulk_out->bEndpointAddress; ++ ++ usb_set_intfdata(intf, adev); ++ ++ ret = appletbdrm_get_information(adev); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to get display information\n"); ++ ++ ret = appletbdrm_signal_readiness(adev); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to signal readiness\n"); ++ ++ ret = appletbdrm_clear_display(adev); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to clear display\n"); ++ ++ return appletbdrm_setup_mode_config(adev); ++} ++ ++static void appletbdrm_disconnect(struct usb_interface *intf) ++{ ++ struct appletbdrm_device *adev = usb_get_intfdata(intf); ++ struct drm_device *drm = &adev->drm; ++ ++ drm_dev_unplug(drm); ++ drm_atomic_helper_shutdown(drm); ++} ++ ++static void appletbdrm_shutdown(struct usb_interface *intf) ++{ ++ struct appletbdrm_device *adev = usb_get_intfdata(intf); ++ ++ /* ++ * The framebuffer needs to be cleared on shutdown since its content ++ * persists across boots ++ */ ++ drm_atomic_helper_shutdown(&adev->drm); ++} ++ ++static const struct usb_device_id appletbdrm_usb_id_table[] = { ++ { USB_DEVICE_INTERFACE_CLASS(0x05ac, 0x8302, USB_CLASS_AUDIO_VIDEO) }, ++ {} ++}; ++MODULE_DEVICE_TABLE(usb, appletbdrm_usb_id_table); ++ ++static struct usb_driver appletbdrm_usb_driver = { ++ .name = "appletbdrm", ++ .probe = appletbdrm_probe, ++ .disconnect = appletbdrm_disconnect, ++ .shutdown = appletbdrm_shutdown, ++ .id_table = appletbdrm_usb_id_table, ++}; ++module_usb_driver(appletbdrm_usb_driver); ++ ++MODULE_AUTHOR("Kerem Karabay "); ++MODULE_DESCRIPTION("Apple Touch Bar DRM Driver"); ++MODULE_LICENSE("GPL"); +-- +2.42.0 + diff --git a/1005-Documentation-leds-standardise-keyboard-backlight-le.patch b/1016-Documentation-leds-standardise-keyboard-backlight-le.patch similarity index 100% rename from 1005-Documentation-leds-standardise-keyboard-backlight-le.patch rename to 1016-Documentation-leds-standardise-keyboard-backlight-le.patch diff --git a/1006-HID-hid-apple-magic-backlight-Add-driver-for-keyboar.patch b/1017-HID-hid-apple-magic-backlight-Add-driver-for-keyboar.patch similarity index 89% rename from 1006-HID-hid-apple-magic-backlight-Add-driver-for-keyboar.patch rename to 1017-HID-hid-apple-magic-backlight-Add-driver-for-keyboar.patch index 69b6ca1..e4a687b 100644 --- a/1006-HID-hid-apple-magic-backlight-Add-driver-for-keyboar.patch +++ b/1017-HID-hid-apple-magic-backlight-Add-driver-for-keyboar.patch @@ -24,12 +24,12 @@ Reviewed-by: Thomas Weißschuh create mode 100644 drivers/hid/hid-apple-magic-backlight.c diff --git a/MAINTAINERS b/MAINTAINERS -index 6a47510d1592..b93c68158be3 100644 +index dfc63d257..9148bda0a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -9201,6 +9201,12 @@ F: include/linux/pm.h - F: include/linux/suspend.h - F: kernel/power/ +@@ -9169,6 +9169,12 @@ L: linux-input@vger.kernel.org + S: Maintained + F: drivers/hid/hid-appletb-* +HID APPLE MAGIC BACKLIGHT DRIVER +M: Orlando Chamberlain @@ -41,12 +41,12 @@ index 6a47510d1592..b93c68158be3 100644 M: Jiri Kosina M: Benjamin Tissoires diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig -index e2a5d30c8895..fe489632bfd9 100644 +index 4e238df87..83fbab6d4 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig -@@ -130,6 +130,19 @@ config HID_APPLE - Say Y here if you want support for keyboards of Apple iBooks, PowerBooks, - MacBooks, MacBook Pros and Apple Aluminum. +@@ -169,6 +169,19 @@ config HID_APPLETB_KBD + To compile this driver as a module, choose M here: the + module will be called hid-appletb-kbd. +config HID_APPLE_MAGIC_BACKLIGHT + tristate "Apple Magic Keyboard Backlight" @@ -61,24 +61,24 @@ index e2a5d30c8895..fe489632bfd9 100644 + To compile this driver as a module, choose M here: the + module will be called hid-apple-magic-backlight. + - config HID_APPLEIR - tristate "Apple infrared receiver" - depends on (USB_HID) + config HID_ASUS + tristate "Asus" + depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile -index e8014c1a2f8b..dc8df002bc86 100644 +index 5b60015fd..581f5e720 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile -@@ -26,6 +26,7 @@ obj-$(CONFIG_HID_ACCUTOUCH) += hid-accutouch.o - obj-$(CONFIG_HID_ALPS) += hid-alps.o - obj-$(CONFIG_HID_ACRUX) += hid-axff.o - obj-$(CONFIG_HID_APPLE) += hid-apple.o -+obj-$(CONFIG_HID_APPLE_MAGIC_BACKLIGHT) += hid-apple-magic-backlight.o +@@ -31,6 +31,7 @@ obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o + obj-$(CONFIG_HID_APPLETB_BL) += hid-appletb-bl.o + obj-$(CONFIG_HID_APPLETB_KBD) += hid-appletb-kbd.o ++obj-$(CONFIG_HID_APPLE_MAGIC_BACKLIGHT) += hid-apple-magic-backlight.o obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o obj-$(CONFIG_HID_ASUS) += hid-asus.o + obj-$(CONFIG_HID_AUREAL) += hid-aureal.o diff --git a/drivers/hid/hid-apple-magic-backlight.c b/drivers/hid/hid-apple-magic-backlight.c new file mode 100644 -index 000000000000..f0fc02ff3b2d +index 000000000..f0fc02ff3 --- /dev/null +++ b/drivers/hid/hid-apple-magic-backlight.c @@ -0,0 +1,120 @@ @@ -203,5 +203,5 @@ index 000000000000..f0fc02ff3b2d +MODULE_AUTHOR("Orlando Chamberlain "); +MODULE_LICENSE("GPL"); -- -2.39.1 +2.39.2