brintos

brintos / linux-shallow public Read only

0
0
Text · 10.2 KiB · 37ccc63 Raw
426 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3* Simple driver for Texas Instruments LM3639 Backlight + Flash LED driver chip4* Copyright (C) 2012 Texas Instruments5*/6#include <linux/module.h>7#include <linux/slab.h>8#include <linux/i2c.h>9#include <linux/leds.h>10#include <linux/backlight.h>11#include <linux/err.h>12#include <linux/delay.h>13#include <linux/uaccess.h>14#include <linux/interrupt.h>15#include <linux/regmap.h>16#include <linux/platform_data/lm3639_bl.h>17 18#define REG_DEV_ID	0x0019#define REG_CHECKSUM	0x0120#define REG_BL_CONF_1	0x0221#define REG_BL_CONF_2	0x0322#define REG_BL_CONF_3	0x0423#define REG_BL_CONF_4	0x0524#define REG_FL_CONF_1	0x0625#define REG_FL_CONF_2	0x0726#define REG_FL_CONF_3	0x0827#define REG_IO_CTRL	0x0928#define REG_ENABLE	0x0A29#define REG_FLAG	0x0B30#define REG_MAX		REG_FLAG31 32struct lm3639_chip_data {33	struct device *dev;34	struct lm3639_platform_data *pdata;35 36	struct backlight_device *bled;37	struct led_classdev cdev_flash;38	struct led_classdev cdev_torch;39	struct regmap *regmap;40 41	unsigned int bled_mode;42	unsigned int bled_map;43	unsigned int last_flag;44};45 46/* initialize chip */47static int lm3639_chip_init(struct lm3639_chip_data *pchip)48{49	int ret;50	unsigned int reg_val;51	struct lm3639_platform_data *pdata = pchip->pdata;52 53	/* input pins config. */54	ret =55	    regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x08,56			       pdata->pin_pwm);57	if (ret < 0)58		goto out;59 60	reg_val = (pdata->pin_pwm & 0x40) | pdata->pin_strobe | pdata->pin_tx;61	ret = regmap_update_bits(pchip->regmap, REG_IO_CTRL, 0x7C, reg_val);62	if (ret < 0)63		goto out;64 65	/* init brightness */66	ret = regmap_write(pchip->regmap, REG_BL_CONF_4, pdata->init_brt_led);67	if (ret < 0)68		goto out;69 70	ret = regmap_write(pchip->regmap, REG_BL_CONF_3, pdata->init_brt_led);71	if (ret < 0)72		goto out;73 74	/* output pins config. */75	if (!pdata->init_brt_led) {76		reg_val = pdata->fled_pins;77		reg_val |= pdata->bled_pins;78	} else {79		reg_val = pdata->fled_pins;80		reg_val |= pdata->bled_pins | 0x01;81	}82 83	ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x79, reg_val);84	if (ret < 0)85		goto out;86 87	return ret;88out:89	dev_err(pchip->dev, "i2c failed to access register\n");90	return ret;91}92 93/* update and get brightness */94static int lm3639_bled_update_status(struct backlight_device *bl)95{96	int ret;97	unsigned int reg_val;98	struct lm3639_chip_data *pchip = bl_get_data(bl);99	struct lm3639_platform_data *pdata = pchip->pdata;100 101	ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);102	if (ret < 0)103		goto out;104 105	if (reg_val != 0)106		dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);107 108	/* pwm control */109	if (pdata->pin_pwm) {110		if (pdata->pwm_set_intensity)111			pdata->pwm_set_intensity(bl->props.brightness,112						 pdata->max_brt_led);113		else114			dev_err(pchip->dev,115				"No pwm control func. in plat-data\n");116		return bl->props.brightness;117	}118 119	/* i2c control and set brigtness */120	ret = regmap_write(pchip->regmap, REG_BL_CONF_4, bl->props.brightness);121	if (ret < 0)122		goto out;123	ret = regmap_write(pchip->regmap, REG_BL_CONF_3, bl->props.brightness);124	if (ret < 0)125		goto out;126 127	if (!bl->props.brightness)128		ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x00);129	else130		ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x01);131	if (ret < 0)132		goto out;133 134	return bl->props.brightness;135out:136	dev_err(pchip->dev, "i2c failed to access registers\n");137	return bl->props.brightness;138}139 140static int lm3639_bled_get_brightness(struct backlight_device *bl)141{142	int ret;143	unsigned int reg_val;144	struct lm3639_chip_data *pchip = bl_get_data(bl);145	struct lm3639_platform_data *pdata = pchip->pdata;146 147	if (pdata->pin_pwm) {148		if (pdata->pwm_get_intensity)149			bl->props.brightness = pdata->pwm_get_intensity();150		else151			dev_err(pchip->dev,152				"No pwm control func. in plat-data\n");153		return bl->props.brightness;154	}155 156	ret = regmap_read(pchip->regmap, REG_BL_CONF_1, &reg_val);157	if (ret < 0)158		goto out;159	if (reg_val & 0x10)160		ret = regmap_read(pchip->regmap, REG_BL_CONF_4, &reg_val);161	else162		ret = regmap_read(pchip->regmap, REG_BL_CONF_3, &reg_val);163	if (ret < 0)164		goto out;165	bl->props.brightness = reg_val;166 167	return bl->props.brightness;168out:169	dev_err(pchip->dev, "i2c failed to access register\n");170	return bl->props.brightness;171}172 173static const struct backlight_ops lm3639_bled_ops = {174	.options = BL_CORE_SUSPENDRESUME,175	.update_status = lm3639_bled_update_status,176	.get_brightness = lm3639_bled_get_brightness,177};178 179/* backlight mapping mode */180static ssize_t lm3639_bled_mode_store(struct device *dev,181				      struct device_attribute *devAttr,182				      const char *buf, size_t size)183{184	ssize_t ret;185	struct lm3639_chip_data *pchip = dev_get_drvdata(dev);186	unsigned int state;187 188	ret = kstrtouint(buf, 10, &state);189	if (ret)190		goto out_input;191 192	if (!state)193		ret =194		    regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,195				       0x00);196	else197		ret =198		    regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,199				       0x10);200 201	if (ret < 0)202		goto out;203 204	return size;205 206out:207	dev_err(pchip->dev, "%s:i2c access fail to register\n", __func__);208	return ret;209 210out_input:211	dev_err(pchip->dev, "%s:input conversion fail\n", __func__);212	return ret;213 214}215 216static DEVICE_ATTR(bled_mode, S_IWUSR, NULL, lm3639_bled_mode_store);217 218/* torch */219static void lm3639_torch_brightness_set(struct led_classdev *cdev,220					enum led_brightness brightness)221{222	int ret;223	unsigned int reg_val;224	struct lm3639_chip_data *pchip;225 226	pchip = container_of(cdev, struct lm3639_chip_data, cdev_torch);227 228	ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);229	if (ret < 0)230		goto out;231	if (reg_val != 0)232		dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);233 234	/* brightness 0 means off state */235	if (!brightness) {236		ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);237		if (ret < 0)238			goto out;239		return;240	}241 242	ret = regmap_update_bits(pchip->regmap,243				 REG_FL_CONF_1, 0x70, (brightness - 1) << 4);244	if (ret < 0)245		goto out;246	ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x02);247	if (ret < 0)248		goto out;249 250	return;251out:252	dev_err(pchip->dev, "i2c failed to access register\n");253}254 255/* flash */256static void lm3639_flash_brightness_set(struct led_classdev *cdev,257					enum led_brightness brightness)258{259	int ret;260	unsigned int reg_val;261	struct lm3639_chip_data *pchip;262 263	pchip = container_of(cdev, struct lm3639_chip_data, cdev_flash);264 265	ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);266	if (ret < 0)267		goto out;268	if (reg_val != 0)269		dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);270 271	/* torch off before flash control */272	ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);273	if (ret < 0)274		goto out;275 276	/* brightness 0 means off state */277	if (!brightness)278		return;279 280	ret = regmap_update_bits(pchip->regmap,281				 REG_FL_CONF_1, 0x0F, brightness - 1);282	if (ret < 0)283		goto out;284	ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x06);285	if (ret < 0)286		goto out;287 288	return;289out:290	dev_err(pchip->dev, "i2c failed to access register\n");291}292 293static const struct regmap_config lm3639_regmap = {294	.reg_bits = 8,295	.val_bits = 8,296	.max_register = REG_MAX,297};298 299static int lm3639_probe(struct i2c_client *client)300{301	int ret;302	struct lm3639_chip_data *pchip;303	struct lm3639_platform_data *pdata = dev_get_platdata(&client->dev);304	struct backlight_properties props;305 306	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {307		dev_err(&client->dev, "i2c functionality check fail.\n");308		return -EOPNOTSUPP;309	}310 311	if (pdata == NULL) {312		dev_err(&client->dev, "Needs Platform Data.\n");313		return -ENODATA;314	}315 316	pchip = devm_kzalloc(&client->dev,317			     sizeof(struct lm3639_chip_data), GFP_KERNEL);318	if (!pchip)319		return -ENOMEM;320 321	pchip->pdata = pdata;322	pchip->dev = &client->dev;323 324	pchip->regmap = devm_regmap_init_i2c(client, &lm3639_regmap);325	if (IS_ERR(pchip->regmap)) {326		ret = PTR_ERR(pchip->regmap);327		dev_err(&client->dev, "fail : allocate register map: %d\n",328			ret);329		return ret;330	}331	i2c_set_clientdata(client, pchip);332 333	/* chip initialize */334	ret = lm3639_chip_init(pchip);335	if (ret < 0) {336		dev_err(&client->dev, "fail : chip init\n");337		goto err_out;338	}339 340	/* backlight */341	memset(&props, 0, sizeof(struct backlight_properties));342	props.type = BACKLIGHT_RAW;343	props.brightness = pdata->init_brt_led;344	props.max_brightness = pdata->max_brt_led;345	pchip->bled =346	    devm_backlight_device_register(pchip->dev, "lm3639_bled",347					   pchip->dev, pchip, &lm3639_bled_ops,348					   &props);349	if (IS_ERR(pchip->bled)) {350		dev_err(&client->dev, "fail : backlight register\n");351		ret = PTR_ERR(pchip->bled);352		goto err_out;353	}354 355	ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode);356	if (ret < 0) {357		dev_err(&client->dev, "failed : add sysfs entries\n");358		goto err_out;359	}360 361	/* flash */362	pchip->cdev_flash.name = "lm3639_flash";363	pchip->cdev_flash.max_brightness = 16;364	pchip->cdev_flash.brightness_set = lm3639_flash_brightness_set;365	ret = led_classdev_register((struct device *)366				    &client->dev, &pchip->cdev_flash);367	if (ret < 0) {368		dev_err(&client->dev, "fail : flash register\n");369		goto err_flash;370	}371 372	/* torch */373	pchip->cdev_torch.name = "lm3639_torch";374	pchip->cdev_torch.max_brightness = 8;375	pchip->cdev_torch.brightness_set = lm3639_torch_brightness_set;376	ret = led_classdev_register((struct device *)377				    &client->dev, &pchip->cdev_torch);378	if (ret < 0) {379		dev_err(&client->dev, "fail : torch register\n");380		goto err_torch;381	}382 383	return 0;384 385err_torch:386	led_classdev_unregister(&pchip->cdev_flash);387err_flash:388	device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);389err_out:390	return ret;391}392 393static void lm3639_remove(struct i2c_client *client)394{395	struct lm3639_chip_data *pchip = i2c_get_clientdata(client);396 397	regmap_write(pchip->regmap, REG_ENABLE, 0x00);398 399	led_classdev_unregister(&pchip->cdev_torch);400	led_classdev_unregister(&pchip->cdev_flash);401	if (pchip->bled)402		device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);403}404 405static const struct i2c_device_id lm3639_id[] = {406	{ LM3639_NAME },407	{}408};409 410MODULE_DEVICE_TABLE(i2c, lm3639_id);411static struct i2c_driver lm3639_i2c_driver = {412	.driver = {413		   .name = LM3639_NAME,414		   },415	.probe = lm3639_probe,416	.remove = lm3639_remove,417	.id_table = lm3639_id,418};419 420module_i2c_driver(lm3639_i2c_driver);421 422MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639");423MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");424MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");425MODULE_LICENSE("GPL v2");426