diff options
Diffstat (limited to 'drivers/leds/leds-lp8501.c')
-rw-r--r-- | drivers/leds/leds-lp8501.c | 313 |
1 files changed, 34 insertions, 279 deletions
diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c index ac50aa88939a..ee4ff4586bc0 100644 --- a/drivers/leds/leds-lp8501.c +++ b/drivers/leds/leds-lp8501.c @@ -20,27 +20,14 @@ #include "leds-lp55xx-common.h" -#define LP8501_PROGRAM_LENGTH 32 +#define LP8501_PAGES_PER_ENGINE 1 #define LP8501_MAX_LEDS 9 /* Registers */ #define LP8501_REG_ENABLE 0x00 #define LP8501_ENABLE BIT(6) -#define LP8501_EXEC_M 0x3F -#define LP8501_EXEC_ENG1_M 0x30 -#define LP8501_EXEC_ENG2_M 0x0C -#define LP8501_EXEC_ENG3_M 0x03 -#define LP8501_RUN_ENG1 0x20 -#define LP8501_RUN_ENG2 0x08 -#define LP8501_RUN_ENG3 0x02 #define LP8501_REG_OP_MODE 0x01 -#define LP8501_MODE_ENG1_M 0x30 -#define LP8501_MODE_ENG2_M 0x0C -#define LP8501_MODE_ENG3_M 0x03 -#define LP8501_LOAD_ENG1 0x10 -#define LP8501_LOAD_ENG2 0x04 -#define LP8501_LOAD_ENG3 0x01 #define LP8501_REG_PWR_CONFIG 0x05 #define LP8501_PWR_CONFIG_M 0x03 @@ -58,35 +45,14 @@ #define LP8501_INT_CLK BIT(0) #define LP8501_DEFAULT_CFG (LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE) +#define LP8501_REG_STATUS 0x3A +#define LP8501_ENGINE_BUSY BIT(4) + #define LP8501_REG_RESET 0x3D #define LP8501_RESET 0xFF -#define LP8501_REG_PROG_PAGE_SEL 0x4F -#define LP8501_PAGE_ENG1 0 -#define LP8501_PAGE_ENG2 1 -#define LP8501_PAGE_ENG3 2 - #define LP8501_REG_PROG_MEM 0x50 -#define LP8501_ENG1_IS_LOADING(mode) \ - ((mode & LP8501_MODE_ENG1_M) == LP8501_LOAD_ENG1) -#define LP8501_ENG2_IS_LOADING(mode) \ - ((mode & LP8501_MODE_ENG2_M) == LP8501_LOAD_ENG2) -#define LP8501_ENG3_IS_LOADING(mode) \ - ((mode & LP8501_MODE_ENG3_M) == LP8501_LOAD_ENG3) - -static inline void lp8501_wait_opmode_done(void) -{ - usleep_range(1000, 2000); -} - -static void lp8501_set_led_current(struct lp55xx_led *led, u8 led_current) -{ - led->led_current = led_current; - lp55xx_write(led->chip, LP8501_REG_LED_CURRENT_BASE + led->chan_nr, - led_current); -} - static int lp8501_post_init_device(struct lp55xx_chip *chip) { int ret; @@ -113,178 +79,30 @@ static int lp8501_post_init_device(struct lp55xx_chip *chip) LP8501_PWR_CONFIG_M, chip->pdata->pwr_sel); } -static void lp8501_load_engine(struct lp55xx_chip *chip) -{ - enum lp55xx_engine_index idx = chip->engine_idx; - static const u8 mask[] = { - [LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M, - [LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M, - [LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M, - }; - - static const u8 val[] = { - [LP55XX_ENGINE_1] = LP8501_LOAD_ENG1, - [LP55XX_ENGINE_2] = LP8501_LOAD_ENG2, - [LP55XX_ENGINE_3] = LP8501_LOAD_ENG3, - }; - - static const u8 page_sel[] = { - [LP55XX_ENGINE_1] = LP8501_PAGE_ENG1, - [LP55XX_ENGINE_2] = LP8501_PAGE_ENG2, - [LP55XX_ENGINE_3] = LP8501_PAGE_ENG3, - }; - - lp55xx_update_bits(chip, LP8501_REG_OP_MODE, mask[idx], val[idx]); - - lp8501_wait_opmode_done(); - - lp55xx_write(chip, LP8501_REG_PROG_PAGE_SEL, page_sel[idx]); -} - -static void lp8501_stop_engine(struct lp55xx_chip *chip) -{ - lp55xx_write(chip, LP8501_REG_OP_MODE, 0); - lp8501_wait_opmode_done(); -} - -static void lp8501_turn_off_channels(struct lp55xx_chip *chip) -{ - int i; - - for (i = 0; i < LP8501_MAX_LEDS; i++) - lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + i, 0); -} - static void lp8501_run_engine(struct lp55xx_chip *chip, bool start) { - int ret; - u8 mode; - u8 exec; - /* stop engine */ if (!start) { - lp8501_stop_engine(chip); - lp8501_turn_off_channels(chip); - return; - } - - /* - * To run the engine, - * operation mode and enable register should updated at the same time - */ - - ret = lp55xx_read(chip, LP8501_REG_OP_MODE, &mode); - if (ret) - return; - - ret = lp55xx_read(chip, LP8501_REG_ENABLE, &exec); - if (ret) - return; - - /* change operation mode to RUN only when each engine is loading */ - if (LP8501_ENG1_IS_LOADING(mode)) { - mode = (mode & ~LP8501_MODE_ENG1_M) | LP8501_RUN_ENG1; - exec = (exec & ~LP8501_EXEC_ENG1_M) | LP8501_RUN_ENG1; - } - - if (LP8501_ENG2_IS_LOADING(mode)) { - mode = (mode & ~LP8501_MODE_ENG2_M) | LP8501_RUN_ENG2; - exec = (exec & ~LP8501_EXEC_ENG2_M) | LP8501_RUN_ENG2; - } - - if (LP8501_ENG3_IS_LOADING(mode)) { - mode = (mode & ~LP8501_MODE_ENG3_M) | LP8501_RUN_ENG3; - exec = (exec & ~LP8501_EXEC_ENG3_M) | LP8501_RUN_ENG3; - } - - lp55xx_write(chip, LP8501_REG_OP_MODE, mode); - lp8501_wait_opmode_done(); - - lp55xx_update_bits(chip, LP8501_REG_ENABLE, LP8501_EXEC_M, exec); -} - -static int lp8501_update_program_memory(struct lp55xx_chip *chip, - const u8 *data, size_t size) -{ - u8 pattern[LP8501_PROGRAM_LENGTH] = {0}; - unsigned cmd; - char c[3]; - int update_size; - int nrchars; - int offset = 0; - int ret; - int i; - - /* clear program memory before updating */ - for (i = 0; i < LP8501_PROGRAM_LENGTH; i++) - lp55xx_write(chip, LP8501_REG_PROG_MEM + i, 0); - - i = 0; - while ((offset < size - 1) && (i < LP8501_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; - - update_size = i; - for (i = 0; i < update_size; i++) - lp55xx_write(chip, LP8501_REG_PROG_MEM + i, pattern[i]); - - return 0; - -err: - dev_err(&chip->cl->dev, "wrong pattern format\n"); - return -EINVAL; -} - -static void lp8501_firmware_loaded(struct lp55xx_chip *chip) -{ - const struct firmware *fw = chip->fw; - - if (fw->size > LP8501_PROGRAM_LENGTH) { - dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", - fw->size); + lp55xx_stop_all_engine(chip); + lp55xx_turn_off_channels(chip); return; } - /* - * Program memory sequence - * 1) set engine mode to "LOAD" - * 2) write firmware data into program memory - */ - - lp8501_load_engine(chip); - lp8501_update_program_memory(chip, fw->data, fw->size); -} - -static int lp8501_led_brightness(struct lp55xx_led *led) -{ - struct lp55xx_chip *chip = led->chip; - int ret; - - mutex_lock(&chip->lock); - ret = lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr, - led->brightness); - mutex_unlock(&chip->lock); - - return ret; + lp55xx_run_engine_common(chip); } /* Chip specific configurations */ static struct lp55xx_device_config lp8501_cfg = { + .reg_op_mode = { + .addr = LP8501_REG_OP_MODE, + }, + .reg_exec = { + .addr = LP8501_REG_ENABLE, + }, + .engine_busy = { + .addr = LP8501_REG_STATUS, + .mask = LP8501_ENGINE_BUSY, + }, .reset = { .addr = LP8501_REG_RESET, .val = LP8501_RESET, @@ -293,95 +111,32 @@ static struct lp55xx_device_config lp8501_cfg = { .addr = LP8501_REG_ENABLE, .val = LP8501_ENABLE, }, + .prog_mem_base = { + .addr = LP8501_REG_PROG_MEM, + }, + .reg_led_pwm_base = { + .addr = LP8501_REG_LED_PWM_BASE, + }, + .reg_led_current_base = { + .addr = LP8501_REG_LED_CURRENT_BASE, + }, + .pages_per_engine = LP8501_PAGES_PER_ENGINE, .max_channel = LP8501_MAX_LEDS, .post_init_device = lp8501_post_init_device, - .brightness_fn = lp8501_led_brightness, - .set_led_current = lp8501_set_led_current, - .firmware_cb = lp8501_firmware_loaded, + .brightness_fn = lp55xx_led_brightness, + .set_led_current = lp55xx_set_led_current, + .firmware_cb = lp55xx_firmware_loaded_cb, .run_engine = lp8501_run_engine, }; -static int lp8501_probe(struct i2c_client *client) -{ - const struct i2c_device_id *id = i2c_client_get_device_id(client); - int ret; - struct lp55xx_chip *chip; - struct lp55xx_led *led; - struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev); - struct device_node *np = dev_of_node(&client->dev); - - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->cfg = &lp8501_cfg; - - if (!pdata) { - if (np) { - pdata = lp55xx_of_populate_pdata(&client->dev, np, - chip); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - } else { - dev_err(&client->dev, "no platform data\n"); - return -EINVAL; - } - } - - led = devm_kcalloc(&client->dev, - pdata->num_channels, sizeof(*led), GFP_KERNEL); - if (!led) - return -ENOMEM; - - chip->cl = client; - chip->pdata = pdata; - - mutex_init(&chip->lock); - - i2c_set_clientdata(client, led); - - ret = lp55xx_init_device(chip); - if (ret) - goto err_init; - - dev_info(&client->dev, "%s Programmable led chip found\n", id->name); - - ret = lp55xx_register_leds(led, chip); - if (ret) - goto err_out; - - ret = lp55xx_register_sysfs(chip); - if (ret) { - dev_err(&client->dev, "registering sysfs failed\n"); - goto err_out; - } - - return 0; - -err_out: - lp55xx_deinit_device(chip); -err_init: - return ret; -} - -static void lp8501_remove(struct i2c_client *client) -{ - struct lp55xx_led *led = i2c_get_clientdata(client); - struct lp55xx_chip *chip = led->chip; - - lp8501_stop_engine(chip); - lp55xx_unregister_sysfs(chip); - lp55xx_deinit_device(chip); -} - static const struct i2c_device_id lp8501_id[] = { - { "lp8501", 0 }, + { "lp8501", .driver_data = (kernel_ulong_t)&lp8501_cfg, }, { } }; MODULE_DEVICE_TABLE(i2c, lp8501_id); static const struct of_device_id of_lp8501_leds_match[] = { - { .compatible = "ti,lp8501", }, + { .compatible = "ti,lp8501", .data = &lp8501_cfg, }, {}, }; @@ -392,8 +147,8 @@ static struct i2c_driver lp8501_driver = { .name = "lp8501", .of_match_table = of_lp8501_leds_match, }, - .probe = lp8501_probe, - .remove = lp8501_remove, + .probe = lp55xx_probe, + .remove = lp55xx_remove, .id_table = lp8501_id, }; |