forked from analogdevicesinc/linux
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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]>
- Loading branch information
Michael Hanselmann
authored and
Linus Torvalds
committed
Jun 25, 2006
1 parent
17660bd
commit 5474c12
Showing
26 changed files
with
1,529 additions
and
711 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,200 +3,148 @@ | |
* Contains support for the backlight. | ||
* | ||
* Copyright (C) 2000 Benjamin Herrenschmidt | ||
* Copyright (C) 2006 Michael Hanselmann <[email protected]> | ||
* | ||
*/ | ||
|
||
#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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.