diff options
| author | Adrian Betschart <adrian.betschart@cinemaone.ch> | 2026-09-11 17:13:14 +0200 |
|---|---|---|
| committer | Alex Deucher <alexander.deucher@amd.com> | 2026-10-01 17:18:05 -0400 |
| commit | 106b4a2a78b2a2f14885604891957b84bba502db (patch) | |
| tree | 5d40eef2428fb1a4791ab46a4b19091a5910eac2 /drivers/hid/hid-elecom.c | |
| download | linux-stable-106b4a2a78b2a2f14885604891957b84bba502db.tar.gz linux-stable-106b4a2a78b2a2f14885604891957b84bba502db.zip | |
drm/amd/display: Try RGB before YCbCr 4:4:4 in stream validationgrafted
amdgpu_dm_create_validate_stream_for_sink() walks encoding_order[] and
uses the first encoding that validates. YCbCr 4:4:4 is listed before
RGB, so an HDMI sink that advertises 4:4:4 gets YCbCr 4:4:4 whenever the
"color format" property is left at AUTO, even though RGB fits the same
link.
That contradicts the documented AUTO behaviour for HDMI in enum
drm_connector_color_format (RGB, falling back to YCbCr 4:2:0 only when
the bandwidth is not available or the mode is 4:2:0-only), which the
amdgpu implementation of the property also describes. It also leaves
the "Broadcast RGB" property without effect on such sinks, since the
quantization range it selects only applies to RGB output.
Try RGB first. The mask still holds every encoding the sink supports,
so a mode that cannot carry RGB falls back exactly as before.
For reference, v7.2 picked RGB here unless YCbCr 4:4:4 was forced
through debugfs, while earlier kernels picked YCbCr 4:4:4 for any HDMI
sink that advertised it.
Fixes: 0b0ff65d3ca1 ("drm/amd/display: Refactor stream validation")
Suggested-by: Adolfo Rodrigues <adolfotregosa@gmail.com>
Assisted-by: Claude Code:claude-fable-5-1
Signed-off-by: Adrian Betschart <adrian.betschart@cinemaone.ch>
Reviewed-by: Fangzhi Zuo <jerry.zuo@amd.com>
Tested-by: Adolfo Rodrigues <adolfotregosa@gmail.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
(cherry picked from commit 7de432bc753133764dc40d37f9388da56de251e7)
Diffstat (limited to 'drivers/hid/hid-elecom.c')
| -rw-r--r-- | drivers/hid/hid-elecom.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c new file mode 100644 index 000000000..d21ead40b --- /dev/null +++ b/drivers/hid/hid-elecom.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HID driver for ELECOM devices: + * - BM084 Bluetooth Mouse + * - EX-G Trackballs (M-XT3DRBK, M-XT3URBK, M-XT4DRBK) + * - DEFT Trackballs (M-DT1DRBK, M-DT1URBK, M-DT2DRBK, M-DT2URBK) + * - HUGE Trackballs (M-HT1DRBK, M-HT1URBK) + * - HUGE Plus Trackball (M-HT1MRBK) + * + * Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com> + * Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com> + * Copyright (c) 2017 Diego Elio Pettenò <flameeyes@flameeyes.eu> + * Copyright (c) 2017 Alex Manoussakis <amanou@gnu.org> + * Copyright (c) 2017 Tomasz Kramkowski <tk@the-tk.com> + * Copyright (c) 2020 YOSHIOKA Takuma <lo48576@hard-wi.red> + * Copyright (c) 2022 Takahiro Fujii <fujii@xaxxi.net> + */ + +/* + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +/* + * Certain ELECOM mice misreport their button count meaning that they only work + * correctly with the ELECOM mouse assistant software which is unavailable for + * Linux. A four extra INPUT reports and a FEATURE report are described by the + * report descriptor but it does not appear that these enable software to + * control what the extra buttons map to. The only simple and straightforward + * solution seems to involve fixing up the report descriptor. + */ +#define MOUSE_BUTTONS_MAX 8 +static void mouse_button_fixup(struct hid_device *hdev, + __u8 *rdesc, unsigned int rsize, + unsigned int button_bit_count, + unsigned int padding_bit, + unsigned int button_report_size, + unsigned int button_usage_maximum, + int nbuttons) +{ + if (rsize < 32 || rdesc[button_bit_count] != 0x95 || + rdesc[button_report_size] != 0x75 || + rdesc[button_report_size + 1] != 0x01 || + rdesc[button_usage_maximum] != 0x29 || rdesc[padding_bit] != 0x75) + return; + hid_info(hdev, "Fixing up Elecom mouse button count\n"); + nbuttons = clamp(nbuttons, 0, MOUSE_BUTTONS_MAX); + rdesc[button_bit_count + 1] = nbuttons; + rdesc[button_usage_maximum + 1] = nbuttons; + rdesc[padding_bit + 1] = MOUSE_BUTTONS_MAX - nbuttons; +} + +static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + switch (hdev->product) { + case USB_DEVICE_ID_ELECOM_BM084: + /* The BM084 Bluetooth mouse includes a non-existing horizontal + * wheel in the HID descriptor. */ + if (*rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) { + hid_info(hdev, "Fixing up Elecom BM084 report descriptor\n"); + rdesc[47] = 0x00; + } + break; + case USB_DEVICE_ID_ELECOM_M_XGL20DLBK: + /* + * Report descriptor format: + * 20: button bit count + * 28: padding bit count + * 22: button report size + * 14: button usage maximum + */ + mouse_button_fixup(hdev, rdesc, *rsize, 20, 28, 22, 14, 8); + break; + case USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB: + case USB_DEVICE_ID_ELECOM_M_XT3URBK_018F: + case USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC: + case USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD: + /* + * Report descriptor format: + * 12: button bit count + * 30: padding bit count + * 14: button report size + * 20: button usage maximum + */ + mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 6); + break; + case USB_DEVICE_ID_ELECOM_M_DT1URBK: + case USB_DEVICE_ID_ELECOM_M_DT1DRBK: + case USB_DEVICE_ID_ELECOM_M_HT1URBK_010C: + case USB_DEVICE_ID_ELECOM_M_HT1URBK_019B: + case USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D: + /* + * Report descriptor format: + * 12: button bit count + * 30: padding bit count + * 14: button report size + * 20: button usage maximum + */ + mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8); + break; + case USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C: + case USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E: + /* + * Report descriptor format: + * 22: button bit count + * 30: padding bit count + * 24: button report size + * 16: button usage maximum + */ + mouse_button_fixup(hdev, rdesc, *rsize, 22, 30, 24, 16, 6); + break; + case USB_DEVICE_ID_ELECOM_M_DT2DRBK: + case USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C: + /* + * Report descriptor format: + * 22: button bit count + * 30: padding bit count + * 24: button report size + * 16: button usage maximum + */ + mouse_button_fixup(hdev, rdesc, *rsize, 22, 30, 24, 16, 8); + break; + case USB_DEVICE_ID_ELECOM_M_HT1MRBK: + case USB_DEVICE_ID_ELECOM_M_HT1MRBK_01AB: + case USB_DEVICE_ID_ELECOM_M_HT1MRBK_01AC: + /* + * Report descriptor format: + * 24: button bit count + * 28: padding bit count + * 22: button report size + * 16: button usage maximum + */ + mouse_button_fixup(hdev, rdesc, *rsize, 24, 28, 22, 16, 8); + break; + } + return rdesc; +} + +static const struct hid_device_id elecom_devices[] = { + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1MRBK_01AC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT2DRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK_010C) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK_019B) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1MRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1MRBK_01AB) }, + { } +}; +MODULE_DEVICE_TABLE(hid, elecom_devices); + +static struct hid_driver elecom_driver = { + .name = "elecom", + .id_table = elecom_devices, + .report_fixup = elecom_report_fixup +}; +module_hid_driver(elecom_driver); + +MODULE_DESCRIPTION("HID driver for ELECOM devices"); +MODULE_LICENSE("GPL"); |
