X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fpowerpc%2Fplatforms%2Fpowermac%2Fbacklight.c;h=8be2f7d071f0846806b19b797ff589393b8a1508;hb=9464c7cf61b9433057924c36e6e02f303a00e768;hp=d6641549105523be39a647c2c1409833b6e3d176;hpb=41689045f6a3cbe0550e1d34e9cc20d2e8c432ba;p=linux-2.6.git diff --git a/arch/powerpc/platforms/powermac/backlight.c b/arch/powerpc/platforms/powermac/backlight.c index d66415491..8be2f7d07 100644 --- a/arch/powerpc/platforms/powermac/backlight.c +++ b/arch/powerpc/platforms/powermac/backlight.c @@ -3,222 +3,200 @@ * Contains support for the backlight. * * Copyright (C) 2000 Benjamin Herrenschmidt - * Copyright (C) 2006 Michael Hanselmann * */ +#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include #include -#define OLD_BACKLIGHT_MAX 15 - -static void pmac_backlight_key_worker(void *data); -static void pmac_backlight_set_legacy_worker(void *data); - -static DECLARE_WORK(pmac_backlight_key_work, pmac_backlight_key_worker, NULL); -static DECLARE_WORK(pmac_backlight_set_legacy_work, pmac_backlight_set_legacy_worker, NULL); - -/* Although these variables are used in interrupt context, it makes no sense to - * protect them. No user is able to produce enough key events per second and - * notice the errors that might happen. - */ -static int pmac_backlight_key_queued; -static int pmac_backlight_set_legacy_queued; - -/* The via-pmu code allows the backlight to be grabbed, in which case the - * in-kernel control of the brightness needs to be disabled. This should - * only be used by really old PowerBooks. - */ -static atomic_t kernel_backlight_disabled = ATOMIC_INIT(0); - -/* Protect the pmac_backlight variable */ -DEFINE_MUTEX(pmac_backlight_mutex); - -/* 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; - -int pmac_has_backlight_type(const char *type) -{ - struct device_node* bk_node = find_devices("backlight"); +#include +#include - if (bk_node) { - char *prop = get_property(bk_node, "backlight-control", NULL); - if (prop && strncmp(prop, type, strlen(type)) == 0) - return 1; - } +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; - return 0; -} +static void backlight_callback(void *); +static DECLARE_WORK(backlight_work, backlight_callback, NULL); -int pmac_backlight_curve_lookup(struct fb_info *info, int value) +void register_backlight_controller(struct backlight_controller *ctrler, + void *data, char *type) { - 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); + struct device_node* bk_node; + char *prop; + int valid = 0; - /* 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; - } - } + /* 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 + if (bk_node) { + prop = get_property(bk_node, "backlight-control", NULL); + if (prop && !strncmp(prop, type, strlen(type))) + valid = 1; } - - return level; -} - -static void pmac_backlight_key_worker(void *data) -{ - if (atomic_read(&kernel_backlight_disabled)) + 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; + } - 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 + - ((pmac_backlight_key_queued?-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); +#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; } - mutex_unlock(&pmac_backlight_mutex); +#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); } +EXPORT_SYMBOL(register_backlight_controller); -/* This function is called in interrupt context */ -void pmac_backlight_key(int direction) +void unregister_backlight_controller(struct backlight_controller + *ctrler, void *data) { - if (atomic_read(&kernel_backlight_disabled)) - return; - - /* we can receive multiple interrupts here, but the scheduled work - * will run only once, with the last value - */ - pmac_backlight_key_queued = direction; - schedule_work(&pmac_backlight_key_work); + /* We keep the current backlight level (for now) */ + if (ctrler == backlighter && data == backlighter_data) + backlighter = NULL; } +EXPORT_SYMBOL(unregister_backlight_controller); -static int __pmac_backlight_set_legacy_brightness(int brightness) +static int __set_backlight_enable(int enable) { - 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 + 1) / - (OLD_BACKLIGHT_MAX + 1); - - if (props->brightness > props->max_brightness) - props->brightness = props->max_brightness; - else if (props->brightness < 0) - props->brightness = 0; - - props->update_status(pmac_backlight); - up(&pmac_backlight->sem); - - error = 0; - } - mutex_unlock(&pmac_backlight_mutex); - - return error; + 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; } - -static void pmac_backlight_set_legacy_worker(void *data) +int set_backlight_enable(int enable) { - if (atomic_read(&kernel_backlight_disabled)) - return; - - __pmac_backlight_set_legacy_brightness(pmac_backlight_set_legacy_queued); + if (!backlighter) + return -ENODEV; + backlight_req_enable = enable; + schedule_work(&backlight_work); + return 0; } -/* This function is called in interrupt context */ -void pmac_backlight_set_legacy_brightness_pmu(int brightness) { - if (atomic_read(&kernel_backlight_disabled)) - return; - - pmac_backlight_set_legacy_queued = brightness; - schedule_work(&pmac_backlight_set_legacy_work); -} +EXPORT_SYMBOL(set_backlight_enable); -int pmac_backlight_set_legacy_brightness(int brightness) +int get_backlight_enable(void) { - return __pmac_backlight_set_legacy_brightness(brightness); + if (!backlighter) + return -ENODEV; + return backlight_enabled; } +EXPORT_SYMBOL(get_backlight_enable); -int pmac_backlight_get_legacy_brightness() +static int __set_backlight_level(int level) { - int result = -ENXIO; - - mutex_lock(&pmac_backlight_mutex); - if (pmac_backlight) { - struct backlight_properties *props; - - down(&pmac_backlight->sem); - props = pmac_backlight->props; - - result = props->brightness * - (OLD_BACKLIGHT_MAX + 1) / - (props->max_brightness + 1); - - up(&pmac_backlight->sem); + 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" } - mutex_unlock(&pmac_backlight_mutex); - - return result; + return rc; } - -void pmac_backlight_disable() +int set_backlight_level(int level) { - atomic_inc(&kernel_backlight_disabled); + if (!backlighter) + return -ENODEV; + backlight_req_level = level; + schedule_work(&backlight_work); + return 0; } -void pmac_backlight_enable() +EXPORT_SYMBOL(set_backlight_level); + +int get_backlight_level(void) { - atomic_dec(&kernel_backlight_disabled); + if (!backlighter) + return -ENODEV; + return backlight_level; } +EXPORT_SYMBOL(get_backlight_level); -EXPORT_SYMBOL_GPL(pmac_backlight); -EXPORT_SYMBOL_GPL(pmac_backlight_mutex); -EXPORT_SYMBOL_GPL(pmac_has_backlight_type); +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); +}