diff options
Diffstat (limited to 'arch/powerpc/platforms/powermac/backlight.c')
| -rw-r--r-- | arch/powerpc/platforms/powermac/backlight.c | 136 |
1 files changed, 103 insertions, 33 deletions
diff --git a/arch/powerpc/platforms/powermac/backlight.c b/arch/powerpc/platforms/powermac/backlight.c index 498b042e183..a00096b1c71 100644 --- a/arch/powerpc/platforms/powermac/backlight.c +++ b/arch/powerpc/platforms/powermac/backlight.c @@ -7,41 +7,66 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/fb.h> #include <linux/backlight.h> +#include <linux/adb.h> +#include <linux/pmu.h> +#include <linux/atomic.h> +#include <linux/export.h> #include <asm/prom.h> #include <asm/backlight.h> #define OLD_BACKLIGHT_MAX 15 -/* Protect the pmac_backlight variable */ +static void pmac_backlight_key_worker(struct work_struct *work); +static void pmac_backlight_set_legacy_worker(struct work_struct *work); + +static DECLARE_WORK(pmac_backlight_key_work, pmac_backlight_key_worker); +static DECLARE_WORK(pmac_backlight_set_legacy_work, pmac_backlight_set_legacy_worker); + +/* 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 below. + You should hold this lock when using the pmac_backlight pointer to + prevent its potential removal. */ DEFINE_MUTEX(pmac_backlight_mutex); /* Main backlight storage * - * Backlight drivers in this variable are required to have the "props" + * Backlight drivers in this variable are required to have the "ops" * 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"); + struct device_node* bk_node = of_find_node_by_name(NULL, "backlight"); if (bk_node) { - char *prop = get_property(bk_node, "backlight-control", NULL); - if (prop && strncmp(prop, type, strlen(type)) == 0) + const char *prop = of_get_property(bk_node, + "backlight-control", NULL); + if (prop && strncmp(prop, type, strlen(type)) == 0) { + of_node_put(bk_node); return 1; + } + of_node_put(bk_node); } return 0; @@ -72,18 +97,21 @@ int pmac_backlight_curve_lookup(struct fb_info *info, int value) return level; } -static void pmac_backlight_key(int direction) +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; + props = &pmac_backlight->props; brightness = props->brightness + - ((direction?-1:1) * (props->max_brightness / 15)); + ((pmac_backlight_key_queued?-1:1) * + (props->max_brightness / 15)); if (brightness < 0) brightness = 0; @@ -91,24 +119,25 @@ static void pmac_backlight_key(int direction) brightness = props->max_brightness; props->brightness = brightness; - props->update_status(pmac_backlight); - - up(&pmac_backlight->sem); + backlight_update_status(pmac_backlight); } mutex_unlock(&pmac_backlight_mutex); } -void pmac_backlight_key_up() +/* This function is called in interrupt context */ +void pmac_backlight_key(int direction) { - pmac_backlight_key(0); + 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); } -void pmac_backlight_key_down() -{ - pmac_backlight_key(1); -} - -int pmac_backlight_set_legacy_brightness(int brightness) +static int __pmac_backlight_set_legacy_brightness(int brightness) { int error = -ENXIO; @@ -116,12 +145,17 @@ int pmac_backlight_set_legacy_brightness(int brightness) if (pmac_backlight) { struct backlight_properties *props; - down(&pmac_backlight->sem); - props = pmac_backlight->props; + props = &pmac_backlight->props; props->brightness = brightness * - props->max_brightness / OLD_BACKLIGHT_MAX; - props->update_status(pmac_backlight); - up(&pmac_backlight->sem); + (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; + + backlight_update_status(pmac_backlight); error = 0; } @@ -130,6 +164,28 @@ int pmac_backlight_set_legacy_brightness(int brightness) return error; } +static void pmac_backlight_set_legacy_worker(struct work_struct *work) +{ + if (atomic_read(&kernel_backlight_disabled)) + return; + + __pmac_backlight_set_legacy_brightness(pmac_backlight_set_legacy_queued); +} + +/* 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); +} + +int pmac_backlight_set_legacy_brightness(int brightness) +{ + return __pmac_backlight_set_legacy_brightness(brightness); +} + int pmac_backlight_get_legacy_brightness() { int result = -ENXIO; @@ -138,13 +194,27 @@ int pmac_backlight_get_legacy_brightness() if (pmac_backlight) { struct backlight_properties *props; - down(&pmac_backlight->sem); - props = pmac_backlight->props; + props = &pmac_backlight->props; + result = props->brightness * - OLD_BACKLIGHT_MAX / props->max_brightness; - up(&pmac_backlight->sem); + (OLD_BACKLIGHT_MAX + 1) / + (props->max_brightness + 1); } mutex_unlock(&pmac_backlight_mutex); return result; } + +void pmac_backlight_disable() +{ + atomic_inc(&kernel_backlight_disabled); +} + +void pmac_backlight_enable() +{ + atomic_dec(&kernel_backlight_disabled); +} + +EXPORT_SYMBOL_GPL(pmac_backlight); +EXPORT_SYMBOL_GPL(pmac_backlight_mutex); +EXPORT_SYMBOL_GPL(pmac_has_backlight_type); |
