blob: 006712bf16bcf8a9974de1a23d03701da676af29 [file] [log] [blame]
Jaeseong GIM07bd2bb2012-06-16 18:55:52 -07001/*
2 * Copyright (C) 2011-2012, LG Eletronics,Inc. All rights reserved.
3 * LM3533 backlight device driver
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 as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/platform_device.h>
23#include <linux/kernel.h>
24#include <linux/spinlock.h>
25#include <linux/backlight.h>
26#include <linux/fb.h>
27#include <linux/delay.h>
28#include <linux/gpio.h>
29#include <mach/board.h>
30
31#include <mach/board_lge.h>
32#include <linux/earlysuspend.h>
33
34#define MAX_BRIGHTNESS_lm3533 0xFF
35#define MAX_BRIGHTNESS_lm3528 0x7F
36#define DEFAULT_BRIGHTNESS 0xA5
37#define MIN_BRIGHTNESS 0x0F
38#define I2C_BL_NAME "lm3533"
39
40#define DEFAULT_FTM_BRIGHTNESS 0x0F
41
42#define BL_ON 1
43#define BL_OFF 0
44
45static struct i2c_client *lm3533_i2c_client;
46
47struct backlight_platform_data {
48 void (*platform_init)(void);
49 int gpio;
50 unsigned int mode;
51 int max_current;
52 int init_on_boot;
53 int min_brightness;
54 int max_brightness;
55 int default_brightness;
56 int factory_brightness;
57};
58
59struct lm3533_device {
60 struct i2c_client *client;
61 struct backlight_device *bl_dev;
62 int gpio;
63 int max_current;
64 int min_brightness;
65 int max_brightness;
66 int default_brightness;
67 int factory_brightness;
68 struct mutex bl_mutex;
69};
70
71static const struct i2c_device_id lm3533_bl_id[] = {
72 { I2C_BL_NAME, 0 },
73 { },
74};
75
76static int lm3533_write_reg(struct i2c_client *client,
77 unsigned char reg, unsigned char val);
78
79static int cur_main_lcd_level = DEFAULT_BRIGHTNESS;
80static int saved_main_lcd_level = DEFAULT_BRIGHTNESS;
81
82static int backlight_status = BL_ON;
83static struct lm3533_device *main_lm3533_dev;
84#ifdef CONFIG_HAS_EARLYSUSPEND
85static struct early_suspend early_suspend;
86
87#if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \
88 defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL)
89static int is_early_suspended = false;
90static int requested_in_early_suspend_lcd_level= 0;
91#endif
92
93#endif /* CONFIG_HAS_EARLYSUSPEND */
94
95#if !defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) && \
96 !defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL)
97static struct early_suspend * h;
98#endif
99
100static void lm3533_hw_reset(void)
101{
102 int gpio = main_lm3533_dev->gpio;
103
104 if (gpio_is_valid(gpio)) {
105 gpio_direction_output(gpio, 1);
106 gpio_set_value_cansleep(gpio, 1);
107 mdelay(1);
108 } else {
109 pr_err("%s: gpio is not valid !!\n", __func__);
110 }
111}
112
113static int lm3533_write_reg(struct i2c_client *client,
114 unsigned char reg, unsigned char val)
115{
116 int err;
117 u8 buf[2];
118 struct i2c_msg msg = {
119 client->addr, 0, 2, buf
120 };
121
122 buf[0] = reg;
123 buf[1] = val;
124
125 err = i2c_transfer(client->adapter, &msg, 1);
126 if (err < 0)
127 dev_err(&client->dev, "i2c write error\n");
128 return 0;
129}
130
131static int exp_min_value = 150;
132static int cal_value;
133static char mapped_value[256] = {
134 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,
135 10, 16, 21, 26, 31, 35, 39, 43, 47, 51, 54, 58, 61, 64, 67,
136 70, 73, 76, 78, 81, 83, 86, 88, 91, 93, 95, 97, 99, 101,103,
137 105,107,109,111,113,114,116,118,119,121,123,124,126,127,129,
138 130,132,133,134,136,137,138,140,141,142,144,145,146,147,148,
139 149,151,152,153,154,155,156,157,158,159,160,161,162,163,164,
140 165,166,167,168,169,170,171,172,173,174,174,175,176,177,178,
141 179,179,180,181,182,183,183,184,185,186,187,187,188,189,189,
142 190,191,192,192,193,194,194,195,196,196,197,198,198,199,200,
143 200,201,202,202,203,204,204,205,205,206,207,207,208,208,209,
144 210,210,211,211,212,212,213,213,214,215,215,216,216,217,217,
145 218,218,219,219,220,220,221,221,222,222,223,223,224,224,225,
146 225,226,226,227,227,228,228,229,229,230,230,230,231,231,232,
147 232,233,233,234,234,234,235,235,236,236,237,237,237,238,238,
148 239,239,240,240,240,241,241,242,242,242,243,243,243,244,244,
149 245,245,245,246,246,247,247,247,248,248,248,249,249,250,250,
150 250,251,251,251,252,252,252,253,253,253,254,254,254,255,255,
151 255
152};
153
154static void lm3533_set_main_current_level(struct i2c_client *client, int level)
155{
156 struct lm3533_device *dev;
157 dev = (struct lm3533_device *)i2c_get_clientdata(client);
158
159 if (lge_get_factory_boot() &&
160 ((lge_pm_get_cable_type() == CABLE_56K) ||
161 (lge_pm_get_cable_type() == CABLE_130K) ||
162 (lge_pm_get_cable_type() == CABLE_910K))) {
163 level = dev->factory_brightness;
164 }
165
166 if (level == -1)
167 level = dev->default_brightness;
168
169 cur_main_lcd_level = level;
170 dev->bl_dev->props.brightness = cur_main_lcd_level;
171
172 mutex_lock(&main_lm3533_dev->bl_mutex);
173 if (level != 0) {
174 cal_value = mapped_value[level];
175 lm3533_write_reg(main_lm3533_dev->client, 0x40, cal_value);
176 } else {
177 lm3533_write_reg(client, 0x27, 0x00);
178 }
179 mutex_unlock(&main_lm3533_dev->bl_mutex);
180}
181
182void lm3533_backlight_on(int level)
183{
184
185#if defined(CONFIG_HAS_EARLYSUSPEND) && \
186 (defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \
187 defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL))
188
189 if (is_early_suspended) {
190 requested_in_early_suspend_lcd_level = level;
191 return;
192 }
193#endif /* CONFIG_HAS_EARLYSUSPEND */
194 if (backlight_status == BL_OFF) {
195 lm3533_hw_reset();
196 lm3533_write_reg(main_lm3533_dev->client, 0x10, 0x0);
197#if defined(CONFIG_LGE_BACKLIGHT_CABC)
198 lm3533_write_reg(main_lm3533_dev->client, 0x14, 0x1);
199#else
200 lm3533_write_reg(main_lm3533_dev->client, 0x14, 0x0);
201#endif
202 lm3533_write_reg(main_lm3533_dev->client, 0x1A, 0x00);
203 lm3533_write_reg(main_lm3533_dev->client, 0x1F, 0x13);
204 lm3533_write_reg(main_lm3533_dev->client, 0x27, 0x1);
205#if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \
206 defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL)
207 lm3533_write_reg(main_lm3533_dev->client, 0x2C, 0xC);
208 lm3533_write_reg(main_lm3533_dev->client, 0x12, 0x9);
209 lm3533_write_reg(main_lm3533_dev->client, 0x13, 0x9);
210#else
211 lm3533_write_reg(main_lm3533_dev->client, 0x2C, 0xE);
212#endif
213 }
214
215 lm3533_set_main_current_level(main_lm3533_dev->client, level);
216 backlight_status = BL_ON;
217
218 return;
219}
220
221#if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \
222 defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL) || \
223 !defined(CONFIG_HAS_EARLYSUSPEND)
224void lm3533_backlight_off(void)
225#else
226void lm3533_backlight_off(struct early_suspend * h)
227#endif
228{
229 int gpio = main_lm3533_dev->gpio;
230
231 if (backlight_status == BL_OFF)
232 return;
233 saved_main_lcd_level = cur_main_lcd_level;
234 lm3533_set_main_current_level(main_lm3533_dev->client, 0);
235 backlight_status = BL_OFF;
236
237 gpio_direction_output(gpio, 0);
238 msleep(6);
239
240 return;
241}
242
243void lm3533_lcd_backlight_set_level(int level)
244{
245 if (level > MAX_BRIGHTNESS_lm3533)
246 level = MAX_BRIGHTNESS_lm3533;
247
248 if (lm3533_i2c_client != NULL) {
249 if (level == 0) {
250#if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \
251 defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL) || \
252 !defined(CONFIG_HAS_EARLYSUSPEND)
253 lm3533_backlight_off();
254#else
255 lm3533_backlight_off(h);
256#endif
257 } else {
258 lm3533_backlight_on(level);
259 }
260 } else {
261 pr_err("%s(): No client\n", __func__);
262 }
263}
264EXPORT_SYMBOL(lm3533_lcd_backlight_set_level);
265
266#if defined(CONFIG_HAS_EARLYSUSPEND) && \
267 (defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \
268 defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL))
269
270void lm3533_early_suspend(struct early_suspend * h)
271{
272 is_early_suspended = true;
273
274 pr_info("%s[Start] backlight_status: %d\n", __func__,
275 backlight_status);
276 if (backlight_status == BL_OFF)
277 return;
278
279 lm3533_lcd_backlight_set_level(0);
280}
281
282void lm3533_late_resume(struct early_suspend * h)
283{
284 is_early_suspended = false;
285
286 pr_info("%s[Start] backlight_status: %d\n", __func__,
287 backlight_status);
288 if (backlight_status == BL_ON)
289 return;
290
291 lm3533_lcd_backlight_set_level(requested_in_early_suspend_lcd_level);
292 return;
293}
294#endif /* CONFIG_HAS_EARLYSUSPEND */
295
296static int bl_set_intensity(struct backlight_device *bd)
297{
298 struct i2c_client *client = to_i2c_client(bd->dev.parent);
299
300 lm3533_set_main_current_level(client, bd->props.brightness);
301 cur_main_lcd_level = bd->props.brightness;
302
303 return 0;
304}
305
306static int bl_get_intensity(struct backlight_device *bd)
307{
308 unsigned char val = 0;
309 val &= 0x1f;
310 return (int)val;
311}
312
313static ssize_t lcd_backlight_show_level(struct device *dev,
314 struct device_attribute *attr, char *buf)
315{
316 int r;
317 r = snprintf(buf, PAGE_SIZE, "LCD Backlight Level is "
318 ": %d\n", cal_value);
319 return r;
320}
321
322static ssize_t lcd_backlight_store_level(struct device *dev,
323 struct device_attribute *attr,
324 const char *buf, size_t count)
325{
326 int level;
327 struct i2c_client *client = to_i2c_client(dev);
328
329 if (!count)
330 return -EINVAL;
331
332 level = simple_strtoul(buf, NULL, 10);
333
334 if (level > MAX_BRIGHTNESS_lm3533)
335 level = MAX_BRIGHTNESS_lm3533;
336
337 lm3533_set_main_current_level(client, level);
338 cur_main_lcd_level = level;
339
340 return count;
341}
342
343static int lm3533_bl_resume(struct i2c_client *client)
344{
345#if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \
346 defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL)
347 lm3533_lcd_backlight_set_level(saved_main_lcd_level);
348#else
349 lm3533_backlight_on(saved_main_lcd_level);
350#endif
351 return 0;
352}
353
354static int lm3533_bl_suspend(struct i2c_client *client, pm_message_t state)
355{
356 printk(KERN_INFO"%s: new state: %d\n", __func__, state.event);
357
358#if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \
359 defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL) || \
360 !defined(CONFIG_HAS_EARLYSUSPEND)
361 lm3533_lcd_backlight_set_level(saved_main_lcd_level);
362#else
363 lm3533_backlight_off(h);
364#endif
365 return 0;
366}
367
368static ssize_t lcd_backlight_show_on_off(struct device *dev,
369 struct device_attribute *attr, char *buf)
370{
371 int r = 0;
372 pr_info("%s received (prev backlight_status: %s)\n", __func__,
373 backlight_status ? "ON" : "OFF");
374 return r;
375}
376
377static ssize_t lcd_backlight_store_on_off(struct device *dev,
378 struct device_attribute *attr,
379 const char *buf, size_t count)
380{
381 int on_off;
382 struct i2c_client *client = to_i2c_client(dev);
383
384 if (!count)
385 return -EINVAL;
386
387 pr_info("%s received (prev backlight_status: %s)\n", __func__,
388 backlight_status ? "ON" : "OFF");
389
390 on_off = simple_strtoul(buf, NULL, 10);
391
392 printk(KERN_ERR "%d", on_off);
393
394 if (on_off == 1) {
395 lm3533_bl_resume(client);
396 } else if (on_off == 0)
397 lm3533_bl_suspend(client, PMSG_SUSPEND);
398
399 return count;
400}
401static ssize_t lcd_backlight_show_exp_min_value(struct device *dev,
402 struct device_attribute *attr, char *buf)
403{
404 int r;
405 r = snprintf(buf, PAGE_SIZE, "LCD Backlight : %d\n", exp_min_value);
406 return r;
407}
408
409static ssize_t lcd_backlight_store_exp_min_value(struct device *dev,
410 struct device_attribute *attr,
411 const char *buf, size_t count)
412{
413 int value;
414
415 if (!count)
416 return -EINVAL;
417
418 value = simple_strtoul(buf, NULL, 10);
419 exp_min_value = value;
420
421 return count;
422}
423
424DEVICE_ATTR(lm3533_level, 0644, lcd_backlight_show_level,
425 lcd_backlight_store_level);
426DEVICE_ATTR(lm3533_backlight_on_off, 0644, lcd_backlight_show_on_off,
427 lcd_backlight_store_on_off);
428DEVICE_ATTR(lm3533_exp_min_value, 0644, lcd_backlight_show_exp_min_value,
429 lcd_backlight_store_exp_min_value);
430
431static struct backlight_ops lm3533_bl_ops = {
432 .update_status = bl_set_intensity,
433 .get_brightness = bl_get_intensity,
434};
435
436static int lm3533_probe(struct i2c_client *i2c_dev,
437 const struct i2c_device_id *id)
438{
439 struct backlight_platform_data *pdata;
440 struct lm3533_device *dev;
441 struct backlight_device *bl_dev;
442 struct backlight_properties props;
443 int err;
444
445 pr_info("%s: i2c probe start\n", __func__);
446
447 pdata = i2c_dev->dev.platform_data;
448 lm3533_i2c_client = i2c_dev;
449
450 dev = kzalloc(sizeof(struct lm3533_device), GFP_KERNEL);
451 if (dev == NULL) {
452 dev_err(&i2c_dev->dev, "fail alloc for lm3533_device\n");
453 return 0;
454 }
455
456 pr_info("%s: gpio = %d\n", __func__,pdata->gpio);
457
458 if (pdata->gpio && gpio_request(pdata->gpio, "lm3533 reset") != 0) {
459 return -ENODEV;
460 }
461
462 main_lm3533_dev = dev;
463
464 memset(&props, 0, sizeof(struct backlight_properties));
465 props.type = BACKLIGHT_RAW;
466
467 props.max_brightness = MAX_BRIGHTNESS_lm3533;
468 bl_dev = backlight_device_register(I2C_BL_NAME, &i2c_dev->dev,
469 NULL, &lm3533_bl_ops, &props);
470 bl_dev->props.max_brightness = MAX_BRIGHTNESS_lm3533;
471 bl_dev->props.brightness = DEFAULT_BRIGHTNESS;
472 bl_dev->props.power = FB_BLANK_UNBLANK;
473
474 dev->bl_dev = bl_dev;
475 dev->client = i2c_dev;
476 dev->gpio = pdata->gpio;
477 dev->max_current = pdata->max_current;
478 dev->min_brightness = pdata->min_brightness;
479 dev->default_brightness = pdata->default_brightness;
480 dev->max_brightness = pdata->max_brightness;
481 i2c_set_clientdata(i2c_dev, dev);
482
483 if (pdata->factory_brightness <= 0)
484 dev->factory_brightness = DEFAULT_FTM_BRIGHTNESS;
485 else
486 dev->factory_brightness = pdata->factory_brightness;
487
488 mutex_init(&dev->bl_mutex);
489
490 err = device_create_file(&i2c_dev->dev,
491 &dev_attr_lm3533_level);
492 err = device_create_file(&i2c_dev->dev,
493 &dev_attr_lm3533_backlight_on_off);
494 err = device_create_file(&i2c_dev->dev,
495 &dev_attr_lm3533_exp_min_value);
496
497#ifdef CONFIG_HAS_EARLYSUSPEND
498#if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \
499 defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL)
500 early_suspend.suspend = lm3533_early_suspend;
501 early_suspend.resume = lm3533_late_resume;
502#else
503 early_suspend.suspend = lm3533_backlight_off;
504#endif
505 register_early_suspend(&early_suspend);
506#endif /* CONFIG_HAS_EARLYSUSPEND */
507 return 0;
508}
509
510static int lm3533_remove(struct i2c_client *i2c_dev)
511{
512 struct lm3533_device *dev;
513 int gpio = main_lm3533_dev->gpio;
514
515 device_remove_file(&i2c_dev->dev, &dev_attr_lm3533_level);
516 device_remove_file(&i2c_dev->dev, &dev_attr_lm3533_backlight_on_off);
517 dev = (struct lm3533_device *)i2c_get_clientdata(i2c_dev);
518 backlight_device_unregister(dev->bl_dev);
519 i2c_set_clientdata(i2c_dev, NULL);
520
521 if (gpio_is_valid(gpio))
522 gpio_free(gpio);
523 return 0;
524}
525
526static struct i2c_driver main_lm3533_driver = {
527 .probe = lm3533_probe,
528 .remove = lm3533_remove,
529 .suspend = NULL,
530 .resume = NULL,
531 .id_table = lm3533_bl_id,
532 .driver = {
533 .name = I2C_BL_NAME,
534 .owner = THIS_MODULE,
535 },
536};
537
538
539static int __init lcd_backlight_init(void)
540{
541 static int err;
542
543 err = i2c_add_driver(&main_lm3533_driver);
544
545 return err;
546}
547
548module_init(lcd_backlight_init);
549
550MODULE_DESCRIPTION("LM3533 Backlight Control");
551MODULE_AUTHOR("Jaeseong Gim <jaeseong.gim@lge.com>");
552MODULE_LICENSE("GPL");