diff options
Diffstat (limited to 'drivers/leds/wmt-lcd-backlight.c')
-rwxr-xr-x | drivers/leds/wmt-lcd-backlight.c | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/drivers/leds/wmt-lcd-backlight.c b/drivers/leds/wmt-lcd-backlight.c new file mode 100755 index 00000000..d51b7c5b --- /dev/null +++ b/drivers/leds/wmt-lcd-backlight.c @@ -0,0 +1,329 @@ +/************************************************************** +Copyright (c) 2008 WonderMedia Technologies, Inc. + +Module Name: + $Workfile: wmt-lcd-backlight.c $ +Abstract: + This program is the WMT LCD backlight driver for Android 1.6 system. + Andriod1.6 API adjusts the LCD backlight by writing follwing file: + /sys/class/leds/lcd-backlight/brightness + Use WMT PWM to control the LCD backlight +Revision History: + Jan.08.2010 First Created by HowayHuo + +**************************************************************/ +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/leds.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "../char/wmt-pwm.h" + +#define BACKLIGHT_ON 0 +#define BACKLIGHT_CLOSE 1 + +struct wmt_pwm_reg_t { + unsigned int id; + unsigned int scalar; + unsigned int period; + unsigned int duty; + unsigned int config; + unsigned int status; +}; + +static struct wmt_pwm_reg_t g_pwm_setting; + +#define ENV_LCD_BACKLIGHT "wmt.display.pwm" + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); + +struct back_light_limit { + int max; + int min; + int diff; + int step; + int allheigh; +}; + +struct back_light_limit g_backlight_limit; + +static unsigned int g_oldbrightness; +static unsigned int g_isresume = 0; + +extern void vpp_set_lvds_blank(int blank); + + +void backlight_get_env(void) +{ + unsigned char buf[100]; + int varlen = 100; + int max = -1, min = -1; + int val = -1; + + memset((char *)&g_backlight_limit, 0, sizeof(struct back_light_limit)); + if( wmt_getsyspara(ENV_LCD_BACKLIGHT,buf,&varlen) == 0) { + //printk("\r\n backlight_get_env : %s = %s \n",ENV_LCD_BACKLIGHT,buf); + sscanf(buf,"%x",&val); + g_pwm_setting.id = val&0x0F; + if (g_pwm_setting.id > 1) { + printk("PWM%d is invaild , using default PWM0\n", g_pwm_setting.id); + g_pwm_setting.id = 0; + } + min = (val >> 8)&0xff; + max = (val >> 16)&0xff; + if ((min >= max) || (max > 100) || (min < 0)) { + printk("backlight_get_env error : max = %d , min = %d ", max, min); + goto out; + } + g_backlight_limit.max = max; + g_backlight_limit.min = min; + } else { + printk("## Warning: %s not defined\n",ENV_LCD_BACKLIGHT); + goto out; + } + return; +out: + g_pwm_setting.id = 0; + g_backlight_limit.max = 90; + g_backlight_limit.min = 10; +} + + +/* + * For simplicity, we use "brightness" as if it were a linear function + * of PWM duty cycle. However, a logarithmic function of duty cycle is + * probably a better match for perceived brightness: two is half as bright + * as four, four is half as bright as eight, etc + */ +static void lcd_brightness(struct led_classdev *cdev, enum led_brightness b) +{ + unsigned int duty; + int val = 0; + + /*printk(KERN_ALERT "[%s]pwm_no = %d pwm_period = %d b= %d\n", __func__, pwm_no,pwm_period, b);*/ + + if (g_isresume == 1) { + if (b <= g_oldbrightness) + return; + g_isresume = 0; + } + g_oldbrightness = b; + if (!b) { + g_pwm_setting.status = BACKLIGHT_CLOSE; + pwm_set_gpio(g_pwm_setting.id, 0); + /*vpp_set_lvds_blank(1);*/ + return; + } + + if ((b != 255) || (!g_backlight_limit.allheigh)) { + val = (b*g_backlight_limit.diff*1000)/255; + duty = (val * g_backlight_limit.step) /1000; + duty = (duty + g_backlight_limit.min)/1000; + if(duty) { + if(--duty > 0xFFF) + duty = 0xFFF; + } + } else + duty = g_backlight_limit.max/1000; + + //printk("max = %d , min = %d , b= %d , duty = %d\n", g_backlight_limit.max, g_backlight_limit.min, b,duty); + duty = duty-1; + + pwm_set_duty(g_pwm_setting.id, duty); + + //fix : framework do not set brightness to 0 when suspend + //if (g_pwm_setting.status == BACKLIGHT_CLOSE) { + if (!get_lcd_power()) { + /*vpp_set_lvds_blank(0);*/ + pwm_set_gpio(g_pwm_setting.id, 1); + g_pwm_setting.status = BACKLIGHT_ON; + } +} + +/* + * NOTE: we reuse the platform_data structure of GPIO leds, + * but repurpose its "gpio" number as a PWM channel number. + */ +static int lcd_backlight_probe(struct platform_device *pdev) +{ + const struct gpio_led_platform_data *pdata; + struct led_classdev *leds; + int i; + int status; + unsigned int brightness; + unsigned int pwm_period; + + pdata = pdev->dev.platform_data; + if (!pdata || pdata->num_leds < 1) + return -ENODEV; + + leds = kcalloc(pdata->num_leds, sizeof(*leds), GFP_KERNEL); + if (!leds) + return -ENOMEM; + + auto_pll_divisor(DEV_PWM,CLK_ENABLE,0,0); + + pwm_period = pwm_get_period(g_pwm_setting.id) + 1; + pwm_period = pwm_period*1000; + backlight_get_env(); + g_backlight_limit.allheigh = 0; + if (g_backlight_limit.max == 100) + g_backlight_limit.allheigh = 1; + g_backlight_limit.diff = (g_backlight_limit.max - g_backlight_limit.min); + g_backlight_limit.max = (pwm_period*g_backlight_limit.max)/100; + g_backlight_limit.min = (pwm_period*g_backlight_limit.min)/100; + g_backlight_limit.step = (g_backlight_limit.max - g_backlight_limit.min) / g_backlight_limit.diff; + + /*calculate the default brightness*/ + brightness = (pwm_get_duty(g_pwm_setting.id) * 255) / pwm_period; + + for (i = 0; i < pdata->num_leds; i++) { + struct led_classdev *led = leds + i; + const struct gpio_led *dat = pdata->leds + i; + + led->name = dat->name; + led->brightness = brightness; + led->brightness_set = lcd_brightness; + led->default_trigger = dat->default_trigger; + + /* Hand it over to the LED framework */ + status = led_classdev_register(&pdev->dev, led); + if (status < 0) { + goto err; + } + } + + platform_set_drvdata(pdev, leds); + g_pwm_setting.status = BACKLIGHT_ON; + return 0; + +err: + if (i > 0) { + for (i = i - 1; i >= 0; i--) { + led_classdev_unregister(leds + i); + } + } + kfree(leds); + + return status; +} + +static int __exit lcd_backlight_remove(struct platform_device *pdev) +{ + const struct gpio_led_platform_data *pdata; + struct led_classdev *leds; + unsigned i; + + pdata = pdev->dev.platform_data; + leds = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->num_leds; i++) { + struct led_classdev *led = leds + i; + + led_classdev_unregister(led); + } + + kfree(leds); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static int lcd_backlight_suspend +( + struct platform_device *pdev, /*!<; // a pointer point to struct device */ + pm_message_t state /*!<; // suspend state */ +) +{ + unsigned int addr; + + addr = PWM_CTRL_REG_ADDR + (0x10 * g_pwm_setting.id); + g_pwm_setting.config = (REG32_VAL(addr) & 0xFFF); + pwm_set_control(g_pwm_setting.id, 0); + addr = PWM_PERIOD_REG_ADDR + (0x10 * g_pwm_setting.id); + g_pwm_setting.period = (REG32_VAL(addr) & 0xFFF); + + addr = PWM_SCALAR_REG_ADDR + (0x10 * g_pwm_setting.id); + g_pwm_setting.scalar = (REG32_VAL(addr) & 0xFFF); + //g_pwm_setting.duty = 0; // for android , AP will set duty to 0 + + return 0; +} + +static int lcd_backlight_resume +( + struct platform_device *pdev /*!<; // a pointer point to struct device */ +) +{ + pwm_set_scalar(g_pwm_setting.id, g_pwm_setting.scalar); + pwm_set_period(g_pwm_setting.id, g_pwm_setting.period); + + pwm_set_duty(g_pwm_setting.id, 5); + pwm_set_control(g_pwm_setting.id, g_pwm_setting.config); + + g_isresume = 1; + + return 0; +} + + +static struct gpio_led lcd_pwm[] = { + { + .name = "lcd-backlight", + }, +}; + + +static struct gpio_led_platform_data lcd_backlight_data = { + .leds = lcd_pwm, + .num_leds = ARRAY_SIZE(lcd_pwm), +}; + +static struct platform_device lcd_backlight_device = { + .name = "lcd-backlight", + .id = 0, + .dev = { .platform_data = &lcd_backlight_data, + }, +}; + +static struct platform_driver lcd_backlight_driver = { + .driver = { + .name = "lcd-backlight", + .owner = THIS_MODULE, + }, + /* REVISIT add suspend() and resume() methods */ + .probe = lcd_backlight_probe, + .remove = __exit_p(lcd_backlight_remove), + .suspend = lcd_backlight_suspend, + .resume = lcd_backlight_resume +}; + +static int __init lcd_backlight_init(void) +{ + int ret; + + ret = platform_device_register(&lcd_backlight_device); + if(ret) { + printk("[lcd_backlight_init]Error: Can not register LCD backlight device\n"); + return ret; + } + //ret = platform_driver_probe(&lcd_backlight_driver, lcd_backlight_probe); + ret = platform_driver_register(&lcd_backlight_driver); + if(ret) { + printk("[lcd_backlight_init]Error: Can not register LCD backlight driver\n"); + platform_device_unregister(&lcd_backlight_device); + return ret; + } + return 0; +} +module_init(lcd_backlight_init); + +static void __exit lcd_backlight_exit(void) +{ + platform_driver_unregister(&lcd_backlight_driver); + platform_device_unregister(&lcd_backlight_device); +} +module_exit(lcd_backlight_exit); + +MODULE_DESCRIPTION("Driver for LCD with PWM-controlled brightness"); +MODULE_LICENSE("GPL"); + |