diff options
author | Michael Hanselmann <linux-kernel@hansmi.ch> | 2006-06-25 05:47:08 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-25 10:00:59 -0700 |
commit | 5474c120aafe78ca54bf272f7a01107c42da2b21 (patch) | |
tree | c1b002a27703ce92c816bfb9844752186e33d403 /arch/powerpc/platforms | |
parent | 17660bdd5c1f1a165273c1a59cb5b87670a81cc4 (diff) |
[PATCH] Rewritten backlight infrastructure for portable Apple computers
This patch contains a total rewrite of the backlight infrastructure for
portable Apple computers. Backward compatibility is retained. A sysfs
interface allows userland to control the brightness with more steps than
before. Userland is allowed to upload a brightness curve for different
monitors, similar to Mac OS X.
[akpm@osdl.org: add needed exports]
Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Richard Purdie <rpurdie@rpsys.net>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r-- | arch/powerpc/platforms/powermac/backlight.c | 270 |
1 files changed, 109 insertions, 161 deletions
diff --git a/arch/powerpc/platforms/powermac/backlight.c b/arch/powerpc/platforms/powermac/backlight.c index 8be2f7d071f..498b042e183 100644 --- a/arch/powerpc/platforms/powermac/backlight.c +++ b/arch/powerpc/platforms/powermac/backlight.c @@ -3,200 +3,148 @@ * Contains support for the backlight. * * Copyright (C) 2000 Benjamin Herrenschmidt + * Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> * */ #include <linux/config.h> #include <linux/kernel.h> -#include <linux/module.h> -#include <linux/stddef.h> -#include <linux/reboot.h> -#include <linux/nvram.h> -#include <linux/console.h> -#include <asm/sections.h> -#include <asm/ptrace.h> -#include <asm/io.h> -#include <asm/pgtable.h> -#include <asm/system.h> +#include <linux/fb.h> +#include <linux/backlight.h> #include <asm/prom.h> -#include <asm/machdep.h> -#include <asm/nvram.h> #include <asm/backlight.h> -#include <linux/adb.h> -#include <linux/pmu.h> +#define OLD_BACKLIGHT_MAX 15 -static struct backlight_controller *backlighter; -static void* backlighter_data; -static int backlight_autosave; -static int backlight_level = BACKLIGHT_MAX; -static int backlight_enabled = 1; -static int backlight_req_level = -1; -static int backlight_req_enable = -1; +/* Protect the pmac_backlight variable */ +DEFINE_MUTEX(pmac_backlight_mutex); -static void backlight_callback(void *); -static DECLARE_WORK(backlight_work, backlight_callback, NULL); +/* Main backlight storage + * + * Backlight drivers in this variable are required to have the "props" + * attribute set and to have an update_status function. + * + * We can only store one backlight here, but since Apple laptops have only one + * internal display, it doesn't matter. Other backlight drivers can be used + * independently. + * + * Lock ordering: + * pmac_backlight_mutex (global, main backlight) + * pmac_backlight->sem (backlight class) + */ +struct backlight_device *pmac_backlight; -void register_backlight_controller(struct backlight_controller *ctrler, - void *data, char *type) +int pmac_has_backlight_type(const char *type) { - struct device_node* bk_node; - char *prop; - int valid = 0; - - /* There's already a matching controller, bail out */ - if (backlighter != NULL) - return; - - bk_node = find_devices("backlight"); - -#ifdef CONFIG_ADB_PMU - /* Special case for the old PowerBook since I can't test on it */ - backlight_autosave = machine_is_compatible("AAPL,3400/2400") - || machine_is_compatible("AAPL,3500"); - if ((backlight_autosave - || machine_is_compatible("AAPL,PowerBook1998") - || machine_is_compatible("PowerBook1,1")) - && !strcmp(type, "pmu")) - valid = 1; -#endif + struct device_node* bk_node = find_devices("backlight"); + if (bk_node) { - prop = get_property(bk_node, "backlight-control", NULL); - if (prop && !strncmp(prop, type, strlen(type))) - valid = 1; - } - if (!valid) - return; - backlighter = ctrler; - backlighter_data = data; - - if (bk_node && !backlight_autosave) - prop = get_property(bk_node, "bklt", NULL); - else - prop = NULL; - if (prop) { - backlight_level = ((*prop)+1) >> 1; - if (backlight_level > BACKLIGHT_MAX) - backlight_level = BACKLIGHT_MAX; + char *prop = get_property(bk_node, "backlight-control", NULL); + if (prop && strncmp(prop, type, strlen(type)) == 0) + return 1; } -#ifdef CONFIG_ADB_PMU - if (backlight_autosave) { - struct adb_request req; - pmu_request(&req, NULL, 2, 0xd9, 0); - while (!req.complete) - pmu_poll(); - backlight_level = req.reply[0] >> 4; - } -#endif - acquire_console_sem(); - if (!backlighter->set_enable(1, backlight_level, data)) - backlight_enabled = 1; - release_console_sem(); - - printk(KERN_INFO "Registered \"%s\" backlight controller," - "level: %d/15\n", type, backlight_level); + return 0; } -EXPORT_SYMBOL(register_backlight_controller); -void unregister_backlight_controller(struct backlight_controller - *ctrler, void *data) +int pmac_backlight_curve_lookup(struct fb_info *info, int value) { - /* We keep the current backlight level (for now) */ - if (ctrler == backlighter && data == backlighter_data) - backlighter = NULL; + int level = (FB_BACKLIGHT_LEVELS - 1); + + if (info && info->bl_dev) { + int i, max = 0; + + /* Look for biggest value */ + for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) + max = max((int)info->bl_curve[i], max); + + /* Look for nearest value */ + for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) { + int diff = abs(info->bl_curve[i] - value); + if (diff < max) { + max = diff; + level = i; + } + } + + } + + return level; } -EXPORT_SYMBOL(unregister_backlight_controller); -static int __set_backlight_enable(int enable) +static void pmac_backlight_key(int direction) { - int rc; - - if (!backlighter) - return -ENODEV; - acquire_console_sem(); - rc = backlighter->set_enable(enable, backlight_level, - backlighter_data); - if (!rc) - backlight_enabled = enable; - release_console_sem(); - return rc; + mutex_lock(&pmac_backlight_mutex); + if (pmac_backlight) { + struct backlight_properties *props; + int brightness; + + down(&pmac_backlight->sem); + props = pmac_backlight->props; + + brightness = props->brightness + + ((direction?-1:1) * (props->max_brightness / 15)); + + if (brightness < 0) + brightness = 0; + else if (brightness > props->max_brightness) + brightness = props->max_brightness; + + props->brightness = brightness; + props->update_status(pmac_backlight); + + up(&pmac_backlight->sem); + } + mutex_unlock(&pmac_backlight_mutex); } -int set_backlight_enable(int enable) + +void pmac_backlight_key_up() { - if (!backlighter) - return -ENODEV; - backlight_req_enable = enable; - schedule_work(&backlight_work); - return 0; + pmac_backlight_key(0); } -EXPORT_SYMBOL(set_backlight_enable); - -int get_backlight_enable(void) +void pmac_backlight_key_down() { - if (!backlighter) - return -ENODEV; - return backlight_enabled; + pmac_backlight_key(1); } -EXPORT_SYMBOL(get_backlight_enable); -static int __set_backlight_level(int level) +int pmac_backlight_set_legacy_brightness(int brightness) { - int rc = 0; - - if (!backlighter) - return -ENODEV; - if (level < BACKLIGHT_MIN) - level = BACKLIGHT_OFF; - if (level > BACKLIGHT_MAX) - level = BACKLIGHT_MAX; - acquire_console_sem(); - if (backlight_enabled) - rc = backlighter->set_level(level, backlighter_data); - if (!rc) - backlight_level = level; - release_console_sem(); - if (!rc && !backlight_autosave) { - level <<=1; - if (level & 0x10) - level |= 0x01; - // -- todo: save to property "bklt" + int error = -ENXIO; + + mutex_lock(&pmac_backlight_mutex); + if (pmac_backlight) { + struct backlight_properties *props; + + down(&pmac_backlight->sem); + props = pmac_backlight->props; + props->brightness = brightness * + props->max_brightness / OLD_BACKLIGHT_MAX; + props->update_status(pmac_backlight); + up(&pmac_backlight->sem); + + error = 0; } - return rc; + mutex_unlock(&pmac_backlight_mutex); + + return error; } -int set_backlight_level(int level) + +int pmac_backlight_get_legacy_brightness() { - if (!backlighter) - return -ENODEV; - backlight_req_level = level; - schedule_work(&backlight_work); - return 0; -} + int result = -ENXIO; -EXPORT_SYMBOL(set_backlight_level); + mutex_lock(&pmac_backlight_mutex); + if (pmac_backlight) { + struct backlight_properties *props; -int get_backlight_level(void) -{ - if (!backlighter) - return -ENODEV; - return backlight_level; -} -EXPORT_SYMBOL(get_backlight_level); + down(&pmac_backlight->sem); + props = pmac_backlight->props; + result = props->brightness * + OLD_BACKLIGHT_MAX / props->max_brightness; + up(&pmac_backlight->sem); + } + mutex_unlock(&pmac_backlight_mutex); -static void backlight_callback(void *dummy) -{ - int level, enable; - - do { - level = backlight_req_level; - enable = backlight_req_enable; - mb(); - - if (level >= 0) - __set_backlight_level(level); - if (enable >= 0) - __set_backlight_enable(enable); - } while(cmpxchg(&backlight_req_level, level, -1) != level || - cmpxchg(&backlight_req_enable, enable, -1) != enable); + return result; } |