X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fpowerpc%2Fplatforms%2Fpowermac%2Fbacklight.c;h=c3a89414ddc099d1baf91fe28234fe87b75b02b5;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=8be2f7d071f0846806b19b797ff589393b8a1508;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/arch/powerpc/platforms/powermac/backlight.c b/arch/powerpc/platforms/powermac/backlight.c index 8be2f7d07..c3a89414d 100644 --- a/arch/powerpc/platforms/powermac/backlight.c +++ b/arch/powerpc/platforms/powermac/backlight.c @@ -3,200 +3,223 @@ * 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 -#include -#include +#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; +static void pmac_backlight_key_worker(struct work_struct *work); +static void pmac_backlight_set_legacy_worker(struct work_struct *work); -static void backlight_callback(void *); -static DECLARE_WORK(backlight_work, backlight_callback, NULL); +static DECLARE_WORK(pmac_backlight_key_work, pmac_backlight_key_worker); +static DECLARE_WORK(pmac_backlight_set_legacy_work, pmac_backlight_set_legacy_worker); -void register_backlight_controller(struct backlight_controller *ctrler, - void *data, char *type) -{ - struct device_node* bk_node; - char *prop; - int valid = 0; +/* 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; - /* There's already a matching controller, bail out */ - if (backlighter != NULL) - return; +/* 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"); - 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; + const char *prop = get_property(bk_node, + "backlight-control", NULL); + if (prop && strncmp(prop, type, strlen(type)) == 0) + return 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; + + return 0; +} + +int pmac_backlight_curve_lookup(struct fb_info *info, int value) +{ + 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; + } + } + } -#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; + return level; +} + +static void pmac_backlight_key_worker(struct work_struct *work) +{ + if (atomic_read(&kernel_backlight_disabled)) + return; + + 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); } -#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); + mutex_unlock(&pmac_backlight_mutex); } -EXPORT_SYMBOL(register_backlight_controller); -void unregister_backlight_controller(struct backlight_controller - *ctrler, void *data) +/* This function is called in interrupt context */ +void pmac_backlight_key(int direction) { - /* We keep the current backlight level (for now) */ - if (ctrler == backlighter && data == backlighter_data) - backlighter = NULL; + 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); } -EXPORT_SYMBOL(unregister_backlight_controller); -static int __set_backlight_enable(int enable) +static int __pmac_backlight_set_legacy_brightness(int brightness) { - 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; + 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 set_backlight_enable(int enable) + +static void pmac_backlight_set_legacy_worker(struct work_struct *work) { - if (!backlighter) - return -ENODEV; - backlight_req_enable = enable; - schedule_work(&backlight_work); - return 0; + if (atomic_read(&kernel_backlight_disabled)) + return; + + __pmac_backlight_set_legacy_brightness(pmac_backlight_set_legacy_queued); } -EXPORT_SYMBOL(set_backlight_enable); +/* This function is called in interrupt context */ +void pmac_backlight_set_legacy_brightness_pmu(int brightness) { + if (atomic_read(&kernel_backlight_disabled)) + return; -int get_backlight_enable(void) -{ - if (!backlighter) - return -ENODEV; - return backlight_enabled; + pmac_backlight_set_legacy_queued = brightness; + schedule_work(&pmac_backlight_set_legacy_work); } -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" - } - return rc; + return __pmac_backlight_set_legacy_brightness(brightness); } -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) + down(&pmac_backlight->sem); + props = pmac_backlight->props; + + result = props->brightness * + (OLD_BACKLIGHT_MAX + 1) / + (props->max_brightness + 1); + + up(&pmac_backlight->sem); + } + mutex_unlock(&pmac_backlight_mutex); + + return result; +} + +void pmac_backlight_disable() { - if (!backlighter) - return -ENODEV; - return backlight_level; + atomic_inc(&kernel_backlight_disabled); } -EXPORT_SYMBOL(get_backlight_level); -static void backlight_callback(void *dummy) +void pmac_backlight_enable() { - 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); + atomic_dec(&kernel_backlight_disabled); } + +EXPORT_SYMBOL_GPL(pmac_backlight); +EXPORT_SYMBOL_GPL(pmac_backlight_mutex); +EXPORT_SYMBOL_GPL(pmac_has_backlight_type);