Merge tag 'leds-next-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds
Pull LED updates from Lee Jones:
"This submission is a mix of new features, a number of fixes, some
clean-up/refactoring and a few Device Tree binding updates. The most
significant changes include new device support for the IS31FL3236A and
QNAP's status LEDs, a number of fixes for the Qualcomm flash, LP55xx,
MAX77705 and QNAP MCU drivers, along with some generic LED consumer
binding documentation and unification.
New Support & Features:
- Add support for the IS31FL3236A LED driver, including an optional
22kHz PWM frequency setting.
- Add support for the red and green status LEDs on QNAP MCU devices.
Improvements & Fixes:
- Fix an issue in the MAX77705 driver where an error value was
assigned but not returned.
- Correctly update the torch current clamp setting in the Qualcomm
flash LED driver to prevent unexpected clamping.
- Add a separate register map for the PMI8998 to the Qualcomm flash
LED driver to handle its unique register layout.
- Fix the state numbering for the USB LED in the QNAP MCU driver to
avoid command conflicts.
- Correct the memory programming address calculation for non-paged
devices like the LP5562 in the LP55xx driver.
- Add Device Tree support to the generic led_get() function, allowing
LEDs to be looked up by name.
Cleanups & Refactoring:
- Fix a spelling mistake in the Kconfig help text for the BD2606MVV
driver.
- Use devm_mutex_init() in the IS31FL319x driver to simplify code.
Device Tree Bindings Updates:
- Drop the redundant '-db' suffix from the IS31FL319x binding.
- Introduce generic LED consumer bindings for referencing LEDs via
phandles.
- Unify the 'leds' property across several bindings to use the new
generic consumer binding.
- Convert the AMS AS3645A LED controller binding to the DT schema
format"
* tag 'leds-next-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds:
dt-bindings: leds: as3645: Convert to DT schema
leds: led-class: Add Device Tree support to led_get()
dt-bindings: leds: Unify 'leds' property
dt-bindings: leds: Add generic LED consumer documentation
leds: is31fl319x: Use devm_mutex_init()
leds: leds-lp55xx: Use correct address for memory programming
dt-bindings: leds: issi,is31fl319x: Drop 'db' suffix duplicating dtschema
leds: qnap-mcu: Add support for the red and green status LEDs
leds: qnap-mcu: Fix state numbering for USB LED
leds: flash: leds-qcom-flash: Add a separate register map for PMI8998
leds: flash: leds-qcom-flash: Update torch current clamp setting
leds: max77705: Function return instead of variable assignment
leds: Kconfig: Fix spelling mistake "limitiation" -> "limitation"
leds: leds-is31fl32xx: Add support for is31fl3236a
This commit is contained in:
@@ -1,85 +0,0 @@
|
||||
Analog devices AS3645A device tree bindings
|
||||
|
||||
The AS3645A flash LED controller can drive two LEDs, one high current
|
||||
flash LED and one indicator LED. The high current flash LED can be
|
||||
used in torch mode as well.
|
||||
|
||||
Ranges below noted as [a, b] are closed ranges between a and b, i.e. a
|
||||
and b are included in the range.
|
||||
|
||||
Please also see common.txt in the same directory.
|
||||
|
||||
|
||||
Required properties
|
||||
===================
|
||||
|
||||
compatible : Must be "ams,as3645a".
|
||||
reg : The I2C address of the device. Typically 0x30.
|
||||
#address-cells : 1
|
||||
#size-cells : 0
|
||||
|
||||
|
||||
Required properties of the flash child node (0)
|
||||
===============================================
|
||||
|
||||
reg: 0
|
||||
flash-timeout-us: Flash timeout in microseconds. The value must be in
|
||||
the range [100000, 850000] and divisible by 50000.
|
||||
flash-max-microamp: Maximum flash current in microamperes. Has to be
|
||||
in the range between [200000, 500000] and
|
||||
divisible by 20000.
|
||||
led-max-microamp: Maximum torch (assist) current in microamperes. The
|
||||
value must be in the range between [20000, 160000] and
|
||||
divisible by 20000.
|
||||
ams,input-max-microamp: Maximum flash controller input current. The
|
||||
value must be in the range [1250000, 2000000]
|
||||
and divisible by 50000.
|
||||
|
||||
|
||||
Optional properties of the flash child node
|
||||
===========================================
|
||||
|
||||
function : See Documentation/devicetree/bindings/leds/common.txt.
|
||||
color : See Documentation/devicetree/bindings/leds/common.txt.
|
||||
label : See Documentation/devicetree/bindings/leds/common.txt (deprecated).
|
||||
|
||||
|
||||
Required properties of the indicator child node (1)
|
||||
===================================================
|
||||
|
||||
reg: 1
|
||||
led-max-microamp: Maximum indicator current. The allowed values are
|
||||
2500, 5000, 7500 and 10000.
|
||||
|
||||
Optional properties of the indicator child node
|
||||
===============================================
|
||||
|
||||
function : See Documentation/devicetree/bindings/leds/common.txt.
|
||||
color : See Documentation/devicetree/bindings/leds/common.txt.
|
||||
label : See Documentation/devicetree/bindings/leds/common.txt (deprecated).
|
||||
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
as3645a@30 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x30>;
|
||||
compatible = "ams,as3645a";
|
||||
led@0 {
|
||||
reg = <0x0>;
|
||||
flash-timeout-us = <150000>;
|
||||
flash-max-microamp = <320000>;
|
||||
led-max-microamp = <60000>;
|
||||
ams,input-max-microamp = <1750000>;
|
||||
function = LED_FUNCTION_FLASH;
|
||||
};
|
||||
led@1 {
|
||||
reg = <0x1>;
|
||||
led-max-microamp = <10000>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
};
|
||||
};
|
||||
130
Documentation/devicetree/bindings/leds/ams,as3645a.yaml
Normal file
130
Documentation/devicetree/bindings/leds/ams,as3645a.yaml
Normal file
@@ -0,0 +1,130 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/ams,as3645a.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AS3645A LED Controller
|
||||
|
||||
maintainers:
|
||||
- Sakari Ailus <sakari.ailus@iki.fi>
|
||||
|
||||
description:
|
||||
The AS3645A flash LED controller can drive two LEDs, one
|
||||
high current flash LED and one indicator LED. The high
|
||||
current flash LED can be used in torch mode as well.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ams,as3645a
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
led@0:
|
||||
description: led0 describes the 'flash' feature
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 0
|
||||
|
||||
flash-timeout-us:
|
||||
minimum: 100000
|
||||
maximum: 850000
|
||||
multipleOf: 50000
|
||||
|
||||
flash-max-microamp:
|
||||
minimum: 200000
|
||||
maximum: 500000
|
||||
multipleOf: 20000
|
||||
|
||||
led-max-microamp:
|
||||
minimum: 20000
|
||||
maximum: 160000
|
||||
multipleOf: 20000
|
||||
description:
|
||||
Maximum current when in torch (assist) mode.
|
||||
|
||||
ams,input-max-microamp:
|
||||
minimum: 1250000
|
||||
maximum: 2000000
|
||||
multipleOf: 50000
|
||||
|
||||
required:
|
||||
- reg
|
||||
- flash-timeout-us
|
||||
- flash-max-microamp
|
||||
- led-max-microamp
|
||||
- ams,input-max-microamp
|
||||
|
||||
led@1:
|
||||
description: led1 describes the 'indicator' feature
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 1
|
||||
|
||||
led-max-microamp:
|
||||
enum:
|
||||
- 2500
|
||||
- 5000
|
||||
- 7500
|
||||
- 10000
|
||||
description:
|
||||
Maximum indicator current.
|
||||
|
||||
required:
|
||||
- reg
|
||||
- led-max-microamp
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#size-cells"
|
||||
- "#address-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
i2c{
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led-controller@30 {
|
||||
compatible = "ams,as3645a";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x30>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
flash-timeout-us = <150000>;
|
||||
flash-max-microamp = <320000>;
|
||||
led-max-microamp = <60000>;
|
||||
ams,input-max-microamp = <1750000>;
|
||||
function = LED_FUNCTION_FLASH;
|
||||
};
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
led-max-microamp = <10000>;
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -23,11 +23,7 @@ properties:
|
||||
compatible:
|
||||
const: led-backlight
|
||||
|
||||
leds:
|
||||
description: A list of LED nodes
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
maxItems: 1
|
||||
leds: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
@@ -42,7 +42,6 @@ properties:
|
||||
description: GPIO attached to the SDB pin.
|
||||
|
||||
audio-gain-db:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
default: 0
|
||||
description: Audio gain selection for external analog modulation input.
|
||||
enum: [0, 3, 6, 9, 12, 15, 18, 21]
|
||||
|
||||
67
Documentation/devicetree/bindings/leds/leds-consumer.yaml
Normal file
67
Documentation/devicetree/bindings/leds/leds-consumer.yaml
Normal file
@@ -0,0 +1,67 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/leds-consumer.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Common leds consumer
|
||||
|
||||
maintainers:
|
||||
- Aleksandrs Vinarskis <alex@vinarskis.com>
|
||||
|
||||
description:
|
||||
Some LED defined in DT are required by other DT consumers, for example
|
||||
v4l2 subnode may require privacy or flash LED. Unlike trigger-source
|
||||
approach which is typically used as 'soft' binding, referencing LED
|
||||
devices by phandle makes things simpler when 'hard' binding is desired.
|
||||
|
||||
Document LED properties that its consumers may define.
|
||||
|
||||
select: true
|
||||
|
||||
properties:
|
||||
leds:
|
||||
oneOf:
|
||||
- type: object
|
||||
- $ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
description:
|
||||
A list of LED device(s) required by a particular consumer.
|
||||
items:
|
||||
maxItems: 1
|
||||
|
||||
led-names:
|
||||
description:
|
||||
A list of device name(s). Used to map LED devices to their respective
|
||||
functions, when consumer requires more than one LED.
|
||||
|
||||
additionalProperties: true
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
privacy_led: privacy-led {
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
default-state = "off";
|
||||
function = LED_FUNCTION_INDICATOR;
|
||||
gpios = <&tlmm 110 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
v4l2_node: camera@36 {
|
||||
reg = <0x36>;
|
||||
|
||||
leds = <&privacy_led>;
|
||||
led-names = "privacy";
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@@ -17,10 +17,7 @@ properties:
|
||||
compatible:
|
||||
const: leds-group-multicolor
|
||||
|
||||
leds:
|
||||
description:
|
||||
An aray of monochromatic leds
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
leds: true
|
||||
|
||||
required:
|
||||
- leds
|
||||
|
||||
@@ -17,6 +17,14 @@ properties:
|
||||
An array of phandles, each referring to a flash LED, a sub-node of the LED
|
||||
driver device node.
|
||||
|
||||
leds:
|
||||
minItems: 1
|
||||
maxItems: 1
|
||||
|
||||
led-names:
|
||||
enum:
|
||||
- privacy
|
||||
|
||||
lens-focus:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
|
||||
@@ -674,7 +674,7 @@ config LEDS_BD2606MVV
|
||||
help
|
||||
This option enables support for BD2606MVV LED driver chips
|
||||
accessed via the I2C bus. It supports setting brightness, with
|
||||
the limitiation that there are groups of two channels sharing
|
||||
the limitation that there are groups of two channels sharing
|
||||
a brightness setting, but not the on/off setting.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
@@ -114,36 +114,55 @@ enum {
|
||||
REG_THERM_THRSH1,
|
||||
REG_THERM_THRSH2,
|
||||
REG_THERM_THRSH3,
|
||||
REG_TORCH_CLAMP,
|
||||
REG_MAX_COUNT,
|
||||
};
|
||||
|
||||
static const struct reg_field mvflash_3ch_pmi8998_regs[REG_MAX_COUNT] = {
|
||||
[REG_STATUS1] = REG_FIELD(0x08, 0, 5),
|
||||
[REG_STATUS2] = REG_FIELD(0x09, 0, 7),
|
||||
[REG_STATUS3] = REG_FIELD(0x0a, 0, 7),
|
||||
[REG_CHAN_TIMER] = REG_FIELD_ID(0x40, 0, 7, 3, 1),
|
||||
[REG_ITARGET] = REG_FIELD_ID(0x43, 0, 6, 3, 1),
|
||||
[REG_MODULE_EN] = REG_FIELD(0x46, 7, 7),
|
||||
[REG_IRESOLUTION] = REG_FIELD(0x47, 0, 5),
|
||||
[REG_CHAN_STROBE] = REG_FIELD_ID(0x49, 0, 2, 3, 1),
|
||||
[REG_CHAN_EN] = REG_FIELD(0x4c, 0, 2),
|
||||
[REG_THERM_THRSH1] = REG_FIELD(0x56, 0, 2),
|
||||
[REG_THERM_THRSH2] = REG_FIELD(0x57, 0, 2),
|
||||
[REG_THERM_THRSH3] = REG_FIELD(0x58, 0, 2),
|
||||
[REG_TORCH_CLAMP] = REG_FIELD(0xea, 0, 6),
|
||||
};
|
||||
|
||||
static const struct reg_field mvflash_3ch_regs[REG_MAX_COUNT] = {
|
||||
REG_FIELD(0x08, 0, 7), /* status1 */
|
||||
REG_FIELD(0x09, 0, 7), /* status2 */
|
||||
REG_FIELD(0x0a, 0, 7), /* status3 */
|
||||
REG_FIELD_ID(0x40, 0, 7, 3, 1), /* chan_timer */
|
||||
REG_FIELD_ID(0x43, 0, 6, 3, 1), /* itarget */
|
||||
REG_FIELD(0x46, 7, 7), /* module_en */
|
||||
REG_FIELD(0x47, 0, 5), /* iresolution */
|
||||
REG_FIELD_ID(0x49, 0, 2, 3, 1), /* chan_strobe */
|
||||
REG_FIELD(0x4c, 0, 2), /* chan_en */
|
||||
REG_FIELD(0x56, 0, 2), /* therm_thrsh1 */
|
||||
REG_FIELD(0x57, 0, 2), /* therm_thrsh2 */
|
||||
REG_FIELD(0x58, 0, 2), /* therm_thrsh3 */
|
||||
[REG_STATUS1] = REG_FIELD(0x08, 0, 7),
|
||||
[REG_STATUS2] = REG_FIELD(0x09, 0, 7),
|
||||
[REG_STATUS3] = REG_FIELD(0x0a, 0, 7),
|
||||
[REG_CHAN_TIMER] = REG_FIELD_ID(0x40, 0, 7, 3, 1),
|
||||
[REG_ITARGET] = REG_FIELD_ID(0x43, 0, 6, 3, 1),
|
||||
[REG_MODULE_EN] = REG_FIELD(0x46, 7, 7),
|
||||
[REG_IRESOLUTION] = REG_FIELD(0x47, 0, 5),
|
||||
[REG_CHAN_STROBE] = REG_FIELD_ID(0x49, 0, 2, 3, 1),
|
||||
[REG_CHAN_EN] = REG_FIELD(0x4c, 0, 2),
|
||||
[REG_THERM_THRSH1] = REG_FIELD(0x56, 0, 2),
|
||||
[REG_THERM_THRSH2] = REG_FIELD(0x57, 0, 2),
|
||||
[REG_THERM_THRSH3] = REG_FIELD(0x58, 0, 2),
|
||||
[REG_TORCH_CLAMP] = REG_FIELD(0xec, 0, 6),
|
||||
};
|
||||
|
||||
static const struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = {
|
||||
REG_FIELD(0x06, 0, 7), /* status1 */
|
||||
REG_FIELD(0x07, 0, 6), /* status2 */
|
||||
REG_FIELD(0x09, 0, 7), /* status3 */
|
||||
REG_FIELD_ID(0x3e, 0, 7, 4, 1), /* chan_timer */
|
||||
REG_FIELD_ID(0x42, 0, 6, 4, 1), /* itarget */
|
||||
REG_FIELD(0x46, 7, 7), /* module_en */
|
||||
REG_FIELD(0x49, 0, 3), /* iresolution */
|
||||
REG_FIELD_ID(0x4a, 0, 6, 4, 1), /* chan_strobe */
|
||||
REG_FIELD(0x4e, 0, 3), /* chan_en */
|
||||
REG_FIELD(0x7a, 0, 2), /* therm_thrsh1 */
|
||||
REG_FIELD(0x78, 0, 2), /* therm_thrsh2 */
|
||||
[REG_STATUS1] = REG_FIELD(0x06, 0, 7),
|
||||
[REG_STATUS2] = REG_FIELD(0x07, 0, 6),
|
||||
[REG_STATUS3] = REG_FIELD(0x09, 0, 7),
|
||||
[REG_CHAN_TIMER] = REG_FIELD_ID(0x3e, 0, 7, 4, 1),
|
||||
[REG_ITARGET] = REG_FIELD_ID(0x42, 0, 6, 4, 1),
|
||||
[REG_MODULE_EN] = REG_FIELD(0x46, 7, 7),
|
||||
[REG_IRESOLUTION] = REG_FIELD(0x49, 0, 3),
|
||||
[REG_CHAN_STROBE] = REG_FIELD_ID(0x4a, 0, 6, 4, 1),
|
||||
[REG_CHAN_EN] = REG_FIELD(0x4e, 0, 3),
|
||||
[REG_THERM_THRSH1] = REG_FIELD(0x7a, 0, 2),
|
||||
[REG_THERM_THRSH2] = REG_FIELD(0x78, 0, 2),
|
||||
[REG_TORCH_CLAMP] = REG_FIELD(0xed, 0, 6),
|
||||
};
|
||||
|
||||
struct qcom_flash_data {
|
||||
@@ -156,6 +175,7 @@ struct qcom_flash_data {
|
||||
u8 max_channels;
|
||||
u8 chan_en_bits;
|
||||
u8 revision;
|
||||
u8 torch_clamp;
|
||||
};
|
||||
|
||||
struct qcom_flash_led {
|
||||
@@ -702,6 +722,7 @@ static int qcom_flash_register_led_device(struct device *dev,
|
||||
u32 current_ua, timeout_us;
|
||||
u32 channels[4];
|
||||
int i, rc, count;
|
||||
u8 torch_clamp;
|
||||
|
||||
count = fwnode_property_count_u32(node, "led-sources");
|
||||
if (count <= 0) {
|
||||
@@ -751,6 +772,12 @@ static int qcom_flash_register_led_device(struct device *dev,
|
||||
current_ua = min_t(u32, current_ua, TORCH_CURRENT_MAX_UA * led->chan_count);
|
||||
led->max_torch_current_ma = current_ua / UA_PER_MA;
|
||||
|
||||
torch_clamp = (current_ua / led->chan_count) / TORCH_IRES_UA;
|
||||
if (torch_clamp != 0)
|
||||
torch_clamp--;
|
||||
|
||||
flash_data->torch_clamp = max_t(u8, flash_data->torch_clamp, torch_clamp);
|
||||
|
||||
if (fwnode_property_present(node, "flash-max-microamp")) {
|
||||
flash->led_cdev.flags |= LED_DEV_CAP_FLASH;
|
||||
|
||||
@@ -851,13 +878,20 @@ static int qcom_flash_led_probe(struct platform_device *pdev)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (val == FLASH_SUBTYPE_3CH_PM8150_VAL || val == FLASH_SUBTYPE_3CH_PMI8998_VAL) {
|
||||
if (val == FLASH_SUBTYPE_3CH_PM8150_VAL) {
|
||||
flash_data->hw_type = QCOM_MVFLASH_3CH;
|
||||
flash_data->max_channels = 3;
|
||||
regs = devm_kmemdup(dev, mvflash_3ch_regs, sizeof(mvflash_3ch_regs),
|
||||
GFP_KERNEL);
|
||||
if (!regs)
|
||||
return -ENOMEM;
|
||||
} else if (val == FLASH_SUBTYPE_3CH_PMI8998_VAL) {
|
||||
flash_data->hw_type = QCOM_MVFLASH_3CH;
|
||||
flash_data->max_channels = 3;
|
||||
regs = devm_kmemdup(dev, mvflash_3ch_pmi8998_regs,
|
||||
sizeof(mvflash_3ch_pmi8998_regs), GFP_KERNEL);
|
||||
if (!regs)
|
||||
return -ENOMEM;
|
||||
} else if (val == FLASH_SUBTYPE_4CH_VAL) {
|
||||
flash_data->hw_type = QCOM_MVFLASH_4CH;
|
||||
flash_data->max_channels = 4;
|
||||
@@ -917,8 +951,7 @@ static int qcom_flash_led_probe(struct platform_device *pdev)
|
||||
flash_data->leds_count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
return regmap_field_write(flash_data->r_fields[REG_TORCH_CLAMP], flash_data->torch_clamp);
|
||||
release:
|
||||
while (flash_data->v4l2_flash[flash_data->leds_count] && flash_data->leds_count)
|
||||
v4l2_flash_release(flash_data->v4l2_flash[flash_data->leds_count--]);
|
||||
|
||||
@@ -252,15 +252,23 @@ static const struct class leds_class = {
|
||||
* of_led_get() - request a LED device via the LED framework
|
||||
* @np: device node to get the LED device from
|
||||
* @index: the index of the LED
|
||||
* @name: the name of the LED used to map it to its function, if present
|
||||
*
|
||||
* Returns the LED device parsed from the phandle specified in the "leds"
|
||||
* property of a device tree node or a negative error-code on failure.
|
||||
*/
|
||||
static struct led_classdev *of_led_get(struct device_node *np, int index)
|
||||
static struct led_classdev *of_led_get(struct device_node *np, int index,
|
||||
const char *name)
|
||||
{
|
||||
struct device *led_dev;
|
||||
struct device_node *led_node;
|
||||
|
||||
/*
|
||||
* For named LEDs, first look up the name in the "led-names" property.
|
||||
* If it cannot be found, then of_parse_phandle() will propagate the error.
|
||||
*/
|
||||
if (name)
|
||||
index = of_property_match_string(np, "led-names", name);
|
||||
led_node = of_parse_phandle(np, "leds", index);
|
||||
if (!led_node)
|
||||
return ERR_PTR(-ENOENT);
|
||||
@@ -324,7 +332,7 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev,
|
||||
if (!dev)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
led = of_led_get(dev->of_node, index);
|
||||
led = of_led_get(dev->of_node, index, NULL);
|
||||
if (IS_ERR(led))
|
||||
return led;
|
||||
|
||||
@@ -342,9 +350,14 @@ EXPORT_SYMBOL_GPL(devm_of_led_get);
|
||||
struct led_classdev *led_get(struct device *dev, char *con_id)
|
||||
{
|
||||
struct led_lookup_data *lookup;
|
||||
struct led_classdev *led_cdev;
|
||||
const char *provider = NULL;
|
||||
struct device *led_dev;
|
||||
|
||||
led_cdev = of_led_get(dev->of_node, -1, con_id);
|
||||
if (!IS_ERR(led_cdev) || PTR_ERR(led_cdev) != -ENOENT)
|
||||
return led_cdev;
|
||||
|
||||
mutex_lock(&leds_lookup_lock);
|
||||
list_for_each_entry(lookup, &leds_lookup_list, list) {
|
||||
if (!strcmp(lookup->dev_id, dev_name(dev)) &&
|
||||
|
||||
@@ -483,11 +483,6 @@ static inline int is31fl3196_db_to_gain(u32 dezibel)
|
||||
return dezibel / IS31FL3196_AUDIO_GAIN_DB_STEP;
|
||||
}
|
||||
|
||||
static void is31f1319x_mutex_destroy(void *lock)
|
||||
{
|
||||
mutex_destroy(lock);
|
||||
}
|
||||
|
||||
static int is31fl319x_probe(struct i2c_client *client)
|
||||
{
|
||||
struct is31fl319x_chip *is31;
|
||||
@@ -503,8 +498,7 @@ static int is31fl319x_probe(struct i2c_client *client)
|
||||
if (!is31)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&is31->lock);
|
||||
err = devm_add_action_or_reset(dev, is31f1319x_mutex_destroy, &is31->lock);
|
||||
err = devm_mutex_init(dev, &is31->lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#define IS31FL3216_CONFIG_SSD_ENABLE BIT(7)
|
||||
#define IS31FL3216_CONFIG_SSD_DISABLE 0
|
||||
|
||||
#define IS31FL32XX_PWM_FREQUENCY_22KHZ 0x01
|
||||
|
||||
struct is31fl32xx_priv;
|
||||
struct is31fl32xx_led_data {
|
||||
struct led_classdev cdev;
|
||||
@@ -53,6 +55,7 @@ struct is31fl32xx_priv {
|
||||
* @pwm_update_reg : address of PWM Update register
|
||||
* @global_control_reg : address of Global Control register (optional)
|
||||
* @reset_reg : address of Reset register (optional)
|
||||
* @output_frequency_setting_reg: address of output frequency register (optional)
|
||||
* @pwm_register_base : address of first PWM register
|
||||
* @pwm_registers_reversed: : true if PWM registers count down instead of up
|
||||
* @led_control_register_base : address of first LED control register (optional)
|
||||
@@ -76,6 +79,7 @@ struct is31fl32xx_chipdef {
|
||||
u8 pwm_update_reg;
|
||||
u8 global_control_reg;
|
||||
u8 reset_reg;
|
||||
u8 output_frequency_setting_reg;
|
||||
u8 pwm_register_base;
|
||||
bool pwm_registers_reversed;
|
||||
u8 led_control_register_base;
|
||||
@@ -90,6 +94,19 @@ static const struct is31fl32xx_chipdef is31fl3236_cdef = {
|
||||
.pwm_update_reg = 0x25,
|
||||
.global_control_reg = 0x4a,
|
||||
.reset_reg = 0x4f,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x01,
|
||||
.led_control_register_base = 0x26,
|
||||
.enable_bits_per_led_control_register = 1,
|
||||
};
|
||||
|
||||
static const struct is31fl32xx_chipdef is31fl3236a_cdef = {
|
||||
.channels = 36,
|
||||
.shutdown_reg = 0x00,
|
||||
.pwm_update_reg = 0x25,
|
||||
.global_control_reg = 0x4a,
|
||||
.reset_reg = 0x4f,
|
||||
.output_frequency_setting_reg = 0x4b,
|
||||
.pwm_register_base = 0x01,
|
||||
.led_control_register_base = 0x26,
|
||||
.enable_bits_per_led_control_register = 1,
|
||||
@@ -101,6 +118,7 @@ static const struct is31fl32xx_chipdef is31fl3235_cdef = {
|
||||
.pwm_update_reg = 0x25,
|
||||
.global_control_reg = 0x4a,
|
||||
.reset_reg = 0x4f,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x05,
|
||||
.led_control_register_base = 0x2a,
|
||||
.enable_bits_per_led_control_register = 1,
|
||||
@@ -112,6 +130,7 @@ static const struct is31fl32xx_chipdef is31fl3218_cdef = {
|
||||
.pwm_update_reg = 0x16,
|
||||
.global_control_reg = IS31FL32XX_REG_NONE,
|
||||
.reset_reg = 0x17,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x01,
|
||||
.led_control_register_base = 0x13,
|
||||
.enable_bits_per_led_control_register = 6,
|
||||
@@ -126,6 +145,7 @@ static const struct is31fl32xx_chipdef is31fl3216_cdef = {
|
||||
.pwm_update_reg = 0xB0,
|
||||
.global_control_reg = IS31FL32XX_REG_NONE,
|
||||
.reset_reg = IS31FL32XX_REG_NONE,
|
||||
.output_frequency_setting_reg = IS31FL32XX_REG_NONE,
|
||||
.pwm_register_base = 0x10,
|
||||
.pwm_registers_reversed = true,
|
||||
.led_control_register_base = 0x01,
|
||||
@@ -363,8 +383,21 @@ static struct is31fl32xx_led_data *is31fl32xx_find_led_data(
|
||||
static int is31fl32xx_parse_dt(struct device *dev,
|
||||
struct is31fl32xx_priv *priv)
|
||||
{
|
||||
const struct is31fl32xx_chipdef *cdef = priv->cdef;
|
||||
int ret = 0;
|
||||
|
||||
if ((cdef->output_frequency_setting_reg != IS31FL32XX_REG_NONE) &&
|
||||
of_property_read_bool(dev_of_node(dev), "issi,22khz-pwm")) {
|
||||
|
||||
ret = is31fl32xx_write(priv, cdef->output_frequency_setting_reg,
|
||||
IS31FL32XX_PWM_FREQUENCY_22KHZ);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to write output PWM frequency register\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
|
||||
struct led_init_data init_data = {};
|
||||
struct is31fl32xx_led_data *led_data =
|
||||
@@ -404,12 +437,13 @@ static int is31fl32xx_parse_dt(struct device *dev,
|
||||
}
|
||||
|
||||
static const struct of_device_id of_is31fl32xx_match[] = {
|
||||
{ .compatible = "issi,is31fl3236", .data = &is31fl3236_cdef, },
|
||||
{ .compatible = "issi,is31fl3235", .data = &is31fl3235_cdef, },
|
||||
{ .compatible = "issi,is31fl3218", .data = &is31fl3218_cdef, },
|
||||
{ .compatible = "si-en,sn3218", .data = &is31fl3218_cdef, },
|
||||
{ .compatible = "issi,is31fl3216", .data = &is31fl3216_cdef, },
|
||||
{ .compatible = "si-en,sn3216", .data = &is31fl3216_cdef, },
|
||||
{ .compatible = "issi,is31fl3236", .data = &is31fl3236_cdef, },
|
||||
{ .compatible = "issi,is31fl3236a", .data = &is31fl3236a_cdef, },
|
||||
{ .compatible = "issi,is31fl3235", .data = &is31fl3235_cdef, },
|
||||
{ .compatible = "issi,is31fl3218", .data = &is31fl3218_cdef, },
|
||||
{ .compatible = "si-en,sn3218", .data = &is31fl3218_cdef, },
|
||||
{ .compatible = "issi,is31fl3216", .data = &is31fl3216_cdef, },
|
||||
{ .compatible = "si-en,sn3216", .data = &is31fl3216_cdef, },
|
||||
{},
|
||||
};
|
||||
|
||||
@@ -466,6 +500,7 @@ static void is31fl32xx_remove(struct i2c_client *client)
|
||||
*/
|
||||
static const struct i2c_device_id is31fl32xx_id[] = {
|
||||
{ "is31fl3236" },
|
||||
{ "is31fl3236a" },
|
||||
{ "is31fl3235" },
|
||||
{ "is31fl3218" },
|
||||
{ "sn3218" },
|
||||
|
||||
@@ -212,7 +212,7 @@ int lp55xx_update_program_memory(struct lp55xx_chip *chip,
|
||||
* For LED chip that support page, PAGE is already set in load_engine.
|
||||
*/
|
||||
if (!cfg->pages_per_engine)
|
||||
start_addr += LP55xx_BYTES_PER_PAGE * idx;
|
||||
start_addr += LP55xx_BYTES_PER_PAGE * (idx - 1);
|
||||
|
||||
for (page = 0; page < program_length / LP55xx_BYTES_PER_PAGE; page++) {
|
||||
/* Write to the next page each 32 bytes (if supported) */
|
||||
|
||||
@@ -180,7 +180,7 @@ static int max77705_add_led(struct device *dev, struct regmap *regmap, struct fw
|
||||
|
||||
ret = fwnode_property_read_u32(np, "reg", ®);
|
||||
if (ret || reg >= MAX77705_LED_NUM_LEDS)
|
||||
ret = -EINVAL;
|
||||
return -EINVAL;
|
||||
|
||||
info = devm_kcalloc(dev, num_channels, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
|
||||
@@ -104,9 +104,9 @@ static int qnap_mcu_register_err_led(struct device *dev, struct qnap_mcu *mcu, i
|
||||
}
|
||||
|
||||
enum qnap_mcu_usb_led_mode {
|
||||
QNAP_MCU_USB_LED_ON = 1,
|
||||
QNAP_MCU_USB_LED_OFF = 3,
|
||||
QNAP_MCU_USB_LED_BLINK = 2,
|
||||
QNAP_MCU_USB_LED_ON = 0,
|
||||
QNAP_MCU_USB_LED_OFF = 2,
|
||||
QNAP_MCU_USB_LED_BLINK = 1,
|
||||
};
|
||||
|
||||
struct qnap_mcu_usb_led {
|
||||
@@ -137,7 +137,7 @@ static int qnap_mcu_usb_led_set(struct led_classdev *led_cdev,
|
||||
* Byte 3 is shared between the usb led target on/off/blink
|
||||
* and also the buzzer control (in the input driver)
|
||||
*/
|
||||
cmd[2] = 'D' + usb_led->mode;
|
||||
cmd[2] = 'E' + usb_led->mode;
|
||||
|
||||
return qnap_mcu_exec_with_ack(usb_led->mcu, cmd, sizeof(cmd));
|
||||
}
|
||||
@@ -161,7 +161,7 @@ static int qnap_mcu_usb_led_blink_set(struct led_classdev *led_cdev,
|
||||
* Byte 3 is shared between the USB LED target on/off/blink
|
||||
* and also the buzzer control (in the input driver)
|
||||
*/
|
||||
cmd[2] = 'D' + usb_led->mode;
|
||||
cmd[2] = 'E' + usb_led->mode;
|
||||
|
||||
return qnap_mcu_exec_with_ack(usb_led->mcu, cmd, sizeof(cmd));
|
||||
}
|
||||
@@ -190,6 +190,166 @@ static int qnap_mcu_register_usb_led(struct device *dev, struct qnap_mcu *mcu)
|
||||
return qnap_mcu_usb_led_set(&usb_led->cdev, 0);
|
||||
}
|
||||
|
||||
enum qnap_mcu_status_led_mode {
|
||||
QNAP_MCU_STATUS_LED_OFF = 0,
|
||||
QNAP_MCU_STATUS_LED_ON = 1,
|
||||
QNAP_MCU_STATUS_LED_BLINK_FAST = 2, /* 500ms / 500ms */
|
||||
QNAP_MCU_STATUS_LED_BLINK_SLOW = 3, /* 1s / 1s */
|
||||
};
|
||||
|
||||
struct qnap_mcu_status_led {
|
||||
struct led_classdev cdev;
|
||||
struct qnap_mcu_status_led *red;
|
||||
u8 mode;
|
||||
};
|
||||
|
||||
struct qnap_mcu_status {
|
||||
struct qnap_mcu *mcu;
|
||||
struct qnap_mcu_status_led red;
|
||||
struct qnap_mcu_status_led green;
|
||||
};
|
||||
|
||||
static inline struct qnap_mcu_status_led *cdev_to_qnap_mcu_status_led(struct led_classdev *led_cdev)
|
||||
{
|
||||
return container_of(led_cdev, struct qnap_mcu_status_led, cdev);
|
||||
}
|
||||
|
||||
static inline struct qnap_mcu_status *statusled_to_qnap_mcu_status(struct qnap_mcu_status_led *led)
|
||||
{
|
||||
return container_of(led->red, struct qnap_mcu_status, red);
|
||||
}
|
||||
|
||||
static u8 qnap_mcu_status_led_encode(struct qnap_mcu_status *status)
|
||||
{
|
||||
if (status->red.mode == QNAP_MCU_STATUS_LED_OFF) {
|
||||
switch (status->green.mode) {
|
||||
case QNAP_MCU_STATUS_LED_OFF:
|
||||
return '9';
|
||||
case QNAP_MCU_STATUS_LED_ON:
|
||||
return '6';
|
||||
case QNAP_MCU_STATUS_LED_BLINK_FAST:
|
||||
return '5';
|
||||
case QNAP_MCU_STATUS_LED_BLINK_SLOW:
|
||||
return 'A';
|
||||
}
|
||||
} else if (status->green.mode == QNAP_MCU_STATUS_LED_OFF) {
|
||||
switch (status->red.mode) {
|
||||
case QNAP_MCU_STATUS_LED_OFF:
|
||||
return '9';
|
||||
case QNAP_MCU_STATUS_LED_ON:
|
||||
return '7';
|
||||
case QNAP_MCU_STATUS_LED_BLINK_FAST:
|
||||
return '4';
|
||||
case QNAP_MCU_STATUS_LED_BLINK_SLOW:
|
||||
return 'B';
|
||||
}
|
||||
} else if (status->green.mode == QNAP_MCU_STATUS_LED_ON &&
|
||||
status->red.mode == QNAP_MCU_STATUS_LED_ON) {
|
||||
return 'D';
|
||||
} else if (status->green.mode == QNAP_MCU_STATUS_LED_BLINK_SLOW &&
|
||||
status->red.mode == QNAP_MCU_STATUS_LED_BLINK_SLOW) {
|
||||
return 'C';
|
||||
}
|
||||
|
||||
/*
|
||||
* Here both LEDs are on in some fashion, either both blinking fast,
|
||||
* or in different speeds, so default to fast blinking for both.
|
||||
*/
|
||||
return '8';
|
||||
}
|
||||
|
||||
static int qnap_mcu_status_led_update(struct qnap_mcu *mcu,
|
||||
struct qnap_mcu_status *status)
|
||||
{
|
||||
u8 cmd[] = { '@', 'C', 0 };
|
||||
|
||||
cmd[2] = qnap_mcu_status_led_encode(status);
|
||||
|
||||
return qnap_mcu_exec_with_ack(mcu, cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static int qnap_mcu_status_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct qnap_mcu_status_led *status_led = cdev_to_qnap_mcu_status_led(led_cdev);
|
||||
struct qnap_mcu_status *base = statusled_to_qnap_mcu_status(status_led);
|
||||
|
||||
/* Don't disturb a possible set blink-mode if LED stays on */
|
||||
if (brightness != 0 && status_led->mode >= QNAP_MCU_STATUS_LED_BLINK_FAST)
|
||||
return 0;
|
||||
|
||||
status_led->mode = brightness ? QNAP_MCU_STATUS_LED_ON :
|
||||
QNAP_MCU_STATUS_LED_OFF;
|
||||
|
||||
return qnap_mcu_status_led_update(base->mcu, base);
|
||||
}
|
||||
|
||||
static int qnap_mcu_status_led_blink_set(struct led_classdev *led_cdev,
|
||||
unsigned long *delay_on,
|
||||
unsigned long *delay_off)
|
||||
{
|
||||
struct qnap_mcu_status_led *status_led = cdev_to_qnap_mcu_status_led(led_cdev);
|
||||
struct qnap_mcu_status *base = statusled_to_qnap_mcu_status(status_led);
|
||||
|
||||
if (status_led->mode == QNAP_MCU_STATUS_LED_OFF)
|
||||
return 0;
|
||||
|
||||
if (*delay_on <= 500) {
|
||||
*delay_on = 500;
|
||||
*delay_off = 500;
|
||||
status_led->mode = QNAP_MCU_STATUS_LED_BLINK_FAST;
|
||||
} else {
|
||||
*delay_on = 1000;
|
||||
*delay_off = 1000;
|
||||
status_led->mode = QNAP_MCU_STATUS_LED_BLINK_SLOW;
|
||||
}
|
||||
|
||||
return qnap_mcu_status_led_update(base->mcu, base);
|
||||
}
|
||||
|
||||
static int qnap_mcu_register_status_leds(struct device *dev, struct qnap_mcu *mcu)
|
||||
{
|
||||
struct qnap_mcu_status *status;
|
||||
int ret;
|
||||
|
||||
status = devm_kzalloc(dev, sizeof(*status), GFP_KERNEL);
|
||||
if (!status)
|
||||
return -ENOMEM;
|
||||
|
||||
status->mcu = mcu;
|
||||
|
||||
/*
|
||||
* point to the red led, so that statusled_to_qnap_mcu_status
|
||||
* can resolve the main status struct containing both leds
|
||||
*/
|
||||
status->red.red = &status->red;
|
||||
status->green.red = &status->red;
|
||||
|
||||
status->red.mode = QNAP_MCU_STATUS_LED_OFF;
|
||||
status->red.cdev.name = "red:status";
|
||||
status->red.cdev.brightness_set_blocking = qnap_mcu_status_led_set;
|
||||
status->red.cdev.blink_set = qnap_mcu_status_led_blink_set;
|
||||
status->red.cdev.brightness = 0;
|
||||
status->red.cdev.max_brightness = 1;
|
||||
|
||||
status->green.mode = QNAP_MCU_STATUS_LED_OFF;
|
||||
status->green.cdev.name = "green:status";
|
||||
status->green.cdev.brightness_set_blocking = qnap_mcu_status_led_set;
|
||||
status->green.cdev.blink_set = qnap_mcu_status_led_blink_set;
|
||||
status->green.cdev.brightness = 0;
|
||||
status->green.cdev.max_brightness = 1;
|
||||
|
||||
ret = devm_led_classdev_register(dev, &status->red.cdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_led_classdev_register(dev, &status->green.cdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return qnap_mcu_status_led_update(status->mcu, status);
|
||||
}
|
||||
|
||||
static int qnap_mcu_leds_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qnap_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
|
||||
@@ -210,6 +370,11 @@ static int qnap_mcu_leds_probe(struct platform_device *pdev)
|
||||
"failed to register USB LED\n");
|
||||
}
|
||||
|
||||
ret = qnap_mcu_register_status_leds(&pdev->dev, mcu);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"failed to register status LEDs\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user