From patchwork Wed Dec 30 22:27:41 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Pali_Roh=C3=A1r?= X-Patchwork-Id: 7934941 Return-Path: X-Original-To: patchwork-platform-driver-x86@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id AFFFCBEEE5 for ; Wed, 30 Dec 2015 22:29:13 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 65F26202E6 for ; Wed, 30 Dec 2015 22:29:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7D65A20219 for ; Wed, 30 Dec 2015 22:29:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751017AbbL3W2W (ORCPT ); Wed, 30 Dec 2015 17:28:22 -0500 Received: from mail-wm0-f54.google.com ([74.125.82.54]:37867 "EHLO mail-wm0-f54.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750820AbbL3W2V (ORCPT ); Wed, 30 Dec 2015 17:28:21 -0500 Received: by mail-wm0-f54.google.com with SMTP id f206so93301284wmf.0; Wed, 30 Dec 2015 14:28:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; bh=iqO0+SuvXPgebtDlJ96oiaXs1hqBm1bAl9WR7GaUiB4=; b=Phky49e4CGXMtu11Krh/8kC7LG+ltErggKdZTpY7nXqUCJ+XsGMLgYAPzFvFBAgSng PeEq7wlOl8pntGESN2sR9of8doihlqMcJ+XTNBAzSKgK/PyGQn5dtnEJ5KABXHkgHbzJ 0GFY9Um7fRxKTYFf5OFNUltpKvsQyb6GTZdbzJBqlqAp/H6KP8qbWq9k9h3xdlwZYpUm Uocy9lP9Yfdm2LiGotVNrSfrDUoznZ6rAOoyIocuuhnsjuEf5tPq+WlCGuhjpOtYZ20E /RY9M0KTxNM+shMV0tTmlcDKmfyFuqCwfMa4dKMsyOrnHopL2zzIt7pC5Z2AEdePyAdR KSuw== X-Received: by 10.194.249.97 with SMTP id yt1mr76138508wjc.89.1451514499493; Wed, 30 Dec 2015 14:28:19 -0800 (PST) Received: from localhost.localdomain (ip-88-212-34-237.antik.sk. [88.212.34.237]) by smtp.gmail.com with ESMTPSA id j3sm43558354wmj.19.2015.12.30.14.28.17 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 30 Dec 2015 14:28:18 -0800 (PST) From: =?UTF-8?q?Pali=20Roh=C3=A1r?= To: Henrique de Moraes Holschuh , Darren Hart Cc: ibm-acpi-devel@lists.sourceforge.net, platform-driver-x86@vger.kernel.org, linux-kernel@vger.kernel.org, Fabio D'Urso , =?UTF-8?q?Pali=20Roh=C3=A1r?= Subject: [PATCH v2] thinkpad_acpi: Add support for keyboard backlight Date: Wed, 30 Dec 2015 23:27:41 +0100 Message-Id: <1451514461-31999-1-git-send-email-pali.rohar@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1450982818-11694-1-git-send-email-pali.rohar@gmail.com> References: <1450982818-11694-1-git-send-email-pali.rohar@gmail.com> MIME-Version: 1.0 Sender: platform-driver-x86-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: platform-driver-x86@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Tested-by: Fabio D'Urso Acked-by: Henrique de Moraes Holschuh --- Changes since v1: * Added LED_CORE_SUSPENDRESUME to preserve led state across suspend/hibernate --- 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, },