xref: /linux/drivers/video/backlight/lm3639_bl.c (revision 3932b9ca55b0be314a36d3e84faff3e823c081f5)
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 
36 struct 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 */
51 static 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;
92 out:
93 	dev_err(pchip->dev, "i2c failed to access register\n");
94 	return ret;
95 }
96 
97 /* update and get brightness */
98 static 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;
139 out:
140 	dev_err(pchip->dev, "i2c failed to access registers\n");
141 	return bl->props.brightness;
142 }
143 
144 static 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;
172 out:
173 	dev_err(pchip->dev, "i2c failed to access register\n");
174 	return bl->props.brightness;
175 }
176 
177 static 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 */
184 static 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 
210 out:
211 	dev_err(pchip->dev, "%s:i2c access fail to register\n", __func__);
212 	return ret;
213 
214 out_input:
215 	dev_err(pchip->dev, "%s:input conversion fail\n", __func__);
216 	return ret;
217 
218 }
219 
220 static DEVICE_ATTR(bled_mode, S_IWUSR, NULL, lm3639_bled_mode_store);
221 
222 /* torch */
223 static 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;
255 out:
256 	dev_err(pchip->dev, "i2c failed to access register\n");
257 	return;
258 }
259 
260 /* flash */
261 static void lm3639_flash_brightness_set(struct led_classdev *cdev,
262 					enum led_brightness brightness)
263 {
264 	int ret;
265 	unsigned int reg_val;
266 	struct lm3639_chip_data *pchip;
267 
268 	pchip = container_of(cdev, struct lm3639_chip_data, cdev_flash);
269 
270 	ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
271 	if (ret < 0)
272 		goto out;
273 	if (reg_val != 0)
274 		dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
275 
276 	/* torch off before flash control */
277 	ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
278 	if (ret < 0)
279 		goto out;
280 
281 	/* brightness 0 means off state */
282 	if (!brightness)
283 		return;
284 
285 	ret = regmap_update_bits(pchip->regmap,
286 				 REG_FL_CONF_1, 0x0F, brightness - 1);
287 	if (ret < 0)
288 		goto out;
289 	ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x06);
290 	if (ret < 0)
291 		goto out;
292 
293 	return;
294 out:
295 	dev_err(pchip->dev, "i2c failed to access register\n");
296 	return;
297 }
298 
299 static const struct regmap_config lm3639_regmap = {
300 	.reg_bits = 8,
301 	.val_bits = 8,
302 	.max_register = REG_MAX,
303 };
304 
305 static int lm3639_probe(struct i2c_client *client,
306 				  const struct i2c_device_id *id)
307 {
308 	int ret;
309 	struct lm3639_chip_data *pchip;
310 	struct lm3639_platform_data *pdata = dev_get_platdata(&client->dev);
311 	struct backlight_properties props;
312 
313 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
314 		dev_err(&client->dev, "i2c functionality check fail.\n");
315 		return -EOPNOTSUPP;
316 	}
317 
318 	if (pdata == NULL) {
319 		dev_err(&client->dev, "Needs Platform Data.\n");
320 		return -ENODATA;
321 	}
322 
323 	pchip = devm_kzalloc(&client->dev,
324 			     sizeof(struct lm3639_chip_data), GFP_KERNEL);
325 	if (!pchip)
326 		return -ENOMEM;
327 
328 	pchip->pdata = pdata;
329 	pchip->dev = &client->dev;
330 
331 	pchip->regmap = devm_regmap_init_i2c(client, &lm3639_regmap);
332 	if (IS_ERR(pchip->regmap)) {
333 		ret = PTR_ERR(pchip->regmap);
334 		dev_err(&client->dev, "fail : allocate register map: %d\n",
335 			ret);
336 		return ret;
337 	}
338 	i2c_set_clientdata(client, pchip);
339 
340 	/* chip initialize */
341 	ret = lm3639_chip_init(pchip);
342 	if (ret < 0) {
343 		dev_err(&client->dev, "fail : chip init\n");
344 		goto err_out;
345 	}
346 
347 	/* backlight */
348 	props.type = BACKLIGHT_RAW;
349 	props.brightness = pdata->init_brt_led;
350 	props.max_brightness = pdata->max_brt_led;
351 	pchip->bled =
352 	    devm_backlight_device_register(pchip->dev, "lm3639_bled",
353 					   pchip->dev, pchip, &lm3639_bled_ops,
354 					   &props);
355 	if (IS_ERR(pchip->bled)) {
356 		dev_err(&client->dev, "fail : backlight register\n");
357 		ret = PTR_ERR(pchip->bled);
358 		goto err_out;
359 	}
360 
361 	ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode);
362 	if (ret < 0) {
363 		dev_err(&client->dev, "failed : add sysfs entries\n");
364 		goto err_out;
365 	}
366 
367 	/* flash */
368 	pchip->cdev_flash.name = "lm3639_flash";
369 	pchip->cdev_flash.max_brightness = 16;
370 	pchip->cdev_flash.brightness_set = lm3639_flash_brightness_set;
371 	ret = led_classdev_register((struct device *)
372 				    &client->dev, &pchip->cdev_flash);
373 	if (ret < 0) {
374 		dev_err(&client->dev, "fail : flash register\n");
375 		goto err_flash;
376 	}
377 
378 	/* torch */
379 	pchip->cdev_torch.name = "lm3639_torch";
380 	pchip->cdev_torch.max_brightness = 8;
381 	pchip->cdev_torch.brightness_set = lm3639_torch_brightness_set;
382 	ret = led_classdev_register((struct device *)
383 				    &client->dev, &pchip->cdev_torch);
384 	if (ret < 0) {
385 		dev_err(&client->dev, "fail : torch register\n");
386 		goto err_torch;
387 	}
388 
389 	return 0;
390 
391 err_torch:
392 	led_classdev_unregister(&pchip->cdev_flash);
393 err_flash:
394 	device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
395 err_out:
396 	return ret;
397 }
398 
399 static int lm3639_remove(struct i2c_client *client)
400 {
401 	struct lm3639_chip_data *pchip = i2c_get_clientdata(client);
402 
403 	regmap_write(pchip->regmap, REG_ENABLE, 0x00);
404 
405 	if (&pchip->cdev_torch)
406 		led_classdev_unregister(&pchip->cdev_torch);
407 	if (&pchip->cdev_flash)
408 		led_classdev_unregister(&pchip->cdev_flash);
409 	if (pchip->bled)
410 		device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
411 	return 0;
412 }
413 
414 static const struct i2c_device_id lm3639_id[] = {
415 	{LM3639_NAME, 0},
416 	{}
417 };
418 
419 MODULE_DEVICE_TABLE(i2c, lm3639_id);
420 static struct i2c_driver lm3639_i2c_driver = {
421 	.driver = {
422 		   .name = LM3639_NAME,
423 		   },
424 	.probe = lm3639_probe,
425 	.remove = lm3639_remove,
426 	.id_table = lm3639_id,
427 };
428 
429 module_i2c_driver(lm3639_i2c_driver);
430 
431 MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639");
432 MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
433 MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
434 MODULE_LICENSE("GPL v2");
435