From patchwork Thu Jan 17 10:10:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 155811 Delivered-To: patch@linaro.org Received: by 2002:a02:48:0:0:0:0:0 with SMTP id 69csp1772406jaa; Thu, 17 Jan 2019 02:10:11 -0800 (PST) X-Google-Smtp-Source: ALg8bN6HDBPkuUGuCHhzPupjU2K4xO48XQsgPf//aPCzraWVbQxzKRP6+IzsIho+iDwgU6FGKIO5 X-Received: by 2002:a17:902:ac8f:: with SMTP id h15mr13972803plr.245.1547719811750; Thu, 17 Jan 2019 02:10:11 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1547719811; cv=none; d=google.com; s=arc-20160816; b=dpHTyphjEXVYzZVbEfJ/MgfWdBHVVoyIoVoN6qKfSNXwAM43pI2e8BOA+kHme5CchI 5rQ9c5sv/N8Nx3JUMOo3GluPGt6AMh7o9kldaXElIly6U/I8KCcA4tkTt2IUAoeIMYpJ Ewvsel1rWbCY9GKzZwvTbL0OyUxaj1NXQJ5SAkexjS/Bicid96T9tuYNSpeQet1l3JxV w2Xl1douDiwMPu4KDzPWFX+fCRWmyeFEx1maj1kcixiGB9EK5t9RvJ+ln9vCy/LSpOSs iPMzpt7Sf14m4KeUvlGTBW9Ajx71xkPCiSdQdI1pAs1Yl++c2/Er0xL+BVvUzI8V+KZ2 /j0A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=DNj+aiicDslSoM9hX9xORtIY6jjAKBUZFYJoqcDmOZI=; b=jp6cgoxUgKxAf5tRzdkE14n0+kR152YRw3HtiYSP1ksKPaMhdloRvqdnPfIZML69T9 OOjJMqX1cckG49pHeIcvhfGRQNHXvaBuwW9UXYBu4EGmqAs7O9lROwD89ZcfwokOzHKS 0isQDGAcQ5AXSA7pzmoGAfvxZTSw2MSTTAiY4pjSffGubtkB+SS49L8wydun5teuYuIF bi+basOXrSj2xKIkszRGSK3FpqxQkg6GDaw3w2LWf2P+1cSTxZegQ4rq2hPyYwM3ZUH6 eWq8AXUOUHVib75Tth1DXY3fOhoT291rZn59Bgk2RmrPz7dNoHhSQC8PMaCLQCYwcMgs DDQQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=YsrVu+EC; spf=pass (google.com: best guess record for domain of linux-leds-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-leds-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id q10si1201869pll.221.2019.01.17.02.10.11; Thu, 17 Jan 2019 02:10:11 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-leds-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=YsrVu+EC; spf=pass (google.com: best guess record for domain of linux-leds-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-leds-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726948AbfAQKKL (ORCPT + 1 other); Thu, 17 Jan 2019 05:10:11 -0500 Received: from mail-lf1-f67.google.com ([209.85.167.67]:41068 "EHLO mail-lf1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726493AbfAQKKL (ORCPT ); Thu, 17 Jan 2019 05:10:11 -0500 Received: by mail-lf1-f67.google.com with SMTP id c16so7318218lfj.8 for ; Thu, 17 Jan 2019 02:10:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=DNj+aiicDslSoM9hX9xORtIY6jjAKBUZFYJoqcDmOZI=; b=YsrVu+ECQgCksqWygRX4sIik6oC7gRJ+a9F0wAYcfOXzrqx5NqXm8lKHnNq8/JBbJm F2DjsVAybFG9eS4JEdostposVP2jTjIHCXHPaOHDK3n1Z0/i9FOnghMfsTc0J64gCP2I njJipLFxQPRb4MS+M7H3uQdbyVEOpkCRdUVfY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=DNj+aiicDslSoM9hX9xORtIY6jjAKBUZFYJoqcDmOZI=; b=tDqWVYQWAvDn2QTu0UhDj9aMjOjMPVQCco6gPJdNjf5YmwRW+n4fYDVFXxOZIZmst9 gu46U0ojajk/mkQmuhs1/u0L/j9zi4az+Xi+j409r14ZDLMwrhP9RdyAzWWSgZzChfyA DG5FiaK//sbZlGpplai/dncLlhuxBL8+TIJRxbJwJ5In0C3s1IlVQAGoOtYWt5gS1iPN VvnDF0bwypJBsRebkzAeQYQXI7aipjUwR7hsdRs4JOwCLsPyIBtukJXicajjb3nA0dBk av1hghoX19Q1MW/GTf2Gv3uGtFOQq94WDSPcZYFXDeaQQTykbPWIluU/NVYz/ms60qLl MotQ== X-Gm-Message-State: AJcUukcD+JN/7P3PAmzxfz1DuhFpRIrb6mNSNfLRYBM2EdUglS6JsgPY WBOwArwsnrbuNpuP5OmgLd5iecy/06k= X-Received: by 2002:a19:2584:: with SMTP id l126mr9446453lfl.69.1547719808083; Thu, 17 Jan 2019 02:10:08 -0800 (PST) Received: from genomnajs.ideon.se ([85.235.10.227]) by smtp.gmail.com with ESMTPSA id u79-v6sm188848lje.36.2019.01.17.02.10.06 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 17 Jan 2019 02:10:06 -0800 (PST) From: Linus Walleij To: Jacek Anaszewski , Pavel Machek Cc: linux-leds@vger.kernel.org, Linus Walleij Subject: [PATCH RESEND] leds: core: Support blocking HW blink operations Date: Thu, 17 Jan 2019 11:10:02 +0100 Message-Id: <20190117101002.22134-1-linus.walleij@linaro.org> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Sender: linux-leds-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org I ran into this when working on a keyboard driver for the Razer family: the .blink_set() callback for setting hardware blinking on a LED only exist in a non-blocking (fastpath) variant, such as when blinking can be enabled by simply writing a memory-mapped register and protected by spinlocks. On USB keyboards with blinkable LEDs controlled with USB out-of-band commands this will of course not work: these calls need to come from process context. To support this: add a new .blink_set_blocking() callback in the same vein as .brightness_set_blocking() and add a flag and some code to the delayed work so that this will be able to fire the .blink_set_blocking() call. ALl of this will be handled transparently from the led_blink_set() call so all current users can keep using that. Signed-off-by: Linus Walleij --- drivers/leds/led-core.c | 49 ++++++++++++++++++++++++++++++++++++++--- include/linux/leds.h | 9 ++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) -- 2.20.1 diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index ede4fa0ac2cc..94870a3d59af 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -108,6 +108,20 @@ static void set_brightness_delayed(struct work_struct *ws) container_of(ws, struct led_classdev, set_brightness_work); int ret = 0; + if (test_and_clear_bit(LED_BLINK_HW_SET, &led_cdev->work_flags)) { + if (led_cdev->blink_set) + ret = led_cdev->blink_set(led_cdev, + &led_cdev->blink_delay_on, + &led_cdev->blink_delay_off); + else if (led_cdev->blink_set_blocking) + ret = led_cdev->blink_set_blocking(led_cdev, + &led_cdev->blink_delay_on, + &led_cdev->blink_delay_off); + if (ret) + dev_err(led_cdev->dev, + "setting hardware blink failed (%d)\n", ret); + } + if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) { led_cdev->delayed_set_value = LED_OFF; led_stop_software_blink(led_cdev); @@ -157,15 +171,44 @@ static void led_set_software_blink(struct led_classdev *led_cdev, mod_timer(&led_cdev->blink_timer, jiffies + 1); } +static int led_set_hardware_blink_nosleep(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + /* We have a non-blocking call, use it */ + if (led_cdev->blink_set) + return led_cdev->blink_set(led_cdev, delay_on, delay_off); + + /* Tell the worker to set up the blinking hardware */ + if (delay_on) + led_cdev->blink_delay_on = *delay_on; + else + led_cdev->blink_delay_on = 0; + if (delay_off) + led_cdev->blink_delay_off = *delay_off; + else + led_cdev->blink_delay_off = 0; + set_bit(LED_BLINK_HW_SET, &led_cdev->work_flags); + schedule_work(&led_cdev->set_brightness_work); + + return 0; +} static void led_blink_setup(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { + /* + * If not oneshot, and we have hardware support for blinking, + * relay the request to the driver. + */ if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) && - led_cdev->blink_set && - !led_cdev->blink_set(led_cdev, delay_on, delay_off)) - return; + (led_cdev->blink_set || led_cdev->blink_set_blocking)) { + /* If this fails we fall back to software blink */ + if (!led_set_hardware_blink_nosleep(led_cdev, + delay_on, delay_off)) + return; + } /* blink with 1 Hz as default if nothing specified */ if (!*delay_on && !*delay_off) diff --git a/include/linux/leds.h b/include/linux/leds.h index 5263f87e1d2c..7050383d72a7 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -62,6 +62,7 @@ struct led_classdev { #define LED_BLINK_INVERT 3 #define LED_BLINK_BRIGHTNESS_CHANGE 4 #define LED_BLINK_DISABLE 5 +#define LED_BLINK_HW_SET 6 /* Set LED brightness level * Must not sleep. Use brightness_set_blocking for drivers @@ -89,6 +90,14 @@ struct led_classdev { int (*blink_set)(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off); + /* + * Set LED blinking immediately but may sleep (block) the caller + * to access the LED device register. + */ + int (*blink_set_blocking)(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off); + int (*pattern_set)(struct led_classdev *led_cdev, struct led_pattern *pattern, u32 len, int repeat);