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