1// SPDX-License-Identifier: GPL-2.0-only
2/*
3* Simple driver for Texas Instruments LM3639 Backlight + Flash LED driver chip
4* Copyright (C) 2012 Texas Instruments
5*/
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	0x00
19#define REG_CHECKSUM	0x01
20#define REG_BL_CONF_1	0x02
21#define REG_BL_CONF_2	0x03
22#define REG_BL_CONF_3	0x04
23#define REG_BL_CONF_4	0x05
24#define REG_FL_CONF_1	0x06
25#define REG_FL_CONF_2	0x07
26#define REG_FL_CONF_3	0x08
27#define REG_IO_CTRL	0x09
28#define REG_ENABLE	0x0A
29#define REG_FLAG	0x0B
30#define REG_MAX		REG_FLAG
31
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		else
114			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	else
130		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		else
151			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	else
162		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	else
197		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, 0},
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