Commit 545ae665 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull LED updates from Pavel Machek:

 - New driver for TI TPS6105X

 - Add managed API to get a LED from a device driver

 - Misc fixes and updates

* tag 'leds-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds: (22 commits)
  leds: lm3692x: Disable chip on brightness 0
  leds: lm3692x: Split out lm3692x_leds_disable
  leds: lm3692x: Move lm3692x_init and rename to lm3692x_leds_enable
  leds: lm3692x: Make sure we don't exceed the maximum LED current
  dt: bindings: lm3692x: Add led-max-microamp property
  leds: lm3692x: Allow to configure over voltage protection
  dt: bindings: lm3692x: Add ti,ovp-microvolt property
  leds: populate the device's of_node
  leds: Add managed API to get a LED from a device driver
  leds: Add of_led_get() and led_put()
  leds: lm3532: add pointer to documentation and fix typo
  leds: lm3532: use extended registration so that LED can be used for backlight
  leds: lm3642: remove warnings for bad strtol, cleanup gotos
  leds: rb532: cleanup whitespace
  ledtrig-pattern: fix email address quoting in MODULE_AUTHOR()
  dt-bindings: mfd: update TI tps6105x chip bindings
  leds: tps6105x: add driver for MFD chip LED mode
  led: max77650: add of_match table
  leds: bd2802: Convert to use GPIO descriptors
  leds: pca963x: Fix open-drain initialization
  ...
