[PATCH] Rewritten backlight infrastructure for portable Apple computers
authorMichael Hanselmann <linux-kernel@hansmi.ch>
Sun, 25 Jun 2006 12:47:08 +0000 (05:47 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Sun, 25 Jun 2006 17:00:59 +0000 (10:00 -0700)
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:
arch/powerpc/kernel/traps.c
arch/powerpc/platforms/powermac/backlight.c
arch/powerpc/xmon/xmon.c
drivers/macintosh/Kconfig
drivers/macintosh/Makefile
drivers/macintosh/adbhid.c
drivers/macintosh/via-pmu-backlight.c [new file with mode: 0644]
drivers/macintosh/via-pmu.c
drivers/video/Kconfig
drivers/video/aty/Makefile
drivers/video/aty/aty128fb.c
drivers/video/aty/atyfb.h
drivers/video/aty/atyfb_base.c
drivers/video/aty/radeon_backlight.c [new file with mode: 0644]
drivers/video/aty/radeon_base.c
drivers/video/aty/radeonfb.h
drivers/video/chipsfb.c
drivers/video/fbsysfs.c
drivers/video/nvidia/Makefile
drivers/video/nvidia/nv_backlight.c [new file with mode: 0644]
drivers/video/nvidia/nv_proto.h
drivers/video/nvidia/nvidia.c
drivers/video/riva/fbdev.c
include/asm-powerpc/backlight.h
include/linux/fb.h
include/linux/pmu.h

index 91a6e04..52f5659 100644 (file)
@@ -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
index 8be2f7d..498b042 100644 (file)
  * 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;
 }
index 4735b41..0741df8 100644 (file)
@@ -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>
index ccf5df4..37cd6ee 100644 (file)
@@ -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"
index 6081acd..8972e53 100644 (file)
@@ -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
index 394334e..c26e123 100644 (file)
@@ -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 (file)
index 0000000..b42d05f
--- /dev/null
@@ -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;
+}
index c63d4e7..2a355ae 100644 (file)
@@ -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:
index 5a2840a..168ede7 100644 (file)
@@ -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
index 1852139..a6cc0e9 100644 (file)
@@ -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)
 
index f7bbff4..db878fd 100644 (file)
@@ -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
index e9b7a64..43d2cb5 100644 (file)
@@ -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;
index c054bb2..c5185f7 100644 (file)
@@ -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 (file)
index 0000000..7de66b8
--- /dev/null
@@ -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
+}
index 387a18a..c5ecbb0 100644 (file)
 #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)
index 217e00a..1645943 100644 (file)
@@ -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__ */
index 72ff6bf..d76bbfa 100644 (file)
@@ -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
index 34e0739..3ceb8c1 100644 (file)
@@ -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
index 690d37e..ca47432 100644 (file)
@@ -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 (file)
index 0000000..1c1c10c
--- /dev/null
@@ -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
+}
index b149a69..6fba656 100644 (file)
@@ -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__ */
index 093ab99..03a7c1e 100644 (file)
@@ -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
 #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)
index 3e9308f..d4384ab 100644 (file)
@@ -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);
index 1ba1f27..a5e9e65 100644 (file)
@@ -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
index 315d897..f128168 100644 (file)
@@ -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
index ecce591..2ed807d 100644 (file)
@@ -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__ */