Message ID | 1461599173-29234-1-git-send-email-tim.gardner@canonical.com |
---|---|
State | New |
Headers | show |
On 25.04.2016 17:46, tim.gardner@canonical.com wrote: > From: Pali Rohár <pali.rohar@gmail.com> > > BugLink: http://bugs.launchpad.net/bugs/1574498 Personally I do not see the reason to SRU. It is enabling a HW feature which is not terribly critical. But ok, the change is affecting only a very specific platform driver and mostly adds bits to it. So should be testable against laptops with and without keyboard backlight. -Stefan > > This patch adds support for controlling keyboard backlight via standard > linux led class interface (::kbd_backlight). It uses ACPI HKEY device with > MLCG and MLCS methods. > > Signed-off-by: Pali Rohár <pali.rohar@gmail.com> > Tested-by: Fabio D'Urso <fabiodurso@hotmail.it> > Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> > Signed-off-by: Darren Hart <dvhart@linux.intel.com> > (cherry picked from commit bb28f3d51ff5e1be541d057708011cc1efe6fae9) > Signed-off-by: Tim Gardner <tim.gardner@canonical.com> > --- > drivers/platform/x86/thinkpad_acpi.c | 206 +++++++++++++++++++++++++++++++++++ > 1 file changed, 206 insertions(+) > > diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c > index 0bed473..a268a7a 100644 > --- a/drivers/platform/x86/thinkpad_acpi.c > +++ b/drivers/platform/x86/thinkpad_acpi.c > @@ -303,6 +303,7 @@ static struct { > u32 hotkey_mask:1; > u32 hotkey_wlsw:1; > u32 hotkey_tablet:1; > + u32 kbdlight:1; > u32 light:1; > u32 light_status:1; > u32 bright_acpimode:1; > @@ -4986,6 +4987,207 @@ static struct ibm_struct video_driver_data = { > #endif /* CONFIG_THINKPAD_ACPI_VIDEO */ > > /************************************************************************* > + * Keyboard backlight subdriver > + */ > + > +static int kbdlight_set_level(int level) > +{ > + if (!hkey_handle) > + return -ENXIO; > + > + if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level)) > + return -EIO; > + > + return 0; > +} > + > +static int kbdlight_get_level(void) > +{ > + int status = 0; > + > + if (!hkey_handle) > + return -ENXIO; > + > + if (!acpi_evalf(hkey_handle, &status, "MLCG", "dd", 0)) > + return -EIO; > + > + if (status < 0) > + return status; > + > + return status & 0x3; > +} > + > +static bool kbdlight_is_supported(void) > +{ > + int status = 0; > + > + if (!hkey_handle) > + return false; > + > + if (!acpi_has_method(hkey_handle, "MLCG")) { > + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG is unavailable\n"); > + return false; > + } > + > + if (!acpi_evalf(hkey_handle, &status, "MLCG", "qdd", 0)) { > + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG failed\n"); > + return false; > + } > + > + if (status < 0) { > + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG err: %d\n", status); > + return false; > + } > + > + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG returned 0x%x\n", status); > + /* > + * Guessed test for keyboard backlight: > + * > + * Machines with backlight keyboard return: > + * b010100000010000000XX - ThinkPad X1 Carbon 3rd > + * b110100010010000000XX - ThinkPad x230 > + * b010100000010000000XX - ThinkPad x240 > + * b010100000010000000XX - ThinkPad W541 > + * (XX is current backlight level) > + * > + * Machines without backlight keyboard return: > + * b10100001000000000000 - ThinkPad x230 > + * b10110001000000000000 - ThinkPad E430 > + * b00000000000000000000 - ThinkPad E450 > + * > + * Candidate BITs for detection test (XOR): > + * b01000000001000000000 > + * ^ > + */ > + return status & BIT(9); > +} > + > +static void kbdlight_set_worker(struct work_struct *work) > +{ > + struct tpacpi_led_classdev *data = > + container_of(work, struct tpacpi_led_classdev, work); > + > + if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) > + kbdlight_set_level(data->new_state); > +} > + > +static void kbdlight_sysfs_set(struct led_classdev *led_cdev, > + enum led_brightness brightness) > +{ > + struct tpacpi_led_classdev *data = > + container_of(led_cdev, > + struct tpacpi_led_classdev, > + led_classdev); > + data->new_state = brightness; > + queue_work(tpacpi_wq, &data->work); > +} > + > +static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev) > +{ > + int level; > + > + level = kbdlight_get_level(); > + if (level < 0) > + return 0; > + > + return level; > +} > + > +static struct tpacpi_led_classdev tpacpi_led_kbdlight = { > + .led_classdev = { > + .name = "tpacpi::kbd_backlight", > + .max_brightness = 2, > + .brightness_set = &kbdlight_sysfs_set, > + .brightness_get = &kbdlight_sysfs_get, > + .flags = LED_CORE_SUSPENDRESUME, > + } > +}; > + > +static int __init kbdlight_init(struct ibm_init_struct *iibm) > +{ > + int rc; > + > + vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n"); > + > + TPACPI_ACPIHANDLE_INIT(hkey); > + INIT_WORK(&tpacpi_led_kbdlight.work, kbdlight_set_worker); > + > + if (!kbdlight_is_supported()) { > + tp_features.kbdlight = 0; > + vdbg_printk(TPACPI_DBG_INIT, "kbdlight is unsupported\n"); > + return 1; > + } > + > + tp_features.kbdlight = 1; > + > + rc = led_classdev_register(&tpacpi_pdev->dev, > + &tpacpi_led_kbdlight.led_classdev); > + if (rc < 0) { > + tp_features.kbdlight = 0; > + return rc; > + } > + > + return 0; > +} > + > +static void kbdlight_exit(void) > +{ > + if (tp_features.kbdlight) > + led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev); > + flush_workqueue(tpacpi_wq); > +} > + > +static int kbdlight_read(struct seq_file *m) > +{ > + int level; > + > + if (!tp_features.kbdlight) { > + seq_printf(m, "status:\t\tnot supported\n"); > + } else { > + level = kbdlight_get_level(); > + if (level < 0) > + seq_printf(m, "status:\t\terror %d\n", level); > + else > + seq_printf(m, "status:\t\t%d\n", level); > + seq_printf(m, "commands:\t0, 1, 2\n"); > + } > + > + return 0; > +} > + > +static int kbdlight_write(char *buf) > +{ > + char *cmd; > + int level = -1; > + > + if (!tp_features.kbdlight) > + return -ENODEV; > + > + while ((cmd = next_cmd(&buf))) { > + if (strlencmp(cmd, "0") == 0) > + level = 0; > + else if (strlencmp(cmd, "1") == 0) > + level = 1; > + else if (strlencmp(cmd, "2") == 0) > + level = 2; > + else > + return -EINVAL; > + } > + > + if (level == -1) > + return -EINVAL; > + > + return kbdlight_set_level(level); > +} > + > +static struct ibm_struct kbdlight_driver_data = { > + .name = "kbdlight", > + .read = kbdlight_read, > + .write = kbdlight_write, > + .exit = kbdlight_exit, > +}; > + > +/************************************************************************* > * Light (thinklight) subdriver > */ > > @@ -9207,6 +9409,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { > }, > #endif > { > + .init = kbdlight_init, > + .data = &kbdlight_driver_data, > + }, > + { > .init = light_init, > .data = &light_driver_data, > }, >
On Mon, Apr 25, 2016 at 09:46:13AM -0600, tim.gardner@canonical.com wrote: > From: Pali Rohár <pali.rohar@gmail.com> > > BugLink: http://bugs.launchpad.net/bugs/1574498 > > This patch adds support for controlling keyboard backlight via standard > linux led class interface (::kbd_backlight). It uses ACPI HKEY device with > MLCG and MLCS methods. > > Signed-off-by: Pali Rohár <pali.rohar@gmail.com> > Tested-by: Fabio D'Urso <fabiodurso@hotmail.it> > Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> > Signed-off-by: Darren Hart <dvhart@linux.intel.com> > (cherry picked from commit bb28f3d51ff5e1be541d057708011cc1efe6fae9) > Signed-off-by: Tim Gardner <tim.gardner@canonical.com> > --- > drivers/platform/x86/thinkpad_acpi.c | 206 +++++++++++++++++++++++++++++++++++ > 1 file changed, 206 insertions(+) > > diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c > index 0bed473..a268a7a 100644 > --- a/drivers/platform/x86/thinkpad_acpi.c > +++ b/drivers/platform/x86/thinkpad_acpi.c > @@ -303,6 +303,7 @@ static struct { > u32 hotkey_mask:1; > u32 hotkey_wlsw:1; > u32 hotkey_tablet:1; > + u32 kbdlight:1; > u32 light:1; > u32 light_status:1; > u32 bright_acpimode:1; > @@ -4986,6 +4987,207 @@ static struct ibm_struct video_driver_data = { > #endif /* CONFIG_THINKPAD_ACPI_VIDEO */ > > /************************************************************************* > + * Keyboard backlight subdriver > + */ > + > +static int kbdlight_set_level(int level) > +{ > + if (!hkey_handle) > + return -ENXIO; > + > + if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level)) > + return -EIO; > + > + return 0; > +} > + > +static int kbdlight_get_level(void) > +{ > + int status = 0; > + > + if (!hkey_handle) > + return -ENXIO; > + > + if (!acpi_evalf(hkey_handle, &status, "MLCG", "dd", 0)) > + return -EIO; > + > + if (status < 0) > + return status; > + > + return status & 0x3; > +} > + > +static bool kbdlight_is_supported(void) > +{ > + int status = 0; > + > + if (!hkey_handle) > + return false; > + > + if (!acpi_has_method(hkey_handle, "MLCG")) { > + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG is unavailable\n"); > + return false; > + } > + > + if (!acpi_evalf(hkey_handle, &status, "MLCG", "qdd", 0)) { > + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG failed\n"); > + return false; > + } > + > + if (status < 0) { > + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG err: %d\n", status); > + return false; > + } > + > + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG returned 0x%x\n", status); > + /* > + * Guessed test for keyboard backlight: > + * > + * Machines with backlight keyboard return: > + * b010100000010000000XX - ThinkPad X1 Carbon 3rd > + * b110100010010000000XX - ThinkPad x230 > + * b010100000010000000XX - ThinkPad x240 > + * b010100000010000000XX - ThinkPad W541 > + * (XX is current backlight level) > + * > + * Machines without backlight keyboard return: > + * b10100001000000000000 - ThinkPad x230 > + * b10110001000000000000 - ThinkPad E430 > + * b00000000000000000000 - ThinkPad E450 > + * > + * Candidate BITs for detection test (XOR): > + * b01000000001000000000 > + * ^ > + */ > + return status & BIT(9); > +} > + > +static void kbdlight_set_worker(struct work_struct *work) > +{ > + struct tpacpi_led_classdev *data = > + container_of(work, struct tpacpi_led_classdev, work); > + > + if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) > + kbdlight_set_level(data->new_state); > +} > + > +static void kbdlight_sysfs_set(struct led_classdev *led_cdev, > + enum led_brightness brightness) > +{ > + struct tpacpi_led_classdev *data = > + container_of(led_cdev, > + struct tpacpi_led_classdev, > + led_classdev); > + data->new_state = brightness; > + queue_work(tpacpi_wq, &data->work); > +} > + > +static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev) > +{ > + int level; > + > + level = kbdlight_get_level(); > + if (level < 0) > + return 0; > + > + return level; > +} > + > +static struct tpacpi_led_classdev tpacpi_led_kbdlight = { > + .led_classdev = { > + .name = "tpacpi::kbd_backlight", > + .max_brightness = 2, > + .brightness_set = &kbdlight_sysfs_set, > + .brightness_get = &kbdlight_sysfs_get, > + .flags = LED_CORE_SUSPENDRESUME, > + } > +}; > + > +static int __init kbdlight_init(struct ibm_init_struct *iibm) > +{ > + int rc; > + > + vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n"); > + > + TPACPI_ACPIHANDLE_INIT(hkey); > + INIT_WORK(&tpacpi_led_kbdlight.work, kbdlight_set_worker); > + > + if (!kbdlight_is_supported()) { > + tp_features.kbdlight = 0; > + vdbg_printk(TPACPI_DBG_INIT, "kbdlight is unsupported\n"); > + return 1; > + } > + > + tp_features.kbdlight = 1; > + > + rc = led_classdev_register(&tpacpi_pdev->dev, > + &tpacpi_led_kbdlight.led_classdev); > + if (rc < 0) { > + tp_features.kbdlight = 0; > + return rc; > + } > + > + return 0; > +} > + > +static void kbdlight_exit(void) > +{ > + if (tp_features.kbdlight) > + led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev); > + flush_workqueue(tpacpi_wq); > +} > + > +static int kbdlight_read(struct seq_file *m) > +{ > + int level; > + > + if (!tp_features.kbdlight) { > + seq_printf(m, "status:\t\tnot supported\n"); > + } else { > + level = kbdlight_get_level(); > + if (level < 0) > + seq_printf(m, "status:\t\terror %d\n", level); > + else > + seq_printf(m, "status:\t\t%d\n", level); > + seq_printf(m, "commands:\t0, 1, 2\n"); > + } > + > + return 0; > +} > + > +static int kbdlight_write(char *buf) > +{ > + char *cmd; > + int level = -1; > + > + if (!tp_features.kbdlight) > + return -ENODEV; > + > + while ((cmd = next_cmd(&buf))) { > + if (strlencmp(cmd, "0") == 0) > + level = 0; > + else if (strlencmp(cmd, "1") == 0) > + level = 1; > + else if (strlencmp(cmd, "2") == 0) > + level = 2; > + else > + return -EINVAL; > + } > + > + if (level == -1) > + return -EINVAL; > + > + return kbdlight_set_level(level); > +} > + > +static struct ibm_struct kbdlight_driver_data = { > + .name = "kbdlight", > + .read = kbdlight_read, > + .write = kbdlight_write, > + .exit = kbdlight_exit, > +}; > + > +/************************************************************************* > * Light (thinklight) subdriver > */ > > @@ -9207,6 +9409,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { > }, > #endif > { > + .init = kbdlight_init, > + .data = &kbdlight_driver_data, > + }, > + { > .init = light_init, > .data = &light_driver_data, > }, > -- This is h/w enablement, but nicely self contained in the thinkpad driver. It looks reasonable assuming we are getting/already have the bits to leverage it in gnome/unity. Acked-by: Andy Whitcroft <apw@canonical.com> -apw
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 0bed473..a268a7a 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -303,6 +303,7 @@ static struct { u32 hotkey_mask:1; u32 hotkey_wlsw:1; u32 hotkey_tablet:1; + u32 kbdlight:1; u32 light:1; u32 light_status:1; u32 bright_acpimode:1; @@ -4986,6 +4987,207 @@ static struct ibm_struct video_driver_data = { #endif /* CONFIG_THINKPAD_ACPI_VIDEO */ /************************************************************************* + * Keyboard backlight subdriver + */ + +static int kbdlight_set_level(int level) +{ + if (!hkey_handle) + return -ENXIO; + + if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level)) + return -EIO; + + return 0; +} + +static int kbdlight_get_level(void) +{ + int status = 0; + + if (!hkey_handle) + return -ENXIO; + + if (!acpi_evalf(hkey_handle, &status, "MLCG", "dd", 0)) + return -EIO; + + if (status < 0) + return status; + + return status & 0x3; +} + +static bool kbdlight_is_supported(void) +{ + int status = 0; + + if (!hkey_handle) + return false; + + if (!acpi_has_method(hkey_handle, "MLCG")) { + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG is unavailable\n"); + return false; + } + + if (!acpi_evalf(hkey_handle, &status, "MLCG", "qdd", 0)) { + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG failed\n"); + return false; + } + + if (status < 0) { + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG err: %d\n", status); + return false; + } + + vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG returned 0x%x\n", status); + /* + * Guessed test for keyboard backlight: + * + * Machines with backlight keyboard return: + * b010100000010000000XX - ThinkPad X1 Carbon 3rd + * b110100010010000000XX - ThinkPad x230 + * b010100000010000000XX - ThinkPad x240 + * b010100000010000000XX - ThinkPad W541 + * (XX is current backlight level) + * + * Machines without backlight keyboard return: + * b10100001000000000000 - ThinkPad x230 + * b10110001000000000000 - ThinkPad E430 + * b00000000000000000000 - ThinkPad E450 + * + * Candidate BITs for detection test (XOR): + * b01000000001000000000 + * ^ + */ + return status & BIT(9); +} + +static void kbdlight_set_worker(struct work_struct *work) +{ + struct tpacpi_led_classdev *data = + container_of(work, struct tpacpi_led_classdev, work); + + if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) + kbdlight_set_level(data->new_state); +} + +static void kbdlight_sysfs_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct tpacpi_led_classdev *data = + container_of(led_cdev, + struct tpacpi_led_classdev, + led_classdev); + data->new_state = brightness; + queue_work(tpacpi_wq, &data->work); +} + +static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev) +{ + int level; + + level = kbdlight_get_level(); + if (level < 0) + return 0; + + return level; +} + +static struct tpacpi_led_classdev tpacpi_led_kbdlight = { + .led_classdev = { + .name = "tpacpi::kbd_backlight", + .max_brightness = 2, + .brightness_set = &kbdlight_sysfs_set, + .brightness_get = &kbdlight_sysfs_get, + .flags = LED_CORE_SUSPENDRESUME, + } +}; + +static int __init kbdlight_init(struct ibm_init_struct *iibm) +{ + int rc; + + vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n"); + + TPACPI_ACPIHANDLE_INIT(hkey); + INIT_WORK(&tpacpi_led_kbdlight.work, kbdlight_set_worker); + + if (!kbdlight_is_supported()) { + tp_features.kbdlight = 0; + vdbg_printk(TPACPI_DBG_INIT, "kbdlight is unsupported\n"); + return 1; + } + + tp_features.kbdlight = 1; + + rc = led_classdev_register(&tpacpi_pdev->dev, + &tpacpi_led_kbdlight.led_classdev); + if (rc < 0) { + tp_features.kbdlight = 0; + return rc; + } + + return 0; +} + +static void kbdlight_exit(void) +{ + if (tp_features.kbdlight) + led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev); + flush_workqueue(tpacpi_wq); +} + +static int kbdlight_read(struct seq_file *m) +{ + int level; + + if (!tp_features.kbdlight) { + seq_printf(m, "status:\t\tnot supported\n"); + } else { + level = kbdlight_get_level(); + if (level < 0) + seq_printf(m, "status:\t\terror %d\n", level); + else + seq_printf(m, "status:\t\t%d\n", level); + seq_printf(m, "commands:\t0, 1, 2\n"); + } + + return 0; +} + +static int kbdlight_write(char *buf) +{ + char *cmd; + int level = -1; + + if (!tp_features.kbdlight) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "0") == 0) + level = 0; + else if (strlencmp(cmd, "1") == 0) + level = 1; + else if (strlencmp(cmd, "2") == 0) + level = 2; + else + return -EINVAL; + } + + if (level == -1) + return -EINVAL; + + return kbdlight_set_level(level); +} + +static struct ibm_struct kbdlight_driver_data = { + .name = "kbdlight", + .read = kbdlight_read, + .write = kbdlight_write, + .exit = kbdlight_exit, +}; + +/************************************************************************* * Light (thinklight) subdriver */ @@ -9207,6 +9409,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { }, #endif { + .init = kbdlight_init, + .data = &kbdlight_driver_data, + }, + { .init = light_init, .data = &light_driver_data, },