10
10
11
11
#include <linux/acpi.h>
12
12
#include <linux/backlight.h>
13
+ #include <linux/bitfield.h>
13
14
#include <linux/bitops.h>
14
15
#include <linux/bug.h>
15
16
#include <linux/debugfs.h>
@@ -85,6 +86,31 @@ enum {
85
86
SALS_FNLOCK_OFF = 0xf ,
86
87
};
87
88
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
+
88
114
struct ideapad_dytc_priv {
89
115
enum platform_profile_option current_profile ;
90
116
struct platform_profile_handler pprof ;
@@ -122,6 +148,7 @@ struct ideapad_private {
122
148
} features ;
123
149
struct {
124
150
bool initialized ;
151
+ int type ;
125
152
struct led_classdev led ;
126
153
unsigned int last_brightness ;
127
154
} kbd_bl ;
@@ -242,6 +269,16 @@ static int exec_sals(acpi_handle handle, unsigned long arg)
242
269
return exec_simple_method (handle , "SALS" , arg );
243
270
}
244
271
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
+
245
282
static int eval_dytc (acpi_handle handle , unsigned long cmd , unsigned long * res )
246
283
{
247
284
return eval_int_with_arg (handle , "DYTC" , cmd , res );
@@ -1275,16 +1312,47 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
1275
1312
/*
1276
1313
* keyboard backlight
1277
1314
*/
1315
+ static int ideapad_kbd_bl_check_tristate (int type )
1316
+ {
1317
+ return (type == KBD_BL_TRISTATE ) || (type == KBD_BL_TRISTATE_AUTO );
1318
+ }
1319
+
1278
1320
static int ideapad_kbd_bl_brightness_get (struct ideapad_private * priv )
1279
1321
{
1280
- unsigned long hals ;
1322
+ unsigned long value ;
1281
1323
int err ;
1282
1324
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 );
1284
1352
if (err )
1285
1353
return err ;
1286
1354
1287
- return !!test_bit (HALS_KBD_BL_STATE_BIT , & hals );
1355
+ return !!test_bit (HALS_KBD_BL_STATE_BIT , & value );
1288
1356
}
1289
1357
1290
1358
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
1296
1364
1297
1365
static int ideapad_kbd_bl_brightness_set (struct ideapad_private * priv , unsigned int brightness )
1298
1366
{
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
+ }
1300
1382
1301
1383
if (err )
1302
1384
return err ;
@@ -1349,8 +1431,13 @@ static int ideapad_kbd_bl_init(struct ideapad_private *priv)
1349
1431
1350
1432
priv -> kbd_bl .last_brightness = brightness ;
1351
1433
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
+
1352
1440
priv -> kbd_bl .led .name = "platform::" LED_FUNCTION_KBD_BACKLIGHT ;
1353
- priv -> kbd_bl .led .max_brightness = 1 ;
1354
1441
priv -> kbd_bl .led .brightness_get = ideapad_kbd_bl_led_cdev_brightness_get ;
1355
1442
priv -> kbd_bl .led .brightness_set_blocking = ideapad_kbd_bl_led_cdev_brightness_set ;
1356
1443
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)
1461
1548
case 2 :
1462
1549
ideapad_backlight_notify_power (priv );
1463
1550
break ;
1551
+ case KBD_BL_KBLC_CHANGED_EVENT :
1464
1552
case 1 :
1465
1553
/*
1466
1554
* Some IdeaPads report event 1 every ~20
@@ -1562,13 +1650,31 @@ static void ideapad_check_features(struct ideapad_private *priv)
1562
1650
if (test_bit (HALS_FNLOCK_SUPPORT_BIT , & val ))
1563
1651
priv -> features .fn_lock = true;
1564
1652
1565
- if (test_bit (HALS_KBD_BL_SUPPORT_BIT , & val ))
1653
+ if (test_bit (HALS_KBD_BL_SUPPORT_BIT , & val )) {
1566
1654
priv -> features .kbd_bl = true;
1655
+ priv -> kbd_bl .type = KBD_BL_STANDARD ;
1656
+ }
1567
1657
1568
1658
if (test_bit (HALS_USB_CHARGING_SUPPORT_BIT , & val ))
1569
1659
priv -> features .usb_charging = true;
1570
1660
}
1571
1661
}
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
+ }
1572
1678
}
1573
1679
1574
1680
#if IS_ENABLED (CONFIG_ACPI_WMI )
0 commit comments