Skip to content

Commit ecaa186

Browse files
stuarthayhurstjwrdegoede
authored andcommitted
platform/x86: ideapad-laptop: Add support for keyboard backlights using KBLC ACPI symbol
Newer Lenovo laptops seem to use the KBLC symbol to control the backlight Add support for handling the keyboard backlight on these devices Signed-off-by: Stuart Hayhurst <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Hans de Goede <[email protected]> Signed-off-by: Hans de Goede <[email protected]>
1 parent 5ee473b commit ecaa186

File tree

1 file changed

+112
-6
lines changed

1 file changed

+112
-6
lines changed

drivers/platform/x86/ideapad-laptop.c

Lines changed: 112 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <linux/acpi.h>
1212
#include <linux/backlight.h>
13+
#include <linux/bitfield.h>
1314
#include <linux/bitops.h>
1415
#include <linux/bug.h>
1516
#include <linux/debugfs.h>
@@ -85,6 +86,31 @@ enum {
8586
SALS_FNLOCK_OFF = 0xf,
8687
};
8788

89+
/*
90+
* These correspond to the number of supported states - 1
91+
* Future keyboard types may need a new system, if there's a collision
92+
* KBD_BL_TRISTATE_AUTO has no way to report or set the auto state
93+
* so it effectively has 3 states, but needs to handle 4
94+
*/
95+
enum {
96+
KBD_BL_STANDARD = 1,
97+
KBD_BL_TRISTATE = 2,
98+
KBD_BL_TRISTATE_AUTO = 3,
99+
};
100+
101+
#define KBD_BL_QUERY_TYPE 0x1
102+
#define KBD_BL_TRISTATE_TYPE 0x5
103+
#define KBD_BL_TRISTATE_AUTO_TYPE 0x7
104+
105+
#define KBD_BL_COMMAND_GET 0x2
106+
#define KBD_BL_COMMAND_SET 0x3
107+
#define KBD_BL_COMMAND_TYPE GENMASK(7, 4)
108+
109+
#define KBD_BL_GET_BRIGHTNESS GENMASK(15, 1)
110+
#define KBD_BL_SET_BRIGHTNESS GENMASK(19, 16)
111+
112+
#define KBD_BL_KBLC_CHANGED_EVENT 12
113+
88114
struct ideapad_dytc_priv {
89115
enum platform_profile_option current_profile;
90116
struct platform_profile_handler pprof;
@@ -122,6 +148,7 @@ struct ideapad_private {
122148
} features;
123149
struct {
124150
bool initialized;
151+
int type;
125152
struct led_classdev led;
126153
unsigned int last_brightness;
127154
} kbd_bl;
@@ -242,6 +269,16 @@ static int exec_sals(acpi_handle handle, unsigned long arg)
242269
return exec_simple_method(handle, "SALS", arg);
243270
}
244271

