|
8 | 8 | * Copyright (c) 2006-2007 Jiri Kosina
|
9 | 9 | * Copyright (c) 2008 Jiri Slaby <[email protected]>
|
10 | 10 | * Copyright (c) 2019 Paul Pawlowski <[email protected]>
|
| 11 | + * Copyright (c) 2023 Orlando Chamberlain <[email protected]> |
| 12 | + * Copyright (c) 2024 Aditya Garg <[email protected]> |
11 | 13 | */
|
12 | 14 |
|
13 | 15 | /*
|
|
23 | 25 | #include <linux/timer.h>
|
24 | 26 | #include <linux/string.h>
|
25 | 27 | #include <linux/leds.h>
|
| 28 | +#include <dt-bindings/leds/common.h> |
26 | 29 |
|
27 | 30 | #include "hid-ids.h"
|
28 | 31 |
|
|
38 | 41 | #define APPLE_RDESC_BATTERY BIT(9)
|
39 | 42 | #define APPLE_BACKLIGHT_CTL BIT(10)
|
40 | 43 | #define APPLE_IS_NON_APPLE BIT(11)
|
| 44 | +#define APPLE_MAGIC_BACKLIGHT BIT(12) |
41 | 45 |
|
42 | 46 | #define APPLE_FLAG_FKEY 0x01
|
43 | 47 |
|
44 | 48 | #define HID_COUNTRY_INTERNATIONAL_ISO 13
|
45 | 49 | #define APPLE_BATTERY_TIMEOUT_MS 60000
|
46 | 50 |
|
| 51 | +#define HID_USAGE_MAGIC_BL 0xff00000f |
| 52 | +#define APPLE_MAGIC_REPORT_ID_POWER 3 |
| 53 | +#define APPLE_MAGIC_REPORT_ID_BRIGHTNESS 1 |
| 54 | + |
47 | 55 | static unsigned int fnmode = 3;
|
48 | 56 | module_param(fnmode, uint, 0644);
|
49 | 57 | MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, "
|
@@ -81,6 +89,12 @@ struct apple_sc_backlight {
|
81 | 89 | struct hid_device *hdev;
|
82 | 90 | };
|
83 | 91 |
|
| 92 | +struct apple_magic_backlight { |
| 93 | + struct led_classdev cdev; |
| 94 | + struct hid_report *brightness; |
| 95 | + struct hid_report *power; |
| 96 | +}; |
| 97 | + |
84 | 98 | struct apple_sc {
|
85 | 99 | struct hid_device *hdev;
|
86 | 100 | unsigned long quirks;
|
@@ -822,6 +836,66 @@ static int apple_backlight_init(struct hid_device *hdev)
|
822 | 836 | return ret;
|
823 | 837 | }
|
824 | 838 |
|
| 839 | +static void apple_magic_backlight_report_set(struct hid_report *rep, s32 value, u8 rate) |
| 840 | +{ |
| 841 | + rep->field[0]->value[0] = value; |
| 842 | + rep->field[1]->value[0] = 0x5e; /* Mimic Windows */ |
| 843 | + rep->field[1]->value[0] |= rate << 8; |
| 844 | + |
| 845 | + hid_hw_request(rep->device, rep, HID_REQ_SET_REPORT); |
| 846 | +} |
| 847 | + |
| 848 | +static void apple_magic_backlight_set(struct apple_magic_backlight *backlight, |
| 849 | + int brightness, char rate) |
| 850 | +{ |
| 851 | + apple_magic_backlight_report_set(backlight->power, brightness ? 1 : 0, rate); |
| 852 | + if (brightness) |
| 853 | + apple_magic_backlight_report_set(backlight->brightness, brightness, rate); |
| 854 | +} |
| 855 | + |
| 856 | +static int apple_magic_backlight_led_set(struct led_classdev *led_cdev, |
| 857 | + enum led_brightness brightness) |
| 858 | +{ |
| 859 | + struct apple_magic_backlight *backlight = container_of(led_cdev, |
| 860 | + struct apple_magic_backlight, cdev); |
| 861 | + |
| 862 | + apple_magic_backlight_set(backlight, brightness, 1); |
| 863 | + return 0; |
| 864 | +} |
| 865 | + |
| 866 | +static int apple_magic_backlight_init(struct hid_device *hdev) |
| 867 | +{ |
| 868 | + struct apple_magic_backlight *backlight; |
| 869 | + struct hid_report_enum *report_enum; |
| 870 | + |
| 871 | + /* |
| 872 | + * Ensure this usb endpoint is for the keyboard backlight, not touchbar |
| 873 | + * backlight. |
| 874 | + */ |
| 875 | + if (hdev->collection[0].usage != HID_USAGE_MAGIC_BL) |
| 876 | + return -ENODEV; |
| 877 | + |
| 878 | + backlight = devm_kzalloc(&hdev->dev, sizeof(*backlight), GFP_KERNEL); |
| 879 | + if (!backlight) |
| 880 | + return -ENOMEM; |
| 881 | + |
| 882 | + report_enum = &hdev->report_enum[HID_FEATURE_REPORT]; |
| 883 | + backlight->brightness = report_enum->report_id_hash[APPLE_MAGIC_REPORT_ID_BRIGHTNESS]; |
| 884 | + backlight->power = report_enum->report_id_hash[APPLE_MAGIC_REPORT_ID_POWER]; |
| 885 | + |
| 886 | + if (!backlight->brightness || !backlight->power) |
| 887 | + return -ENODEV; |
| 888 | + |
| 889 | + backlight->cdev.name = ":white:" LED_FUNCTION_KBD_BACKLIGHT; |
| 890 | + backlight->cdev.max_brightness = backlight->brightness->field[0]->logical_maximum; |
| 891 | + backlight->cdev.brightness_set_blocking = apple_magic_backlight_led_set; |
| 892 | + |
| 893 | + apple_magic_backlight_set(backlight, 0, 0); |
| 894 | + |
| 895 | + return devm_led_classdev_register(&hdev->dev, &backlight->cdev); |
| 896 | + |
| 897 | +} |
| 898 | + |
825 | 899 | static int apple_probe(struct hid_device *hdev,
|
826 | 900 | const struct hid_device_id *id)
|
827 | 901 | {
|
@@ -860,7 +934,18 @@ static int apple_probe(struct hid_device *hdev,
|
860 | 934 | if (quirks & APPLE_BACKLIGHT_CTL)
|
861 | 935 | apple_backlight_init(hdev);
|
862 | 936 |
|
| 937 | + if (quirks & APPLE_MAGIC_BACKLIGHT) { |
| 938 | + ret = apple_magic_backlight_init(hdev); |
| 939 | + if (ret) |
| 940 | + goto out_err; |
| 941 | + } |
| 942 | + |
863 | 943 | return 0;
|
| 944 | + |
| 945 | +out_err: |
| 946 | + del_timer_sync(&asc->battery_timer); |
| 947 | + hid_hw_stop(hdev); |
| 948 | + return ret; |
864 | 949 | }
|
865 | 950 |
|
866 | 951 | static void apple_remove(struct hid_device *hdev)
|
@@ -1073,6 +1158,8 @@ static const struct hid_device_id apple_devices[] = {
|
1073 | 1158 | .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY },
|
1074 | 1159 | { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021),
|
1075 | 1160 | .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
|
| 1161 | + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT), |
| 1162 | + .driver_data = APPLE_MAGIC_BACKLIGHT }, |
1076 | 1163 |
|
1077 | 1164 | { }
|
1078 | 1165 | };
|
|
0 commit comments