parents 15f8e733 260718b3
...@@ -18,6 +18,10 @@ Required properties: ...@@ -18,6 +18,10 @@ Required properties:
Optional properties: Optional properties:
- enable-gpios : gpio pin to enable/disable the device. - enable-gpios : gpio pin to enable/disable the device.
- vled-supply : LED supply - vled-supply : LED supply
- ti,ovp-microvolt: Overvoltage protection in
micro-volt, can be 17000000, 21000000, 25000000 or
29000000. If ti,ovp-microvolt is not specified it
defaults to 29000000.
Required child properties: Required child properties:
- reg : 0 - Will enable all LED sync paths - reg : 0 - Will enable all LED sync paths
...@@ -31,6 +35,8 @@ Optional child properties: ...@@ -31,6 +35,8 @@ Optional child properties:
- label : see Documentation/devicetree/bindings/leds/common.txt (deprecated) - label : see Documentation/devicetree/bindings/leds/common.txt (deprecated)
- linux,default-trigger : - linux,default-trigger :
see Documentation/devicetree/bindings/leds/common.txt see Documentation/devicetree/bindings/leds/common.txt
- led-max-microamp :
see Documentation/devicetree/bindings/leds/common.txt
Example: Example:
...@@ -44,12 +50,14 @@ led-controller@36 { ...@@ -44,12 +50,14 @@ led-controller@36 {
enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
vled-supply = <&vbatt>; vled-supply = <&vbatt>;
ti,ovp-microvolt = <29000000>;
led@0 { led@0 {
reg = <0>; reg = <0>;
function = LED_FUNCTION_BACKLIGHT; function = LED_FUNCTION_BACKLIGHT;
color = <LED_COLOR_ID_WHITE>; color = <LED_COLOR_ID_WHITE>;
linux,default-trigger = "backlight"; linux,default-trigger = "backlight";
led-max-microamp = <20000>;
}; };
} }
......
...@@ -7,11 +7,56 @@ Required properties: ...@@ -7,11 +7,56 @@ Required properties:
- compatible: "ti,tps61050" or "ti,tps61052" - compatible: "ti,tps61050" or "ti,tps61052"
- reg: Specifies the I2C slave address - reg: Specifies the I2C slave address
Example: Optional sub-node:
This subnode selects the chip's operational mode.
There can be at most one single available subnode.
- regulator: presence of this sub-node puts the chip in regulator mode.
see ../regulator/regulator.yaml
- led: presence of this sub-node puts the chip in led mode.
Optional properties:
- function : see ../leds/common.txt
- color : see ../leds/common.txt
- label : see ../leds/common.txt
(deprecated)
Example (GPIO operation only):
i2c0 {
tps61052@33 {
compatible = "ti,tps61052";
reg = <0x33>;
};
};
Example (GPIO + regulator operation):
i2c0 { i2c0 {
tps61052@33 { tps61052@33 {
compatible = "ti,tps61052"; compatible = "ti,tps61052";
reg = <0x33>; reg = <0x33>;
regulator {
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
regulator-always-on;
};
};
};
Example (GPIO + led operation):
#include <dt-bindings/leds/common.h>
i2c0 {
tps61052@33 {
compatible = "ti,tps61052";
reg = <0x33>;
led {
color = <LED_COLOR_ID_WHITE>;
};
}; };
}; };
...@@ -836,6 +836,16 @@ config LEDS_LM36274 ...@@ -836,6 +836,16 @@ config LEDS_LM36274
Say Y to enable the LM36274 LED driver for TI LMU devices. Say Y to enable the LM36274 LED driver for TI LMU devices.
This supports the LED device LM36274. This supports the LED device LM36274.
config LEDS_TPS6105X
tristate "LED support for TI TPS6105X"
depends on LEDS_CLASS
depends on TPS6105X
default y if TPS6105X
help
This driver supports TPS61050/TPS61052 LED chips.
It is a single boost converter primarily for white LEDs and
audio amplifiers.
comment "LED Triggers" comment "LED Triggers"
source "drivers/leds/trigger/Kconfig" source "drivers/leds/trigger/Kconfig"
......
...@@ -85,6 +85,7 @@ obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o ...@@ -85,6 +85,7 @@ obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o
obj-$(CONFIG_LEDS_TI_LMU_COMMON) += leds-ti-lmu-common.o obj-$(CONFIG_LEDS_TI_LMU_COMMON) += leds-ti-lmu-common.o
obj-$(CONFIG_LEDS_LM3697) += leds-lm3697.o obj-$(CONFIG_LEDS_LM3697) += leds-lm3697.o
obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o
obj-$(CONFIG_LEDS_TPS6105X) += leds-tps6105x.o
# LED SPI Drivers # LED SPI Drivers
obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <uapi/linux/uleds.h> #include <uapi/linux/uleds.h>
#include <linux/of.h>
#include "leds.h" #include "leds.h"
static struct class *leds_class; static struct class *leds_class;
...@@ -214,6 +215,98 @@ static int led_resume(struct device *dev) ...@@ -214,6 +215,98 @@ static int led_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume); static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
/**
* 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
*
* 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.
*/
struct led_classdev *of_led_get(struct device_node *np, int index)
{
struct device *led_dev;
struct led_classdev *led_cdev;
struct device_node *led_node;
led_node = of_parse_phandle(np, "leds", index);
if (!led_node)
return ERR_PTR(-ENOENT);
led_dev = class_find_device_by_of_node(leds_class, led_node);
of_node_put(led_node);
if (!led_dev)
return ERR_PTR(-EPROBE_DEFER);
led_cdev = dev_get_drvdata(led_dev);
if (!try_module_get(led_cdev->dev->parent->driver->owner))
return ERR_PTR(-ENODEV);
return led_cdev;
}
EXPORT_SYMBOL_GPL(of_led_get);
/**
* led_put() - release a LED device
* @led_cdev: LED device
*/
void led_put(struct led_classdev *led_cdev)
{
module_put(led_cdev->dev->parent->driver->owner);
}
EXPORT_SYMBOL_GPL(led_put);
static void devm_led_release(struct device *dev, void *res)
{
struct led_classdev **p = res;
led_put(*p);
}
/**
* devm_of_led_get - Resource-managed request of a LED device
* @dev: LED consumer
* @index: index of the LED to obtain in the consumer
*
* The device node of the device is parse to find the request LED device.
* The LED device returned from this function is automatically released
* on driver detach.
*
* @return a pointer to a LED device or ERR_PTR(errno) on failure.
*/
struct led_classdev *__must_check devm_of_led_get(struct device *dev,
int index)
{
struct led_classdev *led;
struct led_classdev **dr;
if (!dev)
return ERR_PTR(-EINVAL);
/* Not using device tree? */
if (!IS_ENABLED(CONFIG_OF) || !dev->of_node)
return ERR_PTR(-ENOTSUPP);
led = of_led_get(dev->of_node, index);
if (IS_ERR(led))
return led;
dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *),
GFP_KERNEL);
if (!dr) {
led_put(led);
return ERR_PTR(-ENOMEM);
}
*dr = led;
devres_add(dev, dr);
return led;
}
EXPORT_SYMBOL_GPL(devm_of_led_get);
static int led_classdev_next_name(const char *init_name, char *name, static int led_classdev_next_name(const char *init_name, char *name,
size_t len) size_t len)
{ {
...@@ -276,8 +369,10 @@ int led_classdev_register_ext(struct device *parent, ...@@ -276,8 +369,10 @@ int led_classdev_register_ext(struct device *parent,
mutex_unlock(&led_cdev->led_access); mutex_unlock(&led_cdev->led_access);
return PTR_ERR(led_cdev->dev); return PTR_ERR(led_cdev->dev);
} }
if (init_data && init_data->fwnode) if (init_data && init_data->fwnode) {
led_cdev->dev->fwnode = init_data->fwnode; led_cdev->dev->fwnode = init_data->fwnode;
led_cdev->dev->of_node = to_of_node(init_data->fwnode);
}
if (ret) if (ret)
dev_warn(parent, "Led %s renamed to %s due to name collision", dev_warn(parent, "Led %s renamed to %s due to name collision",
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/gpio.h> #include <linux/gpio/consumer.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/leds-bd2802.h> #include <linux/leds-bd2802.h>
...@@ -67,6 +67,7 @@ struct led_state { ...@@ -67,6 +67,7 @@ struct led_state {
struct bd2802_led { struct bd2802_led {
struct bd2802_led_platform_data *pdata; struct bd2802_led_platform_data *pdata;
struct i2c_client *client; struct i2c_client *client;
struct gpio_desc *reset;
struct rw_semaphore rwsem; struct rw_semaphore rwsem;
struct led_state led[2]; struct led_state led[2];
...@@ -200,7 +201,7 @@ static void bd2802_update_state(struct bd2802_led *led, enum led_ids id, ...@@ -200,7 +201,7 @@ static void bd2802_update_state(struct bd2802_led *led, enum led_ids id,
return; return;
if (bd2802_is_all_off(led) && !led->adf_on) { if (bd2802_is_all_off(led) && !led->adf_on) {
gpio_set_value(led->pdata->reset_gpio, 0); gpiod_set_value(led->reset, 1);
return; return;
} }
...@@ -226,7 +227,7 @@ static void bd2802_configure(struct bd2802_led *led) ...@@ -226,7 +227,7 @@ static void bd2802_configure(struct bd2802_led *led)
static void bd2802_reset_cancel(struct bd2802_led *led) static void bd2802_reset_cancel(struct bd2802_led *led)
{ {
gpio_set_value(led->pdata->reset_gpio, 1); gpiod_set_value(led->reset, 0);
udelay(100); udelay(100);
bd2802_configure(led); bd2802_configure(led);
} }
...@@ -420,7 +421,7 @@ static void bd2802_disable_adv_conf(struct bd2802_led *led) ...@@ -420,7 +421,7 @@ static void bd2802_disable_adv_conf(struct bd2802_led *led)
bd2802_addr_attributes[i]); bd2802_addr_attributes[i]);
if (bd2802_is_all_off(led)) if (bd2802_is_all_off(led))
gpio_set_value(led->pdata->reset_gpio, 0); gpiod_set_value(led->reset, 1);
led->adf_on = 0; led->adf_on = 0;
} }
...@@ -670,8 +671,16 @@ static int bd2802_probe(struct i2c_client *client, ...@@ -670,8 +671,16 @@ static int bd2802_probe(struct i2c_client *client,
pdata = led->pdata = dev_get_platdata(&client->dev); pdata = led->pdata = dev_get_platdata(&client->dev);
i2c_set_clientdata(client, led); i2c_set_clientdata(client, led);
/* Configure RESET GPIO (L: RESET, H: RESET cancel) */ /*
gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH, "RGB_RESETB"); * Configure RESET GPIO (L: RESET, H: RESET cancel)
*
* We request the reset GPIO as OUT_LOW which means de-asserted,
* board files specifying this GPIO line in a machine descriptor
* table should take care to specify GPIO_ACTIVE_LOW for this line.
*/
led->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(led->reset))
return PTR_ERR(led->reset);
/* Tacss = min 0.1ms */ /* Tacss = min 0.1ms */
udelay(100); udelay(100);
...@@ -685,7 +694,7 @@ static int bd2802_probe(struct i2c_client *client, ...@@ -685,7 +694,7 @@ static int bd2802_probe(struct i2c_client *client,
dev_info(&client->dev, "return 0x%02x\n", ret); dev_info(&client->dev, "return 0x%02x\n", ret);
/* To save the power, reset BD2802 after detecting */ /* To save the power, reset BD2802 after detecting */
gpio_set_value(led->pdata->reset_gpio, 0); gpiod_set_value(led->reset, 1);
/* Default attributes */ /* Default attributes */
led->wave_pattern = BD2802_PATTERN_HALF; led->wave_pattern = BD2802_PATTERN_HALF;
...@@ -720,7 +729,7 @@ static int bd2802_remove(struct i2c_client *client) ...@@ -720,7 +729,7 @@ static int bd2802_remove(struct i2c_client *client)
struct bd2802_led *led = i2c_get_clientdata(client); struct bd2802_led *led = i2c_get_clientdata(client);
int i; int i;
gpio_set_value(led->pdata->reset_gpio, 0); gpiod_set_value(led->reset, 1);
bd2802_unregister_led_classdev(led); bd2802_unregister_led_classdev(led);
if (led->adf_on) if (led->adf_on)
bd2802_disable_adv_conf(led); bd2802_disable_adv_conf(led);
...@@ -750,7 +759,7 @@ static int bd2802_suspend(struct device *dev) ...@@ -750,7 +759,7 @@ static int bd2802_suspend(struct device *dev)
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
struct bd2802_led *led = i2c_get_clientdata(client); struct bd2802_led *led = i2c_get_clientdata(client);
gpio_set_value(led->pdata->reset_gpio, 0); gpiod_set_value(led->reset, 1);
return 0; return 0;
} }
......
...@@ -578,6 +578,12 @@ static int lm3532_parse_node(struct lm3532_data *priv) ...@@ -578,6 +578,12 @@ static int lm3532_parse_node(struct lm3532_data *priv)
priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time); priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time);
device_for_each_child_node(priv->dev, child) { device_for_each_child_node(priv->dev, child) {
struct led_init_data idata = {
.fwnode = child,
.default_label = ":",
.devicename = priv->client->name,
};
led = &priv->leds[i]; led = &priv->leds[i];
ret = fwnode_property_read_u32(child, "reg", &control_bank); ret = fwnode_property_read_u32(child, "reg", &control_bank);
...@@ -652,7 +658,7 @@ static int lm3532_parse_node(struct lm3532_data *priv) ...@@ -652,7 +658,7 @@ static int lm3532_parse_node(struct lm3532_data *priv)
led->led_dev.name = led->label; led->led_dev.name = led->label;
led->led_dev.brightness_set_blocking = lm3532_brightness_set; led->led_dev.brightness_set_blocking = lm3532_brightness_set;
ret = devm_led_classdev_register(priv->dev, &led->led_dev); ret = devm_led_classdev_register_ext(priv->dev, &led->led_dev, &idata);
if (ret) { if (ret) {
dev_err(&priv->client->dev, "led register err: %d\n", dev_err(&priv->client->dev, "led register err: %d\n",
ret); ret);
......
...@@ -106,7 +106,7 @@ static int lm3642_control(struct lm3642_chip_data *chip, ...@@ -106,7 +106,7 @@ static int lm3642_control(struct lm3642_chip_data *chip,
ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag); ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag);
if (ret < 0) { if (ret < 0) {
dev_err(chip->dev, "Failed to read REG_FLAG Register\n"); dev_err(chip->dev, "Failed to read REG_FLAG Register\n");
goto out; return ret;
} }
if (chip->last_flag) if (chip->last_flag)
...@@ -146,11 +146,11 @@ static int lm3642_control(struct lm3642_chip_data *chip, ...@@ -146,11 +146,11 @@ static int lm3642_control(struct lm3642_chip_data *chip,
break; break;
default: default:
return ret; return -EINVAL;
} }
if (ret < 0) { if (ret < 0) {
dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n"); dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n");
goto out; return ret;
} }
if (chip->tx_pin) if (chip->tx_pin)
...@@ -159,13 +159,12 @@ static int lm3642_control(struct lm3642_chip_data *chip, ...@@ -159,13 +159,12 @@ static int lm3642_control(struct lm3642_chip_data *chip,
ret = regmap_update_bits(chip->regmap, REG_ENABLE, ret = regmap_update_bits(chip->regmap, REG_ENABLE,
MODE_BITS_MASK << MODE_BITS_SHIFT, MODE_BITS_MASK << MODE_BITS_SHIFT,
opmode << MODE_BITS_SHIFT); opmode << MODE_BITS_SHIFT);
out:
return ret; return ret;
} }
/* torch */ /* torch */
/* torch pin config for lm3642*/ /* torch pin config for lm3642 */
static ssize_t lm3642_torch_pin_store(struct device *dev, static ssize_t lm3642_torch_pin_store(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t size) const char *buf, size_t size)
...@@ -178,7 +177,7 @@ static ssize_t lm3642_torch_pin_store(struct device *dev, ...@@ -178,7 +177,7 @@ static ssize_t lm3642_torch_pin_store(struct device *dev,
ret = kstrtouint(buf, 10, &state); ret = kstrtouint(buf, 10, &state);
if (ret) if (ret)
goto out_strtoint; return ret;
if (state != 0) if (state != 0)
state = 0x01 << TORCH_PIN_EN_SHIFT; state = 0x01 << TORCH_PIN_EN_SHIFT;
...@@ -186,16 +185,12 @@ static ssize_t lm3642_torch_pin_store(struct device *dev, ...@@ -186,16 +185,12 @@ static ssize_t lm3642_torch_pin_store(struct device *dev,
ret = regmap_update_bits(chip->regmap, REG_ENABLE, ret = regmap_update_bits(chip->regmap, REG_ENABLE,
TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT, TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT,
state); state);
if (ret < 0) if (ret < 0) {
goto out; dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);
return ret;
}
return size; return size;
out:
dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);
return ret;
out_strtoint:
dev_err(chip->dev, "%s: fail to change str to int\n", __func__);
return ret;
} }
static DEVICE_ATTR(torch_pin, S_IWUSR, NULL, lm3642_torch_pin_store); static DEVICE_ATTR(torch_pin, S_IWUSR, NULL, lm3642_torch_pin_store);
...@@ -229,7 +224,7 @@ static ssize_t lm3642_strobe_pin_store(struct device *dev, ...@@ -229,7 +224,7 @@ static ssize_t lm3642_strobe_pin_store(struct device *dev,
ret = kstrtouint(buf, 10, &state); ret = kstrtouint(buf, 10, &state);
if (ret) if (ret)
goto out_strtoint; return ret;
if (state != 0) if (state != 0)
state = 0x01 << STROBE_PIN_EN_SHIFT; state = 0x01 << STROBE_PIN_EN_SHIFT;
...@@ -237,16 +232,12 @@ static ssize_t lm3642_strobe_pin_store(struct device *dev, ...@@ -237,16 +232,12 @@ static ssize_t lm3642_strobe_pin_store(struct device *dev,
ret = regmap_update_bits(chip->regmap, REG_ENABLE, ret = regmap_update_bits(chip->regmap, REG_ENABLE,
STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT, STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT,
state); state);
if (ret < 0) if (ret < 0) {
goto out; dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);
return ret;
}
return size; return size;
out:
dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);
return ret;
out_strtoint:
dev_err(chip->dev, "%s: fail to change str to int\n", __func__);
return ret;
} }
static DEVICE_ATTR(strobe_pin, S_IWUSR, NULL, lm3642_strobe_pin_store); static DEVICE_ATTR(strobe_pin, S_IWUSR, NULL, lm3642_strobe_pin_store);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/log2.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/of.h> #include <linux/of.h>
...@@ -114,6 +115,9 @@ struct lm3692x_led { ...@@ -114,6 +115,9 @@ struct lm3692x_led {
struct regulator *regulator; struct regulator *regulator;
int led_enable; int led_enable;
int model_id; int model_id;
u8 boost_ctrl, brightness_ctrl;
bool enabled;
}; };
static const struct reg_default lm3692x_reg_defs[] = { static const struct reg_default lm3692x_reg_defs[] = {
...@@ -162,44 +166,14 @@ static int lm3692x_fault_check(struct lm3692x_led *led) ...@@ -162,44 +166,14 @@ static int lm3692x_fault_check(struct lm3692x_led *led)
return read_buf; return read_buf;
} }
static int lm3692x_brightness_set(struct led_classdev *led_cdev, static int lm3692x_leds_enable(struct lm3692x_led *led)
enum led_brightness brt_val)
{
struct lm3692x_led *led =
container_of(led_cdev, struct lm3692x_led, led_dev);
int ret;
int led_brightness_lsb = (brt_val >> 5);
mutex_lock(&led->lock);
ret = lm3692x_fault_check(led);
if (ret) {
dev_err(&led->client->dev, "Cannot read/clear faults: %d\n",
ret);
goto out;
}
ret = regmap_write(led->regmap, LM3692X_BRT_MSB, brt_val);
if (ret) {
dev_err(&led->client->dev, "Cannot write MSB: %d\n", ret);
goto out;
}
ret = regmap_write(led->regmap, LM3692X_BRT_LSB, led_brightness_lsb);
if (ret) {
dev_err(&led->client->dev, "Cannot write LSB: %d\n", ret);
goto out;
}
out:
mutex_unlock(&led->lock);
return ret;
}
static int lm3692x_init(struct lm3692x_led *led)
{ {
int enable_state; int enable_state;
int ret, reg_ret; int ret, reg_ret;
if (led->enabled)
return 0;
if (led->regulator) { if (led->regulator) {
ret = regulator_enable(led->regulator); ret = regulator_enable(led->regulator);
if (ret) { if (ret) {
...@@ -249,10 +223,7 @@ static int lm3692x_init(struct lm3692x_led *led) ...@@ -249,10 +223,7 @@ static int lm3692x_init(struct lm3692x_led *led)
if (ret) if (ret)
goto out; goto out;
ret = regmap_write(led->regmap, LM3692X_BOOST_CTRL, ret = regmap_write(led->regmap, LM3692X_BOOST_CTRL, led->boost_ctrl);
LM3692X_BOOST_SW_1MHZ |
LM3692X_BOOST_SW_NO_SHIFT |
LM3692X_OCP_PROT_1_5A);
if (ret) if (ret)
goto out; goto out;
...@@ -305,6 +276,7 @@ static int lm3692x_init(struct lm3692x_led *led) ...@@ -305,6 +276,7 @@ static int lm3692x_init(struct lm3692x_led *led)
ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_ENABLE_MASK, ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_ENABLE_MASK,
enable_state | LM3692X_DEVICE_EN); enable_state | LM3692X_DEVICE_EN);
led->enabled = true;
return ret; return ret;
out: out:
dev_err(&led->client->dev, "Fail writing initialization values\n"); dev_err(&led->client->dev, "Fail writing initialization values\n");
...@@ -322,10 +294,92 @@ static int lm3692x_init(struct lm3692x_led *led) ...@@ -322,10 +294,92 @@ static int lm3692x_init(struct lm3692x_led *led)
return ret; return ret;
} }
static int lm3692x_leds_disable(struct lm3692x_led *led)
{
int ret;
if (!led->enabled)
return 0;
ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_DEVICE_EN, 0);
if (ret) {
dev_err(&led->client->dev, "Failed to disable regulator: %d\n",
ret);
return ret;
}
if (led->enable_gpio)
gpiod_direction_output(led->enable_gpio, 0);
if (led->regulator) {
ret = regulator_disable(led->regulator);
if (ret)
dev_err(&led->client->dev,
"Failed to disable regulator: %d\n", ret);
}
led->enabled = false;
return ret;
}
static int lm3692x_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brt_val)
{
struct lm3692x_led *led =
container_of(led_cdev, struct lm3692x_led, led_dev);
int ret;
int led_brightness_lsb = (brt_val >> 5);
mutex_lock(&led->lock);
if (brt_val == 0) {
ret = lm3692x_leds_disable(led);
goto out;
} else {
lm3692x_leds_enable(led);
}
ret = lm3692x_fault_check(led);
if (ret) {
dev_err(&led->client->dev, "Cannot read/clear faults: %d\n",
ret);
goto out;
}
ret = regmap_write(led->regmap, LM3692X_BRT_MSB, brt_val);
if (ret) {
dev_err(&led->client->dev, "Cannot write MSB: %d\n", ret);
goto out;
}
ret = regmap_write(led->regmap, LM3692X_BRT_LSB, led_brightness_lsb);
if (ret) {
dev_err(&led->client->dev, "Cannot write LSB: %d\n", ret);
goto out;
}
out:
mutex_unlock(&led->lock);
return ret;
}
static enum led_brightness lm3692x_max_brightness(struct lm3692x_led *led,
u32 max_cur)
{
u32 max_code;
/* see p.12 of LM36922 data sheet for brightness formula */
max_code = ((max_cur * 1000) - 37806) / 12195;
if (max_code > 0x7FF)
max_code = 0x7FF;
return max_code >> 3;
}
static int lm3692x_probe_dt(struct lm3692x_led *led) static int lm3692x_probe_dt(struct lm3692x_led *led)
{ {
struct fwnode_handle *child = NULL; struct fwnode_handle *child = NULL;
struct led_init_data init_data = {}; struct led_init_data init_data = {};
u32 ovp, max_cur;
int ret; int ret;
led->enable_gpio = devm_gpiod_get_optional(&led->client->dev, led->enable_gpio = devm_gpiod_get_optional(&led->client->dev,
...@@ -350,6 +404,32 @@ static int lm3692x_probe_dt(struct lm3692x_led *led) ...@@ -350,6 +404,32 @@ static int lm3692x_probe_dt(struct lm3692x_led *led)
led->regulator = NULL; led->regulator = NULL;
} }
led->boost_ctrl = LM3692X_BOOST_SW_1MHZ |
LM3692X_BOOST_SW_NO_SHIFT |
LM3692X_OCP_PROT_1_5A;
ret = device_property_read_u32(&led->client->dev,
"ti,ovp-microvolt", &ovp);
if (ret) {
led->boost_ctrl |= LM3692X_OVP_29V;
} else {
switch (ovp) {
case 17000000:
break;
case 21000000:
led->boost_ctrl |= LM3692X_OVP_21V;
break;
case 25000000:
led->boost_ctrl |= LM3692X_OVP_25V;
break;
case 29000000:
led->boost_ctrl |= LM3692X_OVP_29V;
break;
default:
dev_err(&led->client->dev, "Invalid OVP %d\n", ovp);
return -EINVAL;
}
}
child = device_get_next_child_node(&led->client->dev, child); child = device_get_next_child_node(&led->client->dev, child);
if (!child) { if (!child) {
dev_err(&led->client->dev, "No LED Child node\n"); dev_err(&led->client->dev, "No LED Child node\n");
...@@ -365,6 +445,10 @@ static int lm3692x_probe_dt(struct lm3692x_led *led) ...@@ -365,6 +445,10 @@ static int lm3692x_probe_dt(struct lm3692x_led *led)
return ret; return ret;
} }
ret = fwnode_property_read_u32(child, "led-max-microamp", &max_cur);
led->led_dev.max_brightness = ret ? LED_FULL :
lm3692x_max_brightness(led, max_cur);
init_data.fwnode = child; init_data.fwnode = child;
init_data.devicename = led->client->name; init_data.devicename = led->client->name;
init_data.default_label = ":"; init_data.default_label = ":";
...@@ -407,7 +491,7 @@ static int lm3692x_probe(struct i2c_client *client, ...@@ -407,7 +491,7 @@ static int lm3692x_probe(struct i2c_client *client,
if (ret) if (ret)
return ret; return ret;
ret = lm3692x_init(led); ret = lm3692x_leds_enable(led);
if (ret) if (ret)
return ret; return ret;
...@@ -419,23 +503,9 @@ static int lm3692x_remove(struct i2c_client *client) ...@@ -419,23 +503,9 @@ static int lm3692x_remove(struct i2c_client *client)
struct lm3692x_led *led = i2c_get_clientdata(client); struct lm3692x_led *led = i2c_get_clientdata(client);
int ret; int ret;
ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_DEVICE_EN, 0); ret = lm3692x_leds_disable(led);
if (ret) { if (ret)
dev_err(&led->client->dev, "Failed to disable regulator: %d\n",
ret);
return ret; return ret;
}
if (led->enable_gpio)
gpiod_direction_output(led->enable_gpio, 0);
if (led->regulator) {
ret = regulator_disable(led->regulator);
if (ret)
dev_err(&led->client->dev,
"Failed to disable regulator: %d\n", ret);
}
mutex_destroy(&led->lock); mutex_destroy(&led->lock);
return 0; return 0;
......
...@@ -40,6 +40,8 @@ ...@@ -40,6 +40,8 @@
#define PCA963X_LED_PWM 0x2 /* Controlled through PWM */ #define PCA963X_LED_PWM 0x2 /* Controlled through PWM */
#define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ #define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
#define PCA963X_MODE2_OUTDRV 0x04 /* Open-drain or totem pole */
#define PCA963X_MODE2_INVRT 0x10 /* Normal or inverted direction */
#define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */ #define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */
#define PCA963X_MODE1 0x00 #define PCA963X_MODE1 0x00
...@@ -438,12 +440,12 @@ static int pca963x_probe(struct i2c_client *client, ...@@ -438,12 +440,12 @@ static int pca963x_probe(struct i2c_client *client,
PCA963X_MODE2); PCA963X_MODE2);
/* Configure output: open-drain or totem pole (push-pull) */ /* Configure output: open-drain or totem pole (push-pull) */
if (pdata->outdrv == PCA963X_OPEN_DRAIN) if (pdata->outdrv == PCA963X_OPEN_DRAIN)
mode2 |= 0x01; mode2 &= ~PCA963X_MODE2_OUTDRV;
else else
mode2 |= 0x05; mode2 |= PCA963X_MODE2_OUTDRV;
/* Configure direction: normal or inverted */ /* Configure direction: normal or inverted */
if (pdata->dir == PCA963X_INVERTED) if (pdata->dir == PCA963X_INVERTED)
mode2 |= 0x10; mode2 |= PCA963X_MODE2_INVRT;
i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2, i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2,
mode2); mode2);
} }
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Sven Van Asbroeck
*/
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/tps6105x.h>
#include <linux/regmap.h>
struct tps6105x_priv {
struct regmap *regmap;
struct led_classdev cdev;
struct fwnode_handle *fwnode;
};
static void tps6105x_handle_put(void *data)
{
struct tps6105x_priv *priv = data;
fwnode_handle_put(priv->fwnode);
}
static int tps6105x_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct tps6105x_priv *priv = container_of(cdev, struct tps6105x_priv,
cdev);
return regmap_update_bits(priv->regmap, TPS6105X_REG_0,
TPS6105X_REG0_TORCHC_MASK,
brightness << TPS6105X_REG0_TORCHC_SHIFT);
}
static int tps6105x_led_probe(struct platform_device *pdev)
{
struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev);
struct tps6105x_platform_data *pdata = tps6105x->pdata;
struct led_init_data init_data = { };
struct tps6105x_priv *priv;
int ret;
/* This instance is not set for torch mode so bail out */
if (pdata->mode != TPS6105X_MODE_TORCH) {
dev_info(&pdev->dev,
"chip not in torch mode, exit probe");
return -EINVAL;
}
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* fwnode/devicetree is optional. NULL is allowed for priv->fwnode */
priv->fwnode = device_get_next_child_node(pdev->dev.parent, NULL);
ret = devm_add_action_or_reset(&pdev->dev, tps6105x_handle_put, priv);
if (ret)
return ret;
priv->regmap = tps6105x->regmap;
priv->cdev.brightness_set_blocking = tps6105x_brightness_set;
priv->cdev.max_brightness = 7;
init_data.devicename = "tps6105x";
init_data.default_label = ":torch";
init_data.fwnode = priv->fwnode;
ret = regmap_update_bits(tps6105x->regmap, TPS6105X_REG_0,
TPS6105X_REG0_MODE_MASK |
TPS6105X_REG0_TORCHC_MASK,
TPS6105X_REG0_MODE_TORCH <<
TPS6105X_REG0_MODE_SHIFT);
if (ret)
return ret;
return devm_led_classdev_register_ext(&pdev->dev, &priv->cdev,
&init_data);
}
static struct platform_driver led_driver = {
.probe = tps6105x_led_probe,
.driver = {
.name = "tps6105x-leds",
},
};
module_platform_driver(led_driver);
MODULE_DESCRIPTION("TPS6105x LED driver");
MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
MODULE_LICENSE("GPL v2");
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
#define _LEDS_BD2802_H_ #define _LEDS_BD2802_H_
struct bd2802_led_platform_data{ struct bd2802_led_platform_data{
int reset_gpio;
u8 rgb_time; u8 rgb_time;
}; };
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
struct device; struct device;
struct led_pattern; struct led_pattern;
struct device_node;
/* /*
* LED Core * LED Core
*/ */
...@@ -196,6 +197,11 @@ void devm_led_classdev_unregister(struct device *parent, ...@@ -196,6 +197,11 @@ void devm_led_classdev_unregister(struct device *parent,
void led_classdev_suspend(struct led_classdev *led_cdev); void led_classdev_suspend(struct led_classdev *led_cdev);
void led_classdev_resume(struct led_classdev *led_cdev); void led_classdev_resume(struct led_classdev *led_cdev);
extern struct led_classdev *of_led_get(struct device_node *np, int index);
extern void led_put(struct led_classdev *led_cdev);
struct led_classdev *__must_check devm_of_led_get(struct device *dev,
int index);
/** /**
* led_blink_set - set blinking with software fallback * led_blink_set - set blinking with software fallback
* @led_cdev: the LED to start blinking * @led_cdev: the LED to start blinking
......
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