18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci* Simple driver for Texas Instruments LM3639 Backlight + Flash LED driver chip
48c2ecf20Sopenharmony_ci* Copyright (C) 2012 Texas Instruments
58c2ecf20Sopenharmony_ci*/
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci#include <linux/i2c.h>
98c2ecf20Sopenharmony_ci#include <linux/leds.h>
108c2ecf20Sopenharmony_ci#include <linux/backlight.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/regmap.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_data/lm3639_bl.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define REG_DEV_ID	0x00
198c2ecf20Sopenharmony_ci#define REG_CHECKSUM	0x01
208c2ecf20Sopenharmony_ci#define REG_BL_CONF_1	0x02
218c2ecf20Sopenharmony_ci#define REG_BL_CONF_2	0x03
228c2ecf20Sopenharmony_ci#define REG_BL_CONF_3	0x04
238c2ecf20Sopenharmony_ci#define REG_BL_CONF_4	0x05
248c2ecf20Sopenharmony_ci#define REG_FL_CONF_1	0x06
258c2ecf20Sopenharmony_ci#define REG_FL_CONF_2	0x07
268c2ecf20Sopenharmony_ci#define REG_FL_CONF_3	0x08
278c2ecf20Sopenharmony_ci#define REG_IO_CTRL	0x09
288c2ecf20Sopenharmony_ci#define REG_ENABLE	0x0A
298c2ecf20Sopenharmony_ci#define REG_FLAG	0x0B
308c2ecf20Sopenharmony_ci#define REG_MAX		REG_FLAG
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct lm3639_chip_data {
338c2ecf20Sopenharmony_ci	struct device *dev;
348c2ecf20Sopenharmony_ci	struct lm3639_platform_data *pdata;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	struct backlight_device *bled;
378c2ecf20Sopenharmony_ci	struct led_classdev cdev_flash;
388c2ecf20Sopenharmony_ci	struct led_classdev cdev_torch;
398c2ecf20Sopenharmony_ci	struct regmap *regmap;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	unsigned int bled_mode;
428c2ecf20Sopenharmony_ci	unsigned int bled_map;
438c2ecf20Sopenharmony_ci	unsigned int last_flag;
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* initialize chip */
478c2ecf20Sopenharmony_cistatic int lm3639_chip_init(struct lm3639_chip_data *pchip)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	int ret;
508c2ecf20Sopenharmony_ci	unsigned int reg_val;
518c2ecf20Sopenharmony_ci	struct lm3639_platform_data *pdata = pchip->pdata;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	/* input pins config. */
548c2ecf20Sopenharmony_ci	ret =
558c2ecf20Sopenharmony_ci	    regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x08,
568c2ecf20Sopenharmony_ci			       pdata->pin_pwm);
578c2ecf20Sopenharmony_ci	if (ret < 0)
588c2ecf20Sopenharmony_ci		goto out;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	reg_val = (pdata->pin_pwm & 0x40) | pdata->pin_strobe | pdata->pin_tx;
618c2ecf20Sopenharmony_ci	ret = regmap_update_bits(pchip->regmap, REG_IO_CTRL, 0x7C, reg_val);
628c2ecf20Sopenharmony_ci	if (ret < 0)
638c2ecf20Sopenharmony_ci		goto out;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* init brightness */
668c2ecf20Sopenharmony_ci	ret = regmap_write(pchip->regmap, REG_BL_CONF_4, pdata->init_brt_led);
678c2ecf20Sopenharmony_ci	if (ret < 0)
688c2ecf20Sopenharmony_ci		goto out;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	ret = regmap_write(pchip->regmap, REG_BL_CONF_3, pdata->init_brt_led);
718c2ecf20Sopenharmony_ci	if (ret < 0)
728c2ecf20Sopenharmony_ci		goto out;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* output pins config. */
758c2ecf20Sopenharmony_ci	if (!pdata->init_brt_led) {
768c2ecf20Sopenharmony_ci		reg_val = pdata->fled_pins;
778c2ecf20Sopenharmony_ci		reg_val |= pdata->bled_pins;
788c2ecf20Sopenharmony_ci	} else {
798c2ecf20Sopenharmony_ci		reg_val = pdata->fled_pins;
808c2ecf20Sopenharmony_ci		reg_val |= pdata->bled_pins | 0x01;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x79, reg_val);
848c2ecf20Sopenharmony_ci	if (ret < 0)
858c2ecf20Sopenharmony_ci		goto out;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return ret;
888c2ecf20Sopenharmony_ciout:
898c2ecf20Sopenharmony_ci	dev_err(pchip->dev, "i2c failed to access register\n");
908c2ecf20Sopenharmony_ci	return ret;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* update and get brightness */
948c2ecf20Sopenharmony_cistatic int lm3639_bled_update_status(struct backlight_device *bl)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	int ret;
978c2ecf20Sopenharmony_ci	unsigned int reg_val;
988c2ecf20Sopenharmony_ci	struct lm3639_chip_data *pchip = bl_get_data(bl);
998c2ecf20Sopenharmony_ci	struct lm3639_platform_data *pdata = pchip->pdata;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
1028c2ecf20Sopenharmony_ci	if (ret < 0)
1038c2ecf20Sopenharmony_ci		goto out;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (reg_val != 0)
1068c2ecf20Sopenharmony_ci		dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* pwm control */
1098c2ecf20Sopenharmony_ci	if (pdata->pin_pwm) {
1108c2ecf20Sopenharmony_ci		if (pdata->pwm_set_intensity)
1118c2ecf20Sopenharmony_ci			pdata->pwm_set_intensity(bl->props.brightness,
1128c2ecf20Sopenharmony_ci						 pdata->max_brt_led);
1138c2ecf20Sopenharmony_ci		else
1148c2ecf20Sopenharmony_ci			dev_err(pchip->dev,
1158c2ecf20Sopenharmony_ci				"No pwm control func. in plat-data\n");
1168c2ecf20Sopenharmony_ci		return bl->props.brightness;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* i2c control and set brigtness */
1208c2ecf20Sopenharmony_ci	ret = regmap_write(pchip->regmap, REG_BL_CONF_4, bl->props.brightness);
1218c2ecf20Sopenharmony_ci	if (ret < 0)
1228c2ecf20Sopenharmony_ci		goto out;
1238c2ecf20Sopenharmony_ci	ret = regmap_write(pchip->regmap, REG_BL_CONF_3, bl->props.brightness);
1248c2ecf20Sopenharmony_ci	if (ret < 0)
1258c2ecf20Sopenharmony_ci		goto out;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (!bl->props.brightness)
1288c2ecf20Sopenharmony_ci		ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x00);
1298c2ecf20Sopenharmony_ci	else
1308c2ecf20Sopenharmony_ci		ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x01);
1318c2ecf20Sopenharmony_ci	if (ret < 0)
1328c2ecf20Sopenharmony_ci		goto out;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	return bl->props.brightness;
1358c2ecf20Sopenharmony_ciout:
1368c2ecf20Sopenharmony_ci	dev_err(pchip->dev, "i2c failed to access registers\n");
1378c2ecf20Sopenharmony_ci	return bl->props.brightness;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int lm3639_bled_get_brightness(struct backlight_device *bl)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	int ret;
1438c2ecf20Sopenharmony_ci	unsigned int reg_val;
1448c2ecf20Sopenharmony_ci	struct lm3639_chip_data *pchip = bl_get_data(bl);
1458c2ecf20Sopenharmony_ci	struct lm3639_platform_data *pdata = pchip->pdata;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (pdata->pin_pwm) {
1488c2ecf20Sopenharmony_ci		if (pdata->pwm_get_intensity)
1498c2ecf20Sopenharmony_ci			bl->props.brightness = pdata->pwm_get_intensity();
1508c2ecf20Sopenharmony_ci		else
1518c2ecf20Sopenharmony_ci			dev_err(pchip->dev,
1528c2ecf20Sopenharmony_ci				"No pwm control func. in plat-data\n");
1538c2ecf20Sopenharmony_ci		return bl->props.brightness;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ret = regmap_read(pchip->regmap, REG_BL_CONF_1, &reg_val);
1578c2ecf20Sopenharmony_ci	if (ret < 0)
1588c2ecf20Sopenharmony_ci		goto out;
1598c2ecf20Sopenharmony_ci	if (reg_val & 0x10)
1608c2ecf20Sopenharmony_ci		ret = regmap_read(pchip->regmap, REG_BL_CONF_4, &reg_val);
1618c2ecf20Sopenharmony_ci	else
1628c2ecf20Sopenharmony_ci		ret = regmap_read(pchip->regmap, REG_BL_CONF_3, &reg_val);
1638c2ecf20Sopenharmony_ci	if (ret < 0)
1648c2ecf20Sopenharmony_ci		goto out;
1658c2ecf20Sopenharmony_ci	bl->props.brightness = reg_val;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return bl->props.brightness;
1688c2ecf20Sopenharmony_ciout:
1698c2ecf20Sopenharmony_ci	dev_err(pchip->dev, "i2c failed to access register\n");
1708c2ecf20Sopenharmony_ci	return bl->props.brightness;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic const struct backlight_ops lm3639_bled_ops = {
1748c2ecf20Sopenharmony_ci	.options = BL_CORE_SUSPENDRESUME,
1758c2ecf20Sopenharmony_ci	.update_status = lm3639_bled_update_status,
1768c2ecf20Sopenharmony_ci	.get_brightness = lm3639_bled_get_brightness,
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci/* backlight mapping mode */
1808c2ecf20Sopenharmony_cistatic ssize_t lm3639_bled_mode_store(struct device *dev,
1818c2ecf20Sopenharmony_ci				      struct device_attribute *devAttr,
1828c2ecf20Sopenharmony_ci				      const char *buf, size_t size)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	ssize_t ret;
1858c2ecf20Sopenharmony_ci	struct lm3639_chip_data *pchip = dev_get_drvdata(dev);
1868c2ecf20Sopenharmony_ci	unsigned int state;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	ret = kstrtouint(buf, 10, &state);
1898c2ecf20Sopenharmony_ci	if (ret)
1908c2ecf20Sopenharmony_ci		goto out_input;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (!state)
1938c2ecf20Sopenharmony_ci		ret =
1948c2ecf20Sopenharmony_ci		    regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,
1958c2ecf20Sopenharmony_ci				       0x00);
1968c2ecf20Sopenharmony_ci	else
1978c2ecf20Sopenharmony_ci		ret =
1988c2ecf20Sopenharmony_ci		    regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,
1998c2ecf20Sopenharmony_ci				       0x10);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (ret < 0)
2028c2ecf20Sopenharmony_ci		goto out;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return size;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ciout:
2078c2ecf20Sopenharmony_ci	dev_err(pchip->dev, "%s:i2c access fail to register\n", __func__);
2088c2ecf20Sopenharmony_ci	return ret;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ciout_input:
2118c2ecf20Sopenharmony_ci	dev_err(pchip->dev, "%s:input conversion fail\n", __func__);
2128c2ecf20Sopenharmony_ci	return ret;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic DEVICE_ATTR(bled_mode, S_IWUSR, NULL, lm3639_bled_mode_store);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/* torch */
2198c2ecf20Sopenharmony_cistatic void lm3639_torch_brightness_set(struct led_classdev *cdev,
2208c2ecf20Sopenharmony_ci					enum led_brightness brightness)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	int ret;
2238c2ecf20Sopenharmony_ci	unsigned int reg_val;
2248c2ecf20Sopenharmony_ci	struct lm3639_chip_data *pchip;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	pchip = container_of(cdev, struct lm3639_chip_data, cdev_torch);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
2298c2ecf20Sopenharmony_ci	if (ret < 0)
2308c2ecf20Sopenharmony_ci		goto out;
2318c2ecf20Sopenharmony_ci	if (reg_val != 0)
2328c2ecf20Sopenharmony_ci		dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* brightness 0 means off state */
2358c2ecf20Sopenharmony_ci	if (!brightness) {
2368c2ecf20Sopenharmony_ci		ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
2378c2ecf20Sopenharmony_ci		if (ret < 0)
2388c2ecf20Sopenharmony_ci			goto out;
2398c2ecf20Sopenharmony_ci		return;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	ret = regmap_update_bits(pchip->regmap,
2438c2ecf20Sopenharmony_ci				 REG_FL_CONF_1, 0x70, (brightness - 1) << 4);
2448c2ecf20Sopenharmony_ci	if (ret < 0)
2458c2ecf20Sopenharmony_ci		goto out;
2468c2ecf20Sopenharmony_ci	ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x02);
2478c2ecf20Sopenharmony_ci	if (ret < 0)
2488c2ecf20Sopenharmony_ci		goto out;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return;
2518c2ecf20Sopenharmony_ciout:
2528c2ecf20Sopenharmony_ci	dev_err(pchip->dev, "i2c failed to access register\n");
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci/* flash */
2568c2ecf20Sopenharmony_cistatic void lm3639_flash_brightness_set(struct led_classdev *cdev,
2578c2ecf20Sopenharmony_ci					enum led_brightness brightness)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	int ret;
2608c2ecf20Sopenharmony_ci	unsigned int reg_val;
2618c2ecf20Sopenharmony_ci	struct lm3639_chip_data *pchip;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	pchip = container_of(cdev, struct lm3639_chip_data, cdev_flash);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
2668c2ecf20Sopenharmony_ci	if (ret < 0)
2678c2ecf20Sopenharmony_ci		goto out;
2688c2ecf20Sopenharmony_ci	if (reg_val != 0)
2698c2ecf20Sopenharmony_ci		dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/* torch off before flash control */
2728c2ecf20Sopenharmony_ci	ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
2738c2ecf20Sopenharmony_ci	if (ret < 0)
2748c2ecf20Sopenharmony_ci		goto out;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* brightness 0 means off state */
2778c2ecf20Sopenharmony_ci	if (!brightness)
2788c2ecf20Sopenharmony_ci		return;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	ret = regmap_update_bits(pchip->regmap,
2818c2ecf20Sopenharmony_ci				 REG_FL_CONF_1, 0x0F, brightness - 1);
2828c2ecf20Sopenharmony_ci	if (ret < 0)
2838c2ecf20Sopenharmony_ci		goto out;
2848c2ecf20Sopenharmony_ci	ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x06);
2858c2ecf20Sopenharmony_ci	if (ret < 0)
2868c2ecf20Sopenharmony_ci		goto out;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return;
2898c2ecf20Sopenharmony_ciout:
2908c2ecf20Sopenharmony_ci	dev_err(pchip->dev, "i2c failed to access register\n");
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic const struct regmap_config lm3639_regmap = {
2948c2ecf20Sopenharmony_ci	.reg_bits = 8,
2958c2ecf20Sopenharmony_ci	.val_bits = 8,
2968c2ecf20Sopenharmony_ci	.max_register = REG_MAX,
2978c2ecf20Sopenharmony_ci};
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic int lm3639_probe(struct i2c_client *client,
3008c2ecf20Sopenharmony_ci				  const struct i2c_device_id *id)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	int ret;
3038c2ecf20Sopenharmony_ci	struct lm3639_chip_data *pchip;
3048c2ecf20Sopenharmony_ci	struct lm3639_platform_data *pdata = dev_get_platdata(&client->dev);
3058c2ecf20Sopenharmony_ci	struct backlight_properties props;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
3088c2ecf20Sopenharmony_ci		dev_err(&client->dev, "i2c functionality check fail.\n");
3098c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (pdata == NULL) {
3138c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Needs Platform Data.\n");
3148c2ecf20Sopenharmony_ci		return -ENODATA;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	pchip = devm_kzalloc(&client->dev,
3188c2ecf20Sopenharmony_ci			     sizeof(struct lm3639_chip_data), GFP_KERNEL);
3198c2ecf20Sopenharmony_ci	if (!pchip)
3208c2ecf20Sopenharmony_ci		return -ENOMEM;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	pchip->pdata = pdata;
3238c2ecf20Sopenharmony_ci	pchip->dev = &client->dev;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	pchip->regmap = devm_regmap_init_i2c(client, &lm3639_regmap);
3268c2ecf20Sopenharmony_ci	if (IS_ERR(pchip->regmap)) {
3278c2ecf20Sopenharmony_ci		ret = PTR_ERR(pchip->regmap);
3288c2ecf20Sopenharmony_ci		dev_err(&client->dev, "fail : allocate register map: %d\n",
3298c2ecf20Sopenharmony_ci			ret);
3308c2ecf20Sopenharmony_ci		return ret;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, pchip);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/* chip initialize */
3358c2ecf20Sopenharmony_ci	ret = lm3639_chip_init(pchip);
3368c2ecf20Sopenharmony_ci	if (ret < 0) {
3378c2ecf20Sopenharmony_ci		dev_err(&client->dev, "fail : chip init\n");
3388c2ecf20Sopenharmony_ci		goto err_out;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/* backlight */
3428c2ecf20Sopenharmony_ci	props.type = BACKLIGHT_RAW;
3438c2ecf20Sopenharmony_ci	props.brightness = pdata->init_brt_led;
3448c2ecf20Sopenharmony_ci	props.max_brightness = pdata->max_brt_led;
3458c2ecf20Sopenharmony_ci	pchip->bled =
3468c2ecf20Sopenharmony_ci	    devm_backlight_device_register(pchip->dev, "lm3639_bled",
3478c2ecf20Sopenharmony_ci					   pchip->dev, pchip, &lm3639_bled_ops,
3488c2ecf20Sopenharmony_ci					   &props);
3498c2ecf20Sopenharmony_ci	if (IS_ERR(pchip->bled)) {
3508c2ecf20Sopenharmony_ci		dev_err(&client->dev, "fail : backlight register\n");
3518c2ecf20Sopenharmony_ci		ret = PTR_ERR(pchip->bled);
3528c2ecf20Sopenharmony_ci		goto err_out;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode);
3568c2ecf20Sopenharmony_ci	if (ret < 0) {
3578c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed : add sysfs entries\n");
3588c2ecf20Sopenharmony_ci		goto err_out;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* flash */
3628c2ecf20Sopenharmony_ci	pchip->cdev_flash.name = "lm3639_flash";
3638c2ecf20Sopenharmony_ci	pchip->cdev_flash.max_brightness = 16;
3648c2ecf20Sopenharmony_ci	pchip->cdev_flash.brightness_set = lm3639_flash_brightness_set;
3658c2ecf20Sopenharmony_ci	ret = led_classdev_register((struct device *)
3668c2ecf20Sopenharmony_ci				    &client->dev, &pchip->cdev_flash);
3678c2ecf20Sopenharmony_ci	if (ret < 0) {
3688c2ecf20Sopenharmony_ci		dev_err(&client->dev, "fail : flash register\n");
3698c2ecf20Sopenharmony_ci		goto err_flash;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* torch */
3738c2ecf20Sopenharmony_ci	pchip->cdev_torch.name = "lm3639_torch";
3748c2ecf20Sopenharmony_ci	pchip->cdev_torch.max_brightness = 8;
3758c2ecf20Sopenharmony_ci	pchip->cdev_torch.brightness_set = lm3639_torch_brightness_set;
3768c2ecf20Sopenharmony_ci	ret = led_classdev_register((struct device *)
3778c2ecf20Sopenharmony_ci				    &client->dev, &pchip->cdev_torch);
3788c2ecf20Sopenharmony_ci	if (ret < 0) {
3798c2ecf20Sopenharmony_ci		dev_err(&client->dev, "fail : torch register\n");
3808c2ecf20Sopenharmony_ci		goto err_torch;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	return 0;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cierr_torch:
3868c2ecf20Sopenharmony_ci	led_classdev_unregister(&pchip->cdev_flash);
3878c2ecf20Sopenharmony_cierr_flash:
3888c2ecf20Sopenharmony_ci	device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
3898c2ecf20Sopenharmony_cierr_out:
3908c2ecf20Sopenharmony_ci	return ret;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int lm3639_remove(struct i2c_client *client)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct lm3639_chip_data *pchip = i2c_get_clientdata(client);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	regmap_write(pchip->regmap, REG_ENABLE, 0x00);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	led_classdev_unregister(&pchip->cdev_torch);
4008c2ecf20Sopenharmony_ci	led_classdev_unregister(&pchip->cdev_flash);
4018c2ecf20Sopenharmony_ci	if (pchip->bled)
4028c2ecf20Sopenharmony_ci		device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
4038c2ecf20Sopenharmony_ci	return 0;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic const struct i2c_device_id lm3639_id[] = {
4078c2ecf20Sopenharmony_ci	{LM3639_NAME, 0},
4088c2ecf20Sopenharmony_ci	{}
4098c2ecf20Sopenharmony_ci};
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lm3639_id);
4128c2ecf20Sopenharmony_cistatic struct i2c_driver lm3639_i2c_driver = {
4138c2ecf20Sopenharmony_ci	.driver = {
4148c2ecf20Sopenharmony_ci		   .name = LM3639_NAME,
4158c2ecf20Sopenharmony_ci		   },
4168c2ecf20Sopenharmony_ci	.probe = lm3639_probe,
4178c2ecf20Sopenharmony_ci	.remove = lm3639_remove,
4188c2ecf20Sopenharmony_ci	.id_table = lm3639_id,
4198c2ecf20Sopenharmony_ci};
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cimodule_i2c_driver(lm3639_i2c_driver);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639");
4248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
4258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
4268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
427