From patchwork Tue Nov 27 15:01:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 152135 Delivered-To: patch@linaro.org Received: by 2002:a2e:299d:0:0:0:0:0 with SMTP id p29-v6csp1376833ljp; Tue, 27 Nov 2018 07:01:22 -0800 (PST) X-Google-Smtp-Source: AFSGD/Uun9+15TCXa+DQgsn5+CHTzhLscesG518qCciCmQW/6Q5TombOJ7GXBqy5XSXlqcJ8OXm2 X-Received: by 2002:a63:3204:: with SMTP id y4mr29327127pgy.41.1543330882306; Tue, 27 Nov 2018 07:01:22 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1543330882; cv=none; d=google.com; s=arc-20160816; b=CwmOY/avePSs8vbwzaNTs3jp5famX6pVs8/1uwOXsE8/uaKz1kWja0EqOUp1ya1RH2 9HiINVkjYyTmoKbKdFjf1EOPIuLmSGSs+oYcABB6HhRTX/MG71X9//dMvSFtTLCGhRja pKeyZmvgoP7JzYlV2M3jfLaI2gWUrVSFSPepAFLAmfYBN7VgNJ2lx15us9PTo3Z/RDbM 7g5IWPTK3xNyU0BuqN1z/dhmLOsgg0U5GawYfozRmEKB/LbBdYh+h7oDXekHfq6lslid BPflG939ncNTbIXxu1k0z35P/Ps1Yzmi2AjhAe0+x0ApJH7BiG0yUGBzWo1PZPnYypAv Y7wA== 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=lSM1mO2crFVUgYRaefDRQRZBgWNIxU8/2mNWfK3TRSY=; b=K6kvN8kWk3qaTSz6bd7dNRqd+B1wyQSJHQeVcAPieN/HVUWyctfYNcy3efg/5/44se NGPo1KcdLscnJHbLr54nxN0PwIdrTJfVd7/s7fpv5lkOMcI3BTHCXHutHUAI/il+Gyg5 nv+09/tJ/OFXMg27fuH+7Asi7dwDgc3E6z19mKShm4bvh/pr4GeC2lSWeIFGbgFPBaBR OdWCU2rVCeisXdXlbzPN3Id6vXvmEBSplkktFcxKSAF7RtMQT69bZr1V2tt5nIDsQb4R +SK44fZmxprEUvpqf0Z40ZF6qa+oyZJreWiI6qqmK9ptHikJvfrQEyiRgb2dkI1lc/Qj z63Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=W7srkRZL; 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 m75si4103208pga.432.2018.11.27.07.01.22; Tue, 27 Nov 2018 07:01:22 -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=W7srkRZL; 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 S1726587AbeK1B7c (ORCPT + 1 other); Tue, 27 Nov 2018 20:59:32 -0500 Received: from mail-lj1-f196.google.com ([209.85.208.196]:35327 "EHLO mail-lj1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726381AbeK1B7c (ORCPT ); Tue, 27 Nov 2018 20:59:32 -0500 Received: by mail-lj1-f196.google.com with SMTP id x85-v6so20358576ljb.2 for ; Tue, 27 Nov 2018 07:01:19 -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=lSM1mO2crFVUgYRaefDRQRZBgWNIxU8/2mNWfK3TRSY=; b=W7srkRZL87iT20cZmNVrd5Eg8XZvi/pK8lFMhKTpM8M2v4o6O6akhfBplbx1+BgyIH UppmCOExjBcCn4yLyi/2PWemWjveLJa7YukM3vD2IxSMP0FbSaPvEF2CQW1cSbVq/Om3 7kmHPAmJl2Sc0xtz62ybzHJGdj/8gM3+Sypl4= 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=lSM1mO2crFVUgYRaefDRQRZBgWNIxU8/2mNWfK3TRSY=; b=HWil4sajbogQaoxmkT9Hx2F8zd1ZZDVhdaLZb17l8zCDLlUC784XZGYHFbs8gOTdPE fmWYFd80AaOr/GAH+IM6O5pcWAmHCx0ZEuDS/WqrV1jLexPjEgVyU/HOmpGwsuD/Nuvn QXTxPUNFKWWzXJFNLkqRMi3B+taDCvBWobOeaeKYQ+6yeZkE2s+YM9T3/u15g1COkX0D egRaQy2smDoTTpoLwti5RiKbkzMZiBVx3zrUbwJhkPxl5EYA8/3ZJrYk9V7qhkZ1DUsP AL3YLVu2XXMNU9CEM8sPMy9WRObCBII1Des2+ohPnwS5M0woNKxZhyHAoclxbaYOBd5A S67w== X-Gm-Message-State: AA+aEWZSt/d68LCqzVSbRtMsLmtvQPt9pxI5guATeSnAFAR0kHJ++Yrf F045J/WzMjExHovHDANocDzRYw== X-Received: by 2002:a2e:9457:: with SMTP id o23-v6mr3877398ljh.7.1543330878703; Tue, 27 Nov 2018 07:01:18 -0800 (PST) Received: from genomnajs.ideon.se ([85.235.10.227]) by smtp.gmail.com with ESMTPSA id w16-v6sm614256ljw.11.2018.11.27.07.01.15 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Tue, 27 Nov 2018 07:01:17 -0800 (PST) From: Linus Walleij To: Jacek Anaszewski , Pavel Machek Cc: linux-leds@vger.kernel.org, Linus Walleij Subject: [PATCH] leds: core: Support blocking HW blink operations Date: Tue, 27 Nov 2018 16:01:06 +0100 Message-Id: <20181127150106.20213-1-linus.walleij@linaro.org> X-Mailer: git-send-email 2.19.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.19.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 7393a316d9fa..57f3df0774e7 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -61,6 +61,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 @@ -88,6 +89,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);