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 | |
parent | 17660bdd5c1f1a165273c1a59cb5b87670a81cc4 (diff) | |
download | linux-3.10-5474c120aafe78ca54bf272f7a01107c42da2b21.tar.gz linux-3.10-5474c120aafe78ca54bf272f7a01107c42da2b21.tar.bz2 linux-3.10-5474c120aafe78ca54bf272f7a01107c42da2b21.zip |
[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>
26 files changed, 1529 insertions, 711 deletions
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 91a6e04d974..52f5659534f 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -32,6 +32,7 @@ #include <linux/delay.h> #include <linux/kprobes.h> #include <linux/kexec.h> +#include <linux/backlight.h> #include <asm/kdebug.h> #include <asm/pgtable.h> @@ -105,10 +106,18 @@ int die(const char *str, struct pt_regs *regs, long err) spin_lock_irq(&die_lock); bust_spinlocks(1); #ifdef CONFIG_PMAC_BACKLIGHT - if (machine_is(powermac)) { - set_backlight_enable(1); - set_backlight_level(BACKLIGHT_MAX); + mutex_lock(&pmac_backlight_mutex); + if (machine_is(powermac) && pmac_backlight) { + struct backlight_properties *props; + + down(&pmac_backlight->sem); + props = pmac_backlight->props; + props->brightness = props->max_brightness; + props->power = FB_BLANK_UNBLANK; + props->update_status(pmac_backlight); + up(&pmac_backlight->sem); } + mutex_unlock(&pmac_backlight_mutex); #endif printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter); #ifdef CONFIG_PREEMPT 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; } diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 4735b41c113..0741df8c41b 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -26,9 +26,6 @@ #include <asm/prom.h> #include <asm/machdep.h> #include <asm/xmon.h> -#ifdef CONFIG_PMAC_BACKLIGHT -#include <asm/backlight.h> -#endif #include <asm/processor.h> #include <asm/pgtable.h> #include <asm/mmu.h> diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index ccf5df44cde..37cd6ee4586 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -99,17 +99,22 @@ config PMAC_MEDIABAY devices are not fully supported in the bay as I never had one to try with -# made a separate option since backlight may end up beeing used -# on non-powerbook machines (but only on PMU based ones AFAIK) config PMAC_BACKLIGHT bool "Backlight control for LCD screens" depends on ADB_PMU && (BROKEN || !PPC64) help - Say Y here to build in code to manage the LCD backlight on a - Macintosh PowerBook. With this code, the backlight will be turned - on and off appropriately on power-management and lid-open/lid-closed - events; also, the PowerBook button device will be enabled so you can - change the screen brightness. + Say Y here to enable Macintosh specific extensions of the generic + backlight code. With this enabled, the brightness keys on older + PowerBooks will be enabled so you can change the screen brightness. + Newer models should use an userspace daemon like pbbuttonsd. + +config PMAC_BACKLIGHT_LEGACY + bool "Provide legacy ioctl's on /dev/pmu for the backlight" + depends on PMAC_BACKLIGHT && (BROKEN || !PPC64) + help + Say Y if you want to enable legacy ioctl's on /dev/pmu. This is for + programs which use this old interface. New and updated programs + should use the backlight classes in sysfs. config ADB_MACIO bool "Include MacIO (CHRP) ADB driver" diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index 6081acdea40..8972e53d2dc 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_INPUT_ADBHID) += adbhid.o obj-$(CONFIG_ANSLCD) += ans-lcd.o obj-$(CONFIG_ADB_PMU) += via-pmu.o +obj-$(CONFIG_PMAC_BACKLIGHT) += via-pmu-backlight.o obj-$(CONFIG_ADB_CUDA) += via-cuda.o obj-$(CONFIG_PMAC_APM_EMU) += apm_emu.o obj-$(CONFIG_PMAC_SMU) += smu.o diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c index 394334ec576..c26e1236b27 100644 --- a/drivers/macintosh/adbhid.c +++ b/drivers/macintosh/adbhid.c @@ -503,9 +503,7 @@ adbhid_buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int auto case 0x1f: /* Powerbook button device */ { int down = (data[1] == (data[1] & 0xf)); -#ifdef CONFIG_PMAC_BACKLIGHT - int backlight = get_backlight_level(); -#endif + /* * XXX: Where is the contrast control for the passive? * -- Cort @@ -530,29 +528,17 @@ adbhid_buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int auto case 0xa: /* brightness decrease */ #ifdef CONFIG_PMAC_BACKLIGHT - if (!disable_kernel_backlight) { - if (down && backlight >= 0) { - if (backlight > BACKLIGHT_OFF) - set_backlight_level(backlight-1); - else - set_backlight_level(BACKLIGHT_OFF); - } - } -#endif /* CONFIG_PMAC_BACKLIGHT */ + if (!disable_kernel_backlight && down) + pmac_backlight_key_down(); +#endif input_report_key(adbhid[id]->input, KEY_BRIGHTNESSDOWN, down); break; case 0x9: /* brightness increase */ #ifdef CONFIG_PMAC_BACKLIGHT - if (!disable_kernel_backlight) { - if (down && backlight >= 0) { - if (backlight < BACKLIGHT_MAX) - set_backlight_level(backlight+1); - else - set_backlight_level(BACKLIGHT_MAX); - } - } -#endif /* CONFIG_PMAC_BACKLIGHT */ + if (!disable_kernel_backlight && down) + pmac_backlight_key_up(); +#endif input_report_key(adbhid[id]->input, KEY_BRIGHTNESSUP, down); break; diff --git a/drivers/macintosh/via-pmu-backlight.c b/drivers/macintosh/via-pmu-backlight.c new file mode 100644 index 00000000000..b42d05f2aaf --- /dev/null +++ b/drivers/macintosh/via-pmu-backlight.c @@ -0,0 +1,150 @@ +/* + * Backlight code for via-pmu + * + * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi. + * Copyright (C) 2001-2002 Benjamin Herrenschmidt + * Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> + * + */ + +#include <asm/ptrace.h> +#include <linux/adb.h> +#include <linux/pmu.h> +#include <asm/backlight.h> +#include <asm/prom.h> + +#define MAX_PMU_LEVEL 0xFF + +static struct device_node *vias; +static struct backlight_properties pmu_backlight_data; + +static int pmu_backlight_get_level_brightness(struct fb_info *info, + int level) +{ + int pmulevel; + + /* Get and convert the value */ + mutex_lock(&info->bl_mutex); + pmulevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_PMU_LEVEL; + mutex_unlock(&info->bl_mutex); + + if (pmulevel < 0) + pmulevel = 0; + else if (pmulevel > MAX_PMU_LEVEL) + pmulevel = MAX_PMU_LEVEL; + + return pmulevel; +} + +static int pmu_backlight_update_status(struct backlight_device *bd) +{ + struct fb_info *info = class_get_devdata(&bd->class_dev); + struct adb_request req; + int pmulevel, level = bd->props->brightness; + + if (vias == NULL) + return -ENODEV; + + if (bd->props->power != FB_BLANK_UNBLANK || + bd->props->fb_blank != FB_BLANK_UNBLANK) + level = 0; + + pmulevel = pmu_backlight_get_level_brightness(info, level); + + pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, pmulevel); + pmu_wait_complete(&req); + + pmu_request(&req, NULL, 2, PMU_POWER_CTRL, + PMU_POW_BACKLIGHT | (level > 0 ? PMU_POW_ON : PMU_POW_OFF)); + pmu_wait_complete(&req); + + return 0; +} + +static int pmu_backlight_get_brightness(struct backlight_device *bd) +{ + return bd->props->brightness; +} + +static struct backlight_properties pmu_backlight_data = { + .owner = THIS_MODULE, + .get_brightness = pmu_backlight_get_brightness, + .update_status = pmu_backlight_update_status, + .max_brightness = (FB_BACKLIGHT_LEVELS - 1), +}; + +void __init pmu_backlight_init(struct device_node *in_vias) +{ + struct backlight_device *bd; + struct fb_info *info; + char name[10]; + int level, autosave; + + vias = in_vias; + + /* Special case for the old PowerBook since I can't test on it */ + autosave = + machine_is_compatible("AAPL,3400/2400") || + machine_is_compatible("AAPL,3500"); + + if (!autosave && + !pmac_has_backlight_type("pmu") && + !machine_is_compatible("AAPL,PowerBook1998") && + !machine_is_compatible("PowerBook1,1")) + return; + + /* Actually, this is a hack, but I don't know of a better way + * to get the first framebuffer device. + */ + info = registered_fb[0]; + if (!info) { + printk("pmubl: No framebuffer found\n"); + goto error; + } + + snprintf(name, sizeof(name), "pmubl%d", info->node); + + bd = backlight_device_register(name, info, &pmu_backlight_data); + if (IS_ERR(bd)) { + printk("pmubl: Backlight registration failed\n"); + goto error; + } + + mutex_lock(&info->bl_mutex); + info->bl_dev = bd; + fb_bl_default_curve(info, 0x7F, 0x46, 0x0E); + mutex_unlock(&info->bl_mutex); + + level = pmu_backlight_data.max_brightness; + + if (autosave) { + /* read autosaved value if available */ + struct adb_request req; + pmu_request(&req, NULL, 2, 0xd9, 0); + pmu_wait_complete(&req); + + mutex_lock(&info->bl_mutex); + level = pmac_backlight_curve_lookup(info, + (req.reply[0] >> 4) * + pmu_backlight_data.max_brightness / 15); + mutex_unlock(&info->bl_mutex); + } + + up(&bd->sem); + bd->props->brightness = level; + bd->props->power = FB_BLANK_UNBLANK; + bd->props->update_status(bd); + down(&bd->sem); + + mutex_lock(&pmac_backlight_mutex); + if (!pmac_backlight) + pmac_backlight = bd; + mutex_unlock(&pmac_backlight_mutex); + + printk("pmubl: Backlight initialized (%s)\n", name); + + return; + +error: + return; +} diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index c63d4e7984b..2a355ae5956 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -144,7 +144,6 @@ static int data_index; static int data_len; static volatile int adb_int_pending; static volatile int disable_poll; -static struct adb_request bright_req_1, bright_req_2; static struct device_node *vias; static int pmu_kind = PMU_UNKNOWN; static int pmu_fully_inited = 0; @@ -161,7 +160,7 @@ static int drop_interrupts; #if defined(CONFIG_PM) && defined(CONFIG_PPC32) static int option_lid_wakeup = 1; #endif /* CONFIG_PM && CONFIG_PPC32 */ -#if (defined(CONFIG_PM)&&defined(CONFIG_PPC32))||defined(CONFIG_PMAC_BACKLIGHT) +#if (defined(CONFIG_PM)&&defined(CONFIG_PPC32))||defined(CONFIG_PMAC_BACKLIGHT_LEGACY) static int sleep_in_progress; #endif static unsigned long async_req_locks; @@ -208,10 +207,6 @@ static int proc_get_info(char *page, char **start, off_t off, int count, int *eof, void *data); static int proc_get_irqstats(char *page, char **start, off_t off, int count, int *eof, void *data); -#ifdef CONFIG_PMAC_BACKLIGHT -static int pmu_set_backlight_level(int level, void* data); -static int pmu_set_backlight_enable(int on, int level, void* data); -#endif /* CONFIG_PMAC_BACKLIGHT */ static void pmu_pass_intr(unsigned char *data, int len); static int proc_get_batt(char *page, char **start, off_t off, int count, int *eof, void *data); @@ -292,13 +287,6 @@ static char *pbook_type[] = { "Core99" }; -#ifdef CONFIG_PMAC_BACKLIGHT -static struct backlight_controller pmu_backlight_controller = { - pmu_set_backlight_enable, - pmu_set_backlight_level -}; -#endif /* CONFIG_PMAC_BACKLIGHT */ - int __init find_via_pmu(void) { u64 taddr; @@ -417,8 +405,6 @@ static int __init via_pmu_start(void) if (vias == NULL) return -ENODEV; - bright_req_1.complete = 1; - bright_req_2.complete = 1; batt_req.complete = 1; #ifndef CONFIG_PPC_MERGE @@ -483,9 +469,9 @@ static int __init via_pmu_dev_init(void) return -ENODEV; #ifdef CONFIG_PMAC_BACKLIGHT - /* Enable backlight */ - register_backlight_controller(&pmu_backlight_controller, NULL, "pmu"); -#endif /* CONFIG_PMAC_BACKLIGHT */ + /* Initialize backlight */ + pmu_backlight_init(vias); +#endif #ifdef CONFIG_PPC32 if (machine_is_compatible("AAPL,3400/2400") || @@ -1424,7 +1410,7 @@ next: #ifdef CONFIG_INPUT_ADBHID if (!disable_kernel_backlight) #endif /* CONFIG_INPUT_ADBHID */ - set_backlight_level(data[1] >> 4); + pmac_backlight_set_legacy_brightness(data[1] >> 4); #endif /* CONFIG_PMAC_BACKLIGHT */ } /* Tick interrupt */ @@ -1674,61 +1660,6 @@ gpio1_interrupt(int irq, void *arg, struct pt_regs *regs) return IRQ_NONE; } -#ifdef CONFIG_PMAC_BACKLIGHT -static int backlight_to_bright[] = { - 0x7f, 0x46, 0x42, 0x3e, 0x3a, 0x36, 0x32, 0x2e, - 0x2a, 0x26, 0x22, 0x1e, 0x1a, 0x16, 0x12, 0x0e -}; - -static int -pmu_set_backlight_enable(int on, int level, void* data) -{ - struct adb_request req; - - if (vias == NULL) - return -ENODEV; - - if (on) { - pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, - backlight_to_bright[level]); - pmu_wait_complete(&req); - } - pmu_request(&req, NULL, 2, PMU_POWER_CTRL, - PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF)); - pmu_wait_complete(&req); - - return 0; -} - -static void -pmu_bright_complete(struct adb_request *req) -{ - if (req == &bright_req_1) - clear_bit(1, &async_req_locks); - if (req == &bright_req_2) - clear_bit(2, &async_req_locks); -} - -static int -pmu_set_backlight_level(int level, void* data) -{ - if (vias == NULL) - return -ENODEV; - - if (test_and_set_bit(1, &async_req_locks)) - return -EAGAIN; - pmu_request(&bright_req_1, pmu_bright_complete, 2, PMU_BACKLIGHT_BRIGHT, - backlight_to_bright[level]); - if (test_and_set_bit(2, &async_req_locks)) - return -EAGAIN; - pmu_request(&bright_req_2, pmu_bright_complete, 2, PMU_POWER_CTRL, - PMU_POW_BACKLIGHT | (level > BACKLIGHT_OFF ? - PMU_POW_ON : PMU_POW_OFF)); - - return 0; -} -#endif /* CONFIG_PMAC_BACKLIGHT */ - void pmu_enable_irled(int on) { @@ -2145,9 +2076,8 @@ pmac_suspend_devices(void) return -EBUSY; } - /* Wait for completion of async backlight requests */ - while (!bright_req_1.complete || !bright_req_2.complete || - !batt_req.complete) + /* Wait for completion of async requests */ + while (!batt_req.complete) pmu_poll(); /* Giveup the lazy FPU & vec so we don't have to back them @@ -2678,26 +2608,34 @@ pmu_ioctl(struct inode * inode, struct file *filp, return put_user(1, argp); #endif /* CONFIG_PM && CONFIG_PPC32 */ -#ifdef CONFIG_PMAC_BACKLIGHT - /* Backlight should have its own device or go via - * the fbdev - */ +#ifdef CONFIG_PMAC_BACKLIGHT_LEGACY + /* Compatibility ioctl's for backlight */ case PMU_IOC_GET_BACKLIGHT: + { + int brightness; + if (sleep_in_progress) return -EBUSY; - error = get_backlight_level(); - if (error < 0) - return error; - return put_user(error, argp); + + brightness = pmac_backlight_get_legacy_brightness(); + if (brightness < 0) + return brightness; + else + return put_user(brightness, argp); + + } case PMU_IOC_SET_BACKLIGHT: { - __u32 value; + int brightness; + if (sleep_in_progress) return -EBUSY; - error = get_user(value, argp); - if (!error) - error = set_backlight_level(value); - break; + + error = get_user(brightness, argp); + if (error) + return error; + + return pmac_backlight_set_legacy_brightness(brightness); } #ifdef CONFIG_INPUT_ADBHID case PMU_IOC_GRAB_BACKLIGHT: { @@ -2713,7 +2651,7 @@ pmu_ioctl(struct inode * inode, struct file *filp, return 0; } #endif /* CONFIG_INPUT_ADBHID */ -#endif /* CONFIG_PMAC_BACKLIGHT */ +#endif /* CONFIG_PMAC_BACKLIGHT_LEGACY */ case PMU_IOC_GET_MODEL: return put_user(pmu_kind, argp); case PMU_IOC_HAS_ADB: diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 5a2840aeb54..168ede7902b 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -86,6 +86,11 @@ config FB_FIRMWARE_EDID combination with certain motherboards and monitors are known to suffer from this problem. +config FB_BACKLIGHT + bool + depends on FB + default n + config FB_MODE_HELPERS bool "Enable Video Mode Handling Helpers" depends on FB @@ -717,6 +722,16 @@ config FB_NVIDIA_I2C independently validate video mode parameters, you should say Y here. +config FB_NVIDIA_BACKLIGHT + bool "Support for backlight control" + depends on FB_NVIDIA && PPC_PMAC + select FB_BACKLIGHT + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + default y + help + Say Y here if you want to control the backlight of your display. + config FB_RIVA tristate "nVidia Riva support" depends on FB && PCI @@ -755,6 +770,16 @@ config FB_RIVA_DEBUG of debugging informations to provide to the maintainer when something goes wrong. +config FB_RIVA_BACKLIGHT + bool "Support for backlight control" + depends on FB_RIVA && PPC_PMAC + select FB_BACKLIGHT + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + default y + help + Say Y here if you want to control the backlight of your display. + config FB_I810 tristate "Intel 810/815 support (EXPERIMENTAL)" depends on FB && EXPERIMENTAL && PCI && X86_32 @@ -993,6 +1018,7 @@ config FB_RADEON There is a product page at http://apps.ati.com/ATIcompare/ + config FB_RADEON_I2C bool "DDC/I2C for ATI Radeon support" depends on FB_RADEON @@ -1000,6 +1026,16 @@ config FB_RADEON_I2C help Say Y here if you want DDC/I2C support for your Radeon board. +config FB_RADEON_BACKLIGHT + bool "Support for backlight control" + depends on FB_RADEON && PPC_PMAC + select FB_BACKLIGHT + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + default y + help + Say Y here if you want to control the backlight of your display. + config FB_RADEON_DEBUG bool "Lots of debug output from Radeon driver" depends on FB_RADEON @@ -1024,6 +1060,16 @@ config FB_ATY128 To compile this driver as a module, choose M here: the module will be called aty128fb. +config FB_ATY128_BACKLIGHT + bool "Support for backlight control" + depends on FB_ATY128 && PPC_PMAC + select FB_BACKLIGHT + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + default y + help + Say Y here if you want to control the backlight of your display. + config FB_ATY tristate "ATI Mach64 display support" if PCI || ATARI depends on FB && !SPARC32 @@ -1066,6 +1112,16 @@ config FB_ATY_GX is at <http://support.ati.com/products/pc/mach64/graphics_xpression.html>. +config FB_ATY_BACKLIGHT + bool "Support for backlight control" + depends on FB_ATY && PPC_PMAC + select FB_BACKLIGHT + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + default y + help + Say Y here if you want to control the backlight of your display. + config FB_S3TRIO bool "S3 Trio display support" depends on (FB = y) && PPC && BROKEN diff --git a/drivers/video/aty/Makefile b/drivers/video/aty/Makefile index 18521397a6e..a6cc0e9ec79 100644 --- a/drivers/video/aty/Makefile +++ b/drivers/video/aty/Makefile @@ -10,5 +10,6 @@ atyfb-objs := $(atyfb-y) radeonfb-y := radeon_base.o radeon_pm.o radeon_monitor.o radeon_accel.o radeonfb-$(CONFIG_FB_RADEON_I2C) += radeon_i2c.o +radeonfb-$(CONFIG_FB_RADEON_BACKLIGHT) += radeon_backlight.o radeonfb-objs := $(radeonfb-y) diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index f7bbff4ddc6..db878fd55fb 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -64,6 +64,7 @@ #include <linux/pci.h> #include <linux/ioport.h> #include <linux/console.h> +#include <linux/backlight.h> #include <asm/io.h> #ifdef CONFIG_PPC_PMAC @@ -480,16 +481,6 @@ static struct fb_ops aty128fb_ops = { .fb_imageblit = cfb_imageblit, }; -#ifdef CONFIG_PMAC_BACKLIGHT -static int aty128_set_backlight_enable(int on, int level, void* data); -static int aty128_set_backlight_level(int level, void* data); - -static struct backlight_controller aty128_backlight_controller = { - aty128_set_backlight_enable, - aty128_set_backlight_level -}; -#endif /* CONFIG_PMAC_BACKLIGHT */ - /* * Functions to read from/write to the mmio registers * - endian conversions may possibly be avoided by @@ -1258,19 +1249,35 @@ static void aty128_set_crt_enable(struct aty128fb_par *par, int on) static void aty128_set_lcd_enable(struct aty128fb_par *par, int on) { u32 reg; +#ifdef CONFIG_FB_ATY128_BACKLIGHT + struct fb_info *info = pci_get_drvdata(par->pdev); +#endif if (on) { reg = aty_ld_le32(LVDS_GEN_CNTL); reg |= LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION; reg &= ~LVDS_DISPLAY_DIS; aty_st_le32(LVDS_GEN_CNTL, reg); -#ifdef CONFIG_PMAC_BACKLIGHT - aty128_set_backlight_enable(get_backlight_enable(), - get_backlight_level(), par); +#ifdef CONFIG_FB_ATY128_BACKLIGHT + mutex_lock(&info->bl_mutex); + if (info->bl_dev) { + down(&info->bl_dev->sem); + info->bl_dev->props->update_status(info->bl_dev); + up(&info->bl_dev->sem); + } + mutex_unlock(&info->bl_mutex); #endif } else { -#ifdef CONFIG_PMAC_BACKLIGHT - aty128_set_backlight_enable(0, 0, par); +#ifdef CONFIG_FB_ATY128_BACKLIGHT + mutex_lock(&info->bl_mutex); + if (info->bl_dev) { + down(&info->bl_dev->sem); + info->bl_dev->props->brightness = 0; + info->bl_dev->props->power = FB_BLANK_POWERDOWN; + info->bl_dev->props->update_status(info->bl_dev); + up(&info->bl_dev->sem); + } + mutex_unlock(&info->bl_mutex); #endif reg = aty_ld_le32(LVDS_GEN_CNTL); reg |= LVDS_DISPLAY_DIS; @@ -1691,6 +1698,184 @@ static int __init aty128fb_setup(char *options) } #endif /* MODULE */ +/* Backlight */ +#ifdef CONFIG_FB_ATY128_BACKLIGHT +#define MAX_LEVEL 0xFF + +static struct backlight_properties aty128_bl_data; + +static int aty128_bl_get_level_brightness(struct aty128fb_par *par, + int level) +{ + struct fb_info *info = pci_get_drvdata(par->pdev); + int atylevel; + + /* Get and convert the value */ + mutex_lock(&info->bl_mutex); + atylevel = MAX_LEVEL - + (info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL); + mutex_unlock(&info->bl_mutex); + + if (atylevel < 0) + atylevel = 0; + else if (atylevel > MAX_LEVEL) + atylevel = MAX_LEVEL; + + return atylevel; +} + +/* We turn off the LCD completely instead of just dimming the backlight. + * This provides greater power saving and the display is useless without + * backlight anyway + */ +#define BACKLIGHT_LVDS_OFF +/* That one prevents proper CRT output with LCD off */ +#undef BACKLIGHT_DAC_OFF + +static int aty128_bl_update_status(struct backlight_device *bd) +{ + struct aty128fb_par *par = class_get_devdata(&bd->class_dev); + unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL); + int level; + + if (bd->props->power != FB_BLANK_UNBLANK || + bd->props->fb_blank != FB_BLANK_UNBLANK || + !par->lcd_on) + level = 0; + else + level = bd->props->brightness; + + reg |= LVDS_BL_MOD_EN | LVDS_BLON; + if (level > 0) { + reg |= LVDS_DIGION; + if (!(reg & LVDS_ON)) { + reg &= ~LVDS_BLON; + aty_st_le32(LVDS_GEN_CNTL, reg); + aty_ld_le32(LVDS_GEN_CNTL); + mdelay(10); + reg |= LVDS_BLON; + aty_st_le32(LVDS_GEN_CNTL, reg); + } + reg &= ~LVDS_BL_MOD_LEVEL_MASK; + reg |= (aty128_bl_get_level_brightness(par, level) << LVDS_BL_MOD_LEVEL_SHIFT); +#ifdef BACKLIGHT_LVDS_OFF + reg |= LVDS_ON | LVDS_EN; + reg &= ~LVDS_DISPLAY_DIS; +#endif + aty_st_le32(LVDS_GEN_CNTL, reg); +#ifdef BACKLIGHT_DAC_OFF + aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN)); +#endif + } else { + reg &= ~LVDS_BL_MOD_LEVEL_MASK; + reg |= (aty128_bl_get_level_brightness(par, 0) << LVDS_BL_MOD_LEVEL_SHIFT); +#ifdef BACKLIGHT_LVDS_OFF + reg |= LVDS_DISPLAY_DIS; + aty_st_le32(LVDS_GEN_CNTL, reg); + aty_ld_le32(LVDS_GEN_CNTL); + udelay(10); + reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION); +#endif + aty_st_le32(LVDS_GEN_CNTL, reg); +#ifdef BACKLIGHT_DAC_OFF + aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN); +#endif + } + + return 0; +} + +static int aty128_bl_get_brightness(struct backlight_device *bd) +{ + return bd->props->brightness; +} + +static struct backlight_properties aty128_bl_data = { + .owner = THIS_MODULE, + .get_brightness = aty128_bl_get_brightness, + .update_status = aty128_bl_update_status, + .max_brightness = (FB_BACKLIGHT_LEVELS - 1), +}; + +static void aty128_bl_init(struct aty128fb_par *par) +{ + struct fb_info *info = pci_get_drvdata(par->pdev); + struct backlight_device *bd; + char name[12]; + + /* Could be extended to Rage128Pro LVDS output too */ + if (par->chip_gen != rage_M3) + return; + +#ifdef CONFIG_PMAC_BACKLIGHT + if (!pmac_has_backlight_type("ati")) + return; +#endif + + snprintf(name, sizeof(name), "aty128bl%d", info->node); + + bd = backlight_device_register(name, par, &aty128_bl_data); + if (IS_ERR(bd)) { + info->bl_dev = NULL; + printk("aty128: Backlight registration failed\n"); + goto error; + } + + mutex_lock(&info->bl_mutex); + info->bl_dev = bd; + fb_bl_default_curve(info, 0, + 63 * FB_BACKLIGHT_MAX / MAX_LEVEL, + 219 * FB_BACKLIGHT_MAX / MAX_LEVEL); + mutex_unlock(&info->bl_mutex); + + up(&bd->sem); + bd->props->brightness = aty128_bl_data.max_brightness; + bd->props->power = FB_BLANK_UNBLANK; + bd->props->update_status(bd); + down(&bd->sem); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); + if (!pmac_backlight) + pmac_backlight = bd; + mutex_unlock(&pmac_backlight_mutex); +#endif + + printk("aty128: Backlight initialized (%s)\n", name); + + return; + +error: + return; +} + +static void aty128_bl_exit(struct aty128fb_par *par) +{ + struct fb_info *info = pci_get_drvdata(par->pdev); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); +#endif + + mutex_lock(&info->bl_mutex); + if (info->bl_dev) { +#ifdef CONFIG_PMAC_BACKLIGHT + if (pmac_backlight == info->bl_dev) + pmac_backlight = NULL; +#endif + + backlight_device_unregister(info->bl_dev); + info->bl_dev = NULL; + + printk("aty128: Backlight unloaded\n"); + } + mutex_unlock(&info->bl_mutex); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_unlock(&pmac_backlight_mutex); +#endif +} +#endif /* CONFIG_FB_ATY128_BACKLIGHT */ /* * Initialisation @@ -1835,17 +2020,15 @@ static int __init aty128_init(struct pci_dev *pdev, const struct pci_device_id * if (register_framebuffer(info) < 0) return 0; -#ifdef CONFIG_PMAC_BACKLIGHT - /* Could be extended to Rage128Pro LVDS output too */ - if (par->chip_gen == rage_M3) - register_backlight_controller(&aty128_backlight_controller, par, "ati"); -#endif /* CONFIG_PMAC_BACKLIGHT */ - par->pm_reg = pci_find_capability(pdev, PCI_CAP_ID_PM); par->pdev = pdev; par->asleep = 0; par->lock_blank = 0; - + +#ifdef CONFIG_FB_ATY128_BACKLIGHT + aty128_bl_init(par); +#endif + printk(KERN_INFO "fb%d: %s frame buffer device on %s\n", info->node, info->fix.id, video_card); @@ -1981,6 +2164,10 @@ static void __devexit aty128_remove(struct pci_dev *pdev) par = info->par; +#ifdef CONFIG_FB_ATY128_BACKLIGHT + aty128_bl_exit(par); +#endif + unregister_framebuffer(info); #ifdef CONFIG_MTRR if (par->mtrr.vram_valid) @@ -2011,10 +2198,14 @@ static int aty128fb_blank(int blank, struct fb_info *fb) if (par->lock_blank || par->asleep) return 0; -#ifdef CONFIG_PMAC_BACKLIGHT - if (machine_is(powermac) && blank) - set_backlight_enable(0); -#endif /* CONFIG_PMAC_BACKLIGHT */ +#ifdef CONFIG_FB_ATY128_BACKLIGHT + if (machine_is(powermac) && blank) { + down(&fb->bl_dev->sem); + fb->bl_dev->props->power = FB_BLANK_POWERDOWN; + fb->bl_dev->props->update_status(fb->bl_dev); + up(&fb->bl_dev->sem); + } +#endif if (blank & FB_BLANK_VSYNC_SUSPEND) state |= 2; @@ -2029,10 +2220,14 @@ static int aty128fb_blank(int blank, struct fb_info *fb) aty128_set_crt_enable(par, par->crt_on && !blank); aty128_set_lcd_enable(par, par->lcd_on && !blank); } -#ifdef CONFIG_PMAC_BACKLIGHT - if (machine_is(powermac) && !blank) - set_backlight_enable(1); -#endif /* CONFIG_PMAC_BACKLIGHT */ +#ifdef CONFIG_FB_ATY128_BACKLIGHT + if (machine_is(powermac) && !blank) { + down(&fb->bl_dev->sem); + fb->bl_dev->props->power = FB_BLANK_UNBLANK; + fb->bl_dev->props->update_status(fb->bl_dev); + up(&fb->bl_dev->sem); + } +#endif return 0; } @@ -2138,73 +2333,6 @@ static int aty128fb_ioctl(struct fb_info *info, u_int cmd, u_long arg) return -EINVAL; } -#ifdef CONFIG_PMAC_BACKLIGHT -static int backlight_conv[] = { - 0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e, - 0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24 -}; - -/* We turn off the LCD completely instead of just dimming the backlight. - * This provides greater power saving and the display is useless without - * backlight anyway - */ -#define BACKLIGHT_LVDS_OFF -/* That one prevents proper CRT output with LCD off */ -#undef BACKLIGHT_DAC_OFF - -static int aty128_set_backlight_enable(int on, int level, void *data) -{ - struct aty128fb_par *par = data; - unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL); - - if (!par->lcd_on) - on = 0; - reg |= LVDS_BL_MOD_EN | LVDS_BLON; - if (on && level > BACKLIGHT_OFF) { - reg |= LVDS_DIGION; - if (!(reg & LVDS_ON)) { - reg &= ~LVDS_BLON; - aty_st_le32(LVDS_GEN_CNTL, reg); - (void)aty_ld_le32(LVDS_GEN_CNTL); - mdelay(10); - reg |= LVDS_BLON; - aty_st_le32(LVDS_GEN_CNTL, reg); - } - reg &= ~LVDS_BL_MOD_LEVEL_MASK; - reg |= (backlight_conv[level] << LVDS_BL_MOD_LEVEL_SHIFT); -#ifdef BACKLIGHT_LVDS_OFF - reg |= LVDS_ON | LVDS_EN; - reg &= ~LVDS_DISPLAY_DIS; -#endif - aty_st_le32(LVDS_GEN_CNTL, reg); -#ifdef BACKLIGHT_DAC_OFF - aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN)); -#endif - } else { - reg &= ~LVDS_BL_MOD_LEVEL_MASK; - reg |= (backlight_conv[0] << LVDS_BL_MOD_LEVEL_SHIFT); -#ifdef BACKLIGHT_LVDS_OFF - reg |= LVDS_DISPLAY_DIS; - aty_st_le32(LVDS_GEN_CNTL, reg); - (void)aty_ld_le32(LVDS_GEN_CNTL); - udelay(10); - reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION); -#endif - aty_st_le32(LVDS_GEN_CNTL, reg); -#ifdef BACKLIGHT_DAC_OFF - aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN); -#endif - } - - return 0; -} - -static int aty128_set_backlight_level(int level, void* data) -{ - return aty128_set_backlight_enable(1, level, data); -} -#endif /* CONFIG_PMAC_BACKLIGHT */ - #if 0 /* * Accelerated functions diff --git a/drivers/video/aty/atyfb.h b/drivers/video/aty/atyfb.h index e9b7a64c1ac..43d2cb58af8 100644 --- a/drivers/video/aty/atyfb.h +++ b/drivers/video/aty/atyfb.h @@ -151,6 +151,7 @@ struct atyfb_par { int lock_blank; unsigned long res_start; unsigned long res_size; + struct pci_dev *pdev; #ifdef __sparc__ struct pci_mmap_map *mmap_map; u8 mmaped; diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index c054bb28b1c..c5185f7cf4b 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -66,6 +66,7 @@ #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/wait.h> +#include <linux/backlight.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -2115,45 +2116,142 @@ static int atyfb_pci_resume(struct pci_dev *pdev) #endif /* defined(CONFIG_PM) && defined(CONFIG_PCI) */ -#ifdef CONFIG_PMAC_BACKLIGHT +/* Backlight */ +#ifdef CONFIG_FB_ATY_BACKLIGHT +#define MAX_LEVEL 0xFF - /* - * LCD backlight control - */ +static struct backlight_properties aty_bl_data; -static int backlight_conv[] = { - 0x00, 0x3f, 0x4c, 0x59, 0x66, 0x73, 0x80, 0x8d, - 0x9a, 0xa7, 0xb4, 0xc1, 0xcf, 0xdc, 0xe9, 0xff -}; +static int aty_bl_get_level_brightness(struct atyfb_par *par, int level) +{ + struct fb_info *info = pci_get_drvdata(par->pdev); + int atylevel; + + /* Get and convert the value */ + mutex_lock(&info->bl_mutex); + atylevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL; + mutex_unlock(&info->bl_mutex); + + if (atylevel < 0) + atylevel = 0; + else if (atylevel > MAX_LEVEL) + atylevel = MAX_LEVEL; -static int aty_set_backlight_enable(int on, int level, void *data) + return atylevel; +} + +static int aty_bl_update_status(struct backlight_device *bd) { - struct fb_info *info = (struct fb_info *) data; - struct atyfb_par *par = (struct atyfb_par *) info->par; + struct atyfb_par *par = class_get_devdata(&bd->class_dev); unsigned int reg = aty_ld_lcd(LCD_MISC_CNTL, par); + int level; + + if (bd->props->power != FB_BLANK_UNBLANK || + bd->props->fb_blank != FB_BLANK_UNBLANK) + level = 0; + else + level = bd->props->brightness; reg |= (BLMOD_EN | BIASMOD_EN); - if (on && level > BACKLIGHT_OFF) { + if (level > 0) { reg &= ~BIAS_MOD_LEVEL_MASK; - reg |= (backlight_conv[level] << BIAS_MOD_LEVEL_SHIFT); + reg |= (aty_bl_get_level_brightness(par, level) << BIAS_MOD_LEVEL_SHIFT); } else { reg &= ~BIAS_MOD_LEVEL_MASK; - reg |= (backlight_conv[0] << BIAS_MOD_LEVEL_SHIFT); + reg |= (aty_bl_get_level_brightness(par, 0) << BIAS_MOD_LEVEL_SHIFT); } aty_st_lcd(LCD_MISC_CNTL, reg, par); + return 0; } -static int aty_set_backlight_level(int level, void *data) +static int aty_bl_get_brightness(struct backlight_device *bd) { - return aty_set_backlight_enable(1, level, data); + return bd->props->brightness; } -static struct backlight_controller aty_backlight_controller = { - aty_set_backlight_enable, - aty_set_backlight_level +static struct backlight_properties aty_bl_data = { + .owner = THIS_MODULE, + .get_brightness = aty_bl_get_brightness, + .update_status = aty_bl_update_status, + .max_brightness = (FB_BACKLIGHT_LEVELS - 1), }; -#endif /* CONFIG_PMAC_BACKLIGHT */ + +static void aty_bl_init(struct atyfb_par *par) +{ + struct fb_info *info = pci_get_drvdata(par->pdev); + struct backlight_device *bd; + char name[12]; + +#ifdef CONFIG_PMAC_BACKLIGHT + if (!pmac_has_backlight_type("ati")) + return; +#endif + + snprintf(name, sizeof(name), "atybl%d", info->node); + + bd = backlight_device_register(name, par, &aty_bl_data); + if (IS_ERR(bd)) { + info->bl_dev = NULL; + printk("aty: Backlight registration failed\n"); + goto error; + } + + mutex_lock(&info->bl_mutex); + info->bl_dev = bd; + fb_bl_default_curve(info, 0, + 0x3F * FB_BACKLIGHT_MAX / MAX_LEVEL, + 0xFF * FB_BACKLIGHT_MAX / MAX_LEVEL); + mutex_unlock(&info->bl_mutex); + + up(&bd->sem); + bd->props->brightness = aty_bl_data.max_brightness; + bd->props->power = FB_BLANK_UNBLANK; + bd->props->update_status(bd); + down(&bd->sem); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); + if (!pmac_backlight) + pmac_backlight = bd; + mutex_unlock(&pmac_backlight_mutex); +#endif + + printk("aty: Backlight initialized (%s)\n", name); + + return; + +error: + return; +} + +static void aty_bl_exit(struct atyfb_par *par) +{ + struct fb_info *info = pci_get_drvdata(par->pdev); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); +#endif + + mutex_lock(&info->bl_mutex); + if (info->bl_dev) { +#ifdef CONFIG_PMAC_BACKLIGHT + if (pmac_backlight == info->bl_dev) + pmac_backlight = NULL; +#endif + + backlight_device_unregister(info->bl_dev); + + printk("aty: Backlight unloaded\n"); + } + mutex_unlock(&info->bl_mutex); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_unlock(&pmac_backlight_mutex); +#endif +} + +#endif /* CONFIG_FB_ATY_BACKLIGHT */ static void __init aty_calc_mem_refresh(struct atyfb_par *par, int xclk) { @@ -2513,9 +2611,13 @@ static int __init aty_init(struct fb_info *info, const char *name) /* these bits let the 101 powerbook wake up from sleep -- paulus */ aty_st_lcd(POWER_MANAGEMENT, aty_ld_lcd(POWER_MANAGEMENT, par) | (USE_F32KHZ | TRISTATE_MEM_EN), par); - } else if (M64_HAS(MOBIL_BUS)) - register_backlight_controller(&aty_backlight_controller, info, "ati"); -#endif /* CONFIG_PMAC_BACKLIGHT */ + } else +#endif + if (M64_HAS(MOBIL_BUS)) { +#ifdef CONFIG_FB_ATY_BACKLIGHT + aty_bl_init (par); +#endif + } memset(&var, 0, sizeof(var)); #ifdef CONFIG_PPC @@ -2674,8 +2776,16 @@ static int atyfb_blank(int blank, struct fb_info *info) return 0; #ifdef CONFIG_PMAC_BACKLIGHT - if (machine_is(powermac) && blank > FB_BLANK_NORMAL) - set_backlight_enable(0); + if (machine_is(powermac) && blank > FB_BLANK_NORMAL) { + mutex_lock(&info->bl_mutex); + if (info->bl_dev) { + down(&info->bl_dev->sem); + info->bl_dev->props->power = FB_BLANK_POWERDOWN; + info->bl_dev->props->update_status(info->bl_dev); + up(&info->bl_dev->sem); + } + mutex_unlock(&info->bl_mutex); + } #elif defined(CONFIG_FB_ATY_GENERIC_LCD) if (par->lcd_table && blank > FB_BLANK_NORMAL && (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) { @@ -2706,8 +2816,16 @@ static int atyfb_blank(int blank, struct fb_info *info) aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par); #ifdef CONFIG_PMAC_BACKLIGHT - if (machine_is(powermac) && blank <= FB_BLANK_NORMAL) - set_backlight_enable(1); + if (machine_is(powermac) && blank <= FB_BLANK_NORMAL) { + mutex_lock(&info->bl_mutex); + if (info->bl_dev) { + down(&info->bl_dev->sem); + info->bl_dev->props->power = FB_BLANK_UNBLANK; + info->bl_dev->props->update_status(info->bl_dev); + up(&info->bl_dev->sem); + } + mutex_unlock(&info->bl_mutex); + } #elif defined(CONFIG_FB_ATY_GENERIC_LCD) if (par->lcd_table && blank <= FB_BLANK_NORMAL && (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) { @@ -3440,6 +3558,7 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi par->res_start = res_start; par->res_size = res_size; par->irq = pdev->irq; + par->pdev = pdev; /* Setup "info" structure */ #ifdef __sparc__ @@ -3571,6 +3690,11 @@ static void __devexit atyfb_remove(struct fb_info *info) aty_set_crtc(par, &saved_crtc); par->pll_ops->set_pll(info, &saved_pll); +#ifdef CONFIG_FB_ATY_BACKLIGHT + if (M64_HAS(MOBIL_BUS)) + aty_bl_exit(par); +#endif + unregister_framebuffer(info); #ifdef CONFIG_MTRR diff --git a/drivers/video/aty/radeon_backlight.c b/drivers/video/aty/radeon_backlight.c new file mode 100644 index 00000000000..7de66b855d4 --- /dev/null +++ b/drivers/video/aty/radeon_backlight.c @@ -0,0 +1,247 @@ +/* + * Backlight code for ATI Radeon based graphic cards + * + * Copyright (c) 2000 Ani Joshi <ajoshi@kernel.crashing.org> + * Copyright (c) 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org> + * Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "radeonfb.h" +#include <linux/backlight.h> + +#ifdef CONFIG_PMAC_BACKLIGHT +#include <asm/backlight.h> +#endif + +#define MAX_RADEON_LEVEL 0xFF + +static struct backlight_properties radeon_bl_data; + +struct radeon_bl_privdata { + struct radeonfb_info *rinfo; + uint8_t negative; +}; + +static int radeon_bl_get_level_brightness(struct radeon_bl_privdata *pdata, + int level) +{ + struct fb_info *info = pdata->rinfo->info; + int rlevel; + + mutex_lock(&info->bl_mutex); + + /* Get and convert the value */ + rlevel = pdata->rinfo->info->bl_curve[level] * + FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL; + + mutex_unlock(&info->bl_mutex); + + if (pdata->negative) + rlevel = MAX_RADEON_LEVEL - rlevel; + + if (rlevel < 0) + rlevel = 0; + else if (rlevel > MAX_RADEON_LEVEL) + rlevel = MAX_RADEON_LEVEL; + + return rlevel; +} + +static int radeon_bl_update_status(struct backlight_device *bd) +{ + struct radeon_bl_privdata *pdata = class_get_devdata(&bd->class_dev); + struct radeonfb_info *rinfo = pdata->rinfo; + u32 lvds_gen_cntl, tmpPixclksCntl; + int level; + + if (rinfo->mon1_type != MT_LCD) + return 0; + + /* We turn off the LCD completely instead of just dimming the + * backlight. This provides some greater power saving and the display + * is useless without backlight anyway. + */ + if (bd->props->power != FB_BLANK_UNBLANK || + bd->props->fb_blank != FB_BLANK_UNBLANK) + level = 0; + else + level = bd->props->brightness; + + del_timer_sync(&rinfo->lvds_timer); + radeon_engine_idle(); + + lvds_gen_cntl = INREG(LVDS_GEN_CNTL); + if (level > 0) { + lvds_gen_cntl &= ~LVDS_DISPLAY_DIS; + if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) { + lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON); + lvds_gen_cntl |= LVDS_BLON | LVDS_EN; + OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); + lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; + lvds_gen_cntl |= + (radeon_bl_get_level_brightness(pdata, level) << + LVDS_BL_MOD_LEVEL_SHIFT); + lvds_gen_cntl |= LVDS_ON; + lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN); + rinfo->pending_lvds_gen_cntl = lvds_gen_cntl; + mod_timer(&rinfo->lvds_timer, + jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); + } else { + lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; + lvds_gen_cntl |= + (radeon_bl_get_level_brightness(pdata, level) << + LVDS_BL_MOD_LEVEL_SHIFT); + OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); + } + rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; + rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl + & LVDS_STATE_MASK; + } else { + /* Asic bug, when turning off LVDS_ON, we have to make sure + RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off + */ + tmpPixclksCntl = INPLL(PIXCLKS_CNTL); + if (rinfo->is_mobility || rinfo->is_IGP) + OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb); + lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN); + lvds_gen_cntl |= (radeon_bl_get_level_brightness(pdata, 0) << + LVDS_BL_MOD_LEVEL_SHIFT); + lvds_gen_cntl |= LVDS_DISPLAY_DIS; + OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); + udelay(100); + lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN); + OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); + lvds_gen_cntl &= ~(LVDS_DIGON); + rinfo->pending_lvds_gen_cntl = lvds_gen_cntl; + mod_timer(&rinfo->lvds_timer, + jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); + if (rinfo->is_mobility || rinfo->is_IGP) + OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl); + } + rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; + rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK); + + return 0; +} + +static int radeon_bl_get_brightness(struct backlight_device *bd) +{ + return bd->props->brightness; +} + +static struct backlight_properties radeon_bl_data = { + .owner = THIS_MODULE, + .get_brightness = radeon_bl_get_brightness, + .update_status = radeon_bl_update_status, + .max_brightness = (FB_BACKLIGHT_LEVELS - 1), +}; + +void radeonfb_bl_init(struct radeonfb_info *rinfo) +{ + struct backlight_device *bd; + struct radeon_bl_privdata *pdata; + char name[12]; + + if (rinfo->mon1_type != MT_LCD) + return; + +#ifdef CONFIG_PMAC_BACKLIGHT + if (!pmac_has_backlight_type("ati") && + !pmac_has_backlight_type("mnca")) + return; +#endif + + pdata = kmalloc(sizeof(struct radeon_bl_privdata), GFP_KERNEL); + if (!pdata) { + printk("radeonfb: Memory allocation failed\n"); + goto error; + } + + snprintf(name, sizeof(name), "radeonbl%d", rinfo->info->node); + + bd = backlight_device_register(name, pdata, &radeon_bl_data); + if (IS_ERR(bd)) { + rinfo->info->bl_dev = NULL; + printk("radeonfb: Backlight registration failed\n"); + goto error; + } + + pdata->rinfo = rinfo; + + /* Pardon me for that hack... maybe some day we can figure out in what + * direction backlight should work on a given panel? + */ + pdata->negative = + (rinfo->family != CHIP_FAMILY_RV200 && + rinfo->family != CHIP_FAMILY_RV250 && + rinfo->family != CHIP_FAMILY_RV280 && + rinfo->family != CHIP_FAMILY_RV350); + +#ifdef CONFIG_PMAC_BACKLIGHT + pdata->negative = pdata->negative || + machine_is_compatible("PowerBook4,3") || + machine_is_compatible("PowerBook6,3") || + machine_is_compatible("PowerBook6,5"); +#endif + + mutex_lock(&rinfo->info->bl_mutex); + rinfo->info->bl_dev = bd; + fb_bl_default_curve(rinfo->info, 0, + 63 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL, + 217 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL); + mutex_unlock(&rinfo->info->bl_mutex); + + up(&bd->sem); + bd->props->brightness = radeon_bl_data.max_brightness; + bd->props->power = FB_BLANK_UNBLANK; + bd->props->update_status(bd); + down(&bd->sem); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); + if (!pmac_backlight) + pmac_backlight = bd; + mutex_unlock(&pmac_backlight_mutex); +#endif + + printk("radeonfb: Backlight initialized (%s)\n", name); + + return; + +error: + kfree(pdata); + return; +} + +void radeonfb_bl_exit(struct radeonfb_info *rinfo) +{ +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); +#endif + + mutex_lock(&rinfo->info->bl_mutex); + if (rinfo->info->bl_dev) { + struct radeon_bl_privdata *pdata; + +#ifdef CONFIG_PMAC_BACKLIGHT + if (pmac_backlight == rinfo->info->bl_dev) + pmac_backlight = NULL; +#endif + + pdata = class_get_devdata(&rinfo->info->bl_dev->class_dev); + backlight_device_unregister(rinfo->info->bl_dev); + kfree(pdata); + rinfo->info->bl_dev = NULL; + + printk("radeonfb: Backlight unloaded\n"); + } + mutex_unlock(&rinfo->info->bl_mutex); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_unlock(&pmac_backlight_mutex); +#endif +} diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 387a18a47ac..c5ecbb02e01 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -78,10 +78,6 @@ #include <asm/pci-bridge.h> #include "../macmodes.h" -#ifdef CONFIG_PMAC_BACKLIGHT -#include <asm/backlight.h> -#endif - #ifdef CONFIG_BOOTX_TEXT #include <asm/btext.h> #endif @@ -277,20 +273,6 @@ static int nomtrr = 0; * prototypes */ - -#ifdef CONFIG_PPC_OF - -#ifdef CONFIG_PMAC_BACKLIGHT -static int radeon_set_backlight_enable(int on, int level, void *data); -static int radeon_set_backlight_level(int level, void *data); -static struct backlight_controller radeon_backlight_controller = { - radeon_set_backlight_enable, - radeon_set_backlight_level -}; -#endif /* CONFIG_PMAC_BACKLIGHT */ - -#endif /* CONFIG_PPC_OF */ - static void radeon_unmap_ROM(struct radeonfb_info *rinfo, struct pci_dev *dev) { if (!rinfo->bios_seg) @@ -1913,116 +1895,6 @@ static int __devinit radeon_set_fbinfo (struct radeonfb_info *rinfo) return 0; } - -#ifdef CONFIG_PMAC_BACKLIGHT - -/* TODO: Dbl check these tables, we don't go up to full ON backlight - * in these, possibly because we noticed MacOS doesn't, but I'd prefer - * having some more official numbers from ATI - */ -static int backlight_conv_m6[] = { - 0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e, - 0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24 -}; -static int backlight_conv_m7[] = { - 0x00, 0x3f, 0x4a, 0x55, 0x60, 0x6b, 0x76, 0x81, - 0x8c, 0x97, 0xa2, 0xad, 0xb8, 0xc3, 0xce, 0xd9 -}; - -#define BACKLIGHT_LVDS_OFF -#undef BACKLIGHT_DAC_OFF - -/* We turn off the LCD completely instead of just dimming the backlight. - * This provides some greater power saving and the display is useless - * without backlight anyway. - */ -static int radeon_set_backlight_enable(int on, int level, void *data) -{ - struct radeonfb_info *rinfo = (struct radeonfb_info *)data; - u32 lvds_gen_cntl, tmpPixclksCntl; - int* conv_table; - - if (rinfo->mon1_type != MT_LCD) - return 0; - - /* Pardon me for that hack... maybe some day we can figure - * out in what direction backlight should work on a given - * panel ? - */ - if ((rinfo->family == CHIP_FAMILY_RV200 || - rinfo->family == CHIP_FAMILY_RV250 || - rinfo->family == CHIP_FAMILY_RV280 || - rinfo->family == CHIP_FAMILY_RV350) && - !machine_is_compatible("PowerBook4,3") && - !machine_is_compatible("PowerBook6,3") && - !machine_is_compatible("PowerBook6,5")) - conv_table = backlight_conv_m7; - else - conv_table = backlight_conv_m6; - - del_timer_sync(&rinfo->lvds_timer); - radeon_engine_idle(); - - lvds_gen_cntl = INREG(LVDS_GEN_CNTL); - if (on && (level > BACKLIGHT_OFF)) { - lvds_gen_cntl &= ~LVDS_DISPLAY_DIS; - if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) { - lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON); - lvds_gen_cntl |= LVDS_BLON | LVDS_EN; - OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); - lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; - lvds_gen_cntl |= (conv_table[level] << - LVDS_BL_MOD_LEVEL_SHIFT); - lvds_gen_cntl |= LVDS_ON; - lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN); - rinfo->pending_lvds_gen_cntl = lvds_gen_cntl; - mod_timer(&rinfo->lvds_timer, - jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); - } else { - lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; - lvds_gen_cntl |= (conv_table[level] << - LVDS_BL_MOD_LEVEL_SHIFT); - OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); - } - rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; - rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl - & LVDS_STATE_MASK; - } else { - /* Asic bug, when turning off LVDS_ON, we have to make sure - RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off - */ - tmpPixclksCntl = INPLL(PIXCLKS_CNTL); - if (rinfo->is_mobility || rinfo->is_IGP) - OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb); - lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN); - lvds_gen_cntl |= (conv_table[0] << - LVDS_BL_MOD_LEVEL_SHIFT); - lvds_gen_cntl |= LVDS_DISPLAY_DIS; - OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); - udelay(100); - lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN); - OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); - lvds_gen_cntl &= ~(LVDS_DIGON); - rinfo->pending_lvds_gen_cntl = lvds_gen_cntl; - mod_timer(&rinfo->lvds_timer, - jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); - if (rinfo->is_mobility || rinfo->is_IGP) - OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl); - } - rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; - rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK); - - return 0; -} - - -static int radeon_set_backlight_level(int level, void *data) -{ - return radeon_set_backlight_enable(1, level, data); -} -#endif /* CONFIG_PMAC_BACKLIGHT */ - - /* * This reconfigure the card's internal memory map. In theory, we'd like * to setup the card's memory at the same address as it's PCI bus address, @@ -2477,14 +2349,7 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev, MTRR_TYPE_WRCOMB, 1); #endif -#ifdef CONFIG_PMAC_BACKLIGHT - if (rinfo->mon1_type == MT_LCD) { - register_backlight_controller(&radeon_backlight_controller, - rinfo, "ati"); - register_backlight_controller(&radeon_backlight_controller, - rinfo, "mnca"); - } -#endif + radeonfb_bl_init(rinfo); printk ("radeonfb (%s): %s\n", pci_name(rinfo->pdev), rinfo->name); @@ -2528,7 +2393,8 @@ static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev) if (!rinfo) return; - + + radeonfb_bl_exit(rinfo); radeonfb_pm_exit(rinfo); if (rinfo->mon1_EDID) diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h index 217e00ab4a2..1645943b112 100644 --- a/drivers/video/aty/radeonfb.h +++ b/drivers/video/aty/radeonfb.h @@ -625,4 +625,13 @@ extern int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_ extern void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode, int reg_only); +/* Backlight functions */ +#ifdef CONFIG_FB_RADEON_BACKLIGHT +extern void radeonfb_bl_init(struct radeonfb_info *rinfo); +extern void radeonfb_bl_exit(struct radeonfb_info *rinfo); +#else +static inline void radeonfb_bl_init(struct radeonfb_info *rinfo) {} +static inline void radeonfb_bl_exit(struct radeonfb_info *rinfo) {} +#endif + #endif /* __RADEONFB_H__ */ diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index 72ff6bf75e5..d76bbfac92c 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c @@ -148,9 +148,24 @@ static int chipsfb_set_par(struct fb_info *info) static int chipsfb_blank(int blank, struct fb_info *info) { #ifdef CONFIG_PMAC_BACKLIGHT - // used to disable backlight only for blank > 1, but it seems - // useful at blank = 1 too (saves battery, extends backlight life) - set_backlight_enable(!blank); + mutex_lock(&pmac_backlight_mutex); + + if (pmac_backlight) { + down(&pmac_backlight->sem); + + /* used to disable backlight only for blank > 1, but it seems + * useful at blank = 1 too (saves battery, extends backlight + * life) + */ + if (blank) + pmac_backlight->props->power = FB_BLANK_POWERDOWN; + else + pmac_backlight->props->power = FB_BLANK_UNBLANK; + pmac_backlight->props->update_status(pmac_backlight); + up(&pmac_backlight->sem); + } + + mutex_unlock(&pmac_backlight_mutex); #endif /* CONFIG_PMAC_BACKLIGHT */ return 1; /* get fb_blank to set the colormap to all black */ @@ -401,7 +416,14 @@ chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) #ifdef CONFIG_PMAC_BACKLIGHT /* turn on the backlight */ - set_backlight_enable(1); + mutex_lock(&pmac_backlight_mutex); + if (pmac_backlight) { + down(&pmac_backlight->sem); + pmac_backlight->props->power = FB_BLANK_UNBLANK; + pmac_backlight->props->update_status(pmac_backlight); + up(&pmac_backlight->sem); + } + mutex_unlock(&pmac_backlight_mutex); #endif /* CONFIG_PMAC_BACKLIGHT */ #ifdef CONFIG_PPC diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index 34e07399756..3ceb8c1b392 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -18,6 +18,7 @@ #include <linux/kernel.h> #include <linux/fb.h> #include <linux/console.h> +#include <linux/module.h> /** * framebuffer_alloc - creates a new frame buffer info structure @@ -55,6 +56,10 @@ struct fb_info *framebuffer_alloc(size_t size, struct device *dev) info->device = dev; +#ifdef CONFIG_FB_BACKLIGHT + mutex_init(&info->bl_mutex); +#endif + return info; #undef PADDING #undef BYTES_PER_LONG @@ -414,6 +419,65 @@ static ssize_t show_fbstate(struct class_device *class_device, char *buf) return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state); } +#ifdef CONFIG_FB_BACKLIGHT +static ssize_t store_bl_curve(struct class_device *class_device, + const char *buf, size_t count) +{ + struct fb_info *fb_info = class_get_devdata(class_device); + u8 tmp_curve[FB_BACKLIGHT_LEVELS]; + unsigned int i; + + if (count != (FB_BACKLIGHT_LEVELS / 8 * 24)) + return -EINVAL; + + for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i) + if (sscanf(&buf[i * 24], + "%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n", + &tmp_curve[i * 8 + 0], + &tmp_curve[i * 8 + 1], + &tmp_curve[i * 8 + 2], + &tmp_curve[i * 8 + 3], + &tmp_curve[i * 8 + 4], + &tmp_curve[i * 8 + 5], + &tmp_curve[i * 8 + 6], + &tmp_curve[i * 8 + 7]) != 8) + return -EINVAL; + + /* If there has been an error in the input data, we won't + * reach this loop. + */ + mutex_lock(&fb_info->bl_mutex); + for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i) + fb_info->bl_curve[i] = tmp_curve[i]; + mutex_unlock(&fb_info->bl_mutex); + + return count; +} + +static ssize_t show_bl_curve(struct class_device *class_device, char *buf) +{ + struct fb_info *fb_info = class_get_devdata(class_device); + ssize_t len = 0; + unsigned int i; + + mutex_lock(&fb_info->bl_mutex); + for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8) + len += snprintf(&buf[len], PAGE_SIZE, + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + fb_info->bl_curve[i + 0], + fb_info->bl_curve[i + 1], + fb_info->bl_curve[i + 2], + fb_info->bl_curve[i + 3], + fb_info->bl_curve[i + 4], + fb_info->bl_curve[i + 5], + fb_info->bl_curve[i + 6], + fb_info->bl_curve[i + 7]); + mutex_unlock(&fb_info->bl_mutex); + + return len; +} +#endif + /* When cmap is added back in it should be a binary attribute * not a text one. Consideration should also be given to converting * fbdev to use configfs instead of sysfs */ @@ -432,6 +496,9 @@ static struct class_device_attribute class_device_attrs[] = { __ATTR(con_rotate, S_IRUGO|S_IWUSR, show_con_rotate, store_con_rotate), __ATTR(con_rotate_all, S_IWUSR, NULL, store_con_rotate_all), __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate), +#ifdef CONFIG_FB_BACKLIGHT + __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve), +#endif }; int fb_init_class_device(struct fb_info *fb_info) @@ -454,4 +521,25 @@ void fb_cleanup_class_device(struct fb_info *fb_info) &class_device_attrs[i]); } +#ifdef CONFIG_FB_BACKLIGHT +/* This function generates a linear backlight curve + * + * 0: off + * 1-7: min + * 8-127: linear from min to max + */ +void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max) +{ + unsigned int i, flat, count, range = (max - min); + + fb_info->bl_curve[0] = off; + for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat) + fb_info->bl_curve[flat] = min; + + count = FB_BACKLIGHT_LEVELS * 15 / 16; + for (i = 0; i < count; ++i) + fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count); +} +EXPORT_SYMBOL_GPL(fb_bl_default_curve); +#endif diff --git a/drivers/video/nvidia/Makefile b/drivers/video/nvidia/Makefile index 690d37e8de5..ca47432113e 100644 --- a/drivers/video/nvidia/Makefile +++ b/drivers/video/nvidia/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_FB_NVIDIA) += nvidiafb.o nvidiafb-y := nvidia.o nv_hw.o nv_setup.o \ nv_accel.o nvidiafb-$(CONFIG_FB_NVIDIA_I2C) += nv_i2c.o +nvidiafb-$(CONFIG_FB_NVIDIA_BACKLIGHT) += nv_backlight.o nvidiafb-$(CONFIG_PPC_OF) += nv_of.o -nvidiafb-objs := $(nvidiafb-y)
\ No newline at end of file +nvidiafb-objs := $(nvidiafb-y) diff --git a/drivers/video/nvidia/nv_backlight.c b/drivers/video/nvidia/nv_backlight.c new file mode 100644 index 00000000000..1c1c10c699c --- /dev/null +++ b/drivers/video/nvidia/nv_backlight.c @@ -0,0 +1,175 @@ +/* + * Backlight code for nVidia based graphic cards + * + * Copyright 2004 Antonino Daplas <adaplas@pol.net> + * Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/backlight.h> +#include <linux/fb.h> +#include <linux/pci.h> +#include "nv_local.h" +#include "nv_type.h" +#include "nv_proto.h" + +#ifdef CONFIG_PMAC_BACKLIGHT +#include <asm/backlight.h> +#include <asm/machdep.h> +#endif + +/* We do not have any information about which values are allowed, thus + * we used safe values. + */ +#define MIN_LEVEL 0x158 +#define MAX_LEVEL 0x534 + +static struct backlight_properties nvidia_bl_data; + +static int nvidia_bl_get_level_brightness(struct nvidia_par *par, + int level) +{ + struct fb_info *info = pci_get_drvdata(par->pci_dev); + int nlevel; + + /* Get and convert the value */ + mutex_lock(&info->bl_mutex); + nlevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL; + mutex_unlock(&info->bl_mutex); + + if (nlevel < 0) + nlevel = 0; + else if (nlevel < MIN_LEVEL) + nlevel = MIN_LEVEL; + else if (nlevel > MAX_LEVEL) + nlevel = MAX_LEVEL; + + return nlevel; +} + +static int nvidia_bl_update_status(struct backlight_device *bd) +{ + struct nvidia_par *par = class_get_devdata(&bd->class_dev); + u32 tmp_pcrt, tmp_pmc, fpcontrol; + int level; + + if (!par->FlatPanel) + return 0; + + if (bd->props->power != FB_BLANK_UNBLANK || + bd->props->fb_blank != FB_BLANK_UNBLANK) + level = 0; + else + level = bd->props->brightness; + + tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF; + tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC; + fpcontrol = NV_RD32(par->PRAMDAC, 0x0848) & 0xCFFFFFCC; + + if (level > 0) { + tmp_pcrt |= 0x1; + tmp_pmc |= (1 << 31); /* backlight bit */ + tmp_pmc |= nvidia_bl_get_level_brightness(par, level) << 16; + fpcontrol |= par->fpSyncs; + } else + fpcontrol |= 0x20000022; + + NV_WR32(par->PCRTC0, 0x081C, tmp_pcrt); + NV_WR32(par->PMC, 0x10F0, tmp_pmc); + NV_WR32(par->PRAMDAC, 0x848, fpcontrol); + + return 0; +} + +static int nvidia_bl_get_brightness(struct backlight_device *bd) +{ + return bd->props->brightness; +} + +static struct backlight_properties nvidia_bl_data = { + .owner = THIS_MODULE, + .get_brightness = nvidia_bl_get_brightness, + .update_status = nvidia_bl_update_status, + .max_brightness = (FB_BACKLIGHT_LEVELS - 1), +}; + +void nvidia_bl_init(struct nvidia_par *par) +{ + struct fb_info *info = pci_get_drvdata(par->pci_dev); + struct backlight_device *bd; + char name[12]; + + if (!par->FlatPanel) + return; + +#ifdef CONFIG_PMAC_BACKLIGHT + if (!machine_is(powermac) || + !pmac_has_backlight_type("mnca")) + return; +#endif + + snprintf(name, sizeof(name), "nvidiabl%d", info->node); + + bd = backlight_device_register(name, par, &nvidia_bl_data); + if (IS_ERR(bd)) { + info->bl_dev = NULL; + printk("nvidia: Backlight registration failed\n"); + goto error; + } + + mutex_lock(&info->bl_mutex); + info->bl_dev = bd; + fb_bl_default_curve(info, 0, + 0x158 * FB_BACKLIGHT_MAX / MAX_LEVEL, + 0x534 * FB_BACKLIGHT_MAX / MAX_LEVEL); + mutex_unlock(&info->bl_mutex); + + up(&bd->sem); + bd->props->brightness = nvidia_bl_data.max_brightness; + bd->props->power = FB_BLANK_UNBLANK; + bd->props->update_status(bd); + down(&bd->sem); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); + if (!pmac_backlight) + pmac_backlight = bd; + mutex_unlock(&pmac_backlight_mutex); +#endif + + printk("nvidia: Backlight initialized (%s)\n", name); + + return; + +error: + return; +} + +void nvidia_bl_exit(struct nvidia_par *par) +{ + struct fb_info *info = pci_get_drvdata(par->pci_dev); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); +#endif + + mutex_lock(&info->bl_mutex); + if (info->bl_dev) { +#ifdef CONFIG_PMAC_BACKLIGHT + if (pmac_backlight == info->bl_dev) + pmac_backlight = NULL; +#endif + + backlight_device_unregister(info->bl_dev); + + printk("nvidia: Backlight unloaded\n"); + } + mutex_unlock(&info->bl_mutex); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_unlock(&pmac_backlight_mutex); +#endif +} diff --git a/drivers/video/nvidia/nv_proto.h b/drivers/video/nvidia/nv_proto.h index b149a690ee0..6fba656cd56 100644 --- a/drivers/video/nvidia/nv_proto.h +++ b/drivers/video/nvidia/nv_proto.h @@ -63,4 +63,14 @@ extern void nvidiafb_imageblit(struct fb_info *info, const struct fb_image *image); extern int nvidiafb_sync(struct fb_info *info); extern u8 byte_rev[256]; + +/* in nv_backlight.h */ +#ifdef CONFIG_FB_NVIDIA_BACKLIGHT +extern void nvidia_bl_init(struct nvidia_par *par); +extern void nvidia_bl_exit(struct nvidia_par *par); +#else +static inline void nvidia_bl_init(struct nvidia_par *par) {} +static inline void nvidia_bl_exit(struct nvidia_par *par) {} +#endif + #endif /* __NV_PROTO_H__ */ diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index 093ab9977c7..03a7c1e9ce3 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -22,6 +22,7 @@ #include <linux/init.h> #include <linux/pci.h> #include <linux/console.h> +#include <linux/backlight.h> #ifdef CONFIG_MTRR #include <asm/mtrr.h> #endif @@ -29,10 +30,6 @@ #include <asm/prom.h> #include <asm/pci-bridge.h> #endif -#ifdef CONFIG_PMAC_BACKLIGHT -#include <asm/machdep.h> -#include <asm/backlight.h> -#endif #include "nv_local.h" #include "nv_type.h" @@ -470,75 +467,6 @@ static struct fb_var_screeninfo __devinitdata nvidiafb_default_var = { .vmode = FB_VMODE_NONINTERLACED }; -/* - * Backlight control - */ -#ifdef CONFIG_PMAC_BACKLIGHT - -static int nvidia_backlight_levels[] = { - 0x158, - 0x192, - 0x1c6, - 0x200, - 0x234, - 0x268, - 0x2a2, - 0x2d6, - 0x310, - 0x344, - 0x378, - 0x3b2, - 0x3e6, - 0x41a, - 0x454, - 0x534, -}; - -/* ------------------------------------------------------------------------- * - * - * Backlight operations - * - * ------------------------------------------------------------------------- */ - -static int nvidia_set_backlight_enable(int on, int level, void *data) -{ - struct nvidia_par *par = data; - u32 tmp_pcrt, tmp_pmc, fpcontrol; - - tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF; - tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC; - fpcontrol = NV_RD32(par->PRAMDAC, 0x0848) & 0xCFFFFFCC; - - if (on && (level > BACKLIGHT_OFF)) { - tmp_pcrt |= 0x1; - tmp_pmc |= (1 << 31); // backlight bit - tmp_pmc |= nvidia_backlight_levels[level - 1] << 16; - } - - if (on) - fpcontrol |= par->fpSyncs; - else - fpcontrol |= 0x20000022; - - NV_WR32(par->PCRTC0, 0x081C, tmp_pcrt); - NV_WR32(par->PMC, 0x10F0, tmp_pmc); - NV_WR32(par->PRAMDAC, 0x848, fpcontrol); - - return 0; -} - -static int nvidia_set_backlight_level(int level, void *data) -{ - return nvidia_set_backlight_enable(1, level, data); -} - -static struct backlight_controller nvidia_backlight_controller = { - nvidia_set_backlight_enable, - nvidia_set_backlight_level -}; - -#endif /* CONFIG_PMAC_BACKLIGHT */ - static void nvidiafb_load_cursor_image(struct nvidia_par *par, u8 * data8, u16 bg, u16 fg, u32 w, u32 h) { @@ -1355,10 +1283,15 @@ static int nvidiafb_blank(int blank, struct fb_info *info) NVWriteSeq(par, 0x01, tmp); NVWriteCrtc(par, 0x1a, vesa); -#ifdef CONFIG_PMAC_BACKLIGHT - if (par->FlatPanel && machine_is(powermac)) { - set_backlight_enable(!blank); +#ifdef CONFIG_FB_NVIDIA_BACKLIGHT + mutex_lock(&info->bl_mutex); + if (info->bl_dev) { + down(&info->bl_dev->sem); + info->bl_dev->props->power = blank; + info->bl_dev->props->update_status(info->bl_dev); + up(&info->bl_dev->sem); } + mutex_unlock(&info->bl_mutex); #endif NVTRACE_LEAVE(); @@ -1741,11 +1674,9 @@ static int __devinit nvidiafb_probe(struct pci_dev *pd, "PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n", info->fix.id, par->FbMapSize / (1024 * 1024), info->fix.smem_start); -#ifdef CONFIG_PMAC_BACKLIGHT - if (par->FlatPanel && machine_is(powermac)) - register_backlight_controller(&nvidia_backlight_controller, - par, "mnca"); -#endif + + nvidia_bl_init(par); + NVTRACE_LEAVE(); return 0; @@ -1775,6 +1706,8 @@ static void __exit nvidiafb_remove(struct pci_dev *pd) NVTRACE_ENTER(); + nvidia_bl_exit(par); + unregister_framebuffer(info); #ifdef CONFIG_MTRR if (par->mtrr.vram_valid) diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 3e9308f0f16..d4384ab1df6 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -41,6 +41,7 @@ #include <linux/fb.h> #include <linux/init.h> #include <linux/pci.h> +#include <linux/backlight.h> #ifdef CONFIG_MTRR #include <asm/mtrr.h> #endif @@ -272,34 +273,154 @@ static const struct riva_regs reg_template = { /* * Backlight control */ -#ifdef CONFIG_PMAC_BACKLIGHT +#ifdef CONFIG_FB_RIVA_BACKLIGHT +/* We do not have any information about which values are allowed, thus + * we used safe values. + */ +#define MIN_LEVEL 0x158 +#define MAX_LEVEL 0x534 -static int riva_backlight_levels[] = { - 0x158, - 0x192, - 0x1c6, - 0x200, - 0x234, - 0x268, - 0x2a2, - 0x2d6, - 0x310, - 0x344, - 0x378, - 0x3b2, - 0x3e6, - 0x41a, - 0x454, - 0x534, -}; +static struct backlight_properties riva_bl_data; + +static int riva_bl_get_level_brightness(struct riva_par *par, + int level) +{ + struct fb_info *info = pci_get_drvdata(par->pdev); + int nlevel; + + /* Get and convert the value */ + mutex_lock(&info->bl_mutex); + nlevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL; + mutex_unlock(&info->bl_mutex); + + if (nlevel < 0) + nlevel = 0; + else if (nlevel < MIN_LEVEL) + nlevel = MIN_LEVEL; + else if (nlevel > MAX_LEVEL) + nlevel = MAX_LEVEL; + + return nlevel; +} + +static int riva_bl_update_status(struct backlight_device *bd) +{ + struct riva_par *par = class_get_devdata(&bd->class_dev); + U032 tmp_pcrt, tmp_pmc; + int level; + + if (bd->props->power != FB_BLANK_UNBLANK || + bd->props->fb_blank != FB_BLANK_UNBLANK) + level = 0; + else + level = bd->props->brightness; + + tmp_pmc = par->riva.PMC[0x10F0/4] & 0x0000FFFF; + tmp_pcrt = par->riva.PCRTC0[0x081C/4] & 0xFFFFFFFC; + if(level > 0) { + tmp_pcrt |= 0x1; + tmp_pmc |= (1 << 31); /* backlight bit */ + tmp_pmc |= riva_bl_get_level_brightness(par, level) << 16; /* level */ + } + par->riva.PCRTC0[0x081C/4] = tmp_pcrt; + par->riva.PMC[0x10F0/4] = tmp_pmc; + + return 0; +} + +static int riva_bl_get_brightness(struct backlight_device *bd) +{ + return bd->props->brightness; +} -static int riva_set_backlight_enable(int on, int level, void *data); -static int riva_set_backlight_level(int level, void *data); -static struct backlight_controller riva_backlight_controller = { - riva_set_backlight_enable, - riva_set_backlight_level +static struct backlight_properties riva_bl_data = { + .owner = THIS_MODULE, + .get_brightness = riva_bl_get_brightness, + .update_status = riva_bl_update_status, + .max_brightness = (FB_BACKLIGHT_LEVELS - 1), }; -#endif /* CONFIG_PMAC_BACKLIGHT */ + +static void riva_bl_init(struct riva_par *par) +{ + struct fb_info *info = pci_get_drvdata(par->pdev); + struct backlight_device *bd; + char name[12]; + + if (!par->FlatPanel) + return; + +#ifdef CONFIG_PMAC_BACKLIGHT + if (!machine_is(powermac) || + !pmac_has_backlight_type("mnca")) + return; +#endif + + snprintf(name, sizeof(name), "rivabl%d", info->node); + + bd = backlight_device_register(name, par, &riva_bl_data); + if (IS_ERR(bd)) { + info->bl_dev = NULL; + printk("riva: Backlight registration failed\n"); + goto error; + } + + mutex_lock(&info->bl_mutex); + info->bl_dev = bd; + fb_bl_default_curve(info, 0, + 0x158 * FB_BACKLIGHT_MAX / MAX_LEVEL, + 0x534 * FB_BACKLIGHT_MAX / MAX_LEVEL); + mutex_unlock(&info->bl_mutex); + + up(&bd->sem); + bd->props->brightness = riva_bl_data.max_brightness; + bd->props->power = FB_BLANK_UNBLANK; + bd->props->update_status(bd); + down(&bd->sem); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); + if (!pmac_backlight) + pmac_backlight = bd; + mutex_unlock(&pmac_backlight_mutex); +#endif + + printk("riva: Backlight initialized (%s)\n", name); + + return; + +error: + return; +} + +static void riva_bl_exit(struct riva_par *par) +{ + struct fb_info *info = pci_get_drvdata(par->pdev); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); +#endif + + mutex_lock(&info->bl_mutex); + if (info->bl_dev) { +#ifdef CONFIG_PMAC_BACKLIGHT + if (pmac_backlight == info->bl_dev) + pmac_backlight = NULL; +#endif + + backlight_device_unregister(info->bl_dev); + + printk("riva: Backlight unloaded\n"); + } + mutex_unlock(&info->bl_mutex); + +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_unlock(&pmac_backlight_mutex); +#endif +} +#else +static inline void riva_bl_init(struct riva_par *par) {} +static inline void riva_bl_exit(struct riva_par *par) {} +#endif /* CONFIG_FB_RIVA_BACKLIGHT */ /* ------------------------------------------------------------------------- * * @@ -973,36 +1094,6 @@ static int riva_get_cmap_len(const struct fb_var_screeninfo *var) /* ------------------------------------------------------------------------- * * - * Backlight operations - * - * ------------------------------------------------------------------------- */ - -#ifdef CONFIG_PMAC_BACKLIGHT -static int riva_set_backlight_enable(int on, int level, void *data) -{ - struct riva_par *par = data; - U032 tmp_pcrt, tmp_pmc; - - tmp_pmc = par->riva.PMC[0x10F0/4] & 0x0000FFFF; - tmp_pcrt = par->riva.PCRTC0[0x081C/4] & 0xFFFFFFFC; - if(on && (level > BACKLIGHT_OFF)) { - tmp_pcrt |= 0x1; - tmp_pmc |= (1 << 31); // backlight bit - tmp_pmc |= riva_backlight_levels[level-1] << 16; // level - } - par->riva.PCRTC0[0x081C/4] = tmp_pcrt; - par->riva.PMC[0x10F0/4] = tmp_pmc; - return 0; -} - -static int riva_set_backlight_level(int level, void *data) -{ - return riva_set_backlight_enable(1, level, data); -} -#endif /* CONFIG_PMAC_BACKLIGHT */ - -/* ------------------------------------------------------------------------- * - * * framebuffer operations * * ------------------------------------------------------------------------- */ @@ -1247,10 +1338,15 @@ static int rivafb_blank(int blank, struct fb_info *info) SEQout(par, 0x01, tmp); CRTCout(par, 0x1a, vesa); -#ifdef CONFIG_PMAC_BACKLIGHT - if ( par->FlatPanel && machine_is(powermac)) { - set_backlight_enable(!blank); +#ifdef CONFIG_FB_RIVA_BACKLIGHT + mutex_lock(&info->bl_mutex); + if (info->bl_dev) { + down(&info->bl_dev->sem); + info->bl_dev->props->power = blank; + info->bl_dev->props->update_status(info->bl_dev); + up(&info->bl_dev->sem); } + mutex_unlock(&info->bl_mutex); #endif NVTRACE_LEAVE(); @@ -2037,11 +2133,9 @@ static int __devinit rivafb_probe(struct pci_dev *pd, RIVAFB_VERSION, info->fix.smem_len / (1024 * 1024), info->fix.smem_start); -#ifdef CONFIG_PMAC_BACKLIGHT - if (default_par->FlatPanel && machine_is(powermac)) - register_backlight_controller(&riva_backlight_controller, - default_par, "mnca"); -#endif + + riva_bl_init(info->par); + NVTRACE_LEAVE(); return 0; @@ -2074,6 +2168,8 @@ static void __exit rivafb_remove(struct pci_dev *pd) NVTRACE_ENTER(); + riva_bl_exit(par); + #ifdef CONFIG_FB_RIVA_I2C riva_delete_i2c_busses(par); kfree(par->EDID); diff --git a/include/asm-powerpc/backlight.h b/include/asm-powerpc/backlight.h index 1ba1f27a0b6..a5e9e656e33 100644 --- a/include/asm-powerpc/backlight.h +++ b/include/asm-powerpc/backlight.h @@ -2,30 +2,30 @@ * Routines for handling backlight control on PowerBooks * * For now, implementation resides in - * arch/powerpc/platforms/powermac/pmac_support.c + * arch/powerpc/platforms/powermac/backlight.c * */ #ifndef __ASM_POWERPC_BACKLIGHT_H #define __ASM_POWERPC_BACKLIGHT_H #ifdef __KERNEL__ -/* Abstract values */ -#define BACKLIGHT_OFF 0 -#define BACKLIGHT_MIN 1 -#define BACKLIGHT_MAX 0xf +#include <linux/fb.h> +#include <linux/mutex.h> -struct backlight_controller { - int (*set_enable)(int enable, int level, void *data); - int (*set_level)(int level, void *data); -}; +/* For locking instructions, see the implementation file */ +extern struct backlight_device *pmac_backlight; +extern struct mutex pmac_backlight_mutex; -extern void register_backlight_controller(struct backlight_controller *ctrler, void *data, char *type); -extern void unregister_backlight_controller(struct backlight_controller *ctrler, void *data); +extern void pmac_backlight_calc_curve(struct fb_info*); +extern int pmac_backlight_curve_lookup(struct fb_info *info, int value); -extern int set_backlight_enable(int enable); -extern int get_backlight_enable(void); -extern int set_backlight_level(int level); -extern int get_backlight_level(void); +extern int pmac_has_backlight_type(const char *type); + +extern void pmac_backlight_key_up(void); +extern void pmac_backlight_key_down(void); + +extern int pmac_backlight_set_legacy_brightness(int brightness); +extern int pmac_backlight_get_legacy_brightness(void); #endif /* __KERNEL__ */ #endif diff --git a/include/linux/fb.h b/include/linux/fb.h index 315d89740dd..f1281687e54 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -1,6 +1,7 @@ #ifndef _LINUX_FB_H #define _LINUX_FB_H +#include <linux/backlight.h> #include <asm/types.h> /* Definitions of frame buffers */ @@ -366,6 +367,12 @@ struct fb_cursor { struct fb_image image; /* Cursor image */ }; +#ifdef CONFIG_FB_BACKLIGHT +/* Settings for the generic backlight code */ +#define FB_BACKLIGHT_LEVELS 128 +#define FB_BACKLIGHT_MAX 0xFF +#endif + #ifdef __KERNEL__ #include <linux/fs.h> @@ -756,6 +763,21 @@ struct fb_info { struct fb_cmap cmap; /* Current cmap */ struct list_head modelist; /* mode list */ struct fb_videomode *mode; /* current mode */ + +#ifdef CONFIG_FB_BACKLIGHT + /* Lock ordering: + * bl_mutex (protects bl_dev and bl_curve) + * bl_dev->sem (backlight class) + */ + struct mutex bl_mutex; + + /* assigned backlight device */ + struct backlight_device *bl_dev; + + /* Backlight level curve */ + u8 bl_curve[FB_BACKLIGHT_LEVELS]; +#endif + struct fb_ops *fbops; struct device *device; struct class_device *class_device; /* sysfs per device attrs */ @@ -895,6 +917,7 @@ extern struct fb_info *framebuffer_alloc(size_t size, struct device *dev); extern void framebuffer_release(struct fb_info *info); extern int fb_init_class_device(struct fb_info *fb_info); extern void fb_cleanup_class_device(struct fb_info *head); +extern void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max); /* drivers/video/fbmon.c */ #define FB_MAXTIMINGS 0 diff --git a/include/linux/pmu.h b/include/linux/pmu.h index ecce5912f4d..2ed807ddc08 100644 --- a/include/linux/pmu.h +++ b/include/linux/pmu.h @@ -230,4 +230,8 @@ extern int pmu_battery_count; extern struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES]; extern unsigned int pmu_power_flags; +/* Backlight */ +extern int disable_kernel_backlight; +extern void pmu_backlight_init(struct device_node*); + #endif /* __KERNEL__ */ |