diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-02-02 11:48:46 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-02-02 11:48:46 -0800 |
commit | 545ae66582f7627870b719d318954d0252902519 (patch) | |
tree | edb03cfede46e78d621d4df0accb201e293b32ca /drivers | |
parent | 15f8e73355df9ec48902d128a0ef01a6b8bff453 (diff) | |
parent | 260718b3a35d23fe89d27cc7b5e8bd30f4bba1aa (diff) | |
download | linux-545ae66582f7627870b719d318954d0252902519.tar.bz2 |
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
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/leds/Kconfig | 10 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/led-class.c | 97 | ||||
-rw-r--r-- | drivers/leds/leds-bd2802.c | 27 | ||||
-rw-r--r-- | drivers/leds/leds-lm3532.c | 8 | ||||
-rw-r--r-- | drivers/leds/leds-lm3642.c | 37 | ||||
-rw-r--r-- | drivers/leds/leds-lm3692x.c | 180 | ||||
-rw-r--r-- | drivers/leds/leds-pca963x.c | 8 | ||||
-rw-r--r-- | drivers/leds/leds-tps6105x.c | 89 |
9 files changed, 365 insertions, 92 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 4b68520ac251..d82f1dea3711 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -836,6 +836,16 @@ config LEDS_LM36274 Say Y to enable the LM36274 LED driver for TI LMU devices. 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" source "drivers/leds/trigger/Kconfig" diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 2da39e896ce8..d7e1107753fb 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -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_LM3697) += leds-lm3697.o obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o +obj-$(CONFIG_LEDS_TPS6105X) += leds-tps6105x.o # LED SPI Drivers obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 438774315e6c..1fc40e8af75e 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -19,6 +19,7 @@ #include <linux/spinlock.h> #include <linux/timer.h> #include <uapi/linux/uleds.h> +#include <linux/of.h> #include "leds.h" static struct class *leds_class; @@ -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); +/** + * 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, size_t len) { @@ -276,8 +369,10 @@ int led_classdev_register_ext(struct device *parent, mutex_unlock(&led_cdev->led_access); 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->of_node = to_of_node(init_data->fwnode); + } if (ret) dev_warn(parent, "Led %s renamed to %s due to name collision", diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c index e7ec6bff2b5f..bd61a823d0ca 100644 --- a/drivers/leds/leds-bd2802.c +++ b/drivers/leds/leds-bd2802.c @@ -10,7 +10,7 @@ #include <linux/module.h> #include <linux/i2c.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include <linux/leds.h> #include <linux/leds-bd2802.h> @@ -67,6 +67,7 @@ struct led_state { struct bd2802_led { struct bd2802_led_platform_data *pdata; struct i2c_client *client; + struct gpio_desc *reset; struct rw_semaphore rwsem; struct led_state led[2]; @@ -200,7 +201,7 @@ static void bd2802_update_state(struct bd2802_led *led, enum led_ids id, return; if (bd2802_is_all_off(led) && !led->adf_on) { - gpio_set_value(led->pdata->reset_gpio, 0); + gpiod_set_value(led->reset, 1); return; } @@ -226,7 +227,7 @@ static void bd2802_configure(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); bd2802_configure(led); } @@ -420,7 +421,7 @@ static void bd2802_disable_adv_conf(struct bd2802_led *led) bd2802_addr_attributes[i]); if (bd2802_is_all_off(led)) - gpio_set_value(led->pdata->reset_gpio, 0); + gpiod_set_value(led->reset, 1); led->adf_on = 0; } @@ -670,8 +671,16 @@ static int bd2802_probe(struct i2c_client *client, pdata = led->pdata = dev_get_platdata(&client->dev); 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 */ udelay(100); @@ -685,7 +694,7 @@ static int bd2802_probe(struct i2c_client *client, dev_info(&client->dev, "return 0x%02x\n", ret); /* To save the power, reset BD2802 after detecting */ - gpio_set_value(led->pdata->reset_gpio, 0); + gpiod_set_value(led->reset, 1); /* Default attributes */ led->wave_pattern = BD2802_PATTERN_HALF; @@ -720,7 +729,7 @@ static int bd2802_remove(struct i2c_client *client) struct bd2802_led *led = i2c_get_clientdata(client); int i; - gpio_set_value(led->pdata->reset_gpio, 0); + gpiod_set_value(led->reset, 1); bd2802_unregister_led_classdev(led); if (led->adf_on) bd2802_disable_adv_conf(led); @@ -750,7 +759,7 @@ static int bd2802_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct bd2802_led *led = i2c_get_clientdata(client); - gpio_set_value(led->pdata->reset_gpio, 0); + gpiod_set_value(led->reset, 1); return 0; } diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c index 491268bb34a7..188a57da981a 100644 --- a/drivers/leds/leds-lm3532.c +++ b/drivers/leds/leds-lm3532.c @@ -578,6 +578,12 @@ static int lm3532_parse_node(struct lm3532_data *priv) priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time); 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]; ret = fwnode_property_read_u32(child, "reg", &control_bank); @@ -652,7 +658,7 @@ static int lm3532_parse_node(struct lm3532_data *priv) led->led_dev.name = led->label; 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) { dev_err(&priv->client->dev, "led register err: %d\n", ret); diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c index 480575442ed8..4232906fcb75 100644 --- a/drivers/leds/leds-lm3642.c +++ b/drivers/leds/leds-lm3642.c @@ -106,7 +106,7 @@ static int lm3642_control(struct lm3642_chip_data *chip, ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag); if (ret < 0) { dev_err(chip->dev, "Failed to read REG_FLAG Register\n"); - goto out; + return ret; } if (chip->last_flag) @@ -146,11 +146,11 @@ static int lm3642_control(struct lm3642_chip_data *chip, break; default: - return ret; + return -EINVAL; } if (ret < 0) { dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n"); - goto out; + return ret; } if (chip->tx_pin) @@ -159,13 +159,12 @@ static int lm3642_control(struct lm3642_chip_data *chip, ret = regmap_update_bits(chip->regmap, REG_ENABLE, MODE_BITS_MASK << MODE_BITS_SHIFT, opmode << MODE_BITS_SHIFT); -out: return ret; } /* torch */ -/* torch pin config for lm3642*/ +/* torch pin config for lm3642 */ static ssize_t lm3642_torch_pin_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) @@ -178,7 +177,7 @@ static ssize_t lm3642_torch_pin_store(struct device *dev, ret = kstrtouint(buf, 10, &state); if (ret) - goto out_strtoint; + return ret; if (state != 0) state = 0x01 << TORCH_PIN_EN_SHIFT; @@ -186,16 +185,12 @@ static ssize_t lm3642_torch_pin_store(struct device *dev, ret = regmap_update_bits(chip->regmap, REG_ENABLE, TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT, state); - if (ret < 0) - goto out; + if (ret < 0) { + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return ret; + } 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); @@ -229,7 +224,7 @@ static ssize_t lm3642_strobe_pin_store(struct device *dev, ret = kstrtouint(buf, 10, &state); if (ret) - goto out_strtoint; + return ret; if (state != 0) state = 0x01 << STROBE_PIN_EN_SHIFT; @@ -237,16 +232,12 @@ static ssize_t lm3642_strobe_pin_store(struct device *dev, ret = regmap_update_bits(chip->regmap, REG_ENABLE, STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT, state); - if (ret < 0) - goto out; + if (ret < 0) { + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return ret; + } 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); diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index 8b408102e138..28a51aeb28de 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -6,6 +6,7 @@ #include <linux/i2c.h> #include <linux/init.h> #include <linux/leds.h> +#include <linux/log2.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> @@ -114,6 +115,9 @@ struct lm3692x_led { struct regulator *regulator; int led_enable; int model_id; + + u8 boost_ctrl, brightness_ctrl; + bool enabled; }; static const struct reg_default lm3692x_reg_defs[] = { @@ -162,44 +166,14 @@ static int lm3692x_fault_check(struct lm3692x_led *led) return read_buf; } -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); - - 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) +static int lm3692x_leds_enable(struct lm3692x_led *led) { int enable_state; int ret, reg_ret; + if (led->enabled) + return 0; + if (led->regulator) { ret = regulator_enable(led->regulator); if (ret) { @@ -249,10 +223,7 @@ static int lm3692x_init(struct lm3692x_led *led) if (ret) goto out; - ret = regmap_write(led->regmap, LM3692X_BOOST_CTRL, - LM3692X_BOOST_SW_1MHZ | - LM3692X_BOOST_SW_NO_SHIFT | - LM3692X_OCP_PROT_1_5A); + ret = regmap_write(led->regmap, LM3692X_BOOST_CTRL, led->boost_ctrl); if (ret) goto out; @@ -305,6 +276,7 @@ static int lm3692x_init(struct lm3692x_led *led) ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_ENABLE_MASK, enable_state | LM3692X_DEVICE_EN); + led->enabled = true; return ret; out: dev_err(&led->client->dev, "Fail writing initialization values\n"); @@ -322,10 +294,92 @@ out: 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) { struct fwnode_handle *child = NULL; struct led_init_data init_data = {}; + u32 ovp, max_cur; int ret; led->enable_gpio = devm_gpiod_get_optional(&led->client->dev, @@ -350,6 +404,32 @@ static int lm3692x_probe_dt(struct lm3692x_led *led) 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); if (!child) { dev_err(&led->client->dev, "No LED Child node\n"); @@ -365,6 +445,10 @@ static int lm3692x_probe_dt(struct lm3692x_led *led) 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.devicename = led->client->name; init_data.default_label = ":"; @@ -407,7 +491,7 @@ static int lm3692x_probe(struct i2c_client *client, if (ret) return ret; - ret = lm3692x_init(led); + ret = lm3692x_leds_enable(led); if (ret) return ret; @@ -419,23 +503,9 @@ static int lm3692x_remove(struct i2c_client *client) struct lm3692x_led *led = i2c_get_clientdata(client); int ret; - 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); + ret = lm3692x_leds_disable(led); + if (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); return 0; diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 4afc317901a8..66cdc003b8f4 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -40,6 +40,8 @@ #define PCA963X_LED_PWM 0x2 /* Controlled through PWM */ #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_MODE1 0x00 @@ -438,12 +440,12 @@ static int pca963x_probe(struct i2c_client *client, PCA963X_MODE2); /* Configure output: open-drain or totem pole (push-pull) */ if (pdata->outdrv == PCA963X_OPEN_DRAIN) - mode2 |= 0x01; + mode2 &= ~PCA963X_MODE2_OUTDRV; else - mode2 |= 0x05; + mode2 |= PCA963X_MODE2_OUTDRV; /* Configure direction: normal or inverted */ if (pdata->dir == PCA963X_INVERTED) - mode2 |= 0x10; + mode2 |= PCA963X_MODE2_INVRT; i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2, mode2); } diff --git a/drivers/leds/leds-tps6105x.c b/drivers/leds/leds-tps6105x.c new file mode 100644 index 000000000000..09fd88a6c8f0 --- /dev/null +++ b/drivers/leds/leds-tps6105x.c @@ -0,0 +1,89 @@ +// 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"); |