162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * LP5521 LED chip driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation 662306a36Sopenharmony_ci * Copyright (C) 2012 Texas Instruments 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> 962306a36Sopenharmony_ci * Milo(Woogyom) Kim <milo.kim@ti.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/firmware.h> 1462306a36Sopenharmony_ci#include <linux/i2c.h> 1562306a36Sopenharmony_ci#include <linux/leds.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/platform_data/leds-lp55xx.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "leds-lp55xx-common.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define LP5521_PROGRAM_LENGTH 32 2562306a36Sopenharmony_ci#define LP5521_MAX_LEDS 3 2662306a36Sopenharmony_ci#define LP5521_CMD_DIRECT 0x3F 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Registers */ 2962306a36Sopenharmony_ci#define LP5521_REG_ENABLE 0x00 3062306a36Sopenharmony_ci#define LP5521_REG_OP_MODE 0x01 3162306a36Sopenharmony_ci#define LP5521_REG_R_PWM 0x02 3262306a36Sopenharmony_ci#define LP5521_REG_G_PWM 0x03 3362306a36Sopenharmony_ci#define LP5521_REG_B_PWM 0x04 3462306a36Sopenharmony_ci#define LP5521_REG_R_CURRENT 0x05 3562306a36Sopenharmony_ci#define LP5521_REG_G_CURRENT 0x06 3662306a36Sopenharmony_ci#define LP5521_REG_B_CURRENT 0x07 3762306a36Sopenharmony_ci#define LP5521_REG_CONFIG 0x08 3862306a36Sopenharmony_ci#define LP5521_REG_STATUS 0x0C 3962306a36Sopenharmony_ci#define LP5521_REG_RESET 0x0D 4062306a36Sopenharmony_ci#define LP5521_REG_R_PROG_MEM 0x10 4162306a36Sopenharmony_ci#define LP5521_REG_G_PROG_MEM 0x30 4262306a36Sopenharmony_ci#define LP5521_REG_B_PROG_MEM 0x50 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Base register to set LED current */ 4562306a36Sopenharmony_ci#define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT 4662306a36Sopenharmony_ci/* Base register to set the brightness */ 4762306a36Sopenharmony_ci#define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Bits in ENABLE register */ 5062306a36Sopenharmony_ci#define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */ 5162306a36Sopenharmony_ci#define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */ 5262306a36Sopenharmony_ci#define LP5521_EXEC_RUN 0x2A 5362306a36Sopenharmony_ci#define LP5521_ENABLE_DEFAULT \ 5462306a36Sopenharmony_ci (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM) 5562306a36Sopenharmony_ci#define LP5521_ENABLE_RUN_PROGRAM \ 5662306a36Sopenharmony_ci (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* CONFIG register */ 5962306a36Sopenharmony_ci#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ 6062306a36Sopenharmony_ci#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ 6162306a36Sopenharmony_ci#define LP5521_CP_MODE_MASK 0x18 /* Charge pump mode */ 6262306a36Sopenharmony_ci#define LP5521_CP_MODE_SHIFT 3 6362306a36Sopenharmony_ci#define LP5521_R_TO_BATT 0x04 /* R out: 0 = CP, 1 = Vbat */ 6462306a36Sopenharmony_ci#define LP5521_CLK_INT 0x01 /* Internal clock */ 6562306a36Sopenharmony_ci#define LP5521_DEFAULT_CFG (LP5521_PWM_HF | LP5521_PWRSAVE_EN) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* Status */ 6862306a36Sopenharmony_ci#define LP5521_EXT_CLK_USED 0x08 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* default R channel current register value */ 7162306a36Sopenharmony_ci#define LP5521_REG_R_CURR_DEFAULT 0xAF 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Reset register value */ 7462306a36Sopenharmony_ci#define LP5521_RESET 0xFF 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Program Memory Operations */ 7762306a36Sopenharmony_ci#define LP5521_MODE_R_M 0x30 /* Operation Mode Register */ 7862306a36Sopenharmony_ci#define LP5521_MODE_G_M 0x0C 7962306a36Sopenharmony_ci#define LP5521_MODE_B_M 0x03 8062306a36Sopenharmony_ci#define LP5521_LOAD_R 0x10 8162306a36Sopenharmony_ci#define LP5521_LOAD_G 0x04 8262306a36Sopenharmony_ci#define LP5521_LOAD_B 0x01 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define LP5521_R_IS_LOADING(mode) \ 8562306a36Sopenharmony_ci ((mode & LP5521_MODE_R_M) == LP5521_LOAD_R) 8662306a36Sopenharmony_ci#define LP5521_G_IS_LOADING(mode) \ 8762306a36Sopenharmony_ci ((mode & LP5521_MODE_G_M) == LP5521_LOAD_G) 8862306a36Sopenharmony_ci#define LP5521_B_IS_LOADING(mode) \ 8962306a36Sopenharmony_ci ((mode & LP5521_MODE_B_M) == LP5521_LOAD_B) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define LP5521_EXEC_R_M 0x30 /* Enable Register */ 9262306a36Sopenharmony_ci#define LP5521_EXEC_G_M 0x0C 9362306a36Sopenharmony_ci#define LP5521_EXEC_B_M 0x03 9462306a36Sopenharmony_ci#define LP5521_EXEC_M 0x3F 9562306a36Sopenharmony_ci#define LP5521_RUN_R 0x20 9662306a36Sopenharmony_ci#define LP5521_RUN_G 0x08 9762306a36Sopenharmony_ci#define LP5521_RUN_B 0x02 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic inline void lp5521_wait_opmode_done(void) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci /* operation mode change needs to be longer than 153 us */ 10262306a36Sopenharmony_ci usleep_range(200, 300); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic inline void lp5521_wait_enable_done(void) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci /* it takes more 488 us to update ENABLE register */ 10862306a36Sopenharmony_ci usleep_range(500, 600); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci led->led_current = led_current; 11462306a36Sopenharmony_ci lp55xx_write(led->chip, LP5521_REG_LED_CURRENT_BASE + led->chan_nr, 11562306a36Sopenharmony_ci led_current); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void lp5521_load_engine(struct lp55xx_chip *chip) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci enum lp55xx_engine_index idx = chip->engine_idx; 12162306a36Sopenharmony_ci static const u8 mask[] = { 12262306a36Sopenharmony_ci [LP55XX_ENGINE_1] = LP5521_MODE_R_M, 12362306a36Sopenharmony_ci [LP55XX_ENGINE_2] = LP5521_MODE_G_M, 12462306a36Sopenharmony_ci [LP55XX_ENGINE_3] = LP5521_MODE_B_M, 12562306a36Sopenharmony_ci }; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci static const u8 val[] = { 12862306a36Sopenharmony_ci [LP55XX_ENGINE_1] = LP5521_LOAD_R, 12962306a36Sopenharmony_ci [LP55XX_ENGINE_2] = LP5521_LOAD_G, 13062306a36Sopenharmony_ci [LP55XX_ENGINE_3] = LP5521_LOAD_B, 13162306a36Sopenharmony_ci }; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], val[idx]); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci lp5521_wait_opmode_done(); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void lp5521_stop_all_engines(struct lp55xx_chip *chip) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci lp55xx_write(chip, LP5521_REG_OP_MODE, 0); 14162306a36Sopenharmony_ci lp5521_wait_opmode_done(); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void lp5521_stop_engine(struct lp55xx_chip *chip) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci enum lp55xx_engine_index idx = chip->engine_idx; 14762306a36Sopenharmony_ci static const u8 mask[] = { 14862306a36Sopenharmony_ci [LP55XX_ENGINE_1] = LP5521_MODE_R_M, 14962306a36Sopenharmony_ci [LP55XX_ENGINE_2] = LP5521_MODE_G_M, 15062306a36Sopenharmony_ci [LP55XX_ENGINE_3] = LP5521_MODE_B_M, 15162306a36Sopenharmony_ci }; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], 0); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci lp5521_wait_opmode_done(); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void lp5521_run_engine(struct lp55xx_chip *chip, bool start) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci int ret; 16162306a36Sopenharmony_ci u8 mode; 16262306a36Sopenharmony_ci u8 exec; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* stop engine */ 16562306a36Sopenharmony_ci if (!start) { 16662306a36Sopenharmony_ci lp5521_stop_engine(chip); 16762306a36Sopenharmony_ci lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); 16862306a36Sopenharmony_ci lp5521_wait_opmode_done(); 16962306a36Sopenharmony_ci return; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * To run the engine, 17462306a36Sopenharmony_ci * operation mode and enable register should updated at the same time 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = lp55xx_read(chip, LP5521_REG_OP_MODE, &mode); 17862306a36Sopenharmony_ci if (ret) 17962306a36Sopenharmony_ci return; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = lp55xx_read(chip, LP5521_REG_ENABLE, &exec); 18262306a36Sopenharmony_ci if (ret) 18362306a36Sopenharmony_ci return; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* change operation mode to RUN only when each engine is loading */ 18662306a36Sopenharmony_ci if (LP5521_R_IS_LOADING(mode)) { 18762306a36Sopenharmony_ci mode = (mode & ~LP5521_MODE_R_M) | LP5521_RUN_R; 18862306a36Sopenharmony_ci exec = (exec & ~LP5521_EXEC_R_M) | LP5521_RUN_R; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (LP5521_G_IS_LOADING(mode)) { 19262306a36Sopenharmony_ci mode = (mode & ~LP5521_MODE_G_M) | LP5521_RUN_G; 19362306a36Sopenharmony_ci exec = (exec & ~LP5521_EXEC_G_M) | LP5521_RUN_G; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (LP5521_B_IS_LOADING(mode)) { 19762306a36Sopenharmony_ci mode = (mode & ~LP5521_MODE_B_M) | LP5521_RUN_B; 19862306a36Sopenharmony_ci exec = (exec & ~LP5521_EXEC_B_M) | LP5521_RUN_B; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci lp55xx_write(chip, LP5521_REG_OP_MODE, mode); 20262306a36Sopenharmony_ci lp5521_wait_opmode_done(); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci lp55xx_update_bits(chip, LP5521_REG_ENABLE, LP5521_EXEC_M, exec); 20562306a36Sopenharmony_ci lp5521_wait_enable_done(); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int lp5521_update_program_memory(struct lp55xx_chip *chip, 20962306a36Sopenharmony_ci const u8 *data, size_t size) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci enum lp55xx_engine_index idx = chip->engine_idx; 21262306a36Sopenharmony_ci u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; 21362306a36Sopenharmony_ci static const u8 addr[] = { 21462306a36Sopenharmony_ci [LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM, 21562306a36Sopenharmony_ci [LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM, 21662306a36Sopenharmony_ci [LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM, 21762306a36Sopenharmony_ci }; 21862306a36Sopenharmony_ci unsigned cmd; 21962306a36Sopenharmony_ci char c[3]; 22062306a36Sopenharmony_ci int nrchars; 22162306a36Sopenharmony_ci int ret; 22262306a36Sopenharmony_ci int offset = 0; 22362306a36Sopenharmony_ci int i = 0; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) { 22662306a36Sopenharmony_ci /* separate sscanfs because length is working only for %s */ 22762306a36Sopenharmony_ci ret = sscanf(data + offset, "%2s%n ", c, &nrchars); 22862306a36Sopenharmony_ci if (ret != 1) 22962306a36Sopenharmony_ci goto err; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ret = sscanf(c, "%2x", &cmd); 23262306a36Sopenharmony_ci if (ret != 1) 23362306a36Sopenharmony_ci goto err; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci pattern[i] = (u8)cmd; 23662306a36Sopenharmony_ci offset += nrchars; 23762306a36Sopenharmony_ci i++; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Each instruction is 16bit long. Check that length is even */ 24162306a36Sopenharmony_ci if (i % 2) 24262306a36Sopenharmony_ci goto err; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci for (i = 0; i < LP5521_PROGRAM_LENGTH; i++) { 24562306a36Sopenharmony_ci ret = lp55xx_write(chip, addr[idx] + i, pattern[i]); 24662306a36Sopenharmony_ci if (ret) 24762306a36Sopenharmony_ci return -EINVAL; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return size; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cierr: 25362306a36Sopenharmony_ci dev_err(&chip->cl->dev, "wrong pattern format\n"); 25462306a36Sopenharmony_ci return -EINVAL; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void lp5521_firmware_loaded(struct lp55xx_chip *chip) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci const struct firmware *fw = chip->fw; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (fw->size > LP5521_PROGRAM_LENGTH) { 26262306a36Sopenharmony_ci dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", 26362306a36Sopenharmony_ci fw->size); 26462306a36Sopenharmony_ci return; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * Program memory sequence 26962306a36Sopenharmony_ci * 1) set engine mode to "LOAD" 27062306a36Sopenharmony_ci * 2) write firmware data into program memory 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci lp5521_load_engine(chip); 27462306a36Sopenharmony_ci lp5521_update_program_memory(chip, fw->data, fw->size); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int lp5521_post_init_device(struct lp55xx_chip *chip) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci int ret; 28062306a36Sopenharmony_ci u8 val; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * Make sure that the chip is reset by reading back the r channel 28462306a36Sopenharmony_ci * current reg. This is dummy read is required on some platforms - 28562306a36Sopenharmony_ci * otherwise further access to the R G B channels in the 28662306a36Sopenharmony_ci * LP5521_REG_ENABLE register will not have any effect - strange! 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci ret = lp55xx_read(chip, LP5521_REG_R_CURRENT, &val); 28962306a36Sopenharmony_ci if (ret) { 29062306a36Sopenharmony_ci dev_err(&chip->cl->dev, "error in resetting chip\n"); 29162306a36Sopenharmony_ci return ret; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci if (val != LP5521_REG_R_CURR_DEFAULT) { 29462306a36Sopenharmony_ci dev_err(&chip->cl->dev, 29562306a36Sopenharmony_ci "unexpected data in register (expected 0x%x got 0x%x)\n", 29662306a36Sopenharmony_ci LP5521_REG_R_CURR_DEFAULT, val); 29762306a36Sopenharmony_ci ret = -EINVAL; 29862306a36Sopenharmony_ci return ret; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci usleep_range(10000, 20000); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Set all PWMs to direct control mode */ 30362306a36Sopenharmony_ci ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Update configuration for the clock setting */ 30662306a36Sopenharmony_ci val = LP5521_DEFAULT_CFG; 30762306a36Sopenharmony_ci if (!lp55xx_is_extclk_used(chip)) 30862306a36Sopenharmony_ci val |= LP5521_CLK_INT; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci val |= (chip->pdata->charge_pump_mode << LP5521_CP_MODE_SHIFT) & LP5521_CP_MODE_MASK; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci ret = lp55xx_write(chip, LP5521_REG_CONFIG, val); 31362306a36Sopenharmony_ci if (ret) 31462306a36Sopenharmony_ci return ret; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* Initialize all channels PWM to zero -> leds off */ 31762306a36Sopenharmony_ci lp55xx_write(chip, LP5521_REG_R_PWM, 0); 31862306a36Sopenharmony_ci lp55xx_write(chip, LP5521_REG_G_PWM, 0); 31962306a36Sopenharmony_ci lp55xx_write(chip, LP5521_REG_B_PWM, 0); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* Set engines are set to run state when OP_MODE enables engines */ 32262306a36Sopenharmony_ci ret = lp55xx_write(chip, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); 32362306a36Sopenharmony_ci if (ret) 32462306a36Sopenharmony_ci return ret; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci lp5521_wait_enable_done(); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct lp55xx_platform_data *pdata = chip->pdata; 33462306a36Sopenharmony_ci int ret; 33562306a36Sopenharmony_ci u8 status; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci ret = lp55xx_read(chip, LP5521_REG_STATUS, &status); 33862306a36Sopenharmony_ci if (ret < 0) 33962306a36Sopenharmony_ci return ret; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (pdata->clock_mode != LP55XX_CLOCK_EXT) 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Check that ext clock is really in use if requested */ 34562306a36Sopenharmony_ci if ((status & LP5521_EXT_CLK_USED) == 0) 34662306a36Sopenharmony_ci return -EIO; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int lp5521_multicolor_brightness(struct lp55xx_led *led) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct lp55xx_chip *chip = led->chip; 35462306a36Sopenharmony_ci int ret; 35562306a36Sopenharmony_ci int i; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci mutex_lock(&chip->lock); 35862306a36Sopenharmony_ci for (i = 0; i < led->mc_cdev.num_colors; i++) { 35962306a36Sopenharmony_ci ret = lp55xx_write(chip, 36062306a36Sopenharmony_ci LP5521_REG_LED_PWM_BASE + 36162306a36Sopenharmony_ci led->mc_cdev.subled_info[i].channel, 36262306a36Sopenharmony_ci led->mc_cdev.subled_info[i].brightness); 36362306a36Sopenharmony_ci if (ret) 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci mutex_unlock(&chip->lock); 36762306a36Sopenharmony_ci return ret; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic int lp5521_led_brightness(struct lp55xx_led *led) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct lp55xx_chip *chip = led->chip; 37362306a36Sopenharmony_ci int ret; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci mutex_lock(&chip->lock); 37662306a36Sopenharmony_ci ret = lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr, 37762306a36Sopenharmony_ci led->brightness); 37862306a36Sopenharmony_ci mutex_unlock(&chip->lock); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return ret; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic ssize_t show_engine_mode(struct device *dev, 38462306a36Sopenharmony_ci struct device_attribute *attr, 38562306a36Sopenharmony_ci char *buf, int nr) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); 38862306a36Sopenharmony_ci struct lp55xx_chip *chip = led->chip; 38962306a36Sopenharmony_ci enum lp55xx_engine_mode mode = chip->engines[nr - 1].mode; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci switch (mode) { 39262306a36Sopenharmony_ci case LP55XX_ENGINE_RUN: 39362306a36Sopenharmony_ci return sprintf(buf, "run\n"); 39462306a36Sopenharmony_ci case LP55XX_ENGINE_LOAD: 39562306a36Sopenharmony_ci return sprintf(buf, "load\n"); 39662306a36Sopenharmony_ci case LP55XX_ENGINE_DISABLED: 39762306a36Sopenharmony_ci default: 39862306a36Sopenharmony_ci return sprintf(buf, "disabled\n"); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_cishow_mode(1) 40262306a36Sopenharmony_cishow_mode(2) 40362306a36Sopenharmony_cishow_mode(3) 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic ssize_t store_engine_mode(struct device *dev, 40662306a36Sopenharmony_ci struct device_attribute *attr, 40762306a36Sopenharmony_ci const char *buf, size_t len, int nr) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); 41062306a36Sopenharmony_ci struct lp55xx_chip *chip = led->chip; 41162306a36Sopenharmony_ci struct lp55xx_engine *engine = &chip->engines[nr - 1]; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci mutex_lock(&chip->lock); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci chip->engine_idx = nr; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (!strncmp(buf, "run", 3)) { 41862306a36Sopenharmony_ci lp5521_run_engine(chip, true); 41962306a36Sopenharmony_ci engine->mode = LP55XX_ENGINE_RUN; 42062306a36Sopenharmony_ci } else if (!strncmp(buf, "load", 4)) { 42162306a36Sopenharmony_ci lp5521_stop_engine(chip); 42262306a36Sopenharmony_ci lp5521_load_engine(chip); 42362306a36Sopenharmony_ci engine->mode = LP55XX_ENGINE_LOAD; 42462306a36Sopenharmony_ci } else if (!strncmp(buf, "disabled", 8)) { 42562306a36Sopenharmony_ci lp5521_stop_engine(chip); 42662306a36Sopenharmony_ci engine->mode = LP55XX_ENGINE_DISABLED; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci mutex_unlock(&chip->lock); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return len; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_cistore_mode(1) 43462306a36Sopenharmony_cistore_mode(2) 43562306a36Sopenharmony_cistore_mode(3) 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic ssize_t store_engine_load(struct device *dev, 43862306a36Sopenharmony_ci struct device_attribute *attr, 43962306a36Sopenharmony_ci const char *buf, size_t len, int nr) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); 44262306a36Sopenharmony_ci struct lp55xx_chip *chip = led->chip; 44362306a36Sopenharmony_ci int ret; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci mutex_lock(&chip->lock); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci chip->engine_idx = nr; 44862306a36Sopenharmony_ci lp5521_load_engine(chip); 44962306a36Sopenharmony_ci ret = lp5521_update_program_memory(chip, buf, len); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci mutex_unlock(&chip->lock); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return ret; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_cistore_load(1) 45662306a36Sopenharmony_cistore_load(2) 45762306a36Sopenharmony_cistore_load(3) 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic ssize_t lp5521_selftest(struct device *dev, 46062306a36Sopenharmony_ci struct device_attribute *attr, 46162306a36Sopenharmony_ci char *buf) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); 46462306a36Sopenharmony_ci struct lp55xx_chip *chip = led->chip; 46562306a36Sopenharmony_ci int ret; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci mutex_lock(&chip->lock); 46862306a36Sopenharmony_ci ret = lp5521_run_selftest(chip, buf); 46962306a36Sopenharmony_ci mutex_unlock(&chip->lock); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", ret ? "FAIL" : "OK"); 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci/* device attributes */ 47562306a36Sopenharmony_cistatic LP55XX_DEV_ATTR_RW(engine1_mode, show_engine1_mode, store_engine1_mode); 47662306a36Sopenharmony_cistatic LP55XX_DEV_ATTR_RW(engine2_mode, show_engine2_mode, store_engine2_mode); 47762306a36Sopenharmony_cistatic LP55XX_DEV_ATTR_RW(engine3_mode, show_engine3_mode, store_engine3_mode); 47862306a36Sopenharmony_cistatic LP55XX_DEV_ATTR_WO(engine1_load, store_engine1_load); 47962306a36Sopenharmony_cistatic LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load); 48062306a36Sopenharmony_cistatic LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load); 48162306a36Sopenharmony_cistatic LP55XX_DEV_ATTR_RO(selftest, lp5521_selftest); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic struct attribute *lp5521_attributes[] = { 48462306a36Sopenharmony_ci &dev_attr_engine1_mode.attr, 48562306a36Sopenharmony_ci &dev_attr_engine2_mode.attr, 48662306a36Sopenharmony_ci &dev_attr_engine3_mode.attr, 48762306a36Sopenharmony_ci &dev_attr_engine1_load.attr, 48862306a36Sopenharmony_ci &dev_attr_engine2_load.attr, 48962306a36Sopenharmony_ci &dev_attr_engine3_load.attr, 49062306a36Sopenharmony_ci &dev_attr_selftest.attr, 49162306a36Sopenharmony_ci NULL 49262306a36Sopenharmony_ci}; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic const struct attribute_group lp5521_group = { 49562306a36Sopenharmony_ci .attrs = lp5521_attributes, 49662306a36Sopenharmony_ci}; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* Chip specific configurations */ 49962306a36Sopenharmony_cistatic struct lp55xx_device_config lp5521_cfg = { 50062306a36Sopenharmony_ci .reset = { 50162306a36Sopenharmony_ci .addr = LP5521_REG_RESET, 50262306a36Sopenharmony_ci .val = LP5521_RESET, 50362306a36Sopenharmony_ci }, 50462306a36Sopenharmony_ci .enable = { 50562306a36Sopenharmony_ci .addr = LP5521_REG_ENABLE, 50662306a36Sopenharmony_ci .val = LP5521_ENABLE_DEFAULT, 50762306a36Sopenharmony_ci }, 50862306a36Sopenharmony_ci .max_channel = LP5521_MAX_LEDS, 50962306a36Sopenharmony_ci .post_init_device = lp5521_post_init_device, 51062306a36Sopenharmony_ci .brightness_fn = lp5521_led_brightness, 51162306a36Sopenharmony_ci .multicolor_brightness_fn = lp5521_multicolor_brightness, 51262306a36Sopenharmony_ci .set_led_current = lp5521_set_led_current, 51362306a36Sopenharmony_ci .firmware_cb = lp5521_firmware_loaded, 51462306a36Sopenharmony_ci .run_engine = lp5521_run_engine, 51562306a36Sopenharmony_ci .dev_attr_group = &lp5521_group, 51662306a36Sopenharmony_ci}; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int lp5521_probe(struct i2c_client *client) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci const struct i2c_device_id *id = i2c_client_get_device_id(client); 52162306a36Sopenharmony_ci int ret; 52262306a36Sopenharmony_ci struct lp55xx_chip *chip; 52362306a36Sopenharmony_ci struct lp55xx_led *led; 52462306a36Sopenharmony_ci struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev); 52562306a36Sopenharmony_ci struct device_node *np = dev_of_node(&client->dev); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); 52862306a36Sopenharmony_ci if (!chip) 52962306a36Sopenharmony_ci return -ENOMEM; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci chip->cfg = &lp5521_cfg; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (!pdata) { 53462306a36Sopenharmony_ci if (np) { 53562306a36Sopenharmony_ci pdata = lp55xx_of_populate_pdata(&client->dev, np, 53662306a36Sopenharmony_ci chip); 53762306a36Sopenharmony_ci if (IS_ERR(pdata)) 53862306a36Sopenharmony_ci return PTR_ERR(pdata); 53962306a36Sopenharmony_ci } else { 54062306a36Sopenharmony_ci dev_err(&client->dev, "no platform data\n"); 54162306a36Sopenharmony_ci return -EINVAL; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci led = devm_kcalloc(&client->dev, 54662306a36Sopenharmony_ci pdata->num_channels, sizeof(*led), GFP_KERNEL); 54762306a36Sopenharmony_ci if (!led) 54862306a36Sopenharmony_ci return -ENOMEM; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci chip->cl = client; 55162306a36Sopenharmony_ci chip->pdata = pdata; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci mutex_init(&chip->lock); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci i2c_set_clientdata(client, led); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci ret = lp55xx_init_device(chip); 55862306a36Sopenharmony_ci if (ret) 55962306a36Sopenharmony_ci goto err_init; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci dev_info(&client->dev, "%s programmable led chip found\n", id->name); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ret = lp55xx_register_leds(led, chip); 56462306a36Sopenharmony_ci if (ret) 56562306a36Sopenharmony_ci goto err_out; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci ret = lp55xx_register_sysfs(chip); 56862306a36Sopenharmony_ci if (ret) { 56962306a36Sopenharmony_ci dev_err(&client->dev, "registering sysfs failed\n"); 57062306a36Sopenharmony_ci goto err_out; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cierr_out: 57662306a36Sopenharmony_ci lp55xx_deinit_device(chip); 57762306a36Sopenharmony_cierr_init: 57862306a36Sopenharmony_ci return ret; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic void lp5521_remove(struct i2c_client *client) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct lp55xx_led *led = i2c_get_clientdata(client); 58462306a36Sopenharmony_ci struct lp55xx_chip *chip = led->chip; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci lp5521_stop_all_engines(chip); 58762306a36Sopenharmony_ci lp55xx_unregister_sysfs(chip); 58862306a36Sopenharmony_ci lp55xx_deinit_device(chip); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic const struct i2c_device_id lp5521_id[] = { 59262306a36Sopenharmony_ci { "lp5521", 0 }, /* Three channel chip */ 59362306a36Sopenharmony_ci { } 59462306a36Sopenharmony_ci}; 59562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lp5521_id); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic const struct of_device_id of_lp5521_leds_match[] = { 59862306a36Sopenharmony_ci { .compatible = "national,lp5521", }, 59962306a36Sopenharmony_ci {}, 60062306a36Sopenharmony_ci}; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_lp5521_leds_match); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic struct i2c_driver lp5521_driver = { 60562306a36Sopenharmony_ci .driver = { 60662306a36Sopenharmony_ci .name = "lp5521", 60762306a36Sopenharmony_ci .of_match_table = of_lp5521_leds_match, 60862306a36Sopenharmony_ci }, 60962306a36Sopenharmony_ci .probe = lp5521_probe, 61062306a36Sopenharmony_ci .remove = lp5521_remove, 61162306a36Sopenharmony_ci .id_table = lp5521_id, 61262306a36Sopenharmony_ci}; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cimodule_i2c_driver(lp5521_driver); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ciMODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo"); 61762306a36Sopenharmony_ciMODULE_AUTHOR("Milo Kim <milo.kim@ti.com>"); 61862306a36Sopenharmony_ciMODULE_DESCRIPTION("LP5521 LED engine"); 61962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 620