@@ -481,6 +481,12 @@ acpi_video_device_set_state(struct acpi_video_device *device, int state)
return status;
}
+static unsigned int (*acpi_brightness_hook_routine)
+ (void *dev, unsigned int brightness) = NULL;
+static char *acpi_brightness_hook_driver;
+static void *acpi_brightness_hook_dev;
+static unsigned int acpi_brightness_hook_max;
+
static int
acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
union acpi_object **levels)
@@ -520,13 +526,25 @@ acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
struct acpi_object_list args = { 1, &arg0 };
int state;
- arg0.integer.value = level;
+ /* If another driver has registered a brightness hook override,
+ * set the brightness using that method, otherwise set the brightness
+ * using the acpi _BCM method. */
+ if ( acpi_brightness_hook_routine ) {
+ status = acpi_brightness_hook_routine(acpi_brightness_hook_dev,
+ level * acpi_brightness_hook_max / 100);
+ if ( status != 0 ) {
+ ACPI_ERROR((AE_INFO, "brightness hook failed"));
+ return -EIO;
+ }
+ } else {
+ arg0.integer.value = level;
- status = acpi_evaluate_object(device->dev->handle, "_BCM",
- &args, NULL);
- if (ACPI_FAILURE(status)) {
- ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));
- return -EIO;
+ status = acpi_evaluate_object(device->dev->handle, "_BCM",
+ &args, NULL);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));
+ return -EIO;
+ }
}
device->brightness->curr = level;
@@ -607,7 +625,10 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
acpi_status status = AE_OK;
int i;
- if (device->cap._BQC || device->cap._BCQ) {
+ /* Try to get the current brightness using the _BQC/_BCQ method, only
+ * if another driver has not registered a brightness hook override. */
+ if (!acpi_brightness_hook_routine
+ && (device->cap._BQC || device->cap._BCQ)) {
char *buf = device->cap._BQC ? "_BQC" : "_BCQ";
status = acpi_evaluate_integer(device->dev->handle, buf,
@@ -784,7 +805,7 @@ acpi_video_cmp_level(const void *a, const void *b)
* device : video output device (LCD, CRT, ..)
*
* Return Value:
- * Maximum brightness level
+ * 0 on success, error code on failure.
*
* Allocate and initialize device->brightness.
*/
@@ -944,6 +965,99 @@ out:
* device : video output device (LCD, CRT, ..)
*
* Return Value:
+ * 0 on success, error code on failure.
+ *
+ * Allocate and initialize device->brightness. when a driver has registered
+ * a brightness hook override via acpi_brightness_hook_register.
+ *
+ * Cobbles up a fake brightness 'levels' array (emulating a _BCL list) and
+ * sets max brightness -- effectively what acpi_video_init_brightness does
+ * for the native acpi brightness methods
+ */
+static int
+acpi_brightness_hook_init(struct acpi_video_device *device)
+{
+ struct acpi_video_device_brightness *br = NULL;
+ int result = -EINVAL;
+ int nsteps = 10;
+ int count, i;
+ static int initialized = 0;
+
+ if ( initialized )
+ return 1;
+ initialized = 1;
+
+ device->brightness = NULL;
+
+ br = kzalloc(sizeof(*br), GFP_KERNEL);
+ if (!br) {
+ printk(KERN_ERR "can't allocate memory\n");
+ result = -ENOMEM;
+ return result;
+ }
+ br->levels = kmalloc((nsteps + 2) * sizeof *(br->levels),
+ GFP_KERNEL);
+ if (!br->levels) {
+ result = -ENOMEM;
+ kfree(br);
+ return result;
+ }
+
+ for (count=2, i = 1; i <= nsteps; i++)
+ br->levels[count++] = (u32) i * 100 / nsteps;
+ br->levels[0] = 100;
+ br->levels[1] = br->levels[2+(nsteps/2)];
+ br->count = count;
+ device->brightness = br;
+
+ result = acpi_video_device_lcd_set_level(device, 100);
+ if (result) {
+ kfree(br->levels);
+ kfree(br);
+ device->brightness = NULL;
+ return result;
+ }
+
+ /* Switch off acpi's native brightness switch control */
+ brightness_switch_enabled = 0;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "set up %d brightness levels\n", nsteps));
+
+ return 0;
+}
+
+/*
+ * Arg:
+ * driver_name : name of the registering driver
+ * set_brightness_routine: pointer to the new set_brightness method
+ * dev : arbitrary pointer passed to set_brightness_routine
+ * max_brightness : set_brightnes_routines' maximum brightness value
+ *
+ * Return Value:
+ * None
+ *
+ * Register a brightness hook override method, which ACPI will use
+ * instead of its native _BCM/_BCL/_BQC methods.
+ */
+void acpi_brightness_hook_register(
+ char *driver_name,
+ unsigned int (*set_brightness_routine)
+ (void *dev, unsigned int brightness),
+ void *dev, int max_brightness)
+{
+ acpi_brightness_hook_routine = set_brightness_routine;
+ acpi_brightness_hook_driver = driver_name,
+ acpi_brightness_hook_dev = dev;
+ acpi_brightness_hook_max = max_brightness;
+}
+EXPORT_SYMBOL(acpi_brightness_hook_register);
+
+/*
+ * Arg:
+ * device : video output device (LCD, CRT, ..)
+ *
+ * Return Value:
* None
*
* Find out all required AML methods defined under the output
@@ -984,22 +1098,33 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
device->cap._DSS = 1;
}
- if (acpi_video_backlight_support()) {
+ if (acpi_brightness_hook_routine || acpi_video_backlight_support()) {
int result;
static int count = 0;
char *name;
- result = acpi_video_init_brightness(device);
+ /* If another driver has registered a brightness hook override,
+ * call the brightness_hook init, otherwise call the native
+ * acpi_video brightness init. */
+ if (acpi_brightness_hook_routine)
+ result = acpi_brightness_hook_init(device);
+ else
+ result = acpi_video_init_brightness(device);
if (result)
return;
name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
if (!name)
return;
- sprintf(name, "acpi_video%d", count++);
+ if (acpi_brightness_hook_routine)
+ sprintf(name, "%s", acpi_brightness_hook_driver);
+ else
+ sprintf(name, "acpi_video%d", count++);
device->backlight = backlight_device_register(name,
NULL, device, &acpi_backlight_ops);
device->backlight->props.max_brightness = device->brightness->count-3;
+ dev_info(&device->dev->dev,
+ "registered as backlight/%s\n", name);
kfree(name);
result = sysfs_create_link(&device->backlight->dev.kobj,
@@ -2,9 +2,19 @@
#define __ACPI_VIDEO_H
#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+extern void acpi_brightness_hook_register(
+ char *driver_name,
+ unsigned int (*set_brightness_routine)
+ (void *dev, unsigned int brightness),
+ void *dev, unsigned int max_brightness);
extern int acpi_video_register(void);
extern void acpi_video_unregister(void);
#else
+static inline acpi_brightness_hook_register(
+ char *driver_name,
+ unsigned int (*set_brightness_routine)
+ (void *dev, unsigned int brightness),
+ void *dev, unsigned int max_brightness) { return; }
static inline int acpi_video_register(void) { return 0; }
static inline void acpi_video_unregister(void) { return; }
#endif