diff options
author | G.Shark Jeong <gshark.jeong@gmail.com> | 2012-10-04 20:12:55 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-05 14:04:53 -0400 |
commit | 0f59858d511960caefb42c4535dc73c2c5f3136c (patch) | |
tree | dd0936348366a9f9b7d03d5706a50217eab89e09 /drivers/video | |
parent | 0c2a665a648ea74879ce6333d3a31a96a7c361ce (diff) |
backlight: add new lm3639 backlight driver
This driver is a general version for LM3639 backlgiht + flash driver chip
of TI.
LM3639:
The LM3639 is a single chip LCD Display Backlight driver + white LED
Camera driver. Programming is done over an I2C compatible interface.
www.ti.com
[akpm@linux-foundation.org: code layout tweaks]
Signed-off-by: G.Shark Jeong <gshark.jeong@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Cc: Daniel Jeong <daniel.jeong@ti.com>
Cc: Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/backlight/Kconfig | 9 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/backlight/lm3639_bl.c | 437 |
3 files changed, 447 insertions, 0 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index b9008b54ac5d..2b98fb8e711d 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig | |||
@@ -359,6 +359,15 @@ config BACKLIGHT_LM3630 | |||
359 | help | 359 | help |
360 | This supports TI LM3630 Backlight Driver | 360 | This supports TI LM3630 Backlight Driver |
361 | 361 | ||
362 | config BACKLIGHT_LM3639 | ||
363 | tristate "Backlight Driver for LM3639" | ||
364 | depends on BACKLIGHT_CLASS_DEVICE && I2C | ||
365 | select REGMAP_I2C | ||
366 | select NEW_LEDS | ||
367 | select LEDS_CLASS | ||
368 | help | ||
369 | This supports TI LM3639 Backlight + 1.5A Flash LED Driver | ||
370 | |||
362 | config BACKLIGHT_LP855X | 371 | config BACKLIGHT_LP855X |
363 | tristate "Backlight driver for TI LP855X" | 372 | tristate "Backlight driver for TI LP855X" |
364 | depends on BACKLIGHT_CLASS_DEVICE && I2C | 373 | depends on BACKLIGHT_CLASS_DEVICE && I2C |
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b0725313cd4d..a151378da45b 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile | |||
@@ -24,6 +24,7 @@ obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o | |||
24 | obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o | 24 | obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o |
25 | obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o | 25 | obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o |
26 | obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o | 26 | obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o |
27 | obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o | ||
27 | obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o | 28 | obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o |
28 | obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o | 29 | obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o |
29 | obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o | 30 | obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o |
diff --git a/drivers/video/backlight/lm3639_bl.c b/drivers/video/backlight/lm3639_bl.c new file mode 100644 index 000000000000..c6915c6c3cd1 --- /dev/null +++ b/drivers/video/backlight/lm3639_bl.c | |||
@@ -0,0 +1,437 @@ | |||
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 __devinit 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 | pdata->bled_pins; | ||
81 | else | ||
82 | reg_val = pdata->fled_pins | pdata->bled_pins | 0x01; | ||
83 | |||
84 | ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x79, reg_val); | ||
85 | if (ret < 0) | ||
86 | goto out; | ||
87 | |||
88 | return ret; | ||
89 | out: | ||
90 | dev_err(pchip->dev, "i2c failed to access register\n"); | ||
91 | return ret; | ||
92 | } | ||
93 | |||
94 | /* update and get brightness */ | ||
95 | static int lm3639_bled_update_status(struct backlight_device *bl) | ||
96 | { | ||
97 | int ret; | ||
98 | unsigned int reg_val; | ||
99 | struct lm3639_chip_data *pchip = bl_get_data(bl); | ||
100 | struct lm3639_platform_data *pdata = pchip->pdata; | ||
101 | |||
102 | ret = regmap_read(pchip->regmap, REG_FLAG, ®_val); | ||
103 | if (ret < 0) | ||
104 | goto out; | ||
105 | |||
106 | if (reg_val != 0) | ||
107 | dev_info(pchip->dev, "last flag is 0x%x\n", reg_val); | ||
108 | |||
109 | /* pwm control */ | ||
110 | if (pdata->pin_pwm) { | ||
111 | if (pdata->pwm_set_intensity) | ||
112 | pdata->pwm_set_intensity(bl->props.brightness, | ||
113 | pdata->max_brt_led); | ||
114 | else | ||
115 | dev_err(pchip->dev, | ||
116 | "No pwm control func. in plat-data\n"); | ||
117 | return bl->props.brightness; | ||
118 | } | ||
119 | |||
120 | /* i2c control and set brigtness */ | ||
121 | ret = regmap_write(pchip->regmap, REG_BL_CONF_4, bl->props.brightness); | ||
122 | if (ret < 0) | ||
123 | goto out; | ||
124 | ret = regmap_write(pchip->regmap, REG_BL_CONF_3, bl->props.brightness); | ||
125 | if (ret < 0) | ||
126 | goto out; | ||
127 | |||
128 | if (!bl->props.brightness) | ||
129 | ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x00); | ||
130 | else | ||
131 | ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x01); | ||
132 | if (ret < 0) | ||
133 | goto out; | ||
134 | |||
135 | return bl->props.brightness; | ||
136 | out: | ||
137 | dev_err(pchip->dev, "i2c failed to access registers\n"); | ||
138 | return bl->props.brightness; | ||
139 | } | ||
140 | |||
141 | static int lm3639_bled_get_brightness(struct backlight_device *bl) | ||
142 | { | ||
143 | int ret; | ||
144 | unsigned int reg_val; | ||
145 | struct lm3639_chip_data *pchip = bl_get_data(bl); | ||
146 | struct lm3639_platform_data *pdata = pchip->pdata; | ||
147 | |||
148 | if (pdata->pin_pwm) { | ||
149 | if (pdata->pwm_get_intensity) | ||
150 | bl->props.brightness = pdata->pwm_get_intensity(); | ||
151 | else | ||
152 | dev_err(pchip->dev, | ||
153 | "No pwm control func. in plat-data\n"); | ||
154 | return bl->props.brightness; | ||
155 | } | ||
156 | |||
157 | ret = regmap_read(pchip->regmap, REG_BL_CONF_1, ®_val); | ||
158 | if (ret < 0) | ||
159 | goto out; | ||
160 | if (reg_val & 0x10) | ||
161 | ret = regmap_read(pchip->regmap, REG_BL_CONF_4, ®_val); | ||
162 | else | ||
163 | ret = regmap_read(pchip->regmap, REG_BL_CONF_3, ®_val); | ||
164 | if (ret < 0) | ||
165 | goto out; | ||
166 | bl->props.brightness = reg_val; | ||
167 | |||
168 | return bl->props.brightness; | ||
169 | out: | ||
170 | dev_err(pchip->dev, "i2c failed to access register\n"); | ||
171 | return bl->props.brightness; | ||
172 | } | ||
173 | |||
174 | static const struct backlight_ops lm3639_bled_ops = { | ||
175 | .options = BL_CORE_SUSPENDRESUME, | ||
176 | .update_status = lm3639_bled_update_status, | ||
177 | .get_brightness = lm3639_bled_get_brightness, | ||
178 | }; | ||
179 | |||
180 | /* backlight mapping mode */ | ||
181 | static ssize_t lm3639_bled_mode_store(struct device *dev, | ||
182 | struct device_attribute *devAttr, | ||
183 | const char *buf, size_t size) | ||
184 | { | ||
185 | ssize_t ret; | ||
186 | struct lm3639_chip_data *pchip = dev_get_drvdata(dev); | ||
187 | unsigned int state; | ||
188 | |||
189 | ret = kstrtouint(buf, 10, &state); | ||
190 | if (ret) | ||
191 | goto out_input; | ||
192 | |||
193 | if (!state) | ||
194 | ret = | ||
195 | regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10, | ||
196 | 0x00); | ||
197 | else | ||
198 | ret = | ||
199 | regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10, | ||
200 | 0x10); | ||
201 | |||
202 | if (ret < 0) | ||
203 | goto out; | ||
204 | |||
205 | return size; | ||
206 | |||
207 | out: | ||
208 | dev_err(pchip->dev, "%s:i2c access fail to register\n", __func__); | ||
209 | return size; | ||
210 | |||
211 | out_input: | ||
212 | dev_err(pchip->dev, "%s:input conversion fail\n", __func__); | ||
213 | return size; | ||
214 | |||
215 | } | ||
216 | |||
217 | static DEVICE_ATTR(bled_mode, 0666, NULL, lm3639_bled_mode_store); | ||
218 | |||
219 | /* torch */ | ||
220 | static void lm3639_torch_brightness_set(struct led_classdev *cdev, | ||
221 | enum led_brightness brightness) | ||
222 | { | ||
223 | int ret; | ||
224 | unsigned int reg_val; | ||
225 | struct lm3639_chip_data *pchip; | ||
226 | |||
227 | pchip = container_of(cdev, struct lm3639_chip_data, cdev_torch); | ||
228 | |||
229 | ret = regmap_read(pchip->regmap, REG_FLAG, ®_val); | ||
230 | if (ret < 0) | ||
231 | goto out; | ||
232 | if (reg_val != 0) | ||
233 | dev_info(pchip->dev, "last flag is 0x%x\n", reg_val); | ||
234 | |||
235 | /* brightness 0 means off state */ | ||
236 | if (!brightness) { | ||
237 | ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00); | ||
238 | if (ret < 0) | ||
239 | goto out; | ||
240 | return; | ||
241 | } | ||
242 | |||
243 | ret = regmap_update_bits(pchip->regmap, | ||
244 | REG_FL_CONF_1, 0x70, (brightness - 1) << 4); | ||
245 | if (ret < 0) | ||
246 | goto out; | ||
247 | ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x02); | ||
248 | if (ret < 0) | ||
249 | goto out; | ||
250 | |||
251 | return; | ||
252 | out: | ||
253 | dev_err(pchip->dev, "i2c failed to access register\n"); | ||
254 | return; | ||
255 | } | ||
256 | |||
257 | /* flash */ | ||
258 | static void lm3639_flash_brightness_set(struct led_classdev *cdev, | ||
259 | enum led_brightness brightness) | ||
260 | { | ||
261 | int ret; | ||
262 | unsigned int reg_val; | ||
263 | struct lm3639_chip_data *pchip; | ||
264 | |||
265 | pchip = container_of(cdev, struct lm3639_chip_data, cdev_flash); | ||
266 | |||
267 | ret = regmap_read(pchip->regmap, REG_FLAG, ®_val); | ||
268 | if (ret < 0) | ||
269 | goto out; | ||
270 | if (reg_val != 0) | ||
271 | dev_info(pchip->dev, "last flag is 0x%x\n", reg_val); | ||
272 | |||
273 | /* torch off before flash control */ | ||
274 | ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00); | ||
275 | if (ret < 0) | ||
276 | goto out; | ||
277 | |||
278 | /* brightness 0 means off state */ | ||
279 | if (!brightness) | ||
280 | return; | ||
281 | |||
282 | ret = regmap_update_bits(pchip->regmap, | ||
283 | REG_FL_CONF_1, 0x0F, brightness - 1); | ||
284 | if (ret < 0) | ||
285 | goto out; | ||
286 | ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x06); | ||
287 | if (ret < 0) | ||
288 | goto out; | ||
289 | |||
290 | return; | ||
291 | out: | ||
292 | dev_err(pchip->dev, "i2c failed to access register\n"); | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | static const struct regmap_config lm3639_regmap = { | ||
297 | .reg_bits = 8, | ||
298 | .val_bits = 8, | ||
299 | .max_register = REG_MAX, | ||
300 | }; | ||
301 | |||
302 | static int __devinit lm3639_probe(struct i2c_client *client, | ||
303 | const struct i2c_device_id *id) | ||
304 | { | ||
305 | int ret; | ||
306 | struct lm3639_chip_data *pchip; | ||
307 | struct lm3639_platform_data *pdata = client->dev.platform_data; | ||
308 | struct backlight_properties props; | ||
309 | |||
310 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
311 | dev_err(&client->dev, "i2c functionality check fail.\n"); | ||
312 | return -EOPNOTSUPP; | ||
313 | } | ||
314 | |||
315 | if (pdata == NULL) { | ||
316 | dev_err(&client->dev, "Needs Platform Data.\n"); | ||
317 | return -ENODATA; | ||
318 | } | ||
319 | |||
320 | pchip = devm_kzalloc(&client->dev, | ||
321 | sizeof(struct lm3639_chip_data), GFP_KERNEL); | ||
322 | if (!pchip) | ||
323 | return -ENOMEM; | ||
324 | |||
325 | pchip->pdata = pdata; | ||
326 | pchip->dev = &client->dev; | ||
327 | |||
328 | pchip->regmap = devm_regmap_init_i2c(client, &lm3639_regmap); | ||
329 | if (IS_ERR(pchip->regmap)) { | ||
330 | ret = PTR_ERR(pchip->regmap); | ||
331 | dev_err(&client->dev, "fail : allocate register map: %d\n", | ||
332 | ret); | ||
333 | return ret; | ||
334 | } | ||
335 | i2c_set_clientdata(client, pchip); | ||
336 | |||
337 | /* chip initialize */ | ||
338 | ret = lm3639_chip_init(pchip); | ||
339 | if (ret < 0) { | ||
340 | dev_err(&client->dev, "fail : chip init\n"); | ||
341 | goto err_out; | ||
342 | } | ||
343 | |||
344 | /* backlight */ | ||
345 | props.type = BACKLIGHT_RAW; | ||
346 | props.brightness = pdata->init_brt_led; | ||
347 | props.max_brightness = pdata->max_brt_led; | ||
348 | pchip->bled = | ||
349 | backlight_device_register("lm3639_bled", pchip->dev, pchip, | ||
350 | &lm3639_bled_ops, &props); | ||
351 | if (IS_ERR(pchip->bled)) { | ||
352 | dev_err(&client->dev, "fail : backlight register\n"); | ||
353 | ret = -EIO; | ||
354 | goto err_out; | ||
355 | } | ||
356 | |||
357 | ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode); | ||
358 | if (ret < 0) { | ||
359 | dev_err(&client->dev, "failed : add sysfs entries\n"); | ||
360 | ret = -EIO; | ||
361 | goto err_bled_mode; | ||
362 | } | ||
363 | |||
364 | /* flash */ | ||
365 | pchip->cdev_flash.name = "lm3639_flash"; | ||
366 | pchip->cdev_flash.max_brightness = 16; | ||
367 | pchip->cdev_flash.brightness_set = lm3639_flash_brightness_set; | ||
368 | ret = led_classdev_register((struct device *) | ||
369 | &client->dev, &pchip->cdev_flash); | ||
370 | if (ret < 0) { | ||
371 | dev_err(&client->dev, "fail : flash register\n"); | ||
372 | ret = -EIO; | ||
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 | ret = -EIO; | ||
385 | goto err_torch; | ||
386 | } | ||
387 | |||
388 | return 0; | ||
389 | |||
390 | err_torch: | ||
391 | led_classdev_unregister(&pchip->cdev_flash); | ||
392 | err_flash: | ||
393 | device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode); | ||
394 | err_bled_mode: | ||
395 | backlight_device_unregister(pchip->bled); | ||
396 | err_out: | ||
397 | return ret; | ||
398 | } | ||
399 | |||
400 | static int __devexit lm3639_remove(struct i2c_client *client) | ||
401 | { | ||
402 | struct lm3639_chip_data *pchip = i2c_get_clientdata(client); | ||
403 | |||
404 | regmap_write(pchip->regmap, REG_ENABLE, 0x00); | ||
405 | |||
406 | if (&pchip->cdev_torch) | ||
407 | led_classdev_unregister(&pchip->cdev_torch); | ||
408 | if (&pchip->cdev_flash) | ||
409 | led_classdev_unregister(&pchip->cdev_flash); | ||
410 | if (pchip->bled) { | ||
411 | device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode); | ||
412 | backlight_device_unregister(pchip->bled); | ||
413 | } | ||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | static const struct i2c_device_id lm3639_id[] = { | ||
418 | {LM3639_NAME, 0}, | ||
419 | {} | ||
420 | }; | ||
421 | |||
422 | MODULE_DEVICE_TABLE(i2c, lm3639_id); | ||
423 | static struct i2c_driver lm3639_i2c_driver = { | ||
424 | .driver = { | ||
425 | .name = LM3639_NAME, | ||
426 | }, | ||
427 | .probe = lm3639_probe, | ||
428 | .remove = __devexit_p(lm3639_remove), | ||
429 | .id_table = lm3639_id, | ||
430 | }; | ||
431 | |||
432 | module_i2c_driver(lm3639_i2c_driver); | ||
433 | |||
434 | MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639"); | ||
435 | MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); | ||
436 | MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); | ||
437 | MODULE_LICENSE("GPL v2"); | ||