brintos

brintos / linux-shallow public Read only

0
0
Text · 12.4 KiB · f68771b Raw
530 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3* Simple driver for Texas Instruments LM355x LED Flash driver chip4* Copyright (C) 2012 Texas Instruments5*/6 7#include <linux/module.h>8#include <linux/delay.h>9#include <linux/i2c.h>10#include <linux/leds.h>11#include <linux/slab.h>12#include <linux/platform_device.h>13#include <linux/fs.h>14#include <linux/regmap.h>15#include <linux/platform_data/leds-lm355x.h>16 17enum lm355x_type {18	CHIP_LM3554 = 0,19	CHIP_LM3556,20};21 22enum lm355x_regs {23	REG_FLAG = 0,24	REG_TORCH_CFG,25	REG_TORCH_CTRL,26	REG_STROBE_CFG,27	REG_FLASH_CTRL,28	REG_INDI_CFG,29	REG_INDI_CTRL,30	REG_OPMODE,31	REG_MAX,32};33 34/* operation mode */35enum lm355x_mode {36	MODE_SHDN = 0,37	MODE_INDIC,38	MODE_TORCH,39	MODE_FLASH40};41 42/* register map info. */43struct lm355x_reg_data {44	u8 regno;45	u8 mask;46	u8 shift;47};48 49struct lm355x_chip_data {50	struct device *dev;51	enum lm355x_type type;52 53	struct led_classdev cdev_flash;54	struct led_classdev cdev_torch;55	struct led_classdev cdev_indicator;56 57	struct lm355x_platform_data *pdata;58	struct regmap *regmap;59	struct mutex lock;60 61	unsigned int last_flag;62	struct lm355x_reg_data *regs;63};64 65/* specific indicator function for lm3556 */66enum lm3556_indic_pulse_time {67	PULSE_TIME_0_MS = 0,68	PULSE_TIME_32_MS,69	PULSE_TIME_64_MS,70	PULSE_TIME_92_MS,71	PULSE_TIME_128_MS,72	PULSE_TIME_160_MS,73	PULSE_TIME_196_MS,74	PULSE_TIME_224_MS,75	PULSE_TIME_256_MS,76	PULSE_TIME_288_MS,77	PULSE_TIME_320_MS,78	PULSE_TIME_352_MS,79	PULSE_TIME_384_MS,80	PULSE_TIME_416_MS,81	PULSE_TIME_448_MS,82	PULSE_TIME_480_MS,83};84 85enum lm3556_indic_n_blank {86	INDIC_N_BLANK_0 = 0,87	INDIC_N_BLANK_1,88	INDIC_N_BLANK_2,89	INDIC_N_BLANK_3,90	INDIC_N_BLANK_4,91	INDIC_N_BLANK_5,92	INDIC_N_BLANK_6,93	INDIC_N_BLANK_7,94	INDIC_N_BLANK_8,95	INDIC_N_BLANK_9,96	INDIC_N_BLANK_10,97	INDIC_N_BLANK_11,98	INDIC_N_BLANK_12,99	INDIC_N_BLANK_13,100	INDIC_N_BLANK_14,101	INDIC_N_BLANK_15,102};103 104enum lm3556_indic_period {105	INDIC_PERIOD_0 = 0,106	INDIC_PERIOD_1,107	INDIC_PERIOD_2,108	INDIC_PERIOD_3,109	INDIC_PERIOD_4,110	INDIC_PERIOD_5,111	INDIC_PERIOD_6,112	INDIC_PERIOD_7,113};114 115#define INDIC_PATTERN_SIZE 4116 117struct indicator {118	u8 blinking;119	u8 period_cnt;120};121 122/* indicator pattern data only for lm3556 */123static struct indicator indicator_pattern[INDIC_PATTERN_SIZE] = {124	[0] = {(INDIC_N_BLANK_1 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_1},125	[1] = {(INDIC_N_BLANK_15 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_2},126	[2] = {(INDIC_N_BLANK_10 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_4},127	[3] = {(INDIC_N_BLANK_5 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_7},128};129 130static struct lm355x_reg_data lm3554_regs[REG_MAX] = {131	[REG_FLAG] = {0xD0, 0xBF, 0},132	[REG_TORCH_CFG] = {0xE0, 0x80, 7},133	[REG_TORCH_CTRL] = {0xA0, 0x38, 3},134	[REG_STROBE_CFG] = {0xE0, 0x04, 2},135	[REG_FLASH_CTRL] = {0xB0, 0x78, 3},136	[REG_INDI_CFG] = {0xE0, 0x08, 3},137	[REG_INDI_CTRL] = {0xA0, 0xC0, 6},138	[REG_OPMODE] = {0xA0, 0x03, 0},139};140 141static struct lm355x_reg_data lm3556_regs[REG_MAX] = {142	[REG_FLAG] = {0x0B, 0xFF, 0},143	[REG_TORCH_CFG] = {0x0A, 0x10, 4},144	[REG_TORCH_CTRL] = {0x09, 0x70, 4},145	[REG_STROBE_CFG] = {0x0A, 0x20, 5},146	[REG_FLASH_CTRL] = {0x09, 0x0F, 0},147	[REG_INDI_CFG] = {0xFF, 0xFF, 0},148	[REG_INDI_CTRL] = {0x09, 0x70, 4},149	[REG_OPMODE] = {0x0A, 0x03, 0},150};151 152static char lm355x_name[][I2C_NAME_SIZE] = {153	[CHIP_LM3554] = LM3554_NAME,154	[CHIP_LM3556] = LM3556_NAME,155};156 157/* chip initialize */158static int lm355x_chip_init(struct lm355x_chip_data *chip)159{160	int ret;161	unsigned int reg_val;162	struct lm355x_platform_data *pdata = chip->pdata;163 164	/* input and output pins configuration */165	switch (chip->type) {166	case CHIP_LM3554:167		reg_val = (u32)pdata->pin_tx2 | (u32)pdata->ntc_pin;168		ret = regmap_update_bits(chip->regmap, 0xE0, 0x28, reg_val);169		if (ret < 0)170			goto out;171		reg_val = (u32)pdata->pass_mode;172		ret = regmap_update_bits(chip->regmap, 0xA0, 0x04, reg_val);173		if (ret < 0)174			goto out;175		break;176 177	case CHIP_LM3556:178		reg_val = (u32)pdata->pin_tx2 | (u32)pdata->ntc_pin |179		          (u32)pdata->pass_mode;180		ret = regmap_update_bits(chip->regmap, 0x0A, 0xC4, reg_val);181		if (ret < 0)182			goto out;183		break;184	default:185		return -ENODATA;186	}187 188	return ret;189out:190	dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);191	return ret;192}193 194/* chip control */195static int lm355x_control(struct lm355x_chip_data *chip,196			   u8 brightness, enum lm355x_mode opmode)197{198	int ret;199	unsigned int reg_val;200	struct lm355x_platform_data *pdata = chip->pdata;201	struct lm355x_reg_data *preg = chip->regs;202 203	ret = regmap_read(chip->regmap, preg[REG_FLAG].regno, &chip->last_flag);204	if (ret < 0)205		goto out;206	if (chip->last_flag & preg[REG_FLAG].mask)207		dev_info(chip->dev, "%s Last FLAG is 0x%x\n",208			 lm355x_name[chip->type],209			 chip->last_flag & preg[REG_FLAG].mask);210	/* brightness 0 means shutdown */211	if (!brightness)212		opmode = MODE_SHDN;213 214	switch (opmode) {215	case MODE_TORCH:216		ret =217		    regmap_update_bits(chip->regmap, preg[REG_TORCH_CTRL].regno,218				       preg[REG_TORCH_CTRL].mask,219				       (brightness - 1)220				       << preg[REG_TORCH_CTRL].shift);221		if (ret < 0)222			goto out;223 224		if (pdata->pin_tx1 != LM355x_PIN_TORCH_DISABLE) {225			ret =226			    regmap_update_bits(chip->regmap,227					       preg[REG_TORCH_CFG].regno,228					       preg[REG_TORCH_CFG].mask,229					       0x01 <<230					       preg[REG_TORCH_CFG].shift);231			if (ret < 0)232				goto out;233			opmode = MODE_SHDN;234			dev_info(chip->dev,235				 "torch brt is set - ext. torch pin mode\n");236		}237		break;238 239	case MODE_FLASH:240 241		ret =242		    regmap_update_bits(chip->regmap, preg[REG_FLASH_CTRL].regno,243				       preg[REG_FLASH_CTRL].mask,244				       (brightness - 1)245				       << preg[REG_FLASH_CTRL].shift);246		if (ret < 0)247			goto out;248 249		if (pdata->pin_strobe != LM355x_PIN_STROBE_DISABLE) {250			if (chip->type == CHIP_LM3554)251				reg_val = 0x00;252			else253				reg_val = 0x01;254			ret =255			    regmap_update_bits(chip->regmap,256					       preg[REG_STROBE_CFG].regno,257					       preg[REG_STROBE_CFG].mask,258					       reg_val <<259					       preg[REG_STROBE_CFG].shift);260			if (ret < 0)261				goto out;262			opmode = MODE_SHDN;263			dev_info(chip->dev,264				 "flash brt is set - ext. strobe pin mode\n");265		}266		break;267 268	case MODE_INDIC:269		ret =270		    regmap_update_bits(chip->regmap, preg[REG_INDI_CTRL].regno,271				       preg[REG_INDI_CTRL].mask,272				       (brightness - 1)273				       << preg[REG_INDI_CTRL].shift);274		if (ret < 0)275			goto out;276 277		if (pdata->pin_tx2 != LM355x_PIN_TX_DISABLE) {278			ret =279			    regmap_update_bits(chip->regmap,280					       preg[REG_INDI_CFG].regno,281					       preg[REG_INDI_CFG].mask,282					       0x01 <<283					       preg[REG_INDI_CFG].shift);284			if (ret < 0)285				goto out;286			opmode = MODE_SHDN;287		}288		break;289	case MODE_SHDN:290		break;291	default:292		return -EINVAL;293	}294	/* operation mode control */295	ret = regmap_update_bits(chip->regmap, preg[REG_OPMODE].regno,296				 preg[REG_OPMODE].mask,297				 opmode << preg[REG_OPMODE].shift);298	if (ret < 0)299		goto out;300	return ret;301out:302	dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);303	return ret;304}305 306/* torch */307 308static int lm355x_torch_brightness_set(struct led_classdev *cdev,309					enum led_brightness brightness)310{311	struct lm355x_chip_data *chip =312	    container_of(cdev, struct lm355x_chip_data, cdev_torch);313	int ret;314 315	mutex_lock(&chip->lock);316	ret = lm355x_control(chip, brightness, MODE_TORCH);317	mutex_unlock(&chip->lock);318	return ret;319}320 321/* flash */322 323static int lm355x_strobe_brightness_set(struct led_classdev *cdev,324					 enum led_brightness brightness)325{326	struct lm355x_chip_data *chip =327	    container_of(cdev, struct lm355x_chip_data, cdev_flash);328	int ret;329 330	mutex_lock(&chip->lock);331	ret = lm355x_control(chip, brightness, MODE_FLASH);332	mutex_unlock(&chip->lock);333	return ret;334}335 336/* indicator */337 338static int lm355x_indicator_brightness_set(struct led_classdev *cdev,339					    enum led_brightness brightness)340{341	struct lm355x_chip_data *chip =342	    container_of(cdev, struct lm355x_chip_data, cdev_indicator);343	int ret;344 345	mutex_lock(&chip->lock);346	ret = lm355x_control(chip, brightness, MODE_INDIC);347	mutex_unlock(&chip->lock);348	return ret;349}350 351/* indicator pattern only for lm3556*/352static ssize_t pattern_store(struct device *dev,353			     struct device_attribute *attr,354			     const char *buf, size_t size)355{356	ssize_t ret;357	struct led_classdev *led_cdev = dev_get_drvdata(dev);358	struct lm355x_chip_data *chip =359	    container_of(led_cdev, struct lm355x_chip_data, cdev_indicator);360	unsigned int state;361 362	ret = kstrtouint(buf, 10, &state);363	if (ret)364		goto out;365	if (state > INDIC_PATTERN_SIZE - 1)366		state = INDIC_PATTERN_SIZE - 1;367 368	ret = regmap_write(chip->regmap, 0x04,369			   indicator_pattern[state].blinking);370	if (ret < 0)371		goto out;372 373	ret = regmap_write(chip->regmap, 0x05,374			   indicator_pattern[state].period_cnt);375	if (ret < 0)376		goto out;377 378	return size;379out:380	dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);381	return ret;382}383 384static DEVICE_ATTR_WO(pattern);385 386static struct attribute *lm355x_indicator_attrs[] = {387	&dev_attr_pattern.attr,388	NULL389};390ATTRIBUTE_GROUPS(lm355x_indicator);391 392static const struct regmap_config lm355x_regmap = {393	.reg_bits = 8,394	.val_bits = 8,395	.max_register = 0xFF,396};397 398/* module initialize */399static int lm355x_probe(struct i2c_client *client)400{401	const struct i2c_device_id *id = i2c_client_get_device_id(client);402	struct lm355x_platform_data *pdata = dev_get_platdata(&client->dev);403	struct lm355x_chip_data *chip;404 405	int err;406 407	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {408		dev_err(&client->dev, "i2c functionality check fail.\n");409		return -EOPNOTSUPP;410	}411 412	if (pdata == NULL) {413		dev_err(&client->dev, "needs Platform Data.\n");414		return -ENODATA;415	}416 417	chip = devm_kzalloc(&client->dev,418			    sizeof(struct lm355x_chip_data), GFP_KERNEL);419	if (!chip)420		return -ENOMEM;421 422	chip->dev = &client->dev;423	chip->type = id->driver_data;424	switch (id->driver_data) {425	case CHIP_LM3554:426		chip->regs = lm3554_regs;427		break;428	case CHIP_LM3556:429		chip->regs = lm3556_regs;430		break;431	default:432		return -ENOSYS;433	}434	chip->pdata = pdata;435 436	chip->regmap = devm_regmap_init_i2c(client, &lm355x_regmap);437	if (IS_ERR(chip->regmap)) {438		err = PTR_ERR(chip->regmap);439		dev_err(&client->dev,440			"Failed to allocate register map: %d\n", err);441		return err;442	}443 444	mutex_init(&chip->lock);445	i2c_set_clientdata(client, chip);446 447	err = lm355x_chip_init(chip);448	if (err < 0)449		goto err_out;450 451	/* flash */452	chip->cdev_flash.name = "flash";453	chip->cdev_flash.max_brightness = 16;454	chip->cdev_flash.brightness_set_blocking = lm355x_strobe_brightness_set;455	chip->cdev_flash.default_trigger = "flash";456	err = led_classdev_register(&client->dev, &chip->cdev_flash);457	if (err < 0)458		goto err_out;459	/* torch */460	chip->cdev_torch.name = "torch";461	chip->cdev_torch.max_brightness = 8;462	chip->cdev_torch.brightness_set_blocking = lm355x_torch_brightness_set;463	chip->cdev_torch.default_trigger = "torch";464	err = led_classdev_register(&client->dev, &chip->cdev_torch);465	if (err < 0)466		goto err_create_torch_file;467	/* indicator */468	chip->cdev_indicator.name = "indicator";469	if (id->driver_data == CHIP_LM3554)470		chip->cdev_indicator.max_brightness = 4;471	else472		chip->cdev_indicator.max_brightness = 8;473	chip->cdev_indicator.brightness_set_blocking =474					lm355x_indicator_brightness_set;475	/* indicator pattern control only for LM3556 */476	if (id->driver_data == CHIP_LM3556)477		chip->cdev_indicator.groups = lm355x_indicator_groups;478	err = led_classdev_register(&client->dev, &chip->cdev_indicator);479	if (err < 0)480		goto err_create_indicator_file;481 482	dev_info(&client->dev, "%s is initialized\n",483		 lm355x_name[id->driver_data]);484	return 0;485 486err_create_indicator_file:487	led_classdev_unregister(&chip->cdev_torch);488err_create_torch_file:489	led_classdev_unregister(&chip->cdev_flash);490err_out:491	return err;492}493 494static void lm355x_remove(struct i2c_client *client)495{496	struct lm355x_chip_data *chip = i2c_get_clientdata(client);497	struct lm355x_reg_data *preg = chip->regs;498 499	regmap_write(chip->regmap, preg[REG_OPMODE].regno, 0);500	led_classdev_unregister(&chip->cdev_indicator);501	led_classdev_unregister(&chip->cdev_torch);502	led_classdev_unregister(&chip->cdev_flash);503	dev_info(&client->dev, "%s is removed\n", lm355x_name[chip->type]);504}505 506static const struct i2c_device_id lm355x_id[] = {507	{LM3554_NAME, CHIP_LM3554},508	{LM3556_NAME, CHIP_LM3556},509	{}510};511 512MODULE_DEVICE_TABLE(i2c, lm355x_id);513 514static struct i2c_driver lm355x_i2c_driver = {515	.driver = {516		   .name = LM355x_NAME,517		   .pm = NULL,518		   },519	.probe = lm355x_probe,520	.remove = lm355x_remove,521	.id_table = lm355x_id,522};523 524module_i2c_driver(lm355x_i2c_driver);525 526MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM355x");527MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>");528MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>");529MODULE_LICENSE("GPL v2");530