162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci* Simple driver for Texas Instruments LM355x LED Flash driver chip 462306a36Sopenharmony_ci* Copyright (C) 2012 Texas Instruments 562306a36Sopenharmony_ci*/ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/i2c.h> 1062306a36Sopenharmony_ci#include <linux/leds.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci#include <linux/platform_data/leds-lm355x.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cienum lm355x_type { 1862306a36Sopenharmony_ci CHIP_LM3554 = 0, 1962306a36Sopenharmony_ci CHIP_LM3556, 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cienum lm355x_regs { 2362306a36Sopenharmony_ci REG_FLAG = 0, 2462306a36Sopenharmony_ci REG_TORCH_CFG, 2562306a36Sopenharmony_ci REG_TORCH_CTRL, 2662306a36Sopenharmony_ci REG_STROBE_CFG, 2762306a36Sopenharmony_ci REG_FLASH_CTRL, 2862306a36Sopenharmony_ci REG_INDI_CFG, 2962306a36Sopenharmony_ci REG_INDI_CTRL, 3062306a36Sopenharmony_ci REG_OPMODE, 3162306a36Sopenharmony_ci REG_MAX, 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* operation mode */ 3562306a36Sopenharmony_cienum lm355x_mode { 3662306a36Sopenharmony_ci MODE_SHDN = 0, 3762306a36Sopenharmony_ci MODE_INDIC, 3862306a36Sopenharmony_ci MODE_TORCH, 3962306a36Sopenharmony_ci MODE_FLASH 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* register map info. */ 4362306a36Sopenharmony_cistruct lm355x_reg_data { 4462306a36Sopenharmony_ci u8 regno; 4562306a36Sopenharmony_ci u8 mask; 4662306a36Sopenharmony_ci u8 shift; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct lm355x_chip_data { 5062306a36Sopenharmony_ci struct device *dev; 5162306a36Sopenharmony_ci enum lm355x_type type; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci struct led_classdev cdev_flash; 5462306a36Sopenharmony_ci struct led_classdev cdev_torch; 5562306a36Sopenharmony_ci struct led_classdev cdev_indicator; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci struct lm355x_platform_data *pdata; 5862306a36Sopenharmony_ci struct regmap *regmap; 5962306a36Sopenharmony_ci struct mutex lock; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci unsigned int last_flag; 6262306a36Sopenharmony_ci struct lm355x_reg_data *regs; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* specific indicator function for lm3556 */ 6662306a36Sopenharmony_cienum lm3556_indic_pulse_time { 6762306a36Sopenharmony_ci PULSE_TIME_0_MS = 0, 6862306a36Sopenharmony_ci PULSE_TIME_32_MS, 6962306a36Sopenharmony_ci PULSE_TIME_64_MS, 7062306a36Sopenharmony_ci PULSE_TIME_92_MS, 7162306a36Sopenharmony_ci PULSE_TIME_128_MS, 7262306a36Sopenharmony_ci PULSE_TIME_160_MS, 7362306a36Sopenharmony_ci PULSE_TIME_196_MS, 7462306a36Sopenharmony_ci PULSE_TIME_224_MS, 7562306a36Sopenharmony_ci PULSE_TIME_256_MS, 7662306a36Sopenharmony_ci PULSE_TIME_288_MS, 7762306a36Sopenharmony_ci PULSE_TIME_320_MS, 7862306a36Sopenharmony_ci PULSE_TIME_352_MS, 7962306a36Sopenharmony_ci PULSE_TIME_384_MS, 8062306a36Sopenharmony_ci PULSE_TIME_416_MS, 8162306a36Sopenharmony_ci PULSE_TIME_448_MS, 8262306a36Sopenharmony_ci PULSE_TIME_480_MS, 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cienum lm3556_indic_n_blank { 8662306a36Sopenharmony_ci INDIC_N_BLANK_0 = 0, 8762306a36Sopenharmony_ci INDIC_N_BLANK_1, 8862306a36Sopenharmony_ci INDIC_N_BLANK_2, 8962306a36Sopenharmony_ci INDIC_N_BLANK_3, 9062306a36Sopenharmony_ci INDIC_N_BLANK_4, 9162306a36Sopenharmony_ci INDIC_N_BLANK_5, 9262306a36Sopenharmony_ci INDIC_N_BLANK_6, 9362306a36Sopenharmony_ci INDIC_N_BLANK_7, 9462306a36Sopenharmony_ci INDIC_N_BLANK_8, 9562306a36Sopenharmony_ci INDIC_N_BLANK_9, 9662306a36Sopenharmony_ci INDIC_N_BLANK_10, 9762306a36Sopenharmony_ci INDIC_N_BLANK_11, 9862306a36Sopenharmony_ci INDIC_N_BLANK_12, 9962306a36Sopenharmony_ci INDIC_N_BLANK_13, 10062306a36Sopenharmony_ci INDIC_N_BLANK_14, 10162306a36Sopenharmony_ci INDIC_N_BLANK_15, 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cienum lm3556_indic_period { 10562306a36Sopenharmony_ci INDIC_PERIOD_0 = 0, 10662306a36Sopenharmony_ci INDIC_PERIOD_1, 10762306a36Sopenharmony_ci INDIC_PERIOD_2, 10862306a36Sopenharmony_ci INDIC_PERIOD_3, 10962306a36Sopenharmony_ci INDIC_PERIOD_4, 11062306a36Sopenharmony_ci INDIC_PERIOD_5, 11162306a36Sopenharmony_ci INDIC_PERIOD_6, 11262306a36Sopenharmony_ci INDIC_PERIOD_7, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define INDIC_PATTERN_SIZE 4 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistruct indicator { 11862306a36Sopenharmony_ci u8 blinking; 11962306a36Sopenharmony_ci u8 period_cnt; 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* indicator pattern data only for lm3556 */ 12362306a36Sopenharmony_cistatic struct indicator indicator_pattern[INDIC_PATTERN_SIZE] = { 12462306a36Sopenharmony_ci [0] = {(INDIC_N_BLANK_1 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_1}, 12562306a36Sopenharmony_ci [1] = {(INDIC_N_BLANK_15 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_2}, 12662306a36Sopenharmony_ci [2] = {(INDIC_N_BLANK_10 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_4}, 12762306a36Sopenharmony_ci [3] = {(INDIC_N_BLANK_5 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_7}, 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic struct lm355x_reg_data lm3554_regs[REG_MAX] = { 13162306a36Sopenharmony_ci [REG_FLAG] = {0xD0, 0xBF, 0}, 13262306a36Sopenharmony_ci [REG_TORCH_CFG] = {0xE0, 0x80, 7}, 13362306a36Sopenharmony_ci [REG_TORCH_CTRL] = {0xA0, 0x38, 3}, 13462306a36Sopenharmony_ci [REG_STROBE_CFG] = {0xE0, 0x04, 2}, 13562306a36Sopenharmony_ci [REG_FLASH_CTRL] = {0xB0, 0x78, 3}, 13662306a36Sopenharmony_ci [REG_INDI_CFG] = {0xE0, 0x08, 3}, 13762306a36Sopenharmony_ci [REG_INDI_CTRL] = {0xA0, 0xC0, 6}, 13862306a36Sopenharmony_ci [REG_OPMODE] = {0xA0, 0x03, 0}, 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic struct lm355x_reg_data lm3556_regs[REG_MAX] = { 14262306a36Sopenharmony_ci [REG_FLAG] = {0x0B, 0xFF, 0}, 14362306a36Sopenharmony_ci [REG_TORCH_CFG] = {0x0A, 0x10, 4}, 14462306a36Sopenharmony_ci [REG_TORCH_CTRL] = {0x09, 0x70, 4}, 14562306a36Sopenharmony_ci [REG_STROBE_CFG] = {0x0A, 0x20, 5}, 14662306a36Sopenharmony_ci [REG_FLASH_CTRL] = {0x09, 0x0F, 0}, 14762306a36Sopenharmony_ci [REG_INDI_CFG] = {0xFF, 0xFF, 0}, 14862306a36Sopenharmony_ci [REG_INDI_CTRL] = {0x09, 0x70, 4}, 14962306a36Sopenharmony_ci [REG_OPMODE] = {0x0A, 0x03, 0}, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic char lm355x_name[][I2C_NAME_SIZE] = { 15362306a36Sopenharmony_ci [CHIP_LM3554] = LM3554_NAME, 15462306a36Sopenharmony_ci [CHIP_LM3556] = LM3556_NAME, 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* chip initialize */ 15862306a36Sopenharmony_cistatic int lm355x_chip_init(struct lm355x_chip_data *chip) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci int ret; 16162306a36Sopenharmony_ci unsigned int reg_val; 16262306a36Sopenharmony_ci struct lm355x_platform_data *pdata = chip->pdata; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* input and output pins configuration */ 16562306a36Sopenharmony_ci switch (chip->type) { 16662306a36Sopenharmony_ci case CHIP_LM3554: 16762306a36Sopenharmony_ci reg_val = (u32)pdata->pin_tx2 | (u32)pdata->ntc_pin; 16862306a36Sopenharmony_ci ret = regmap_update_bits(chip->regmap, 0xE0, 0x28, reg_val); 16962306a36Sopenharmony_ci if (ret < 0) 17062306a36Sopenharmony_ci goto out; 17162306a36Sopenharmony_ci reg_val = (u32)pdata->pass_mode; 17262306a36Sopenharmony_ci ret = regmap_update_bits(chip->regmap, 0xA0, 0x04, reg_val); 17362306a36Sopenharmony_ci if (ret < 0) 17462306a36Sopenharmony_ci goto out; 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci case CHIP_LM3556: 17862306a36Sopenharmony_ci reg_val = (u32)pdata->pin_tx2 | (u32)pdata->ntc_pin | 17962306a36Sopenharmony_ci (u32)pdata->pass_mode; 18062306a36Sopenharmony_ci ret = regmap_update_bits(chip->regmap, 0x0A, 0xC4, reg_val); 18162306a36Sopenharmony_ci if (ret < 0) 18262306a36Sopenharmony_ci goto out; 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci default: 18562306a36Sopenharmony_ci return -ENODATA; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ciout: 19062306a36Sopenharmony_ci dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* chip control */ 19562306a36Sopenharmony_cistatic int lm355x_control(struct lm355x_chip_data *chip, 19662306a36Sopenharmony_ci u8 brightness, enum lm355x_mode opmode) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci int ret; 19962306a36Sopenharmony_ci unsigned int reg_val; 20062306a36Sopenharmony_ci struct lm355x_platform_data *pdata = chip->pdata; 20162306a36Sopenharmony_ci struct lm355x_reg_data *preg = chip->regs; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci ret = regmap_read(chip->regmap, preg[REG_FLAG].regno, &chip->last_flag); 20462306a36Sopenharmony_ci if (ret < 0) 20562306a36Sopenharmony_ci goto out; 20662306a36Sopenharmony_ci if (chip->last_flag & preg[REG_FLAG].mask) 20762306a36Sopenharmony_ci dev_info(chip->dev, "%s Last FLAG is 0x%x\n", 20862306a36Sopenharmony_ci lm355x_name[chip->type], 20962306a36Sopenharmony_ci chip->last_flag & preg[REG_FLAG].mask); 21062306a36Sopenharmony_ci /* brightness 0 means shutdown */ 21162306a36Sopenharmony_ci if (!brightness) 21262306a36Sopenharmony_ci opmode = MODE_SHDN; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci switch (opmode) { 21562306a36Sopenharmony_ci case MODE_TORCH: 21662306a36Sopenharmony_ci ret = 21762306a36Sopenharmony_ci regmap_update_bits(chip->regmap, preg[REG_TORCH_CTRL].regno, 21862306a36Sopenharmony_ci preg[REG_TORCH_CTRL].mask, 21962306a36Sopenharmony_ci (brightness - 1) 22062306a36Sopenharmony_ci << preg[REG_TORCH_CTRL].shift); 22162306a36Sopenharmony_ci if (ret < 0) 22262306a36Sopenharmony_ci goto out; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (pdata->pin_tx1 != LM355x_PIN_TORCH_DISABLE) { 22562306a36Sopenharmony_ci ret = 22662306a36Sopenharmony_ci regmap_update_bits(chip->regmap, 22762306a36Sopenharmony_ci preg[REG_TORCH_CFG].regno, 22862306a36Sopenharmony_ci preg[REG_TORCH_CFG].mask, 22962306a36Sopenharmony_ci 0x01 << 23062306a36Sopenharmony_ci preg[REG_TORCH_CFG].shift); 23162306a36Sopenharmony_ci if (ret < 0) 23262306a36Sopenharmony_ci goto out; 23362306a36Sopenharmony_ci opmode = MODE_SHDN; 23462306a36Sopenharmony_ci dev_info(chip->dev, 23562306a36Sopenharmony_ci "torch brt is set - ext. torch pin mode\n"); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci case MODE_FLASH: 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ret = 24262306a36Sopenharmony_ci regmap_update_bits(chip->regmap, preg[REG_FLASH_CTRL].regno, 24362306a36Sopenharmony_ci preg[REG_FLASH_CTRL].mask, 24462306a36Sopenharmony_ci (brightness - 1) 24562306a36Sopenharmony_ci << preg[REG_FLASH_CTRL].shift); 24662306a36Sopenharmony_ci if (ret < 0) 24762306a36Sopenharmony_ci goto out; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (pdata->pin_strobe != LM355x_PIN_STROBE_DISABLE) { 25062306a36Sopenharmony_ci if (chip->type == CHIP_LM3554) 25162306a36Sopenharmony_ci reg_val = 0x00; 25262306a36Sopenharmony_ci else 25362306a36Sopenharmony_ci reg_val = 0x01; 25462306a36Sopenharmony_ci ret = 25562306a36Sopenharmony_ci regmap_update_bits(chip->regmap, 25662306a36Sopenharmony_ci preg[REG_STROBE_CFG].regno, 25762306a36Sopenharmony_ci preg[REG_STROBE_CFG].mask, 25862306a36Sopenharmony_ci reg_val << 25962306a36Sopenharmony_ci preg[REG_STROBE_CFG].shift); 26062306a36Sopenharmony_ci if (ret < 0) 26162306a36Sopenharmony_ci goto out; 26262306a36Sopenharmony_ci opmode = MODE_SHDN; 26362306a36Sopenharmony_ci dev_info(chip->dev, 26462306a36Sopenharmony_ci "flash brt is set - ext. strobe pin mode\n"); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci case MODE_INDIC: 26962306a36Sopenharmony_ci ret = 27062306a36Sopenharmony_ci regmap_update_bits(chip->regmap, preg[REG_INDI_CTRL].regno, 27162306a36Sopenharmony_ci preg[REG_INDI_CTRL].mask, 27262306a36Sopenharmony_ci (brightness - 1) 27362306a36Sopenharmony_ci << preg[REG_INDI_CTRL].shift); 27462306a36Sopenharmony_ci if (ret < 0) 27562306a36Sopenharmony_ci goto out; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (pdata->pin_tx2 != LM355x_PIN_TX_DISABLE) { 27862306a36Sopenharmony_ci ret = 27962306a36Sopenharmony_ci regmap_update_bits(chip->regmap, 28062306a36Sopenharmony_ci preg[REG_INDI_CFG].regno, 28162306a36Sopenharmony_ci preg[REG_INDI_CFG].mask, 28262306a36Sopenharmony_ci 0x01 << 28362306a36Sopenharmony_ci preg[REG_INDI_CFG].shift); 28462306a36Sopenharmony_ci if (ret < 0) 28562306a36Sopenharmony_ci goto out; 28662306a36Sopenharmony_ci opmode = MODE_SHDN; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci case MODE_SHDN: 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci default: 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci /* operation mode control */ 29562306a36Sopenharmony_ci ret = regmap_update_bits(chip->regmap, preg[REG_OPMODE].regno, 29662306a36Sopenharmony_ci preg[REG_OPMODE].mask, 29762306a36Sopenharmony_ci opmode << preg[REG_OPMODE].shift); 29862306a36Sopenharmony_ci if (ret < 0) 29962306a36Sopenharmony_ci goto out; 30062306a36Sopenharmony_ci return ret; 30162306a36Sopenharmony_ciout: 30262306a36Sopenharmony_ci dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); 30362306a36Sopenharmony_ci return ret; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* torch */ 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int lm355x_torch_brightness_set(struct led_classdev *cdev, 30962306a36Sopenharmony_ci enum led_brightness brightness) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct lm355x_chip_data *chip = 31262306a36Sopenharmony_ci container_of(cdev, struct lm355x_chip_data, cdev_torch); 31362306a36Sopenharmony_ci int ret; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci mutex_lock(&chip->lock); 31662306a36Sopenharmony_ci ret = lm355x_control(chip, brightness, MODE_TORCH); 31762306a36Sopenharmony_ci mutex_unlock(&chip->lock); 31862306a36Sopenharmony_ci return ret; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/* flash */ 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int lm355x_strobe_brightness_set(struct led_classdev *cdev, 32462306a36Sopenharmony_ci enum led_brightness brightness) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct lm355x_chip_data *chip = 32762306a36Sopenharmony_ci container_of(cdev, struct lm355x_chip_data, cdev_flash); 32862306a36Sopenharmony_ci int ret; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci mutex_lock(&chip->lock); 33162306a36Sopenharmony_ci ret = lm355x_control(chip, brightness, MODE_FLASH); 33262306a36Sopenharmony_ci mutex_unlock(&chip->lock); 33362306a36Sopenharmony_ci return ret; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/* indicator */ 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int lm355x_indicator_brightness_set(struct led_classdev *cdev, 33962306a36Sopenharmony_ci enum led_brightness brightness) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct lm355x_chip_data *chip = 34262306a36Sopenharmony_ci container_of(cdev, struct lm355x_chip_data, cdev_indicator); 34362306a36Sopenharmony_ci int ret; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci mutex_lock(&chip->lock); 34662306a36Sopenharmony_ci ret = lm355x_control(chip, brightness, MODE_INDIC); 34762306a36Sopenharmony_ci mutex_unlock(&chip->lock); 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/* indicator pattern only for lm3556*/ 35262306a36Sopenharmony_cistatic ssize_t pattern_store(struct device *dev, 35362306a36Sopenharmony_ci struct device_attribute *attr, 35462306a36Sopenharmony_ci const char *buf, size_t size) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci ssize_t ret; 35762306a36Sopenharmony_ci struct led_classdev *led_cdev = dev_get_drvdata(dev); 35862306a36Sopenharmony_ci struct lm355x_chip_data *chip = 35962306a36Sopenharmony_ci container_of(led_cdev, struct lm355x_chip_data, cdev_indicator); 36062306a36Sopenharmony_ci unsigned int state; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ret = kstrtouint(buf, 10, &state); 36362306a36Sopenharmony_ci if (ret) 36462306a36Sopenharmony_ci goto out; 36562306a36Sopenharmony_ci if (state > INDIC_PATTERN_SIZE - 1) 36662306a36Sopenharmony_ci state = INDIC_PATTERN_SIZE - 1; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci ret = regmap_write(chip->regmap, 0x04, 36962306a36Sopenharmony_ci indicator_pattern[state].blinking); 37062306a36Sopenharmony_ci if (ret < 0) 37162306a36Sopenharmony_ci goto out; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci ret = regmap_write(chip->regmap, 0x05, 37462306a36Sopenharmony_ci indicator_pattern[state].period_cnt); 37562306a36Sopenharmony_ci if (ret < 0) 37662306a36Sopenharmony_ci goto out; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return size; 37962306a36Sopenharmony_ciout: 38062306a36Sopenharmony_ci dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic DEVICE_ATTR_WO(pattern); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic struct attribute *lm355x_indicator_attrs[] = { 38762306a36Sopenharmony_ci &dev_attr_pattern.attr, 38862306a36Sopenharmony_ci NULL 38962306a36Sopenharmony_ci}; 39062306a36Sopenharmony_ciATTRIBUTE_GROUPS(lm355x_indicator); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic const struct regmap_config lm355x_regmap = { 39362306a36Sopenharmony_ci .reg_bits = 8, 39462306a36Sopenharmony_ci .val_bits = 8, 39562306a36Sopenharmony_ci .max_register = 0xFF, 39662306a36Sopenharmony_ci}; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/* module initialize */ 39962306a36Sopenharmony_cistatic int lm355x_probe(struct i2c_client *client) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci const struct i2c_device_id *id = i2c_client_get_device_id(client); 40262306a36Sopenharmony_ci struct lm355x_platform_data *pdata = dev_get_platdata(&client->dev); 40362306a36Sopenharmony_ci struct lm355x_chip_data *chip; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci int err; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 40862306a36Sopenharmony_ci dev_err(&client->dev, "i2c functionality check fail.\n"); 40962306a36Sopenharmony_ci return -EOPNOTSUPP; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (pdata == NULL) { 41362306a36Sopenharmony_ci dev_err(&client->dev, "needs Platform Data.\n"); 41462306a36Sopenharmony_ci return -ENODATA; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci chip = devm_kzalloc(&client->dev, 41862306a36Sopenharmony_ci sizeof(struct lm355x_chip_data), GFP_KERNEL); 41962306a36Sopenharmony_ci if (!chip) 42062306a36Sopenharmony_ci return -ENOMEM; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci chip->dev = &client->dev; 42362306a36Sopenharmony_ci chip->type = id->driver_data; 42462306a36Sopenharmony_ci switch (id->driver_data) { 42562306a36Sopenharmony_ci case CHIP_LM3554: 42662306a36Sopenharmony_ci chip->regs = lm3554_regs; 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci case CHIP_LM3556: 42962306a36Sopenharmony_ci chip->regs = lm3556_regs; 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci default: 43262306a36Sopenharmony_ci return -ENOSYS; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci chip->pdata = pdata; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci chip->regmap = devm_regmap_init_i2c(client, &lm355x_regmap); 43762306a36Sopenharmony_ci if (IS_ERR(chip->regmap)) { 43862306a36Sopenharmony_ci err = PTR_ERR(chip->regmap); 43962306a36Sopenharmony_ci dev_err(&client->dev, 44062306a36Sopenharmony_ci "Failed to allocate register map: %d\n", err); 44162306a36Sopenharmony_ci return err; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci mutex_init(&chip->lock); 44562306a36Sopenharmony_ci i2c_set_clientdata(client, chip); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci err = lm355x_chip_init(chip); 44862306a36Sopenharmony_ci if (err < 0) 44962306a36Sopenharmony_ci goto err_out; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* flash */ 45262306a36Sopenharmony_ci chip->cdev_flash.name = "flash"; 45362306a36Sopenharmony_ci chip->cdev_flash.max_brightness = 16; 45462306a36Sopenharmony_ci chip->cdev_flash.brightness_set_blocking = lm355x_strobe_brightness_set; 45562306a36Sopenharmony_ci chip->cdev_flash.default_trigger = "flash"; 45662306a36Sopenharmony_ci err = led_classdev_register(&client->dev, &chip->cdev_flash); 45762306a36Sopenharmony_ci if (err < 0) 45862306a36Sopenharmony_ci goto err_out; 45962306a36Sopenharmony_ci /* torch */ 46062306a36Sopenharmony_ci chip->cdev_torch.name = "torch"; 46162306a36Sopenharmony_ci chip->cdev_torch.max_brightness = 8; 46262306a36Sopenharmony_ci chip->cdev_torch.brightness_set_blocking = lm355x_torch_brightness_set; 46362306a36Sopenharmony_ci chip->cdev_torch.default_trigger = "torch"; 46462306a36Sopenharmony_ci err = led_classdev_register(&client->dev, &chip->cdev_torch); 46562306a36Sopenharmony_ci if (err < 0) 46662306a36Sopenharmony_ci goto err_create_torch_file; 46762306a36Sopenharmony_ci /* indicator */ 46862306a36Sopenharmony_ci chip->cdev_indicator.name = "indicator"; 46962306a36Sopenharmony_ci if (id->driver_data == CHIP_LM3554) 47062306a36Sopenharmony_ci chip->cdev_indicator.max_brightness = 4; 47162306a36Sopenharmony_ci else 47262306a36Sopenharmony_ci chip->cdev_indicator.max_brightness = 8; 47362306a36Sopenharmony_ci chip->cdev_indicator.brightness_set_blocking = 47462306a36Sopenharmony_ci lm355x_indicator_brightness_set; 47562306a36Sopenharmony_ci /* indicator pattern control only for LM3556 */ 47662306a36Sopenharmony_ci if (id->driver_data == CHIP_LM3556) 47762306a36Sopenharmony_ci chip->cdev_indicator.groups = lm355x_indicator_groups; 47862306a36Sopenharmony_ci err = led_classdev_register(&client->dev, &chip->cdev_indicator); 47962306a36Sopenharmony_ci if (err < 0) 48062306a36Sopenharmony_ci goto err_create_indicator_file; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci dev_info(&client->dev, "%s is initialized\n", 48362306a36Sopenharmony_ci lm355x_name[id->driver_data]); 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cierr_create_indicator_file: 48762306a36Sopenharmony_ci led_classdev_unregister(&chip->cdev_torch); 48862306a36Sopenharmony_cierr_create_torch_file: 48962306a36Sopenharmony_ci led_classdev_unregister(&chip->cdev_flash); 49062306a36Sopenharmony_cierr_out: 49162306a36Sopenharmony_ci return err; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic void lm355x_remove(struct i2c_client *client) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct lm355x_chip_data *chip = i2c_get_clientdata(client); 49762306a36Sopenharmony_ci struct lm355x_reg_data *preg = chip->regs; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci regmap_write(chip->regmap, preg[REG_OPMODE].regno, 0); 50062306a36Sopenharmony_ci led_classdev_unregister(&chip->cdev_indicator); 50162306a36Sopenharmony_ci led_classdev_unregister(&chip->cdev_torch); 50262306a36Sopenharmony_ci led_classdev_unregister(&chip->cdev_flash); 50362306a36Sopenharmony_ci dev_info(&client->dev, "%s is removed\n", lm355x_name[chip->type]); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic const struct i2c_device_id lm355x_id[] = { 50762306a36Sopenharmony_ci {LM3554_NAME, CHIP_LM3554}, 50862306a36Sopenharmony_ci {LM3556_NAME, CHIP_LM3556}, 50962306a36Sopenharmony_ci {} 51062306a36Sopenharmony_ci}; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lm355x_id); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic struct i2c_driver lm355x_i2c_driver = { 51562306a36Sopenharmony_ci .driver = { 51662306a36Sopenharmony_ci .name = LM355x_NAME, 51762306a36Sopenharmony_ci .pm = NULL, 51862306a36Sopenharmony_ci }, 51962306a36Sopenharmony_ci .probe = lm355x_probe, 52062306a36Sopenharmony_ci .remove = lm355x_remove, 52162306a36Sopenharmony_ci .id_table = lm355x_id, 52262306a36Sopenharmony_ci}; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cimodule_i2c_driver(lm355x_i2c_driver); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM355x"); 52762306a36Sopenharmony_ciMODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); 52862306a36Sopenharmony_ciMODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); 52962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 530