Commit 5474c120 authored by Michael Hanselmann's avatar Michael Hanselmann Committed by Linus Torvalds

[PATCH] Rewritten backlight infrastructure for portable Apple computers

This patch contains a total rewrite of the backlight infrastructure for
portable Apple computers.  Backward compatibility is retained.  A sysfs
interface allows userland to control the brightness with more steps than
before.  Userland is allowed to upload a brightness curve for different
monitors, similar to Mac OS X.

[akpm@osdl.org: add needed exports]
Signed-off-by: default avatarMichael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Richard Purdie <rpurdie@rpsys.net>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 17660bdd
......@@ -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
......
......@@ -3,200 +3,148 @@
* Contains support for the backlight.
*
* Copyright (C) 2000 Benjamin Herrenschmidt
* Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/reboot.h>
#include <linux/nvram.h>
#include <linux/console.h>
#include <asm/sections.h>
#include <asm/ptrace.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/nvram.h>
#include <asm/backlight.h>
#include <linux/adb.h>
#include <linux/pmu.h>
#define OLD_BACKLIGHT_MAX 15
static struct backlight_controller *backlighter;
static void* backlighter_data;
static int backlight_autosave;
static int backlight_level = BACKLIGHT_MAX;
static int backlight_enabled = 1;
static int backlight_req_level = -1;
static int backlight_req_enable = -1;
/* Protect the pmac_backlight variable */
DEFINE_MUTEX(pmac_backlight_mutex);
static void backlight_callback(void *);
static DECLARE_WORK(backlight_work, backlight_callback, NULL);
/* Main backlight storage
*
* Backlight drivers in this variable are required to have the "props"
* attribute set and to have an update_status function.
*
* We can only store one backlight here, but since Apple laptops have only one
* internal display, it doesn't matter. Other backlight drivers can be used
* independently.
*
* Lock ordering:
* pmac_backlight_mutex (global, main backlight)
* pmac_backlight->sem (backlight class)
*/
struct backlight_device *pmac_backlight;
void register_backlight_controller(struct backlight_controller *ctrler,
void *data, char *type)
int pmac_has_backlight_type(const char *type)
{
struct device_node* bk_node;
char *prop;
int valid = 0;
/* There's already a matching controller, bail out */
if (backlighter != NULL)
return;
bk_node = find_devices("backlight");
#ifdef CONFIG_ADB_PMU
/* Special case for the old PowerBook since I can't test on it */
backlight_autosave = machine_is_compatible("AAPL,3400/2400")
|| machine_is_compatible("AAPL,3500");
if ((backlight_autosave
|| machine_is_compatible("AAPL,PowerBook1998")
|| machine_is_compatible("PowerBook1,1"))
&& !strcmp(type, "pmu"))
valid = 1;
#endif
struct device_node* bk_node = find_devices("backlight");
if (bk_node) {
prop = get_property(bk_node, "backlight-control", NULL);
if (prop && !strncmp(prop, type, strlen(type)))
valid = 1;
}
if (!valid)
return;
backlighter = ctrler;
backlighter_data = data;
if (bk_node && !backlight_autosave)
prop = get_property(bk_node, "bklt", NULL);
else
prop = NULL;
if (prop) {
backlight_level = ((*prop)+1) >> 1;
if (backlight_level > BACKLIGHT_MAX)
backlight_level = BACKLIGHT_MAX;
char *prop = get_property(bk_node, "backlight-control", NULL);
if (prop && strncmp(prop, type, strlen(type)) == 0)
return 1;
}
#ifdef CONFIG_ADB_PMU
if (backlight_autosave) {
struct adb_request req;
pmu_request(&req, NULL, 2, 0xd9, 0);
while (!req.complete)
pmu_poll();
backlight_level = req.reply[0] >> 4;
}
#endif
acquire_console_sem();
if (!backlighter->set_enable(1, backlight_level, data))
backlight_enabled = 1;
release_console_sem();
printk(KERN_INFO "Registered \"%s\" backlight controller,"
"level: %d/15\n", type, backlight_level);
return 0;
}
EXPORT_SYMBOL(register_backlight_controller);
void unregister_backlight_controller(struct backlight_controller
*ctrler, void *data)
int pmac_backlight_curve_lookup(struct fb_info *info, int value)
{
/* We keep the current backlight level (for now) */
if (ctrler == backlighter && data == backlighter_data)
backlighter = NULL;
int level = (FB_BACKLIGHT_LEVELS - 1);
if (info && info->bl_dev) {
int i, max = 0;
/* Look for biggest value */
for (i = 0; i < FB_BACKLIGHT_LEVELS; i++)
max = max((int)info->bl_curve[i], max);
/* Look for nearest value */
for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) {
int diff = abs(info->bl_curve[i] - value);
if (diff < max) {
max = diff;
level = i;
}
}
}
return level;
}
EXPORT_SYMBOL(unregister_backlight_controller);
static int __set_backlight_enable(int enable)
static void pmac_backlight_key(int direction)
{
int rc;
if (!backlighter)
return -ENODEV;
acquire_console_sem();
rc = backlighter->set_enable(enable, backlight_level,
backlighter_data);
if (!rc)
backlight_enabled = enable;
release_console_sem();
return rc;
mutex_lock(&pmac_backlight_mutex);
if (pmac_backlight) {
struct backlight_properties *props;
int brightness;
down(&pmac_backlight->sem);
props = pmac_backlight->props;
brightness = props->brightness +
((direction?-1:1) * (props->max_brightness / 15));
if (brightness < 0)
brightness = 0;
else if (brightness > props->max_brightness)
brightness = props->max_brightness;
props->brightness = brightness;
props->update_status(pmac_backlight);
up(&pmac_backlight->sem);
}
mutex_unlock(&pmac_backlight_mutex);
}
int set_backlight_enable(int enable)
void pmac_backlight_key_up()
{
if (!backlighter)
return -ENODEV;
backlight_req_enable = enable;
schedule_work(&backlight_work);
return 0;
pmac_backlight_key(0);
}
EXPORT_SYMBOL(set_backlight_enable);
int get_backlight_enable(void)
void pmac_backlight_key_down()
{
if (!backlighter)
return -ENODEV;
return backlight_enabled;
pmac_backlight_key(1);
}
EXPORT_SYMBOL(get_backlight_enable);
static int __set_backlight_level(int level)
int pmac_backlight_set_legacy_brightness(int brightness)
{
int rc = 0;
if (!backlighter)
return -ENODEV;
if (level < BACKLIGHT_MIN)
level = BACKLIGHT_OFF;
if (level > BACKLIGHT_MAX)
level = BACKLIGHT_MAX;
acquire_console_sem();
if (backlight_enabled)
rc = backlighter->set_level(level, backlighter_data);
if (!rc)
backlight_level = level;
release_console_sem();
if (!rc && !backlight_autosave) {
level <<=1;
if (level & 0x10)
level |= 0x01;
// -- todo: save to property "bklt"
int error = -ENXIO;
mutex_lock(&pmac_backlight_mutex);
if (pmac_backlight) {
struct backlight_properties *props;
down(&pmac_backlight->sem);
props = pmac_backlight->props;
props->brightness = brightness *
props->max_brightness / OLD_BACKLIGHT_MAX;
props->update_status(pmac_backlight);
up(&pmac_backlight->sem);
error = 0;
}
return rc;
mutex_unlock(&pmac_backlight_mutex);
return error;
}
int set_backlight_level(int level)
int pmac_backlight_get_legacy_brightness()
{
if (!backlighter)
return -ENODEV;
backlight_req_level = level;
schedule_work(&backlight_work);
return 0;
}
int result = -ENXIO;
EXPORT_SYMBOL(set_backlight_level);
mutex_lock(&pmac_backlight_mutex);
if (pmac_backlight) {
struct backlight_properties *props;
int get_backlight_level(void)
{
if (!backlighter)
return -ENODEV;
return backlight_level;
}
EXPORT_SYMBOL(get_backlight_level);
down(&pmac_backlight->sem);
props = pmac_backlight->props;
result = props->brightness *
OLD_BACKLIGHT_MAX / props->max_brightness;
up(&pmac_backlight->sem);
}
mutex_unlock(&pmac_backlight_mutex);
static void backlight_callback(void *dummy)
{
int level, enable;
do {
level = backlight_req_level;
enable = backlight_req_enable;
mb();
if (level >= 0)
__set_backlight_level(level);
if (enable >= 0)
__set_backlight_enable(enable);
} while(cmpxchg(&backlight_req_level, level, -1) != level ||
cmpxchg(&backlight_req_enable, enable, -1) != enable);
return result;
}
......@@ -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>
......
......@@ -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"
......
......@@ -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
......
......@@ -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;
......
/*
* 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;
}
......@@ -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 @@ pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
#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:
......
......@@ -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
......
......@@ -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)
This diff is collapsed.
......@@ -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;
......
......@@ -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
......
/*
* 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
}
......@@ -78,10 +78,6 @@
#include <asm/pci-bridge.h>
#include "../macmodes.h"
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#endif
#ifdef CONFIG_BOOTX_TEXT
#include <asm/btext.h>
#endif
......@@ -277,20 +273,6 @@ static int nomtrr = 0;
* prototypes
*/
#ifdef CONFIG_PPC_OF
#ifdef CONFIG_PMAC_BACKLIGHT
static int radeon_set_backlight_enable(int on, int level, void *data);
static int radeon_set_backlight_level(int level, void *data);
static struct backlight_controller radeon_backlight_controller = {
radeon_set_backlight_enable,
radeon_set_backlight_level
};
#endif /* CONFIG_PMAC_BACKLIGHT */
#endif /* CONFIG_PPC_OF */
static void radeon_unmap_ROM(struct radeonfb_info *rinfo, struct pci_dev *dev)
{
if (!rinfo->bios_seg)
......@@ -1913,116 +1895,6 @@ static int __devinit radeon_set_fbinfo (struct radeonfb_info *rinfo)
return 0;
}
#ifdef CONFIG_PMAC_BACKLIGHT
/* TODO: Dbl check these tables, we don't go up to full ON backlight
* in these, possibly because we noticed MacOS doesn't, but I'd prefer
* having some more official numbers from ATI
*/
static int backlight_conv_m6[] = {
0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e,
0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24
};
static int backlight_conv_m7[] = {
0x00, 0x3f, 0x4a, 0x55, 0x60, 0x6b, 0x76, 0x81,
0x8c, 0x97, 0xa2, 0xad, 0xb8, 0xc3, 0xce, 0xd9
};
#define BACKLIGHT_LVDS_OFF
#undef BACKLIGHT_DAC_OFF
/* We turn off the LCD completely instead of just dimming the backlight.
* This provides some greater power saving and the display is useless
* without backlight anyway.
*/
static int radeon_set_backlight_enable(int on, int level, void *data)
{
struct radeonfb_info *rinfo = (struct radeonfb_info *)data;
u32 lvds_gen_cntl, tmpPixclksCntl;
int* conv_table;
if (rinfo->mon1_type != MT_LCD)
return 0;
/* Pardon me for that hack... maybe some day we can figure
* out in what direction backlight should work on a given
* panel ?
*/
if ((rinfo->family == CHIP_FAMILY_RV200 ||
rinfo->family == CHIP_FAMILY_RV250 ||
rinfo->family == CHIP_FAMILY_RV280 ||
rinfo->family == CHIP_FAMILY_RV350) &&
!machine_is_compatible("PowerBook4,3") &&
!machine_is_compatible("PowerBook6,3") &&
!machine_is_compatible("PowerBook6,5"))
conv_table = backlight_conv_m7;
else
conv_table = backlight_conv_m6;
del_timer_sync(&rinfo->lvds_timer);
radeon_engine_idle();
lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
if (on && (level > BACKLIGHT_OFF)) {
lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) {
lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON);
lvds_gen_cntl |= LVDS_BLON | LVDS_EN;
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
lvds_gen_cntl |= (conv_table[level] <<
LVDS_BL_MOD_LEVEL_SHIFT);
lvds_gen_cntl |= LVDS_ON;
lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN);
rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
mod_timer(&rinfo->lvds_timer,
jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
} else {
lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
lvds_gen_cntl |= (conv_table[level] <<
LVDS_BL_MOD_LEVEL_SHIFT);
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
}
rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl
& LVDS_STATE_MASK;
} else {
/* Asic bug, when turning off LVDS_ON, we have to make sure
RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
*/
tmpPixclksCntl = INPLL(PIXCLKS_CNTL);
if (rinfo->is_mobility || rinfo->is_IGP)
OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN);
lvds_gen_cntl |= (conv_table[0] <<
LVDS_BL_MOD_LEVEL_SHIFT);
lvds_gen_cntl |= LVDS_DISPLAY_DIS;
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
udelay(100);
lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN);
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
lvds_gen_cntl &= ~(LVDS_DIGON);
rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
mod_timer(&rinfo->lvds_timer,
jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
if (rinfo->is_mobility || rinfo->is_IGP)
OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl);
}
rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
return 0;
}
static int radeon_set_backlight_level(int level, void *data)
{
return radeon_set_backlight_enable(1, level, data);
}
#endif /* CONFIG_PMAC_BACKLIGHT */
/*
* This reconfigure the card's internal memory map. In theory, we'd like
* to setup the card's memory at the same address as it's PCI bus address,
......@@ -2477,14 +2349,7 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
MTRR_TYPE_WRCOMB, 1);
#endif
#ifdef CONFIG_PMAC_BACKLIGHT
if (rinfo->mon1_type == MT_LCD) {
register_backlight_controller(&radeon_backlight_controller,
rinfo, "ati");
register_backlight_controller(&radeon_backlight_controller,
rinfo, "mnca");
}
#endif
radeonfb_bl_init(rinfo);
printk ("radeonfb (%s): %s\n", pci_name(rinfo->pdev), rinfo->name);
......@@ -2528,7 +2393,8 @@ static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev)
if (!rinfo)
return;
radeonfb_bl_exit(rinfo);
radeonfb_pm_exit(rinfo);
if (rinfo->mon1_EDID)
......
......@@ -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__ */
......@@ -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
......
......@@ -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
......@@ -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)
/*
* 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
}
......@@ -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__ */
......@@ -22,6 +22,7 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/console.h>
#include <linux/backlight.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
......@@ -29,10 +30,6 @@
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#endif
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/machdep.h>
#include <asm/backlight.h>
#endif
#include "nv_local.h"
#include "nv_type.h"
......@@ -470,75 +467,6 @@ static struct fb_var_screeninfo __devinitdata nvidiafb_default_var = {
.vmode = FB_VMODE_NONINTERLACED
};
/*
* Backlight control
*/
#ifdef CONFIG_PMAC_BACKLIGHT
static int nvidia_backlight_levels[] = {
0x158,
0x192,
0x1c6,
0x200,
0x234,
0x268,
0x2a2,
0x2d6,
0x310,
0x344,
0x378,
0x3b2,
0x3e6,
0x41a,
0x454,
0x534,
};
/* ------------------------------------------------------------------------- *
*
* Backlight operations
*
* ------------------------------------------------------------------------- */
static int nvidia_set_backlight_enable(int on, int level, void *data)
{
struct nvidia_par *par = data;
u32 tmp_pcrt, tmp_pmc, fpcontrol;
tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF;
tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC;
fpcontrol = NV_RD32(par->PRAMDAC, 0x0848) & 0xCFFFFFCC;
if (on && (level > BACKLIGHT_OFF)) {
tmp_pcrt |= 0x1;
tmp_pmc |= (1 << 31); // backlight bit
tmp_pmc |= nvidia_backlight_levels[level - 1] << 16;
}
if (on)
fpcontrol |= par->fpSyncs;
else
fpcontrol |= 0x20000022;
NV_WR32(par->PCRTC0, 0x081C, tmp_pcrt);
NV_WR32(par->PMC, 0x10F0, tmp_pmc);
NV_WR32(par->PRAMDAC, 0x848, fpcontrol);
return 0;
}
static int nvidia_set_backlight_level(int level, void *data)
{
return nvidia_set_backlight_enable(1, level, data);
}
static struct backlight_controller nvidia_backlight_controller = {
nvidia_set_backlight_enable,
nvidia_set_backlight_level
};
#endif /* CONFIG_PMAC_BACKLIGHT */
static void nvidiafb_load_cursor_image(struct nvidia_par *par, u8 * data8,
u16 bg, u16 fg, u32 w, u32 h)
{
......@@ -1355,10 +1283,15 @@ static int nvidiafb_blank(int blank, struct fb_info *info)
NVWriteSeq(par, 0x01, tmp);
NVWriteCrtc(par, 0x1a, vesa);
#ifdef CONFIG_PMAC_BACKLIGHT
if (par->FlatPanel && machine_is(powermac)) {
set_backlight_enable(!blank);
#ifdef CONFIG_FB_NVIDIA_BACKLIGHT
mutex_lock(&info->bl_mutex);
if (info->bl_dev) {
down(&info->bl_dev->sem);
info->bl_dev->props->power = blank;
info->bl_dev->props->update_status(info->bl_dev);
up(&info->bl_dev->sem);
}
mutex_unlock(&info->bl_mutex);
#endif
NVTRACE_LEAVE();
......@@ -1741,11 +1674,9 @@ static int __devinit nvidiafb_probe(struct pci_dev *pd,
"PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
info->fix.id,
par->FbMapSize / (1024 * 1024), info->fix.smem_start);
#ifdef CONFIG_PMAC_BACKLIGHT
if (par->FlatPanel && machine_is(powermac))
register_backlight_controller(&nvidia_backlight_controller,
par, "mnca");
#endif
nvidia_bl_init(par);
NVTRACE_LEAVE();
return 0;
......@@ -1775,6 +1706,8 @@ static void __exit nvidiafb_remove(struct pci_dev *pd)
NVTRACE_ENTER();
nvidia_bl_exit(par);
unregister_framebuffer(info);
#ifdef CONFIG_MTRR
if (par->mtrr.vram_valid)
......
......@@ -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 */
/* ------------------------------------------------------------------------- *
*
......@@ -971,36 +1092,6 @@ static int riva_get_cmap_len(const struct fb_var_screeninfo *var)
return rc;
}
/* ------------------------------------------------------------------------- *
*
* 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);
......
......@@ -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
#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
......
......@@ -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__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment