/*++ linux/drivers/video/backlight/wmt_bl.c Copyright (c) 2013 WonderMedia Technologies, Inc. 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, see . WonderMedia Technologies, Inc. 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. --*/ #include #include #include #include #include #include #include #include #include "wmt_bl.h" #define LTH_BRIGHTNESS 0 #define DFT_BRIGHTNESS 180 #define DFT_PWM0_FREQ 4 /* kHz */ static struct { int gpio; int active; } power = { -1, 0, }; static int backlight_delay = 0; static struct led_trigger *genesis_led; static BLOCKING_NOTIFIER_HEAD(bl_chain_head); int register_bl_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&bl_chain_head, nb); } EXPORT_SYMBOL_GPL(register_bl_notifier); int unregister_bl_notifier(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&bl_chain_head, nb); } EXPORT_SYMBOL_GPL(unregister_bl_notifier); static inline int bl_notifier_call_chain(unsigned long val) { int ret = blocking_notifier_call_chain(&bl_chain_head, val, NULL); return notifier_to_errno(ret); } static void backlight_on(bool on) { static int old_state = -1; if (!gpio_is_valid(power.gpio)) return; if (old_state != on) { if (old_state == false) { /* delay 100 ms between 'lcd power on' and 'backlight * power on' at resume sequence. */ msleep(100); /* wmt.backlight.delay */ msleep(backlight_delay); } gpio_direction_output(power.gpio, on ? power.active : !power.active); led_trigger_event(genesis_led, on ? LED_FULL : LED_OFF); old_state = on; bl_notifier_call_chain(on ? BL_OPEN : BL_CLOSE); } } static int wmt_bl_notify(struct device *dev, int brightness) { return brightness; } static void wmt_bl_notify_after(struct device *dev, int brightness) { backlight_on(brightness != 0); } #define KHZ2PICOS(hz) (1000000000/(hz)) static struct platform_pwm_backlight_data wm8880_pwmbl_data = { .pwm_id = 0, .invert = 1, .lth_brightness = LTH_BRIGHTNESS, /* low threshold */ .hth_brightness = 256, /* high threshold */ .max_brightness = 256, .dft_brightness = DFT_BRIGHTNESS, .pwm_period_ns = KHZ2PICOS(DFT_PWM0_FREQ*1000), /* revisit when clocks are implemented */ .notify = wmt_bl_notify, .notify_after = wmt_bl_notify_after, }; struct platform_device wm8880_device_pwmbl = { .name = "pwm-backlight", .id = 0, .dev = { .platform_data = &wm8880_pwmbl_data, }, }; static int parse_backlight_param(void) { static char env[] = "wmt.backlight.param"; struct platform_pwm_backlight_data *pdata = &wm8880_pwmbl_data; uint8_t buf[64]; size_t l = sizeof(buf); int pwm_freq = DFT_PWM0_FREQ; int rc; if (wmt_getsyspara(env, buf, &l)) { pr_err("please set %s\n", env); return -EINVAL; } /* wmt.backlight.param * :::: * ::: */ rc = sscanf(buf, "%d:%d:%d:%d:%d:%d:%d:%d", &pdata->pwm_id, &pdata->invert, &power.gpio, &power.active, &pdata->lth_brightness, &pdata->hth_brightness, &pwm_freq, &pdata->dft_brightness); if ((rc < 4)) { pr_err("bad env %s %s\n", env, buf); return -EINVAL; } if ((pdata->hth_brightness > pdata->max_brightness) || (pdata->lth_brightness > pdata->hth_brightness)) pdata->hth_brightness = pdata->max_brightness; pdata->pwm_period_ns = KHZ2PICOS(pwm_freq * 1000); pr_info("backlight param: pwm%d(%dkHz), invert %d, brightness %d(%d~%d) ", pdata->pwm_id, pwm_freq, pdata->invert, pdata->dft_brightness, pdata->lth_brightness, pdata->hth_brightness); if (gpio_is_valid(power.gpio)) { if (gpio_request(power.gpio, "pwm_bl switch")) { pr_warning("gpio request %d failed\n", power.gpio); power.gpio = -1; } pr_info("gpio%d (active %d)\n", power.gpio, power.active); } else pr_info("no gpio control\n"); /* parse wmt.backlight.delay */ l = sizeof(buf); if (wmt_getsyspara("wmt.backlight.delay", buf, &l) == 0) { sscanf(buf, "%d", &backlight_delay); } pr_info("backlight delay: %dms\n", backlight_delay); return 0; } static int __init wmt_bl_init(void) { int rc; rc = parse_backlight_param(); if (rc) return rc; led_trigger_register_simple("genesis", &genesis_led); return platform_device_register(&wm8880_device_pwmbl); } static void __exit wmt_bl_exit(void) { led_trigger_unregister_simple(genesis_led); platform_device_unregister(&wm8880_device_pwmbl); } module_init(wmt_bl_init); module_exit(wmt_bl_exit);