[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.

[[email protected]: add needed exports]
Signed-off-by: Michael Hanselmann <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
Cc: Richard Purdie <[email protected]>
Cc: "Antonino A. Daplas" <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
This commit is contained in:
Michael Hanselmann 2006-06-25 05:47:08 -07:00 • committed by Linus Torvalds
commit 5474c120aa
26 changed files with 1529 additions and 711 deletions

View 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"

View 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

View 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;

View file

@ -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;
}

View 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: