summaryrefslogtreecommitdiff
path: root/drivers/leds/leds-lp5562.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds/leds-lp5562.c')
-rw-r--r--drivers/leds/leds-lp5562.c329
1 files changed, 68 insertions, 261 deletions
diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c
index cbd856dac150..14a4af361b26 100644
--- a/drivers/leds/leds-lp5562.c
+++ b/drivers/leds/leds-lp5562.c
@@ -1,28 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* LP5562 LED driver
*
* Copyright (C) 2013 Texas Instruments
*
* Author: Milo(Woogyom) Kim <milo.kim@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
-#include <linux/init.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/platform_data/leds-lp55xx.h>
#include <linux/slab.h>
#include "leds-lp55xx-common.h"
-#define LP5562_PROGRAM_LENGTH 32
#define LP5562_MAX_LEDS 4
/* ENABLE Register 00h */
@@ -41,21 +38,6 @@
/* OPMODE Register 01h */
#define LP5562_REG_OP_MODE 0x01
-#define LP5562_MODE_ENG1_M 0x30
-#define LP5562_MODE_ENG2_M 0x0C
-#define LP5562_MODE_ENG3_M 0x03
-#define LP5562_LOAD_ENG1 0x10
-#define LP5562_LOAD_ENG2 0x04
-#define LP5562_LOAD_ENG3 0x01
-#define LP5562_RUN_ENG1 0x20
-#define LP5562_RUN_ENG2 0x08
-#define LP5562_RUN_ENG3 0x02
-#define LP5562_ENG1_IS_LOADING(mode) \
- ((mode & LP5562_MODE_ENG1_M) == LP5562_LOAD_ENG1)
-#define LP5562_ENG2_IS_LOADING(mode) \
- ((mode & LP5562_MODE_ENG2_M) == LP5562_LOAD_ENG2)
-#define LP5562_ENG3_IS_LOADING(mode) \
- ((mode & LP5562_MODE_ENG3_M) == LP5562_LOAD_ENG3)
/* BRIGHTNESS Registers */
#define LP5562_REG_R_PWM 0x04
@@ -116,7 +98,7 @@ static inline void lp5562_wait_enable_done(void)
static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current)
{
- u8 addr[] = {
+ static const u8 addr[] = {
LP5562_REG_R_CURRENT,
LP5562_REG_G_CURRENT,
LP5562_REG_B_CURRENT,
@@ -127,156 +109,24 @@ static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current)
lp55xx_write(led->chip, addr[led->chan_nr], led_current);
}
-static void lp5562_load_engine(struct lp55xx_chip *chip)
-{
- enum lp55xx_engine_index idx = chip->engine_idx;
- u8 mask[] = {
- [LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M,
- [LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M,
- [LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M,
- };
-
- u8 val[] = {
- [LP55XX_ENGINE_1] = LP5562_LOAD_ENG1,
- [LP55XX_ENGINE_2] = LP5562_LOAD_ENG2,
- [LP55XX_ENGINE_3] = LP5562_LOAD_ENG3,
- };
-
- lp55xx_update_bits(chip, LP5562_REG_OP_MODE, mask[idx], val[idx]);
-
- lp5562_wait_opmode_done();
-}
-
-static void lp5562_stop_engine(struct lp55xx_chip *chip)
-{
- lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DISABLE);
- lp5562_wait_opmode_done();
-}
-
static void lp5562_run_engine(struct lp55xx_chip *chip, bool start)
{
int ret;
- u8 mode;
- u8 exec;
/* stop engine */
if (!start) {
lp55xx_write(chip, LP5562_REG_ENABLE, LP5562_ENABLE_DEFAULT);
lp5562_wait_enable_done();
- lp5562_stop_engine(chip);
+ lp55xx_stop_all_engine(chip);
lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
lp5562_wait_opmode_done();
return;
}
- /*
- * To run the engine,
- * operation mode and enable register should updated at the same time
- */
-
- ret = lp55xx_read(chip, LP5562_REG_OP_MODE, &mode);
- if (ret)
- return;
-
- ret = lp55xx_read(chip, LP5562_REG_ENABLE, &exec);
- if (ret)
- return;
-
- /* change operation mode to RUN only when each engine is loading */
- if (LP5562_ENG1_IS_LOADING(mode)) {
- mode = (mode & ~LP5562_MODE_ENG1_M) | LP5562_RUN_ENG1;
- exec = (exec & ~LP5562_EXEC_ENG1_M) | LP5562_RUN_ENG1;
- }
-
- if (LP5562_ENG2_IS_LOADING(mode)) {
- mode = (mode & ~LP5562_MODE_ENG2_M) | LP5562_RUN_ENG2;
- exec = (exec & ~LP5562_EXEC_ENG2_M) | LP5562_RUN_ENG2;
- }
-
- if (LP5562_ENG3_IS_LOADING(mode)) {
- mode = (mode & ~LP5562_MODE_ENG3_M) | LP5562_RUN_ENG3;
- exec = (exec & ~LP5562_EXEC_ENG3_M) | LP5562_RUN_ENG3;
- }
-
- lp55xx_write(chip, LP5562_REG_OP_MODE, mode);
- lp5562_wait_opmode_done();
-
- lp55xx_update_bits(chip, LP5562_REG_ENABLE, LP5562_EXEC_M, exec);
- lp5562_wait_enable_done();
-}
-
-static int lp5562_update_firmware(struct lp55xx_chip *chip,
- const u8 *data, size_t size)
-{
- enum lp55xx_engine_index idx = chip->engine_idx;
- u8 pattern[LP5562_PROGRAM_LENGTH] = {0};
- u8 addr[] = {
- [LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1,
- [LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2,
- [LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3,
- };
- unsigned cmd;
- char c[3];
- int program_size;
- int nrchars;
- int offset = 0;
- int ret;
- int i;
-
- /* clear program memory before updating */
- for (i = 0; i < LP5562_PROGRAM_LENGTH; i++)
- lp55xx_write(chip, addr[idx] + i, 0);
-
- i = 0;
- while ((offset < size - 1) && (i < LP5562_PROGRAM_LENGTH)) {
- /* separate sscanfs because length is working only for %s */
- ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
- if (ret != 1)
- goto err;
-
- ret = sscanf(c, "%2x", &cmd);
- if (ret != 1)
- goto err;
-
- pattern[i] = (u8)cmd;
- offset += nrchars;
- i++;
- }
-
- /* Each instruction is 16bit long. Check that length is even */
- if (i % 2)
- goto err;
-
- program_size = i;
- for (i = 0; i < program_size; i++)
- lp55xx_write(chip, addr[idx] + i, pattern[i]);
-
- return 0;
-
-err:
- dev_err(&chip->cl->dev, "wrong pattern format\n");
- return -EINVAL;
-}
-
-static void lp5562_firmware_loaded(struct lp55xx_chip *chip)
-{
- const struct firmware *fw = chip->fw;
-
- if (fw->size > LP5562_PROGRAM_LENGTH) {
- dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
- fw->size);
- return;
- }
-
- /*
- * Program momery sequence
- * 1) set engine mode to "LOAD"
- * 2) write firmware data into program memory
- */
-
- lp5562_load_engine(chip);
- lp5562_update_firmware(chip, fw->data, fw->size);
+ ret = lp55xx_run_engine_common(chip);
+ if (!ret)
+ lp5562_wait_enable_done();
}
static int lp5562_post_init_device(struct lp55xx_chip *chip)
@@ -311,21 +161,46 @@ static int lp5562_post_init_device(struct lp55xx_chip *chip)
return 0;
}
-static void lp5562_led_brightness_work(struct work_struct *work)
+static int lp5562_multicolor_brightness(struct lp55xx_led *led)
{
- struct lp55xx_led *led = container_of(work, struct lp55xx_led,
- brightness_work);
struct lp55xx_chip *chip = led->chip;
- u8 addr[] = {
+ static const u8 addr[] = {
LP5562_REG_R_PWM,
LP5562_REG_G_PWM,
LP5562_REG_B_PWM,
LP5562_REG_W_PWM,
};
+ int ret;
+ int i;
+
+ guard(mutex)(&chip->lock);
+ for (i = 0; i < led->mc_cdev.num_colors; i++) {
+ ret = lp55xx_write(chip,
+ addr[led->mc_cdev.subled_info[i].channel],
+ led->mc_cdev.subled_info[i].brightness);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int lp5562_led_brightness(struct lp55xx_led *led)
+{
+ struct lp55xx_chip *chip = led->chip;
+ static const u8 addr[] = {
+ LP5562_REG_R_PWM,
+ LP5562_REG_G_PWM,
+ LP5562_REG_B_PWM,
+ LP5562_REG_W_PWM,
+ };
+ int ret;
+
+ guard(mutex)(&chip->lock);
- mutex_lock(&chip->lock);
- lp55xx_write(chip, addr[led->chan_nr], led->brightness);
- mutex_unlock(&chip->lock);
+ ret = lp55xx_write(chip, addr[led->chan_nr], led->brightness);
+
+ return ret;
}
static void lp5562_write_program_memory(struct lp55xx_chip *chip,
@@ -346,9 +221,9 @@ static void lp5562_write_program_memory(struct lp55xx_chip *chip,
/* check the size of program count */
static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn)
{
- return (ptn->size_r >= LP5562_PROGRAM_LENGTH ||
- ptn->size_g >= LP5562_PROGRAM_LENGTH ||
- ptn->size_b >= LP5562_PROGRAM_LENGTH);
+ return ptn->size_r >= LP55xx_BYTES_PER_PAGE ||
+ ptn->size_g >= LP55xx_BYTES_PER_PAGE ||
+ ptn->size_b >= LP55xx_BYTES_PER_PAGE;
}
static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode)
@@ -367,7 +242,7 @@ static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode)
return -EINVAL;
}
- lp5562_stop_engine(chip);
+ lp55xx_stop_all_engine(chip);
/* Set LED map as RGB */
lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_RGB);
@@ -375,7 +250,7 @@ static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode)
/* Load engines */
for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {
chip->engine_idx = i;
- lp5562_load_engine(chip);
+ lp55xx_load_engine(chip);
}
/* Clear program registers */
@@ -418,9 +293,9 @@ static ssize_t lp5562_store_pattern(struct device *dev,
if (mode > num_patterns || !ptn)
return -EINVAL;
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
+
ret = lp5562_run_predef_led_pattern(chip, mode);
- mutex_unlock(&chip->lock);
if (ret)
return ret;
@@ -470,15 +345,15 @@ static ssize_t lp5562_store_engine_mux(struct device *dev,
return -EINVAL;
}
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
+
lp55xx_update_bits(chip, LP5562_REG_ENG_SEL, mask, val);
- mutex_unlock(&chip->lock);
return len;
}
-static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, lp5562_store_pattern);
-static DEVICE_ATTR(engine_mux, S_IWUSR, NULL, lp5562_store_engine_mux);
+static LP55XX_DEV_ATTR_WO(led_pattern, lp5562_store_pattern);
+static LP55XX_DEV_ATTR_WO(engine_mux, lp5562_store_engine_mux);
static struct attribute *lp5562_attributes[] = {
&dev_attr_led_pattern.attr,
@@ -493,6 +368,12 @@ static const struct attribute_group lp5562_group = {
/* Chip specific configurations */
static struct lp55xx_device_config lp5562_cfg = {
.max_channel = LP5562_MAX_LEDS,
+ .reg_op_mode = {
+ .addr = LP5562_REG_OP_MODE,
+ },
+ .reg_exec = {
+ .addr = LP5562_REG_ENABLE,
+ },
.reset = {
.addr = LP5562_REG_RESET,
.val = LP5562_RESET,
@@ -501,112 +382,38 @@ static struct lp55xx_device_config lp5562_cfg = {
.addr = LP5562_REG_ENABLE,
.val = LP5562_ENABLE_DEFAULT,
},
+ .prog_mem_base = {
+ .addr = LP5562_REG_PROG_MEM_ENG1,
+ },
.post_init_device = lp5562_post_init_device,
.set_led_current = lp5562_set_led_current,
- .brightness_work_fn = lp5562_led_brightness_work,
+ .brightness_fn = lp5562_led_brightness,
+ .multicolor_brightness_fn = lp5562_multicolor_brightness,
.run_engine = lp5562_run_engine,
- .firmware_cb = lp5562_firmware_loaded,
+ .firmware_cb = lp55xx_firmware_loaded_cb,
.dev_attr_group = &lp5562_group,
};
-static int lp5562_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- int ret;
- struct lp55xx_chip *chip;
- struct lp55xx_led *led;
- struct lp55xx_platform_data *pdata;
- struct device_node *np = client->dev.of_node;
-
- if (!client->dev.platform_data) {
- if (np) {
- ret = lp55xx_of_populate_pdata(&client->dev, np);
- if (ret < 0)
- return ret;
- } else {
- dev_err(&client->dev, "no platform data\n");
- return -EINVAL;
- }
- }
- pdata = client->dev.platform_data;
-
- chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
-
- led = devm_kzalloc(&client->dev,
- sizeof(*led) * pdata->num_channels, GFP_KERNEL);
- if (!led)
- return -ENOMEM;
-
- chip->cl = client;
- chip->pdata = pdata;
- chip->cfg = &lp5562_cfg;
-
- mutex_init(&chip->lock);
-
- i2c_set_clientdata(client, led);
-
- ret = lp55xx_init_device(chip);
- if (ret)
- goto err_init;
-
- ret = lp55xx_register_leds(led, chip);
- if (ret)
- goto err_register_leds;
-
- ret = lp55xx_register_sysfs(chip);
- if (ret) {
- dev_err(&client->dev, "registering sysfs failed\n");
- goto err_register_sysfs;
- }
-
- return 0;
-
-err_register_sysfs:
- lp55xx_unregister_leds(led, chip);
-err_register_leds:
- lp55xx_deinit_device(chip);
-err_init:
- return ret;
-}
-
-static int lp5562_remove(struct i2c_client *client)
-{
- struct lp55xx_led *led = i2c_get_clientdata(client);
- struct lp55xx_chip *chip = led->chip;
-
- lp5562_stop_engine(chip);
-
- lp55xx_unregister_sysfs(chip);
- lp55xx_unregister_leds(led, chip);
- lp55xx_deinit_device(chip);
-
- return 0;
-}
-
static const struct i2c_device_id lp5562_id[] = {
- { "lp5562", 0 },
+ { "lp5562", .driver_data = (kernel_ulong_t)&lp5562_cfg, },
{ }
};
MODULE_DEVICE_TABLE(i2c, lp5562_id);
-#ifdef CONFIG_OF
static const struct of_device_id of_lp5562_leds_match[] = {
- { .compatible = "ti,lp5562", },
+ { .compatible = "ti,lp5562", .data = &lp5562_cfg, },
{},
};
MODULE_DEVICE_TABLE(of, of_lp5562_leds_match);
-#endif
static struct i2c_driver lp5562_driver = {
.driver = {
.name = "lp5562",
- .of_match_table = of_match_ptr(of_lp5562_leds_match),
+ .of_match_table = of_lp5562_leds_match,
},
- .probe = lp5562_probe,
- .remove = lp5562_remove,
+ .probe = lp55xx_probe,
+ .remove = lp55xx_remove,
.id_table = lp5562_id,
};