Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'leds-next-6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds

Pull LED updates from Lee Jones:
"New Drivers:
- Add support for MediaTek MT6370 LED Indicator
- Add support for MediaTek MT6370 Flashlight
- Add support for QCOM PMIC Flash
- Add support for Rohm BD2606MVV Charge Pump LED

New Device Support:
- Add support for PMK8550 PWM to QCOM LPG

New Functionality:
- Add support for high resolution PWM to QCOM LPG

Fix-ups:
- Kconfig 'depends' and 'select' dependency changes
- Remove unused / irrelevant includes
- Remove unnecessary checks (already performed further into the call stack)
- Trivial: Fix commentary, simplify error messages
- Rid 'defined but not used' warnings
- Provide documentation
- Explicitly provide include files

Bug Fixes:
- Mark GPIO LED as BROKEN
- Fix Kconfig entries
- Fix various Smatch staticify reports
- Fix error handling (or a lack there of)"

* tag 'leds-next-6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds: (30 commits)
leds: bd2606mvv: Driver for the Rohm 6 Channel i2c LED driver
dt-bindings: leds: Add ROHM BD2606MVV LED
docs: leds: ledtrig-oneshot: Fix spelling mistake
leds: pwm-multicolor: Simplify an error message
dt-bindings: leds: Convert PCA9532 to dtschema
leds: rgb: leds-qcom-lpg: Add support for PMK8550 PWM
leds: rgb: leds-qcom-lpg: Add support for high resolution PWM
dt-bindings: leds-qcom-lpg: Add qcom,pmk8550-pwm compatible string
leds: tca6507: Fix error handling of using fwnode_property_read_string
leds: flash: Set variables mvflash_{3,4}ch_regs storage-class-specifier to static
leds: rgb: mt6370: Correct config name to select in LEDS_MT6370_RGB
MAINTAINERS: Add entry for LED devices documentation
Documentation: leds: MT6370: Use bullet lists for timing variables
Documentation: leds: mt6370: Properly wrap hw_pattern chart
Documentation: leds: Add MT6370 doc to the toctree
leds: rgb: mt6370: Fix implicit declaration for FIELD_GET
docs: leds: Add MT6370 RGB LED pattern document
leds: flash: mt6370: Add MediaTek MT6370 flashlight support
leds: rgb: mt6370: Add MediaTek MT6370 current sink type LED Indicator support
dt-bindings: leds: spmi-flash-led: Add pm6150l compatible
...

