mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
8b589d364f
bring-up lm3530 backlight for mako board. Change-Id: Ib1746f0ce13603e7f981e61f9b9ea83f91fa2b1a
552 lines
15 KiB
C
552 lines
15 KiB
C
/*
|
|
* 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 <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/backlight.h>
|
|
#include <linux/fb.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/gpio.h>
|
|
#include <mach/board.h>
|
|
|
|
#include <mach/board_lge.h>
|
|
#include <linux/earlysuspend.h>
|
|
|
|
#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 <jaeseong.gim@lge.com>");
|
|
MODULE_LICENSE("GPL");
|