/* * Copyright (C) 2011-2012, LG Eletronics,Inc. All rights reserved. * LM3533 backlight device driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_BRIGHTNESS_lm3533 0xFF #define MAX_BRIGHTNESS_lm3528 0x7F #define DEFAULT_BRIGHTNESS 0xA5 #define MIN_BRIGHTNESS 0x0F #define I2C_BL_NAME "lm3533" #define DEFAULT_FTM_BRIGHTNESS 0x0F #define BL_ON 1 #define BL_OFF 0 static struct i2c_client *lm3533_i2c_client; struct backlight_platform_data { void (*platform_init)(void); int gpio; unsigned int mode; int max_current; int init_on_boot; int min_brightness; int max_brightness; int default_brightness; int factory_brightness; }; struct lm3533_device { struct i2c_client *client; struct backlight_device *bl_dev; int gpio; int max_current; int min_brightness; int max_brightness; int default_brightness; int factory_brightness; struct mutex bl_mutex; }; static const struct i2c_device_id lm3533_bl_id[] = { { I2C_BL_NAME, 0 }, { }, }; static int lm3533_write_reg(struct i2c_client *client, unsigned char reg, unsigned char val); static int cur_main_lcd_level = DEFAULT_BRIGHTNESS; static int saved_main_lcd_level = DEFAULT_BRIGHTNESS; static int backlight_status = BL_ON; static struct lm3533_device *main_lm3533_dev; #ifdef CONFIG_HAS_EARLYSUSPEND static struct early_suspend early_suspend; #if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \ defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL) static int is_early_suspended = false; static int requested_in_early_suspend_lcd_level= 0; #endif #endif /* CONFIG_HAS_EARLYSUSPEND */ #if !defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) && \ !defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL) static struct early_suspend * h; #endif static void lm3533_hw_reset(void) { int gpio = main_lm3533_dev->gpio; if (gpio_is_valid(gpio)) { gpio_direction_output(gpio, 1); gpio_set_value_cansleep(gpio, 1); mdelay(1); } else { pr_err("%s: gpio is not valid !!\n", __func__); } } static int lm3533_write_reg(struct i2c_client *client, unsigned char reg, unsigned char val) { int err; u8 buf[2]; struct i2c_msg msg = { client->addr, 0, 2, buf }; buf[0] = reg; buf[1] = val; err = i2c_transfer(client->adapter, &msg, 1); if (err < 0) dev_err(&client->dev, "i2c write error\n"); return 0; } static int exp_min_value = 150; static int cal_value; static char mapped_value[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 10, 16, 21, 26, 31, 35, 39, 43, 47, 51, 54, 58, 61, 64, 67, 70, 73, 76, 78, 81, 83, 86, 88, 91, 93, 95, 97, 99, 101,103, 105,107,109,111,113,114,116,118,119,121,123,124,126,127,129, 130,132,133,134,136,137,138,140,141,142,144,145,146,147,148, 149,151,152,153,154,155,156,157,158,159,160,161,162,163,164, 165,166,167,168,169,170,171,172,173,174,174,175,176,177,178, 179,179,180,181,182,183,183,184,185,186,187,187,188,189,189, 190,191,192,192,193,194,194,195,196,196,197,198,198,199,200, 200,201,202,202,203,204,204,205,205,206,207,207,208,208,209, 210,210,211,211,212,212,213,213,214,215,215,216,216,217,217, 218,218,219,219,220,220,221,221,222,222,223,223,224,224,225, 225,226,226,227,227,228,228,229,229,230,230,230,231,231,232, 232,233,233,234,234,234,235,235,236,236,237,237,237,238,238, 239,239,240,240,240,241,241,242,242,242,243,243,243,244,244, 245,245,245,246,246,247,247,247,248,248,248,249,249,250,250, 250,251,251,251,252,252,252,253,253,253,254,254,254,255,255, 255 }; static void lm3533_set_main_current_level(struct i2c_client *client, int level) { struct lm3533_device *dev; dev = (struct lm3533_device *)i2c_get_clientdata(client); if (lge_get_factory_boot() && ((lge_pm_get_cable_type() == CABLE_56K) || (lge_pm_get_cable_type() == CABLE_130K) || (lge_pm_get_cable_type() == CABLE_910K))) { level = dev->factory_brightness; } if (level == -1) level = dev->default_brightness; cur_main_lcd_level = level; dev->bl_dev->props.brightness = cur_main_lcd_level; mutex_lock(&main_lm3533_dev->bl_mutex); if (level != 0) { cal_value = mapped_value[level]; lm3533_write_reg(main_lm3533_dev->client, 0x40, cal_value); } else { lm3533_write_reg(client, 0x27, 0x00); } mutex_unlock(&main_lm3533_dev->bl_mutex); } void lm3533_backlight_on(int level) { #if defined(CONFIG_HAS_EARLYSUSPEND) && \ (defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \ defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL)) if (is_early_suspended) { requested_in_early_suspend_lcd_level = level; return; } #endif /* CONFIG_HAS_EARLYSUSPEND */ if (backlight_status == BL_OFF) { lm3533_hw_reset(); lm3533_write_reg(main_lm3533_dev->client, 0x10, 0x0); #if defined(CONFIG_LGE_BACKLIGHT_CABC) lm3533_write_reg(main_lm3533_dev->client, 0x14, 0x1); #else lm3533_write_reg(main_lm3533_dev->client, 0x14, 0x0); #endif lm3533_write_reg(main_lm3533_dev->client, 0x1A, 0x00); lm3533_write_reg(main_lm3533_dev->client, 0x1F, 0x13); lm3533_write_reg(main_lm3533_dev->client, 0x27, 0x1); #if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \ defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL) lm3533_write_reg(main_lm3533_dev->client, 0x2C, 0xC); lm3533_write_reg(main_lm3533_dev->client, 0x12, 0x9); lm3533_write_reg(main_lm3533_dev->client, 0x13, 0x9); #else lm3533_write_reg(main_lm3533_dev->client, 0x2C, 0xE); #endif } lm3533_set_main_current_level(main_lm3533_dev->client, level); backlight_status = BL_ON; return; } #if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \ defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL) || \ !defined(CONFIG_HAS_EARLYSUSPEND) void lm3533_backlight_off(void) #else void lm3533_backlight_off(struct early_suspend * h) #endif { int gpio = main_lm3533_dev->gpio; if (backlight_status == BL_OFF) return; saved_main_lcd_level = cur_main_lcd_level; lm3533_set_main_current_level(main_lm3533_dev->client, 0); backlight_status = BL_OFF; gpio_direction_output(gpio, 0); msleep(6); return; } void lm3533_lcd_backlight_set_level(int level) { if (level > MAX_BRIGHTNESS_lm3533) level = MAX_BRIGHTNESS_lm3533; if (lm3533_i2c_client != NULL) { if (level == 0) { #if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \ defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL) || \ !defined(CONFIG_HAS_EARLYSUSPEND) lm3533_backlight_off(); #else lm3533_backlight_off(h); #endif } else { lm3533_backlight_on(level); } } else { pr_err("%s(): No client\n", __func__); } } EXPORT_SYMBOL(lm3533_lcd_backlight_set_level); #if defined(CONFIG_HAS_EARLYSUSPEND) && \ (defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \ defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL)) void lm3533_early_suspend(struct early_suspend * h) { is_early_suspended = true; pr_info("%s[Start] backlight_status: %d\n", __func__, backlight_status); if (backlight_status == BL_OFF) return; lm3533_lcd_backlight_set_level(0); } void lm3533_late_resume(struct early_suspend * h) { is_early_suspended = false; pr_info("%s[Start] backlight_status: %d\n", __func__, backlight_status); if (backlight_status == BL_ON) return; lm3533_lcd_backlight_set_level(requested_in_early_suspend_lcd_level); return; } #endif /* CONFIG_HAS_EARLYSUSPEND */ static int bl_set_intensity(struct backlight_device *bd) { struct i2c_client *client = to_i2c_client(bd->dev.parent); lm3533_set_main_current_level(client, bd->props.brightness); cur_main_lcd_level = bd->props.brightness; return 0; } static int bl_get_intensity(struct backlight_device *bd) { unsigned char val = 0; val &= 0x1f; return (int)val; } static ssize_t lcd_backlight_show_level(struct device *dev, struct device_attribute *attr, char *buf) { int r; r = snprintf(buf, PAGE_SIZE, "LCD Backlight Level is " ": %d\n", cal_value); return r; } static ssize_t lcd_backlight_store_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int level; struct i2c_client *client = to_i2c_client(dev); if (!count) return -EINVAL; level = simple_strtoul(buf, NULL, 10); if (level > MAX_BRIGHTNESS_lm3533) level = MAX_BRIGHTNESS_lm3533; lm3533_set_main_current_level(client, level); cur_main_lcd_level = level; return count; } static int lm3533_bl_resume(struct i2c_client *client) { #if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \ defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL) lm3533_lcd_backlight_set_level(saved_main_lcd_level); #else lm3533_backlight_on(saved_main_lcd_level); #endif return 0; } static int lm3533_bl_suspend(struct i2c_client *client, pm_message_t state) { printk(KERN_INFO"%s: new state: %d\n", __func__, state.event); #if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \ defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL) || \ !defined(CONFIG_HAS_EARLYSUSPEND) lm3533_lcd_backlight_set_level(saved_main_lcd_level); #else lm3533_backlight_off(h); #endif return 0; } static ssize_t lcd_backlight_show_on_off(struct device *dev, struct device_attribute *attr, char *buf) { int r = 0; pr_info("%s received (prev backlight_status: %s)\n", __func__, backlight_status ? "ON" : "OFF"); return r; } static ssize_t lcd_backlight_store_on_off(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int on_off; struct i2c_client *client = to_i2c_client(dev); if (!count) return -EINVAL; pr_info("%s received (prev backlight_status: %s)\n", __func__, backlight_status ? "ON" : "OFF"); on_off = simple_strtoul(buf, NULL, 10); printk(KERN_ERR "%d", on_off); if (on_off == 1) { lm3533_bl_resume(client); } else if (on_off == 0) lm3533_bl_suspend(client, PMSG_SUSPEND); return count; } static ssize_t lcd_backlight_show_exp_min_value(struct device *dev, struct device_attribute *attr, char *buf) { int r; r = snprintf(buf, PAGE_SIZE, "LCD Backlight : %d\n", exp_min_value); return r; } static ssize_t lcd_backlight_store_exp_min_value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; if (!count) return -EINVAL; value = simple_strtoul(buf, NULL, 10); exp_min_value = value; return count; } DEVICE_ATTR(lm3533_level, 0644, lcd_backlight_show_level, lcd_backlight_store_level); DEVICE_ATTR(lm3533_backlight_on_off, 0644, lcd_backlight_show_on_off, lcd_backlight_store_on_off); DEVICE_ATTR(lm3533_exp_min_value, 0644, lcd_backlight_show_exp_min_value, lcd_backlight_store_exp_min_value); static struct backlight_ops lm3533_bl_ops = { .update_status = bl_set_intensity, .get_brightness = bl_get_intensity, }; static int lm3533_probe(struct i2c_client *i2c_dev, const struct i2c_device_id *id) { struct backlight_platform_data *pdata; struct lm3533_device *dev; struct backlight_device *bl_dev; struct backlight_properties props; int err; pr_info("%s: i2c probe start\n", __func__); pdata = i2c_dev->dev.platform_data; lm3533_i2c_client = i2c_dev; dev = kzalloc(sizeof(struct lm3533_device), GFP_KERNEL); if (dev == NULL) { dev_err(&i2c_dev->dev, "fail alloc for lm3533_device\n"); return 0; } pr_info("%s: gpio = %d\n", __func__,pdata->gpio); if (pdata->gpio && gpio_request(pdata->gpio, "lm3533 reset") != 0) { return -ENODEV; } main_lm3533_dev = dev; memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = MAX_BRIGHTNESS_lm3533; bl_dev = backlight_device_register(I2C_BL_NAME, &i2c_dev->dev, NULL, &lm3533_bl_ops, &props); bl_dev->props.max_brightness = MAX_BRIGHTNESS_lm3533; bl_dev->props.brightness = DEFAULT_BRIGHTNESS; bl_dev->props.power = FB_BLANK_UNBLANK; dev->bl_dev = bl_dev; dev->client = i2c_dev; dev->gpio = pdata->gpio; dev->max_current = pdata->max_current; dev->min_brightness = pdata->min_brightness; dev->default_brightness = pdata->default_brightness; dev->max_brightness = pdata->max_brightness; i2c_set_clientdata(i2c_dev, dev); if (pdata->factory_brightness <= 0) dev->factory_brightness = DEFAULT_FTM_BRIGHTNESS; else dev->factory_brightness = pdata->factory_brightness; mutex_init(&dev->bl_mutex); err = device_create_file(&i2c_dev->dev, &dev_attr_lm3533_level); err = device_create_file(&i2c_dev->dev, &dev_attr_lm3533_backlight_on_off); err = device_create_file(&i2c_dev->dev, &dev_attr_lm3533_exp_min_value); #ifdef CONFIG_HAS_EARLYSUSPEND #if defined(CONFIG_FB_MSM_MIPI_LGIT_CMD_WVGA_INVERSE_PT_PANEL) || \ defined(CONFIG_FB_MSM_MIPI_LGIT_VIDEO_WVGA_INVERSE_PT_PANEL) early_suspend.suspend = lm3533_early_suspend; early_suspend.resume = lm3533_late_resume; #else early_suspend.suspend = lm3533_backlight_off; #endif register_early_suspend(&early_suspend); #endif /* CONFIG_HAS_EARLYSUSPEND */ return 0; } static int lm3533_remove(struct i2c_client *i2c_dev) { struct lm3533_device *dev; int gpio = main_lm3533_dev->gpio; device_remove_file(&i2c_dev->dev, &dev_attr_lm3533_level); device_remove_file(&i2c_dev->dev, &dev_attr_lm3533_backlight_on_off); dev = (struct lm3533_device *)i2c_get_clientdata(i2c_dev); backlight_device_unregister(dev->bl_dev); i2c_set_clientdata(i2c_dev, NULL); if (gpio_is_valid(gpio)) gpio_free(gpio); return 0; } static struct i2c_driver main_lm3533_driver = { .probe = lm3533_probe, .remove = lm3533_remove, .suspend = NULL, .resume = NULL, .id_table = lm3533_bl_id, .driver = { .name = I2C_BL_NAME, .owner = THIS_MODULE, }, }; static int __init lcd_backlight_init(void) { static int err; err = i2c_add_driver(&main_lm3533_driver); return err; } module_init(lcd_backlight_init); MODULE_DESCRIPTION("LM3533 Backlight Control"); MODULE_AUTHOR("Jaeseong Gim "); MODULE_LICENSE("GPL");