+3087 -108
+32 -3
Documentation/devicetree/bindings/leds/common.yaml
··· 90 90 - heartbeat 91 91 # LED indicates disk activity 92 92 - disk-activity 93 + # LED indicates disk read activity 93 94 - disk-read 95 + # LED indicates disk write activity 94 96 - disk-write 95 97 # LED flashes at a fixed, configurable rate 96 98 - timer 97 99 # LED alters the brightness for the specified duration with one software 98 100 # timer (requires "led-pattern" property) 99 101 - pattern 102 + # LED indicates mic mute state 103 + - audio-micmute 104 + # LED indicates audio mute state 105 + - audio-mute 106 + # LED indicates bluetooth power state 107 + - bluetooth-power 108 + # LED indicates activity of all CPUs 109 + - cpu 110 + # LED indicates camera flash state 111 + - flash 112 + # LED indicated keyboard capslock 113 + - kbd-capslock 114 + # LED indicates MTD memory activity 115 + - mtd 116 + # LED indicates NAND memory activity (deprecated), 117 + # in new implementations use "mtd" 118 + - nand-disk 119 + # No trigger assigned to the LED. This is the default mode 120 + # if trigger is absent 121 + - none 122 + # LED indicates camera torch state 123 + - torch 124 + # LED indicates USB gadget activity 100 125 - usb-gadget 126 + # LED indicates USB host activity 101 127 - usb-host 128 + # LED indicates USB port state 129 + - usbport 130 + # LED is triggered by CPU activity 102 131 - pattern: "^cpu[0-9]*$" 103 - - pattern: "^hci[0-9]+-power$" 104 132 # LED is triggered by Bluetooth activity 105 - - pattern: "^mmc[0-9]+$" 133 + - pattern: "^hci[0-9]+-power$" 106 134 # LED is triggered by SD/MMC activity 107 - - pattern: "^phy[0-9]+tx$" 135 + - pattern: "^mmc[0-9]+$" 108 136 # LED is triggered by WLAN activity 137 + - pattern: "^phy[0-9]+tx$" 109 138 110 139 led-pattern: 111 140 description: |
-49
Documentation/devicetree/bindings/leds/leds-pca9532.txt
··· 1 - *NXP - pca9532 PWM LED Driver 2 - 3 - The PCA9532 family is SMBus I/O expander optimized for dimming LEDs. 4 - The PWM support 256 steps. 5 - 6 - Required properties: 7 - - compatible: 8 - "nxp,pca9530" 9 - "nxp,pca9531" 10 - "nxp,pca9532" 11 - "nxp,pca9533" 12 - - reg - I2C slave address 13 - 14 - Each led is represented as a sub-node of the nxp,pca9530. 15 - 16 - Optional sub-node properties: 17 - - label: see Documentation/devicetree/bindings/leds/common.txt 18 - - type: Output configuration, see dt-bindings/leds/leds-pca9532.h (default NONE) 19 - - linux,default-trigger: see Documentation/devicetree/bindings/leds/common.txt 20 - - default-state: see Documentation/devicetree/bindings/leds/common.txt 21 - This property is only valid for sub-nodes of type <PCA9532_TYPE_LED>. 22 - 23 - Example: 24 - #include <dt-bindings/leds/leds-pca9532.h> 25 - 26 - leds: pca9530@60 { 27 - compatible = "nxp,pca9530"; 28 - reg = <0x60>; 29 - 30 - red-power { 31 - label = "pca:red:power"; 32 - type = <PCA9532_TYPE_LED>; 33 - }; 34 - green-power { 35 - label = "pca:green:power"; 36 - type = <PCA9532_TYPE_LED>; 37 - }; 38 - kernel-booting { 39 - type = <PCA9532_TYPE_LED>; 40 - default-state = "on"; 41 - }; 42 - sys-stat { 43 - type = <PCA9532_TYPE_LED>; 44 - default-state = "keep"; // don't touch, was set by U-Boot 45 - }; 46 - }; 47 - 48 - For more product information please see the link below: 49 - http://nxp.com/documents/data_sheet/PCA9532.pdf
+1
Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml
··· 27 27 - qcom,pmc8180c-lpg 28 28 - qcom,pmi8994-lpg 29 29 - qcom,pmi8998-lpg 30 + - qcom,pmk8550-pwm 30 31 31 32 "#pwm-cells": 32 33 const: 2
+90
Documentation/devicetree/bindings/leds/nxp,pca953x.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/leds/nxp,pca953x.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: NXP PCA9532 LED Dimmer 8 + 9 + maintainers: 10 + - Riku Voipio <riku.voipio@iki.fi> 11 + 12 + description: | 13 + The PCA9532 family is SMBus I/O expander optimized for dimming LEDs. 14 + The PWM support 256 steps. 15 + 16 + For more product information please see the link below: 17 + https://www.nxp.com/docs/en/data-sheet/PCA9532.pdf 18 + 19 + properties: 20 + compatible: 21 + enum: 22 + - nxp,pca9530 23 + - nxp,pca9531 24 + - nxp,pca9532 25 + - nxp,pca9533 26 + 27 + reg: 28 + maxItems: 1 29 + 30 + gpio-controller: true 31 + 32 + '#gpio-cells': 33 + const: 2 34 + 35 + patternProperties: 36 + "^led-[0-9a-z]+$": 37 + type: object 38 + $ref: common.yaml# 39 + unevaluatedProperties: false 40 + 41 + properties: 42 + type: 43 + description: | 44 + Output configuration, see include/dt-bindings/leds/leds-pca9532.h 45 + $ref: /schemas/types.yaml#/definitions/uint32 46 + default: 0 47 + minimum: 0 48 + maximum: 4 49 + 50 + required: 51 + - compatible 52 + - reg 53 + 54 + additionalProperties: false 55 + 56 + examples: 57 + - | 58 + #include <dt-bindings/leds/leds-pca9532.h> 59 + 60 + i2c { 61 + #address-cells = <1>; 62 + #size-cells = <0>; 63 + 64 + led-controller@62 { 65 + compatible = "nxp,pca9533"; 66 + reg = <0x62>; 67 + 68 + led-1 { 69 + label = "pca:red:power"; 70 + type = <PCA9532_TYPE_LED>; 71 + }; 72 + 73 + led-2 { 74 + label = "pca:green:power"; 75 + type = <PCA9532_TYPE_LED>; 76 + }; 77 + 78 + led-3 { 79 + type = <PCA9532_TYPE_LED>; 80 + default-state = "on"; 81 + }; 82 + 83 + led-4 { 84 + type = <PCA9532_TYPE_LED>; 85 + default-state = "keep"; 86 + }; 87 + }; 88 + }; 89 + 90 + ...
+117
Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/leds/qcom,spmi-flash-led.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Flash LED device inside Qualcomm Technologies, Inc. PMICs 8 + 9 + maintainers: 10 + - Fenglin Wu <quic_fenglinw@quicinc.com> 11 + 12 + description: | 13 + Flash LED controller is present inside some Qualcomm Technologies, Inc. PMICs. 14 + The flash LED module can have different number of LED channels supported 15 + e.g. 3 or 4. There are some different registers between them but they can 16 + both support maximum current up to 1.5 A per channel and they can also support 17 + ganging 2 channels together to supply maximum current up to 2 A. The current 18 + will be split symmetrically on each channel and they will be enabled and 19 + disabled at the same time. 20 + 21 + properties: 22 + compatible: 23 + items: 24 + - enum: 25 + - qcom,pm6150l-flash-led 26 + - qcom,pm8150c-flash-led 27 + - qcom,pm8150l-flash-led 28 + - qcom,pm8350c-flash-led 29 + - const: qcom,spmi-flash-led 30 + 31 + reg: 32 + maxItems: 1 33 + 34 + patternProperties: 35 + "^led-[0-3]$": 36 + type: object 37 + $ref: common.yaml# 38 + unevaluatedProperties: false 39 + description: 40 + Represents the physical LED components which are connected to the 41 + flash LED channels' output. 42 + 43 + properties: 44 + led-sources: 45 + description: 46 + The HW indices of the flash LED channels that connect to the 47 + physical LED 48 + allOf: 49 + - minItems: 1 50 + maxItems: 2 51 + items: 52 + enum: [1, 2, 3, 4] 53 + 54 + led-max-microamp: 55 + anyOf: 56 + - minimum: 5000 57 + maximum: 500000 58 + multipleOf: 5000 59 + - minimum: 10000 60 + maximum: 1000000 61 + multipleOf: 10000 62 + 63 + flash-max-microamp: 64 + anyOf: 65 + - minimum: 12500 66 + maximum: 1500000 67 + multipleOf: 12500 68 + - minimum: 25000 69 + maximum: 2000000 70 + multipleOf: 25000 71 + 72 + flash-max-timeout-us: 73 + minimum: 10000 74 + maximum: 1280000 75 + multipleOf: 10000 76 + 77 + required: 78 + - led-sources 79 + - led-max-microamp 80 + 81 + required: 82 + - compatible 83 + - reg 84 + 85 + additionalProperties: false 86 + 87 + examples: 88 + - | 89 + #include <dt-bindings/leds/common.h> 90 + spmi { 91 + #address-cells = <1>; 92 + #size-cells = <0>; 93 + led-controller@ee00 { 94 + compatible = "qcom,pm8350c-flash-led", "qcom,spmi-flash-led"; 95 + reg = <0xee00>; 96 + 97 + led-0 { 98 + function = LED_FUNCTION_FLASH; 99 + color = <LED_COLOR_ID_WHITE>; 100 + led-sources = <1>, <4>; 101 + led-max-microamp = <300000>; 102 + flash-max-microamp = <2000000>; 103 + flash-max-timeout-us = <1280000>; 104 + function-enumerator = <0>; 105 + }; 106 + 107 + led-1 { 108 + function = LED_FUNCTION_FLASH; 109 + color = <LED_COLOR_ID_YELLOW>; 110 + led-sources = <2>, <3>; 111 + led-max-microamp = <300000>; 112 + flash-max-microamp = <2000000>; 113 + flash-max-timeout-us = <1280000>; 114 + function-enumerator = <1>; 115 + }; 116 + }; 117 + };
+81
Documentation/devicetree/bindings/leds/rohm,bd2606mvv.yaml
··· 1 + # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/leds/rohm,bd2606mvv.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: ROHM BD2606MVV LED controller 8 + 9 + maintainers: 10 + - Andreas Kemnade <andreas@kemnade.info> 11 + 12 + description: 13 + The BD2606 MVV is a programmable LED controller connected via I2C that can 14 + drive 6 separate lines. Each of them can be individually switched on and off, 15 + but the brightness setting is shared between pairs of them. 16 + 17 + Datasheet is available at 18 + https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/led_driver/bd2606mvv_1-e.pdf 19 + 20 + properties: 21 + compatible: 22 + const: rohm,bd2606mvv 23 + 24 + reg: 25 + maxItems: 1 26 + 27 + "#address-cells": 28 + const: 1 29 + 30 + "#size-cells": 31 + const: 0 32 + 33 + enable-gpios: 34 + maxItems: 1 35 + description: GPIO pin to enable/disable the device. 36 + 37 + patternProperties: 38 + "^led@[0-6]$": 39 + type: object 40 + $ref: common.yaml# 41 + unevaluatedProperties: false 42 + 43 + properties: 44 + reg: 45 + minimum: 0 46 + maximum: 6 47 + 48 + required: 49 + - reg 50 + 51 + additionalProperties: false 52 + 53 + examples: 54 + - | 55 + #include <dt-bindings/leds/common.h> 56 + 57 + i2c { 58 + #address-cells = <1>; 59 + #size-cells = <0>; 60 + 61 + led-controller@66 { 62 + compatible = "rohm,bd2606mvv"; 63 + reg = <0x66>; 64 + #address-cells = <1>; 65 + #size-cells = <0>; 66 + 67 + led@0 { 68 + reg = <0x0>; 69 + color = <LED_COLOR_ID_RED>; 70 + function = LED_FUNCTION_POWER; 71 + }; 72 + 73 + led@2 { 74 + reg = <0x2>; 75 + color = <LED_COLOR_ID_WHITE>; 76 + function = LED_FUNCTION_STATUS; 77 + }; 78 + }; 79 + }; 80 + 81 + ...
+1
Documentation/leds/index.rst
··· 25 25 leds-lp5562 26 26 leds-lp55xx 27 27 leds-mlxcpld 28 + leds-mt6370-rgb 28 29 leds-sc27xx 29 30 leds-qcom-lpg
+64
Documentation/leds/leds-mt6370-rgb.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + ========================================= 4 + The device for Mediatek MT6370 RGB LED 5 + ========================================= 6 + 7 + Description 8 + ----------- 9 + 10 + The MT6370 integrates a four-channel RGB LED driver, designed to provide a 11 + variety of lighting effect for mobile device applications. The RGB LED devices 12 + includes a smart LED string controller and it can drive 3 channels of LEDs with 13 + a sink current up to 24mA and a CHG_VIN power good indicator LED with sink 14 + current up to 6mA. It provides three operation modes for RGB LEDs: 15 + PWM Dimming mode, breath pattern mode, and constant current mode. The device 16 + can increase or decrease the brightness of the RGB LED via an I2C interface. 17 + 18 + The breath pattern for a channel can be programmed using the "pattern" trigger, 19 + using the hw_pattern attribute. 20 + 21 + /sys/class/leds/<led>/hw_pattern 22 + -------------------------------- 23 + 24 + Specify a hardware breath pattern for a MT6370 RGB LED. 25 + 26 + The breath pattern is a series of timing pairs, with the hold-time expressed in 27 + milliseconds. And the brightness is controlled by 28 + '/sys/class/leds/<led>/brightness'. The pattern doesn't include the brightness 29 + setting. Hardware pattern only controls the timing for each pattern stage 30 + depending on the current brightness setting. 31 + 32 + Pattern diagram:: 33 + 34 + "0 Tr1 0 Tr2 0 Tf1 0 Tf2 0 Ton 0 Toff" --> '0' for dummy brightness code 35 + 36 + ^ 37 + | ============ 38 + | / \ / 39 + Icurr | / \ / 40 + | / \ / 41 + | / \ / .....repeat 42 + | / \ / 43 + | --- --- --- 44 + |--- --- --- 45 + +----------------------------------============------------> Time 46 + < Tr1><Tr2>< Ton ><Tf1><Tf2 >< Toff >< Tr1><Tr2> 47 + 48 + Timing description: 49 + 50 + * Tr1: First rising time for 0% - 30% load. 51 + * Tr2: Second rising time for 31% - 100% load. 52 + * Ton: On time for 100% load. 53 + * Tf1: First falling time for 100% - 31% load. 54 + * Tf2: Second falling time for 30% to 0% load. 55 + * Toff: Off time for 0% load. 56 + 57 + * Tr1/Tr2/Tf1/Tf2/Ton: 125ms to 3125ms, 200ms per step. 58 + * Toff: 250ms to 6250ms, 400ms per step. 59 + 60 + Pattern example:: 61 + 62 + "0 125 0 125 0 125 0 125 0 625 0 1050" 63 + 64 + This Will configure Tr1/Tr2/Tf1/Tf2 to 125m, Ton to 625ms, and Toff to 1050ms.
+1 -1
Documentation/leds/ledtrig-oneshot.rst
··· 5 5 This is a LED trigger useful for signaling the user of an event where there are 6 6 no clear trap points to put standard led-on and led-off settings. Using this 7 7 trigger, the application needs only to signal the trigger when an event has 8 - happened, than the trigger turns the LED on and than keeps it off for a 8 + happened, then the trigger turns the LED on and then keeps it off for a 9 9 specified amount of time. 10 10 11 11 This trigger is meant to be usable both for sporadic and dense events. In the
+1
MAINTAINERS
··· 11724 11724 S: Maintained 11725 11725 T: git git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git 11726 11726 F: Documentation/devicetree/bindings/leds/ 11727 + F: Documentation/leds/ 11727 11728 F: drivers/leds/ 11728 11729 F: include/dt-bindings/leds/ 11729 11730 F: include/linux/leds.h
+15 -1
drivers/leds/Kconfig
··· 551 551 help 552 552 This option enables support for regulator driven LEDs. 553 553 554 + config LEDS_BD2606MVV 555 + tristate "LED driver for BD2606MVV" 556 + depends on LEDS_CLASS 557 + depends on I2C 558 + select REGMAP_I2C 559 + help 560 + This option enables support for BD2606MVV LED driver chips 561 + accessed via the I2C bus. It supports setting brightness, with 562 + the limitiation that there are groups of two channels sharing 563 + a brightness setting, but not the on/off setting. 564 + 565 + To compile this driver as a module, choose M here: the module will 566 + be called leds-bd2606mvv. 567 + 554 568 config LEDS_BD2802 555 569 tristate "LED driver for BD2802 RGB LED" 556 570 depends on LEDS_CLASS ··· 809 795 config LEDS_TI_LMU_COMMON 810 796 tristate "LED driver for TI LMU" 811 797 depends on LEDS_CLASS 812 - depends on REGMAP 798 + select REGMAP 813 799 help 814 800 Say Y to enable the LED driver for TI LMU devices. 815 801 This supports common features between the TI LM3532, LM3631, LM3632,
+1
drivers/leds/Makefile
··· 17 17 obj-$(CONFIG_LEDS_AW2013) += leds-aw2013.o 18 18 obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o 19 19 obj-$(CONFIG_LEDS_BCM6358) += leds-bcm6358.o 20 + obj-$(CONFIG_LEDS_BD2606MVV) += leds-bd2606mvv.o 20 21 obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o 21 22 obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o 22 23 obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
+28
drivers/leds/flash/Kconfig
··· 61 61 Independent current sources supply for each flash LED support torch 62 62 and strobe mode. 63 63 64 + config LEDS_MT6370_FLASH 65 + tristate "Flash LED Support for MediaTek MT6370 PMIC" 66 + depends on LEDS_CLASS 67 + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS 68 + depends on MFD_MT6370 69 + help 70 + Support 2 channels and torch/strobe mode. 71 + Say Y here to enable support for 72 + MT6370_FLASH_LED device. 73 + 74 + This driver can also be built as a module. If so, the module 75 + will be called "leds-mt6370-flash". 76 + 77 + config LEDS_QCOM_FLASH 78 + tristate "LED support for flash module inside Qualcomm Technologies, Inc. PMIC" 79 + depends on MFD_SPMI_PMIC || COMPILE_TEST 80 + depends on LEDS_CLASS && OF 81 + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS 82 + select REGMAP 83 + help 84 + This option enables support for the flash module found in Qualcomm 85 + Technologies, Inc. PMICs. The flash module can have 3 or 4 flash LED 86 + channels and each channel is programmable to support up to 1.5 A full 87 + scale current. It also supports connecting two channels' output together 88 + to supply one LED component to achieve current up to 2 A. In such case, 89 + the total LED current will be split symmetrically on each channel and 90 + they will be enabled/disabled at the same time. 91 + 64 92 config LEDS_RT4505 65 93 tristate "LED support for RT4505 flashlight controller" 66 94 depends on I2C && OF
+2
drivers/leds/flash/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 3 3 obj-$(CONFIG_LEDS_MT6360) += leds-mt6360.o 4 + obj-$(CONFIG_LEDS_MT6370_FLASH) += leds-mt6370-flash.o 4 5 obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o 5 6 obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o 6 7 obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o 7 8 obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o 8 9 obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o 10 + obj-$(CONFIG_LEDS_QCOM_FLASH) += leds-qcom-flash.o 9 11 obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o 10 12 obj-$(CONFIG_LEDS_RT8515) += leds-rt8515.o 11 13 obj-$(CONFIG_LEDS_SGM3140) += leds-sgm3140.o
+573
drivers/leds/flash/leds-mt6370-flash.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2023 Richtek Technology Corp. 4 + * 5 + * Authors: 6 + * Alice Chen <alice_chen@richtek.com> 7 + * ChiYuan Huang <cy_huang@richtek.com> 8 + */ 9 + 10 + #include <linux/bitops.h> 11 + #include <linux/delay.h> 12 + #include <linux/init.h> 13 + #include <linux/interrupt.h> 14 + #include <linux/kernel.h> 15 + #include <linux/led-class-flash.h> 16 + #include <linux/module.h> 17 + #include <linux/mutex.h> 18 + #include <linux/platform_device.h> 19 + #include <linux/property.h> 20 + #include <linux/regmap.h> 21 + 22 + #include <media/v4l2-flash-led-class.h> 23 + 24 + enum { 25 + MT6370_LED_FLASH1 = 0, 26 + MT6370_LED_FLASH2, 27 + MT6370_MAX_LEDS 28 + }; 29 + 30 + /* Virtual definition for multicolor */ 31 + 32 + #define MT6370_REG_FLEDEN 0x17E 33 + #define MT6370_REG_STRBTO 0x173 34 + #define MT6370_REG_CHGSTAT2 0x1D1 35 + #define MT6370_REG_FLEDSTAT1 0x1D9 36 + #define MT6370_REG_FLEDISTRB(_id) (0x174 + 4 * (_id)) 37 + #define MT6370_REG_FLEDITOR(_id) (0x175 + 4 * (_id)) 38 + #define MT6370_ITORCH_MASK GENMASK(4, 0) 39 + #define MT6370_ISTROBE_MASK GENMASK(6, 0) 40 + #define MT6370_STRBTO_MASK GENMASK(6, 0) 41 + #define MT6370_TORCHEN_MASK BIT(3) 42 + #define MT6370_STROBEN_MASK BIT(2) 43 + #define MT6370_FLCSEN_MASK(_id) BIT(MT6370_LED_FLASH2 - (_id)) 44 + #define MT6370_FLCSEN_MASK_ALL GENMASK(1, 0) 45 + #define MT6370_FLEDCHGVINOVP_MASK BIT(3) 46 + #define MT6370_FLED1STRBTO_MASK BIT(11) 47 + #define MT6370_FLED2STRBTO_MASK BIT(10) 48 + #define MT6370_FLED1STRB_MASK BIT(9) 49 + #define MT6370_FLED2STRB_MASK BIT(8) 50 + #define MT6370_FLED1SHORT_MASK BIT(7) 51 + #define MT6370_FLED2SHORT_MASK BIT(6) 52 + #define MT6370_FLEDLVF_MASK BIT(3) 53 + 54 + #define MT6370_LED_JOINT 2 55 + #define MT6370_RANGE_FLED_REG 4 56 + #define MT6370_ITORCH_MIN_uA 25000 57 + #define MT6370_ITORCH_STEP_uA 12500 58 + #define MT6370_ITORCH_MAX_uA 400000 59 + #define MT6370_ITORCH_DOUBLE_MAX_uA 800000 60 + #define MT6370_ISTRB_MIN_uA 50000 61 + #define MT6370_ISTRB_STEP_uA 12500 62 + #define MT6370_ISTRB_MAX_uA 1500000 63 + #define MT6370_ISTRB_DOUBLE_MAX_uA 3000000 64 + #define MT6370_STRBTO_MIN_US 64000 65 + #define MT6370_STRBTO_STEP_US 32000 66 + #define MT6370_STRBTO_MAX_US 2432000 67 + 68 + #define to_mt6370_led(ptr, member) container_of(ptr, struct mt6370_led, member) 69 + 70 + struct mt6370_led { 71 + struct led_classdev_flash flash; 72 + struct v4l2_flash *v4l2_flash; 73 + struct mt6370_priv *priv; 74 + u8 led_no; 75 + }; 76 + 77 + struct mt6370_priv { 78 + struct regmap *regmap; 79 + struct mutex lock; 80 + unsigned int fled_strobe_used; 81 + unsigned int fled_torch_used; 82 + unsigned int leds_active; 83 + unsigned int leds_count; 84 + struct mt6370_led leds[]; 85 + }; 86 + 87 + static int mt6370_torch_brightness_set(struct led_classdev *lcdev, enum led_brightness level) 88 + { 89 + struct mt6370_led *led = to_mt6370_led(lcdev, flash.led_cdev); 90 + struct mt6370_priv *priv = led->priv; 91 + u32 led_enable_mask = led->led_no == MT6370_LED_JOINT ? MT6370_FLCSEN_MASK_ALL : 92 + MT6370_FLCSEN_MASK(led->led_no); 93 + u32 enable_mask = MT6370_TORCHEN_MASK | led_enable_mask; 94 + u32 val = level ? led_enable_mask : 0; 95 + u32 curr; 96 + int ret, i; 97 + 98 + mutex_lock(&priv->lock); 99 + 100 + /* 101 + * There is only one set of flash control logic, and this flag is used to check if 'strobe' 102 + * is currently being used. 103 + */ 104 + if (priv->fled_strobe_used) { 105 + dev_warn(lcdev->dev, "Please disable strobe first [%d]\n", priv->fled_strobe_used); 106 + ret = -EBUSY; 107 + goto unlock; 108 + } 109 + 110 + if (level) 111 + curr = priv->fled_torch_used | BIT(led->led_no); 112 + else 113 + curr = priv->fled_torch_used & ~BIT(led->led_no); 114 + 115 + if (curr) 116 + val |= MT6370_TORCHEN_MASK; 117 + 118 + if (level) { 119 + level -= 1; 120 + if (led->led_no == MT6370_LED_JOINT) { 121 + u32 flevel[MT6370_MAX_LEDS]; 122 + 123 + /* 124 + * There're two flash channels in MT6370. If joint flash output is used, 125 + * torch current will be averaged output from both channels. 126 + */ 127 + flevel[0] = level / 2; 128 + flevel[1] = level - flevel[0]; 129 + for (i = 0; i < MT6370_MAX_LEDS; i++) { 130 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDITOR(i), 131 + MT6370_ITORCH_MASK, flevel[i]); 132 + if (ret) 133 + goto unlock; 134 + } 135 + } else { 136 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDITOR(led->led_no), 137 + MT6370_ITORCH_MASK, level); 138 + if (ret) 139 + goto unlock; 140 + } 141 + } 142 + 143 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDEN, enable_mask, val); 144 + if (ret) 145 + goto unlock; 146 + 147 + priv->fled_torch_used = curr; 148 + 149 + unlock: 150 + mutex_unlock(&priv->lock); 151 + return ret; 152 + } 153 + 154 + static int mt6370_flash_brightness_set(struct led_classdev_flash *fl_cdev, u32 brightness) 155 + { 156 + /* 157 + * Because of the current spikes when turning on the flash, the brightness should be kept 158 + * by the LED framework. This empty function is used to prevent checking failure when 159 + * led_classdev_flash registers ops. 160 + */ 161 + return 0; 162 + } 163 + 164 + static int _mt6370_flash_brightness_set(struct led_classdev_flash *fl_cdev, u32 brightness) 165 + { 166 + struct mt6370_led *led = to_mt6370_led(fl_cdev, flash); 167 + struct mt6370_priv *priv = led->priv; 168 + struct led_flash_setting *setting = &fl_cdev->brightness; 169 + u32 val = (brightness - setting->min) / setting->step; 170 + int ret, i; 171 + 172 + if (led->led_no == MT6370_LED_JOINT) { 173 + u32 flevel[MT6370_MAX_LEDS]; 174 + 175 + /* 176 + * There're two flash channels in MT6370. If joint flash output is used, storbe 177 + * current will be averaged output from both channels. 178 + */ 179 + flevel[0] = val / 2; 180 + flevel[1] = val - flevel[0]; 181 + for (i = 0; i < MT6370_MAX_LEDS; i++) { 182 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDISTRB(i), 183 + MT6370_ISTROBE_MASK, flevel[i]); 184 + if (ret) 185 + break; 186 + } 187 + } else { 188 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDISTRB(led->led_no), 189 + MT6370_ISTROBE_MASK, val); 190 + } 191 + 192 + return ret; 193 + } 194 + 195 + static int mt6370_strobe_set(struct led_classdev_flash *fl_cdev, bool state) 196 + { 197 + struct mt6370_led *led = to_mt6370_led(fl_cdev, flash); 198 + struct mt6370_priv *priv = led->priv; 199 + struct led_classdev *lcdev = &fl_cdev->led_cdev; 200 + struct led_flash_setting *s = &fl_cdev->brightness; 201 + u32 led_enable_mask = led->led_no == MT6370_LED_JOINT ? MT6370_FLCSEN_MASK_ALL : 202 + MT6370_FLCSEN_MASK(led->led_no); 203 + u32 enable_mask = MT6370_STROBEN_MASK | led_enable_mask; 204 + u32 val = state ? led_enable_mask : 0; 205 + u32 curr; 206 + int ret; 207 + 208 + mutex_lock(&priv->lock); 209 + 210 + /* 211 + * There is only one set of flash control logic, and this flag is used to check if 'torch' 212 + * is currently being used. 213 + */ 214 + if (priv->fled_torch_used) { 215 + dev_warn(lcdev->dev, "Please disable torch first [0x%x]\n", priv->fled_torch_used); 216 + ret = -EBUSY; 217 + goto unlock; 218 + } 219 + 220 + if (state) 221 + curr = priv->fled_strobe_used | BIT(led->led_no); 222 + else 223 + curr = priv->fled_strobe_used & ~BIT(led->led_no); 224 + 225 + if (curr) 226 + val |= MT6370_STROBEN_MASK; 227 + 228 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDEN, enable_mask, val); 229 + if (ret) { 230 + dev_err(lcdev->dev, "[%d] control current source %d fail\n", led->led_no, state); 231 + goto unlock; 232 + } 233 + 234 + /* 235 + * If the flash needs to turn on, configure the flash current to ramp up to the setting 236 + * value. Otherwise, always revert to the minimum one. 237 + */ 238 + ret = _mt6370_flash_brightness_set(fl_cdev, state ? s->val : s->min); 239 + if (ret) { 240 + dev_err(lcdev->dev, "[%d] Failed to set brightness\n", led->led_no); 241 + goto unlock; 242 + } 243 + 244 + /* 245 + * For the flash to turn on/off, we must wait for HW ramping up/down time 5ms/500us to 246 + * prevent the unexpected problem. 247 + */ 248 + if (!priv->fled_strobe_used && curr) 249 + usleep_range(5000, 6000); 250 + else if (priv->fled_strobe_used && !curr) 251 + usleep_range(500, 600); 252 + 253 + priv->fled_strobe_used = curr; 254 + 255 + unlock: 256 + mutex_unlock(&priv->lock); 257 + return ret; 258 + } 259 + 260 + static int mt6370_strobe_get(struct led_classdev_flash *fl_cdev, bool *state) 261 + { 262 + struct mt6370_led *led = to_mt6370_led(fl_cdev, flash); 263 + struct mt6370_priv *priv = led->priv; 264 + 265 + mutex_lock(&priv->lock); 266 + *state = !!(priv->fled_strobe_used & BIT(led->led_no)); 267 + mutex_unlock(&priv->lock); 268 + 269 + return 0; 270 + } 271 + 272 + static int mt6370_timeout_set(struct led_classdev_flash *fl_cdev, u32 timeout) 273 + { 274 + struct mt6370_led *led = to_mt6370_led(fl_cdev, flash); 275 + struct mt6370_priv *priv = led->priv; 276 + struct led_flash_setting *s = &fl_cdev->timeout; 277 + u32 val = (timeout - s->min) / s->step; 278 + 279 + return regmap_update_bits(priv->regmap, MT6370_REG_STRBTO, MT6370_STRBTO_MASK, val); 280 + } 281 + 282 + static int mt6370_fault_get(struct led_classdev_flash *fl_cdev, u32 *fault) 283 + { 284 + struct mt6370_led *led = to_mt6370_led(fl_cdev, flash); 285 + struct mt6370_priv *priv = led->priv; 286 + u16 fled_stat; 287 + unsigned int chg_stat, strobe_timeout_mask, fled_short_mask; 288 + u32 rfault = 0; 289 + int ret; 290 + 291 + ret = regmap_read(priv->regmap, MT6370_REG_CHGSTAT2, &chg_stat); 292 + if (ret) 293 + return ret; 294 + 295 + ret = regmap_raw_read(priv->regmap, MT6370_REG_FLEDSTAT1, &fled_stat, sizeof(fled_stat)); 296 + if (ret) 297 + return ret; 298 + 299 + switch (led->led_no) { 300 + case MT6370_LED_FLASH1: 301 + strobe_timeout_mask = MT6370_FLED1STRBTO_MASK; 302 + fled_short_mask = MT6370_FLED1SHORT_MASK; 303 + break; 304 + 305 + case MT6370_LED_FLASH2: 306 + strobe_timeout_mask = MT6370_FLED2STRBTO_MASK; 307 + fled_short_mask = MT6370_FLED2SHORT_MASK; 308 + break; 309 + 310 + case MT6370_LED_JOINT: 311 + strobe_timeout_mask = MT6370_FLED1STRBTO_MASK | MT6370_FLED2STRBTO_MASK; 312 + fled_short_mask = MT6370_FLED1SHORT_MASK | MT6370_FLED2SHORT_MASK; 313 + break; 314 + default: 315 + return -EINVAL; 316 + } 317 + 318 + if (chg_stat & MT6370_FLEDCHGVINOVP_MASK) 319 + rfault |= LED_FAULT_INPUT_VOLTAGE; 320 + 321 + if (fled_stat & strobe_timeout_mask) 322 + rfault |= LED_FAULT_TIMEOUT; 323 + 324 + if (fled_stat & fled_short_mask) 325 + rfault |= LED_FAULT_SHORT_CIRCUIT; 326 + 327 + if (fled_stat & MT6370_FLEDLVF_MASK) 328 + rfault |= LED_FAULT_UNDER_VOLTAGE; 329 + 330 + *fault = rfault; 331 + return ret; 332 + } 333 + 334 + static const struct led_flash_ops mt6370_flash_ops = { 335 + .flash_brightness_set = mt6370_flash_brightness_set, 336 + .strobe_set = mt6370_strobe_set, 337 + .strobe_get = mt6370_strobe_get, 338 + .timeout_set = mt6370_timeout_set, 339 + .fault_get = mt6370_fault_get, 340 + }; 341 + 342 + #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) 343 + static int mt6370_flash_external_strobe_set(struct v4l2_flash *v4l2_flash, 344 + bool enable) 345 + { 346 + struct led_classdev_flash *flash = v4l2_flash->fled_cdev; 347 + struct mt6370_led *led = to_mt6370_led(flash, flash); 348 + struct mt6370_priv *priv = led->priv; 349 + u32 mask = led->led_no == MT6370_LED_JOINT ? MT6370_FLCSEN_MASK_ALL : 350 + MT6370_FLCSEN_MASK(led->led_no); 351 + u32 val = enable ? mask : 0; 352 + int ret; 353 + 354 + mutex_lock(&priv->lock); 355 + 356 + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDEN, mask, val); 357 + if (ret) 358 + goto unlock; 359 + 360 + if (enable) 361 + priv->fled_strobe_used |= BIT(led->led_no); 362 + else 363 + priv->fled_strobe_used &= ~BIT(led->led_no); 364 + 365 + unlock: 366 + mutex_unlock(&priv->lock); 367 + return ret; 368 + } 369 + 370 + static const struct v4l2_flash_ops v4l2_flash_ops = { 371 + .external_strobe_set = mt6370_flash_external_strobe_set, 372 + }; 373 + 374 + static void mt6370_init_v4l2_flash_config(struct mt6370_led *led, struct v4l2_flash_config *cfg) 375 + { 376 + struct led_classdev *lcdev; 377 + struct led_flash_setting *s = &cfg->intensity; 378 + 379 + lcdev = &led->flash.led_cdev; 380 + 381 + s->min = MT6370_ITORCH_MIN_uA; 382 + s->step = MT6370_ITORCH_STEP_uA; 383 + s->val = s->max = s->min + (lcdev->max_brightness - 1) * s->step; 384 + 385 + cfg->has_external_strobe = 1; 386 + strscpy(cfg->dev_name, dev_name(lcdev->dev), sizeof(cfg->dev_name)); 387 + 388 + cfg->flash_faults = LED_FAULT_SHORT_CIRCUIT | LED_FAULT_TIMEOUT | 389 + LED_FAULT_INPUT_VOLTAGE | LED_FAULT_UNDER_VOLTAGE; 390 + } 391 + #else 392 + static const struct v4l2_flash_ops v4l2_flash_ops; 393 + static void mt6370_init_v4l2_flash_config(struct mt6370_led *led, struct v4l2_flash_config *cfg) 394 + { 395 + } 396 + #endif 397 + 398 + static void mt6370_v4l2_flash_release(void *v4l2_flash) 399 + { 400 + v4l2_flash_release(v4l2_flash); 401 + } 402 + 403 + static int mt6370_led_register(struct device *parent, struct mt6370_led *led, 404 + struct fwnode_handle *fwnode) 405 + { 406 + struct led_init_data init_data = { .fwnode = fwnode }; 407 + struct v4l2_flash_config v4l2_config = {}; 408 + int ret; 409 + 410 + ret = devm_led_classdev_flash_register_ext(parent, &led->flash, &init_data); 411 + if (ret) 412 + return dev_err_probe(parent, ret, "Couldn't register flash %d\n", led->led_no); 413 + 414 + mt6370_init_v4l2_flash_config(led, &v4l2_config); 415 + led->v4l2_flash = v4l2_flash_init(parent, fwnode, &led->flash, &v4l2_flash_ops, 416 + &v4l2_config); 417 + if (IS_ERR(led->v4l2_flash)) 418 + return dev_err_probe(parent, PTR_ERR(led->v4l2_flash), 419 + "Failed to register %d v4l2 sd\n", led->led_no); 420 + 421 + return devm_add_action_or_reset(parent, mt6370_v4l2_flash_release, led->v4l2_flash); 422 + } 423 + 424 + static u32 mt6370_clamp(u32 val, u32 min, u32 max, u32 step) 425 + { 426 + u32 retval; 427 + 428 + retval = clamp_val(val, min, max); 429 + if (step > 1) 430 + retval = rounddown(retval - min, step) + min; 431 + 432 + return retval; 433 + } 434 + 435 + static int mt6370_init_flash_properties(struct device *dev, struct mt6370_led *led, 436 + struct fwnode_handle *fwnode) 437 + { 438 + struct led_classdev_flash *flash = &led->flash; 439 + struct led_classdev *lcdev = &flash->led_cdev; 440 + struct mt6370_priv *priv = led->priv; 441 + struct led_flash_setting *s; 442 + u32 sources[MT6370_MAX_LEDS]; 443 + u32 max_ua, val; 444 + int i, ret, num; 445 + 446 + num = fwnode_property_count_u32(fwnode, "led-sources"); 447 + if (num < 1) 448 + return dev_err_probe(dev, -EINVAL, 449 + "Not specified or wrong number of led-sources\n"); 450 + 451 + ret = fwnode_property_read_u32_array(fwnode, "led-sources", sources, num); 452 + if (ret) 453 + return ret; 454 + 455 + for (i = 0; i < num; i++) { 456 + if (sources[i] >= MT6370_MAX_LEDS) 457 + return -EINVAL; 458 + if (priv->leds_active & BIT(sources[i])) 459 + return -EINVAL; 460 + priv->leds_active |= BIT(sources[i]); 461 + } 462 + 463 + /* If both channels are specified in 'led-sources', joint flash output mode is used */ 464 + led->led_no = num == 2 ? MT6370_LED_JOINT : sources[0]; 465 + 466 + max_ua = num == 2 ? MT6370_ITORCH_DOUBLE_MAX_uA : MT6370_ITORCH_MAX_uA; 467 + val = MT6370_ITORCH_MIN_uA; 468 + ret = fwnode_property_read_u32(fwnode, "led-max-microamp", &val); 469 + if (!ret) 470 + val = mt6370_clamp(val, MT6370_ITORCH_MIN_uA, max_ua, MT6370_ITORCH_STEP_uA); 471 + 472 + lcdev->max_brightness = (val - MT6370_ITORCH_MIN_uA) / MT6370_ITORCH_STEP_uA + 1; 473 + lcdev->brightness_set_blocking = mt6370_torch_brightness_set; 474 + lcdev->flags |= LED_DEV_CAP_FLASH; 475 + 476 + max_ua = num == 2 ? MT6370_ISTRB_DOUBLE_MAX_uA : MT6370_ISTRB_MAX_uA; 477 + val = MT6370_ISTRB_MIN_uA; 478 + ret = fwnode_property_read_u32(fwnode, "flash-max-microamp", &val); 479 + if (!ret) 480 + val = mt6370_clamp(val, MT6370_ISTRB_MIN_uA, max_ua, MT6370_ISTRB_STEP_uA); 481 + 482 + s = &flash->brightness; 483 + s->min = MT6370_ISTRB_MIN_uA; 484 + s->step = MT6370_ISTRB_STEP_uA; 485 + s->val = s->max = val; 486 + 487 + /* Always configure to the minimum level when off to prevent flash current spikes. */ 488 + ret = _mt6370_flash_brightness_set(flash, s->min); 489 + if (ret) 490 + return ret; 491 + 492 + val = MT6370_STRBTO_MIN_US; 493 + ret = fwnode_property_read_u32(fwnode, "flash-max-timeout-us", &val); 494 + if (!ret) 495 + val = mt6370_clamp(val, MT6370_STRBTO_MIN_US, MT6370_STRBTO_MAX_US, 496 + MT6370_STRBTO_STEP_US); 497 + 498 + s = &flash->timeout; 499 + s->min = MT6370_STRBTO_MIN_US; 500 + s->step = MT6370_STRBTO_STEP_US; 501 + s->val = s->max = val; 502 + 503 + flash->ops = &mt6370_flash_ops; 504 + 505 + return 0; 506 + } 507 + 508 + static int mt6370_led_probe(struct platform_device *pdev) 509 + { 510 + struct device *dev = &pdev->dev; 511 + struct mt6370_priv *priv; 512 + struct fwnode_handle *child; 513 + size_t count; 514 + int i = 0, ret; 515 + 516 + count = device_get_child_node_count(dev); 517 + if (!count || count > MT6370_MAX_LEDS) 518 + return dev_err_probe(dev, -EINVAL, 519 + "No child node or node count over max led number %zu\n", count); 520 + 521 + priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL); 522 + if (!priv) 523 + return -ENOMEM; 524 + 525 + priv->leds_count = count; 526 + mutex_init(&priv->lock); 527 + 528 + priv->regmap = dev_get_regmap(dev->parent, NULL); 529 + if (!priv->regmap) 530 + return dev_err_probe(dev, -ENODEV, "Failed to get parent regmap\n"); 531 + 532 + device_for_each_child_node(dev, child) { 533 + struct mt6370_led *led = priv->leds + i; 534 + 535 + led->priv = priv; 536 + 537 + ret = mt6370_init_flash_properties(dev, led, child); 538 + if (ret) { 539 + fwnode_handle_put(child); 540 + return ret; 541 + } 542 + 543 + ret = mt6370_led_register(dev, led, child); 544 + if (ret) { 545 + fwnode_handle_put(child); 546 + return ret; 547 + } 548 + 549 + i++; 550 + } 551 + 552 + return 0; 553 + } 554 + 555 + static const struct of_device_id mt6370_led_of_id[] = { 556 + { .compatible = "mediatek,mt6370-flashlight" }, 557 + {} 558 + }; 559 + MODULE_DEVICE_TABLE(of, mt6370_led_of_id); 560 + 561 + static struct platform_driver mt6370_led_driver = { 562 + .driver = { 563 + .name = "mt6370-flashlight", 564 + .of_match_table = mt6370_led_of_id, 565 + }, 566 + .probe = mt6370_led_probe, 567 + }; 568 + module_platform_driver(mt6370_led_driver); 569 + 570 + MODULE_AUTHOR("Alice Chen <alice_chen@richtek.com>"); 571 + MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); 572 + MODULE_DESCRIPTION("MT6370 FLASH LED Driver"); 573 + MODULE_LICENSE("GPL");
+773
drivers/leds/flash/leds-qcom-flash.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. 4 + */ 5 + 6 + #include <linux/bitfield.h> 7 + #include <linux/bits.h> 8 + #include <linux/leds.h> 9 + #include <linux/led-class-flash.h> 10 + #include <linux/module.h> 11 + #include <linux/platform_device.h> 12 + #include <linux/property.h> 13 + #include <linux/regmap.h> 14 + #include <media/v4l2-flash-led-class.h> 15 + 16 + /* registers definitions */ 17 + #define FLASH_TYPE_REG 0x04 18 + #define FLASH_TYPE_VAL 0x18 19 + 20 + #define FLASH_SUBTYPE_REG 0x05 21 + #define FLASH_SUBTYPE_3CH_VAL 0x04 22 + #define FLASH_SUBTYPE_4CH_VAL 0x07 23 + 24 + #define FLASH_STS_3CH_OTST1 BIT(0) 25 + #define FLASH_STS_3CH_OTST2 BIT(1) 26 + #define FLASH_STS_3CH_OTST3 BIT(2) 27 + #define FLASH_STS_3CH_BOB_THM_OVERLOAD BIT(3) 28 + #define FLASH_STS_3CH_VPH_DROOP BIT(4) 29 + #define FLASH_STS_3CH_BOB_ILIM_S1 BIT(5) 30 + #define FLASH_STS_3CH_BOB_ILIM_S2 BIT(6) 31 + #define FLASH_STS_3CH_BCL_IBAT BIT(7) 32 + 33 + #define FLASH_STS_4CH_VPH_LOW BIT(0) 34 + #define FLASH_STS_4CH_BCL_IBAT BIT(1) 35 + #define FLASH_STS_4CH_BOB_ILIM_S1 BIT(2) 36 + #define FLASH_STS_4CH_BOB_ILIM_S2 BIT(3) 37 + #define FLASH_STS_4CH_OTST2 BIT(4) 38 + #define FLASH_STS_4CH_OTST1 BIT(5) 39 + #define FLASH_STS_4CHG_BOB_THM_OVERLOAD BIT(6) 40 + 41 + #define FLASH_TIMER_EN_BIT BIT(7) 42 + #define FLASH_TIMER_VAL_MASK GENMASK(6, 0) 43 + #define FLASH_TIMER_STEP_MS 10 44 + 45 + #define FLASH_STROBE_HW_SW_SEL_BIT BIT(2) 46 + #define SW_STROBE_VAL 0 47 + #define HW_STROBE_VAL 1 48 + #define FLASH_HW_STROBE_TRIGGER_SEL_BIT BIT(1) 49 + #define STROBE_LEVEL_TRIGGER_VAL 0 50 + #define STROBE_EDGE_TRIGGER_VAL 1 51 + #define FLASH_STROBE_POLARITY_BIT BIT(0) 52 + #define STROBE_ACTIVE_HIGH_VAL 1 53 + 54 + #define FLASH_IRES_MASK_4CH BIT(0) 55 + #define FLASH_IRES_MASK_3CH GENMASK(1, 0) 56 + #define FLASH_IRES_12P5MA_VAL 0 57 + #define FLASH_IRES_5MA_VAL_4CH 1 58 + #define FLASH_IRES_5MA_VAL_3CH 3 59 + 60 + /* constants */ 61 + #define FLASH_CURRENT_MAX_UA 1500000 62 + #define TORCH_CURRENT_MAX_UA 500000 63 + #define FLASH_TOTAL_CURRENT_MAX_UA 2000000 64 + #define FLASH_CURRENT_DEFAULT_UA 1000000 65 + #define TORCH_CURRENT_DEFAULT_UA 200000 66 + 67 + #define TORCH_IRES_UA 5000 68 + #define FLASH_IRES_UA 12500 69 + 70 + #define FLASH_TIMEOUT_MAX_US 1280000 71 + #define FLASH_TIMEOUT_STEP_US 10000 72 + 73 + #define UA_PER_MA 1000 74 + 75 + enum hw_type { 76 + QCOM_MVFLASH_3CH, 77 + QCOM_MVFLASH_4CH, 78 + }; 79 + 80 + enum led_mode { 81 + FLASH_MODE, 82 + TORCH_MODE, 83 + }; 84 + 85 + enum led_strobe { 86 + SW_STROBE, 87 + HW_STROBE, 88 + }; 89 + 90 + enum { 91 + REG_STATUS1, 92 + REG_STATUS2, 93 + REG_STATUS3, 94 + REG_CHAN_TIMER, 95 + REG_ITARGET, 96 + REG_MODULE_EN, 97 + REG_IRESOLUTION, 98 + REG_CHAN_STROBE, 99 + REG_CHAN_EN, 100 + REG_MAX_COUNT, 101 + }; 102 + 103 + static struct reg_field mvflash_3ch_regs[REG_MAX_COUNT] = { 104 + REG_FIELD(0x08, 0, 7), /* status1 */ 105 + REG_FIELD(0x09, 0, 7), /* status2 */ 106 + REG_FIELD(0x0a, 0, 7), /* status3 */ 107 + REG_FIELD_ID(0x40, 0, 7, 3, 1), /* chan_timer */ 108 + REG_FIELD_ID(0x43, 0, 6, 3, 1), /* itarget */ 109 + REG_FIELD(0x46, 7, 7), /* module_en */ 110 + REG_FIELD(0x47, 0, 5), /* iresolution */ 111 + REG_FIELD_ID(0x49, 0, 2, 3, 1), /* chan_strobe */ 112 + REG_FIELD(0x4c, 0, 2), /* chan_en */ 113 + }; 114 + 115 + static struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = { 116 + REG_FIELD(0x06, 0, 7), /* status1 */ 117 + REG_FIELD(0x07, 0, 6), /* status2 */ 118 + REG_FIELD(0x09, 0, 7), /* status3 */ 119 + REG_FIELD_ID(0x3e, 0, 7, 4, 1), /* chan_timer */ 120 + REG_FIELD_ID(0x42, 0, 6, 4, 1), /* itarget */ 121 + REG_FIELD(0x46, 7, 7), /* module_en */ 122 + REG_FIELD(0x49, 0, 3), /* iresolution */ 123 + REG_FIELD_ID(0x4a, 0, 6, 4, 1), /* chan_strobe */ 124 + REG_FIELD(0x4e, 0, 3), /* chan_en */ 125 + }; 126 + 127 + struct qcom_flash_data { 128 + struct v4l2_flash **v4l2_flash; 129 + struct regmap_field *r_fields[REG_MAX_COUNT]; 130 + struct mutex lock; 131 + enum hw_type hw_type; 132 + u8 leds_count; 133 + u8 max_channels; 134 + u8 chan_en_bits; 135 + }; 136 + 137 + struct qcom_flash_led { 138 + struct qcom_flash_data *flash_data; 139 + struct led_classdev_flash flash; 140 + u32 max_flash_current_ma; 141 + u32 max_torch_current_ma; 142 + u32 max_timeout_ms; 143 + u32 flash_current_ma; 144 + u32 flash_timeout_ms; 145 + u8 *chan_id; 146 + u8 chan_count; 147 + bool enabled; 148 + }; 149 + 150 + static int set_flash_module_en(struct qcom_flash_led *led, bool en) 151 + { 152 + struct qcom_flash_data *flash_data = led->flash_data; 153 + u8 led_mask = 0, enable; 154 + int i, rc; 155 + 156 + for (i = 0; i < led->chan_count; i++) 157 + led_mask |= BIT(led->chan_id[i]); 158 + 159 + mutex_lock(&flash_data->lock); 160 + if (en) 161 + flash_data->chan_en_bits |= led_mask; 162 + else 163 + flash_data->chan_en_bits &= ~led_mask; 164 + 165 + enable = !!flash_data->chan_en_bits; 166 + rc = regmap_field_write(flash_data->r_fields[REG_MODULE_EN], enable); 167 + if (rc) 168 + dev_err(led->flash.led_cdev.dev, "write module_en failed, rc=%d\n", rc); 169 + mutex_unlock(&flash_data->lock); 170 + 171 + return rc; 172 + } 173 + 174 + static int set_flash_current(struct qcom_flash_led *led, u32 current_ma, enum led_mode mode) 175 + { 176 + struct qcom_flash_data *flash_data = led->flash_data; 177 + u32 itarg_ua, ires_ua; 178 + u8 shift, ires_mask = 0, ires_val = 0, chan_id; 179 + int i, rc; 180 + 181 + /* 182 + * Split the current across the channels and set the 183 + * IRESOLUTION and ITARGET registers accordingly. 184 + */ 185 + itarg_ua = (current_ma * UA_PER_MA) / led->chan_count + 1; 186 + ires_ua = (mode == FLASH_MODE) ? FLASH_IRES_UA : TORCH_IRES_UA; 187 + 188 + for (i = 0; i < led->chan_count; i++) { 189 + u8 itarget = 0; 190 + 191 + if (itarg_ua > ires_ua) 192 + itarget = itarg_ua / ires_ua - 1; 193 + 194 + chan_id = led->chan_id[i]; 195 + 196 + rc = regmap_fields_write(flash_data->r_fields[REG_ITARGET], chan_id, itarget); 197 + if (rc) 198 + return rc; 199 + 200 + if (flash_data->hw_type == QCOM_MVFLASH_3CH) { 201 + shift = chan_id * 2; 202 + ires_mask |= FLASH_IRES_MASK_3CH << shift; 203 + ires_val |= ((mode == FLASH_MODE) ? 204 + (FLASH_IRES_12P5MA_VAL << shift) : 205 + (FLASH_IRES_5MA_VAL_3CH << shift)); 206 + } else if (flash_data->hw_type == QCOM_MVFLASH_4CH) { 207 + shift = chan_id; 208 + ires_mask |= FLASH_IRES_MASK_4CH << shift; 209 + ires_val |= ((mode == FLASH_MODE) ? 210 + (FLASH_IRES_12P5MA_VAL << shift) : 211 + (FLASH_IRES_5MA_VAL_4CH << shift)); 212 + } else { 213 + dev_err(led->flash.led_cdev.dev, 214 + "HW type %d is not supported\n", flash_data->hw_type); 215 + return -EOPNOTSUPP; 216 + } 217 + } 218 + 219 + return regmap_field_update_bits(flash_data->r_fields[REG_IRESOLUTION], ires_mask, ires_val); 220 + } 221 + 222 + static int set_flash_timeout(struct qcom_flash_led *led, u32 timeout_ms) 223 + { 224 + struct qcom_flash_data *flash_data = led->flash_data; 225 + u8 timer, chan_id; 226 + int rc, i; 227 + 228 + /* set SAFETY_TIMER for all the channels connected to the same LED */ 229 + timeout_ms = min_t(u32, timeout_ms, led->max_timeout_ms); 230 + 231 + for (i = 0; i < led->chan_count; i++) { 232 + chan_id = led->chan_id[i]; 233 + 234 + timer = timeout_ms / FLASH_TIMER_STEP_MS; 235 + timer = clamp_t(u8, timer, 0, FLASH_TIMER_VAL_MASK); 236 + 237 + if (timeout_ms) 238 + timer |= FLASH_TIMER_EN_BIT; 239 + 240 + rc = regmap_fields_write(flash_data->r_fields[REG_CHAN_TIMER], chan_id, timer); 241 + if (rc) 242 + return rc; 243 + } 244 + 245 + return 0; 246 + } 247 + 248 + static int set_flash_strobe(struct qcom_flash_led *led, enum led_strobe strobe, bool state) 249 + { 250 + struct qcom_flash_data *flash_data = led->flash_data; 251 + u8 strobe_sel, chan_en, chan_id, chan_mask = 0; 252 + int rc, i; 253 + 254 + /* Set SW strobe config for all channels connected to the LED */ 255 + for (i = 0; i < led->chan_count; i++) { 256 + chan_id = led->chan_id[i]; 257 + 258 + if (strobe == SW_STROBE) 259 + strobe_sel = FIELD_PREP(FLASH_STROBE_HW_SW_SEL_BIT, SW_STROBE_VAL); 260 + else 261 + strobe_sel = FIELD_PREP(FLASH_STROBE_HW_SW_SEL_BIT, HW_STROBE_VAL); 262 + 263 + strobe_sel |= 264 + FIELD_PREP(FLASH_HW_STROBE_TRIGGER_SEL_BIT, STROBE_LEVEL_TRIGGER_VAL) | 265 + FIELD_PREP(FLASH_STROBE_POLARITY_BIT, STROBE_ACTIVE_HIGH_VAL); 266 + 267 + rc = regmap_fields_write( 268 + flash_data->r_fields[REG_CHAN_STROBE], chan_id, strobe_sel); 269 + if (rc) 270 + return rc; 271 + 272 + chan_mask |= BIT(chan_id); 273 + } 274 + 275 + /* Enable/disable flash channels */ 276 + chan_en = state ? chan_mask : 0; 277 + rc = regmap_field_update_bits(flash_data->r_fields[REG_CHAN_EN], chan_mask, chan_en); 278 + if (rc) 279 + return rc; 280 + 281 + led->enabled = state; 282 + return 0; 283 + } 284 + 285 + static inline struct qcom_flash_led *flcdev_to_qcom_fled(struct led_classdev_flash *flcdev) 286 + { 287 + return container_of(flcdev, struct qcom_flash_led, flash); 288 + } 289 + 290 + static int qcom_flash_brightness_set(struct led_classdev_flash *fled_cdev, u32 brightness) 291 + { 292 + struct qcom_flash_led *led = flcdev_to_qcom_fled(fled_cdev); 293 + 294 + led->flash_current_ma = min_t(u32, led->max_flash_current_ma, brightness / UA_PER_MA); 295 + return 0; 296 + } 297 + 298 + static int qcom_flash_timeout_set(struct led_classdev_flash *fled_cdev, u32 timeout) 299 + { 300 + struct qcom_flash_led *led = flcdev_to_qcom_fled(fled_cdev); 301 + 302 + led->flash_timeout_ms = timeout / USEC_PER_MSEC; 303 + return 0; 304 + } 305 + 306 + static int qcom_flash_strobe_set(struct led_classdev_flash *fled_cdev, bool state) 307 + { 308 + struct qcom_flash_led *led = flcdev_to_qcom_fled(fled_cdev); 309 + int rc; 310 + 311 + rc = set_flash_current(led, led->flash_current_ma, FLASH_MODE); 312 + if (rc) 313 + return rc; 314 + 315 + rc = set_flash_timeout(led, led->flash_timeout_ms); 316 + if (rc) 317 + return rc; 318 + 319 + rc = set_flash_module_en(led, state); 320 + if (rc) 321 + return rc; 322 + 323 + return set_flash_strobe(led, SW_STROBE, state); 324 + } 325 + 326 + static int qcom_flash_strobe_get(struct led_classdev_flash *fled_cdev, bool *state) 327 + { 328 + struct qcom_flash_led *led = flcdev_to_qcom_fled(fled_cdev); 329 + 330 + *state = led->enabled; 331 + return 0; 332 + } 333 + 334 + static int qcom_flash_fault_get(struct led_classdev_flash *fled_cdev, u32 *fault) 335 + { 336 + struct qcom_flash_led *led = flcdev_to_qcom_fled(fled_cdev); 337 + struct qcom_flash_data *flash_data = led->flash_data; 338 + u8 shift, chan_id, chan_mask = 0; 339 + u8 ot_mask = 0, oc_mask = 0, uv_mask = 0; 340 + u32 val, fault_sts = 0; 341 + int i, rc; 342 + 343 + rc = regmap_field_read(flash_data->r_fields[REG_STATUS1], &val); 344 + if (rc) 345 + return rc; 346 + 347 + for (i = 0; i < led->chan_count; i++) { 348 + chan_id = led->chan_id[i]; 349 + shift = chan_id * 2; 350 + 351 + if (val & BIT(shift)) 352 + fault_sts |= LED_FAULT_SHORT_CIRCUIT; 353 + 354 + chan_mask |= BIT(chan_id); 355 + } 356 + 357 + rc = regmap_field_read(flash_data->r_fields[REG_STATUS2], &val); 358 + if (rc) 359 + return rc; 360 + 361 + if (flash_data->hw_type == QCOM_MVFLASH_3CH) { 362 + ot_mask = FLASH_STS_3CH_OTST1 | 363 + FLASH_STS_3CH_OTST2 | 364 + FLASH_STS_3CH_OTST3 | 365 + FLASH_STS_3CH_BOB_THM_OVERLOAD; 366 + oc_mask = FLASH_STS_3CH_BOB_ILIM_S1 | 367 + FLASH_STS_3CH_BOB_ILIM_S2 | 368 + FLASH_STS_3CH_BCL_IBAT; 369 + uv_mask = FLASH_STS_3CH_VPH_DROOP; 370 + } else if (flash_data->hw_type == QCOM_MVFLASH_4CH) { 371 + ot_mask = FLASH_STS_4CH_OTST2 | 372 + FLASH_STS_4CH_OTST1 | 373 + FLASH_STS_4CHG_BOB_THM_OVERLOAD; 374 + oc_mask = FLASH_STS_4CH_BCL_IBAT | 375 + FLASH_STS_4CH_BOB_ILIM_S1 | 376 + FLASH_STS_4CH_BOB_ILIM_S2; 377 + uv_mask = FLASH_STS_4CH_VPH_LOW; 378 + } 379 + 380 + if (val & ot_mask) 381 + fault_sts |= LED_FAULT_OVER_TEMPERATURE; 382 + 383 + if (val & oc_mask) 384 + fault_sts |= LED_FAULT_OVER_CURRENT; 385 + 386 + if (val & uv_mask) 387 + fault_sts |= LED_FAULT_INPUT_VOLTAGE; 388 + 389 + rc = regmap_field_read(flash_data->r_fields[REG_STATUS3], &val); 390 + if (rc) 391 + return rc; 392 + 393 + if (flash_data->hw_type == QCOM_MVFLASH_3CH) { 394 + if (val & chan_mask) 395 + fault_sts |= LED_FAULT_TIMEOUT; 396 + } else if (flash_data->hw_type == QCOM_MVFLASH_4CH) { 397 + for (i = 0; i < led->chan_count; i++) { 398 + chan_id = led->chan_id[i]; 399 + shift = chan_id * 2; 400 + 401 + if (val & BIT(shift)) 402 + fault_sts |= LED_FAULT_TIMEOUT; 403 + } 404 + } 405 + 406 + *fault = fault_sts; 407 + return 0; 408 + } 409 + 410 + static int qcom_flash_led_brightness_set(struct led_classdev *led_cdev, 411 + enum led_brightness brightness) 412 + { 413 + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 414 + struct qcom_flash_led *led = flcdev_to_qcom_fled(fled_cdev); 415 + u32 current_ma = brightness * led->max_torch_current_ma / LED_FULL; 416 + bool enable = !!brightness; 417 + int rc; 418 + 419 + rc = set_flash_current(led, current_ma, TORCH_MODE); 420 + if (rc) 421 + return rc; 422 + 423 + /* Disable flash timeout for torch LED */ 424 + rc = set_flash_timeout(led, 0); 425 + if (rc) 426 + return rc; 427 + 428 + rc = set_flash_module_en(led, enable); 429 + if (rc) 430 + return rc; 431 + 432 + return set_flash_strobe(led, SW_STROBE, enable); 433 + } 434 + 435 + static const struct led_flash_ops qcom_flash_ops = { 436 + .flash_brightness_set = qcom_flash_brightness_set, 437 + .strobe_set = qcom_flash_strobe_set, 438 + .strobe_get = qcom_flash_strobe_get, 439 + .timeout_set = qcom_flash_timeout_set, 440 + .fault_get = qcom_flash_fault_get, 441 + }; 442 + 443 + #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) 444 + static int qcom_flash_external_strobe_set(struct v4l2_flash *v4l2_flash, bool enable) 445 + { 446 + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 447 + struct qcom_flash_led *led = flcdev_to_qcom_fled(fled_cdev); 448 + int rc; 449 + 450 + rc = set_flash_module_en(led, enable); 451 + if (rc) 452 + return rc; 453 + 454 + if (enable) 455 + return set_flash_strobe(led, HW_STROBE, true); 456 + else 457 + return set_flash_strobe(led, SW_STROBE, false); 458 + } 459 + 460 + static enum led_brightness 461 + qcom_flash_intensity_to_led_brightness(struct v4l2_flash *v4l2_flash, s32 intensity) 462 + { 463 + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 464 + struct qcom_flash_led *led = flcdev_to_qcom_fled(fled_cdev); 465 + u32 current_ma = intensity / UA_PER_MA; 466 + 467 + current_ma = min_t(u32, current_ma, led->max_torch_current_ma); 468 + if (!current_ma) 469 + return LED_OFF; 470 + 471 + return (current_ma * LED_FULL) / led->max_torch_current_ma; 472 + } 473 + 474 + static s32 qcom_flash_brightness_to_led_intensity(struct v4l2_flash *v4l2_flash, 475 + enum led_brightness brightness) 476 + { 477 + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 478 + struct qcom_flash_led *led = flcdev_to_qcom_fled(fled_cdev); 479 + 480 + return (brightness * led->max_torch_current_ma * UA_PER_MA) / LED_FULL; 481 + } 482 + 483 + static const struct v4l2_flash_ops qcom_v4l2_flash_ops = { 484 + .external_strobe_set = qcom_flash_external_strobe_set, 485 + .intensity_to_led_brightness = qcom_flash_intensity_to_led_brightness, 486 + .led_brightness_to_intensity = qcom_flash_brightness_to_led_intensity, 487 + }; 488 + 489 + static int 490 + qcom_flash_v4l2_init(struct device *dev, struct qcom_flash_led *led, struct fwnode_handle *fwnode) 491 + { 492 + struct qcom_flash_data *flash_data = led->flash_data; 493 + struct v4l2_flash_config v4l2_cfg = { 0 }; 494 + struct led_flash_setting *intensity = &v4l2_cfg.intensity; 495 + 496 + if (!(led->flash.led_cdev.flags & LED_DEV_CAP_FLASH)) 497 + return 0; 498 + 499 + intensity->min = intensity->step = TORCH_IRES_UA * led->chan_count; 500 + intensity->max = led->max_torch_current_ma * UA_PER_MA; 501 + intensity->val = min_t(u32, intensity->max, TORCH_CURRENT_DEFAULT_UA); 502 + 503 + strscpy(v4l2_cfg.dev_name, led->flash.led_cdev.dev->kobj.name, 504 + sizeof(v4l2_cfg.dev_name)); 505 + 506 + v4l2_cfg.has_external_strobe = true; 507 + v4l2_cfg.flash_faults = LED_FAULT_INPUT_VOLTAGE | 508 + LED_FAULT_OVER_CURRENT | 509 + LED_FAULT_SHORT_CIRCUIT | 510 + LED_FAULT_OVER_TEMPERATURE | 511 + LED_FAULT_TIMEOUT; 512 + 513 + flash_data->v4l2_flash[flash_data->leds_count] = 514 + v4l2_flash_init(dev, fwnode, &led->flash, &qcom_v4l2_flash_ops, &v4l2_cfg); 515 + return PTR_ERR_OR_ZERO(flash_data->v4l2_flash); 516 + } 517 + # else 518 + static int 519 + qcom_flash_v4l2_init(struct device *dev, struct qcom_flash_led *led, struct fwnode_handle *fwnode) 520 + { 521 + return 0; 522 + } 523 + #endif 524 + 525 + static int qcom_flash_register_led_device(struct device *dev, 526 + struct fwnode_handle *node, struct qcom_flash_led *led) 527 + { 528 + struct qcom_flash_data *flash_data = led->flash_data; 529 + struct led_init_data init_data; 530 + struct led_classdev_flash *flash = &led->flash; 531 + struct led_flash_setting *brightness, *timeout; 532 + u32 count, current_ua, timeout_us; 533 + u32 channels[4]; 534 + int i, rc; 535 + 536 + count = fwnode_property_count_u32(node, "led-sources"); 537 + if (count <= 0) { 538 + dev_err(dev, "No led-sources specified\n"); 539 + return -ENODEV; 540 + } 541 + 542 + if (count > flash_data->max_channels) { 543 + dev_err(dev, "led-sources count %u exceeds maximum channel count %u\n", 544 + count, flash_data->max_channels); 545 + return -EINVAL; 546 + } 547 + 548 + rc = fwnode_property_read_u32_array(node, "led-sources", channels, count); 549 + if (rc < 0) { 550 + dev_err(dev, "Failed to read led-sources property, rc=%d\n", rc); 551 + return rc; 552 + } 553 + 554 + led->chan_count = count; 555 + led->chan_id = devm_kcalloc(dev, count, sizeof(u8), GFP_KERNEL); 556 + if (!led->chan_id) 557 + return -ENOMEM; 558 + 559 + for (i = 0; i < count; i++) { 560 + if ((channels[i] == 0) || (channels[i] > flash_data->max_channels)) { 561 + dev_err(dev, "led-source out of HW support range [1-%u]\n", 562 + flash_data->max_channels); 563 + return -EINVAL; 564 + } 565 + 566 + /* Make chan_id indexing from 0 */ 567 + led->chan_id[i] = channels[i] - 1; 568 + } 569 + 570 + rc = fwnode_property_read_u32(node, "led-max-microamp", &current_ua); 571 + if (rc < 0) { 572 + dev_err(dev, "Failed to read led-max-microamp property, rc=%d\n", rc); 573 + return rc; 574 + } 575 + 576 + if (current_ua == 0) { 577 + dev_err(dev, "led-max-microamp shouldn't be 0\n"); 578 + return -EINVAL; 579 + } 580 + 581 + current_ua = min_t(u32, current_ua, TORCH_CURRENT_MAX_UA * led->chan_count); 582 + led->max_torch_current_ma = current_ua / UA_PER_MA; 583 + 584 + if (fwnode_property_present(node, "flash-max-microamp")) { 585 + flash->led_cdev.flags |= LED_DEV_CAP_FLASH; 586 + 587 + rc = fwnode_property_read_u32(node, "flash-max-microamp", &current_ua); 588 + if (rc < 0) { 589 + dev_err(dev, "Failed to read flash-max-microamp property, rc=%d\n", 590 + rc); 591 + return rc; 592 + } 593 + 594 + current_ua = min_t(u32, current_ua, FLASH_CURRENT_MAX_UA * led->chan_count); 595 + current_ua = min_t(u32, current_ua, FLASH_TOTAL_CURRENT_MAX_UA); 596 + 597 + /* Initialize flash class LED device brightness settings */ 598 + brightness = &flash->brightness; 599 + brightness->min = brightness->step = FLASH_IRES_UA * led->chan_count; 600 + brightness->max = current_ua; 601 + brightness->val = min_t(u32, current_ua, FLASH_CURRENT_DEFAULT_UA); 602 + 603 + led->max_flash_current_ma = current_ua / UA_PER_MA; 604 + led->flash_current_ma = brightness->val / UA_PER_MA; 605 + 606 + rc = fwnode_property_read_u32(node, "flash-max-timeout-us", &timeout_us); 607 + if (rc < 0) { 608 + dev_err(dev, "Failed to read flash-max-timeout-us property, rc=%d\n", 609 + rc); 610 + return rc; 611 + } 612 + 613 + timeout_us = min_t(u32, timeout_us, FLASH_TIMEOUT_MAX_US); 614 + 615 + /* Initialize flash class LED device timeout settings */ 616 + timeout = &flash->timeout; 617 + timeout->min = timeout->step = FLASH_TIMEOUT_STEP_US; 618 + timeout->val = timeout->max = timeout_us; 619 + 620 + led->max_timeout_ms = led->flash_timeout_ms = timeout_us / USEC_PER_MSEC; 621 + 622 + flash->ops = &qcom_flash_ops; 623 + } 624 + 625 + flash->led_cdev.brightness_set_blocking = qcom_flash_led_brightness_set; 626 + 627 + init_data.fwnode = node; 628 + init_data.devicename = NULL; 629 + init_data.default_label = NULL; 630 + init_data.devname_mandatory = false; 631 + 632 + rc = devm_led_classdev_flash_register_ext(dev, flash, &init_data); 633 + if (rc < 0) { 634 + dev_err(dev, "Register flash LED classdev failed, rc=%d\n", rc); 635 + return rc; 636 + } 637 + 638 + return qcom_flash_v4l2_init(dev, led, node); 639 + } 640 + 641 + static int qcom_flash_led_probe(struct platform_device *pdev) 642 + { 643 + struct qcom_flash_data *flash_data; 644 + struct qcom_flash_led *led; 645 + struct fwnode_handle *child; 646 + struct device *dev = &pdev->dev; 647 + struct regmap *regmap; 648 + struct reg_field *regs; 649 + int count, i, rc; 650 + u32 val, reg_base; 651 + 652 + flash_data = devm_kzalloc(dev, sizeof(*flash_data), GFP_KERNEL); 653 + if (!flash_data) 654 + return -ENOMEM; 655 + 656 + regmap = dev_get_regmap(dev->parent, NULL); 657 + if (!regmap) { 658 + dev_err(dev, "Failed to get parent regmap\n"); 659 + return -EINVAL; 660 + } 661 + 662 + rc = fwnode_property_read_u32(dev->fwnode, "reg", &reg_base); 663 + if (rc < 0) { 664 + dev_err(dev, "Failed to get register base address, rc=%d\n", rc); 665 + return rc; 666 + } 667 + 668 + rc = regmap_read(regmap, reg_base + FLASH_TYPE_REG, &val); 669 + if (rc < 0) { 670 + dev_err(dev, "Read flash LED module type failed, rc=%d\n", rc); 671 + return rc; 672 + } 673 + 674 + if (val != FLASH_TYPE_VAL) { 675 + dev_err(dev, "type %#x is not a flash LED module\n", val); 676 + return -ENODEV; 677 + } 678 + 679 + rc = regmap_read(regmap, reg_base + FLASH_SUBTYPE_REG, &val); 680 + if (rc < 0) { 681 + dev_err(dev, "Read flash LED module subtype failed, rc=%d\n", rc); 682 + return rc; 683 + } 684 + 685 + if (val == FLASH_SUBTYPE_3CH_VAL) { 686 + flash_data->hw_type = QCOM_MVFLASH_3CH; 687 + flash_data->max_channels = 3; 688 + regs = mvflash_3ch_regs; 689 + } else if (val == FLASH_SUBTYPE_4CH_VAL) { 690 + flash_data->hw_type = QCOM_MVFLASH_4CH; 691 + flash_data->max_channels = 4; 692 + regs = mvflash_4ch_regs; 693 + } else { 694 + dev_err(dev, "flash LED subtype %#x is not yet supported\n", val); 695 + return -ENODEV; 696 + } 697 + 698 + for (i = 0; i < REG_MAX_COUNT; i++) 699 + regs[i].reg += reg_base; 700 + 701 + rc = devm_regmap_field_bulk_alloc(dev, regmap, flash_data->r_fields, regs, REG_MAX_COUNT); 702 + if (rc < 0) { 703 + dev_err(dev, "Failed to allocate regmap field, rc=%d\n", rc); 704 + return rc; 705 + } 706 + 707 + platform_set_drvdata(pdev, flash_data); 708 + mutex_init(&flash_data->lock); 709 + 710 + count = device_get_child_node_count(dev); 711 + if (count == 0 || count > flash_data->max_channels) { 712 + dev_err(dev, "No child or child count exceeds %d\n", flash_data->max_channels); 713 + return -EINVAL; 714 + } 715 + 716 + flash_data->v4l2_flash = devm_kcalloc(dev, count, 717 + sizeof(*flash_data->v4l2_flash), GFP_KERNEL); 718 + if (!flash_data->v4l2_flash) 719 + return -ENOMEM; 720 + 721 + device_for_each_child_node(dev, child) { 722 + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); 723 + if (!led) { 724 + rc = -ENOMEM; 725 + goto release; 726 + } 727 + 728 + led->flash_data = flash_data; 729 + rc = qcom_flash_register_led_device(dev, child, led); 730 + if (rc < 0) 731 + goto release; 732 + 733 + flash_data->leds_count++; 734 + } 735 + 736 + return 0; 737 + 738 + release: 739 + while (flash_data->v4l2_flash[flash_data->leds_count] && flash_data->leds_count) 740 + v4l2_flash_release(flash_data->v4l2_flash[flash_data->leds_count--]); 741 + return rc; 742 + } 743 + 744 + static int qcom_flash_led_remove(struct platform_device *pdev) 745 + { 746 + struct qcom_flash_data *flash_data = platform_get_drvdata(pdev); 747 + 748 + while (flash_data->v4l2_flash[flash_data->leds_count] && flash_data->leds_count) 749 + v4l2_flash_release(flash_data->v4l2_flash[flash_data->leds_count--]); 750 + 751 + mutex_destroy(&flash_data->lock); 752 + return 0; 753 + } 754 + 755 + static const struct of_device_id qcom_flash_led_match_table[] = { 756 + { .compatible = "qcom,spmi-flash-led" }, 757 + { } 758 + }; 759 + 760 + MODULE_DEVICE_TABLE(of, qcom_flash_led_match_table); 761 + static struct platform_driver qcom_flash_led_driver = { 762 + .driver = { 763 + .name = "leds-qcom-flash", 764 + .of_match_table = qcom_flash_led_match_table, 765 + }, 766 + .probe = qcom_flash_led_probe, 767 + .remove = qcom_flash_led_remove, 768 + }; 769 + 770 + module_platform_driver(qcom_flash_led_driver); 771 + 772 + MODULE_DESCRIPTION("QCOM Flash LED driver"); 773 + MODULE_LICENSE("GPL");
+160
drivers/leds/leds-bd2606mvv.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2023 Andreas Kemnade 4 + * 5 + * Datasheet: 6 + * https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/led_driver/bd2606mvv_1-e.pdf 7 + * 8 + * If LED brightness cannot be controlled independently due to shared 9 + * brightness registers, max_brightness is set to 1 and only on/off 10 + * is possible for the affected LED pair. 11 + */ 12 + 13 + #include <linux/i2c.h> 14 + #include <linux/leds.h> 15 + #include <linux/module.h> 16 + #include <linux/mod_devicetable.h> 17 + #include <linux/property.h> 18 + #include <linux/regmap.h> 19 + #include <linux/slab.h> 20 + 21 + #define BD2606_MAX_LEDS 6 22 + #define BD2606_MAX_BRIGHTNESS 63 23 + #define BD2606_REG_PWRCNT 3 24 + #define ldev_to_led(c) container_of(c, struct bd2606mvv_led, ldev) 25 + 26 + struct bd2606mvv_led { 27 + unsigned int led_no; 28 + struct led_classdev ldev; 29 + struct bd2606mvv_priv *priv; 30 + }; 31 + 32 + struct bd2606mvv_priv { 33 + struct bd2606mvv_led leds[BD2606_MAX_LEDS]; 34 + struct regmap *regmap; 35 + }; 36 + 37 + static int 38 + bd2606mvv_brightness_set(struct led_classdev *led_cdev, 39 + enum led_brightness brightness) 40 + { 41 + struct bd2606mvv_led *led = ldev_to_led(led_cdev); 42 + struct bd2606mvv_priv *priv = led->priv; 43 + int err; 44 + 45 + if (brightness == 0) 46 + return regmap_update_bits(priv->regmap, 47 + BD2606_REG_PWRCNT, 48 + 1 << led->led_no, 49 + 0); 50 + 51 + /* shared brightness register */ 52 + err = regmap_write(priv->regmap, led->led_no / 2, 53 + led_cdev->max_brightness == 1 ? 54 + BD2606_MAX_BRIGHTNESS : brightness); 55 + if (err) 56 + return err; 57 + 58 + return regmap_update_bits(priv->regmap, 59 + BD2606_REG_PWRCNT, 60 + 1 << led->led_no, 61 + 1 << led->led_no); 62 + } 63 + 64 + static const struct regmap_config bd2606mvv_regmap = { 65 + .reg_bits = 8, 66 + .val_bits = 8, 67 + .max_register = 0x3, 68 + }; 69 + 70 + static int bd2606mvv_probe(struct i2c_client *client) 71 + { 72 + struct fwnode_handle *np, *child; 73 + struct device *dev = &client->dev; 74 + struct bd2606mvv_priv *priv; 75 + struct fwnode_handle *led_fwnodes[BD2606_MAX_LEDS] = { 0 }; 76 + int active_pairs[BD2606_MAX_LEDS / 2] = { 0 }; 77 + int err, reg; 78 + int i; 79 + 80 + np = dev_fwnode(dev); 81 + if (!np) 82 + return -ENODEV; 83 + 84 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 85 + if (!priv) 86 + return -ENOMEM; 87 + 88 + priv->regmap = devm_regmap_init_i2c(client, &bd2606mvv_regmap); 89 + if (IS_ERR(priv->regmap)) { 90 + err = PTR_ERR(priv->regmap); 91 + dev_err(dev, "Failed to allocate register map: %d\n", err); 92 + return err; 93 + } 94 + 95 + i2c_set_clientdata(client, priv); 96 + 97 + fwnode_for_each_available_child_node(np, child) { 98 + struct bd2606mvv_led *led; 99 + 100 + err = fwnode_property_read_u32(child, "reg", &reg); 101 + if (err) { 102 + fwnode_handle_put(child); 103 + return err; 104 + } 105 + if (reg < 0 || reg >= BD2606_MAX_LEDS || led_fwnodes[reg]) { 106 + fwnode_handle_put(child); 107 + return -EINVAL; 108 + } 109 + led = &priv->leds[reg]; 110 + led_fwnodes[reg] = child; 111 + active_pairs[reg / 2]++; 112 + led->priv = priv; 113 + led->led_no = reg; 114 + led->ldev.brightness_set_blocking = bd2606mvv_brightness_set; 115 + led->ldev.max_brightness = BD2606_MAX_BRIGHTNESS; 116 + } 117 + 118 + for (i = 0; i < BD2606_MAX_LEDS; i++) { 119 + struct led_init_data init_data = {}; 120 + 121 + if (!led_fwnodes[i]) 122 + continue; 123 + 124 + init_data.fwnode = led_fwnodes[i]; 125 + /* Check whether brightness can be independently adjusted. */ 126 + if (active_pairs[i / 2] == 2) 127 + priv->leds[i].ldev.max_brightness = 1; 128 + 129 + err = devm_led_classdev_register_ext(dev, 130 + &priv->leds[i].ldev, 131 + &init_data); 132 + if (err < 0) { 133 + fwnode_handle_put(child); 134 + return dev_err_probe(dev, err, 135 + "couldn't register LED %s\n", 136 + priv->leds[i].ldev.name); 137 + } 138 + } 139 + return 0; 140 + } 141 + 142 + static const struct of_device_id __maybe_unused of_bd2606mvv_leds_match[] = { 143 + { .compatible = "rohm,bd2606mvv", }, 144 + {}, 145 + }; 146 + MODULE_DEVICE_TABLE(of, of_bd2606mvv_leds_match); 147 + 148 + static struct i2c_driver bd2606mvv_driver = { 149 + .driver = { 150 + .name = "leds-bd2606mvv", 151 + .of_match_table = of_match_ptr(of_bd2606mvv_leds_match), 152 + }, 153 + .probe_new = bd2606mvv_probe, 154 + }; 155 + 156 + module_i2c_driver(bd2606mvv_driver); 157 + 158 + MODULE_AUTHOR("Andreas Kemnade <andreas@kemnade.info>"); 159 + MODULE_DESCRIPTION("BD2606 LED driver"); 160 + MODULE_LICENSE("GPL");
+3 -7
drivers/leds/leds-lp8860.c
··· 15 15 #include <linux/module.h> 16 16 #include <linux/mutex.h> 17 17 #include <linux/of.h> 18 - #include <linux/of_gpio.h> 19 18 #include <linux/gpio/consumer.h> 20 19 #include <linux/slab.h> 21 20 ··· 249 250 } 250 251 } 251 252 252 - if (led->enable_gpio) 253 - gpiod_direction_output(led->enable_gpio, 1); 253 + gpiod_direction_output(led->enable_gpio, 1); 254 254 255 255 ret = lp8860_fault_check(led); 256 256 if (ret) ··· 292 294 293 295 out: 294 296 if (ret) 295 - if (led->enable_gpio) 296 - gpiod_direction_output(led->enable_gpio, 0); 297 + gpiod_direction_output(led->enable_gpio, 0); 297 298 298 299 if (led->regulator) { 299 300 ret = regulator_disable(led->regulator); ··· 446 449 struct lp8860_led *led = i2c_get_clientdata(client); 447 450 int ret; 448 451 449 - if (led->enable_gpio) 450 - gpiod_direction_output(led->enable_gpio, 0); 452 + gpiod_direction_output(led->enable_gpio, 0); 451 453 452 454 if (led->regulator) { 453 455 ret = regulator_disable(led->regulator);
+3 -2
drivers/leds/leds-tca6507.c
··· 691 691 if (fwnode_property_read_string(child, "label", &led.name)) 692 692 led.name = fwnode_get_name(child); 693 693 694 - fwnode_property_read_string(child, "linux,default-trigger", 695 - &led.default_trigger); 694 + if (fwnode_property_read_string(child, "linux,default-trigger", 695 + &led.default_trigger)) 696 + led.default_trigger = NULL; 696 697 697 698 led.flags = 0; 698 699 if (fwnode_device_is_compatible(child, "gpio"))
+1 -1
drivers/leds/leds-tlc591xx.c
··· 135 135 .max_register = 0x1e, 136 136 }; 137 137 138 - static const struct of_device_id of_tlc591xx_leds_match[] = { 138 + static const struct of_device_id of_tlc591xx_leds_match[] __maybe_unused = { 139 139 { .compatible = "ti,tlc59116", 140 140 .data = &tlc59116 }, 141 141 { .compatible = "ti,tlc59108",
+13
drivers/leds/rgb/Kconfig
··· 26 26 27 27 If compiled as a module, the module will be named leds-qcom-lpg. 28 28 29 + config LEDS_MT6370_RGB 30 + tristate "LED Support for MediaTek MT6370 PMIC" 31 + depends on MFD_MT6370 32 + select LINEAR_RANGES 33 + help 34 + Say Y here to enable support for MT6370_RGB LED device. 35 + In MT6370, there are four channel current-sink LED drivers that 36 + support hardware pattern for constant current, PWM, and breath mode. 37 + Isink4 channel can also be used as a CHG_VIN power good indicator. 38 + 39 + This driver can also be built as a module. If so, the module 40 + will be called "leds-mt6370-rgb". 41 + 29 42 endif # LEDS_CLASS_MULTICOLOR
+1
drivers/leds/rgb/Makefile
··· 2 2 3 3 obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o 4 4 obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o 5 + obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370-rgb.o
+1011
drivers/leds/rgb/leds-mt6370-rgb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2023 Richtek Technology Corp. 4 + * 5 + * Authors: 6 + * ChiYuan Huang <cy_huang@richtek.com> 7 + * Alice Chen <alice_chen@richtek.com> 8 + */ 9 + 10 + #include <linux/bitfield.h> 11 + #include <linux/bitops.h> 12 + #include <linux/kernel.h> 13 + #include <linux/leds.h> 14 + #include <linux/led-class-multicolor.h> 15 + #include <linux/linear_range.h> 16 + #include <linux/mod_devicetable.h> 17 + #include <linux/module.h> 18 + #include <linux/mutex.h> 19 + #include <linux/platform_device.h> 20 + #include <linux/property.h> 21 + #include <linux/regmap.h> 22 + #include <linux/util_macros.h> 23 + 24 + #include <asm/unaligned.h> 25 + 26 + enum { 27 + MT6370_LED_ISNK1 = 0, 28 + MT6370_LED_ISNK2, 29 + MT6370_LED_ISNK3, 30 + MT6370_LED_ISNK4, 31 + MT6370_MAX_LEDS 32 + }; 33 + 34 + enum mt6370_led_mode { 35 + MT6370_LED_PWM_MODE = 0, 36 + MT6370_LED_BREATH_MODE, 37 + MT6370_LED_REG_MODE, 38 + MT6370_LED_MAX_MODE 39 + }; 40 + 41 + enum mt6370_led_field { 42 + F_RGB_EN = 0, 43 + F_CHGIND_EN, 44 + F_LED1_CURR, 45 + F_LED2_CURR, 46 + F_LED3_CURR, 47 + F_LED4_CURR, 48 + F_LED1_MODE, 49 + F_LED2_MODE, 50 + F_LED3_MODE, 51 + F_LED4_MODE, 52 + F_LED1_DUTY, 53 + F_LED2_DUTY, 54 + F_LED3_DUTY, 55 + F_LED4_DUTY, 56 + F_LED1_FREQ, 57 + F_LED2_FREQ, 58 + F_LED3_FREQ, 59 + F_LED4_FREQ, 60 + F_MAX_FIELDS 61 + }; 62 + 63 + enum mt6370_led_ranges { 64 + R_LED123_CURR = 0, 65 + R_LED4_CURR, 66 + R_LED_TRFON, 67 + R_LED_TOFF, 68 + R_MAX_RANGES 69 + }; 70 + 71 + enum mt6370_pattern { 72 + P_LED_TR1 = 0, 73 + P_LED_TR2, 74 + P_LED_TF1, 75 + P_LED_TF2, 76 + P_LED_TON, 77 + P_LED_TOFF, 78 + P_MAX_PATTERNS 79 + }; 80 + 81 + #define MT6370_REG_DEV_INFO 0x100 82 + #define MT6370_REG_RGB1_DIM 0x182 83 + #define MT6370_REG_RGB2_DIM 0x183 84 + #define MT6370_REG_RGB3_DIM 0x184 85 + #define MT6370_REG_RGB_EN 0x185 86 + #define MT6370_REG_RGB1_ISNK 0x186 87 + #define MT6370_REG_RGB2_ISNK 0x187 88 + #define MT6370_REG_RGB3_ISNK 0x188 89 + #define MT6370_REG_RGB1_TR 0x189 90 + #define MT6370_REG_RGB_CHRIND_DIM 0x192 91 + #define MT6370_REG_RGB_CHRIND_CTRL 0x193 92 + #define MT6370_REG_RGB_CHRIND_TR 0x194 93 + 94 + #define MT6372_REG_RGB_EN 0x182 95 + #define MT6372_REG_RGB1_ISNK 0x183 96 + #define MT6372_REG_RGB2_ISNK 0x184 97 + #define MT6372_REG_RGB3_ISNK 0x185 98 + #define MT6372_REG_RGB4_ISNK 0x186 99 + #define MT6372_REG_RGB1_DIM 0x187 100 + #define MT6372_REG_RGB2_DIM 0x188 101 + #define MT6372_REG_RGB3_DIM 0x189 102 + #define MT6372_REG_RGB4_DIM 0x18A 103 + #define MT6372_REG_RGB12_FREQ 0x18B 104 + #define MT6372_REG_RGB34_FREQ 0x18C 105 + #define MT6372_REG_RGB1_TR 0x18D 106 + 107 + #define MT6370_VENDOR_ID_MASK GENMASK(7, 4) 108 + #define MT6372_VENDOR_ID 0x9 109 + #define MT6372C_VENDOR_ID 0xb 110 + #define MT6370_CHEN_BIT(id) BIT(MT6370_LED_ISNK4 - id) 111 + #define MT6370_VIRTUAL_MULTICOLOR 5 112 + #define MC_CHANNEL_NUM 3 113 + #define MT6370_PWM_DUTY (BIT(5) - 1) 114 + #define MT6372_PWM_DUTY (BIT(8) - 1) 115 + 116 + struct mt6370_led { 117 + /* 118 + * If the color of the LED in DT is set to 119 + * - 'LED_COLOR_ID_RGB' 120 + * - 'LED_COLOR_ID_MULTI' 121 + * The member 'index' of this struct will be set to 122 + * 'MT6370_VIRTUAL_MULTICOLOR'. 123 + * If so, this LED will choose 'struct led_classdev_mc mc' to use. 124 + * Instead, if the member 'index' of this struct is set to 125 + * 'MT6370_LED_ISNK1' ~ 'MT6370_LED_ISNK4', then this LED will choose 126 + * 'struct led_classdev isink' to use. 127 + */ 128 + union { 129 + struct led_classdev isink; 130 + struct led_classdev_mc mc; 131 + }; 132 + struct mt6370_priv *priv; 133 + enum led_default_state default_state; 134 + u32 index; 135 + }; 136 + 137 + struct mt6370_pdata { 138 + const unsigned int *tfreq; 139 + unsigned int tfreq_len; 140 + u16 reg_rgb1_tr; 141 + s16 reg_rgb_chrind_tr; 142 + u8 pwm_duty; 143 + }; 144 + 145 + struct mt6370_priv { 146 + /* Per LED access lock */ 147 + struct mutex lock; 148 + struct regmap *regmap; 149 + struct regmap_field *fields[F_MAX_FIELDS]; 150 + const struct reg_field *reg_fields; 151 + const struct linear_range *ranges; 152 + struct reg_cfg *reg_cfgs; 153 + const struct mt6370_pdata *pdata; 154 + unsigned int leds_count; 155 + unsigned int leds_active; 156 + struct mt6370_led leds[]; 157 + }; 158 + 159 + static const struct reg_field common_reg_fields[F_MAX_FIELDS] = { 160 + [F_RGB_EN] = REG_FIELD(MT6370_REG_RGB_EN, 4, 7), 161 + [F_CHGIND_EN] = REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 7, 7), 162 + [F_LED1_CURR] = REG_FIELD(MT6370_REG_RGB1_ISNK, 0, 2), 163 + [F_LED2_CURR] = REG_FIELD(MT6370_REG_RGB2_ISNK, 0, 2), 164 + [F_LED3_CURR] = REG_FIELD(MT6370_REG_RGB3_ISNK, 0, 2), 165 + [F_LED4_CURR] = REG_FIELD(MT6370_REG_RGB_CHRIND_CTRL, 0, 1), 166 + [F_LED1_MODE] = REG_FIELD(MT6370_REG_RGB1_DIM, 5, 6), 167 + [F_LED2_MODE] = REG_FIELD(MT6370_REG_RGB2_DIM, 5, 6), 168 + [F_LED3_MODE] = REG_FIELD(MT6370_REG_RGB3_DIM, 5, 6), 169 + [F_LED4_MODE] = REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 5, 6), 170 + [F_LED1_DUTY] = REG_FIELD(MT6370_REG_RGB1_DIM, 0, 4), 171 + [F_LED2_DUTY] = REG_FIELD(MT6370_REG_RGB2_DIM, 0, 4), 172 + [F_LED3_DUTY] = REG_FIELD(MT6370_REG_RGB3_DIM, 0, 4), 173 + [F_LED4_DUTY] = REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 0, 4), 174 + [F_LED1_FREQ] = REG_FIELD(MT6370_REG_RGB1_ISNK, 3, 5), 175 + [F_LED2_FREQ] = REG_FIELD(MT6370_REG_RGB2_ISNK, 3, 5), 176 + [F_LED3_FREQ] = REG_FIELD(MT6370_REG_RGB3_ISNK, 3, 5), 177 + [F_LED4_FREQ] = REG_FIELD(MT6370_REG_RGB_CHRIND_CTRL, 2, 4), 178 + }; 179 + 180 + static const struct reg_field mt6372_reg_fields[F_MAX_FIELDS] = { 181 + [F_RGB_EN] = REG_FIELD(MT6372_REG_RGB_EN, 4, 7), 182 + [F_CHGIND_EN] = REG_FIELD(MT6372_REG_RGB_EN, 3, 3), 183 + [F_LED1_CURR] = REG_FIELD(MT6372_REG_RGB1_ISNK, 0, 3), 184 + [F_LED2_CURR] = REG_FIELD(MT6372_REG_RGB2_ISNK, 0, 3), 185 + [F_LED3_CURR] = REG_FIELD(MT6372_REG_RGB3_ISNK, 0, 3), 186 + [F_LED4_CURR] = REG_FIELD(MT6372_REG_RGB4_ISNK, 0, 3), 187 + [F_LED1_MODE] = REG_FIELD(MT6372_REG_RGB1_ISNK, 6, 7), 188 + [F_LED2_MODE] = REG_FIELD(MT6372_REG_RGB2_ISNK, 6, 7), 189 + [F_LED3_MODE] = REG_FIELD(MT6372_REG_RGB3_ISNK, 6, 7), 190 + [F_LED4_MODE] = REG_FIELD(MT6372_REG_RGB4_ISNK, 6, 7), 191 + [F_LED1_DUTY] = REG_FIELD(MT6372_REG_RGB1_DIM, 0, 7), 192 + [F_LED2_DUTY] = REG_FIELD(MT6372_REG_RGB2_DIM, 0, 7), 193 + [F_LED3_DUTY] = REG_FIELD(MT6372_REG_RGB3_DIM, 0, 7), 194 + [F_LED4_DUTY] = REG_FIELD(MT6372_REG_RGB4_DIM, 0, 7), 195 + [F_LED1_FREQ] = REG_FIELD(MT6372_REG_RGB12_FREQ, 5, 7), 196 + [F_LED2_FREQ] = REG_FIELD(MT6372_REG_RGB12_FREQ, 2, 4), 197 + [F_LED3_FREQ] = REG_FIELD(MT6372_REG_RGB34_FREQ, 5, 7), 198 + [F_LED4_FREQ] = REG_FIELD(MT6372_REG_RGB34_FREQ, 2, 4), 199 + }; 200 + 201 + /* Current unit: microamp, time unit: millisecond */ 202 + static const struct linear_range common_led_ranges[R_MAX_RANGES] = { 203 + [R_LED123_CURR] = { 4000, 1, 6, 4000 }, 204 + [R_LED4_CURR] = { 2000, 1, 3, 2000 }, 205 + [R_LED_TRFON] = { 125, 0, 15, 200 }, 206 + [R_LED_TOFF] = { 250, 0, 15, 400 }, 207 + }; 208 + 209 + static const struct linear_range mt6372_led_ranges[R_MAX_RANGES] = { 210 + [R_LED123_CURR] = { 2000, 1, 14, 2000 }, 211 + [R_LED4_CURR] = { 2000, 1, 14, 2000 }, 212 + [R_LED_TRFON] = { 125, 0, 15, 250 }, 213 + [R_LED_TOFF] = { 250, 0, 15, 500 }, 214 + }; 215 + 216 + static const unsigned int common_tfreqs[] = { 217 + 10000, 5000, 2000, 1000, 500, 200, 5, 1, 218 + }; 219 + 220 + static const unsigned int mt6372_tfreqs[] = { 221 + 8000, 4000, 2000, 1000, 500, 250, 8, 4, 222 + }; 223 + 224 + static const struct mt6370_pdata common_pdata = { 225 + .tfreq = common_tfreqs, 226 + .tfreq_len = ARRAY_SIZE(common_tfreqs), 227 + .pwm_duty = MT6370_PWM_DUTY, 228 + .reg_rgb1_tr = MT6370_REG_RGB1_TR, 229 + .reg_rgb_chrind_tr = MT6370_REG_RGB_CHRIND_TR, 230 + }; 231 + 232 + static const struct mt6370_pdata mt6372_pdata = { 233 + .tfreq = mt6372_tfreqs, 234 + .tfreq_len = ARRAY_SIZE(mt6372_tfreqs), 235 + .pwm_duty = MT6372_PWM_DUTY, 236 + .reg_rgb1_tr = MT6372_REG_RGB1_TR, 237 + .reg_rgb_chrind_tr = -1, 238 + }; 239 + 240 + static enum mt6370_led_field mt6370_get_led_current_field(unsigned int led_no) 241 + { 242 + switch (led_no) { 243 + case MT6370_LED_ISNK1: 244 + return F_LED1_CURR; 245 + case MT6370_LED_ISNK2: 246 + return F_LED2_CURR; 247 + case MT6370_LED_ISNK3: 248 + return F_LED3_CURR; 249 + default: 250 + return F_LED4_CURR; 251 + } 252 + } 253 + 254 + static int mt6370_set_led_brightness(struct mt6370_priv *priv, unsigned int led_no, 255 + unsigned int level) 256 + { 257 + enum mt6370_led_field sel_field; 258 + 259 + sel_field = mt6370_get_led_current_field(led_no); 260 + 261 + return regmap_field_write(priv->fields[sel_field], level); 262 + } 263 + 264 + static int mt6370_get_led_brightness(struct mt6370_priv *priv, unsigned int led_no, 265 + unsigned int *level) 266 + { 267 + enum mt6370_led_field sel_field; 268 + 269 + sel_field = mt6370_get_led_current_field(led_no); 270 + 271 + return regmap_field_read(priv->fields[sel_field], level); 272 + } 273 + 274 + static int mt6370_set_led_duty(struct mt6370_priv *priv, unsigned int led_no, unsigned int ton, 275 + unsigned int toff) 276 + { 277 + const struct mt6370_pdata *pdata = priv->pdata; 278 + enum mt6370_led_field sel_field; 279 + unsigned int divisor, ratio; 280 + 281 + divisor = pdata->pwm_duty; 282 + ratio = ton * divisor / (ton + toff); 283 + 284 + switch (led_no) { 285 + case MT6370_LED_ISNK1: 286 + sel_field = F_LED1_DUTY; 287 + break; 288 + case MT6370_LED_ISNK2: 289 + sel_field = F_LED2_DUTY; 290 + break; 291 + case MT6370_LED_ISNK3: 292 + sel_field = F_LED3_DUTY; 293 + break; 294 + default: 295 + sel_field = F_LED4_DUTY; 296 + break; 297 + } 298 + 299 + return regmap_field_write(priv->fields[sel_field], ratio); 300 + } 301 + 302 + static int mt6370_set_led_freq(struct mt6370_priv *priv, unsigned int led_no, unsigned int ton, 303 + unsigned int toff) 304 + { 305 + const struct mt6370_pdata *pdata = priv->pdata; 306 + enum mt6370_led_field sel_field; 307 + unsigned int tfreq_len = pdata->tfreq_len; 308 + unsigned int tsum, sel; 309 + 310 + tsum = ton + toff; 311 + 312 + if (tsum > pdata->tfreq[0] || tsum < pdata->tfreq[tfreq_len - 1]) 313 + return -EOPNOTSUPP; 314 + 315 + sel = find_closest_descending(tsum, pdata->tfreq, tfreq_len); 316 + 317 + switch (led_no) { 318 + case MT6370_LED_ISNK1: 319 + sel_field = F_LED1_FREQ; 320 + break; 321 + case MT6370_LED_ISNK2: 322 + sel_field = F_LED2_FREQ; 323 + break; 324 + case MT6370_LED_ISNK3: 325 + sel_field = F_LED3_FREQ; 326 + break; 327 + default: 328 + sel_field = F_LED4_FREQ; 329 + break; 330 + } 331 + 332 + return regmap_field_write(priv->fields[sel_field], sel); 333 + } 334 + 335 + static void mt6370_get_breath_reg_base(struct mt6370_priv *priv, unsigned int led_no, 336 + unsigned int *base) 337 + { 338 + const struct mt6370_pdata *pdata = priv->pdata; 339 + 340 + if (pdata->reg_rgb_chrind_tr < 0) { 341 + *base = pdata->reg_rgb1_tr + led_no * 3; 342 + return; 343 + } 344 + 345 + switch (led_no) { 346 + case MT6370_LED_ISNK1: 347 + case MT6370_LED_ISNK2: 348 + case MT6370_LED_ISNK3: 349 + *base = pdata->reg_rgb1_tr + led_no * 3; 350 + break; 351 + default: 352 + *base = pdata->reg_rgb_chrind_tr; 353 + break; 354 + } 355 + } 356 + 357 + static int mt6370_gen_breath_pattern(struct mt6370_priv *priv, struct led_pattern *pattern, u32 len, 358 + u8 *pattern_val, u32 val_len) 359 + { 360 + enum mt6370_led_ranges sel_range; 361 + struct led_pattern *curr; 362 + unsigned int sel; 363 + u32 val = 0; 364 + int i; 365 + 366 + if (len < P_MAX_PATTERNS && val_len < P_MAX_PATTERNS / 2) 367 + return -EINVAL; 368 + 369 + /* 370 + * Pattern list 371 + * tr1: byte 0, b'[7:4] 372 + * tr2: byte 0, b'[3:0] 373 + * tf1: byte 1, b'[7:4] 374 + * tf2: byte 1, b'[3:0] 375 + * ton: byte 2, b'[7:4] 376 + * toff: byte 2, b'[3:0] 377 + */ 378 + for (i = 0; i < P_MAX_PATTERNS; i++) { 379 + curr = pattern + i; 380 + 381 + sel_range = i == P_LED_TOFF ? R_LED_TOFF : R_LED_TRFON; 382 + 383 + linear_range_get_selector_within(priv->ranges + sel_range, curr->delta_t, &sel); 384 + 385 + if (i % 2) { 386 + val |= sel; 387 + } else { 388 + val <<= 8; 389 + val |= sel << 4; 390 + } 391 + } 392 + 393 + put_unaligned_be24(val, pattern_val); 394 + 395 + return 0; 396 + } 397 + 398 + static int mt6370_set_led_mode(struct mt6370_priv *priv, unsigned int led_no, 399 + enum mt6370_led_mode mode) 400 + { 401 + enum mt6370_led_field sel_field; 402 + 403 + switch (led_no) { 404 + case MT6370_LED_ISNK1: 405 + sel_field = F_LED1_MODE; 406 + break; 407 + case MT6370_LED_ISNK2: 408 + sel_field = F_LED2_MODE; 409 + break; 410 + case MT6370_LED_ISNK3: 411 + sel_field = F_LED3_MODE; 412 + break; 413 + default: 414 + sel_field = F_LED4_MODE; 415 + break; 416 + } 417 + 418 + return regmap_field_write(priv->fields[sel_field], mode); 419 + } 420 + 421 + static int mt6370_mc_brightness_set(struct led_classdev *lcdev, enum led_brightness level) 422 + { 423 + struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev); 424 + struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc); 425 + struct mt6370_priv *priv = led->priv; 426 + struct mc_subled *subled; 427 + unsigned int enable, disable; 428 + int i, ret; 429 + 430 + mutex_lock(&priv->lock); 431 + 432 + led_mc_calc_color_components(mccdev, level); 433 + 434 + ret = regmap_field_read(priv->fields[F_RGB_EN], &enable); 435 + if (ret) 436 + goto out_unlock; 437 + 438 + disable = enable; 439 + 440 + for (i = 0; i < mccdev->num_colors; i++) { 441 + u32 brightness; 442 + 443 + subled = mccdev->subled_info + i; 444 + brightness = min(subled->brightness, lcdev->max_brightness); 445 + disable &= ~MT6370_CHEN_BIT(subled->channel); 446 + 447 + if (level == 0) { 448 + enable &= ~MT6370_CHEN_BIT(subled->channel); 449 + 450 + ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_REG_MODE); 451 + if (ret) 452 + goto out_unlock; 453 + 454 + continue; 455 + } 456 + 457 + if (brightness == 0) { 458 + enable &= ~MT6370_CHEN_BIT(subled->channel); 459 + continue; 460 + } 461 + 462 + enable |= MT6370_CHEN_BIT(subled->channel); 463 + 464 + ret = mt6370_set_led_brightness(priv, subled->channel, brightness); 465 + if (ret) 466 + goto out_unlock; 467 + } 468 + 469 + ret = regmap_field_write(priv->fields[F_RGB_EN], disable); 470 + if (ret) 471 + goto out_unlock; 472 + 473 + ret = regmap_field_write(priv->fields[F_RGB_EN], enable); 474 + 475 + out_unlock: 476 + mutex_unlock(&priv->lock); 477 + 478 + return ret; 479 + } 480 + 481 + static int mt6370_mc_blink_set(struct led_classdev *lcdev, 482 + unsigned long *delay_on, 483 + unsigned long *delay_off) 484 + { 485 + struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev); 486 + struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc); 487 + struct mt6370_priv *priv = led->priv; 488 + struct mc_subled *subled; 489 + unsigned int enable, disable; 490 + int i, ret; 491 + 492 + mutex_lock(&priv->lock); 493 + 494 + if (!*delay_on && !*delay_off) 495 + *delay_on = *delay_off = 500; 496 + 497 + ret = regmap_field_read(priv->fields[F_RGB_EN], &enable); 498 + if (ret) 499 + goto out_unlock; 500 + 501 + disable = enable; 502 + 503 + for (i = 0; i < mccdev->num_colors; i++) { 504 + subled = mccdev->subled_info + i; 505 + 506 + disable &= ~MT6370_CHEN_BIT(subled->channel); 507 + 508 + ret = mt6370_set_led_duty(priv, subled->channel, *delay_on, *delay_off); 509 + if (ret) 510 + goto out_unlock; 511 + 512 + ret = mt6370_set_led_freq(priv, subled->channel, *delay_on, *delay_off); 513 + if (ret) 514 + goto out_unlock; 515 + 516 + ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_PWM_MODE); 517 + if (ret) 518 + goto out_unlock; 519 + } 520 + 521 + /* Toggle to make pattern timing the same */ 522 + ret = regmap_field_write(priv->fields[F_RGB_EN], disable); 523 + if (ret) 524 + goto out_unlock; 525 + 526 + ret = regmap_field_write(priv->fields[F_RGB_EN], enable); 527 + 528 + out_unlock: 529 + mutex_unlock(&priv->lock); 530 + 531 + return ret; 532 + } 533 + 534 + static int mt6370_mc_pattern_set(struct led_classdev *lcdev, struct led_pattern *pattern, u32 len, 535 + int repeat) 536 + { 537 + struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev); 538 + struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc); 539 + struct mt6370_priv *priv = led->priv; 540 + struct mc_subled *subled; 541 + unsigned int reg_base, enable, disable; 542 + u8 params[P_MAX_PATTERNS / 2]; 543 + int i, ret; 544 + 545 + mutex_lock(&priv->lock); 546 + 547 + ret = mt6370_gen_breath_pattern(priv, pattern, len, params, sizeof(params)); 548 + if (ret) 549 + goto out_unlock; 550 + 551 + ret = regmap_field_read(priv->fields[F_RGB_EN], &enable); 552 + if (ret) 553 + goto out_unlock; 554 + 555 + disable = enable; 556 + 557 + for (i = 0; i < mccdev->num_colors; i++) { 558 + subled = mccdev->subled_info + i; 559 + 560 + mt6370_get_breath_reg_base(priv, subled->channel, &reg_base); 561 + disable &= ~MT6370_CHEN_BIT(subled->channel); 562 + 563 + ret = regmap_raw_write(priv->regmap, reg_base, params, sizeof(params)); 564 + if (ret) 565 + goto out_unlock; 566 + 567 + ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_BREATH_MODE); 568 + if (ret) 569 + goto out_unlock; 570 + } 571 + 572 + /* Toggle to make pattern timing be the same */ 573 + ret = regmap_field_write(priv->fields[F_RGB_EN], disable); 574 + if (ret) 575 + goto out_unlock; 576 + 577 + ret = regmap_field_write(priv->fields[F_RGB_EN], enable); 578 + 579 + out_unlock: 580 + mutex_unlock(&priv->lock); 581 + 582 + return ret; 583 + } 584 + 585 + static inline int mt6370_mc_pattern_clear(struct led_classdev *lcdev) 586 + { 587 + struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev); 588 + struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc); 589 + struct mt6370_priv *priv = led->priv; 590 + struct mc_subled *subled; 591 + int i, ret; 592 + 593 + mutex_lock(&led->priv->lock); 594 + 595 + for (i = 0; i < mccdev->num_colors; i++) { 596 + subled = mccdev->subled_info + i; 597 + 598 + ret = mt6370_set_led_mode(priv, subled->channel, MT6370_LED_REG_MODE); 599 + if (ret) 600 + break; 601 + } 602 + 603 + mutex_unlock(&led->priv->lock); 604 + 605 + return ret; 606 + } 607 + 608 + static int mt6370_isnk_brightness_set(struct led_classdev *lcdev, 609 + enum led_brightness level) 610 + { 611 + struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink); 612 + struct mt6370_priv *priv = led->priv; 613 + unsigned int enable; 614 + int ret; 615 + 616 + mutex_lock(&priv->lock); 617 + 618 + ret = regmap_field_read(priv->fields[F_RGB_EN], &enable); 619 + if (ret) 620 + goto out_unlock; 621 + 622 + if (level == 0) { 623 + enable &= ~MT6370_CHEN_BIT(led->index); 624 + 625 + ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_REG_MODE); 626 + if (ret) 627 + goto out_unlock; 628 + } else { 629 + enable |= MT6370_CHEN_BIT(led->index); 630 + 631 + ret = mt6370_set_led_brightness(priv, led->index, level); 632 + if (ret) 633 + goto out_unlock; 634 + } 635 + 636 + ret = regmap_field_write(priv->fields[F_RGB_EN], enable); 637 + 638 + out_unlock: 639 + mutex_unlock(&priv->lock); 640 + 641 + return ret; 642 + } 643 + 644 + static int mt6370_isnk_blink_set(struct led_classdev *lcdev, unsigned long *delay_on, 645 + unsigned long *delay_off) 646 + { 647 + struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink); 648 + struct mt6370_priv *priv = led->priv; 649 + int ret; 650 + 651 + mutex_lock(&priv->lock); 652 + 653 + if (!*delay_on && !*delay_off) 654 + *delay_on = *delay_off = 500; 655 + 656 + ret = mt6370_set_led_duty(priv, led->index, *delay_on, *delay_off); 657 + if (ret) 658 + goto out_unlock; 659 + 660 + ret = mt6370_set_led_freq(priv, led->index, *delay_on, *delay_off); 661 + if (ret) 662 + goto out_unlock; 663 + 664 + ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_PWM_MODE); 665 + 666 + out_unlock: 667 + mutex_unlock(&priv->lock); 668 + 669 + return ret; 670 + } 671 + 672 + static int mt6370_isnk_pattern_set(struct led_classdev *lcdev, struct led_pattern *pattern, u32 len, 673 + int repeat) 674 + { 675 + struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink); 676 + struct mt6370_priv *priv = led->priv; 677 + unsigned int reg_base; 678 + u8 params[P_MAX_PATTERNS / 2]; 679 + int ret; 680 + 681 + mutex_lock(&priv->lock); 682 + 683 + ret = mt6370_gen_breath_pattern(priv, pattern, len, params, sizeof(params)); 684 + if (ret) 685 + goto out_unlock; 686 + 687 + mt6370_get_breath_reg_base(priv, led->index, &reg_base); 688 + 689 + ret = regmap_raw_write(priv->regmap, reg_base, params, sizeof(params)); 690 + if (ret) 691 + goto out_unlock; 692 + 693 + ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_BREATH_MODE); 694 + 695 + out_unlock: 696 + mutex_unlock(&priv->lock); 697 + 698 + return ret; 699 + } 700 + 701 + static inline int mt6370_isnk_pattern_clear(struct led_classdev *lcdev) 702 + { 703 + struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink); 704 + struct mt6370_priv *priv = led->priv; 705 + int ret; 706 + 707 + mutex_lock(&led->priv->lock); 708 + ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_REG_MODE); 709 + mutex_unlock(&led->priv->lock); 710 + 711 + return ret; 712 + } 713 + 714 + static int mt6370_assign_multicolor_info(struct device *dev, struct mt6370_led *led, 715 + struct fwnode_handle *fwnode) 716 + { 717 + struct mt6370_priv *priv = led->priv; 718 + struct fwnode_handle *child; 719 + struct mc_subled *sub_led; 720 + u32 num_color = 0; 721 + int ret; 722 + 723 + sub_led = devm_kcalloc(dev, MC_CHANNEL_NUM, sizeof(*sub_led), GFP_KERNEL); 724 + if (!sub_led) 725 + return -ENOMEM; 726 + 727 + fwnode_for_each_child_node(fwnode, child) { 728 + u32 reg, color; 729 + 730 + ret = fwnode_property_read_u32(child, "reg", &reg); 731 + if (ret || reg > MT6370_LED_ISNK3 || priv->leds_active & BIT(reg)) { 732 + fwnode_handle_put(child); 733 + return -EINVAL; 734 + } 735 + 736 + ret = fwnode_property_read_u32(child, "color", &color); 737 + if (ret) { 738 + fwnode_handle_put(child); 739 + return dev_err_probe(dev, ret, "LED %d, no color specified\n", led->index); 740 + } 741 + 742 + priv->leds_active |= BIT(reg); 743 + sub_led[num_color].color_index = color; 744 + sub_led[num_color].channel = reg; 745 + sub_led[num_color].intensity = 0; 746 + num_color++; 747 + } 748 + 749 + if (num_color < 2) 750 + return dev_err_probe(dev, -EINVAL, 751 + "Multicolor must include 2 or more LED channels\n"); 752 + 753 + led->mc.num_colors = num_color; 754 + led->mc.subled_info = sub_led; 755 + 756 + return 0; 757 + } 758 + 759 + static int mt6370_init_led_properties(struct device *dev, struct mt6370_led *led, 760 + struct led_init_data *init_data) 761 + { 762 + struct mt6370_priv *priv = led->priv; 763 + struct led_classdev *lcdev; 764 + enum mt6370_led_ranges sel_range; 765 + u32 max_uA, max_level; 766 + int ret; 767 + 768 + if (led->index == MT6370_VIRTUAL_MULTICOLOR) { 769 + ret = mt6370_assign_multicolor_info(dev, led, init_data->fwnode); 770 + if (ret) 771 + return ret; 772 + 773 + lcdev = &led->mc.led_cdev; 774 + lcdev->brightness_set_blocking = mt6370_mc_brightness_set; 775 + lcdev->blink_set = mt6370_mc_blink_set; 776 + lcdev->pattern_set = mt6370_mc_pattern_set; 777 + lcdev->pattern_clear = mt6370_mc_pattern_clear; 778 + } else { 779 + lcdev = &led->isink; 780 + lcdev->brightness_set_blocking = mt6370_isnk_brightness_set; 781 + lcdev->blink_set = mt6370_isnk_blink_set; 782 + lcdev->pattern_set = mt6370_isnk_pattern_set; 783 + lcdev->pattern_clear = mt6370_isnk_pattern_clear; 784 + } 785 + 786 + ret = fwnode_property_read_u32(init_data->fwnode, "led-max-microamp", &max_uA); 787 + if (ret) { 788 + dev_warn(dev, "Not specified led-max-microamp, config to the minimum\n"); 789 + max_uA = 0; 790 + } 791 + 792 + if (led->index == MT6370_LED_ISNK4) 793 + sel_range = R_LED4_CURR; 794 + else 795 + sel_range = R_LED123_CURR; 796 + 797 + linear_range_get_selector_within(priv->ranges + sel_range, max_uA, &max_level); 798 + 799 + lcdev->max_brightness = max_level; 800 + 801 + led->default_state = led_init_default_state_get(init_data->fwnode); 802 + 803 + return 0; 804 + } 805 + 806 + static int mt6370_isnk_init_default_state(struct mt6370_led *led) 807 + { 808 + struct mt6370_priv *priv = led->priv; 809 + unsigned int enable, level; 810 + int ret; 811 + 812 + ret = mt6370_get_led_brightness(priv, led->index, &level); 813 + if (ret) 814 + return ret; 815 + 816 + ret = regmap_field_read(priv->fields[F_RGB_EN], &enable); 817 + if (ret) 818 + return ret; 819 + 820 + if (!(enable & MT6370_CHEN_BIT(led->index))) 821 + level = 0; 822 + 823 + switch (led->default_state) { 824 + case LEDS_DEFSTATE_ON: 825 + led->isink.brightness = led->isink.max_brightness; 826 + break; 827 + case LEDS_DEFSTATE_KEEP: 828 + led->isink.brightness = min(level, led->isink.max_brightness); 829 + break; 830 + default: 831 + led->isink.brightness = 0; 832 + break; 833 + } 834 + 835 + return mt6370_isnk_brightness_set(&led->isink, led->isink.brightness); 836 + } 837 + 838 + static int mt6370_multicolor_led_register(struct device *dev, struct mt6370_led *led, 839 + struct led_init_data *init_data) 840 + { 841 + int ret; 842 + 843 + ret = mt6370_mc_brightness_set(&led->mc.led_cdev, 0); 844 + if (ret) 845 + return dev_err_probe(dev, ret, "Couldn't set multicolor brightness\n"); 846 + 847 + ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc, init_data); 848 + if (ret) 849 + return dev_err_probe(dev, ret, "Couldn't register multicolor\n"); 850 + 851 + return 0; 852 + } 853 + 854 + static int mt6370_led_register(struct device *dev, struct mt6370_led *led, 855 + struct led_init_data *init_data) 856 + { 857 + struct mt6370_priv *priv = led->priv; 858 + int ret; 859 + 860 + if (led->index == MT6370_VIRTUAL_MULTICOLOR) 861 + return mt6370_multicolor_led_register(dev, led, init_data); 862 + 863 + /* If ISNK4 is declared, change its mode from HW auto to SW control */ 864 + if (led->index == MT6370_LED_ISNK4) { 865 + ret = regmap_field_write(priv->fields[F_CHGIND_EN], 1); 866 + if (ret) 867 + return dev_err_probe(dev, ret, "Failed to set CHRIND to SW\n"); 868 + } 869 + 870 + ret = mt6370_isnk_init_default_state(led); 871 + if (ret) 872 + return dev_err_probe(dev, ret, "Failed to init %d isnk state\n", led->index); 873 + 874 + ret = devm_led_classdev_register_ext(dev, &led->isink, init_data); 875 + if (ret) 876 + return dev_err_probe(dev, ret, "Couldn't register isink %d\n", led->index); 877 + 878 + return 0; 879 + } 880 + 881 + static int mt6370_check_vendor_info(struct mt6370_priv *priv) 882 + { 883 + unsigned int devinfo, vid; 884 + int ret; 885 + 886 + ret = regmap_read(priv->regmap, MT6370_REG_DEV_INFO, &devinfo); 887 + if (ret) 888 + return ret; 889 + 890 + vid = FIELD_GET(MT6370_VENDOR_ID_MASK, devinfo); 891 + if (vid == MT6372_VENDOR_ID || vid == MT6372C_VENDOR_ID) { 892 + priv->reg_fields = mt6372_reg_fields; 893 + priv->ranges = mt6372_led_ranges; 894 + priv->pdata = &mt6372_pdata; 895 + } else { 896 + /* Common for MT6370/71 */ 897 + priv->reg_fields = common_reg_fields; 898 + priv->ranges = common_led_ranges; 899 + priv->pdata = &common_pdata; 900 + } 901 + 902 + return 0; 903 + } 904 + 905 + static int mt6370_leds_probe(struct platform_device *pdev) 906 + { 907 + struct device *dev = &pdev->dev; 908 + struct mt6370_priv *priv; 909 + struct fwnode_handle *child; 910 + size_t count; 911 + unsigned int i = 0; 912 + int ret; 913 + 914 + count = device_get_child_node_count(dev); 915 + if (!count || count > MT6370_MAX_LEDS) 916 + return dev_err_probe(dev, -EINVAL, 917 + "No child node or node count over max LED number %zu\n", 918 + count); 919 + 920 + priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL); 921 + if (!priv) 922 + return -ENOMEM; 923 + 924 + priv->leds_count = count; 925 + mutex_init(&priv->lock); 926 + 927 + priv->regmap = dev_get_regmap(dev->parent, NULL); 928 + if (!priv->regmap) 929 + return dev_err_probe(dev, -ENODEV, "Failed to get parent regmap\n"); 930 + 931 + ret = mt6370_check_vendor_info(priv); 932 + if (ret) 933 + return dev_err_probe(dev, ret, "Failed to check vendor info\n"); 934 + 935 + ret = devm_regmap_field_bulk_alloc(dev, priv->regmap, priv->fields, priv->reg_fields, 936 + F_MAX_FIELDS); 937 + if (ret) 938 + return dev_err_probe(dev, ret, "Failed to allocate regmap field\n"); 939 + 940 + device_for_each_child_node(dev, child) { 941 + struct mt6370_led *led = priv->leds + i++; 942 + struct led_init_data init_data = { .fwnode = child }; 943 + u32 reg, color; 944 + 945 + ret = fwnode_property_read_u32(child, "reg", &reg); 946 + if (ret) { 947 + dev_err(dev, "Failed to parse reg property\n"); 948 + goto fwnode_release; 949 + } 950 + 951 + if (reg >= MT6370_MAX_LEDS) { 952 + ret = -EINVAL; 953 + dev_err(dev, "Error reg property number\n"); 954 + goto fwnode_release; 955 + } 956 + 957 + ret = fwnode_property_read_u32(child, "color", &color); 958 + if (ret) { 959 + dev_err(dev, "Failed to parse color property\n"); 960 + goto fwnode_release; 961 + } 962 + 963 + if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI) 964 + reg = MT6370_VIRTUAL_MULTICOLOR; 965 + 966 + if (priv->leds_active & BIT(reg)) { 967 + ret = -EINVAL; 968 + dev_err(dev, "Duplicate reg property\n"); 969 + goto fwnode_release; 970 + } 971 + 972 + priv->leds_active |= BIT(reg); 973 + 974 + led->index = reg; 975 + led->priv = priv; 976 + 977 + ret = mt6370_init_led_properties(dev, led, &init_data); 978 + if (ret) 979 + goto fwnode_release; 980 + 981 + ret = mt6370_led_register(dev, led, &init_data); 982 + if (ret) 983 + goto fwnode_release; 984 + } 985 + 986 + return 0; 987 + 988 + fwnode_release: 989 + fwnode_handle_put(child); 990 + return ret; 991 + } 992 + 993 + static const struct of_device_id mt6370_rgbled_device_table[] = { 994 + { .compatible = "mediatek,mt6370-indicator" }, 995 + {} 996 + }; 997 + MODULE_DEVICE_TABLE(of, mt6370_rgbled_device_table); 998 + 999 + static struct platform_driver mt6370_rgbled_driver = { 1000 + .driver = { 1001 + .name = "mt6370-indicator", 1002 + .of_match_table = mt6370_rgbled_device_table, 1003 + }, 1004 + .probe = mt6370_leds_probe, 1005 + }; 1006 + module_platform_driver(mt6370_rgbled_driver); 1007 + 1008 + MODULE_AUTHOR("Alice Chen <alice_chen@richtek.com>"); 1009 + MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); 1010 + MODULE_DESCRIPTION("MediaTek MT6370 RGB LED Driver"); 1011 + MODULE_LICENSE("GPL");
+2 -2
drivers/leds/rgb/leds-pwm-multicolor.c
··· 158 158 ret = led_pwm_mc_set(cdev, cdev->brightness); 159 159 if (ret) 160 160 return dev_err_probe(&pdev->dev, ret, 161 - "failed to set led PWM value for %s: %d", 162 - cdev->name, ret); 161 + "failed to set led PWM value for %s\n", 162 + cdev->name); 163 163 164 164 platform_set_drvdata(pdev, priv); 165 165 return 0;
+111 -41
drivers/leds/rgb/leds-qcom-lpg.c
··· 2 2 /* 3 3 * Copyright (c) 2017-2022 Linaro Ltd 4 4 * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. 5 + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. 5 6 */ 6 7 #include <linux/bits.h> 7 8 #include <linux/bitfield.h> ··· 18 17 #define LPG_SUBTYPE_REG 0x05 19 18 #define LPG_SUBTYPE_LPG 0x2 20 19 #define LPG_SUBTYPE_PWM 0xb 20 + #define LPG_SUBTYPE_HI_RES_PWM 0xc 21 21 #define LPG_SUBTYPE_LPG_LITE 0x11 22 22 #define LPG_PATTERN_CONFIG_REG 0x40 23 23 #define LPG_SIZE_CLK_REG 0x41 24 24 #define PWM_CLK_SELECT_MASK GENMASK(1, 0) 25 + #define PWM_CLK_SELECT_HI_RES_MASK GENMASK(2, 0) 26 + #define PWM_SIZE_HI_RES_MASK GENMASK(6, 4) 25 27 #define LPG_PREDIV_CLK_REG 0x42 26 28 #define PWM_FREQ_PRE_DIV_MASK GENMASK(6, 5) 27 29 #define PWM_FREQ_EXP_MASK GENMASK(2, 0) ··· 47 43 #define LPG_LUT_REG(x) (0x40 + (x) * 2) 48 44 #define RAMP_CONTROL_REG 0xc8 49 45 50 - #define LPG_RESOLUTION 512 46 + #define LPG_RESOLUTION_9BIT BIT(9) 47 + #define LPG_RESOLUTION_15BIT BIT(15) 51 48 #define LPG_MAX_M 7 49 + #define LPG_MAX_PREDIV 6 52 50 53 51 struct lpg_channel; 54 52 struct lpg_data; ··· 112 106 * @clk_sel: reference clock frequency selector 113 107 * @pre_div_sel: divider selector of the reference clock 114 108 * @pre_div_exp: exponential divider of the reference clock 109 + * @pwm_resolution_sel: pwm resolution selector 115 110 * @ramp_enabled: duty cycle is driven by iterating over lookup table 116 111 * @ramp_ping_pong: reverse through pattern, rather than wrapping to start 117 112 * @ramp_oneshot: perform only a single pass over the pattern ··· 145 138 unsigned int clk_sel; 146 139 unsigned int pre_div_sel; 147 140 unsigned int pre_div_exp; 141 + unsigned int pwm_resolution_sel; 148 142 149 143 bool ramp_enabled; 150 144 bool ramp_ping_pong; ··· 261 253 } 262 254 263 255 static const unsigned int lpg_clk_rates[] = {0, 1024, 32768, 19200000}; 256 + static const unsigned int lpg_clk_rates_hi_res[] = {0, 1024, 32768, 19200000, 76800000}; 264 257 static const unsigned int lpg_pre_divs[] = {1, 3, 5, 6}; 258 + static const unsigned int lpg_pwm_resolution[] = {9}; 259 + static const unsigned int lpg_pwm_resolution_hi_res[] = {8, 9, 10, 11, 12, 13, 14, 15}; 265 260 266 261 static int lpg_calc_freq(struct lpg_channel *chan, uint64_t period) 267 262 { 268 - unsigned int clk_sel, best_clk = 0; 263 + unsigned int i, pwm_resolution_count, best_pwm_resolution_sel = 0; 264 + const unsigned int *clk_rate_arr, *pwm_resolution_arr; 265 + unsigned int clk_sel, clk_len, best_clk = 0; 269 266 unsigned int div, best_div = 0; 270 267 unsigned int m, best_m = 0; 268 + unsigned int resolution; 271 269 unsigned int error; 272 270 unsigned int best_err = UINT_MAX; 271 + u64 max_period, min_period; 273 272 u64 best_period = 0; 274 - u64 max_period; 273 + u64 max_res; 275 274 276 275 /* 277 276 * The PWM period is determined by: ··· 287 272 * period = -------------------------- 288 273 * refclk 289 274 * 290 - * With resolution fixed at 2^9 bits, pre_div = {1, 3, 5, 6} and 275 + * Resolution = 2^9 bits for PWM or 276 + * 2^{8, 9, 10, 11, 12, 13, 14, 15} bits for high resolution PWM 277 + * pre_div = {1, 3, 5, 6} and 291 278 * M = [0..7]. 292 279 * 293 - * This allows for periods between 27uS and 384s, as the PWM framework 294 - * wants a period of equal or lower length than requested, reject 295 - * anything below 27uS. 280 + * This allows for periods between 27uS and 384s for PWM channels and periods between 281 + * 3uS and 24576s for high resolution PWMs. 282 + * The PWM framework wants a period of equal or lower length than requested, 283 + * reject anything below minimum period. 296 284 */ 297 - if (period <= (u64)NSEC_PER_SEC * LPG_RESOLUTION / 19200000) 285 + 286 + if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { 287 + clk_rate_arr = lpg_clk_rates_hi_res; 288 + clk_len = ARRAY_SIZE(lpg_clk_rates_hi_res); 289 + pwm_resolution_arr = lpg_pwm_resolution_hi_res; 290 + pwm_resolution_count = ARRAY_SIZE(lpg_pwm_resolution_hi_res); 291 + max_res = LPG_RESOLUTION_15BIT; 292 + } else { 293 + clk_rate_arr = lpg_clk_rates; 294 + clk_len = ARRAY_SIZE(lpg_clk_rates); 295 + pwm_resolution_arr = lpg_pwm_resolution; 296 + pwm_resolution_count = ARRAY_SIZE(lpg_pwm_resolution); 297 + max_res = LPG_RESOLUTION_9BIT; 298 + } 299 + 300 + min_period = (u64)NSEC_PER_SEC * 301 + div64_u64((1 << pwm_resolution_arr[0]), clk_rate_arr[clk_len - 1]); 302 + if (period <= min_period) 298 303 return -EINVAL; 299 304 300 305 /* Limit period to largest possible value, to avoid overflows */ 301 - max_period = (u64)NSEC_PER_SEC * LPG_RESOLUTION * 6 * (1 << LPG_MAX_M) / 1024; 306 + max_period = (u64)NSEC_PER_SEC * max_res * LPG_MAX_PREDIV * 307 + div64_u64((1 << LPG_MAX_M), 1024); 302 308 if (period > max_period) 303 309 period = max_period; 304 310 305 311 /* 306 - * Search for the pre_div, refclk and M by solving the rewritten formula 307 - * for each refclk and pre_div value: 312 + * Search for the pre_div, refclk, resolution and M by solving the rewritten formula 313 + * for each refclk, resolution and pre_div value: 308 314 * 309 315 * period * refclk 310 316 * M = log2 ------------------------------------- 311 317 * NSEC_PER_SEC * pre_div * resolution 312 318 */ 313 - for (clk_sel = 1; clk_sel < ARRAY_SIZE(lpg_clk_rates); clk_sel++) { 314 - u64 numerator = period * lpg_clk_rates[clk_sel]; 315 319 316 - for (div = 0; div < ARRAY_SIZE(lpg_pre_divs); div++) { 317 - u64 denominator = (u64)NSEC_PER_SEC * lpg_pre_divs[div] * LPG_RESOLUTION; 318 - u64 actual; 319 - u64 ratio; 320 + for (i = 0; i < pwm_resolution_count; i++) { 321 + resolution = 1 << pwm_resolution_arr[i]; 322 + for (clk_sel = 1; clk_sel < clk_len; clk_sel++) { 323 + u64 numerator = period * clk_rate_arr[clk_sel]; 320 324 321 - if (numerator < denominator) 322 - continue; 325 + for (div = 0; div < ARRAY_SIZE(lpg_pre_divs); div++) { 326 + u64 denominator = (u64)NSEC_PER_SEC * lpg_pre_divs[div] * 327 + resolution; 328 + u64 actual; 329 + u64 ratio; 323 330 324 - ratio = div64_u64(numerator, denominator); 325 - m = ilog2(ratio); 326 - if (m > LPG_MAX_M) 327 - m = LPG_MAX_M; 331 + if (numerator < denominator) 332 + continue; 328 333 329 - actual = DIV_ROUND_UP_ULL(denominator * (1 << m), lpg_clk_rates[clk_sel]); 334 + ratio = div64_u64(numerator, denominator); 335 + m = ilog2(ratio); 336 + if (m > LPG_MAX_M) 337 + m = LPG_MAX_M; 330 338 331 - error = period - actual; 332 - if (error < best_err) { 333 - best_err = error; 334 - 335 - best_div = div; 336 - best_m = m; 337 - best_clk = clk_sel; 338 - best_period = actual; 339 + actual = DIV_ROUND_UP_ULL(denominator * (1 << m), 340 + clk_rate_arr[clk_sel]); 341 + error = period - actual; 342 + if (error < best_err) { 343 + best_err = error; 344 + best_div = div; 345 + best_m = m; 346 + best_clk = clk_sel; 347 + best_period = actual; 348 + best_pwm_resolution_sel = i; 349 + } 339 350 } 340 351 } 341 352 } 342 - 343 353 chan->clk_sel = best_clk; 344 354 chan->pre_div_sel = best_div; 345 355 chan->pre_div_exp = best_m; 346 356 chan->period = best_period; 347 - 357 + chan->pwm_resolution_sel = best_pwm_resolution_sel; 348 358 return 0; 349 359 } 350 360 351 361 static void lpg_calc_duty(struct lpg_channel *chan, uint64_t duty) 352 362 { 353 - unsigned int max = LPG_RESOLUTION - 1; 363 + unsigned int max; 354 364 unsigned int val; 365 + unsigned int clk_rate; 355 366 356 - val = div64_u64(duty * lpg_clk_rates[chan->clk_sel], 367 + if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { 368 + max = LPG_RESOLUTION_15BIT - 1; 369 + clk_rate = lpg_clk_rates_hi_res[chan->clk_sel]; 370 + } else { 371 + max = LPG_RESOLUTION_9BIT - 1; 372 + clk_rate = lpg_clk_rates[chan->clk_sel]; 373 + } 374 + 375 + val = div64_u64(duty * clk_rate, 357 376 (u64)NSEC_PER_SEC * lpg_pre_divs[chan->pre_div_sel] * (1 << chan->pre_div_exp)); 358 377 359 378 chan->pwm_value = min(val, max); ··· 403 354 404 355 val = chan->clk_sel; 405 356 406 - /* Specify 9bit resolution, based on the subtype of the channel */ 357 + /* Specify resolution, based on the subtype of the channel */ 407 358 switch (chan->subtype) { 408 359 case LPG_SUBTYPE_LPG: 409 360 val |= GENMASK(5, 4); 410 361 break; 411 362 case LPG_SUBTYPE_PWM: 412 363 val |= BIT(2); 364 + break; 365 + case LPG_SUBTYPE_HI_RES_PWM: 366 + val |= FIELD_PREP(PWM_SIZE_HI_RES_MASK, chan->pwm_resolution_sel); 413 367 break; 414 368 case LPG_SUBTYPE_LPG_LITE: 415 369 default: ··· 722 670 triled_set(lpg, triled_mask, triled_mask); 723 671 724 672 chan = led->channels[0]; 725 - duty = div_u64(chan->pwm_value * chan->period, LPG_RESOLUTION); 673 + duty = div_u64(chan->pwm_value * chan->period, LPG_RESOLUTION_9BIT); 726 674 *delay_on = div_u64(duty, NSEC_PER_MSEC); 727 675 *delay_off = div_u64(chan->period - duty, NSEC_PER_MSEC); 728 676 ··· 1029 977 { 1030 978 struct lpg *lpg = container_of(chip, struct lpg, pwm); 1031 979 struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; 980 + unsigned int resolution; 1032 981 unsigned int pre_div; 1033 982 unsigned int refclk; 1034 983 unsigned int val; ··· 1041 988 if (ret) 1042 989 return ret; 1043 990 1044 - refclk = lpg_clk_rates[val & PWM_CLK_SELECT_MASK]; 991 + if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { 992 + refclk = lpg_clk_rates_hi_res[FIELD_GET(PWM_CLK_SELECT_HI_RES_MASK, val)]; 993 + resolution = lpg_pwm_resolution_hi_res[FIELD_GET(PWM_SIZE_HI_RES_MASK, val)]; 994 + } else { 995 + refclk = lpg_clk_rates[FIELD_GET(PWM_CLK_SELECT_MASK, val)]; 996 + resolution = 9; 997 + } 998 + 1045 999 if (refclk) { 1046 1000 ret = regmap_read(lpg->map, chan->base + LPG_PREDIV_CLK_REG, &val); 1047 1001 if (ret) ··· 1061 1001 if (ret) 1062 1002 return ret; 1063 1003 1064 - state->period = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * LPG_RESOLUTION * pre_div * (1 << m), refclk); 1004 + state->period = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * (1 << resolution) * 1005 + pre_div * (1 << m), refclk); 1065 1006 state->duty_cycle = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * pwm_value * pre_div * (1 << m), refclk); 1066 1007 } else { 1067 1008 state->period = 0; ··· 1210 1149 } 1211 1150 1212 1151 cdev->default_trigger = of_get_property(np, "linux,default-trigger", NULL); 1213 - cdev->max_brightness = LPG_RESOLUTION - 1; 1152 + cdev->max_brightness = LPG_RESOLUTION_9BIT - 1; 1214 1153 1215 1154 if (!of_property_read_string(np, "default-state", &state) && 1216 1155 !strcmp(state, "on")) ··· 1490 1429 }, 1491 1430 }; 1492 1431 1432 + static const struct lpg_data pmk8550_pwm_data = { 1433 + .num_channels = 2, 1434 + .channels = (const struct lpg_channel_data[]) { 1435 + { .base = 0xe800 }, 1436 + { .base = 0xe900 }, 1437 + }, 1438 + }; 1439 + 1493 1440 static const struct of_device_id lpg_of_table[] = { 1494 1441 { .compatible = "qcom,pm8150b-lpg", .data = &pm8150b_lpg_data }, 1495 1442 { .compatible = "qcom,pm8150l-lpg", .data = &pm8150l_lpg_data }, ··· 1508 1439 { .compatible = "qcom,pmi8994-lpg", .data = &pmi8994_lpg_data }, 1509 1440 { .compatible = "qcom,pmi8998-lpg", .data = &pmi8998_lpg_data }, 1510 1441 { .compatible = "qcom,pmc8180c-lpg", .data = &pm8150l_lpg_data }, 1442 + { .compatible = "qcom,pmk8550-pwm", .data = &pmk8550_pwm_data }, 1511 1443 {} 1512 1444 }; 1513 1445 MODULE_DEVICE_TABLE(of, lpg_of_table);
+1
drivers/leds/trigger/Kconfig
··· 83 83 config LEDS_TRIGGER_GPIO 84 84 tristate "LED GPIO Trigger" 85 85 depends on GPIOLIB || COMPILE_TEST 86 + depends on BROKEN 86 87 help 87 88 This allows LEDs to be controlled by gpio events. It's good 88 89 when using gpios as switches and triggering the needed LEDs
+1 -1
include/linux/leds.h
··· 274 274 * 275 275 * Note that if software blinking is active, simply calling 276 276 * led_cdev->brightness_set() will not stop the blinking, 277 - * use led_classdev_brightness_set() instead. 277 + * use led_set_brightness() instead. 278 278 */ 279 279 void led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, 280 280 unsigned long *delay_off);