272+
static int exec_kblc(acpi_handle handle, unsigned long arg)
273+
{
274+
return exec_simple_method(handle, "KBLC", arg);
275+
}
276+
277+
static int eval_kblc(acpi_handle handle, unsigned long cmd, unsigned long *res)
278+
{
279+
return eval_int_with_arg(handle, "KBLC", cmd, res);
280+
}
281+
245282
static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res)
246283
{
247284
return eval_int_with_arg(handle, "DYTC", cmd, res);
@@ -1275,16 +1312,47 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
12751312
/*
12761313
* keyboard backlight
12771314
*/
1315+
static int ideapad_kbd_bl_check_tristate(int type)
1316+
{
1317+
return (type == KBD_BL_TRISTATE) || (type == KBD_BL_TRISTATE_AUTO);
1318+
}
1319+
12781320
static int ideapad_kbd_bl_brightness_get(struct ideapad_private *priv)
12791321
{
1280-
unsigned long hals;
1322+
unsigned long value;
12811323
int err;
12821324

1283-
err = eval_hals(priv->adev->handle, &hals);
1325+
if (ideapad_kbd_bl_check_tristate(priv->kbd_bl.type)) {
1326+
err = eval_kblc(priv->adev->handle,
1327+
FIELD_PREP(KBD_BL_COMMAND_TYPE, priv->kbd_bl.type) |
1328+
KBD_BL_COMMAND_GET,
1329+
&value);
1330+
1331+
if (err)
1332+
return err;
1333+
1334+
/* Convert returned value to brightness level */
1335+
value = FIELD_GET(KBD_BL_GET_BRIGHTNESS, value);
1336+
1337+
/* Off, low or high */
1338+
if (value <= priv->kbd_bl.led.max_brightness)
1339+
return value;
1340+
1341+
/* Auto, report as off */
1342+
if (value == priv->kbd_bl.led.max_brightness + 1)
1343+
return 0;
1344+
1345+
/* Unknown value */
1346+
dev_warn(&priv->platform_device->dev,
1347+
"Unknown keyboard backlight value: %lu", value);
1348+
return -EINVAL;
1349+
}
1350+
1351+
err = eval_hals(priv->adev->handle, &value);
12841352
if (err)
12851353
return err;
12861354

1287-
return !!test_bit(HALS_KBD_BL_STATE_BIT, &hals);
1355+
return !!test_bit(HALS_KBD_BL_STATE_BIT, &value);
12881356
}
12891357

12901358
static enum led_brightness ideapad_kbd_bl_led_cdev_brightness_get(struct led_classdev *led_cdev)
@@ -1296,7 +1364,21 @@ static enum led_brightness ideapad_kbd_bl_led_cdev_brightness_get(struct led_cla
12961364

12971365
static int ideapad_kbd_bl_brightness_set(struct ideapad_private *priv, unsigned int brightness)
12981366
{
1299-
int err = exec_sals(priv->adev->handle, brightness ? SALS_KBD_BL_ON : SALS_KBD_BL_OFF);
1367+
int err;
1368+
unsigned long value;
1369+
int type = priv->kbd_bl.type;
1370+
1371+
if (ideapad_kbd_bl_check_tristate(type)) {
1372+
if (brightness > priv->kbd_bl.led.max_brightness)
1373+
return -EINVAL;
1374+
1375+
value = FIELD_PREP(KBD_BL_SET_BRIGHTNESS, brightness) |
1376+
FIELD_PREP(KBD_BL_COMMAND_TYPE, type) |
1377+
KBD_BL_COMMAND_SET;
1378+
err = exec_kblc(priv->adev->handle, value);
1379+
} else {
1380+
err = exec_sals(priv->adev->handle, brightness ? SALS_KBD_BL_ON : SALS_KBD_BL_OFF);
1381+
}
13001382

13011383
if (err)
13021384
return err;
@@ -1349,8 +1431,13 @@ static int ideapad_kbd_bl_init(struct ideapad_private *priv)
13491431

13501432
priv->kbd_bl.last_brightness = brightness;
13511433

1434+
if (ideapad_kbd_bl_check_tristate(priv->kbd_bl.type)) {
1435+
priv->kbd_bl.led.max_brightness = 2;
1436+
} else {
1437+
priv->kbd_bl.led.max_brightness = 1;
1438+
}
1439+
13521440
priv->kbd_bl.led.name = "platform::" LED_FUNCTION_KBD_BACKLIGHT;
1353-
priv->kbd_bl.led.max_brightness = 1;
13541441
priv->kbd_bl.led.brightness_get = ideapad_kbd_bl_led_cdev_brightness_get;
13551442
priv->kbd_bl.led.brightness_set_blocking = ideapad_kbd_bl_led_cdev_brightness_set;
13561443
priv->kbd_bl.led.flags = LED_BRIGHT_HW_CHANGED;
@@ -1461,6 +1548,7 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
14611548
case 2:
14621549
ideapad_backlight_notify_power(priv);
14631550
break;
1551+
case KBD_BL_KBLC_CHANGED_EVENT:
14641552
case 1:
14651553
/*
14661554
* Some IdeaPads report event 1 every ~20
@@ -1562,13 +1650,31 @@ static void ideapad_check_features(struct ideapad_private *priv)
15621650
if (test_bit(HALS_FNLOCK_SUPPORT_BIT, &val))
15631651
priv->features.fn_lock = true;
15641652

1565-
if (test_bit(HALS_KBD_BL_SUPPORT_BIT, &val))
1653+
if (test_bit(HALS_KBD_BL_SUPPORT_BIT, &val)) {
15661654
priv->features.kbd_bl = true;
1655+
priv->kbd_bl.type = KBD_BL_STANDARD;
1656+
}
15671657

15681658
if (test_bit(HALS_USB_CHARGING_SUPPORT_BIT, &val))
15691659
priv->features.usb_charging = true;
15701660
}
15711661
}
1662+
1663+
if (acpi_has_method(handle, "KBLC")) {
1664+
if (!eval_kblc(priv->adev->handle, KBD_BL_QUERY_TYPE, &val)) {
1665+
if (val == KBD_BL_TRISTATE_TYPE) {
1666+
priv->features.kbd_bl = true;
1667+
priv->kbd_bl.type = KBD_BL_TRISTATE;
1668+
} else if (val == KBD_BL_TRISTATE_AUTO_TYPE) {
1669+
priv->features.kbd_bl = true;
1670+
priv->kbd_bl.type = KBD_BL_TRISTATE_AUTO;
1671+
} else {
1672+
dev_warn(&priv->platform_device->dev,
1673+
"Unknown keyboard type: %lu",
1674+
val);
1675+
}
1676+
}
1677+
}
15721678
}
15731679

15741680
#if IS_ENABLED(CONFIG_ACPI_WMI)

0 commit comments

Comments
 (0)