asus-laptop: Add support for Keyboard backlight
Add support for keyboard backlight found in Asus U50VG. The SMC driver for the Apples does it via LED. To be consistent with that we create /sys/class/leds/asus::kbd_backlight/ to control the keyboard backlight. SLKB and GLKB are used to get/set the backlight. On the U50VG is supports 4 brightness level, but this may change with other models. SLKB take a 8 bit integer where the higher bit is used to toggle the backlight, and the over 7 bits control the brightness level. Signed-off-by: Corentin Chary <corentincj@iksaif.net> Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
parent
977c328d81
commit
b7d3fbc2ed
1 changed files with 78 additions and 3 deletions
|
@ -86,6 +86,7 @@
|
|||
#define GLED_ON 0x40 //Gaming LED
|
||||
#define LCD_ON 0x80 //LCD backlight
|
||||
#define GPS_ON 0x100 //GPS
|
||||
#define KEY_ON 0x200 //Keyboard backlight
|
||||
|
||||
#define ASUS_LOG ASUS_HOTK_FILE ": "
|
||||
#define ASUS_ERR KERN_ERR ASUS_LOG
|
||||
|
@ -172,6 +173,10 @@ ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON"); /* R2H */
|
|||
ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */
|
||||
ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST");
|
||||
|
||||
/* Keyboard light */
|
||||
ASUS_HANDLE(kled_set, ASUS_HOTK_PREFIX "SLKB");
|
||||
ASUS_HANDLE(kled_get, ASUS_HOTK_PREFIX "GLKB");
|
||||
|
||||
/*
|
||||
* This is the main structure, we can use it to store anything interesting
|
||||
* about the hotk device
|
||||
|
@ -263,6 +268,7 @@ ASUS_LED(tled, "touchpad", 1);
|
|||
ASUS_LED(rled, "record", 1);
|
||||
ASUS_LED(pled, "phone", 1);
|
||||
ASUS_LED(gled, "gaming", 1);
|
||||
ASUS_LED(kled, "kbd_backlight", 3);
|
||||
|
||||
struct key_entry {
|
||||
char type;
|
||||
|
@ -419,6 +425,60 @@ ASUS_LED_HANDLER(rled, RLED_ON);
|
|||
ASUS_LED_HANDLER(tled, TLED_ON);
|
||||
ASUS_LED_HANDLER(gled, GLED_ON);
|
||||
|
||||
/*
|
||||
* Keyboard backlight
|
||||
*/
|
||||
static int get_kled_lvl(void)
|
||||
{
|
||||
unsigned long long kblv;
|
||||
struct acpi_object_list params;
|
||||
union acpi_object in_obj;
|
||||
acpi_status rv;
|
||||
|
||||
params.count = 1;
|
||||
params.pointer = &in_obj;
|
||||
in_obj.type = ACPI_TYPE_INTEGER;
|
||||
in_obj.integer.value = 2;
|
||||
|
||||
rv = acpi_evaluate_integer(kled_get_handle, NULL, ¶ms, &kblv);
|
||||
if (ACPI_FAILURE(rv)) {
|
||||
pr_warning("Error reading kled level\n");
|
||||
return 0;
|
||||
}
|
||||
return kblv;
|
||||
}
|
||||
|
||||
static int set_kled_lvl(int kblv)
|
||||
{
|
||||
if (kblv > 0)
|
||||
kblv = (1 << 7) | (kblv & 0x7F);
|
||||
else
|
||||
kblv = 0;
|
||||
|
||||
if (write_acpi_int(kled_set_handle, NULL, kblv, NULL)) {
|
||||
pr_warning("Keyboard LED display write failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kled_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
kled_led_wk = value;
|
||||
queue_work(led_workqueue, &kled_led_work);
|
||||
}
|
||||
|
||||
static void kled_led_update(struct work_struct *ignored)
|
||||
{
|
||||
set_kled_lvl(kled_led_wk);
|
||||
}
|
||||
|
||||
static enum led_brightness kled_led_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
return get_kled_lvl();
|
||||
}
|
||||
|
||||
static int get_lcd_state(void)
|
||||
{
|
||||
return read_status(LCD_ON);
|
||||
|
@ -1059,6 +1119,9 @@ static int asus_hotk_get_info(void)
|
|||
|
||||
ASUS_HANDLE_INIT(ledd_set);
|
||||
|
||||
ASUS_HANDLE_INIT(kled_set);
|
||||
ASUS_HANDLE_INIT(kled_get);
|
||||
|
||||
/*
|
||||
* The HWRS method return informations about the hardware.
|
||||
* 0x80 bit is for WLAN, 0x100 for Bluetooth.
|
||||
|
@ -1190,6 +1253,10 @@ static int asus_hotk_add(struct acpi_device *device)
|
|||
/* LCD Backlight is on by default */
|
||||
write_status(NULL, 1, LCD_ON);
|
||||
|
||||
/* Keyboard Backlight is on by default */
|
||||
if (kled_set_handle)
|
||||
set_kled_lvl(1);
|
||||
|
||||
/* LED display is off by default */
|
||||
hotk->ledd_status = 0xFFF;
|
||||
|
||||
|
@ -1244,6 +1311,7 @@ static void asus_led_exit(void)
|
|||
ASUS_LED_UNREGISTER(pled);
|
||||
ASUS_LED_UNREGISTER(rled);
|
||||
ASUS_LED_UNREGISTER(gled);
|
||||
ASUS_LED_UNREGISTER(kled);
|
||||
}
|
||||
|
||||
static void asus_input_exit(void)
|
||||
|
@ -1323,13 +1391,20 @@ static int asus_led_init(struct device *dev)
|
|||
if (rv)
|
||||
goto out4;
|
||||
|
||||
led_workqueue = create_singlethread_workqueue("led_workqueue");
|
||||
if (!led_workqueue)
|
||||
if (kled_set_handle && kled_get_handle)
|
||||
rv = ASUS_LED_REGISTER(kled, dev);
|
||||
if (rv)
|
||||
goto out5;
|
||||
|
||||
led_workqueue = create_singlethread_workqueue("led_workqueue");
|
||||
if (!led_workqueue)
|
||||
goto out6;
|
||||
|
||||
return 0;
|
||||
out5:
|
||||
out6:
|
||||
rv = -ENOMEM;
|
||||
ASUS_LED_UNREGISTER(kled);
|
||||
out5:
|
||||
ASUS_LED_UNREGISTER(gled);
|
||||
out4:
|
||||
ASUS_LED_UNREGISTER(pled);
|
||||
|
|
Reference in a new issue