203 lines
4.6 KiB
C
203 lines
4.6 KiB
C
|
/*
|
||
|
* Miscellaneous procedures for dealing with the PowerMac hardware.
|
||
|
* Contains support for the backlight.
|
||
|
*
|
||
|
* Copyright (C) 2000 Benjamin Herrenschmidt
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#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 <asm/prom.h>
|
||
|
#include <asm/machdep.h>
|
||
|
#include <asm/nvram.h>
|
||
|
#include <asm/backlight.h>
|
||
|
|
||
|
#include <linux/adb.h>
|
||
|
#include <linux/pmu.h>
|
||
|
|
||
|
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;
|
||
|
|
||
|
static void backlight_callback(void *);
|
||
|
static DECLARE_WORK(backlight_work, backlight_callback, NULL);
|
||
|
|
||
|
void register_backlight_controller(struct backlight_controller *ctrler,
|
||
|
void *data, 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
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
#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);
|
||
|
}
|
||
|
EXPORT_SYMBOL(register_backlight_controller);
|
||
|
|
||
|
void unregister_backlight_controller(struct backlight_controller
|
||
|
*ctrler, void *data)
|
||
|
{
|
||
|
/* We keep the current backlight level (for now) */
|
||
|
if (ctrler == backlighter && data == backlighter_data)
|
||
|
backlighter = NULL;
|
||
|
}
|
||
|
EXPORT_SYMBOL(unregister_backlight_controller);
|
||
|
|
||
|
static int __set_backlight_enable(int enable)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
int set_backlight_enable(int enable)
|
||
|
{
|
||
|
if (!backlighter)
|
||
|
return -ENODEV;
|
||
|
backlight_req_enable = enable;
|
||
|
schedule_work(&backlight_work);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
EXPORT_SYMBOL(set_backlight_enable);
|
||
|
|
||
|
int get_backlight_enable(void)
|
||
|
{
|
||
|
if (!backlighter)
|
||
|
return -ENODEV;
|
||
|
return backlight_enabled;
|
||
|
}
|
||
|
EXPORT_SYMBOL(get_backlight_enable);
|
||
|
|
||
|
static int __set_backlight_level(int level)
|
||
|
{
|
||
|
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"
|
||
|
}
|
||
|
return rc;
|
||
|
}
|
||
|
int set_backlight_level(int level)
|
||
|
{
|
||
|
if (!backlighter)
|
||
|
return -ENODEV;
|
||
|
backlight_req_level = level;
|
||
|
schedule_work(&backlight_work);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
EXPORT_SYMBOL(set_backlight_level);
|
||
|
|
||
|
int get_backlight_level(void)
|
||
|
{
|
||
|
if (!backlighter)
|
||
|
return -ENODEV;
|
||
|
return backlight_level;
|
||
|
}
|
||
|
EXPORT_SYMBOL(get_backlight_level);
|
||
|
|
||
|
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);
|
||
|
}
|