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

[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
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -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
+109 −161
Original line number Diff line number Diff line
@@ -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;
		char *prop = get_property(bk_node, "backlight-control", NULL);
		if (prop && strncmp(prop, type, strlen(type)) == 0)
			return 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;

	return 0;
}

#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;
int pmac_backlight_curve_lookup(struct fb_info *info, int value)
{
	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;
			}
#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;
	return level;
}
int set_backlight_enable(int enable)

static void pmac_backlight_key(int direction)
{
	if (!backlighter)
		return -ENODEV;
	backlight_req_enable = enable;
	schedule_work(&backlight_work);
	return 0;
}
	mutex_lock(&pmac_backlight_mutex);
	if (pmac_backlight) {
		struct backlight_properties *props;
		int brightness;

EXPORT_SYMBOL(set_backlight_enable);
		down(&pmac_backlight->sem);
		props = pmac_backlight->props;

int get_backlight_enable(void)
{
	if (!backlighter)
		return -ENODEV;
	return backlight_enabled;
		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);
}
EXPORT_SYMBOL(get_backlight_enable);

static int __set_backlight_level(int level)
void pmac_backlight_key_up()
{
	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"
	pmac_backlight_key(0);
}
	return rc;

void pmac_backlight_key_down()
{
	pmac_backlight_key(1);
}
int set_backlight_level(int level)

int pmac_backlight_set_legacy_brightness(int brightness)
{
	if (!backlighter)
		return -ENODEV;
	backlight_req_level = level;
	schedule_work(&backlight_work);
	return 0;
	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;
	}
	mutex_unlock(&pmac_backlight_mutex);

EXPORT_SYMBOL(set_backlight_level);
	return error;
}

int get_backlight_level(void)
int pmac_backlight_get_legacy_brightness()
{
	if (!backlighter)
		return -ENODEV;
	return backlight_level;
	int result = -ENXIO;

	mutex_lock(&pmac_backlight_mutex);
	if (pmac_backlight) {
		struct backlight_properties *props;

		down(&pmac_backlight->sem);
		props = pmac_backlight->props;
		result = props->brightness *
			OLD_BACKLIGHT_MAX / props->max_brightness;
		up(&pmac_backlight->sem);
	}
EXPORT_SYMBOL(get_backlight_level);
	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;
}
+0 −3
Original line number Diff line number Diff line
@@ -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>
+12 −7
Original line number Diff line number Diff line
@@ -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"
+1 −0
Original line number Diff line number Diff line
@@ -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
Loading