Commit d835ff6c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'leds-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds

Pull LED updates from Pavel Machek:
 "Nothing too exciting here, just some fixes"

* tag 'leds-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds:
  leds: pca9532: Assign gpio base dynamically
  leds: trigger: pattern: Switch to using the new API kobj_to_dev()
  leds: LEDS_BLINK_LGM should depend on X86
  leds: lgm: Fix spelling mistake "prepate" -> "prepare"
  MAINTAINERS: Remove Dan Murphy's bouncing email
  leds-lm3642: convert comma to semicolon
  leds: rt4505: Add support for Richtek RT4505 flash LED controller
  leds: rt4505: Add DT binding document for Richtek RT4505
  leds: Kconfig: LEDS_CLASS is usually selected.
  leds: lgm: Improve Kconfig help
  leds: lgm: fix gpiolib dependency
parents a7efd197 23a70045
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/leds-rt4505.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Richtek RT4505 Single Channel LED Driver
maintainers:
- ChiYuan Huang <cy_huang@richtek.com>
description: |
The RT4505 is a flash LED driver that can support up to 375mA and 1.5A for
torch and flash mode, respectively.
The data sheet can be found at:
https://www.richtek.com/assets/product_file/RT4505/DS4505-02.pdf
properties:
compatible:
const: richtek,rt4505
reg:
description: I2C slave address of the controller.
maxItems: 1
led:
type: object
$ref: common.yaml#
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/leds/common.h>
i2c0 {
#address-cells = <1>;
#size-cells = <0>;
led-controller@63 {
compatible = "richtek,rt4505";
reg = <0x63>;
rt4505_flash: led {
function = LED_FUNCTION_FLASH;
color = <LED_COLOR_ID_WHITE>;
led-max-microamp = <375000>;
flash-max-microamp = <1500000>;
flash-max-timeout-us = <800000>;
};
};
};
......@@ -18,7 +18,7 @@ config LEDS_CLASS
tristate "LED Class Support"
help
This option enables the LED sysfs class in /sys/class/leds. You'll
need this to do anything useful with LEDs. If unsure, say N.
need this to do anything useful with LEDs. If unsure, say Y.
config LEDS_CLASS_FLASH
tristate "LED Flash Class Support"
......@@ -928,13 +928,12 @@ config LEDS_ACER_A500
This option enables support for the Power Button LED of
Acer Iconia Tab A500.
source "drivers/leds/blink/Kconfig"
comment "Flash and Torch LED drivers"
source "drivers/leds/flash/Kconfig"
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"
comment "LED Blink"
source "drivers/leds/blink/Kconfig"
endif # NEW_LEDS
......@@ -110,4 +110,4 @@ obj-$(CONFIG_LEDS_CLASS_FLASH) += flash/
obj-$(CONFIG_LEDS_TRIGGERS) += trigger/
# LED Blink
obj-$(CONFIG_LEDS_BLINK) += blink/
obj-y += blink/
menuconfig LEDS_BLINK
bool "LED Blink support"
depends on LEDS_CLASS
config LEDS_LGM
tristate "LED support for LGM SoC series"
depends on X86 || COMPILE_TEST
depends on GPIOLIB && LEDS_CLASS && MFD_SYSCON && OF
help
This option enables blink support for the leds class.
If unsure, say Y.
This option enables support for LEDs connected to GPIO lines on
Lightning Mountain (LGM) SoC. Lightning Mountain is a AnyWAN
gateway-on-a-chip SoC to be shipped on mid and high end home
gateways and routers.
if LEDS_BLINK
These LEDs are driven by a Serial Shift Output (SSO) controller.
The driver supports hardware blinking and the LEDs can be configured
to be triggered by software/CPU or by hardware.
config LEDS_BLINK_LGM
tristate "LED support for Intel LGM SoC series"
depends on LEDS_CLASS
depends on MFD_SYSCON
depends on OF
help
Parallel to serial conversion, which is also called SSO controller,
can drive external shift register for LED outputs.
This enables LED support for Serial Shift Output controller(SSO).
endif # LEDS_BLINK
Say 'Y' here if you are working on LGM SoC based platform. Otherwise,
say 'N'. To compile this driver as a module, choose M here: the module
will be called leds-lgm-sso.
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_LEDS_BLINK_LGM) += leds-lgm-sso.o
obj-$(CONFIG_LEDS_LGM) += leds-lgm-sso.o
......@@ -793,7 +793,7 @@ static int intel_sso_led_probe(struct platform_device *pdev)
ret = clk_prepare_enable(priv->gclk);
if (ret) {
dev_err(dev, "Failed to prepate/enable sso gate clock!\n");
dev_err(dev, "Failed to prepare/enable sso gate clock!\n");
return ret;
}
......
......@@ -2,6 +2,17 @@
if LEDS_CLASS_FLASH
config LEDS_RT4505
tristate "LED support for RT4505 flashlight controller"
depends on I2C && OF
depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
select REGMAP_I2C
help
This option enables support for the RT4505 flash LED controller.
RT4505 includes torch and flash functions with programmable current.
And it's commonly used to compensate the illuminance for the camera
inside the mobile product like as phones or tablets.
config LEDS_RT8515
tristate "LED support for Richtek RT8515 flash/torch LED"
depends on GPIOLIB
......
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o
obj-$(CONFIG_LEDS_RT8515) += leds-rt8515.o
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/bitops.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/led-class-flash.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <media/v4l2-flash-led-class.h>
#define RT4505_REG_RESET 0x0
#define RT4505_REG_CONFIG 0x8
#define RT4505_REG_ILED 0x9
#define RT4505_REG_ENABLE 0xA
#define RT4505_REG_FLAGS 0xB
#define RT4505_RESET_MASK BIT(7)
#define RT4505_FLASHTO_MASK GENMASK(2, 0)
#define RT4505_ITORCH_MASK GENMASK(7, 5)
#define RT4505_ITORCH_SHIFT 5
#define RT4505_IFLASH_MASK GENMASK(4, 0)
#define RT4505_ENABLE_MASK GENMASK(5, 0)
#define RT4505_TORCH_SET (BIT(0) | BIT(4))
#define RT4505_FLASH_SET (BIT(0) | BIT(1) | BIT(2) | BIT(4))
#define RT4505_EXT_FLASH_SET (BIT(0) | BIT(1) | BIT(4) | BIT(5))
#define RT4505_FLASH_GET (BIT(0) | BIT(1) | BIT(4))
#define RT4505_OVP_MASK BIT(3)
#define RT4505_SHORT_MASK BIT(2)
#define RT4505_OTP_MASK BIT(1)
#define RT4505_TIMEOUT_MASK BIT(0)
#define RT4505_ITORCH_MINUA 46000
#define RT4505_ITORCH_MAXUA 375000
#define RT4505_ITORCH_STPUA 47000
#define RT4505_IFLASH_MINUA 93750
#define RT4505_IFLASH_MAXUA 1500000
#define RT4505_IFLASH_STPUA 93750
#define RT4505_FLASHTO_MINUS 100000
#define RT4505_FLASHTO_MAXUS 800000
#define RT4505_FLASHTO_STPUS 100000
struct rt4505_priv {
struct device *dev;
struct regmap *regmap;
struct mutex lock;
struct led_classdev_flash flash;
struct v4l2_flash *v4l2_flash;
};
static int rt4505_torch_brightness_set(struct led_classdev *lcdev,
enum led_brightness level)
{
struct rt4505_priv *priv =
container_of(lcdev, struct rt4505_priv, flash.led_cdev);
u32 val = 0;
int ret;
mutex_lock(&priv->lock);
if (level != LED_OFF) {
ret = regmap_update_bits(priv->regmap,
RT4505_REG_ILED, RT4505_ITORCH_MASK,
(level - 1) << RT4505_ITORCH_SHIFT);
if (ret)
goto unlock;
val = RT4505_TORCH_SET;
}
ret = regmap_update_bits(priv->regmap, RT4505_REG_ENABLE,
RT4505_ENABLE_MASK, val);
unlock:
mutex_unlock(&priv->lock);
return ret;
}
static enum led_brightness rt4505_torch_brightness_get(
struct led_classdev *lcdev)
{
struct rt4505_priv *priv =
container_of(lcdev, struct rt4505_priv, flash.led_cdev);
u32 val;
int ret;
mutex_lock(&priv->lock);
ret = regmap_read(priv->regmap, RT4505_REG_ENABLE, &val);
if (ret) {
dev_err(lcdev->dev, "Failed to get LED enable\n");
ret = LED_OFF;
goto unlock;
}
if ((val & RT4505_ENABLE_MASK) != RT4505_TORCH_SET) {
ret = LED_OFF;
goto unlock;
}
ret = regmap_read(priv->regmap, RT4505_REG_ILED, &val);
if (ret) {
dev_err(lcdev->dev, "Failed to get LED brightness\n");
ret = LED_OFF;
goto unlock;
}
ret = ((val & RT4505_ITORCH_MASK) >> RT4505_ITORCH_SHIFT) + 1;
unlock:
mutex_unlock(&priv->lock);
return ret;
}
static int rt4505_flash_brightness_set(struct led_classdev_flash *fled_cdev,
u32 brightness)
{
struct rt4505_priv *priv =
container_of(fled_cdev, struct rt4505_priv, flash);
struct led_flash_setting *s = &fled_cdev->brightness;
u32 val = (brightness - s->min) / s->step;
int ret;
mutex_lock(&priv->lock);
ret = regmap_update_bits(priv->regmap, RT4505_REG_ILED,
RT4505_IFLASH_MASK, val);
mutex_unlock(&priv->lock);
return ret;
}
static int rt4505_flash_strobe_set(struct led_classdev_flash *fled_cdev,
bool state)
{
struct rt4505_priv *priv =
container_of(fled_cdev, struct rt4505_priv, flash);
u32 val = state ? RT4505_FLASH_SET : 0;
int ret;
mutex_lock(&priv->lock);
ret = regmap_update_bits(priv->regmap, RT4505_REG_ENABLE,
RT4505_ENABLE_MASK, val);
mutex_unlock(&priv->lock);
return ret;
}
static int rt4505_flash_strobe_get(struct led_classdev_flash *fled_cdev,
bool *state)
{
struct rt4505_priv *priv =
container_of(fled_cdev, struct rt4505_priv, flash);
u32 val;
int ret;
mutex_lock(&priv->lock);
ret = regmap_read(priv->regmap, RT4505_REG_ENABLE, &val);
if (ret)
goto unlock;
*state = (val & RT4505_FLASH_GET) == RT4505_FLASH_GET;
unlock:
mutex_unlock(&priv->lock);
return ret;
}
static int rt4505_flash_timeout_set(struct led_classdev_flash *fled_cdev,
u32 timeout)
{
struct rt4505_priv *priv =
container_of(fled_cdev, struct rt4505_priv, flash);
struct led_flash_setting *s = &fled_cdev->timeout;
u32 val = (timeout - s->min) / s->step;
int ret;
mutex_lock(&priv->lock);
ret = regmap_update_bits(priv->regmap, RT4505_REG_CONFIG,
RT4505_FLASHTO_MASK, val);
mutex_unlock(&priv->lock);
return ret;
}
static int rt4505_fault_get(struct led_classdev_flash *fled_cdev, u32 *fault)
{
struct rt4505_priv *priv =
container_of(fled_cdev, struct rt4505_priv, flash);
u32 val, led_faults = 0;
int ret;
ret = regmap_read(priv->regmap, RT4505_REG_FLAGS, &val);
if (ret)
return ret;
if (val & RT4505_OVP_MASK)
led_faults |= LED_FAULT_OVER_VOLTAGE;
if (val & RT4505_SHORT_MASK)
led_faults |= LED_FAULT_SHORT_CIRCUIT;
if (val & RT4505_OTP_MASK)
led_faults |= LED_FAULT_OVER_TEMPERATURE;
if (val & RT4505_TIMEOUT_MASK)
led_faults |= LED_FAULT_TIMEOUT;
*fault = led_faults;
return 0;
}
static const struct led_flash_ops rt4505_flash_ops = {
.flash_brightness_set = rt4505_flash_brightness_set,
.strobe_set = rt4505_flash_strobe_set,
.strobe_get = rt4505_flash_strobe_get,
.timeout_set = rt4505_flash_timeout_set,
.fault_get = rt4505_fault_get,
};
static bool rt4505_is_accessible_reg(struct device *dev, unsigned int reg)
{
if (reg == RT4505_REG_RESET ||
(reg >= RT4505_REG_CONFIG && reg <= RT4505_REG_FLAGS))
return true;
return false;
}
static const struct regmap_config rt4505_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = RT4505_REG_FLAGS,
.readable_reg = rt4505_is_accessible_reg,
.writeable_reg = rt4505_is_accessible_reg,
};
#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
static int rt4505_flash_external_strobe_set(struct v4l2_flash *v4l2_flash,
bool enable)
{
struct led_classdev_flash *flash = v4l2_flash->fled_cdev;
struct rt4505_priv *priv =
container_of(flash, struct rt4505_priv, flash);
u32 val = enable ? RT4505_EXT_FLASH_SET : 0;
int ret;
mutex_lock(&priv->lock);
ret = regmap_update_bits(priv->regmap, RT4505_REG_ENABLE,
RT4505_ENABLE_MASK, val);
mutex_unlock(&priv->lock);
return ret;
}
static const struct v4l2_flash_ops v4l2_flash_ops = {
.external_strobe_set = rt4505_flash_external_strobe_set,
};
static void rt4505_init_v4l2_config(struct rt4505_priv *priv,
struct v4l2_flash_config *config)
{
struct led_classdev_flash *flash = &priv->flash;
struct led_classdev *lcdev = &flash->led_cdev;
struct led_flash_setting *s;
strscpy(config->dev_name, lcdev->dev->kobj.name,
sizeof(config->dev_name));
s = &config->intensity;
s->min = RT4505_ITORCH_MINUA;
s->step = RT4505_ITORCH_STPUA;
s->max = s->val = s->min + (lcdev->max_brightness - 1) * s->step;
config->flash_faults = LED_FAULT_OVER_VOLTAGE |
LED_FAULT_SHORT_CIRCUIT |
LED_FAULT_LED_OVER_TEMPERATURE |
LED_FAULT_TIMEOUT;
config->has_external_strobe = 1;
}
#else
static const struct v4l2_flash_ops v4l2_flash_ops;
static void rt4505_init_v4l2_config(struct rt4505_priv *priv,
struct v4l2_flash_config *config)
{
}
#endif
static void rt4505_init_flash_properties(struct rt4505_priv *priv,
struct fwnode_handle *child)
{
struct led_classdev_flash *flash = &priv->flash;
struct led_classdev *lcdev = &flash->led_cdev;
struct led_flash_setting *s;
u32 val;
int ret;
ret = fwnode_property_read_u32(child, "led-max-microamp", &val);
if (ret) {
dev_warn(priv->dev, "led-max-microamp DT property missing\n");
val = RT4505_ITORCH_MINUA;
} else
val = clamp_val(val, RT4505_ITORCH_MINUA, RT4505_ITORCH_MAXUA);
lcdev->max_brightness =
(val - RT4505_ITORCH_MINUA) / RT4505_ITORCH_STPUA + 1;
lcdev->brightness_set_blocking = rt4505_torch_brightness_set;
lcdev->brightness_get = rt4505_torch_brightness_get;
lcdev->flags |= LED_DEV_CAP_FLASH;
ret = fwnode_property_read_u32(child, "flash-max-microamp", &val);
if (ret) {
dev_warn(priv->dev, "flash-max-microamp DT property missing\n");
val = RT4505_IFLASH_MINUA;
} else
val = clamp_val(val, RT4505_IFLASH_MINUA, RT4505_IFLASH_MAXUA);
s = &flash->brightness;
s->min = RT4505_IFLASH_MINUA;
s->step = RT4505_IFLASH_STPUA;
s->max = s->val = val;
ret = fwnode_property_read_u32(child, "flash-max-timeout-us", &val);
if (ret) {
dev_warn(priv->dev,
"flash-max-timeout-us DT property missing\n");
val = RT4505_FLASHTO_MINUS;
} else
val = clamp_val(val, RT4505_FLASHTO_MINUS,
RT4505_FLASHTO_MAXUS);
s = &flash->timeout;
s->min = RT4505_FLASHTO_MINUS;
s->step = RT4505_FLASHTO_STPUS;
s->max = s->val = val;
flash->ops = &rt4505_flash_ops;
}
static int rt4505_probe(struct i2c_client *client)
{
struct rt4505_priv *priv;
struct fwnode_handle *child;
struct led_init_data init_data = {};
struct v4l2_flash_config v4l2_config = {};
int ret;
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = &client->dev;
mutex_init(&priv->lock);
priv->regmap = devm_regmap_init_i2c(client, &rt4505_regmap_config);
if (IS_ERR(priv->regmap)) {
dev_err(priv->dev, "Failed to allocate register map\n");
return PTR_ERR(priv->regmap);
}
ret = regmap_write(priv->regmap, RT4505_REG_RESET, RT4505_RESET_MASK);
if (ret) {
dev_err(priv->dev, "Failed to reset registers\n");
return ret;
}
child = fwnode_get_next_available_child_node(client->dev.fwnode, NULL);
if (!child) {
dev_err(priv->dev, "Failed to get child node\n");
return -EINVAL;
}
init_data.fwnode = child;
rt4505_init_flash_properties(priv, child);
ret = devm_led_classdev_flash_register_ext(priv->dev, &priv->flash,
&init_data);
if (ret) {
dev_err(priv->dev, "Failed to register flash\n");
return ret;
}
rt4505_init_v4l2_config(priv, &v4l2_config);
priv->v4l2_flash = v4l2_flash_init(priv->dev, init_data.fwnode,
&priv->flash, &v4l2_flash_ops,
&v4l2_config);
if (IS_ERR(priv->v4l2_flash)) {
dev_err(priv->dev, "Failed to register v4l2 flash\n");
return PTR_ERR(priv->v4l2_flash);
}
i2c_set_clientdata(client, priv);
return 0;
}
static int rt4505_remove(struct i2c_client *client)
{
struct rt4505_priv *priv = i2c_get_clientdata(client);
v4l2_flash_release(priv->v4l2_flash);
return 0;
}
static void rt4505_shutdown(struct i2c_client *client)
{
struct rt4505_priv *priv = i2c_get_clientdata(client);
/* Reset registers to make sure all off before shutdown */
regmap_write(priv->regmap, RT4505_REG_RESET, RT4505_RESET_MASK);
}
static const struct of_device_id __maybe_unused rt4505_leds_match[] = {
{ .compatible = "richtek,rt4505", },
{}
};
MODULE_DEVICE_TABLE(of, rt4505_leds_match);
static struct i2c_driver rt4505_driver = {
.driver = {
.name = "rt4505",
.of_match_table = of_match_ptr(rt4505_leds_match),
},
.probe_new = rt4505_probe,
.remove = rt4505_remove,
.shutdown = rt4505_shutdown,
};
module_i2c_driver(rt4505_driver);
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
MODULE_LICENSE("GPL v2");
......@@ -339,7 +339,7 @@ static int lm3642_probe(struct i2c_client *client,
chip->cdev_flash.max_brightness = 16;
chip->cdev_flash.brightness_set_blocking = lm3642_strobe_brightness_set;
chip->cdev_flash.default_trigger = "flash";
chip->cdev_flash.groups = lm3642_flash_groups,
chip->cdev_flash.groups = lm3642_flash_groups;
err = led_classdev_register(&client->dev, &chip->cdev_flash);
if (err < 0) {
dev_err(chip->dev, "failed to register flash\n");
......@@ -351,7 +351,7 @@ static int lm3642_probe(struct i2c_client *client,
chip->cdev_torch.max_brightness = 8;
chip->cdev_torch.brightness_set_blocking = lm3642_torch_brightness_set;
chip->cdev_torch.default_trigger = "torch";
chip->cdev_torch.groups = lm3642_torch_groups,
chip->cdev_torch.groups = lm3642_torch_groups;
err = led_classdev_register(&client->dev, &chip->cdev_torch);
if (err < 0) {
dev_err(chip->dev, "failed to register torch\n");
......
......@@ -480,6 +480,8 @@ pca9532_of_populate_pdata(struct device *dev, struct device_node *np)
if (!pdata)
return ERR_PTR(-ENOMEM);
pdata->gpio_base = -1;
of_property_read_u8_array(np, "nxp,pwm", &pdata->pwm[0],
ARRAY_SIZE(pdata->pwm));
of_property_read_u8_array(np, "nxp,psc", &pdata->psc[0],
......
......@@ -333,7 +333,7 @@ static DEVICE_ATTR_RW(hw_pattern);
static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
struct attribute *attr, int index)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct device *dev = kobj_to_dev(kobj);
struct led_classdev *led_cdev = dev_get_drvdata(dev);
if (attr == &dev_attr_repeat.attr || attr == &dev_attr_pattern.attr)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment