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