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+
88114struct 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+
245282static 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+
12781320static 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
12901358static 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
12971365static 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