From patchwork Tue May 18 15:08:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 441509 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-21.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, MENTIONS_GIT_HOSTING, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 92E4CC433ED for ; Tue, 18 May 2021 15:09:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7469561042 for ; Tue, 18 May 2021 15:09:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1350017AbhERPLH (ORCPT ); Tue, 18 May 2021 11:11:07 -0400 Received: from mail.kernel.org ([198.145.29.99]:49582 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344884AbhERPKb (ORCPT ); Tue, 18 May 2021 11:10:31 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 1F17E61353; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=OgCUxWUT+5/bhavISOf/ZnGzda+Vo5Mq0/2DW/bkllw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Z71+8qSraqX5vMGaXYIbV2zNBrCAyMVq161Xhl+qlReDbhO6muB3NgUskVZeHRYgC gCNC0eWtDqQ6EmRApVX2Eoratv4ed1Z4sJbY6olL+1e9WeaxdQNCgQQZGpWIzNRwio CzD2o81ySsdYF1eynXGvDmuph+aqynyGRorjfOq0vcIoOkSr5VGB8g6FiBeZXdQbQh u0Mxi0eWrUfb6lsfLE5bU1uGU/JVd8O4h44m5F/RhrMGFD9+684m4HurAVTIt7YHED TndMRDJtDJrnf6/o3oETAMrn77Jo7X17KEcbqUKnR0L0ykll6cCb1Y3ECRp250sLVH iTb0cfOu5ZnXA== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HO1-0V; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 02/17] leds: add support for NUC WMI LEDs Date: Tue, 18 May 2021 17:08:51 +0200 Message-Id: <69b8623fe70f9bc636d5cd42d56c7f646f3bbf19.1621349813.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Some Intel Next Unit of Computing (NUC) machines have software-configured LEDs that can be used to display a variety of events: - Power State - HDD Activity - Ethernet - WiFi - Power Limit They can even be controlled directly via software, without any hardware-specific indicator connected into them. Some devices have mono-colored LEDs, but the more advanced ones have RGB leds that can show any color. Different color and 4 blink states can be programmed for thee system states: - powered on (S0); - S3; - Standby. The NUC BIOSes allow to partially set them for S0, but doesn't provide any control for the other states, nor does allow changing the blinking logic. They all use a WMI interface using GUID: 8C5DA44C-CDC3-46b3-8619-4E26D34390B7 But there are 3 different revisions of the spec, all using the same GUID, but two different APIs: - the original one, for NUC6 and to NUCi7: - https://www.intel.com/content/www/us/en/support/articles/000023426/intel-nuc/intel-nuc-kits.html - a new one, starting with NUCi8, with two revisions: - https://raw.githubusercontent.com/nomego/intel_nuc_led/master/specs/INTEL_WMI_LED_0.64.pdf - https://www.intel.com/content/dam/support/us/en/documents/intel-nuc/WMI-Spec-Intel-NUC-NUC10ixFNx.pdf There are some OOT drivers for them, but they use procfs and use a messy interface to setup it. Also, there are different drivers with the same name, each with support for each NUC family. Let's start a new driver from scratch, using the x86 platform WMI core and the LED class. This initial version is compatible with NUCi8 and above, and it was tested with a Hades Canyon NUC (NUC8i7HNK). It provides just the more basic WMI support, allowing to change the LED hardware/firmware indicator for each LED in runtime. Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 + drivers/leds/Kconfig | 8 + drivers/leds/Makefile | 1 + drivers/leds/leds-nuc.c | 481 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 497 insertions(+) create mode 100644 drivers/leds/leds-nuc.c diff --git a/MAINTAINERS b/MAINTAINERS index bd7aff0c120f..316f0e552ca6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13063,6 +13063,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/aia21/ntfs.git F: Documentation/filesystems/ntfs.rst F: fs/ntfs/ +NUC LED DRIVER +M: Mauro Carvalho Chehab +L: linux-leds@vger.kernel.org +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git +F: drivers/staging/nuc-led + NUBUS SUBSYSTEM M: Finn Thain L: linux-m68k@lists.linux-m68k.org diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 49d99cb084db..f5b7f7a02df5 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -271,6 +271,14 @@ config LEDS_MT6323 This option enables support for on-chip LED drivers found on Mediatek MT6323 PMIC. +config LEDS_NUC_WMI + tristate "LED Support for Intel NUC" + depends on LEDS_CLASS + depends on ACPI_WMI + help + This option enables support for the WMI interface for LEDs + present on certain Intel NUC models. + config LEDS_S3C24XX tristate "LED Support for Samsung S3C24XX GPIO LEDs" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 7e604d3028c8..11a4d29bf9a0 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o +obj-$(CONFIG_LEDS_NUC_WMI) += leds-nuc.o obj-$(CONFIG_LEDS_NS2) += leds-ns2.o obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c new file mode 100644 index 000000000000..69bab319122e --- /dev/null +++ b/drivers/leds/leds-nuc.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Intel NUC WMI Control WMI Driver + * + * Currently, it implements only the LED support + * + * Copyright(c) 2021 Mauro Carvalho Chehab + * + * Inspired on WMI from https://github.com/nomego/intel_nuc_led + * + * It follows this spec: + * https://www.intel.com/content/dam/support/us/en/documents/intel-nuc/WMI-Spec-Intel-NUC-NUC10ixFNx.pdf + */ + +#include +#include +#include +#include +#include +#include +#include + +#define NUC_LED_WMI_GUID "8C5DA44C-CDC3-46B3-8619-4E26D34390B7" + +#define MAX_LEDS 7 +#define NUM_INPUT_ARGS 4 +#define NUM_OUTPUT_ARGS 3 + +enum led_cmds { + LED_QUERY = 0x03, + LED_NEW_GET_STATUS = 0x04, + LED_SET_INDICATOR = 0x05, + LED_SET_VALUE = 0x06, + LED_NOTIFICATION = 0x07, + LED_SWITCH_TYPE = 0x08, +}; + +enum led_query_subcmd { + LED_QUERY_LIST_ALL = 0x00, + LED_QUERY_COLOR_TYPE = 0x01, + LED_QUERY_INDICATOR_OPTIONS = 0x02, + LED_QUERY_CONTROL_ITEMS = 0x03, +}; + +enum led_new_get_subcmd { + LED_NEW_GET_CURRENT_INDICATOR = 0x00, + LED_NEW_GET_CONTROL_ITEM = 0x01, +}; + +/* LED color indicator */ +#define LED_BLUE_AMBER BIT(0) +#define LED_BLUE_WHITE BIT(1) +#define LED_RGB BIT(2) +#define LED_SINGLE_COLOR BIT(3) + +/* LED indicator options */ +#define LED_IND_POWER_STATE BIT(0) +#define LED_IND_HDD_ACTIVITY BIT(1) +#define LED_IND_ETHERNET BIT(2) +#define LED_IND_WIFI BIT(3) +#define LED_IND_SOFTWARE BIT(4) +#define LED_IND_POWER_LIMIT BIT(5) +#define LED_IND_DISABLE BIT(6) + +static const char * const led_names[] = { + "nuc::power", + "nuc::hdd", + "nuc::skull", + "nuc::eyes", + "nuc::front1", + "nuc::front2", + "nuc::front3", +}; + +struct nuc_nmi_led { + struct led_classdev cdev; + struct device *dev; + u8 id; + u8 indicator; + u32 color_type; + u32 avail_indicators; + u32 control_items; +}; + +struct nuc_wmi { + struct nuc_nmi_led led[MAX_LEDS * 3]; /* Worse case: RGB LEDs */ + int num_leds; + + /* Avoid concurrent access to WMI */ + struct mutex wmi_lock; +}; + +static int nuc_nmi_led_error(u8 error_code) +{ + switch (error_code) { + case 0: + return 0; + case 0xe1: /* Function not support */ + return -ENOENT; + case 0xe2: /* Undefined device */ + return -ENODEV; + case 0xe3: /* EC no respond */ + return -EIO; + case 0xe4: /* Invalid Parameter */ + return -EINVAL; + case 0xef: /* Unexpected error */ + return -EFAULT; + + /* Revision 1.0 Errors */ + case 0xe5: /* Node busy */ + return -EBUSY; + case 0xe6: /* Destination device is disabled or unavailable */ + return -EACCES; + case 0xe7: /* Invalid CEC Opcode */ + return -ENOENT; + case 0xe8: /* Data Buffer size is not enough */ + return -ENOSPC; + + default: /* Reserved */ + return -EPROTO; + } +} + +static int nuc_nmi_cmd(struct device *dev, + u8 cmd, + u8 input_args[NUM_INPUT_ARGS], + u8 output_args[NUM_OUTPUT_ARGS]) +{ + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct nuc_wmi *priv = dev_get_drvdata(dev); + struct acpi_buffer input; + union acpi_object *obj; + acpi_status status; + int size, ret; + u8 *p; + + input.length = NUM_INPUT_ARGS; + input.pointer = input_args; + + mutex_lock(&priv->wmi_lock); + status = wmi_evaluate_method(NUC_LED_WMI_GUID, 0, cmd, + &input, &output); + mutex_unlock(&priv->wmi_lock); + if (ACPI_FAILURE(status)) { + dev_warn(dev, "cmd %02x (%*ph): ACPI failure: %d\n", + cmd, (int)input.length, input_args, ret); + return status; + } + + obj = output.pointer; + if (!obj) { + dev_warn(dev, "cmd %02x (%*ph): no output\n", + cmd, (int)input.length, input_args); + return -EINVAL; + } + + if (obj->type == ACPI_TYPE_BUFFER) { + if (obj->buffer.length < NUM_OUTPUT_ARGS + 1) { + ret = -EINVAL; + goto err; + } + p = (u8 *)obj->buffer.pointer; + } else if (obj->type == ACPI_TYPE_INTEGER) { + p = (u8 *)&obj->integer.value; + } else { + return -EINVAL; + } + + ret = nuc_nmi_led_error(p[0]); + if (ret) { + dev_warn(dev, "cmd %02x (%*ph): WMI error code: %02x\n", + cmd, (int)input.length, input_args, p[0]); + goto err; + } + + size = NUM_OUTPUT_ARGS + 1; + + if (output_args) { + memcpy(output_args, p + 1, NUM_OUTPUT_ARGS); + + dev_info(dev, "cmd %02x (%*ph), return: %*ph\n", + cmd, (int)input.length, input_args, NUM_OUTPUT_ARGS, output_args); + } else { + dev_info(dev, "cmd %02x (%*ph)\n", + cmd, (int)input.length, input_args); + } + +err: + kfree(obj); + return ret; +} + +static int nuc_wmi_query_leds(struct device *dev) +{ + struct nuc_wmi *priv = dev_get_drvdata(dev); + u8 cmd, input[NUM_INPUT_ARGS] = { 0 }; + u8 output[NUM_OUTPUT_ARGS]; + int i, id, ret; + u8 leds; + + /* + * List all LED types support in the platform + * + * Should work with both NUC8iXXX and NUC10iXXX + * + * FIXME: Should add a fallback code for it to work with older NUCs, + * as LED_QUERY returns an error on older devices like Skull Canyon. + */ + cmd = LED_QUERY; + input[0] = LED_QUERY_LIST_ALL; + ret = nuc_nmi_cmd(dev, cmd, input, output); + if (ret) { + dev_warn(dev, "error %d while listing all LEDs\n", ret); + return ret; + } + + leds = output[0]; + if (!leds) { + dev_warn(dev, "No LEDs found\n"); + return -ENODEV; + } + + for (id = 0; id < MAX_LEDS; id++) { + struct nuc_nmi_led *led = &priv->led[priv->num_leds]; + + if (!(leds & BIT(id))) + continue; + + led->id = id; + + cmd = LED_QUERY; + input[0] = LED_QUERY_COLOR_TYPE; + input[1] = id; + ret = nuc_nmi_cmd(dev, cmd, input, output); + if (ret) { + dev_warn(dev, "error %d on led %i\n", ret, i); + return ret; + } + + led->color_type = output[0] | + output[1] << 8 | + output[2] << 16; + + cmd = LED_NEW_GET_STATUS; + input[0] = LED_NEW_GET_CURRENT_INDICATOR; + input[1] = i; + ret = nuc_nmi_cmd(dev, cmd, input, output); + if (ret) { + dev_warn(dev, "error %d on led %i\n", ret, i); + return ret; + } + + led->indicator = output[0]; + + cmd = LED_QUERY; + input[0] = LED_QUERY_INDICATOR_OPTIONS; + input[1] = i; + ret = nuc_nmi_cmd(dev, cmd, input, output); + if (ret) { + dev_warn(dev, "error %d on led %i\n", ret, i); + return ret; + } + + led->avail_indicators = output[0] | + output[1] << 8 | + output[2] << 16; + + cmd = LED_QUERY; + input[0] = LED_QUERY_CONTROL_ITEMS; + input[1] = i; + input[2] = led->indicator; + ret = nuc_nmi_cmd(dev, cmd, input, output); + if (ret) { + dev_warn(dev, "error %d on led %i\n", ret, i); + return ret; + } + + led->control_items = output[0] | + output[1] << 8 | + output[2] << 16; + + dev_dbg(dev, "%s: id: %02x, color type: %06x, indicator: %06x, control items: %06x\n", + led_names[led->id], led->id, + led->color_type, led->indicator, led->control_items); + + priv->num_leds++; + } + + return 0; +} + +/* + * LED show/store routines + */ + +#define LED_ATTR_RW(_name) \ + DEVICE_ATTR(_name, 0644, show_##_name, store_##_name) + +static const char * const led_indicators[] = { + "Power State", + "HDD Activity", + "Ethernet", + "WiFi", + "Software", + "Power Limit", + "Disable" +}; + +static ssize_t show_indicator(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + int size = PAGE_SIZE; + char *p = buf; + int i, n; + + for (i = 0; i < fls(led->avail_indicators); i++) { + if (!(led->avail_indicators & BIT(i))) + continue; + if (i == led->indicator) + n = scnprintf(p, size, "[%s] ", led_indicators[i]); + else + n = scnprintf(p, size, "%s ", led_indicators[i]); + p += n; + size -= n; + } + size -= scnprintf(p, size, "\n"); + + return PAGE_SIZE - size; +} + +static ssize_t store_indicator(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 cmd, input[NUM_INPUT_ARGS] = { 0 }; + const char *tmp; + int ret, i; + + tmp = strsep((char **)&buf, "\n"); + + for (i = 0; i < fls(led->avail_indicators); i++) { + if (!(led->avail_indicators & BIT(i))) + continue; + + if (!strcasecmp(tmp, led_indicators[i])) { + cmd = LED_SET_INDICATOR; + input[0] = led->id; + input[1] = i; + + dev_dbg(dev, "set led %s indicator to %s\n", + cdev->name, led_indicators[i]); + + ret = nuc_nmi_cmd(dev, cmd, input, NULL); + if (ret) + return ret; + + led->indicator = i; + + return len; + } + } + + return -EINVAL; +} + +static LED_ATTR_RW(indicator); + +/* + * Attributes for multicolor LEDs + */ + +static struct attribute *nuc_wmi_multicolor_led_attr[] = { + &dev_attr_indicator.attr, + NULL, +}; + +static const struct attribute_group nuc_wmi_led_attribute_group = { + .attrs = nuc_wmi_multicolor_led_attr, +}; + +static const struct attribute_group *nuc_wmi_led_attribute_groups[] = { + &nuc_wmi_led_attribute_group, + NULL +}; + +static int nuc_wmi_led_register(struct device *dev, struct nuc_nmi_led *led) +{ + led->cdev.name = led_names[led->id]; + + led->dev = dev; + led->cdev.groups = nuc_wmi_led_attribute_groups; + + /* + * It can't let the classdev to manage the brightness due to several + * reasons: + * + * 1) classdev has some internal logic to manage the brightness, + * at set_brightness_delayed(), which starts disabling the LEDs; + * While this makes sense on most cases, here, it would appear + * that the NUC was powered off, which is not what happens; + * 2) classdev unconditionally tries to set brightness for all + * leds, including the ones that were software-disabled or + * disabled disabled via BIOS menu; + * 3) There are 3 types of brightness values for each LED, depending + * on the CPU power state: S0, S3 and S5. + * + * So, the best seems to export everything via sysfs attributes + * directly. This would require some further changes at the + * LED class, though, or we would need to create our own LED + * class, which seems wrong. + */ + + return devm_led_classdev_register(dev, &led->cdev); +} + +static int nuc_wmi_leds_setup(struct device *dev) +{ + struct nuc_wmi *priv = dev_get_drvdata(dev); + int ret, i; + + ret = nuc_wmi_query_leds(dev); + if (ret) + return ret; + + for (i = 0; i < priv->num_leds; i++) { + ret = nuc_wmi_led_register(dev, &priv->led[i]); + if (ret) { + dev_err(dev, "Failed to register led %d: %s\n", + i, led_names[priv->led[i].id]); + while (--i >= 0) + devm_led_classdev_unregister(dev, &priv->led[i].cdev); + + return ret; + } + } + return 0; +} + +static int nuc_wmi_probe(struct wmi_device *wdev, const void *context) +{ + struct device *dev = &wdev->dev; + struct nuc_wmi *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + mutex_init(&priv->wmi_lock); + + dev_set_drvdata(dev, priv); + + ret = nuc_wmi_leds_setup(dev); + if (ret) + return ret; + + dev_info(dev, "NUC WMI driver initialized.\n"); + return 0; +} + +static const struct wmi_device_id nuc_wmi_descriptor_id_table[] = { + { .guid_string = NUC_LED_WMI_GUID }, + { }, +}; + +static struct wmi_driver nuc_wmi_driver = { + .driver = { + .name = "nuc-wmi", + }, + .probe = nuc_wmi_probe, + .id_table = nuc_wmi_descriptor_id_table, +}; + +module_wmi_driver(nuc_wmi_driver); + +MODULE_DEVICE_TABLE(wmi, nuc_wmi_descriptor_id_table); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_DESCRIPTION("Intel NUC WMI LED driver"); +MODULE_LICENSE("GPL"); From patchwork Tue May 18 15:08:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 442853 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id CBD63C433B4 for ; Tue, 18 May 2021 15:09:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A6FCC61364 for ; Tue, 18 May 2021 15:09:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245562AbhERPK2 (ORCPT ); Tue, 18 May 2021 11:10:28 -0400 Received: from mail.kernel.org ([198.145.29.99]:49412 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237974AbhERPK2 (ORCPT ); Tue, 18 May 2021 11:10:28 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 1C4AC61042; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=IPk6MBpB/XcYX8hxt1d+r0HxwF2eMb4v4R5nHNEAMoA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=o9MbXM51OrGYylej2Azv0sWdy0oIXvObDT9T1v/FiizWiyNlbwE2kaQmcgJr0CW2d EUzyBl8A35i409JCm22FvwTjF0KkY34l3ScM3VzOERiTMMBUgm9PV/GQ+bcIRshywi DFixOHyA9y+PoJKBWE2IgI49Qggjm3opWocxYgJannksbgI5ElhYkaKE8xCNB0MLVs Jole2D+301QF8mp8GoIcPxxE3LRdFrtSsUmHkc2v73ESPUUnZI7ObWozbJRdN5+Bzm D7kAMlrU4tm3ta1M6bjHCZ3ln0MaKzvuN1ajkWAB4MY2v81NANduVXhRlfTPlWATjp g5l2sHDVFyfBQ== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HO6-2k; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 03/17] leds: leds-nuc: detect WMI API detection Date: Tue, 18 May 2021 17:08:52 +0200 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org There are currently 3 known API releases: - https://www.intel.com/content/www/us/en/support/articles/000023426/intel-nuc/intel-nuc-kits.html - https://raw.githubusercontent.com/nomego/intel_nuc_led/master/specs/INTEL_WMI_LED_0.64.pdf - https://www.intel.com/content/dam/support/us/en/documents/intel-nuc/WMI-Spec-Intel-NUC-NUC10ixFNx.pdf Add a logic to detect between them, preventing the driver to work with an unsupported version. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index 69bab319122e..26bc4a4bb57c 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -26,6 +26,13 @@ #define NUM_INPUT_ARGS 4 #define NUM_OUTPUT_ARGS 3 +enum led_api_rev { + LED_API_UNKNOWN, + LED_API_NUC6, + LED_API_REV_0_64, + LED_API_REV_1_0, +}; + enum led_cmds { LED_QUERY = 0x03, LED_NEW_GET_STATUS = 0x04, @@ -33,6 +40,7 @@ enum led_cmds { LED_SET_VALUE = 0x06, LED_NOTIFICATION = 0x07, LED_SWITCH_TYPE = 0x08, + LED_VERSION_CONTROL = 0x09, }; enum led_query_subcmd { @@ -195,7 +203,7 @@ static int nuc_wmi_query_leds(struct device *dev) struct nuc_wmi *priv = dev_get_drvdata(dev); u8 cmd, input[NUM_INPUT_ARGS] = { 0 }; u8 output[NUM_OUTPUT_ARGS]; - int i, id, ret; + int i, id, ret, ver = LED_API_UNKNOWN; u8 leds; /* @@ -209,12 +217,31 @@ static int nuc_wmi_query_leds(struct device *dev) cmd = LED_QUERY; input[0] = LED_QUERY_LIST_ALL; ret = nuc_nmi_cmd(dev, cmd, input, output); - if (ret) { + if (ret == -ENOENT) { + ver = LED_API_NUC6; + } else if (ret) { dev_warn(dev, "error %d while listing all LEDs\n", ret); return ret; + } else { + leds = output[0]; } - leds = output[0]; + if (ver != LED_API_NUC6) { + ret = nuc_nmi_cmd(dev, LED_VERSION_CONTROL, input, output); + if (ret) + return ret; + + ver = output[0] | output[1] << 16; + if (!ver) + ver = LED_API_REV_0_64; + else if (ver == 0x0126) + ver = LED_API_REV_1_0; + } + + /* Currently, only API Revision 0.64 is supported */ + if (ver != LED_API_REV_0_64) + return -ENODEV; + if (!leds) { dev_warn(dev, "No LEDs found\n"); return -ENODEV; From patchwork Tue May 18 15:08:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 442852 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AE91EC433ED for ; Tue, 18 May 2021 15:09:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 912D261364 for ; Tue, 18 May 2021 15:09:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344573AbhERPKb (ORCPT ); Tue, 18 May 2021 11:10:31 -0400 Received: from mail.kernel.org ([198.145.29.99]:49434 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242078AbhERPK2 (ORCPT ); Tue, 18 May 2021 11:10:28 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 2CEF56135F; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=zalE66nfGhQobchmfKxHkP0DtHaHCBxHlfx2X/n5+SI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=t9+aBhqb3vZJzssCN62R+RRU2EA8QxHzEqL1ecvAHVqmnlikdlrj4jtpXVx7ZUKPg of5X9EoeGL6/UZ/wsM4A1jtUKx1TC4Bitz4xgLUYnlKXXZqa+12dR8jnCiGdxBJFRF BfiYIlzvpXM/iXOe0F3rqveSMtoNlIoopdniGgjP/l1DO4l1fvewySEMvTK8PBIqxH VGid3j+pg4NjOLKF3spmTWhowrpVQn7jAaJRsX7ifOzt5kOb8SYhfhUMvwOm5Skztj +jn7znsE7DtBibjlbDD4WfFCenkIzyPL/wtb8fe3JYLCBkZ4cUZPnkHKnP9WGPp+91 kN+l/+CJGeq2Q== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOA-3x; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 04/17] leds: leds-nuc: add support for changing S0 brightness Date: Tue, 18 May 2021 17:08:53 +0200 Message-Id: <443c059cc4ff153bf95cb36ed02a34d2d57e20c9.1621349814.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Now that the core logic is in place, let's add support to adjust the S0 brightness level. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 79 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index 26bc4a4bb57c..e12fa2e7a488 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -395,7 +395,85 @@ static ssize_t store_indicator(struct device *dev, return -EINVAL; } +/* Get S0 brightness */ +static ssize_t show_s0_brightness(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 cmd, input[NUM_INPUT_ARGS] = { 0 }; + u8 output[NUM_OUTPUT_ARGS]; + int ret; + + cmd = LED_NEW_GET_STATUS; + input[0] = LED_NEW_GET_CONTROL_ITEM; + input[1] = led->id; + input[2] = led->indicator; + input[3] = 0; + + ret = nuc_nmi_cmd(dev, cmd, input, output); + if (ret) + return ret; + + /* Multicolor uses a scale from 0 to 100 */ + if (led->color_type & (LED_BLUE_AMBER | LED_BLUE_WHITE | LED_RGB)) + return scnprintf(buf, PAGE_SIZE, "%d%%\n", output[0]); + + /* single color uses 0, 50% and 100% */ + return scnprintf(buf, PAGE_SIZE, "%d%%\n", output[0] * 50); +} + +/* Change S0 brightness */ +static ssize_t store_s0_brightness(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 cmd, input[NUM_INPUT_ARGS] = { 0 }; + int ret; + u8 val; + + if (led->indicator == LED_IND_DISABLE) { + dev_dbg(dev, "Led %s is disabled. ignoring it.\n", cdev->name); + return -EACCES; + } + + if (kstrtou8(buf, 0, &val) || val > 100) + return -EINVAL; + + /* + * For single-color LEDs, the value should be between 0 to 2, but, + * in order to have a consistent API, let's always handle it as if + * it is a percentage, for both multicolor and single color LEDs. + * So: + * value == 0 will disable the LED + * value up to 74% will set it the brightness to 50% + * value equal or above 75% will use the maximum brightness. + */ + if (!(led->color_type & (LED_BLUE_AMBER | LED_BLUE_WHITE | LED_RGB))) { + if (val > 0 && val < 75) + val = 1; + if (val >= 75) + val = 2; + } + + cmd = LED_SET_VALUE; + input[0] = led->id; + input[1] = led->indicator; + input[2] = 0; /* FIXME: replace by an enum */ + input[3] = val; + + ret = nuc_nmi_cmd(dev, cmd, input, NULL); + if (ret) + return ret; + + return len; +} + static LED_ATTR_RW(indicator); +static LED_ATTR_RW(s0_brightness); /* * Attributes for multicolor LEDs @@ -403,6 +481,7 @@ static LED_ATTR_RW(indicator); static struct attribute *nuc_wmi_multicolor_led_attr[] = { &dev_attr_indicator.attr, + &dev_attr_s0_brightness.attr, NULL, }; From patchwork Tue May 18 15:08:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 441507 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 005B1C433ED for ; Tue, 18 May 2021 15:10:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CDB3D611CC for ; Tue, 18 May 2021 15:09:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244656AbhERPLO (ORCPT ); Tue, 18 May 2021 11:11:14 -0400 Received: from mail.kernel.org ([198.145.29.99]:49660 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345056AbhERPKh (ORCPT ); Tue, 18 May 2021 11:10:37 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 2A64561355; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=gLdwdgyLLaR+xP/SNPgHYHTQ7ptcIkwGqK9Ot6um84c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Is0Csuf7ynpzMQrsLhZbi7o6qiZMtO/rOt+f8EysmnH2KLFTIj1tfcXsZ/aJNBKrR ya4+psHNNowXiqDJ9eYXQTiJbIXJusl9Xs1nxE6TIXU7fprNcgB1hTjkVkb9L1A7mo B/Ki5VrZ4Q788tAFASu7VPXubZjI0FOYOYaaWR3LP6imNaUXEOAQ2+1lpv4Fc8/AI+ 9kfoTCzSpPDtIJYZ6+d0IBCrmoKquO6D5RizmJPAHg4T2Jh33Vmm7/y+Ejv31v3a7K YYnurB0sbkBceG8YR9nS70/Y/rxDxTYj2oVjKfQzW492cP3lUoNtNv0RuQhfevUFVa Beuty5tMGZoFw== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOE-5B; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 05/17] leds: leds-nuc: add all types of brightness Date: Tue, 18 May 2021 17:08:54 +0200 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Improve the logic in order to support not only S0 brightness, but also the brightness for other indicators and for all power states. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 369 +++++++++++++++++++++++++++------------- 1 file changed, 249 insertions(+), 120 deletions(-) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index e12fa2e7a488..df65bf17e0e6 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -55,21 +55,89 @@ enum led_new_get_subcmd { LED_NEW_GET_CONTROL_ITEM = 0x01, }; +enum led_function { + LED_FUNC_BRIGHTNESS, + LED_FUNC_COLOR1, + LED_FUNC_COLOR_GREEN, + LED_FUNC_COLOR_BLUE, + + LED_FUNC_BLINK_BEHAVIOR, + LED_FUNC_BLINK_FREQ, + + LED_FUNC_HDD_BEHAVIOR, + LED_FUNC_ETH_TYPE, + LED_FUNC_POWER_LIMIT_SCHEME, + + MAX_LED_FUNC +}; + +enum led_indicators { + LED_IND_POWER_STATE, + LED_IND_HDD_ACTIVITY, + LED_IND_ETHERNET, + LED_IND_WIFI, + LED_IND_SOFTWARE, + LED_IND_POWER_LIMIT, + LED_IND_DISABLE, + + MAX_IND = LED_IND_DISABLE +}; + +/* + * control items ID for each of the valid indicators on spec Rev 0.64. + */ +static const u8 led_func_rev_0_64[MAX_IND][MAX_LED_FUNC] = { + [LED_IND_POWER_STATE] = { /* Offsets for each power state */ + [LED_FUNC_BRIGHTNESS] = 0x00, + [LED_FUNC_BLINK_BEHAVIOR] = 0x01, + [LED_FUNC_BLINK_FREQ] = 0x02, + [LED_FUNC_COLOR1] = 0x03, + [LED_FUNC_COLOR_GREEN] = 0x04, + [LED_FUNC_COLOR_BLUE] = 0x05 + }, + [LED_IND_HDD_ACTIVITY] = { + [LED_FUNC_BRIGHTNESS] = 0x00, + [LED_FUNC_COLOR1] = 0x01, + [LED_FUNC_COLOR_GREEN] = 0x02, + [LED_FUNC_COLOR_BLUE] = 0x03, + [LED_FUNC_HDD_BEHAVIOR] = 0x04 + }, + [LED_IND_ETHERNET] = { + [LED_FUNC_ETH_TYPE] = 0x00, + [LED_FUNC_BRIGHTNESS] = 0x01, + [LED_FUNC_COLOR1] = 0x02, + [LED_FUNC_COLOR_GREEN] = 0x03, + [LED_FUNC_COLOR_BLUE] = 0x04 + }, + [LED_IND_WIFI] = { + [LED_FUNC_BRIGHTNESS] = 0x00, + [LED_FUNC_COLOR1] = 0x01, + [LED_FUNC_COLOR_GREEN] = 0x02, + [LED_FUNC_COLOR_BLUE] = 0x03 + }, + [LED_IND_SOFTWARE] = { + [LED_FUNC_BRIGHTNESS] = 0x00, + [LED_FUNC_BLINK_BEHAVIOR] = 0x01, + [LED_FUNC_BLINK_FREQ] = 0x02, + [LED_FUNC_COLOR1] = 0x03, + [LED_FUNC_COLOR_GREEN] = 0x04, + [LED_FUNC_COLOR_BLUE] = 0x05 + }, + [LED_IND_POWER_LIMIT] = { + [LED_FUNC_POWER_LIMIT_SCHEME] = 0x00, + [LED_FUNC_BRIGHTNESS] = 0x01, + [LED_FUNC_COLOR1] = 0x02, + [LED_FUNC_COLOR_GREEN] = 0x03, + [LED_FUNC_COLOR_BLUE] = 0x04 + }, +}; + /* LED color indicator */ #define LED_BLUE_AMBER BIT(0) #define LED_BLUE_WHITE BIT(1) #define LED_RGB BIT(2) #define LED_SINGLE_COLOR BIT(3) -/* LED indicator options */ -#define LED_IND_POWER_STATE BIT(0) -#define LED_IND_HDD_ACTIVITY BIT(1) -#define LED_IND_ETHERNET BIT(2) -#define LED_IND_WIFI BIT(3) -#define LED_IND_SOFTWARE BIT(4) -#define LED_IND_POWER_LIMIT BIT(5) -#define LED_IND_DISABLE BIT(6) - static const char * const led_names[] = { "nuc::power", "nuc::hdd", @@ -87,7 +155,6 @@ struct nuc_nmi_led { u8 indicator; u32 color_type; u32 avail_indicators; - u32 control_items; }; struct nuc_wmi { @@ -201,9 +268,9 @@ static int nuc_nmi_cmd(struct device *dev, static int nuc_wmi_query_leds(struct device *dev) { struct nuc_wmi *priv = dev_get_drvdata(dev); - u8 cmd, input[NUM_INPUT_ARGS] = { 0 }; + u8 input[NUM_INPUT_ARGS] = { 0 }; u8 output[NUM_OUTPUT_ARGS]; - int i, id, ret, ver = LED_API_UNKNOWN; + int id, ret, ver = LED_API_UNKNOWN; u8 leds; /* @@ -214,9 +281,8 @@ static int nuc_wmi_query_leds(struct device *dev) * FIXME: Should add a fallback code for it to work with older NUCs, * as LED_QUERY returns an error on older devices like Skull Canyon. */ - cmd = LED_QUERY; input[0] = LED_QUERY_LIST_ALL; - ret = nuc_nmi_cmd(dev, cmd, input, output); + ret = nuc_nmi_cmd(dev, LED_QUERY, input, output); if (ret == -ENOENT) { ver = LED_API_NUC6; } else if (ret) { @@ -255,12 +321,11 @@ static int nuc_wmi_query_leds(struct device *dev) led->id = id; - cmd = LED_QUERY; input[0] = LED_QUERY_COLOR_TYPE; input[1] = id; - ret = nuc_nmi_cmd(dev, cmd, input, output); + ret = nuc_nmi_cmd(dev, LED_QUERY, input, output); if (ret) { - dev_warn(dev, "error %d on led %i\n", ret, i); + dev_warn(dev, "error %d on led %i\n", ret, id); return ret; } @@ -268,23 +333,11 @@ static int nuc_wmi_query_leds(struct device *dev) output[1] << 8 | output[2] << 16; - cmd = LED_NEW_GET_STATUS; - input[0] = LED_NEW_GET_CURRENT_INDICATOR; - input[1] = i; - ret = nuc_nmi_cmd(dev, cmd, input, output); - if (ret) { - dev_warn(dev, "error %d on led %i\n", ret, i); - return ret; - } - - led->indicator = output[0]; - - cmd = LED_QUERY; input[0] = LED_QUERY_INDICATOR_OPTIONS; - input[1] = i; - ret = nuc_nmi_cmd(dev, cmd, input, output); + input[1] = id; + ret = nuc_nmi_cmd(dev, LED_QUERY, input, output); if (ret) { - dev_warn(dev, "error %d on led %i\n", ret, i); + dev_warn(dev, "error %d on led %i\n", ret, id); return ret; } @@ -292,23 +345,18 @@ static int nuc_wmi_query_leds(struct device *dev) output[1] << 8 | output[2] << 16; - cmd = LED_QUERY; - input[0] = LED_QUERY_CONTROL_ITEMS; - input[1] = i; - input[2] = led->indicator; - ret = nuc_nmi_cmd(dev, cmd, input, output); + input[0] = LED_NEW_GET_CURRENT_INDICATOR; + input[1] = id; + ret = nuc_nmi_cmd(dev, LED_NEW_GET_STATUS, input, output); if (ret) { - dev_warn(dev, "error %d on led %i\n", ret, i); + dev_warn(dev, "error %d on led %i\n", ret, id); return ret; } + led->indicator = output[0]; - led->control_items = output[0] | - output[1] << 8 | - output[2] << 16; - - dev_dbg(dev, "%s: id: %02x, color type: %06x, indicator: %06x, control items: %06x\n", - led_names[led->id], led->id, - led->color_type, led->indicator, led->control_items); + dev_dbg(dev, "%s: id: %02x, color type: %06x, indicator: %02x (avail %06x)\n", + led_names[led->id], led->id, led->color_type, + led->indicator, led->avail_indicators); priv->num_leds++; } @@ -316,6 +364,82 @@ static int nuc_wmi_query_leds(struct device *dev) return 0; } +static bool nuc_wmi_test_control(struct device *dev, + struct nuc_nmi_led *led, u8 ctrl) +{ + int ret, avail_ctrls; + u8 output[NUM_OUTPUT_ARGS]; + u8 input[NUM_INPUT_ARGS] = { + LED_QUERY_CONTROL_ITEMS, + led->id, + led->indicator + }; + + ret = nuc_nmi_cmd(dev, LED_QUERY, input, output); + if (ret) + return false; + + avail_ctrls = output[0] | + output[1] << 8 | + output[2] << 16; + + return avail_ctrls & BIT(ctrl); +} + +static int nuc_wmi_get_brightness_offset(struct device *dev, + struct nuc_nmi_led *led, u8 offset) +{ + u8 input[NUM_INPUT_ARGS]; + u8 output[NUM_OUTPUT_ARGS]; + int ret, ctrl; + + if (led->indicator == LED_IND_DISABLE) + return -ENODEV; + + ctrl = led_func_rev_0_64[led->indicator][LED_FUNC_BRIGHTNESS] + offset; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + input[0] = LED_NEW_GET_CONTROL_ITEM; + input[1] = led->id; + input[2] = led->indicator; + input[3] = ctrl; + + ret = nuc_nmi_cmd(dev, LED_NEW_GET_STATUS, input, output); + if (ret) + return ret; + + dev_dbg(dev, "%s: id: %02x, brightness: %02x\n", + led_names[led->id], led->id, output[0]); + + return output[0]; +} + +static ssize_t nuc_wmi_set_brightness_offset(struct device *dev, + struct nuc_nmi_led *led, + u8 offset, + u8 val) +{ + u8 input[NUM_INPUT_ARGS]; + int ctrl; + + if (led->indicator == LED_IND_DISABLE) + return -ENODEV; + + ctrl = led_func_rev_0_64[led->indicator][LED_FUNC_BRIGHTNESS] + offset; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + input[0] = led->id; + input[1] = led->indicator; + input[2] = ctrl; + input[3] = val; + + return nuc_nmi_cmd(dev, LED_SET_VALUE, input, NULL); +} + /* * LED show/store routines */ @@ -323,6 +447,21 @@ static int nuc_wmi_query_leds(struct device *dev) #define LED_ATTR_RW(_name) \ DEVICE_ATTR(_name, 0644, show_##_name, store_##_name) +#define LED_ATTR_POWER_STATE_RW(_name, offset) \ + static ssize_t show_##_name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ + { \ + return show_brightness_offset(dev, attr, offset, buf); \ + } \ + static ssize_t store_##_name(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ + { \ + return store_brightness_offset(dev, attr, offset, buf, len); \ + } \ + static DEVICE_ATTR(_name, 0644, show_##_name, store_##_name) + static const char * const led_indicators[] = { "Power State", "HDD Activity", @@ -395,98 +534,93 @@ static ssize_t store_indicator(struct device *dev, return -EINVAL; } -/* Get S0 brightness */ -static ssize_t show_s0_brightness(struct device *dev, - struct device_attribute *attr, - char *buf) +/* Get brightness */ +static ssize_t show_brightness_offset(struct device *dev, + struct device_attribute *attr, + u8 offset, + char *buf) { struct led_classdev *cdev = dev_get_drvdata(dev); struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); - u8 cmd, input[NUM_INPUT_ARGS] = { 0 }; - u8 output[NUM_OUTPUT_ARGS]; int ret; - cmd = LED_NEW_GET_STATUS; - input[0] = LED_NEW_GET_CONTROL_ITEM; - input[1] = led->id; - input[2] = led->indicator; - input[3] = 0; + if (led->indicator != LED_IND_POWER_STATE) + return -ENODEV; - ret = nuc_nmi_cmd(dev, cmd, input, output); - if (ret) + ret = nuc_wmi_get_brightness_offset(dev, led, offset); + if (ret < 0) return ret; - /* Multicolor uses a scale from 0 to 100 */ - if (led->color_type & (LED_BLUE_AMBER | LED_BLUE_WHITE | LED_RGB)) - return scnprintf(buf, PAGE_SIZE, "%d%%\n", output[0]); - - /* single color uses 0, 50% and 100% */ - return scnprintf(buf, PAGE_SIZE, "%d%%\n", output[0] * 50); + return scnprintf(buf, PAGE_SIZE, "%d\n", ret); } -/* Change S0 brightness */ -static ssize_t store_s0_brightness(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) +/* Change brightness */ +static ssize_t store_brightness_offset(struct device *dev, + struct device_attribute *attr, + u8 offset, + const char *buf, size_t len) { struct led_classdev *cdev = dev_get_drvdata(dev); struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); - u8 cmd, input[NUM_INPUT_ARGS] = { 0 }; int ret; u8 val; - if (led->indicator == LED_IND_DISABLE) { - dev_dbg(dev, "Led %s is disabled. ignoring it.\n", cdev->name); - return -EACCES; - } + if (led->indicator != LED_IND_POWER_STATE) + return -ENODEV; if (kstrtou8(buf, 0, &val) || val > 100) return -EINVAL; - /* - * For single-color LEDs, the value should be between 0 to 2, but, - * in order to have a consistent API, let's always handle it as if - * it is a percentage, for both multicolor and single color LEDs. - * So: - * value == 0 will disable the LED - * value up to 74% will set it the brightness to 50% - * value equal or above 75% will use the maximum brightness. - */ - if (!(led->color_type & (LED_BLUE_AMBER | LED_BLUE_WHITE | LED_RGB))) { - if (val > 0 && val < 75) - val = 1; - if (val >= 75) - val = 2; - } - - cmd = LED_SET_VALUE; - input[0] = led->id; - input[1] = led->indicator; - input[2] = 0; /* FIXME: replace by an enum */ - input[3] = val; - - ret = nuc_nmi_cmd(dev, cmd, input, NULL); + ret = nuc_wmi_set_brightness_offset(dev, led, offset, val); if (ret) return ret; return len; } +static enum led_brightness nuc_wmi_get_brightness(struct led_classdev *cdev) +{ + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + + if (led->indicator == LED_IND_POWER_STATE) + return -ENODEV; + + return nuc_wmi_get_brightness_offset(cdev->dev, led, 0); +} + +static int nuc_wmi_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + + if (led->indicator == LED_IND_POWER_STATE) + return -ENODEV; + + return nuc_wmi_set_brightness_offset(cdev->dev, led, 0, brightness); +} + static LED_ATTR_RW(indicator); -static LED_ATTR_RW(s0_brightness); + +LED_ATTR_POWER_STATE_RW(s0_brightness, 0x00); +LED_ATTR_POWER_STATE_RW(s3_brightness, 0x06); +LED_ATTR_POWER_STATE_RW(s5_brightness, 0x0c); +LED_ATTR_POWER_STATE_RW(ready_mode_brightness, 0x12); /* - * Attributes for multicolor LEDs + * Attributes for LEDs */ -static struct attribute *nuc_wmi_multicolor_led_attr[] = { +static struct attribute *nuc_wmi_led_attr[] = { &dev_attr_indicator.attr, &dev_attr_s0_brightness.attr, + &dev_attr_s3_brightness.attr, + &dev_attr_s5_brightness.attr, + &dev_attr_ready_mode_brightness.attr, NULL, }; static const struct attribute_group nuc_wmi_led_attribute_group = { - .attrs = nuc_wmi_multicolor_led_attr, + .attrs = nuc_wmi_led_attr, }; static const struct attribute_group *nuc_wmi_led_attribute_groups[] = { @@ -496,30 +630,25 @@ static const struct attribute_group *nuc_wmi_led_attribute_groups[] = { static int nuc_wmi_led_register(struct device *dev, struct nuc_nmi_led *led) { + int brightness = nuc_wmi_get_brightness_offset(dev, led, 0); + led->cdev.name = led_names[led->id]; - led->dev = dev; led->cdev.groups = nuc_wmi_led_attribute_groups; + led->cdev.brightness_get = nuc_wmi_get_brightness; + led->cdev.brightness_set_blocking = nuc_wmi_set_brightness; - /* - * It can't let the classdev to manage the brightness due to several - * reasons: - * - * 1) classdev has some internal logic to manage the brightness, - * at set_brightness_delayed(), which starts disabling the LEDs; - * While this makes sense on most cases, here, it would appear - * that the NUC was powered off, which is not what happens; - * 2) classdev unconditionally tries to set brightness for all - * leds, including the ones that were software-disabled or - * disabled disabled via BIOS menu; - * 3) There are 3 types of brightness values for each LED, depending - * on the CPU power state: S0, S3 and S5. - * - * So, the best seems to export everything via sysfs attributes - * directly. This would require some further changes at the - * LED class, though, or we would need to create our own LED - * class, which seems wrong. - */ + if (led->color_type & LED_SINGLE_COLOR) + led->cdev.max_brightness = 2; + else + led->cdev.max_brightness = 100; + + /* Ensure that the current bright will be preserved */ + if (brightness >= 0) + led->cdev.delayed_set_value = brightness; + + /* Suppress warnings for the LED(s) indicating the power state */ + led->cdev.flags = LED_HW_PLUGGABLE | LED_UNREGISTERING; return devm_led_classdev_register(dev, &led->cdev); } From patchwork Tue May 18 15:08:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 442849 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 64097C433B4 for ; Tue, 18 May 2021 15:09:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4859561042 for ; Tue, 18 May 2021 15:09:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1350008AbhERPKz (ORCPT ); Tue, 18 May 2021 11:10:55 -0400 Received: from mail.kernel.org ([198.145.29.99]:49524 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343506AbhERPK3 (ORCPT ); Tue, 18 May 2021 11:10:29 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 26C536135C; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=0tES3bdt7Dfa21iZsJYbWnuV6/yUytq3HPpPGDOjRv4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hs46N4bQAY7ZbnQVgpgngh5EM1mhlWEFqutwHEtsW9UDx0nGkbmP5r830gV39wgUs 2B1VOEbazklk+VgHzys2eA/Aj7SDHaTzf6oxlMVCxmwHQ/ZM9boCHKCB1F9oPbDYFH 8DHFsvmkW58Ge/6JnZwU5c8pkdWV/NnYCiOoEmRZ7j1RMrOJV3grFS0P1IGdPaCBof msVhokcehbDeg1d05yCSpCz8BOXcvhAJobsKlHmZPEQMHbzBYPgAexD5vz4vzNqgvn HzyGWy+Rp4PV/fmLdGXDBgI2xaLCxmY9f2GFplYZ1EjKAMndxOsvqS4Uc/6s1RDBf1 vdUDt28s+kgiw== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOI-6d; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 06/17] leds: leds-nuc: allow changing the LED colors Date: Tue, 18 May 2021 17:08:55 +0200 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add routines to allow seeing and changing the LED colors. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 244 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 228 insertions(+), 16 deletions(-) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index df65bf17e0e6..415031d344c6 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -58,8 +58,6 @@ enum led_new_get_subcmd { enum led_function { LED_FUNC_BRIGHTNESS, LED_FUNC_COLOR1, - LED_FUNC_COLOR_GREEN, - LED_FUNC_COLOR_BLUE, LED_FUNC_BLINK_BEHAVIOR, LED_FUNC_BLINK_FREQ, @@ -92,43 +90,31 @@ static const u8 led_func_rev_0_64[MAX_IND][MAX_LED_FUNC] = { [LED_FUNC_BLINK_BEHAVIOR] = 0x01, [LED_FUNC_BLINK_FREQ] = 0x02, [LED_FUNC_COLOR1] = 0x03, - [LED_FUNC_COLOR_GREEN] = 0x04, - [LED_FUNC_COLOR_BLUE] = 0x05 }, [LED_IND_HDD_ACTIVITY] = { [LED_FUNC_BRIGHTNESS] = 0x00, [LED_FUNC_COLOR1] = 0x01, - [LED_FUNC_COLOR_GREEN] = 0x02, - [LED_FUNC_COLOR_BLUE] = 0x03, [LED_FUNC_HDD_BEHAVIOR] = 0x04 }, [LED_IND_ETHERNET] = { [LED_FUNC_ETH_TYPE] = 0x00, [LED_FUNC_BRIGHTNESS] = 0x01, [LED_FUNC_COLOR1] = 0x02, - [LED_FUNC_COLOR_GREEN] = 0x03, - [LED_FUNC_COLOR_BLUE] = 0x04 }, [LED_IND_WIFI] = { [LED_FUNC_BRIGHTNESS] = 0x00, [LED_FUNC_COLOR1] = 0x01, - [LED_FUNC_COLOR_GREEN] = 0x02, - [LED_FUNC_COLOR_BLUE] = 0x03 }, [LED_IND_SOFTWARE] = { [LED_FUNC_BRIGHTNESS] = 0x00, [LED_FUNC_BLINK_BEHAVIOR] = 0x01, [LED_FUNC_BLINK_FREQ] = 0x02, [LED_FUNC_COLOR1] = 0x03, - [LED_FUNC_COLOR_GREEN] = 0x04, - [LED_FUNC_COLOR_BLUE] = 0x05 }, [LED_IND_POWER_LIMIT] = { [LED_FUNC_POWER_LIMIT_SCHEME] = 0x00, [LED_FUNC_BRIGHTNESS] = 0x01, [LED_FUNC_COLOR1] = 0x02, - [LED_FUNC_COLOR_GREEN] = 0x03, - [LED_FUNC_COLOR_BLUE] = 0x04 }, }; @@ -462,6 +448,8 @@ static ssize_t nuc_wmi_set_brightness_offset(struct device *dev, } \ static DEVICE_ATTR(_name, 0644, show_##_name, store_##_name) +/* Show/change the LED indicator */ + static const char * const led_indicators[] = { "Power State", "HDD Activity", @@ -534,7 +522,220 @@ static ssize_t store_indicator(struct device *dev, return -EINVAL; } -/* Get brightness */ +/* Show/change the LED color */ + +enum led_colors { + LED_COLOR_BLUE, + LED_COLOR_AMBER, + LED_COLOR_WHITE +}; + +struct led_color_name { + const char *name; + u8 r, g, b; +}; + +static const struct led_color_name led_colors[] = { + /* The first colors should match the dual-LED colorset */ + [LED_COLOR_BLUE] = { "blue", 0, 0, 0xff }, + [LED_COLOR_AMBER] = { "amber", 0xff, 0xbf, 0 }, + [LED_COLOR_WHITE] = { "white", 0xff, 0xff, 0xff }, + + /* Let's add a couple of common color names as well */ + { "red", 0xff, 0, 0 }, + { "green", 0, 0xff, 0 }, + { "yellow", 0xff, 0xff, 0 }, + { "cyan", 0, 0xff, 0xff }, + { "magenta", 0xff, 0, 0xff }, +}; + +static ssize_t show_color(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS]; + u8 output[NUM_OUTPUT_ARGS]; + int ret, ctrl; + int size = PAGE_SIZE; + char *p = buf; + int color, r, g, b; + + if (led->indicator == LED_IND_DISABLE) + return -ENODEV; + + ctrl = led_func_rev_0_64[led->indicator][LED_FUNC_COLOR1]; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + input[0] = LED_NEW_GET_CONTROL_ITEM; + input[1] = led->id; + input[2] = led->indicator; + input[3] = ctrl; + + ret = nuc_nmi_cmd(dev, LED_NEW_GET_STATUS, input, output); + if (ret) + return ret; + + if (led->color_type & LED_RGB) { + r = output[0]; + + input[3]++; + ret = nuc_nmi_cmd(dev, LED_NEW_GET_STATUS, input, output); + if (ret) + return ret; + + g = output[0]; + + input[3]++; + ret = nuc_nmi_cmd(dev, LED_NEW_GET_STATUS, input, output); + if (ret) + return ret; + + b = output[0]; + + for (color = 0; color < ARRAY_SIZE(led_colors); color++) + if (led_colors[color].r == r && + led_colors[color].g == g && + led_colors[color].b == b) + return scnprintf(p, size, "%s\n", + led_colors[color].name); + + return scnprintf(p, size, "%d,%d,%d\n", r, g, b); + } + + if (!output[0]) + return scnprintf(p, size, "%s\n", + led_colors[LED_COLOR_BLUE].name); + + if (led->color_type & LED_BLUE_AMBER) + return scnprintf(p, size, "%s\n", + led_colors[LED_COLOR_AMBER].name); + + return scnprintf(p, size, "%s\n", led_colors[LED_COLOR_WHITE].name); +} + +static ssize_t store_color(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS] = { 0 }; + int ret, ctrl, color; + const char *tmp; + u8 r, g, b, val; + + if (led->indicator == LED_IND_DISABLE) + return -ENODEV; + + tmp = strsep((char **)&buf, ",\n"); + + for (color = 0; color < ARRAY_SIZE(led_colors); color++) + if (!strcasecmp(tmp, led_colors[color].name)) + break; + + if (color < ARRAY_SIZE(led_colors)) { + r = led_colors[color].r; + g = led_colors[color].g; + b = led_colors[color].b; + } else { + if (kstrtou8(tmp, 0, &r) || r > 255) + return -EINVAL; + + tmp = strsep((char **)&buf, ",\n"); + if (kstrtou8(tmp, 0, &g) || g > 255) + return -EINVAL; + + tmp = strsep((char **)&buf, " \n"); + if (kstrtou8(tmp, 0, &b) || b > 255) + return -EINVAL; + + if (led->color_type & LED_SINGLE_COLOR) { + for (color = 0; color <= LED_COLOR_WHITE; color++) + if (led_colors[color].r == r && + led_colors[color].g == g && + led_colors[color].b == b) + break; + } + } + + ctrl = led_func_rev_0_64[led->indicator][LED_FUNC_COLOR1]; + + /* Dual color LEDs */ + if (!(led->color_type & LED_RGB)) { + if (color == LED_COLOR_BLUE) + val = 0; + else { + if (led->color_type & LED_BLUE_AMBER && + color != LED_COLOR_AMBER) + return -EINVAL; + else if (color != LED_COLOR_WHITE) + return -EINVAL; + val =1; + } + + input[0] = led->id; + input[1] = led->indicator; + input[2] = ctrl; + input[3] = val; + + ret = nuc_nmi_cmd(dev, LED_SET_VALUE, input, NULL); + if (ret) + return ret; + + return len; + } + + /* RGB LEDs */ + input[0] = led->id; + input[1] = led->indicator; + input[2] = ctrl; + input[3] = r; + + ret = nuc_nmi_cmd(dev, LED_SET_VALUE, input, NULL); + if (ret) + return ret; + + input[0] = led->id; + input[1] = led->indicator; + input[2]++; + input[3] = g; + + ret = nuc_nmi_cmd(dev, LED_SET_VALUE, input, NULL); + if (ret) + return ret; + + input[0] = led->id; + input[1] = led->indicator; + input[2]++; + input[3] = b; + + ret = nuc_nmi_cmd(dev, LED_SET_VALUE, input, NULL); + if (ret) + return ret; + + return len; + + return -EINVAL; +} + +static umode_t nuc_wmi_led_color_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + umode_t mode = attr->mode; + + if (led->color_type & LED_SINGLE_COLOR) + return 0; + + return mode; +} + +/* Show/store brightness */ static ssize_t show_brightness_offset(struct device *dev, struct device_attribute *attr, u8 offset, @@ -554,7 +755,6 @@ static ssize_t show_brightness_offset(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%d\n", ret); } -/* Change brightness */ static ssize_t store_brightness_offset(struct device *dev, struct device_attribute *attr, u8 offset, @@ -600,6 +800,7 @@ static int nuc_wmi_set_brightness(struct led_classdev *cdev, } static LED_ATTR_RW(indicator); +static LED_ATTR_RW(color); LED_ATTR_POWER_STATE_RW(s0_brightness, 0x00); LED_ATTR_POWER_STATE_RW(s3_brightness, 0x06); @@ -623,8 +824,19 @@ static const struct attribute_group nuc_wmi_led_attribute_group = { .attrs = nuc_wmi_led_attr, }; +static struct attribute *nuc_wmi_led_color_attr[] = { + &dev_attr_color.attr, + NULL, +}; + +static const struct attribute_group nuc_wmi_led_color_attribute_group = { + .is_visible = nuc_wmi_led_color_is_visible, + .attrs = nuc_wmi_led_color_attr, +}; + static const struct attribute_group *nuc_wmi_led_attribute_groups[] = { &nuc_wmi_led_attribute_group, + &nuc_wmi_led_color_attribute_group, NULL }; From patchwork Tue May 18 15:08:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 441512 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8F696C43461 for ; Tue, 18 May 2021 15:09:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 713C861350 for ; Tue, 18 May 2021 15:09:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345155AbhERPKl (ORCPT ); Tue, 18 May 2021 11:10:41 -0400 Received: from mail.kernel.org ([198.145.29.99]:49496 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S244777AbhERPK2 (ORCPT ); Tue, 18 May 2021 11:10:28 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 16A6761261; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=pPHEVYhddjAKeLYMEumM836fer803bytTHNsI1Rz+eA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LVSVdD4AnBvIR4+63eAIMGjrG4Yx5e7Vif3NtwzbsrWlylDZXGzmKfPcuO07ZKCDb coZEMVjunhQVkdYk4exdX+ByCblai0YifMmn93l2MtpHGAuJosReUY/2lkWn2sU4qg XmTPYuNhXeuzMUNdDLg9ems3QjDXy8N+er98rJE6oibuOpQbi/9yTPaaBTYbL++faW gW3oZ+IlIKABl7v5ASMveZoiVnam4s+B7+DKwRrnKcnIt6ArMeNFRYHyFzqbJBuGpF dVBdbWbugiUAZ5NnKq6E1bAKPz+KMQPDWASlByPrmBkfIT7qDXnKnXX/v8D6HC90Ks 5sGoPSnqmkiGw== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOM-7o; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 07/17] leds: leds-nuc: add support for WMI API version 1.0 Date: Tue, 18 May 2021 17:08:56 +0200 Message-Id: <234ebbff2cb1d15634b5f10aa98e58c11d24a65c.1621349814.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org The control indicators for WMI version 1.0 (used on NUCi10 and above) are on different locations. The main difference is on single color LEDs. Also, the Power State brightness names are defined on a different way, and there are 3 groups instead of 4. As the driver was written with some tables to map the control option values, it is easy to extend it to support the new definitions: all we need to do is to add the V1.0 tables and ensure that the right table will be used. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 119 +++++++++++++++++++++++++++++++++------- 1 file changed, 99 insertions(+), 20 deletions(-) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index 415031d344c6..e0090626aeec 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -62,6 +62,7 @@ enum led_function { LED_FUNC_BLINK_BEHAVIOR, LED_FUNC_BLINK_FREQ, + LED_FUNC_POWER_STATE_NUM_CTRLS, LED_FUNC_HDD_BEHAVIOR, LED_FUNC_ETH_TYPE, LED_FUNC_POWER_LIMIT_SCHEME, @@ -84,8 +85,11 @@ enum led_indicators { /* * control items ID for each of the valid indicators on spec Rev 0.64. */ -static const u8 led_func_rev_0_64[MAX_IND][MAX_LED_FUNC] = { - [LED_IND_POWER_STATE] = { /* Offsets for each power state */ +static const u8 led_func_multicolor[MAX_IND][MAX_LED_FUNC] = { + [LED_IND_POWER_STATE] = { + [LED_FUNC_POWER_STATE_NUM_CTRLS] = 0x06, + + /* Offsets for each power state */ [LED_FUNC_BRIGHTNESS] = 0x00, [LED_FUNC_BLINK_BEHAVIOR] = 0x01, [LED_FUNC_BLINK_FREQ] = 0x02, @@ -118,6 +122,24 @@ static const u8 led_func_rev_0_64[MAX_IND][MAX_LED_FUNC] = { }, }; +static const u8 led_func_rev_1_0_singlecolor[MAX_IND][MAX_LED_FUNC] = { + [LED_IND_POWER_STATE] = { + [LED_FUNC_POWER_STATE_NUM_CTRLS] = 0x02, + + /* Offsets for each power state */ + [LED_FUNC_BRIGHTNESS] = 0x00, + [LED_FUNC_BLINK_BEHAVIOR] = 0x01, + }, + [LED_IND_HDD_ACTIVITY] = { + [LED_FUNC_BRIGHTNESS] = 0x00, + [LED_FUNC_HDD_BEHAVIOR] = 0x01 + }, + [LED_IND_SOFTWARE] = { + [LED_FUNC_BRIGHTNESS] = 0x00, + [LED_FUNC_BLINK_BEHAVIOR] = 0x01, + }, +}; + /* LED color indicator */ #define LED_BLUE_AMBER BIT(0) #define LED_BLUE_WHITE BIT(1) @@ -141,6 +163,9 @@ struct nuc_nmi_led { u8 indicator; u32 color_type; u32 avail_indicators; + enum led_api_rev api_rev; + + const u8 (*reg_table)[MAX_LED_FUNC]; }; struct nuc_wmi { @@ -251,7 +276,7 @@ static int nuc_nmi_cmd(struct device *dev, return ret; } -static int nuc_wmi_query_leds(struct device *dev) +static int nuc_wmi_query_leds(struct device *dev, enum led_api_rev *api_rev) { struct nuc_wmi *priv = dev_get_drvdata(dev); u8 input[NUM_INPUT_ARGS] = { 0 }; @@ -291,9 +316,11 @@ static int nuc_wmi_query_leds(struct device *dev) } /* Currently, only API Revision 0.64 is supported */ - if (ver != LED_API_REV_0_64) + if (ver != LED_API_REV_0_64 && ver != LED_API_REV_1_0) return -ENODEV; + *api_rev = ver; + if (!leds) { dev_warn(dev, "No LEDs found\n"); return -ENODEV; @@ -382,7 +409,7 @@ static int nuc_wmi_get_brightness_offset(struct device *dev, if (led->indicator == LED_IND_DISABLE) return -ENODEV; - ctrl = led_func_rev_0_64[led->indicator][LED_FUNC_BRIGHTNESS] + offset; + ctrl = led->reg_table[led->indicator][LED_FUNC_BRIGHTNESS] + offset; if (!nuc_wmi_test_control(dev, led, ctrl)) return -ENODEV; @@ -413,7 +440,7 @@ static ssize_t nuc_wmi_set_brightness_offset(struct device *dev, if (led->indicator == LED_IND_DISABLE) return -ENODEV; - ctrl = led_func_rev_0_64[led->indicator][LED_FUNC_BRIGHTNESS] + offset; + ctrl = led->reg_table[led->indicator][LED_FUNC_BRIGHTNESS] + offset; if (!nuc_wmi_test_control(dev, led, ctrl)) return -ENODEV; @@ -564,7 +591,7 @@ static ssize_t show_color(struct device *dev, if (led->indicator == LED_IND_DISABLE) return -ENODEV; - ctrl = led_func_rev_0_64[led->indicator][LED_FUNC_COLOR1]; + ctrl = led->reg_table[led->indicator][LED_FUNC_COLOR1]; if (!nuc_wmi_test_control(dev, led, ctrl)) return -ENODEV; @@ -661,7 +688,7 @@ static ssize_t store_color(struct device *dev, } } - ctrl = led_func_rev_0_64[led->indicator][LED_FUNC_COLOR1]; + ctrl = led->reg_table[led->indicator][LED_FUNC_COLOR1]; /* Dual color LEDs */ if (!(led->color_type & LED_RGB)) { @@ -748,6 +775,8 @@ static ssize_t show_brightness_offset(struct device *dev, if (led->indicator != LED_IND_POWER_STATE) return -ENODEV; + offset *= led->reg_table[led->indicator][LED_FUNC_POWER_STATE_NUM_CTRLS]; + ret = nuc_wmi_get_brightness_offset(dev, led, offset); if (ret < 0) return ret; @@ -771,6 +800,8 @@ static ssize_t store_brightness_offset(struct device *dev, if (kstrtou8(buf, 0, &val) || val > 100) return -EINVAL; + offset *= led->reg_table[led->indicator][LED_FUNC_POWER_STATE_NUM_CTRLS]; + ret = nuc_wmi_set_brightness_offset(dev, led, offset, val); if (ret) return ret; @@ -799,13 +830,40 @@ static int nuc_wmi_set_brightness(struct led_classdev *cdev, return nuc_wmi_set_brightness_offset(cdev->dev, led, 0, brightness); } +static umode_t nuc_wmi_led_power_state_is_visible(struct kobject *kobj, + struct attribute *attr, + int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + + umode_t mode = attr->mode; + + if (!strcmp(attr->name, "s0_brightness") || + !strcmp(attr->name, "s3_brightness")) + return mode; + + if (led->api_rev == LED_API_REV_0_64) { + if (!strcmp(attr->name, "s5_brightness") || + !strcmp(attr->name, "ready_mode_brightness")) + return mode; + } else { + if (!strcmp(attr->name, "standby_brightness")) + return mode; + } + + return 0; +} + static LED_ATTR_RW(indicator); static LED_ATTR_RW(color); -LED_ATTR_POWER_STATE_RW(s0_brightness, 0x00); -LED_ATTR_POWER_STATE_RW(s3_brightness, 0x06); -LED_ATTR_POWER_STATE_RW(s5_brightness, 0x0c); -LED_ATTR_POWER_STATE_RW(ready_mode_brightness, 0x12); +LED_ATTR_POWER_STATE_RW(s0_brightness, 0); +LED_ATTR_POWER_STATE_RW(s3_brightness, 1); +LED_ATTR_POWER_STATE_RW(s5_brightness, 2); // Rev 0.64 +LED_ATTR_POWER_STATE_RW(standby_brightness, 2); // Rev 1.0 +LED_ATTR_POWER_STATE_RW(ready_mode_brightness, 3); // Rev 1.0 /* * Attributes for LEDs @@ -813,15 +871,25 @@ LED_ATTR_POWER_STATE_RW(ready_mode_brightness, 0x12); static struct attribute *nuc_wmi_led_attr[] = { &dev_attr_indicator.attr, + NULL, +}; + +static const struct attribute_group nuc_wmi_led_attribute_group = { + .attrs = nuc_wmi_led_attr, +}; + +static struct attribute *nuc_wmi_led_power_state_attr[] = { &dev_attr_s0_brightness.attr, &dev_attr_s3_brightness.attr, + &dev_attr_standby_brightness.attr, &dev_attr_s5_brightness.attr, &dev_attr_ready_mode_brightness.attr, NULL, }; -static const struct attribute_group nuc_wmi_led_attribute_group = { - .attrs = nuc_wmi_led_attr, +static const struct attribute_group nuc_wmi_led_power_state_group = { + .is_visible = nuc_wmi_led_power_state_is_visible, + .attrs = nuc_wmi_led_power_state_attr, }; static struct attribute *nuc_wmi_led_color_attr[] = { @@ -836,26 +904,36 @@ static const struct attribute_group nuc_wmi_led_color_attribute_group = { static const struct attribute_group *nuc_wmi_led_attribute_groups[] = { &nuc_wmi_led_attribute_group, + &nuc_wmi_led_power_state_group, &nuc_wmi_led_color_attribute_group, NULL }; -static int nuc_wmi_led_register(struct device *dev, struct nuc_nmi_led *led) +static int nuc_wmi_led_register(struct device *dev, struct nuc_nmi_led *led, + enum led_api_rev api_rev) { - int brightness = nuc_wmi_get_brightness_offset(dev, led, 0); + int brightness; led->cdev.name = led_names[led->id]; led->dev = dev; led->cdev.groups = nuc_wmi_led_attribute_groups; led->cdev.brightness_get = nuc_wmi_get_brightness; led->cdev.brightness_set_blocking = nuc_wmi_set_brightness; + led->api_rev = api_rev; - if (led->color_type & LED_SINGLE_COLOR) + if (led->color_type & LED_SINGLE_COLOR) { + if (led->api_rev == LED_API_REV_1_0) + led->reg_table = led_func_rev_1_0_singlecolor; + else + led->reg_table = led_func_multicolor; led->cdev.max_brightness = 2; - else + } else { led->cdev.max_brightness = 100; + led->reg_table = led_func_multicolor; + } /* Ensure that the current bright will be preserved */ + brightness = nuc_wmi_get_brightness_offset(dev, led, 0); if (brightness >= 0) led->cdev.delayed_set_value = brightness; @@ -868,14 +946,15 @@ static int nuc_wmi_led_register(struct device *dev, struct nuc_nmi_led *led) static int nuc_wmi_leds_setup(struct device *dev) { struct nuc_wmi *priv = dev_get_drvdata(dev); + enum led_api_rev api_rev; int ret, i; - ret = nuc_wmi_query_leds(dev); + ret = nuc_wmi_query_leds(dev, &api_rev); if (ret) return ret; for (i = 0; i < priv->num_leds; i++) { - ret = nuc_wmi_led_register(dev, &priv->led[i]); + ret = nuc_wmi_led_register(dev, &priv->led[i], api_rev); if (ret) { dev_err(dev, "Failed to register led %d: %s\n", i, led_names[priv->led[i].id]); From patchwork Tue May 18 15:08:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 442851 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-21.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, MENTIONS_GIT_HOSTING, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5DC14C433ED for ; Tue, 18 May 2021 15:09:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 38B0661285 for ; Tue, 18 May 2021 15:09:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345044AbhERPKh (ORCPT ); Tue, 18 May 2021 11:10:37 -0400 Received: from mail.kernel.org ([198.145.29.99]:49446 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243998AbhERPK2 (ORCPT ); Tue, 18 May 2021 11:10:28 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 1984C61285; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=x3wrQxrzTGadyRwOA3LxdKBmzxjB74aMlEUAq2sqERQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sLVQFqaYcstlsnRpIgsAtVlPumAIAutpQKvXrWY3SSJsai8j2c6sbh/B/UQ0cSiEg OF9iYYzvRJ3td3MGkR2RWX2oiwUqkMg5gBbcnc4LJctAkcs9QKvcrdM/j/MjCjp6ja gvDp5qHO+5dfVPSRtoAOhaxg+bMAfabGiPfkSRIcLLYa2m0Z7BmFry6EVJQivhqlvH jz+sQyALmNOV8ReC/80t4TOw5BYXkp+YlDbGt4cO6mWIBPMhdei3Rly3KtFpdciwLf SZoF5MNHwkMZTd5Yt8iK6Vb/zep9SRoUkxEFR24AwZQ3AuhnESAmev5Qjt/PBrDTB+ WEgHk4lcrF+FA== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOQ-8y; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 08/17] leds: leds-nuc: add basic support for NUC6 WMI Date: Tue, 18 May 2021 17:08:57 +0200 Message-Id: <751ab7326957e0a05c568620b450a5690585d19b.1621349814.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org The NUC6 and NUCi7 supports an earlier version of the LEDs WMI, as specified at: https://www.intel.com/content/www/us/en/support/articles/000023426/intel-nuc/intel-nuc-kits.html Implement the query part of the LED detection for those devices. Weird enough, at least with Skull Canyon (NUC6i7KYB) using the latest firmware release (KYSKLi70 0071), the WMI call return all zeros. It could well be due to a regression at the Intel's firmware, although this model was not announced as supporting this WMI. At the manufacturer's site, only NUC Kits NUC7i[x]BN and NUC6CAY are mentioned. Yet, it sounds to me that this is due to a firmware bug: $ sudo fwts wmi - ... Test 1 of 1: Windows Management Instrumentation test. ... \_SB_.WMTF._WDG (1 of 1) GUID: 86CCFD48-205E-4A77-9C48-2021CBEDE341 WMI Method: Flags : 0x02 (Method) Object ID : TF Instance : 0x01 Driver : intel-wmi-thunderbolt (Intel) FAILED [LOW] WMIMultipleMethod: Test 1, GUID 86CCFD48-205E-4A77-9C48-2021CBEDE341 has multiple associated methods WMTF defined, this is a firmware bug that leads to ambiguous behaviour. ... \AMW0._WDG (1 of 2) GUID: 8C5DA44C-CDC3-46B3-8619-4E26D34390B7 WMI Method: Flags : 0x02 (Method) Object ID : AA Instance : 0x01 PASSED: Test 1, 8C5DA44C-CDC3-46B3-8619-4E26D34390B7 has associated method \AMW0.WMAA ... Low failures: 1 wmi: GUID 86CCFD48-205E-4A77-9C48-2021CBEDE341 has multiple associated methods WMTF defined, this is a firmware bug that leads to ambiguous behaviour. Anyway, this was good enough to test that this patch will be producing exactly the WMI query as the NUC6 OOT driver at: https://github.com/milesp20/intel_nuc_led/ Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 142 +++++++++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 32 deletions(-) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index e0090626aeec..cd15ed824234 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -8,12 +8,15 @@ * * Inspired on WMI from https://github.com/nomego/intel_nuc_led * - * It follows this spec: - * https://www.intel.com/content/dam/support/us/en/documents/intel-nuc/WMI-Spec-Intel-NUC-NUC10ixFNx.pdf + * It follows those specs: + * https://www.intel.com/content/www/us/en/support/articles/000023426/intel-nuc/intel-nuc-kits.html + * https://raw.githubusercontent.com/nomego/intel_nuc_led/master/specs/INTEL_WMI_LED_0.64.pdf + * https://www.intel.com/content/dam/support/us/en/documents/intel-nuc/WMI-Spec-Intel-NUC-NUC10ixFNx.pdf */ #include #include +#include #include #include #include @@ -34,12 +37,21 @@ enum led_api_rev { }; enum led_cmds { + /* NUC6-specific cmds */ + LED_OLD_GET_STATUS = 0x01, + LED_OLD_SET_LED = 0x02, + + /* Rev 0.64 and 1.0 cmds */ + LED_QUERY = 0x03, LED_NEW_GET_STATUS = 0x04, LED_SET_INDICATOR = 0x05, LED_SET_VALUE = 0x06, LED_NOTIFICATION = 0x07, LED_SWITCH_TYPE = 0x08, + + /* Rev 1.0 cmds */ + LED_VERSION_CONTROL = 0x09, }; @@ -55,6 +67,11 @@ enum led_new_get_subcmd { LED_NEW_GET_CONTROL_ITEM = 0x01, }; +enum led_old_get_subcmd { + LED_OLD_GET_S0_POWER = 0x01, + LED_OLD_GET_S0_RING = 0x02, +}; + enum led_function { LED_FUNC_BRIGHTNESS, LED_FUNC_COLOR1, @@ -146,14 +163,19 @@ static const u8 led_func_rev_1_0_singlecolor[MAX_IND][MAX_LED_FUNC] = { #define LED_RGB BIT(2) #define LED_SINGLE_COLOR BIT(3) +#define POWER_LED 0 +#define RING_LED (MAX_LEDS + 1) + static const char * const led_names[] = { - "nuc::power", + [POWER_LED] = "nuc::power", "nuc::hdd", "nuc::skull", "nuc::eyes", "nuc::front1", "nuc::front2", "nuc::front3", + + [RING_LED] = "nuc::ring", // NUC6 models }; struct nuc_nmi_led { @@ -276,51 +298,101 @@ static int nuc_nmi_cmd(struct device *dev, return ret; } +static int nuc_wmi_query_leds_nuc6(struct device *dev) +{ + // FIXME: add a check for the specific models that are known to work + struct nuc_wmi *priv = dev_get_drvdata(dev); + u8 cmd, input[NUM_INPUT_ARGS] = { 0 }; + u8 output[NUM_OUTPUT_ARGS]; + struct nuc_nmi_led *led; + int ret; + + cmd = LED_OLD_GET_STATUS; + input[0] = LED_OLD_GET_S0_POWER; + ret = nuc_nmi_cmd(dev, cmd, input, output); + if (ret) { + dev_warn(dev, "Get S0 Power: error %d\n", ret); + return ret; + } + + led = &priv->led[priv->num_leds]; + led->id = POWER_LED; + led->color_type = LED_BLUE_AMBER; + led->avail_indicators = LED_IND_POWER_STATE; + led->indicator = fls(led->avail_indicators); + priv->num_leds++; + + cmd = LED_OLD_GET_STATUS; + input[0] = LED_OLD_GET_S0_RING; + ret = nuc_nmi_cmd(dev, cmd, input, output); + if (ret) { + dev_warn(dev, "Get S0 Ring: error %d\n", ret); + return ret; + } + led = &priv->led[priv->num_leds]; + led->id = RING_LED; + led->color_type = LED_BLUE_AMBER; + led->avail_indicators = LED_IND_SOFTWARE; + led->indicator = fls(led->avail_indicators); + priv->num_leds++; + + return 0; +} + static int nuc_wmi_query_leds(struct device *dev, enum led_api_rev *api_rev) { struct nuc_wmi *priv = dev_get_drvdata(dev); u8 input[NUM_INPUT_ARGS] = { 0 }; u8 output[NUM_OUTPUT_ARGS]; - int id, ret, ver = LED_API_UNKNOWN; + int id, ret, ver = LED_API_UNKNOWN, nuc_ver = 0; u8 leds; + const char *dmi_name; + + dmi_name = dmi_get_system_info(DMI_PRODUCT_NAME); + if (!dmi_name || !*dmi_name) + dmi_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (strncmp(dmi_name, "NUC", 3)) + return -ENODEV; + + dmi_name +=3; + while (*dmi_name) { + if (*dmi_name < '0' || *dmi_name > '9') + break; + nuc_ver = (*dmi_name - '0') + nuc_ver * 10; + dmi_name++; + } + + if (nuc_ver < 6) + return -ENODEV; + + if (nuc_ver < 8) { + *api_rev = LED_API_NUC6; + return nuc_wmi_query_leds_nuc6(dev); + } - /* - * List all LED types support in the platform - * - * Should work with both NUC8iXXX and NUC10iXXX - * - * FIXME: Should add a fallback code for it to work with older NUCs, - * as LED_QUERY returns an error on older devices like Skull Canyon. - */ input[0] = LED_QUERY_LIST_ALL; ret = nuc_nmi_cmd(dev, LED_QUERY, input, output); - if (ret == -ENOENT) { - ver = LED_API_NUC6; - } else if (ret) { + if (ret) { dev_warn(dev, "error %d while listing all LEDs\n", ret); return ret; - } else { - leds = output[0]; } - if (ver != LED_API_NUC6) { - ret = nuc_nmi_cmd(dev, LED_VERSION_CONTROL, input, output); - if (ret) - return ret; + leds = output[0]; - ver = output[0] | output[1] << 16; - if (!ver) - ver = LED_API_REV_0_64; - else if (ver == 0x0126) - ver = LED_API_REV_1_0; - } + ret = nuc_nmi_cmd(dev, LED_VERSION_CONTROL, input, output); + if (ret) + return ret; + + ver = output[0] | output[1] << 16; + if (!ver) + *api_rev = LED_API_REV_0_64; + else if (ver == 0x0126) + *api_rev = LED_API_REV_1_0; - /* Currently, only API Revision 0.64 is supported */ - if (ver != LED_API_REV_0_64 && ver != LED_API_REV_1_0) + if (*api_rev == LED_API_UNKNOWN) return -ENODEV; - *api_rev = ver; - if (!leds) { dev_warn(dev, "No LEDs found\n"); return -ENODEV; @@ -916,10 +988,16 @@ static int nuc_wmi_led_register(struct device *dev, struct nuc_nmi_led *led, led->cdev.name = led_names[led->id]; led->dev = dev; + led->api_rev = api_rev; + + if (led->api_rev == LED_API_NUC6) { + // FIXME: add NUC6-specific API bits here + return devm_led_classdev_register(dev, &led->cdev); + } + led->cdev.groups = nuc_wmi_led_attribute_groups; led->cdev.brightness_get = nuc_wmi_get_brightness; led->cdev.brightness_set_blocking = nuc_wmi_set_brightness; - led->api_rev = api_rev; if (led->color_type & LED_SINGLE_COLOR) { if (led->api_rev == LED_API_REV_1_0) From patchwork Tue May 18 15:08:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 442850 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0E673C433B4 for ; Tue, 18 May 2021 15:09:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E8C9461261 for ; Tue, 18 May 2021 15:09:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345128AbhERPKn (ORCPT ); Tue, 18 May 2021 11:10:43 -0400 Received: from mail.kernel.org ([198.145.29.99]:49498 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S245316AbhERPK2 (ORCPT ); Tue, 18 May 2021 11:10:28 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 243DB6135B; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=OF31wuskOSGZ57gBcSDnyeKus3wSQ2N85viq/571JTI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=twerRVXPU/LJX87CKzUJMXURoFYahwyv/qetsHkE59uBky39byvpggqF9t6aWKlfv HGiFDJdh3favyPvXevyxMOlteRw68MN/Qi3XiGPoEXDdTSeO6Jz2OlgUZqSCNbOtTJ lmEOT46qes5BcvLHJ95bk+MtdztOF682UCtR0mdeuftFh4gNDYadsPf+UxkkZ9DgaP +kQ1pK1MdxAi2LjQXA+/h7FSn5uz6IgdYuBt9SMd0IuiDxHMjym8s/BeJkuVFysQFj 3TVbIp3WKhk1utPBdqv1Vet38XkmF1zecN1+YKBUo34O5O36/regF4PrBXgb/9y/JK iRAKzeddE8VZw== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOU-Ae; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 09/17] leds: leds-nuc: add brightness and color for NUC6 API Date: Tue, 18 May 2021 17:08:58 +0200 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org The NUC6 WMI API is really simple: it has just 2 messages, that retrieves everything for a LED, and it has just 2 LEDs. Add support for retrieving and set brightness and color. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 198 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 191 insertions(+), 7 deletions(-) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index cd15ed824234..03fa8bafc5de 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -302,14 +302,13 @@ static int nuc_wmi_query_leds_nuc6(struct device *dev) { // FIXME: add a check for the specific models that are known to work struct nuc_wmi *priv = dev_get_drvdata(dev); - u8 cmd, input[NUM_INPUT_ARGS] = { 0 }; + u8 input[NUM_INPUT_ARGS] = { 0 }; u8 output[NUM_OUTPUT_ARGS]; struct nuc_nmi_led *led; int ret; - cmd = LED_OLD_GET_STATUS; input[0] = LED_OLD_GET_S0_POWER; - ret = nuc_nmi_cmd(dev, cmd, input, output); + ret = nuc_nmi_cmd(dev, LED_OLD_GET_STATUS, input, output); if (ret) { dev_warn(dev, "Get S0 Power: error %d\n", ret); return ret; @@ -322,9 +321,8 @@ static int nuc_wmi_query_leds_nuc6(struct device *dev) led->indicator = fls(led->avail_indicators); priv->num_leds++; - cmd = LED_OLD_GET_STATUS; input[0] = LED_OLD_GET_S0_RING; - ret = nuc_nmi_cmd(dev, cmd, input, output); + ret = nuc_nmi_cmd(dev, LED_OLD_GET_STATUS, input, output); if (ret) { dev_warn(dev, "Get S0 Ring: error %d\n", ret); return ret; @@ -547,6 +545,167 @@ static ssize_t nuc_wmi_set_brightness_offset(struct device *dev, } \ static DEVICE_ATTR(_name, 0644, show_##_name, store_##_name) +/* + * NUC6 specific logic + */ + +static int nuc_wmi_nuc6_led_get_set(struct device *dev, + struct nuc_nmi_led *led, int *brightness, + int *blink_fade, int *color_state) +{ + u8 input[NUM_INPUT_ARGS] = { 0 }; + u8 output[NUM_OUTPUT_ARGS]; + int ret; + + if (led->id == POWER_LED) + input[0] = LED_OLD_GET_S0_POWER; + else + input[0] = LED_OLD_GET_S0_RING; + + ret = nuc_nmi_cmd(dev, LED_OLD_GET_STATUS, input, output); + if (ret) { + dev_warn(dev, "Get %s: error %d\n", led_names[led->id], ret); + return ret; + } + + if (brightness && *brightness >= 0) + input[1] = *brightness; + else + input[1] = output[0]; + + if (blink_fade && *blink_fade >= 0) + input[2] = *blink_fade; + else + input[2] = output[1]; + + if (color_state && *color_state >= 0) + input[3] = *color_state; + else + input[3] = output[2]; + + ret = nuc_nmi_cmd(dev, LED_OLD_SET_LED, input, output); + if (ret) { + dev_warn(dev, "Get %s: error %d\n", led_names[led->id], ret); + return ret; + } + + if (brightness) + *brightness = output[0]; + if (blink_fade) + *blink_fade = output[1]; + if (color_state) + *color_state = output[2]; + + return 0; +} + +static enum led_brightness nuc_wmi_nuc6_get_brightness(struct led_classdev *cdev) +{ + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + int ret, brightness = -1; + + ret = nuc_wmi_nuc6_led_get_set(cdev->dev, led, &brightness, NULL, NULL); + if (ret) + return ret; + + return brightness; +} + +static int nuc_wmi_nuc6_set_brightness(struct led_classdev *cdev, + enum led_brightness bright) +{ + int brightness = bright; + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + + return nuc_wmi_nuc6_led_get_set(cdev->dev, led, &brightness, + NULL, NULL); +} + +static const char * const nuc6_power_colors[] = { + "disable", + "blue", + "amber" +}; + +static const char * const nuc6_ring_colors[] = { + "disable", + "cyan", + "pink", + "yellow", + "blue", + "red", + "green", + "white" +}; + +static ssize_t nuc6_show_color(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + int color = -1, ret, arr_size, i, n; + const char * const*color_names; + int size = PAGE_SIZE; + char *p = buf; + + ret = nuc_wmi_nuc6_led_get_set(dev, led, NULL, NULL, &color); + if (ret) + return ret; + + if (led->id == POWER_LED) { + color_names = nuc6_power_colors; + arr_size = ARRAY_SIZE(nuc6_power_colors); + } else { + color_names = nuc6_ring_colors; + arr_size = ARRAY_SIZE(nuc6_ring_colors); + } + + for (i = 0; i < arr_size; i++) { + if (i == color) + n = scnprintf(p, size, "[%s] ", color_names[i]); + else + n = scnprintf(p, size, "%s ", color_names[i]); + p += n; + size -= n; + } + size -= scnprintf(p, size, "\n"); + + return PAGE_SIZE - size; + +} + +static ssize_t nuc6_store_color(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + const char *tmp; + int ret, color; + + tmp = strsep((char **)&buf, ",\n"); + + if (led->id == POWER_LED) { + for (color = ARRAY_SIZE(nuc6_power_colors)+1; color >= 0; color--) + if (!strcasecmp(tmp, nuc6_power_colors[color])) + break; + } else { + for (color = ARRAY_SIZE(nuc6_ring_colors)+1; color >= 0; color--) + if (!strcasecmp(tmp, nuc6_ring_colors[color])) + break; + } + + if (color < 0) + return -EINVAL; + + ret = nuc_wmi_nuc6_led_get_set(dev, led, NULL, NULL, &color); + if (ret) + return ret; + + return len; +} + /* Show/change the LED indicator */ static const char * const led_indicators[] = { @@ -660,6 +819,9 @@ static ssize_t show_color(struct device *dev, char *p = buf; int color, r, g, b; + if (led->api_rev == LED_API_NUC6) + return nuc6_show_color(dev, attr, buf); + if (led->indicator == LED_IND_DISABLE) return -ENODEV; @@ -726,6 +888,9 @@ static ssize_t store_color(struct device *dev, const char *tmp; u8 r, g, b, val; + if (led->api_rev == LED_API_NUC6) + return nuc6_store_color(dev, attr, buf, len); + if (led->indicator == LED_IND_DISABLE) return -ENODEV; @@ -828,6 +993,9 @@ static umode_t nuc_wmi_led_color_is_visible(struct kobject *kobj, struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); umode_t mode = attr->mode; + if (led->api_rev == LED_API_NUC6) + return mode; + if (led->color_type & LED_SINGLE_COLOR) return 0; @@ -981,17 +1149,33 @@ static const struct attribute_group *nuc_wmi_led_attribute_groups[] = { NULL }; +static const struct attribute_group *nuc_wmi_nuc6_led_attribute_groups[] = { + &nuc_wmi_led_color_attribute_group, + NULL +}; + static int nuc_wmi_led_register(struct device *dev, struct nuc_nmi_led *led, enum led_api_rev api_rev) { - int brightness; + int ret, brightness; led->cdev.name = led_names[led->id]; led->dev = dev; led->api_rev = api_rev; if (led->api_rev == LED_API_NUC6) { - // FIXME: add NUC6-specific API bits here + brightness = -1; + ret = nuc_wmi_nuc6_led_get_set(dev, led, &brightness, + NULL, NULL); + if (ret) + return ret; + + led->cdev.groups = nuc_wmi_nuc6_led_attribute_groups; + led->cdev.delayed_set_value = brightness; + led->cdev.max_brightness = 100; + led->cdev.brightness_get = nuc_wmi_nuc6_get_brightness; + led->cdev.brightness_set_blocking = nuc_wmi_nuc6_set_brightness; + return devm_led_classdev_register(dev, &led->cdev); } From patchwork Tue May 18 15:08:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 442845 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 32D2FC433B4 for ; Tue, 18 May 2021 15:10:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0E6B761261 for ; Tue, 18 May 2021 15:10:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1350045AbhERPLQ (ORCPT ); Tue, 18 May 2021 11:11:16 -0400 Received: from mail.kernel.org ([198.145.29.99]:49498 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345115AbhERPKk (ORCPT ); Tue, 18 May 2021 11:10:40 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 3253E6135D; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=8ScJfoPrpnNH7Ei2h6t3H6qX9PFlvMLFJOxUxAyJWbo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MGpA9S7fryvwpywoR65U81eKP6hAtoGc6aMbEfHuEda4ggeClN9x95PlMaZmwzdg4 kLWy38h5Oh+xuH0sjkd98ffmR/5kJc31txtieHonumuc3SdcGdWOogQtQKtxc41Eqn I93tsyr0KsAYWu/ctEyRGpM7QoniqaKMgJV4+T4wOigjpLcbrhyWquQ91l3GvGR1oi 031LMAksauC0gTx7UmJhiSKYIyv72p/6OF1jtWR1h0mrgill9WjxR7fxLpOuceXWns cGKpzz7wH88kdI0qRyjBigAWXz4UAwEFpT3FiS9tDSn9Cxfue7ZnoT2+BzjDNCkJ7a +LztTjvPwTVLQ== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOY-Bw; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 10/17] leds: leds-nuc: Add support to blink behavior for NUC8/10 Date: Tue, 18 May 2021 17:08:59 +0200 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org The hardware blink logic works for both Power State and Software controlled LEDs. Just like brightness, there is one different blink behavior per different power state. Due to that, the logic is somewhat more complex than what it would be expected otherwise. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 347 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 322 insertions(+), 25 deletions(-) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index 03fa8bafc5de..a5eb625d7b51 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -530,18 +530,30 @@ static ssize_t nuc_wmi_set_brightness_offset(struct device *dev, #define LED_ATTR_RW(_name) \ DEVICE_ATTR(_name, 0644, show_##_name, store_##_name) -#define LED_ATTR_POWER_STATE_RW(_name, offset) \ +#define LED_ATTR_POWER_STATE_RW(_name, _offname, _offset) \ static ssize_t show_##_name(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - return show_brightness_offset(dev, attr, offset, buf); \ + struct led_classdev *cdev = dev_get_drvdata(dev); \ + struct nuc_nmi_led *led; \ + \ + led = container_of(cdev, struct nuc_nmi_led, cdev); \ + if (led->indicator != LED_IND_POWER_STATE) \ + return -ENODEV; \ + return offset_show_##_offname(dev, attr, _offset, buf); \ } \ static ssize_t store_##_name(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ { \ - return store_brightness_offset(dev, attr, offset, buf, len); \ + struct led_classdev *cdev = dev_get_drvdata(dev); \ + struct nuc_nmi_led *led; \ + \ + led = container_of(cdev, struct nuc_nmi_led, cdev); \ + if (led->indicator != LED_IND_POWER_STATE) \ + return -ENODEV; \ + return offset_store_##_offname(dev, attr, _offset, buf, len); \ } \ static DEVICE_ATTR(_name, 0644, show_##_name, store_##_name) @@ -684,7 +696,7 @@ static ssize_t nuc6_store_color(struct device *dev, const char *tmp; int ret, color; - tmp = strsep((char **)&buf, ",\n"); + tmp = strsep((char **)&buf, "\n"); if (led->id == POWER_LED) { for (color = ARRAY_SIZE(nuc6_power_colors)+1; color >= 0; color--) @@ -1003,7 +1015,7 @@ static umode_t nuc_wmi_led_color_is_visible(struct kobject *kobj, } /* Show/store brightness */ -static ssize_t show_brightness_offset(struct device *dev, +static ssize_t offset_show_brightness(struct device *dev, struct device_attribute *attr, u8 offset, char *buf) @@ -1012,9 +1024,6 @@ static ssize_t show_brightness_offset(struct device *dev, struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); int ret; - if (led->indicator != LED_IND_POWER_STATE) - return -ENODEV; - offset *= led->reg_table[led->indicator][LED_FUNC_POWER_STATE_NUM_CTRLS]; ret = nuc_wmi_get_brightness_offset(dev, led, offset); @@ -1024,7 +1033,7 @@ static ssize_t show_brightness_offset(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%d\n", ret); } -static ssize_t store_brightness_offset(struct device *dev, +static ssize_t offset_store_brightness(struct device *dev, struct device_attribute *attr, u8 offset, const char *buf, size_t len) @@ -1034,9 +1043,6 @@ static ssize_t store_brightness_offset(struct device *dev, int ret; u8 val; - if (led->indicator != LED_IND_POWER_STATE) - return -ENODEV; - if (kstrtou8(buf, 0, &val) || val > 100) return -EINVAL; @@ -1070,6 +1076,8 @@ static int nuc_wmi_set_brightness(struct led_classdev *cdev, return nuc_wmi_set_brightness_offset(cdev->dev, led, 0, brightness); } +#define cmp_attr_prefix(a, b) strncmp(a, b, strlen(b)) + static umode_t nuc_wmi_led_power_state_is_visible(struct kobject *kobj, struct attribute *attr, int idx) @@ -1077,33 +1085,297 @@ static umode_t nuc_wmi_led_power_state_is_visible(struct kobject *kobj, struct device *dev = kobj_to_dev(kobj); struct led_classdev *cdev = dev_get_drvdata(dev); struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); - umode_t mode = attr->mode; - if (!strcmp(attr->name, "s0_brightness") || - !strcmp(attr->name, "s3_brightness")) + if (!cmp_attr_prefix(attr->name, "s0_") || + !cmp_attr_prefix(attr->name, "s3_")) return mode; if (led->api_rev == LED_API_REV_0_64) { - if (!strcmp(attr->name, "s5_brightness") || - !strcmp(attr->name, "ready_mode_brightness")) + if (!cmp_attr_prefix(attr->name, "s5_") || + !cmp_attr_prefix(attr->name, "ready_mode_")) return mode; } else { - if (!strcmp(attr->name, "standby_brightness")) + if (!cmp_attr_prefix(attr->name, "standby_")) return mode; } return 0; } +/* Blink */ +static const char * const led_blink_behaviors[] = { + "solid", + "breathing", + "pulsing", + "strobing" +}; + +static const char * const led_blink_frequencies[] = { + "0.1", + "0.2", + "0.3", + "0.4", + "0.5", + "0.6", + "0.7", + "0.8", + "0.9", + "1.0", +}; + +static ssize_t offset_show_blink_behavior(struct device *dev, + struct device_attribute *attr, + u8 offset, + char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS]; + u8 output[NUM_OUTPUT_ARGS]; + int ret, ctrl, val, i, n; + int size = PAGE_SIZE; + char *p = buf; + + if (led->indicator == LED_IND_DISABLE) + return -ENODEV; + + offset *= led->reg_table[led->indicator][LED_FUNC_POWER_STATE_NUM_CTRLS]; + ctrl = led->reg_table[led->indicator][LED_FUNC_BLINK_BEHAVIOR] + offset; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + input[0] = LED_NEW_GET_CONTROL_ITEM; + input[1] = led->id; + input[2] = led->indicator; + input[3] = ctrl; + + ret = nuc_nmi_cmd(dev, LED_NEW_GET_STATUS, input, output); + if (ret) + return ret; + + val = output[0]; + + for (i = 0; i < ARRAY_SIZE(led_blink_behaviors); i++) { + if (i == val) + n = scnprintf(p, size, "[%s] ", led_blink_behaviors[i]); + else + n = scnprintf(p, size, "%s ", led_blink_behaviors[i]); + p += n; + size -= n; + } + size -= scnprintf(p, size, "\n"); + + return PAGE_SIZE - size; +} + +static ssize_t offset_store_blink_behavior(struct device *dev, + struct device_attribute *attr, + u8 offset, + const char *buf, size_t len) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS] = { 0 }; + int ctrl, val, ret; + const char *tmp; + + if (led->indicator == LED_IND_DISABLE) + return -ENODEV; + + + if (led->id != LED_IND_SOFTWARE && led->id != LED_IND_POWER_STATE) + return -ENODEV; + + offset *= led->reg_table[led->indicator][LED_FUNC_POWER_STATE_NUM_CTRLS]; + ctrl = led->reg_table[led->indicator][LED_FUNC_BLINK_BEHAVIOR] + offset; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + tmp = strsep((char **)&buf, "\n"); + + for (val = 0; val < ARRAY_SIZE(led_blink_behaviors); val++) + if (!strcasecmp(tmp, led_blink_behaviors[val])) + break; + + if (val >= ARRAY_SIZE(led_blink_behaviors)) + return -EINVAL; + + input[0] = led->id; + input[1] = led->indicator; + input[2] = ctrl; + input[3] = val; + + ret = nuc_nmi_cmd(dev, LED_SET_VALUE, input, NULL); + if (ret) + return ret; + + return len; +} + +static ssize_t show_blink_behavior(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return offset_show_blink_behavior(dev, attr, 0, buf); +} + +static ssize_t store_blink_behavior(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return offset_store_blink_behavior(dev, attr, 0, buf, len); +} + +static ssize_t offset_show_blink_frequency(struct device *dev, + struct device_attribute *attr, + u8 offset, + char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS]; + u8 output[NUM_OUTPUT_ARGS]; + int ret, ctrl, val, i, n; + int size = PAGE_SIZE; + char *p = buf; + + if (led->indicator == LED_IND_DISABLE) + return -ENODEV; + + offset *= led->reg_table[led->indicator][LED_FUNC_POWER_STATE_NUM_CTRLS]; + ctrl = led->reg_table[led->indicator][LED_FUNC_BLINK_BEHAVIOR] + offset; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + input[0] = LED_NEW_GET_CONTROL_ITEM; + input[1] = led->id; + input[2] = led->indicator; + input[3] = ctrl; + + ret = nuc_nmi_cmd(dev, LED_NEW_GET_STATUS, input, output); + if (ret) + return ret; + + val = output[0]; + + for (i = 0; i < ARRAY_SIZE(led_blink_frequencies); i++) { + if (i == val) + n = scnprintf(p, size, "[%s] ", led_blink_frequencies[i]); + else + n = scnprintf(p, size, "%s ", led_blink_frequencies[i]); + p += n; + size -= n; + } + size -= scnprintf(p, size, "\n"); + + return PAGE_SIZE - size; +} + +static ssize_t offset_store_blink_frequency(struct device *dev, + struct device_attribute *attr, + u8 offset, + const char *buf, size_t len) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS] = { 0 }; + int ctrl, val, ret; + const char *tmp; + + if (led->indicator == LED_IND_DISABLE) + return -ENODEV; + + + if (led->id != LED_IND_SOFTWARE && led->id != LED_IND_POWER_STATE) + return -ENODEV; + + offset *= led->reg_table[led->indicator][LED_FUNC_POWER_STATE_NUM_CTRLS]; + ctrl = led->reg_table[led->indicator][LED_FUNC_BLINK_BEHAVIOR] + offset; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + tmp = strsep((char **)&buf, "\n"); + + for (val = 0; val < ARRAY_SIZE(led_blink_frequencies); val++) + if (!strcasecmp(tmp, led_blink_frequencies[val])) + break; + + if (val >= ARRAY_SIZE(led_blink_frequencies)) + return -EINVAL; + + input[0] = led->id; + input[1] = led->indicator; + input[2] = ctrl; + input[3] = val + 1; + + ret = nuc_nmi_cmd(dev, LED_SET_VALUE, input, NULL); + if (ret) + return ret; + + return len; +} + +static ssize_t show_blink_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return offset_show_blink_frequency(dev, attr, 0, buf); +} + +static ssize_t store_blink_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return offset_store_blink_frequency(dev, attr, 0, buf, len); +} + +static umode_t nuc_wmi_led_blink_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + umode_t mode = attr->mode; + + // TODO: implement for NUC6 API + if (led->api_rev == LED_API_NUC6) + return 0; + + if (led->id == LED_IND_SOFTWARE) + return mode; + + return 0; +} + static LED_ATTR_RW(indicator); static LED_ATTR_RW(color); +static LED_ATTR_RW(blink_behavior); +static LED_ATTR_RW(blink_frequency); -LED_ATTR_POWER_STATE_RW(s0_brightness, 0); -LED_ATTR_POWER_STATE_RW(s3_brightness, 1); -LED_ATTR_POWER_STATE_RW(s5_brightness, 2); // Rev 0.64 -LED_ATTR_POWER_STATE_RW(standby_brightness, 2); // Rev 1.0 -LED_ATTR_POWER_STATE_RW(ready_mode_brightness, 3); // Rev 1.0 +LED_ATTR_POWER_STATE_RW(s0_brightness, brightness, 0); +LED_ATTR_POWER_STATE_RW(s0_blink_behavior, blink_behavior, 0); +LED_ATTR_POWER_STATE_RW(s0_blink_frequency, blink_frequency, 0); +LED_ATTR_POWER_STATE_RW(s3_brightness, brightness, 1); +LED_ATTR_POWER_STATE_RW(s3_blink_behavior, blink_behavior, 1); +LED_ATTR_POWER_STATE_RW(s3_blink_frequency, blink_frequency, 1); + +/* Rev 0.64 */ +LED_ATTR_POWER_STATE_RW(s5_brightness, brightness, 2); +LED_ATTR_POWER_STATE_RW(s5_blink_behavior, blink_behavior, 2); +LED_ATTR_POWER_STATE_RW(s5_blink_frequency, blink_frequency, 2); +LED_ATTR_POWER_STATE_RW(ready_mode_brightness, brightness, 3); +LED_ATTR_POWER_STATE_RW(ready_mode_blink_behavior, blink_behavior, 3); +LED_ATTR_POWER_STATE_RW(ready_mode_blink_frequency, blink_frequency, 3); + +/* Rev 1.0 */ +LED_ATTR_POWER_STATE_RW(standby_brightness, brightness, 2); +LED_ATTR_POWER_STATE_RW(standby_blink_behavior, blink_behavior, 2); +LED_ATTR_POWER_STATE_RW(standby_blink_frequency, blink_frequency, 2); /* * Attributes for LEDs @@ -1124,6 +1396,19 @@ static struct attribute *nuc_wmi_led_power_state_attr[] = { &dev_attr_standby_brightness.attr, &dev_attr_s5_brightness.attr, &dev_attr_ready_mode_brightness.attr, + + &dev_attr_s0_blink_behavior.attr, + &dev_attr_s3_blink_behavior.attr, + &dev_attr_standby_blink_behavior.attr, + &dev_attr_s5_blink_behavior.attr, + &dev_attr_ready_mode_blink_behavior.attr, + + &dev_attr_s0_blink_frequency.attr, + &dev_attr_s3_blink_frequency.attr, + &dev_attr_standby_blink_frequency.attr, + &dev_attr_s5_blink_frequency.attr, + &dev_attr_ready_mode_blink_frequency.attr, + NULL, }; @@ -1142,10 +1427,22 @@ static const struct attribute_group nuc_wmi_led_color_attribute_group = { .attrs = nuc_wmi_led_color_attr, }; +static struct attribute *nuc_wmi_led_blink_behavior_attr[] = { + &dev_attr_blink_behavior.attr, + &dev_attr_blink_frequency.attr, + NULL, +}; + +static const struct attribute_group nuc_wmi_led_blink_attribute_group = { + .is_visible = nuc_wmi_led_blink_is_visible, + .attrs = nuc_wmi_led_blink_behavior_attr, +}; + static const struct attribute_group *nuc_wmi_led_attribute_groups[] = { &nuc_wmi_led_attribute_group, &nuc_wmi_led_power_state_group, &nuc_wmi_led_color_attribute_group, + &nuc_wmi_led_blink_attribute_group, NULL }; From patchwork Tue May 18 15:09:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 441513 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6BCF1C433B4 for ; Tue, 18 May 2021 15:09:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4B99361285 for ; Tue, 18 May 2021 15:09:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344955AbhERPKd (ORCPT ); Tue, 18 May 2021 11:10:33 -0400 Received: from mail.kernel.org ([198.145.29.99]:49438 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243464AbhERPK2 (ORCPT ); Tue, 18 May 2021 11:10:28 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 2F25B61360; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=cG8d0SVSR9AemukQMBOciLYjz3JmBKqR6Q8YNsgQ+pU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Zvq7IFzz55ZAepPag+foeRSI7S5lyllxIhf8LpyXdETrQPhxigTqxrJBqSRxBdxZH 8yeDHst5qwEsgWkPSA6BrkwbrBL+RDI3k77senxvQatiiFjyUxlqA5mebVJFPP/l+r V/O+yy3b5SydNKoTA+n9MiWc3emFhaEBWNXNV6+hr7Tu4JufH85crllbgsv2DzflFX Fa77tXvxAuSS39E9ReBg6JzdqrgVxgWJHvKYJwptO9XLLt9A8g2xtHGdR6lCd7cfew hJ7dfz2G6PsL6uAy50fiBh2Wnf+Gkz47dJWw/EV/3QQe2C1LMiPitsHEvsc9a04fVZ ZNfRq8+SljN4g== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOc-FK; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 11/17] leds: leds-nuc: get rid of an unused variable Date: Tue, 18 May 2021 17:09:00 +0200 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org drivers/staging/nuc-led/nuc-wmi.c: In function ‘nuc_nmi_cmd’: drivers/staging/nuc-led/nuc-wmi.c:242:6: warning: variable ‘size’ set but not used [-Wunused-but-set-variable] 242 | int size, ret; | ^~~~ Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index a5eb625d7b51..e2517e1a367f 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -239,7 +239,7 @@ static int nuc_nmi_cmd(struct device *dev, struct acpi_buffer input; union acpi_object *obj; acpi_status status; - int size, ret; + int ret; u8 *p; input.length = NUM_INPUT_ARGS; @@ -281,8 +281,6 @@ static int nuc_nmi_cmd(struct device *dev, goto err; } - size = NUM_OUTPUT_ARGS + 1; - if (output_args) { memcpy(output_args, p + 1, NUM_OUTPUT_ARGS); From patchwork Tue May 18 15:09:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 442848 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B6548C433B4 for ; Tue, 18 May 2021 15:09:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9A38261042 for ; Tue, 18 May 2021 15:09:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1350010AbhERPLC (ORCPT ); Tue, 18 May 2021 11:11:02 -0400 Received: from mail.kernel.org ([198.145.29.99]:49542 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343962AbhERPKa (ORCPT ); Tue, 18 May 2021 11:10:30 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 3526261361; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=WNd/FZyXWC3bVqUADHT/Lw1l4FiAmrVOx7fhoZ7HJ54=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=X/HtJ/L9SRvPEHCBP4nvU8vDsRytz9haZXplygWzae07Kz3LSxuWanohuNuCV3kjj i6HjcvAFiWrkp2hdkO09yQ+DLkGyxJnFbqsrRupI+xRjn5ecTtVdh7hFDl/A7RFj8F hDc4G+znJuw8BHY1/ysC0djioG+1MoLOSuJB4YXEWP0Cn/WdOT4L0G9escO31kdPEj E7mJCXLnHlpGkSgKxZcf1+Bq8392WHeAYBm5ZrG+Iu1uw01TPqJAvvar5kmJI7LGkP /zschFVjiZ2X5bpKEYi4bd3v4H04/A9j48Rm45oFET7RUrivi3EJYNtJcVvz6h1mcw 1oyZbtBh24dYA== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOg-GV; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 12/17] leds: leds-nuc: implement blink control for NUC6 Date: Tue, 18 May 2021 17:09:01 +0200 Message-Id: <059a46546066cba7e1a41ed9b2afc1d3bf2e5ed5.1621349814.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org The blink control logic for NUC6 API is somewhat messy, as it uses a single register for controlling both the blink type and the frequency, using a random order. Let's use the same API as defined for other versions, splitting this setting on two different properties. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 269 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 267 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index e2517e1a367f..6aa0bf16c8b7 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -716,6 +716,247 @@ static ssize_t nuc6_store_color(struct device *dev, return len; } +enum nuc6_blink_mode_freq { + NUC6_BLINK_MODE_BLINK_1HZ = 0x01, + NUC6_BLINK_MODE_BLINK_0_25HZ = 0x02, + NUC6_BLINK_MODE_FADE_1HZ = 0x03, + NUC6_BLINK_MODE_DONT_BLINK = 0x04, + + /* BIOS equal or upper AY0038 or BN0043 */ + NUC6_BLINK_MODE_BLINK_0_5HZ = 0x05, + NUC6_BLINK_MODE_FADE_0_25HZ = 0x06, + NUC6_BLINK_MODE_FADE_0_5HZ = 0x07 +}; + +enum nuc6_blink_mode { + NUC6_BLINK_MODE_SOLID, + NUC6_BLINK_MODE_BLINK, + NUC6_BLINK_MODE_FADE +}; + +static const char * const nuc6_blink_behavior[] = { + "solid", + "blink", + "fade", +}; + +enum nuc6_blink_freq { + NUC6_BLINK_FREQ_1HZ, + NUC6_BLINK_FREQ_0_5HZ, + NUC6_BLINK_FREQ_0_25HZ, +}; + +static const char * const nuc6_blink_frequency[] = { + "1", + "0.5", + "0.25", +}; + +static int nuc_wmi_nuc6_set_blink(struct device *dev, + struct nuc_nmi_led *led, + int freq, enum nuc6_blink_mode mode) +{ + int val; + + switch(mode) { + case NUC6_BLINK_MODE_SOLID: + val = NUC6_BLINK_MODE_DONT_BLINK; + break; + case NUC6_BLINK_MODE_BLINK: + if (freq == NUC6_BLINK_FREQ_0_25HZ) + val = NUC6_BLINK_MODE_BLINK_0_25HZ; + else if (freq == NUC6_BLINK_FREQ_0_5HZ) + val = NUC6_BLINK_MODE_BLINK_0_5HZ; + else + val = NUC6_BLINK_MODE_BLINK_1HZ; + break; + case NUC6_BLINK_MODE_FADE: + if (freq == NUC6_BLINK_FREQ_0_25HZ) + val = NUC6_BLINK_MODE_FADE_0_25HZ; + else if (freq == NUC6_BLINK_FREQ_0_5HZ) + val = NUC6_BLINK_MODE_FADE_0_5HZ; + else + val = NUC6_BLINK_MODE_FADE_1HZ; + break; + default: + return -EINVAL; + } + + return nuc_wmi_nuc6_led_get_set(dev, led, NULL, &val, NULL); +} + +static ssize_t nuc6_show_blink_behavior(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + int val = -1, mode = -1, ret, i, n; + int size = PAGE_SIZE; + char *p = buf; + + ret = nuc_wmi_nuc6_led_get_set(dev, led, NULL, &val, NULL); + if (ret) + return ret; + + switch (val) { + case NUC6_BLINK_MODE_BLINK_1HZ: + case NUC6_BLINK_MODE_BLINK_0_25HZ: + case NUC6_BLINK_MODE_BLINK_0_5HZ: + mode = NUC6_BLINK_MODE_BLINK; + break; + case NUC6_BLINK_MODE_FADE_1HZ: + case NUC6_BLINK_MODE_FADE_0_25HZ: + case NUC6_BLINK_MODE_FADE_0_5HZ: + mode = NUC6_BLINK_MODE_FADE; + break; + case NUC6_BLINK_MODE_DONT_BLINK: + mode = NUC6_BLINK_MODE_SOLID; + break; + } + + for (i = 0; i < ARRAY_SIZE(nuc6_blink_behavior); i++) { + if (i == mode) + n = scnprintf(p, size, "[%s] ", nuc6_blink_behavior[i]); + else + n = scnprintf(p, size, "%s ", nuc6_blink_behavior[i]); + p += n; + size -= n; + } + size -= scnprintf(p, size, "\n"); + + return PAGE_SIZE - size; + +} + +static ssize_t nuc6_store_blink_behavior(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + int ret, val = -1, freq; + const char *tmp; + + tmp = strsep((char **)&buf, "\n"); + + ret = nuc_wmi_nuc6_led_get_set(dev, led, NULL, &val, NULL); + if (ret) + return ret; + + /* Preserve the frequency */ + switch (val) { + case NUC6_BLINK_MODE_BLINK_0_25HZ: + case NUC6_BLINK_MODE_FADE_0_25HZ: + freq = NUC6_BLINK_FREQ_0_25HZ; + break; + case NUC6_BLINK_MODE_BLINK_0_5HZ: + case NUC6_BLINK_MODE_FADE_0_5HZ: + freq = NUC6_BLINK_FREQ_0_5HZ; + break; + default: + freq = NUC6_BLINK_FREQ_1HZ; + break; + } + + for (val = ARRAY_SIZE(nuc6_blink_behavior)+1; val >= 0; val--) + if (!strcasecmp(tmp, nuc6_blink_behavior[val])) + break; + if (val < 0) + return -EINVAL; + + ret = nuc_wmi_nuc6_set_blink(dev, led, val, freq); + if (ret) + return ret; + + return len; +} + +static ssize_t nuc6_show_blink_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + int val = -1, freq = -1, ret, i, n; + int size = PAGE_SIZE; + char *p = buf; + + ret = nuc_wmi_nuc6_led_get_set(dev, led, NULL, &val, NULL); + if (ret) + return ret; + + switch (val) { + case NUC6_BLINK_MODE_BLINK_0_25HZ: + case NUC6_BLINK_MODE_FADE_0_25HZ: + freq = NUC6_BLINK_FREQ_0_25HZ; + break; + case NUC6_BLINK_MODE_BLINK_0_5HZ: + case NUC6_BLINK_MODE_FADE_0_5HZ: + freq = NUC6_BLINK_FREQ_0_5HZ; + break; + default: + freq = NUC6_BLINK_FREQ_1HZ; + } + + for (i = 0; i < ARRAY_SIZE(nuc6_blink_frequency); i++) { + if (i == freq) + n = scnprintf(p, size, "[%s] ", nuc6_blink_frequency[i]); + else + n = scnprintf(p, size, "%s ", nuc6_blink_frequency[i]); + p += n; + size -= n; + } + size -= scnprintf(p, size, "\n"); + + return PAGE_SIZE - size; +} + +static ssize_t nuc6_store_blink_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + enum nuc6_blink_mode mode; + int ret, freq, val = -1; + const char *tmp; + + tmp = strsep((char **)&buf, "\n"); + + ret = nuc_wmi_nuc6_led_get_set(dev, led, NULL, &val, NULL); + if (ret) + return ret; + + /* Preserve the blink mode */ + switch (val) { + case NUC6_BLINK_MODE_BLINK_1HZ: + case NUC6_BLINK_MODE_BLINK_0_25HZ: + case NUC6_BLINK_MODE_BLINK_0_5HZ: + mode = NUC6_BLINK_MODE_BLINK; + break; + case NUC6_BLINK_MODE_FADE_1HZ: + case NUC6_BLINK_MODE_FADE_0_25HZ: + case NUC6_BLINK_MODE_FADE_0_5HZ: + mode = NUC6_BLINK_MODE_FADE; + break; + default: /* setting frequency NUC6_BLINK_MODE_SOLID won't make sense */ + return -EINVAL; + } + + for (freq = ARRAY_SIZE(nuc6_blink_frequency)+1; freq >= 0; freq--) + if (!strcasecmp(tmp, nuc6_blink_frequency[freq])) + break; + if (freq < 0) + return -EINVAL; + + ret = nuc_wmi_nuc6_set_blink(dev, led, mode, freq); + if (ret) + return ret; + + return len; +} + /* Show/change the LED indicator */ static const char * const led_indicators[] = { @@ -1217,6 +1458,12 @@ static ssize_t show_blink_behavior(struct device *dev, struct device_attribute *attr, char *buf) { + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + + if (led->api_rev == LED_API_NUC6) + return nuc6_show_blink_behavior(dev, attr, buf); + return offset_show_blink_behavior(dev, attr, 0, buf); } @@ -1224,6 +1471,12 @@ static ssize_t store_blink_behavior(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + + if (led->api_rev == LED_API_NUC6) + return nuc6_store_blink_behavior(dev, attr, buf, len); + return offset_store_blink_behavior(dev, attr, 0, buf, len); } @@ -1322,6 +1575,12 @@ static ssize_t show_blink_frequency(struct device *dev, struct device_attribute *attr, char *buf) { + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + + if (led->api_rev == LED_API_NUC6) + return nuc6_show_blink_frequency(dev, attr, buf); + return offset_show_blink_frequency(dev, attr, 0, buf); } @@ -1329,6 +1588,12 @@ static ssize_t store_blink_frequency(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + + if (led->api_rev == LED_API_NUC6) + return nuc6_store_blink_frequency(dev, attr, buf, len); + return offset_store_blink_frequency(dev, attr, 0, buf, len); } @@ -1340,9 +1605,8 @@ static umode_t nuc_wmi_led_blink_is_visible(struct kobject *kobj, struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); umode_t mode = attr->mode; - // TODO: implement for NUC6 API if (led->api_rev == LED_API_NUC6) - return 0; + return mode; if (led->id == LED_IND_SOFTWARE) return mode; @@ -1446,6 +1710,7 @@ static const struct attribute_group *nuc_wmi_led_attribute_groups[] = { static const struct attribute_group *nuc_wmi_nuc6_led_attribute_groups[] = { &nuc_wmi_led_color_attribute_group, + &nuc_wmi_led_blink_attribute_group, NULL }; From patchwork Tue May 18 15:09:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 441511 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 58225C433ED for ; Tue, 18 May 2021 15:09:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3A0BC61261 for ; Tue, 18 May 2021 15:09:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349936AbhERPKq (ORCPT ); Tue, 18 May 2021 11:10:46 -0400 Received: from mail.kernel.org ([198.145.29.99]:49522 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S245708AbhERPK3 (ORCPT ); Tue, 18 May 2021 11:10:29 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 43DA561376; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=irwt4RJFPgBRv9/8y13zzRL/edaGKx0UeKl9yigDS0M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=db1LhzBiRIIlRiB7UoFwrmSfyzqvvSI2W++A5Tnq/22PoSxQWTZKH7zhmvBSC1bKr KNkDRbDgoKNLKqZ8Pqdw1oTLmFE+Fkan3rkNd6ONzlNk+BfjtRERVn6qqxk0JOEh6R DExfTiz1iM69QqmjUo0kKsrbmm+Vale6QfYTPZBcbrfEec92tp+H4rQ0/eFr6UsVvL SbcW921TtUOcYr0a2QhdBEHEDbL0T6TmjwSQhSn1fRip3wvtUt7/OPscGfZuSLJFx4 R/4O1IGRIN4Fvo5ZzswS9KfQ5eDyUG19U+yfEDGVEmoyg9S2tH7/rOfNvT6BkmPqld pQ1uUwG+YxS8w== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOk-Ho; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 13/17] leds: leds-nuc: better detect NUC6/NUC7 devices Date: Tue, 18 May 2021 17:09:02 +0200 Message-Id: <29e9896e41eec83e8e775295d92fe3034735c792.1621349814.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org There's no documented way to detect if the WMI API is valid, as, when it is not valid, it just returns 4 zeros. However, as having a value of 0x00 for the blinking state is not valid, we can check for it at detection time, in order to disable LEDs control on devices that won't support it. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index 6aa0bf16c8b7..af57f54cfb05 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -312,6 +312,13 @@ static int nuc_wmi_query_leds_nuc6(struct device *dev) return ret; } + /* + * Detect if NUC6/NUC7 supports the WMI API by checking the + * returned blink state, as valid values range from 0x01 to 0x07. + */ + if (output[1] == 0x00) + return -ENODEV; + led = &priv->led[priv->num_leds]; led->id = POWER_LED; led->color_type = LED_BLUE_AMBER; @@ -325,6 +332,14 @@ static int nuc_wmi_query_leds_nuc6(struct device *dev) dev_warn(dev, "Get S0 Ring: error %d\n", ret); return ret; } + + /* + * Detect if NUC6/NUC7 supports the WMI API by checking the + * returned blink state, as valid values range from 0x01 to 0x07. + */ + if (output[1] == 0x00) + return -ENODEV; + led = &priv->led[priv->num_leds]; led->id = RING_LED; led->color_type = LED_BLUE_AMBER; From patchwork Tue May 18 15:09:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 441510 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 89249C433ED for ; Tue, 18 May 2021 15:09:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6F75261261 for ; Tue, 18 May 2021 15:09:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345060AbhERPLA (ORCPT ); Tue, 18 May 2021 11:11:00 -0400 Received: from mail.kernel.org ([198.145.29.99]:49540 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343957AbhERPKa (ORCPT ); Tue, 18 May 2021 11:10:30 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 5321961377; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=n6zLHZrATy+wjzdZFOZuRrEPeNvHyrUErO937oDY13M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=j3V0PB8oVMkFDifntPLPtzAMRoexdcP+9UbESVhzL7vyg3xMjbHIoXURXCgvuDl4c yOsV7Gd/ffsmgOA6xHowdRXg+DYPIQGPT8+B5ATl7KcFdVwTcoc/4Rv5YS7B12iDji 8R+YrB/IpbQaVPg4y690M7KFo9gr7pjTqCe0fZxjrLHTCxLczmRRs+cE/2S5ms7dT+ aCGAyNWqb5+2VL/E0wN+qO0z4mWviSoqE4l0obEwXtpF5fQNFa3HBoLHJr5HNz8WOl ugSpeJFekzafFSGfUVdJXCYaI1SVHHOEiURalx/h8riSQ2tM221cPw6AOANqpfYkJ9 ODuhrJ/aLkjsA== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOo-JV; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 14/17] leds: leds-nuc: add support for HDD activity default Date: Tue, 18 May 2021 17:09:03 +0200 Message-Id: <08d9c1d025e25a1a095089b9c38a780aef8fb797.1621349814.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org There are two possible values for HDD activity behavior: - 0 Normally off, ON when active - 1 Normally on, OFF when active Implement a logic to set it. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 77 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index af57f54cfb05..719a57841c03 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -1629,10 +1629,86 @@ static umode_t nuc_wmi_led_blink_is_visible(struct kobject *kobj, return 0; } +/* HDD activity behavior */ +static ssize_t show_hdd_default(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS] = { 0 }; + u8 output[NUM_OUTPUT_ARGS]; + int ctrl, ret, val; + + if (led->indicator != LED_IND_HDD_ACTIVITY) + return -EINVAL; + + ctrl = led->reg_table[led->indicator][LED_FUNC_HDD_BEHAVIOR]; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + input[0] = LED_NEW_GET_CONTROL_ITEM; + input[1] = led->id; + input[2] = led->indicator; + input[3] = ctrl; + + ret = nuc_nmi_cmd(dev, LED_NEW_GET_STATUS, input, output); + if (ret) + return ret; + + val = output[0]; + + if (val == 0) + return scnprintf(buf, PAGE_SIZE, "off\n"); + + return scnprintf(buf, PAGE_SIZE, "on\n"); +} + +static ssize_t store_hdd_default(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS] = { 0 }; + int ctrl, val, ret; + const char *tmp; + + if (led->indicator != LED_IND_HDD_ACTIVITY) + return -EINVAL; + + ctrl = led->reg_table[led->indicator][LED_FUNC_HDD_BEHAVIOR]; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + tmp = strsep((char **)&buf, "\n"); + if (!strcmp(tmp, "on")) + val = 1; + else if (!strcmp(tmp, "off")) + val = 0; + else + return -EINVAL; + + input[0] = led->id; + input[1] = led->indicator; + input[2] = ctrl; + input[3] = val; + + ret = nuc_nmi_cmd(dev, LED_SET_VALUE, input, NULL); + if (ret) + return ret; + + return len; +} + + static LED_ATTR_RW(indicator); static LED_ATTR_RW(color); static LED_ATTR_RW(blink_behavior); static LED_ATTR_RW(blink_frequency); +static LED_ATTR_RW(hdd_default); LED_ATTR_POWER_STATE_RW(s0_brightness, brightness, 0); LED_ATTR_POWER_STATE_RW(s0_blink_behavior, blink_behavior, 0); @@ -1660,6 +1736,7 @@ LED_ATTR_POWER_STATE_RW(standby_blink_frequency, blink_frequency, 2); static struct attribute *nuc_wmi_led_attr[] = { &dev_attr_indicator.attr, + &dev_attr_hdd_default.attr, NULL, }; From patchwork Tue May 18 15:09:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 442847 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BAA61C433ED for ; Tue, 18 May 2021 15:09:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9EBE161261 for ; Tue, 18 May 2021 15:09:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1350024AbhERPLK (ORCPT ); Tue, 18 May 2021 11:11:10 -0400 Received: from mail.kernel.org ([198.145.29.99]:49628 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344982AbhERPKe (ORCPT ); Tue, 18 May 2021 11:10:34 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 6ADCF613B4; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=8fwuaq8tsCOrCFnHrBrg5Jr8mMwviHOuxKo1IfHykBs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=q7b9VfADkXACHt69AAPgCNc1IWGXY7tSSU89sIzY6b12t3jlbaL28p23dYoz2H773 F9FzP0clVvTDHchSQJYwlapeP0hx8l8XXv5kY7VkKb40kOxkZ/KrCkH8M60ENDJ14b +MFGD3JHLmN+L+87OyxNIoxfz+D4QipuvMhPISZvwn2hmWeTlz/kA5gjxOoce3Ftkm g2uWChrKkK0f1Va6TZuyel86dtlggojJOwiZHTAUQ6t2jtsjjeBj9U4/3k68TWsd98 bDC9g8IlSxNwod1vofcwayKg/jC954BF4ElTtf5GmXlOM3us+7cynBpCmUlqABWW4Q 0Jv8sqBp/LLNw== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOs-Kf; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 15/17] leds: leds-nuc: fix software blink behavior logic Date: Tue, 18 May 2021 17:09:04 +0200 Message-Id: <44aed20dc44c893c12ad26b109a8a0e49bc4dab0.1621349814.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org The is_visible logic for it is plain wrong: 1. it is used only during devnode creation; 2. it was using the wrong field (id, instead of indicator). Fix it. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index 719a57841c03..4d4ea6fbeff4 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -1479,6 +1479,9 @@ static ssize_t show_blink_behavior(struct device *dev, if (led->api_rev == LED_API_NUC6) return nuc6_show_blink_behavior(dev, attr, buf); + if (led->indicator != LED_IND_SOFTWARE) + return -EINVAL; + return offset_show_blink_behavior(dev, attr, 0, buf); } @@ -1492,6 +1495,9 @@ static ssize_t store_blink_behavior(struct device *dev, if (led->api_rev == LED_API_NUC6) return nuc6_store_blink_behavior(dev, attr, buf, len); + if (led->indicator != LED_IND_SOFTWARE) + return -EINVAL; + return offset_store_blink_behavior(dev, attr, 0, buf, len); } @@ -1596,6 +1602,9 @@ static ssize_t show_blink_frequency(struct device *dev, if (led->api_rev == LED_API_NUC6) return nuc6_show_blink_frequency(dev, attr, buf); + if (led->indicator != LED_IND_SOFTWARE) + return -EINVAL; + return offset_show_blink_frequency(dev, attr, 0, buf); } @@ -1609,26 +1618,12 @@ static ssize_t store_blink_frequency(struct device *dev, if (led->api_rev == LED_API_NUC6) return nuc6_store_blink_frequency(dev, attr, buf, len); + if (led->indicator != LED_IND_SOFTWARE) + return -EINVAL; + return offset_store_blink_frequency(dev, attr, 0, buf, len); } -static umode_t nuc_wmi_led_blink_is_visible(struct kobject *kobj, - struct attribute *attr, int idx) -{ - struct device *dev = kobj_to_dev(kobj); - struct led_classdev *cdev = dev_get_drvdata(dev); - struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); - umode_t mode = attr->mode; - - if (led->api_rev == LED_API_NUC6) - return mode; - - if (led->id == LED_IND_SOFTWARE) - return mode; - - return 0; -} - /* HDD activity behavior */ static ssize_t show_hdd_default(struct device *dev, struct device_attribute *attr, @@ -1788,7 +1783,6 @@ static struct attribute *nuc_wmi_led_blink_behavior_attr[] = { }; static const struct attribute_group nuc_wmi_led_blink_attribute_group = { - .is_visible = nuc_wmi_led_blink_is_visible, .attrs = nuc_wmi_led_blink_behavior_attr, }; From patchwork Tue May 18 15:09:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 441508 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6112BC43461 for ; Tue, 18 May 2021 15:09:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4565C61261 for ; Tue, 18 May 2021 15:09:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1350030AbhERPLL (ORCPT ); Tue, 18 May 2021 11:11:11 -0400 Received: from mail.kernel.org ([198.145.29.99]:49626 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344988AbhERPKe (ORCPT ); Tue, 18 May 2021 11:10:34 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 66FDF613AF; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=DLdEfx6xwQIJPGQdiBgHEYPiWWkAAU8NZ8TS0QVIJfo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SHXdEkXqJRbfyeImoZNRyK3cV+2OWJTIzLCRSpmvac0YB36l64vEcf3j8IMHwMzTr rdsUkP+i/e/mc97bjaPSUQ8CCMqzes0zhdPPmsv2lrL8ns1LRVUWSf3sbWwqaerr3V 36c9twPHni0prdQ6TqwkytqFHxa9NWMt2pHLKeFqtehyczzW7jBOXWx2zK7vPZdznG emAmqCnOd4VJIzw356tuwW+Sc5Xduo9yncKW/ks1Y5vlTCXogUSvVCYvLF8EEDJcaa /U2ltWkejDiQUgsao9DuagaHyWLCjYbN+zSldjWBeUdbaRgKS5C+CwV/RcduEle03H 3x6cTZSU5Cf4w== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HOw-MP; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 16/17] leds: leds-nuc: add support for changing the ethernet type indicator Date: Tue, 18 May 2021 17:09:05 +0200 Message-Id: <792598f4a1a3219b6517057c92559b0f0a95b419.1621349814.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org The Ethernet type indicator can be configured to show the status of LAN1, LAN1 or both. Add support for it. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 89 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index 4d4ea6fbeff4..f84ec5662f5c 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -1698,12 +1698,100 @@ static ssize_t store_hdd_default(struct device *dev, return len; } +/* Ethernet type */ +static const char * const ethernet_type[] = { + "LAN1", + "LAN2", + "LAN1+LAN2" +}; + +static ssize_t show_ethernet_type(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS] = { 0 }; + u8 output[NUM_OUTPUT_ARGS]; + int ctrl, ret, val, i, n; + int size = PAGE_SIZE; + char *p = buf; + + if (led->indicator != LED_IND_ETHERNET) + return -EINVAL; + + ctrl = led->reg_table[led->indicator][LED_FUNC_ETH_TYPE]; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + input[0] = LED_NEW_GET_CONTROL_ITEM; + input[1] = led->id; + input[2] = led->indicator; + input[3] = ctrl; + + ret = nuc_nmi_cmd(dev, LED_NEW_GET_STATUS, input, output); + if (ret) + return ret; + + val = output[0]; + + for (i = 0; i < ARRAY_SIZE(ethernet_type); i++) { + if (i == val) + n = scnprintf(p, size, "[%s] ", ethernet_type[i]); + else + n = scnprintf(p, size, "%s ", ethernet_type[i]); + p += n; + size -= n; + } + size -= scnprintf(p, size, "\n"); + + return PAGE_SIZE - size; +} + +static ssize_t store_ethernet_type(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS] = { 0 }; + int ctrl, val, ret; + const char *tmp; + + if (led->indicator != LED_IND_ETHERNET) + return -EINVAL; + + ctrl = led->reg_table[led->indicator][LED_FUNC_ETH_TYPE]; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + for (val = 0; val < ARRAY_SIZE(ethernet_type); val++) + if (!strcasecmp(tmp, ethernet_type[val])) + break; + + if (val >= ARRAY_SIZE(ethernet_type)) + return -EINVAL; + + input[0] = led->id; + input[1] = led->indicator; + input[2] = ctrl; + input[3] = val; + + ret = nuc_nmi_cmd(dev, LED_SET_VALUE, input, NULL); + if (ret) + return ret; + + return len; +} static LED_ATTR_RW(indicator); static LED_ATTR_RW(color); static LED_ATTR_RW(blink_behavior); static LED_ATTR_RW(blink_frequency); static LED_ATTR_RW(hdd_default); +static LED_ATTR_RW(ethernet_type); LED_ATTR_POWER_STATE_RW(s0_brightness, brightness, 0); LED_ATTR_POWER_STATE_RW(s0_blink_behavior, blink_behavior, 0); @@ -1732,6 +1820,7 @@ LED_ATTR_POWER_STATE_RW(standby_blink_frequency, blink_frequency, 2); static struct attribute *nuc_wmi_led_attr[] = { &dev_attr_indicator.attr, &dev_attr_hdd_default.attr, + &dev_attr_ethernet_type.attr, NULL, }; From patchwork Tue May 18 15:09:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauro Carvalho Chehab X-Patchwork-Id: 442846 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BBF04C433B4 for ; Tue, 18 May 2021 15:09:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A0FA761261 for ; Tue, 18 May 2021 15:09:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1350036AbhERPLN (ORCPT ); Tue, 18 May 2021 11:11:13 -0400 Received: from mail.kernel.org ([198.145.29.99]:49496 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345055AbhERPKh (ORCPT ); Tue, 18 May 2021 11:10:37 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 6F6C1613B5; Tue, 18 May 2021 15:09:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1621350550; bh=3crpsUukJiwOrmYoQySDsb9Gg2iiXjFUvVuCwAECZfw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ND+HmJkIOBv/3v4uWcAy+U/+zYYO5wypLavi4OJxHUAZ2s3/xeTiO0JsKIT/UDA+U fB9hHWo0t6gHMSld1Ex4kGFCBUUgXPtltHpl/cl1S/Bn1STNIIzW+DVinoBh7Q/17e 0N+gXzuqNgMVQQ+2/zUmf7TUQCTFYWlTtDNu/fMQUYATNj/WYPxlFo5hrfxgfqOqtu yzbTWIPkA06V94opowwU1mHCKpvqBfWGzAW44Mbk4cd2gAPMneufvVvNP/o62ldynN JcHmWDwpotSWEBv2D0QNXVXZ2i9WKjREYlmC3szXREJoLgso5n5flv4biKCc+jX6zg 0m55NBCfZNG7Q== Received: by mail.kernel.org with local (Exim 4.94.2) (envelope-from ) id 1lj1LI-007HP0-Nd; Tue, 18 May 2021 17:09:08 +0200 From: Mauro Carvalho Chehab Cc: linuxarm@huawei.com, mauro.chehab@huawei.com, Mauro Carvalho Chehab , Pavel Machek , gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org Subject: [PATCH v2 17/17] leds: leds-nuc: add support for changing the power limit scheme Date: Tue, 18 May 2021 17:09:06 +0200 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Sender: Mauro Carvalho Chehab To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org The power limit indicator may have 2 behaviors: 1. Its color gradually changes from green to red; 2. It displays a single color Add support for it. Signed-off-by: Mauro Carvalho Chehab --- drivers/leds/leds-nuc.c | 93 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/drivers/leds/leds-nuc.c b/drivers/leds/leds-nuc.c index f84ec5662f5c..c320a7e4c796 100644 --- a/drivers/leds/leds-nuc.c +++ b/drivers/leds/leds-nuc.c @@ -1767,6 +1767,8 @@ static ssize_t store_ethernet_type(struct device *dev, if (!nuc_wmi_test_control(dev, led, ctrl)) return -ENODEV; + tmp = strsep((char **)&buf, "\n"); + for (val = 0; val < ARRAY_SIZE(ethernet_type); val++) if (!strcasecmp(tmp, ethernet_type[val])) break; @@ -1786,12 +1788,102 @@ static ssize_t store_ethernet_type(struct device *dev, return len; } +/* Power Limit Indication scheme */ +static const char * const power_limit_scheme[] = { + "green to red", + "single color" +}; + +static ssize_t show_power_limit_scheme(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS] = { 0 }; + u8 output[NUM_OUTPUT_ARGS]; + int ctrl, ret, val, i, n; + int size = PAGE_SIZE; + char *p = buf; + + if (led->indicator != LED_IND_POWER_LIMIT) + return -EINVAL; + + ctrl = led->reg_table[led->indicator][LED_FUNC_POWER_STATE_NUM_CTRLS]; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + input[0] = LED_NEW_GET_CONTROL_ITEM; + input[1] = led->id; + input[2] = led->indicator; + input[3] = ctrl; + + ret = nuc_nmi_cmd(dev, LED_NEW_GET_STATUS, input, output); + if (ret) + return ret; + + val = output[0]; + + for (i = 0; i < ARRAY_SIZE(power_limit_scheme); i++) { + if (i == val) + n = scnprintf(p, size, "[%s] ", power_limit_scheme[i]); + else + n = scnprintf(p, size, "%s ", power_limit_scheme[i]); + p += n; + size -= n; + } + size -= scnprintf(p, size, "\n"); + + return PAGE_SIZE - size; +} + +static ssize_t store_power_limit_scheme(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct nuc_nmi_led *led = container_of(cdev, struct nuc_nmi_led, cdev); + u8 input[NUM_INPUT_ARGS] = { 0 }; + int ctrl, val, ret; + const char *tmp; + + if (led->indicator != LED_IND_POWER_LIMIT) + return -EINVAL; + + ctrl = led->reg_table[led->indicator][LED_FUNC_POWER_STATE_NUM_CTRLS]; + + if (!nuc_wmi_test_control(dev, led, ctrl)) + return -ENODEV; + + tmp = strsep((char **)&buf, "\n"); + + for (val = 0; val < ARRAY_SIZE(power_limit_scheme); val++) + if (!strcasecmp(tmp, power_limit_scheme[val])) + break; + + if (val >= ARRAY_SIZE(power_limit_scheme)) + return -EINVAL; + + input[0] = led->id; + input[1] = led->indicator; + input[2] = ctrl; + input[3] = val; + + ret = nuc_nmi_cmd(dev, LED_SET_VALUE, input, NULL); + if (ret) + return ret; + + return len; +} + static LED_ATTR_RW(indicator); static LED_ATTR_RW(color); static LED_ATTR_RW(blink_behavior); static LED_ATTR_RW(blink_frequency); static LED_ATTR_RW(hdd_default); static LED_ATTR_RW(ethernet_type); +static LED_ATTR_RW(power_limit_scheme); LED_ATTR_POWER_STATE_RW(s0_brightness, brightness, 0); LED_ATTR_POWER_STATE_RW(s0_blink_behavior, blink_behavior, 0); @@ -1821,6 +1913,7 @@ static struct attribute *nuc_wmi_led_attr[] = { &dev_attr_indicator.attr, &dev_attr_hdd_default.attr, &dev_attr_ethernet_type.attr, + &dev_attr_power_limit_scheme.attr, NULL, };