From patchwork Tue Aug 3 16:26:37 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= X-Patchwork-Id: 12416733 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=-17.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,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 59EC9C4338F for ; Tue, 3 Aug 2021 16:29:27 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 285CD61037 for ; Tue, 3 Aug 2021 16:29:27 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 285CD61037 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=collabora.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=UA4xEFmhldCO/hMB1678pDaV1l3yu48gbOcwmETWEOo=; b=Qrc+KQGtt+4Bz3 mSUtyyV8NE8hO1rgwrFXCCHdbhNxe7qrbhLmJAVhJgJ/ITViOe6c6dh+rVAfPgsNx+JHXp0XXlDSF vAKfB5G6tzBvOdERlqchSIyHznUniMxLC3lQgcuytX65GFTxLn5abcbcPTTdxExmHu+XC2dZyffBr tYb8C3RseLZtUZA8RPkilK49qE4K6tPkY4zRz0NCV0V6+igMToyCYQKu1j/i0uDQHT4Y88Ni0PJXa T221LhzrwyCns0KPi4Jc8XZsdHZVfihGyl699DSoJssVue0S9v+jLUIwafLYS5XRUcdTGHAfhb1N1 oHe88KFa/j16Th/mXh5Q==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mAxGE-003bnz-LN; Tue, 03 Aug 2021 16:27:22 +0000 Received: from bhuna.collabora.co.uk ([2a00:1098:0:82:1000:25:2eeb:e3e3]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mAxG2-003bky-OI for linux-arm-kernel@lists.infradead.org; Tue, 03 Aug 2021 16:27:12 +0000 Received: from localhost.localdomain (unknown [IPv6:2804:14c:1a9:2434:b693:c9:5cb6:b688]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: nfraprado) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id 13F7D1F423F0; Tue, 3 Aug 2021 17:27:03 +0100 (BST) From: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= To: Pavel Machek , Dan Murphy , Bjorn Andersson , Andy Gross , Rob Herring , Jacek Anaszewski Cc: linux-leds@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, Brian Masney , Luca Weiss , Russell King , Georgi Djakov , linux-kernel@vger.kernel.org, phone-devel@vger.kernel.org, ~postmarketos/upstreaming@lists.sr.ht, ~lkcamp/patches@lists.sr.ht, =?utf-8?q?Andr=C3=A9_Almeida?= , kernel@collabora.com Subject: [PATCH v3 1/5] dt-bindings: leds: Add binding for qcom-spmi-flash Date: Tue, 3 Aug 2021 13:26:37 -0300 Message-Id: <20210803162641.1525980-2-nfraprado@collabora.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210803162641.1525980-1-nfraprado@collabora.com> References: <20210803162641.1525980-1-nfraprado@collabora.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210803_092710_976988_DB0BEF22 X-CRM114-Status: GOOD ( 17.55 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Add devicetree binding for Qualcomm's SPMI Flash LEDs which are part of the PM8941 PMIC. These LEDs are used both as lantern and camera flash on phones based on the MSM8974 SoC, like the Nexus 5. Signed-off-by: Nícolas F. R. A. Prado Reviewed-by: Rob Herring --- Changes in v3: - Removed clamp-curr, headroom, startup-dly and safety-timer properties since they didn't impact the behavior. They're now hardcoded in the driver (patch 2) - Added valid ranges for led-max-microamp, flash-max-microamp and flash-max-timeout-us - Removed dt-bindings header file that held some constants (moved to inside the driver in patch 2) Added in v2 .../bindings/leds/leds-qcom-spmi-flash.yaml | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/leds-qcom-spmi-flash.yaml diff --git a/Documentation/devicetree/bindings/leds/leds-qcom-spmi-flash.yaml b/Documentation/devicetree/bindings/leds/leds-qcom-spmi-flash.yaml new file mode 100644 index 000000000000..da674d73c033 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-qcom-spmi-flash.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/leds-qcom-spmi-flash.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SPMI Flash LEDs + +maintainers: + - Nícolas F. R. A. Prado + +description: | + The Qualcomm SPMI Flash LEDs are part of Qualcomm PMICs and are used primarily + as a camera or video flash. They can also be used as a lantern when on torch + mode. + The PMIC is connected to Host processor via SPMI bus. + +properties: + compatible: + const: qcom,spmi-flash-leds + + reg: + maxItems: 1 + + flash-boost-supply: + description: SMBB regulator for LED flash mode + + torch-boost-supply: + description: SMBB regulator for LED torch mode + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + "^led@[0-1]$": + type: object + $ref: common.yaml# + + properties: + reg: + maxItems: 1 + + led-max-microamp: + minimum: 12500 + maximum: 200000 + multipleOf: 12500 + + flash-max-microamp: + minimum: 12500 + maximum: 1000000 + multipleOf: 12500 + + flash-max-timeout-us: + minimum: 10000 + maximum: 1280000 + multipleOf: 10000 + + required: + - reg + +required: + - compatible + - reg + - flash-boost-supply + - torch-boost-supply + +additionalProperties: false + +examples: + - | + #include + + spmi-flash-leds@d300 { + compatible = "qcom,spmi-flash-leds"; + reg = <0xd300 0x100>; + flash-boost-supply = <&pm8941_5vs1>; + torch-boost-supply = <&pm8941_5v>; + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + function = LED_FUNCTION_FLASH; + color = ; + led-max-microamp = <200000>; + flash-max-microamp = <1000000>; + flash-max-timeout-us = <1280000>; + }; + }; +... From patchwork Tue Aug 3 16:26:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= X-Patchwork-Id: 12416741 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=-17.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,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 68B85C4338F for ; Tue, 3 Aug 2021 16:30:04 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 2F89960F94 for ; Tue, 3 Aug 2021 16:30:04 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 2F89960F94 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=collabora.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=yeZX/Z7xgMHtxqTc+yq9/8AAhxPuBBHuKJCcDHtIBpw=; b=A9y7EbXOndeBby UOA9Zntx8k/sBkvqGRViM0UMs1KQjpejcPqkr4MlupMLR87SntC0nJHUmvdaGfsahpqW/V2pDvZw8 4HAFa2KCRdVzxCjYtEJAeLkxaMtJup7HQM2tjsuEMWvj7ZEYsxsVpz5prkYc0hx0AOhi4FGxacWou FQrAj3gX31UF2VyFQFvy7AKKKVyLXkmzjzR8Fg+HhAjbLOpBg4I1WhbW9w/csKVIT4uLyuc5HsZp2 MwihLSxBiE6F/JCm8pDyhqPrRp1VjfO4Rl4S0hfO7jO4pxip/UWRZTirxc6Qyijql8Yk3F7MNA/id TVcLuIB8QXX2Wnwc9Fkw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mAxGP-003bqS-5n; Tue, 03 Aug 2021 16:27:33 +0000 Received: from bhuna.collabora.co.uk ([46.235.227.227]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mAxG7-003bmP-PM for linux-arm-kernel@lists.infradead.org; Tue, 03 Aug 2021 16:27:19 +0000 Received: from localhost.localdomain (unknown [IPv6:2804:14c:1a9:2434:b693:c9:5cb6:b688]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: nfraprado) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id 927FA1F42404; Tue, 3 Aug 2021 17:27:09 +0100 (BST) From: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= To: Pavel Machek , Dan Murphy , Bjorn Andersson , Andy Gross , Rob Herring , Jacek Anaszewski Cc: linux-leds@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, Brian Masney , Luca Weiss , Russell King , Georgi Djakov , linux-kernel@vger.kernel.org, phone-devel@vger.kernel.org, ~postmarketos/upstreaming@lists.sr.ht, ~lkcamp/patches@lists.sr.ht, =?utf-8?q?Andr=C3=A9_Almeida?= , kernel@collabora.com Subject: [PATCH v3 2/5] leds: Add driver for QCOM SPMI Flash LEDs Date: Tue, 3 Aug 2021 13:26:38 -0300 Message-Id: <20210803162641.1525980-3-nfraprado@collabora.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210803162641.1525980-1-nfraprado@collabora.com> References: <20210803162641.1525980-1-nfraprado@collabora.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210803_092716_184386_E654AD0D X-CRM114-Status: GOOD ( 29.55 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Add driver for Qualcomm's SPMI Flash LEDs. These are controlled through an SPMI bus and are part of the PM8941 PMIC. There are two LEDs present on the chip, which can be used independently as camera flash or, when in torch mode, as a lantern. Signed-off-by: Nícolas F. R. A. Prado --- Changes in v3: - The two LEDs can now be operated independently even when in torch mode - The flashes can now strobe consecutive times without needing to manually disable them between strobes - Implemented strobe_get() - Moved dt parsing to a separate function - Split multiplexed torch/flash on/off and torch/flash regulator on/off functions - Set current on brightness callback and timeout on timeout callback, instead of setting everything every time when strobing/turning torch on - And a whole lot of other minor changes Changes in v2: - Thanks to Jacek: - Implemented flash LED class framework - Thanks to Bjorn: - Renamed driver to "qcom spmi flash" - Refactored code - Added missing copyright drivers/leds/flash/Kconfig | 8 + drivers/leds/flash/Makefile | 1 + drivers/leds/flash/leds-qcom-spmi-flash.c | 1251 +++++++++++++++++++++ 3 files changed, 1260 insertions(+) create mode 100644 drivers/leds/flash/leds-qcom-spmi-flash.c diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index 3f49f3edbffb..72f61323cd2a 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -24,4 +24,12 @@ config LEDS_RT8515 To compile this driver as a module, choose M here: the module will be called leds-rt8515. +config LEDS_QCOM_SPMI_FLASH + tristate "Support for QCOM SPMI Flash LEDs" + depends on REGMAP_SPMI + depends on OF + help + This option enables support for the flash/torch LEDs present in + Qualcomm's PM8941 PMIC. + endif # LEDS_CLASS_FLASH diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile index 09aee561f769..c141d277e9b6 100644 --- a/drivers/leds/flash/Makefile +++ b/drivers/leds/flash/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o obj-$(CONFIG_LEDS_RT8515) += leds-rt8515.o +obj-$(CONFIG_LEDS_QCOM_SPMI_FLASH) += leds-qcom-spmi-flash.o diff --git a/drivers/leds/flash/leds-qcom-spmi-flash.c b/drivers/leds/flash/leds-qcom-spmi-flash.c new file mode 100644 index 000000000000..9763707bb986 --- /dev/null +++ b/drivers/leds/flash/leds-qcom-spmi-flash.c @@ -0,0 +1,1251 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Qualcomm SPMI Flash LEDs Driver + * + * Copyright (c) 2020-2021, Nícolas F. R. A. Prado + * Copyright (c) 2021, Collabora Ltd. + * + * Based on QPNP LEDs driver from downstream MSM kernel sources. + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QCOM_FLASH_ADDR_PERIPHERAL_SUBTYPE 0x05 +#define QCOM_FLASH_ADDR_STATUS 0x10 +#define QCOM_FLASH_ADDR_SAFETY_TIMER 0x40 +#define QCOM_FLASH_ADDR_MAX_CURR 0x41 +#define QCOM_FLASH_ADDR_CURR_LED0 0x42 +#define QCOM_FLASH_ADDR_CURR_LED1 0x43 +#define QCOM_FLASH_ADDR_CLAMP_CURR 0x44 +#define QCOM_FLASH_ADDR_ENABLE_CONTROL 0x46 +#define QCOM_FLASH_ADDR_LED_STROBE_CTRL 0x47 +#define QCOM_FLASH_ADDR_LED_TMR_CTRL 0x48 +#define QCOM_FLASH_ADDR_HEADROOM 0x4A +#define QCOM_FLASH_ADDR_STARTUP_DELAY 0x4B +#define QCOM_FLASH_ADDR_MASK_ENABLE 0x4C +#define QCOM_FLASH_ADDR_VREG_OK_FORCE 0x4F +#define QCOM_FLASH_ADDR_LED_UNLOCK_SECURE 0xD0 +#define QCOM_FLASH_ADDR_LED_TORCH 0xE4 +#define QCOM_FLASH_ADDR_FAULT_DETECT 0x51 +#define QCOM_FLASH_ADDR_RAMP_RATE 0x54 +#define QCOM_FLASH_ADDR_VPH_PWR_DROOP 0x5A + +#define QCOM_FLASH_MAX_LEVEL 0x4F +#define QCOM_FLASH_TORCH_MAX_LEVEL 0x0F +#define QCOM_FLASH_NO_MASK 0x00 + +#define QCOM_FLASH_MASK_1 0x20 +#define QCOM_FLASH_MASK_REG_MASK 0xE0 +#define QCOM_FLASH_HEADROOM_MASK 0x03 +#define QCOM_FLASH_SAFETY_TIMER_MASK 0x7F +#define QCOM_FLASH_CURRENT_MASK 0xFF +#define QCOM_FLASH_MAX_CURRENT_MASK 0x7F +#define QCOM_FLASH_TMR_MASK 0x03 +#define QCOM_FLASH_TMR_WATCHDOG 0x03 +#define QCOM_FLASH_TMR_SAFETY 0x00 +#define QCOM_FLASH_FAULT_DETECT_MASK 0x80 +#define QCOM_FLASH_HW_VREG_OK 0x40 +#define QCOM_FLASH_VREG_MASK 0xC0 +#define QCOM_FLASH_STARTUP_DLY_MASK 0x02 +#define QCOM_FLASH_RAMP_RATE_MASK 0xBF +#define QCOM_FLASH_VPH_PWR_DROOP_MASK 0xF3 + +#define QCOM_FLASH_ENABLE_ALL 0xE0 +#define QCOM_FLASH_ENABLE_MODULE 0x80 +#define QCOM_FLASH_ENABLE_MODULE_MASK 0x80 +#define QCOM_FLASH_DISABLE_ALL 0x00 +#define QCOM_FLASH_ENABLE_MASK 0xE0 +#define QCOM_FLASH_ENABLE_LED0 0xC0 +#define QCOM_FLASH_ENABLE_LED1 0xA0 +#define QCOM_FLASH_INIT_MASK 0xE0 +#define QCOM_FLASH_SELFCHECK_ENABLE 0x80 +#define QCOM_FLASH_SELFCHECK_DISABLE 0x00 + +#define QCOM_FLASH_STROBE_SW 0xC0 +#define QCOM_FLASH_STROBE_HW 0x04 +#define QCOM_FLASH_STROBE_MASK 0xC7 +#define QCOM_FLASH_STROBE_LED0 0x80 +#define QCOM_FLASH_STROBE_LED1 0x40 + +#define QCOM_FLASH_TORCH_MASK 0x03 +#define QCOM_FLASH_LED_TORCH_ENABLE 0x00 +#define QCOM_FLASH_LED_TORCH_DISABLE 0x03 +#define QCOM_FLASH_UNLOCK_SECURE 0xA5 +#define QCOM_FLASH_SECURE_MASK 0xFF + +#define QCOM_FLASH_SUBTYPE_DUAL 0x01 +#define QCOM_FLASH_SUBTYPE_SINGLE 0x02 + +#define QCOM_FLASH_DURATION_STEP 10000 +#define QCOM_FLASH_DURATION_MIN 10000 +#define QCOM_FLASH_DURATION_DEFAULT 200000 + +#define QCOM_FLASH_CURRENT_STEP 12500 +#define QCOM_FLASH_CURRENT_MIN 12500 + +#define QCOM_FLASH_DEFAULT_CLAMP 200000 + +#define QCOM_FLASH_MASK_ON_LED0 0x8 +#define QCOM_FLASH_MASK_ON_LED1 0x2 +#define QCOM_FLASH_MASK_STATUS_TIMEDOUT 0x5 + +enum qcom_flash_headroom { + QCOM_FLASH_HEADROOM_250MV, + QCOM_FLASH_HEADROOM_300MV, + QCOM_FLASH_HEADROOM_400MV, + QCOM_FLASH_HEADROOM_500MV, +}; + +enum qcom_flash_startup_dly { + QCOM_FLASH_STARTUP_DLY_10US, + QCOM_FLASH_STARTUP_DLY_32US, + QCOM_FLASH_STARTUP_DLY_64US, + QCOM_FLASH_STARTUP_DLY_128US, +}; + +enum qcom_flash_ids { + QCOM_FLASH_ID_LED0, + QCOM_FLASH_ID_LED1, +}; + +/** + * struct qcom_flash_led - Represents each individual flash LED + * @fled_cdev: flash LED classdev + * @id: LED ID as given by enum qcom_flash_ids + * @default_on: default state for the LED + * @flash_enable_cmd: enable command for particular flash + * @flash_strobe_cmd: strobe command for particular flash + * @current_addr: address to write for current + * @mask_led_on: bitmask for STATUS register that shows if LED is on + * @flash_current_invalidated: whether the flash current in the current register + * was invalidated by torch usage + */ +struct qcom_flash_led { + struct led_classdev_flash fled_cdev; + enum qcom_flash_ids id; + bool default_on; + u8 flash_enable_cmd; + u8 flash_strobe_cmd; + u16 current_addr; + u8 mask_led_on; + bool flash_current_invalidated; +}; + +/** + * struct qcom_flash_device - QCOM SPMI Flash device, contains 2 flash LEDs + * @regmap: regmap used to access LED registers over SPMI + * @base: base register given in device tree + * @dev: device from devicetree + * @flash_supply: voltage regulator to supply the flashes + * @torch_supply: voltage regulator to supply torch mode + * @leds: flash LEDs + * @num_leds: number of LEDs registered (between 0 and 2) + * @lock: lock to protect SPMI transactions + * @torch_enable_cmd: enable flash LED torch mode + * @peripheral_subtype: module peripheral subtype + * @flash_regulator_on: flash regulator status + * @torch_regulator_on: torch regulator status + * @torch_enabled: whether torch mode is enabled + */ +struct qcom_flash_device { + struct regmap *regmap; + unsigned int base; + struct device *dev; + struct regulator *flash_supply; + struct regulator *torch_supply; + struct qcom_flash_led leds[2]; + u8 num_leds; + struct mutex lock; + u8 torch_enable_cmd; + unsigned int peripheral_subtype; + bool flash_regulator_on; + bool torch_regulator_on; + bool torch_enabled; +}; + +struct qcom_flash_config { + unsigned int base; + struct regulator *flash_supply; + struct regulator *torch_supply; + unsigned int num_leds; + + enum qcom_flash_ids id[2]; + bool default_on[2]; + u32 led_max_brightness[2]; + u32 flash_max_brightness[2]; + u32 flash_max_timeout[2]; +}; + +static inline struct qcom_flash_led *flcdev_to_led(struct led_classdev_flash *fled_cdev) +{ + return container_of(fled_cdev, struct qcom_flash_led, fled_cdev); +} + +static inline struct qcom_flash_device *led_to_leds_dev(struct qcom_flash_led *led) +{ + return container_of(led, struct qcom_flash_device, leds[led->id]); +} + +static inline int qcom_flash_read_reg(struct qcom_flash_device *leds_dev, + unsigned int reg, unsigned int *val) +{ + return regmap_read(leds_dev->regmap, leds_dev->base + reg, val); +} + +static inline int qcom_flash_masked_write(struct qcom_flash_device *leds_dev, + unsigned int reg, unsigned int mask, + unsigned int val) +{ + return regmap_update_bits(leds_dev->regmap, leds_dev->base + reg, mask, + val); +} + +static u8 qcom_flash_duration_to_reg(u32 us) +{ + if (us < QCOM_FLASH_DURATION_MIN) + us = QCOM_FLASH_DURATION_MIN; + return (us - QCOM_FLASH_DURATION_MIN) / QCOM_FLASH_DURATION_STEP; +} + +static u8 qcom_flash_current_to_reg(u32 ua) +{ + if (ua < QCOM_FLASH_CURRENT_MIN) + ua = QCOM_FLASH_CURRENT_MIN; + return (ua - QCOM_FLASH_CURRENT_MIN) / QCOM_FLASH_CURRENT_STEP; +} + +static void clamp_align(u32 *v, u32 min, u32 max, u32 step) +{ + *v = clamp_val(*v, min, max); + if (step > 1) + *v = (*v - min) / step * step + min; +} + +static int qcom_flash_status_get(struct qcom_flash_led *led) +{ + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + unsigned int status; + int rc; + + rc = qcom_flash_read_reg(leds_dev, QCOM_FLASH_ADDR_STATUS, &status); + if (rc) { + dev_err(leds_dev->dev, "Failure reading status, rc = %d\n", + rc); + return rc; + } + + return status & led->mask_led_on; +} + +static int qcom_flash_status_clear(struct qcom_flash_device *leds_dev) +{ + unsigned int enable_val; + int rc; + + rc = qcom_flash_read_reg(leds_dev, QCOM_FLASH_ADDR_ENABLE_CONTROL, + &enable_val); + if (rc) { + dev_err(leds_dev->dev, "Enable reg read failed(%d)\n", rc); + return rc; + } + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_ENABLE_CONTROL, + QCOM_FLASH_ENABLE_MASK, QCOM_FLASH_DISABLE_ALL); + if (rc) { + dev_err(leds_dev->dev, "Enable reg write failed(%d)\n", rc); + return rc; + } + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_ENABLE_CONTROL, + QCOM_FLASH_ENABLE_MASK, enable_val); + if (rc) { + dev_err(leds_dev->dev, "Enable reg write failed(%d)\n", rc); + return rc; + } + + return 0; +} + +static int qcom_flash_check_timedout(struct qcom_flash_device *leds_dev) +{ + unsigned int status; + int rc; + + rc = qcom_flash_read_reg(leds_dev, QCOM_FLASH_ADDR_STATUS, &status); + if (rc) { + dev_err(leds_dev->dev, "Failure reading status, rc = %d\n", + rc); + return rc; + } + + return status & QCOM_FLASH_MASK_STATUS_TIMEDOUT; +} + +static int qcom_flash_torch_reg_enable(struct qcom_flash_device *leds_dev, + bool state) +{ + int rc; + + if (leds_dev->torch_enabled == state) + return 0; + + /* + * For the TORCH register (0xE4) to become writable, the UNLOCK_SECURE + * register (0xD0) needs to be written with the UNLOCK_SECURE value + * (0xA5) first. + * It gets re-locked after any register write. + */ + rc = qcom_flash_masked_write(leds_dev, + QCOM_FLASH_ADDR_LED_UNLOCK_SECURE, + QCOM_FLASH_SECURE_MASK, + QCOM_FLASH_UNLOCK_SECURE); + if (rc) { + dev_err(leds_dev->dev, "Secure reg write failed(%d)\n", rc); + return rc; + } + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_LED_TORCH, + QCOM_FLASH_TORCH_MASK, + state ? QCOM_FLASH_LED_TORCH_ENABLE : + QCOM_FLASH_LED_TORCH_DISABLE); + if (rc) { + dev_err(leds_dev->dev, "Torch reg write failed(%d)\n", rc); + return rc; + } + + return 0; +} + +static int qcom_flash_max_brightness_set(struct qcom_flash_led *led, + unsigned int brightness) +{ + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + struct device *dev = leds_dev->dev; + int rc; + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_MAX_CURR, + QCOM_FLASH_CURRENT_MASK, brightness); + if (rc) { + dev_err(dev, "Max current reg write failed(%d)\n", rc); + return rc; + } + + return 0; +} + +static int qcom_flash_brightness_set(struct qcom_flash_led *led, + unsigned int brightness) +{ + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + struct device *dev = leds_dev->dev; + int rc; + + rc = qcom_flash_masked_write(leds_dev, led->current_addr, + QCOM_FLASH_CURRENT_MASK, brightness); + if (rc) { + dev_err(dev, "Current reg write failed(%d)\n", rc); + return rc; + } + + return 0; +} + +static int qcom_flash_fled_regulator_on(struct qcom_flash_device *leds_dev) +{ + int rc; + + if (leds_dev->flash_regulator_on) + return 0; + + rc = regulator_enable(leds_dev->flash_supply); + if (rc) { + dev_err(leds_dev->dev, "Regulator enable failed(%d)\n", rc); + return rc; + } + + leds_dev->flash_regulator_on = true; + + return 0; +} + +static int qcom_flash_fled_regulator_off(struct qcom_flash_device *leds_dev) +{ + unsigned int i; + u8 enable = 0; + int rc; + + if (!leds_dev->flash_regulator_on) + return 0; + + for (i = 0; i < leds_dev->num_leds; i++) { + rc = qcom_flash_status_get(&leds_dev->leds[i]); + if (rc < 0) + return rc; + + if (!rc) + continue; + + enable |= leds_dev->leds[i].flash_enable_cmd; + } + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_ENABLE_CONTROL, + QCOM_FLASH_ENABLE_MASK, enable); + if (rc) + dev_err(leds_dev->dev, "Enable reg write failed(%d)\n", rc); + + if (enable) + return 0; + + rc = regulator_disable(leds_dev->flash_supply); + if (rc) { + dev_err(leds_dev->dev, "Regulator disable failed(%d)\n", rc); + return rc; + } + + leds_dev->flash_regulator_on = false; + + return 0; +} + +static int qcom_flash_torch_regulator_on(struct qcom_flash_device *leds_dev) +{ + int rc; + + if (leds_dev->torch_regulator_on) + return 0; + + rc = regulator_enable(leds_dev->torch_supply); + if (rc) { + dev_err(leds_dev->dev, "Regulator enable failed(%d)\n", rc); + return rc; + } + + leds_dev->torch_regulator_on = true; + + return 0; +} + +static int qcom_flash_torch_regulator_off(struct qcom_flash_device *leds_dev) +{ + int rc; + + if (!leds_dev->torch_regulator_on) + return 0; + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_ENABLE_CONTROL, + QCOM_FLASH_ENABLE_MODULE_MASK, + QCOM_FLASH_DISABLE_ALL); + if (rc) + dev_err(leds_dev->dev, "Enable reg write failed(%d)\n", rc); + + rc = regulator_disable(leds_dev->torch_supply); + if (rc) { + dev_err(leds_dev->dev, "Regulator disable failed(%d)\n", rc); + return rc; + } + + leds_dev->torch_regulator_on = false; + + return 0; +} + +static int qcom_flash_fled_on(struct qcom_flash_led *led) +{ + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + struct device *dev = leds_dev->dev; + int rc, error; + + rc = qcom_flash_fled_regulator_on(leds_dev); + if (rc) + goto error_flash_set; + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_ENABLE_CONTROL, + led->flash_enable_cmd, + led->flash_enable_cmd); + if (rc) { + dev_err(dev, "Enable reg write failed(%d)\n", rc); + goto error_flash_set; + } + + /* + * TODO The downstream driver also allowed HW strobe. Add support for it + * after v4l2 support is added and ISP can be used + */ + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_LED_STROBE_CTRL, + led->flash_strobe_cmd, + led->flash_strobe_cmd); + if (rc) { + dev_err(dev, "LED %d strobe reg write failed(%d)\n", led->id, + rc); + goto error_flash_set; + } + + return 0; + +error_flash_set: + error = qcom_flash_fled_regulator_off(leds_dev); + if (error) + return error; + return rc; +} + +static int qcom_flash_fled_off(struct qcom_flash_led *led) +{ + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + struct device *dev = leds_dev->dev; + int rc, error; + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_LED_STROBE_CTRL, + led->flash_strobe_cmd, + QCOM_FLASH_DISABLE_ALL); + if (rc) + dev_err(dev, "LED %d flash write failed(%d)\n", led->id, rc); + + error = qcom_flash_fled_regulator_off(leds_dev); + if (error) + return error; + return rc; +} + +static int qcom_flash_torch_on(struct qcom_flash_led *led) +{ + int rc, error; + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + struct device *dev = leds_dev->dev; + + if (leds_dev->peripheral_subtype == QCOM_FLASH_SUBTYPE_DUAL) { + rc = qcom_flash_torch_regulator_on(leds_dev); + if (rc) + goto error_reg_write; + } else if (leds_dev->peripheral_subtype == QCOM_FLASH_SUBTYPE_SINGLE) { + rc = qcom_flash_fled_regulator_on(leds_dev); + if (rc) + goto error_flash_set; + + /* + * Write 0x80 to MODULE_ENABLE before writing + * 0xE0 in order to avoid a hardware bug caused + * by register value going from 0x00 to 0xE0. + */ + rc = qcom_flash_masked_write(leds_dev, + QCOM_FLASH_ADDR_ENABLE_CONTROL, + QCOM_FLASH_ENABLE_MODULE_MASK, + QCOM_FLASH_ENABLE_MODULE); + if (rc) { + dev_err(dev, "Enable reg write failed(%d)\n", rc); + goto error_flash_set; + } + } + + rc = qcom_flash_torch_reg_enable(leds_dev, true); + if (rc) + goto error_reg_write; + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_ENABLE_CONTROL, + QCOM_FLASH_ENABLE_MASK, + leds_dev->torch_enable_cmd); + if (rc) { + dev_err(dev, "Enable reg write failed(%d)\n", rc); + goto error_reg_write; + } + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_LED_STROBE_CTRL, + led->flash_strobe_cmd, + led->flash_strobe_cmd); + if (rc) { + dev_err(dev, "LED %d strobe reg write failed(%d)\n", led->id, + rc); + goto error_reg_write; + } + + leds_dev->torch_enabled = true; + + return 0; + +error_reg_write: + if (leds_dev->peripheral_subtype == QCOM_FLASH_SUBTYPE_SINGLE) + goto error_flash_set; + + error = qcom_flash_torch_regulator_off(leds_dev); + if (error) + return error; + return rc; + +error_flash_set: + error = qcom_flash_fled_regulator_off(leds_dev); + if (error) + return error; + return rc; +} + +static int qcom_flash_torch_off(struct qcom_flash_led *led) +{ + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + struct device *dev = leds_dev->dev; + int rc, error; + unsigned int i; + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_LED_STROBE_CTRL, + led->flash_strobe_cmd, QCOM_FLASH_DISABLE_ALL); + if (rc) { + dev_err(dev, "LED %d flash write failed(%d)\n", led->id, rc); + goto error_torch_set; + } + + /* Keep torch on if the other LED is still on */ + for (i = 0; i < leds_dev->num_leds; i++) + if (leds_dev->leds[i].fled_cdev.led_cdev.brightness) + return 0; + + rc = qcom_flash_torch_reg_enable(leds_dev, false); + if (rc) + goto error_torch_set; + + if (leds_dev->peripheral_subtype == QCOM_FLASH_SUBTYPE_DUAL) { + rc = qcom_flash_torch_regulator_off(leds_dev); + if (rc) + return rc; + } else if (leds_dev->peripheral_subtype == QCOM_FLASH_SUBTYPE_SINGLE) { + rc = qcom_flash_fled_regulator_off(leds_dev); + if (rc) + return rc; + } + + leds_dev->torch_enabled = false; + + return 0; + +error_torch_set: + error = qcom_flash_torch_regulator_off(leds_dev); + if (error) + return error; + return rc; +} + +static int qcom_flash_ledcdev_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + struct qcom_flash_led *led = flcdev_to_led(fled_cdev); + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + unsigned int max_brightness; + int rc; + + if (value > led_cdev->max_brightness) { + dev_err(leds_dev->dev, "Invalid brightness value\n"); + return -EINVAL; + } + + mutex_lock(&leds_dev->lock); + if (!value) { + rc = qcom_flash_torch_off(led); + } else { + /* + * The minimum on value for the brightness register is 0, but for + * led_classdev is 1, as 0 there means off. + */ + rc = qcom_flash_brightness_set(led, led_cdev->brightness - 1); + if (rc) + goto unlock; + + led->flash_current_invalidated = true; + + if (leds_dev->torch_enabled) { + /* Clear status to update brightness */ + rc = qcom_flash_status_clear(leds_dev); + if (rc) + goto unlock; + } else { + /* + * Clear status from previous flash strobe so the LED + * can turn on + */ + rc = qcom_flash_check_timedout(leds_dev); + if (rc > 0) + rc = qcom_flash_status_clear(leds_dev); + else if (rc < 0) + goto unlock; + + max_brightness = led_cdev->max_brightness - 1; + rc = qcom_flash_max_brightness_set(led, max_brightness); + if (rc) + goto unlock; + } + rc = qcom_flash_torch_on(led); + } + +unlock: + mutex_unlock(&leds_dev->lock); + return rc; +} + +static int qcom_flash_flcdev_brightness_set(struct led_classdev_flash *fled_cdev, + u32 brightness) +{ + struct qcom_flash_led *led = flcdev_to_led(fled_cdev); + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + unsigned int bright; + int rc; + + /* Can't operate on flash if torch is on */ + if (leds_dev->torch_enabled) + return -EBUSY; + + clamp_align(&brightness, QCOM_FLASH_CURRENT_MIN, + fled_cdev->brightness.max, QCOM_FLASH_CURRENT_STEP); + fled_cdev->brightness.val = brightness; + + bright = qcom_flash_current_to_reg(brightness); + + mutex_lock(&leds_dev->lock); + rc = qcom_flash_brightness_set(led, bright); + if (rc) + goto unlock; + + if (led->flash_current_invalidated) { + bright = qcom_flash_current_to_reg(fled_cdev->brightness.max); + rc = qcom_flash_max_brightness_set(led, bright); + if (rc) + goto unlock; + } + + led->flash_current_invalidated = false; + +unlock: + mutex_unlock(&leds_dev->lock); + return rc; +} + +static int qcom_flash_flcdev_strobe_set(struct led_classdev_flash *fled_cdev, + bool state) +{ + struct qcom_flash_led *led = flcdev_to_led(fled_cdev); + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + unsigned int bright; + unsigned int i; + int rc; + + /* Can't operate on flash if torch is on */ + if (leds_dev->torch_enabled) + return -EBUSY; + + mutex_lock(&leds_dev->lock); + if (!state) { + rc = qcom_flash_fled_off(led); + } else { + /* + * Turn off flash LEDs from previous strobe + */ + rc = qcom_flash_check_timedout(leds_dev); + if (rc > 0) { + for (i = 0; i < leds_dev->num_leds; i++) { + rc = qcom_flash_fled_off(&leds_dev->leds[i]); + if (rc) + goto unlock; + } + } else if (rc < 0) { + goto unlock; + } + + if (led->flash_current_invalidated) { + bright = qcom_flash_current_to_reg(fled_cdev->brightness.val); + rc = qcom_flash_brightness_set(led, bright); + if (rc) + goto unlock; + + bright = qcom_flash_current_to_reg(fled_cdev->brightness.max); + rc = qcom_flash_max_brightness_set(led, bright); + if (rc) + goto unlock; + + led->flash_current_invalidated = false; + } + + rc = qcom_flash_fled_on(led); + } + +unlock: + mutex_unlock(&leds_dev->lock); + return rc; +} + +static int qcom_flash_flcdev_strobe_get(struct led_classdev_flash *fled_cdev, + bool *state) +{ + struct qcom_flash_led *led = flcdev_to_led(fled_cdev); + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + int status; + + mutex_lock(&leds_dev->lock); + status = qcom_flash_status_get(led); + mutex_unlock(&leds_dev->lock); + if (status < 0) + return status; + + *state = status && !leds_dev->torch_enabled; + return 0; +} + +static int qcom_flash_flcdev_timeout_set(struct led_classdev_flash *fled_cdev, + u32 timeout) +{ + struct qcom_flash_led *led = flcdev_to_led(fled_cdev); + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + unsigned int time, i; + int rc; + + clamp_align(&timeout, QCOM_FLASH_DURATION_MIN, fled_cdev->timeout.max, + QCOM_FLASH_DURATION_STEP); + + /* Since the timeout register is shared between LEDs, update for all */ + for (i = 0; i < leds_dev->num_leds; i++) + leds_dev->leds[i].fled_cdev.timeout.val = timeout; + + time = qcom_flash_duration_to_reg(fled_cdev->timeout.val); + + mutex_lock(&leds_dev->lock); + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_SAFETY_TIMER, + QCOM_FLASH_SAFETY_TIMER_MASK, time); + if (rc) + dev_err(leds_dev->dev, "Safety timer reg write failed(%d)\n", + rc); + mutex_unlock(&leds_dev->lock); + + return rc; +} + +static int qcom_flash_flcdev_fault_get(struct led_classdev_flash *fled_cdev, + u32 *fault) +{ + /* + * TODO Add fault detection when we find out how to. No clue from + * inspecting the SPMI registers + */ + *fault = 0; + return 0; +} + +static const struct led_flash_ops flash_ops = { + .flash_brightness_set = qcom_flash_flcdev_brightness_set, + .strobe_set = qcom_flash_flcdev_strobe_set, + .strobe_get = qcom_flash_flcdev_strobe_get, + .timeout_set = qcom_flash_flcdev_timeout_set, + .fault_get = qcom_flash_flcdev_fault_get, +}; + +static int qcom_flash_setup_flcdev(struct qcom_flash_config *cfg, + struct qcom_flash_led *led, + struct device_node *node) +{ + int rc; + struct device *dev = led_to_leds_dev(led)->dev; + struct led_classdev_flash *fled_cdev = &led->fled_cdev; + struct led_classdev *led_cdev = &fled_cdev->led_cdev; + struct led_init_data init_data = {}; + struct led_flash_setting *setting; + + init_data.fwnode = of_fwnode_handle(node); + + led_cdev->brightness_set_blocking = qcom_flash_ledcdev_brightness_set; + + led_cdev->max_brightness = cfg->led_max_brightness[led->id]; + led_cdev->max_brightness /= QCOM_FLASH_CURRENT_STEP; + + setting = &fled_cdev->brightness; + setting->max = cfg->flash_max_brightness[led->id]; + setting->min = QCOM_FLASH_CURRENT_MIN; + setting->step = QCOM_FLASH_CURRENT_STEP; + setting->val = setting->min; + + setting = &fled_cdev->timeout; + setting->max = cfg->flash_max_timeout[led->id]; + setting->min = QCOM_FLASH_DURATION_MIN; + setting->step = QCOM_FLASH_DURATION_STEP; + setting->val = QCOM_FLASH_DURATION_DEFAULT; + + fled_cdev->ops = &flash_ops; + led_cdev->flags |= LED_DEV_CAP_FLASH; + + rc = devm_led_classdev_flash_register_ext(dev, fled_cdev, &init_data); + if (rc) + dev_err(dev, "unable to register led %d,rc=%d\n", led->id, rc); + + return rc; +} + +static int qcom_flash_setup_fled(struct qcom_flash_config *cfg, + struct qcom_flash_led *led, + enum qcom_flash_ids id) +{ + led->id = cfg->id[id]; + led->default_on = cfg->default_on[id]; + led->flash_current_invalidated = true; + + if (led->id == QCOM_FLASH_ID_LED0) { + led->flash_enable_cmd = QCOM_FLASH_ENABLE_LED0; + led->flash_strobe_cmd = QCOM_FLASH_STROBE_LED0; + led->current_addr = QCOM_FLASH_ADDR_CURR_LED0; + led->mask_led_on = QCOM_FLASH_MASK_ON_LED0; + } else { + led->flash_enable_cmd = QCOM_FLASH_ENABLE_LED1; + led->flash_strobe_cmd = QCOM_FLASH_STROBE_LED1; + led->current_addr = QCOM_FLASH_ADDR_CURR_LED1; + led->mask_led_on = QCOM_FLASH_MASK_ON_LED1; + } + + return 0; +} + +static int qcom_flash_setup_regs(struct qcom_flash_device *leds_dev) +{ + int rc; + + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_LED_STROBE_CTRL, + QCOM_FLASH_STROBE_MASK, + QCOM_FLASH_DISABLE_ALL); + if (rc) { + dev_err(leds_dev->dev, "LED flash write failed(%d)\n", rc); + return rc; + } + + /* Disable flash LED module */ + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_ENABLE_CONTROL, + QCOM_FLASH_ENABLE_MASK, + QCOM_FLASH_DISABLE_ALL); + if (rc) { + dev_err(leds_dev->dev, "Enable reg write failed(%d)\n", rc); + return rc; + } + + /* Set Vreg force */ + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_VREG_OK_FORCE, + QCOM_FLASH_VREG_MASK, + QCOM_FLASH_HW_VREG_OK); + if (rc) { + dev_err(leds_dev->dev, "Vreg OK reg write failed(%d)\n", rc); + return rc; + } + + /* Set self fault check */ + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_FAULT_DETECT, + QCOM_FLASH_FAULT_DETECT_MASK, + QCOM_FLASH_SELFCHECK_DISABLE); + if (rc) { + dev_err(leds_dev->dev, "Fault detect reg write failed(%d)\n", + rc); + return rc; + } + + /* Set mask enable */ + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_MASK_ENABLE, + QCOM_FLASH_MASK_REG_MASK, + QCOM_FLASH_MASK_1); + if (rc) { + dev_err(leds_dev->dev, "Mask enable reg write failed(%d)\n", + rc); + return rc; + } + + /* Set ramp rate */ + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_RAMP_RATE, + QCOM_FLASH_RAMP_RATE_MASK, 0xBF); + if (rc) { + dev_err(leds_dev->dev, "Ramp rate reg write failed(%d)\n", rc); + return rc; + } + + /* Enable VPH_PWR_DROOP and set threshold to 2.9V */ + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_VPH_PWR_DROOP, + QCOM_FLASH_VPH_PWR_DROOP_MASK, 0xC2); + if (rc) { + dev_err(leds_dev->dev, "VPH_PWR_DROOP reg write failed(%d)\n", + rc); + return rc; + } + + /* Set headroom */ + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_HEADROOM, + QCOM_FLASH_HEADROOM_MASK, + QCOM_FLASH_HEADROOM_500MV); + if (rc) { + dev_err(leds_dev->dev, "Headroom reg write failed(%d)\n", rc); + return rc; + } + + /* Set startup delay */ + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_STARTUP_DELAY, + QCOM_FLASH_STARTUP_DLY_MASK, + QCOM_FLASH_STARTUP_DLY_10US); + if (rc) { + dev_err(leds_dev->dev, "Startup delay reg write failed(%d)\n", + rc); + return rc; + } + + /* + * TODO The downstream driver also supported watchdog mode. Not sure + * about the difference, so only support safety timer for now + */ + /* Set timer control */ + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_LED_TMR_CTRL, + QCOM_FLASH_TMR_MASK, + QCOM_FLASH_TMR_SAFETY); + if (rc) { + dev_err(leds_dev->dev, "LED timer ctrl reg write failed(%d)\n", + rc); + return rc; + } + + /* Set clamp current */ + rc = qcom_flash_masked_write(leds_dev, QCOM_FLASH_ADDR_CLAMP_CURR, + QCOM_FLASH_CURRENT_MASK, + qcom_flash_current_to_reg(QCOM_FLASH_DEFAULT_CLAMP)); + if (rc) + dev_err(leds_dev->dev, "Clamp current reg write failed(%d)\n", + rc); + + return rc; +} + +static int qcom_flash_setup_led(struct qcom_flash_config *cfg, + struct qcom_flash_led *led, + enum qcom_flash_ids id, + struct device_node *node) +{ + struct qcom_flash_device *leds_dev = led_to_leds_dev(led); + struct led_classdev *led_cdev = &led->fled_cdev.led_cdev; + int rc; + + rc = qcom_flash_setup_fled(cfg, led, id); + if (rc) + return rc; + + rc = qcom_flash_setup_flcdev(cfg, led, node); + if (rc) + return rc; + + /* configure default state */ + if (!led->default_on) { + led_cdev->brightness = LED_OFF; + } else { + led_cdev->brightness = led_cdev->max_brightness; + mutex_lock(&leds_dev->lock); + rc = qcom_flash_torch_on(led); + mutex_unlock(&leds_dev->lock); + if (rc) + return rc; + } + + return 0; +} + +static int qcom_flash_setup_leds_device(struct qcom_flash_device *leds_dev, + struct qcom_flash_config *cfg, + struct device *dev) +{ + int rc; + + leds_dev->dev = dev; + + leds_dev->base = cfg->base; + leds_dev->num_leds = cfg->num_leds; + + leds_dev->regmap = dev_get_regmap(dev->parent, NULL); + if (!leds_dev->regmap) { + dev_err(dev, "Failure getting regmap\n"); + return -ENODEV; + } + + rc = qcom_flash_setup_regs(leds_dev); + if (rc) + return rc; + + rc = qcom_flash_read_reg(leds_dev, QCOM_FLASH_ADDR_PERIPHERAL_SUBTYPE, + &leds_dev->peripheral_subtype); + if (rc) { + dev_err(dev, "Unable to read from addr=%x, rc(%d)\n", + QCOM_FLASH_ADDR_PERIPHERAL_SUBTYPE, rc); + return rc; + } + + leds_dev->flash_supply = cfg->flash_supply; + + if (leds_dev->peripheral_subtype == QCOM_FLASH_SUBTYPE_DUAL) { + leds_dev->torch_supply = cfg->torch_supply; + leds_dev->torch_enable_cmd = QCOM_FLASH_ENABLE_MODULE; + } else { + leds_dev->torch_enable_cmd = QCOM_FLASH_ENABLE_ALL; + } + + mutex_init(&leds_dev->lock); + + return 0; +} + +static int qcom_flash_parse_dt(struct device *dev, + struct qcom_flash_config *cfg, + struct device_node *sub_nodes[]) +{ + struct device_node *node = dev->of_node; + struct device_node *child_node; + const char *temp_string; + int rc, parsed_leds = 0; + u32 val; + + rc = of_property_read_u32(node, "reg", &val); + if (rc < 0) { + dev_err(dev, "Failure reading reg, rc = %d\n", rc); + return rc; + } + cfg->base = val; + + cfg->flash_supply = devm_regulator_get(dev, "flash-boost"); + cfg->torch_supply = devm_regulator_get(dev, "torch-boost"); + + for_each_available_child_of_node(node, child_node) { + /* LED properties */ + + rc = of_property_read_u32(child_node, "reg", + &cfg->id[parsed_leds]); + if (rc) { + dev_err(dev, "Failure reading led id, rc = %d\n", rc); + of_node_put(child_node); + return rc; + } + + /* Assume LED IDs are ordered in DT for simplicity */ + if (cfg->id[parsed_leds] != parsed_leds) { + dev_err(dev, "Unordered LED nodes in DT\n"); + of_node_put(child_node); + return -EINVAL; + } + + sub_nodes[parsed_leds] = child_node; + + rc = of_property_read_string(child_node, "default-state", + &temp_string); + if (!rc) { + if (strncmp(temp_string, "on", sizeof("on")) == 0) + cfg->default_on[parsed_leds] = true; + } else if (rc != -EINVAL) { + dev_err(dev, + "Failure reading default-state, rc = %d\n", + rc); + of_node_put(child_node); + return rc; + } + + /* FLED properties */ + + rc = of_property_read_u32(child_node, "led-max-microamp", + &cfg->led_max_brightness[parsed_leds]); + if (rc < 0) { + dev_err(dev, "Failure reading max_current, rc = %d\n", + rc); + of_node_put(child_node); + return rc; + } + + rc = of_property_read_u32(child_node, "flash-max-microamp", + &cfg->flash_max_brightness[parsed_leds]); + if (rc < 0) { + dev_err(dev, "Failure reading max_current, rc = %d\n", + rc); + of_node_put(child_node); + return rc; + } + + rc = of_property_read_u32(child_node, "flash-max-timeout-us", + &cfg->flash_max_timeout[parsed_leds]); + if (rc < 0) { + dev_err(dev, "Failure reading max_timeout, rc = %d\n", + rc); + of_node_put(child_node); + return rc; + } + + parsed_leds++; + if (parsed_leds >= 2) { + of_node_put(child_node); + break; + } + } + cfg->num_leds = parsed_leds; + + return 0; +} + +static int qcom_flash_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qcom_flash_device *leds_dev; + struct qcom_flash_config cfg = { 0 }; + struct device_node *sub_nodes[2]; + unsigned int i; + int rc; + + leds_dev = devm_kzalloc(dev, sizeof(*leds_dev), GFP_KERNEL); + if (!leds_dev) + return -ENOMEM; + + rc = qcom_flash_parse_dt(dev, &cfg, sub_nodes); + if (rc) + return rc; + + rc = qcom_flash_setup_leds_device(leds_dev, &cfg, dev); + if (rc) + return rc; + + for (i = 0; i < leds_dev->num_leds; i++) { + rc = qcom_flash_setup_led(&cfg, &leds_dev->leds[i], i, + sub_nodes[i]); + if (rc) + return rc; + } + + platform_set_drvdata(pdev, leds_dev); + + return 0; +} + +static int qcom_flash_remove(struct platform_device *pdev) +{ + struct qcom_flash_device *leds_dev = platform_get_drvdata(pdev); + + mutex_destroy(&leds_dev->lock); + + return 0; +} + +static const struct of_device_id qcom_flash_spmi_of_match[] = { + { .compatible = "qcom,spmi-flash-leds" }, + {}, +}; +MODULE_DEVICE_TABLE(of, qcom_flash_spmi_of_match); + +static struct platform_driver qcom_flash_driver = { + .driver = { + .name = "qcom,spmi-flash-leds", + .of_match_table = qcom_flash_spmi_of_match, + }, + .probe = qcom_flash_probe, + .remove = qcom_flash_remove, +}; +module_platform_driver(qcom_flash_driver); + +MODULE_DESCRIPTION("Qualcomm SPMI Flash LED driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Nícolas F. R. A. Prado "); From patchwork Tue Aug 3 16:26:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= X-Patchwork-Id: 12416735 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=-17.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,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 066FBC4338F for ; Tue, 3 Aug 2021 16:29:53 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B4ECB60243 for ; Tue, 3 Aug 2021 16:29:52 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org B4ECB60243 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=collabora.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=4LBM8+iS8dWB/UmlPNbYbL0Rd4yrr6pAfHOZOp/xzKU=; b=mjmiBRc7q25bb3 Hd9bQEW1XNFO7exoMS4/unw3+FKeDfwx81odgjHXBb4x+JGFYXK5r9jPkngiCdKzjHx7poZ8CM2O5 hNVl6tLVOTYnZIib93eRdGpBqbrZJGRk58ap6SPqV56OIDEGjQbp4pPiRtDqi4q15iIY85/MoBfP1 zaX3/bwLG5CqNZCMCGLu05om3bjeaBEHhB9onRlKsOb4so/c7JSKbZYJ5eXIvsoVPwd9/H711zhrL GfhMv537TGAbT/jJnK3FGpp7ghB+gltfG9AWfqg5dp7tCo6Do0rutcSgrmMA/LuhybDUT5npZ/q88 Pmup7cQ7EpzPADszy+pA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mAxGd-003buc-Qr; Tue, 03 Aug 2021 16:27:47 +0000 Received: from bhuna.collabora.co.uk ([2a00:1098:0:82:1000:25:2eeb:e3e3]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mAxGD-003bnl-G6 for linux-arm-kernel@lists.infradead.org; Tue, 03 Aug 2021 16:27:22 +0000 Received: from localhost.localdomain (unknown [IPv6:2804:14c:1a9:2434:b693:c9:5cb6:b688]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: nfraprado) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id 527AC1F423F0; Tue, 3 Aug 2021 17:27:15 +0100 (BST) From: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= To: Pavel Machek , Dan Murphy , Bjorn Andersson , Andy Gross , Rob Herring , Jacek Anaszewski Cc: linux-leds@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, Brian Masney , Luca Weiss , Russell King , Georgi Djakov , linux-kernel@vger.kernel.org, phone-devel@vger.kernel.org, ~postmarketos/upstreaming@lists.sr.ht, ~lkcamp/patches@lists.sr.ht, =?utf-8?q?Andr=C3=A9_Almeida?= , kernel@collabora.com Subject: [PATCH v3 3/5] ARM: qcom_defconfig: Enable QCOM SPMI Flash LEDs Date: Tue, 3 Aug 2021 13:26:39 -0300 Message-Id: <20210803162641.1525980-4-nfraprado@collabora.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210803162641.1525980-1-nfraprado@collabora.com> References: <20210803162641.1525980-1-nfraprado@collabora.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210803_092721_728052_903FE4D5 X-CRM114-Status: GOOD ( 11.63 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Enable module for the Qualcomm SPMI Flash LEDs present on the PM8941 PMIC. These LEDs are used on phones based on the MSM8974 SoC like the Nexus 5. Also enable LED flash class since these LEDs make use of it. Signed-off-by: Nícolas F. R. A. Prado --- Changes in v3: - Made CONFIG_LEDS_CLASS_FLASH =m Changes in v2: - Enabled CONFIG_LEDS_CLASS_FLASH since the driver now depends on it. arch/arm/configs/qcom_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig index 9c286861a9c0..1be619a12637 100644 --- a/arch/arm/configs/qcom_defconfig +++ b/arch/arm/configs/qcom_defconfig @@ -206,6 +206,7 @@ CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_MSM=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y +CONFIG_LEDS_CLASS_FLASH=m CONFIG_LEDS_GPIO=y CONFIG_LEDS_PM8058=y CONFIG_LEDS_TRIGGERS=y @@ -308,3 +309,4 @@ CONFIG_QCOM_WDT=y CONFIG_ARM_PSCI=y CONFIG_CPU_FREQ=y CONFIG_CPUFREQ_DT=y +CONFIG_LEDS_QCOM_SPMI_FLASH=m From patchwork Tue Aug 3 16:26:40 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= X-Patchwork-Id: 12416739 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=-17.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,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 0E8EBC432BE for ; Tue, 3 Aug 2021 16:30:02 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D23DE60F94 for ; Tue, 3 Aug 2021 16:30:01 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org D23DE60F94 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=collabora.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=FYT9vR3ejoFZFoW4bLxXOQ7aelxvfCfjxTOsYAHPqkc=; b=QlSZ/IMN5iJ3zO pEbRik05w4u1cfSs4pJhWxkJBrslvRTblyzJK84aYbfT407FV3aryhpEqE9SHjEe99C21ofUGkVgU foETnQihRfMUyz6m/5K2ZCO1rqShzeT6sj837k7Hqlp6icnwWxX0vphhSpDenFyAXzGsLdPWgv1td JueKWLNuX//xpPVFnOhStPjjutya5YU5j8AKR434bNz6yB+IxIQFk14jmRnpoe0qYo+mP9svUp1Vg BDPbrXOtvuPEi7TyMq/T+U2r6sBTeTJwDAKkOA07X25LcqRSKLCcro7Odt1nMb4YDamDH95GLlTGy uAe7g0TixeKGua1fWWlA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mAxGq-003bzU-A6; Tue, 03 Aug 2021 16:28:00 +0000 Received: from bhuna.collabora.co.uk ([46.235.227.227]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mAxGL-003bpJ-AS for linux-arm-kernel@lists.infradead.org; Tue, 03 Aug 2021 16:27:38 +0000 Received: from localhost.localdomain (unknown [IPv6:2804:14c:1a9:2434:b693:c9:5cb6:b688]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: nfraprado) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id EC4001F42632; Tue, 3 Aug 2021 17:27:20 +0100 (BST) From: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= To: Pavel Machek , Dan Murphy , Bjorn Andersson , Andy Gross , Rob Herring , Jacek Anaszewski Cc: linux-leds@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, Brian Masney , Luca Weiss , Russell King , Georgi Djakov , linux-kernel@vger.kernel.org, phone-devel@vger.kernel.org, ~postmarketos/upstreaming@lists.sr.ht, ~lkcamp/patches@lists.sr.ht, =?utf-8?q?Andr=C3=A9_Almeida?= , kernel@collabora.com Subject: [PATCH v3 4/5] ARM: dts: qcom: pm8941: Add nodes for QCOM SPMI Flash LEDs Date: Tue, 3 Aug 2021 13:26:40 -0300 Message-Id: <20210803162641.1525980-5-nfraprado@collabora.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210803162641.1525980-1-nfraprado@collabora.com> References: <20210803162641.1525980-1-nfraprado@collabora.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210803_092736_943022_249C87E3 X-CRM114-Status: GOOD ( 12.76 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Add nodes for Qualcomm's SPMI Flash LEDs present in PM8941. There are two white LEDs which can be used as flash or torch. Signed-off-by: Nícolas F. R. A. Prado --- Changes in v3: - Disabled node - Removed LED configuration (moved to hammerhead dts in patch 5) Changes in v2: - Moved from hammerhead dts to pm8941 dtsi, as it was this way downstream - Now using values from leds-qcom-spmi-flash.h arch/arm/boot/dts/qcom-pm8941.dtsi | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/arm/boot/dts/qcom-pm8941.dtsi b/arch/arm/boot/dts/qcom-pm8941.dtsi index c1f2012d1c8b..e9e4369a488b 100644 --- a/arch/arm/boot/dts/qcom-pm8941.dtsi +++ b/arch/arm/boot/dts/qcom-pm8941.dtsi @@ -2,6 +2,7 @@ #include #include #include +#include &spmi_bus { @@ -189,5 +190,29 @@ pm8941_5vs2: 5vs2 { regulator-initial-mode = <1>; }; }; + + spmi-flash-leds@d300 { + status = "disabled"; + + compatible = "qcom,spmi-flash-leds"; + reg = <0xd300 0x100>; + flash-boost-supply = <&pm8941_5vs1>; + torch-boost-supply = <&pm8941_5v>; + + #address-cells = <1>; + #size-cells = <0>; + + pm8941_flash0: led@0 { + reg = <0>; + function = LED_FUNCTION_FLASH; + color = ; + }; + + pm8941_flash1: led@1 { + reg = <1>; + function = LED_FUNCTION_FLASH; + color = ; + }; + }; }; }; From patchwork Tue Aug 3 16:26:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= X-Patchwork-Id: 12416737 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=-17.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,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 0552EC4338F for ; Tue, 3 Aug 2021 16:30:01 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id CE53F60243 for ; Tue, 3 Aug 2021 16:30:00 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org CE53F60243 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=collabora.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=5RqlpiihNdqwUpddJX8phT1+Mprth602CYTT+llPN9I=; b=XwM7jOiPWCO5vB 7Pq64awBWNpn39i6Gp+9DTkT7UU13At7jRMvDvkrRDLSLWO6YjG/eVyzYOPRj3dDDWUaVTA8op4Zf B6goUITpxGk30XOImtOevS+0y9Z6TGVyh3hIaoj7yBQVPR+sbKEmMGNeG3Jg+lBHZVXxDV8VnB0y8 e7N9pHLIQnx5QADPFxOew9HG4VOc647F57KHiVXuUGColhY5nFJjpoNyOPU2R6aqBjWs50pNldxOq 8ThEYgju48x69pxKmH3bh+IrdhdqWtVwE4vxAY+cUQ/+h09ELz0YUaTSkyPpM8ePjYgtct8soALr+ hWsgDkw7p34HQcz0bogA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mAxH6-003c5b-SY; Tue, 03 Aug 2021 16:28:17 +0000 Received: from bhuna.collabora.co.uk ([46.235.227.227]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mAxGT-003brX-Sq for linux-arm-kernel@lists.infradead.org; Tue, 03 Aug 2021 16:27:39 +0000 Received: from localhost.localdomain (unknown [IPv6:2804:14c:1a9:2434:b693:c9:5cb6:b688]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: nfraprado) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id 946C51F42408; Tue, 3 Aug 2021 17:27:26 +0100 (BST) From: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= To: Pavel Machek , Dan Murphy , Bjorn Andersson , Andy Gross , Rob Herring , Jacek Anaszewski Cc: linux-leds@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, Brian Masney , Luca Weiss , Russell King , Georgi Djakov , linux-kernel@vger.kernel.org, phone-devel@vger.kernel.org, ~postmarketos/upstreaming@lists.sr.ht, ~lkcamp/patches@lists.sr.ht, =?utf-8?q?Andr=C3=A9_Almeida?= , kernel@collabora.com Subject: [PATCH v3 5/5] ARM: dts: qcom: msm8974-hammerhead: Enable and configure flash LED node Date: Tue, 3 Aug 2021 13:26:41 -0300 Message-Id: <20210803162641.1525980-6-nfraprado@collabora.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210803162641.1525980-1-nfraprado@collabora.com> References: <20210803162641.1525980-1-nfraprado@collabora.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210803_092738_145227_E3A8BF33 X-CRM114-Status: GOOD ( 12.16 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Enable the flash LED node from PM8941 and configure the LEDs. This enables the Nexus 5 to make use of the LEDs as flash or lantern. Signed-off-by: Nícolas F. R. A. Prado --- Added in v3 .../qcom-msm8974-lge-nexus5-hammerhead.dts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/qcom-msm8974-lge-nexus5-hammerhead.dts b/arch/arm/boot/dts/qcom-msm8974-lge-nexus5-hammerhead.dts index 30ee913faae6..78ac56be81b3 100644 --- a/arch/arm/boot/dts/qcom-msm8974-lge-nexus5-hammerhead.dts +++ b/arch/arm/boot/dts/qcom-msm8974-lge-nexus5-hammerhead.dts @@ -759,4 +759,24 @@ otg { }; }; }; + + pm8941@1 { + spmi-flash-leds@d300 { + status = "okay"; + + led@0 { + led-max-microamp = <200000>; + flash-max-microamp = <1000000>; + flash-max-timeout-us = <1280000>; + default-state = "off"; + }; + + led@1 { + led-max-microamp = <200000>; + flash-max-microamp = <1000000>; + flash-max-timeout-us = <1280000>; + default-state = "off"; + }; + }; + }; };