Download raw body.
[patch] Backlight control for Lenovo Legion laptop
The laptop has a NVIDIA/AMDGPU dual graphics. Display backlight
is controlled via ACPI WMI. I have discussed this patch with Ted
Unangst few months ago, but apparently he was busy with other
things, so I'm posting it here for those, who might need it.
Index: dev/acpi/acpiwmi.c
===================================================================
RCS file: /home/cvs/src/sys/dev/acpi/acpiwmi.c,v
diff -u -p -r1.6 acpiwmi.c
--- dev/acpi/acpiwmi.c 14 Jul 2025 23:49:08 -0000 1.6
+++ dev/acpi/acpiwmi.c 21 Oct 2025 08:26:41 -0000
@@ -36,6 +36,9 @@
#define DPRINTF(x)
#endif
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsdisplayvar.h>
+
struct acpiwmi_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
@@ -137,6 +140,9 @@ guid_string(const uint8_t *guid, char *b
static int wmi_asus_init(struct acpiwmi_softc *, struct guidinfo *);
static int wmi_asus_event(struct wmihandler *, int);
+static int wmi_nvbl_init(struct acpiwmi_softc *, struct guidinfo *);
+static int wmi_nvbl_event(struct wmihandler *, int);
+
struct wmi_target {
uint8_t guid[GUID_SIZE];
int (*init)(struct acpiwmi_softc *, struct guidinfo *);
@@ -147,8 +153,14 @@ static struct wmi_target targets[] = {
/* "97845ED0-4E6D-11DE-8A39-0800200C9A66" */
{ 0xd0, 0x5e, 0x84, 0x97, 0x6d, 0x4e, 0xde, 0x11,
0x8a, 0x39, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 },
- wmi_asus_init,
+ wmi_asus_init
},
+ {
+ /* "603E9613-EF25-4338-A3D0-C46177516DB7" */
+ { 0x13, 0x96, 0x3e, 0x60, 0x25, 0xef, 0x38, 0x43,
+ 0xa3, 0xd0, 0xc4, 0x61, 0x77, 0x51, 0x6d, 0xb7 },
+ wmi_nvbl_init
+ }
};
void
@@ -177,7 +189,7 @@ acpiwmi_attach(struct device *parent, st
char buf[64];
DPRINTF(("method: %s\n", guid_string(guids[i].guid, buf)));
#endif
- for (int j = 0; j < 1; j++) {
+ for (int j = 0; j < 2; j++) {
if (memcmp(guids[i].guid, targets[j].guid, GUID_SIZE) == 0)
targets[j].init(sc, &guids[i]);
}
@@ -380,5 +392,115 @@ wmi_asus_event(struct wmihandler *wmi, i
DPRINTF(("asus button %d\n", code));
break;
}
+ return 0;
+}
+
+
+/* Nvidia/amdgpu backlight control, as found on Lenovo Legion
+ laptop. */
+struct wminvbl {
+ struct wmihandler w_wmi;
+ int curr_brightness;
+ int max_brightness;
+};
+
+#define NVIDIA_BRIGHTNESS_METHOD_LEVEL 1
+
+#define NVIDIA_BRIGHTNESS_OP_GET 0
+#define NVIDIA_BRIGHTNESS_OP_SET 1
+#define NVIDIA_BRIGHTNESS_OP_GET_MAX 2
+
+
+static int
+wmi_nvbl_set_brightness(struct wminvbl *wh, int val)
+{
+ return wmi_eval_method(&wh->w_wmi, 1,
+ NVIDIA_BRIGHTNESS_METHOD_LEVEL,
+ NVIDIA_BRIGHTNESS_OP_SET,
+ val);
+}
+
+static int
+wmi_nvbl_get_brightness(struct wminvbl *wh)
+{
+ int rc = wmi_eval_method(&wh->w_wmi, 1,
+ NVIDIA_BRIGHTNESS_METHOD_LEVEL,
+ NVIDIA_BRIGHTNESS_OP_GET, 0);
+ return rc;
+}
+
+static struct wminvbl *
+wmi_nvbl_get(struct acpiwmi_softc *sc)
+{
+ struct wmihandler *wh = sc->sc_handlers.slh_first;
+ while (wh && wh->w_event != wmi_nvbl_event)
+ wh = wh->w_next.sle_next;
+ return (struct wminvbl*)wh;
+}
+
+static int
+wmi_nvbl_get_param(struct wsdisplay_param *dp)
+{
+ struct acpiwmi_softc *sc = acpiwmi_cd.cd_devs[0];
+ struct wminvbl *wh = wmi_nvbl_get(sc);
+ if (!wh) return -1;
+
+ switch(dp->param) {
+ case WSDISPLAYIO_PARAM_BRIGHTNESS:
+ dp->min = 0;
+ dp->max = wh->max_brightness>0?wh->max_brightness:0xffff;
+ dp->curval = wh->curr_brightness;
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+static int
+wmi_nvbl_set_param(struct wsdisplay_param *dp)
+{
+ struct acpiwmi_softc *sc = acpiwmi_cd.cd_devs[0];
+ struct wminvbl *wh = wmi_nvbl_get(sc);
+ if (!wh) return -1;
+
+ switch(dp->param) {
+ case WSDISPLAYIO_PARAM_BRIGHTNESS:
+ wh->curr_brightness = dp->curval;
+ wmi_nvbl_set_brightness(wh, dp->curval);
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+static int
+wmi_nvbl_init(struct acpiwmi_softc *sc, struct guidinfo *ginfo)
+{
+ struct wminvbl *wh;
+
+ wh = malloc(sizeof(*wh), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if(!wh)
+ return -1;
+ wh->w_wmi.w_sc = sc;
+ wh->w_wmi.w_event = wmi_nvbl_event;
+ snprintf(wh->w_wmi.w_method, sizeof(wh->w_wmi.w_method),
+ "WM%c%c", ginfo->oid[0], ginfo->oid[1]);
+
+ ws_get_param = wmi_nvbl_get_param;
+ ws_set_param = wmi_nvbl_set_param;
+
+ SLIST_INSERT_HEAD(&sc->sc_handlers, &wh->w_wmi, w_next);
+
+ wh->max_brightness = wmi_eval_method(&wh->w_wmi, 1,
+ NVIDIA_BRIGHTNESS_METHOD_LEVEL,
+ NVIDIA_BRIGHTNESS_OP_GET_MAX, 0);
+ wh->curr_brightness = wmi_nvbl_get_brightness(wh);
+ return 0;
+}
+
+static int
+wmi_nvbl_event(struct wmihandler *wmi, int code)
+{
+ printf("unhandled event: %d\n", code);
return 0;
}
Index: dev/pci/drm/amd/amdgpu/amdgpu_drv.c
===================================================================
RCS file: /home/cvs/src/sys/dev/pci/drm/amd/amdgpu/amdgpu_drv.c,v
diff -u -p -r1.64 amdgpu_drv.c
--- dev/pci/drm/amd/amdgpu/amdgpu_drv.c 26 Aug 2025 03:33:49 -0000 1.64
+++ dev/pci/drm/amd/amdgpu/amdgpu_drv.c 21 Oct 2025 08:26:41 -0000
@@ -3560,10 +3560,14 @@ amdgpu_wsioctl(void *v, u_long cmd, cadd
switch (dp->param) {
case WSDISPLAYIO_PARAM_BRIGHTNESS:
- dp->min = 0;
- dp->max = bd->props.max_brightness;
- dp->curval = bd->props.brightness;
- return (dp->max > dp->min) ? 0 : -1;
+ if(ws_get_param == NULL) {
+ dp->min = 0;
+ dp->max = bd->props.max_brightness;
+ dp->curval = bd->props.brightness;
+ return (dp->max > dp->min) ? 0 : -1;
+ } else {
+ return ws_get_param(dp);
+ }
}
break;
case WSDISPLAYIO_SETPARAM:
@@ -3572,10 +3576,14 @@ amdgpu_wsioctl(void *v, u_long cmd, cadd
switch (dp->param) {
case WSDISPLAYIO_PARAM_BRIGHTNESS:
- bd->props.brightness = dp->curval;
- backlight_update_status(bd);
- knote_locked(&adev->ddev.note, NOTE_CHANGE);
- return 0;
+ if(ws_set_param == NULL) {
+ bd->props.brightness = dp->curval;
+ backlight_update_status(bd);
+ knote_locked(&adev->ddev.note, NOTE_CHANGE);
+ return 0;
+ } else {
+ return ws_set_param(dp);
+ }
}
break;
case WSDISPLAYIO_SVIDEO:
[patch] Backlight control for Lenovo Legion laptop