Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 67

kernel-2.6.18-238.el5.src.rpm

From: Dave Airlie <airlied@redhat.com>
Date: Mon, 21 Jan 2008 14:52:33 +1000
Subject: [acpi] backport video support from upstream
Message-id: 1200891153.3791.9.camel@localhost
O-Subject: [RHEL5.2 PATCH] BZ#428326 Backport ACPI video support from upstream
Bugzilla: 428326

BZ#

diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 56666a9..02898a7 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
  *  Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org>
+ *  Copyright (C) 2006 Thomas Tuttle <linux-kernel@ttuttle.net>
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *
@@ -28,9 +29,11 @@
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-
+#include <linux/input.h>
+#include <linux/backlight.h>
 #include <asm/uaccess.h>
 
 #include <acpi/acpi_bus.h>
@@ -38,7 +41,6 @@
 
 #define ACPI_VIDEO_COMPONENT		0x08000000
 #define ACPI_VIDEO_CLASS		"video"
-#define ACPI_VIDEO_DRIVER_NAME		"ACPI Video Driver"
 #define ACPI_VIDEO_BUS_NAME		"Video Bus"
 #define ACPI_VIDEO_DEVICE_NAME		"Video Device"
 #define ACPI_VIDEO_NOTIFY_SWITCH	0x80
@@ -47,20 +49,26 @@
 #define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT	0x83
 #define ACPI_VIDEO_NOTIFY_PREV_OUTPUT	0x84
 
-#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS	0x82
-#define	ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS	0x83
-#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS	0x84
-#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS	0x85
-#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF		0x86
+#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS	0x85
+#define	ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS	0x86
+#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS	0x87
+#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS	0x88
+#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF		0x89
 
 #define ACPI_VIDEO_HEAD_INVALID		(~0u - 1)
 #define ACPI_VIDEO_HEAD_END		(~0u)
+#define MAX_NAME_LEN	20
+
+#define ACPI_VIDEO_DISPLAY_CRT 1
+#define ACPI_VIDEO_DISPLAY_TV  2
+#define ACPI_VIDEO_DISPLAY_DVI 3
+#define ACPI_VIDEO_DISPLAY_LCD 4
 
 #define _COMPONENT		ACPI_VIDEO_COMPONENT
-ACPI_MODULE_NAME("acpi_video")
+ACPI_MODULE_NAME("video");
 
-    MODULE_AUTHOR("Bruno Ducrot");
-MODULE_DESCRIPTION(ACPI_VIDEO_DRIVER_NAME);
+MODULE_AUTHOR("Bruno Ducrot");
+MODULE_DESCRIPTION("ACPI Video Driver");
 MODULE_LICENSE("GPL");
 
 static int acpi_video_bus_add(struct acpi_device *device);
@@ -69,7 +77,7 @@ static int acpi_video_bus_match(struct acpi_device *device,
 				struct acpi_driver *driver);
 
 static struct acpi_driver acpi_video_bus = {
-	.name = ACPI_VIDEO_DRIVER_NAME,
+	.name = "video",
 	.class = ACPI_VIDEO_CLASS,
 	.ops = {
 		.add = acpi_video_bus_add,
@@ -97,9 +105,9 @@ struct acpi_video_bus_cap {
 
 struct acpi_video_device_attrib {
 	u32 display_index:4;	/* A zero-based instance of the Display */
-	u32 display_port_attachment:4;	/*This field differenates displays type */
+	u32 display_port_attachment:4;	/*This field differentiates the display type */
 	u32 display_type:4;	/*Describe the specific type in use */
-	u32 vendor_specific:4;	/*Chipset Vendor Specifi */
+	u32 vendor_specific:4;	/*Chipset Vendor Specific */
 	u32 bios_can_detect:1;	/*BIOS can detect the device */
 	u32 depend_on_vga:1;	/*Non-VGA output device whose power is related to 
 				   the VGA device. */
@@ -123,29 +131,32 @@ struct acpi_video_bus {
 	u8 attached_count;
 	struct acpi_video_bus_cap cap;
 	struct acpi_video_bus_flags flags;
-	struct semaphore sem;
 	struct list_head video_device_list;
+	struct mutex device_list_lock;	/* protects video_device_list */
 	struct proc_dir_entry *dir;
+	struct input_dev *input;
+	char phys[32];	/* for input device */
 };
 
 struct acpi_video_device_flags {
 	u8 crt:1;
 	u8 lcd:1;
 	u8 tvout:1;
+	u8 dvi:1;
 	u8 bios:1;
 	u8 unknown:1;
-	u8 reserved:3;
+	u8 reserved:2;
 };
 
 struct acpi_video_device_cap {
 	u8 _ADR:1;		/*Return the unique ID */
 	u8 _BCL:1;		/*Query list of brightness control levels supported */
 	u8 _BCM:1;		/*Set the brightness level */
+	u8 _BQC:1;		/* Get current brightness level */
 	u8 _DDC:1;		/*Return the EDID for this device */
 	u8 _DCS:1;		/*Return status of output device */
 	u8 _DGS:1;		/*Query graphics state */
 	u8 _DSS:1;		/*Device state set */
-	u8 _reserved:1;
 };
 
 struct acpi_video_device_brightness {
@@ -162,6 +173,8 @@ struct acpi_video_device {
 	struct acpi_video_bus *video;
 	struct acpi_device *dev;
 	struct acpi_video_device_brightness *brightness;
+	struct backlight_device *backlight;
+	struct output_device *output_dev;
 };
 
 /* bus */
@@ -256,10 +269,18 @@ static void acpi_video_device_bind(struct acpi_video_bus *video,
 				   struct acpi_video_device *device);
 static int acpi_video_device_enumerate(struct acpi_video_bus *video);
 static int acpi_video_switch_output(struct acpi_video_bus *video, int event);
+static int acpi_video_device_lcd_set_level(struct acpi_video_device *device,
+			int level);
+static int acpi_video_device_lcd_get_level_current(
+			struct acpi_video_device *device,
+			unsigned long *level);
 static int acpi_video_get_next_level(struct acpi_video_device *device,
 				     u32 level_current, u32 event);
 static void acpi_video_switch_brightness(struct acpi_video_device *device,
 					 int event);
+static int acpi_video_device_get_state(struct acpi_video_device *device,
+			    unsigned long *state);
+static int acpi_video_device_set_state(struct acpi_video_device *device, int state);
 
 /* --------------------------------------------------------------------------
                                Video Management
@@ -337,15 +358,17 @@ acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
 static int
 acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
 {
-	int status;
+	int status = AE_OK;
 	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
 	struct acpi_object_list args = { 1, &arg0 };
 
 
 	arg0.integer.value = level;
-	status = acpi_evaluate_object(device->dev->handle, "_BCM", &args, NULL);
 
-	printk(KERN_DEBUG "set_level status: %x\n", status);
+	if (device->cap._BCM)
+		status = acpi_evaluate_object(device->dev->handle, "_BCM",
+					      &args, NULL);
+	device->brightness->curr = level;
 	return status;
 }
 
@@ -353,11 +376,11 @@ static int
 acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
 					unsigned long *level)
 {
-	int status;
-
-	status = acpi_evaluate_integer(device->dev->handle, "_BQC", NULL, level);
-
-	return status;
+	if (device->cap._BQC)
+		return acpi_evaluate_integer(device->dev->handle, "_BQC", NULL,
+					     level);
+	*level = device->brightness->curr;
+	return AE_OK;
 }
 
 static int
@@ -386,7 +409,7 @@ acpi_video_device_EDID(struct acpi_video_device *device,
 	if (ACPI_FAILURE(status))
 		return -ENODEV;
 
-	obj = (union acpi_object *)buffer.pointer;
+	obj = buffer.pointer;
 
 	if (obj && obj->type == ACPI_TYPE_BUFFER)
 		*edid = obj;
@@ -448,16 +471,16 @@ acpi_video_bus_POST_options(struct acpi_video_bus *video,
  *		0.	The system BIOS should NOT automatically switch(toggle)
  *			the active display output.
  *		1.	The system BIOS should automatically switch (toggle) the
- *			active display output. No swich event.
+ *			active display output. No switch event.
  *		2.	The _DGS value should be locked.
  *		3.	The system BIOS should not automatically switch (toggle) the
  *			active display output, but instead generate the display switch
  *			event notify code.
  *	lcd_flag	:
  *		0.	The system BIOS should automatically control the brightness level
- *			of the LCD, when the power changes from AC to DC
+ *			of the LCD when the power changes from AC to DC
  *		1. 	The system BIOS should NOT automatically control the brightness 
- *			level of the LCD, when the power changes from AC to DC.
+ *			level of the LCD when the power changes from AC to DC.
  * Return Value:
  * 		-1	wrong arg.
  */
@@ -489,20 +512,20 @@ acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
  *  Return Value:
  *  	None
  *
- *  Find out all required AML method defined under the output
+ *  Find out all required AML methods defined under the output
  *  device.
  */
 
 static void acpi_video_device_find_cap(struct acpi_video_device *device)
 {
-	acpi_integer status;
 	acpi_handle h_dummy1;
 	int i;
+	u32 max_level = 0;
 	union acpi_object *obj = NULL;
 	struct acpi_video_device_brightness *br = NULL;
 
 
-	memset(&device->cap, 0, 4);
+	memset(&device->cap, 0, sizeof(device->cap));
 
 	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
 		device->cap._ADR = 1;
@@ -513,6 +536,8 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
 	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
 		device->cap._BCM = 1;
 	}
+	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
+		device->cap._BQC = 1;
 	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
 		device->cap._DDC = 1;
 	}
@@ -526,48 +551,53 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
 		device->cap._DSS = 1;
 	}
 
-	status = acpi_video_device_lcd_query_levels(device, &obj);
-
-	if (obj && obj->type == ACPI_TYPE_PACKAGE && obj->package.count >= 2) {
-		int count = 0;
-		union acpi_object *o;
-
-		br = kmalloc(sizeof(*br), GFP_KERNEL);
-		if (!br) {
-			printk(KERN_ERR "can't allocate memory\n");
-		} else {
-			memset(br, 0, sizeof(*br));
-			br->levels = kmalloc(obj->package.count *
-					     sizeof *(br->levels), GFP_KERNEL);
-			if (!br->levels)
-				goto out;
-
-			for (i = 0; i < obj->package.count; i++) {
-				o = (union acpi_object *)&obj->package.
-				    elements[i];
-				if (o->type != ACPI_TYPE_INTEGER) {
-					printk(KERN_ERR PREFIX "Invalid data\n");
-					continue;
-				}
-				br->levels[count] = (u32) o->integer.value;
-				count++;
-			}
-		      out:
-			if (count < 2) {
-				kfree(br->levels);
-				kfree(br);
+	if (ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) {
+
+		if (obj->package.count >= 2) {
+			int count = 0;
+			union acpi_object *o;
+
+			br = kzalloc(sizeof(*br), GFP_KERNEL);
+			if (!br) {
+				printk(KERN_ERR "can't allocate memory\n");
 			} else {
-				br->count = count;
-				device->brightness = br;
-				ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-						  "found %d brightness levels\n",
-						  count));
+				br->levels = kmalloc(obj->package.count *
+						     sizeof *(br->levels), GFP_KERNEL);
+				if (!br->levels)
+					goto out;
+
+				for (i = 0; i < obj->package.count; i++) {
+					o = (union acpi_object *)&obj->package.
+					    elements[i];
+					if (o->type != ACPI_TYPE_INTEGER) {
+						printk(KERN_ERR PREFIX "Invalid data\n");
+						continue;
+					}
+					br->levels[count] = (u32) o->integer.value;
+
+					if (br->levels[count] > max_level)
+						max_level = br->levels[count];
+					count++;
+				}
+			      out:
+				if (count < 2) {
+					kfree(br->levels);
+					kfree(br);
+				} else {
+					br->count = count;
+					device->brightness = br;
+					ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+							  "found %d brightness levels\n",
+							  count));
+				}
 			}
 		}
+
+	} else {
+		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available LCD brightness level\n"));
 	}
 
 	kfree(obj);
-
 	return;
 }
 
@@ -578,14 +608,14 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
  *  Return Value:
  *  	None
  *
- *  Find out all required AML method defined under the video bus device.
+ *  Find out all required AML methods defined under the video bus device.
  */
 
 static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
 {
 	acpi_handle h_dummy1;
 
-	memset(&video->cap, 0, 4);
+	memset(&video->cap, 0, sizeof(video->cap));
 	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
 		video->cap._DOS = 1;
 	}
@@ -623,19 +653,19 @@ static int acpi_video_bus_check(struct acpi_video_bus *video)
 	 * to check well known required nodes.
 	 */
 
-	/* Does this device able to support video switching ? */
+	/* Does this device support video switching? */
 	if (video->cap._DOS) {
 		video->flags.multihead = 1;
 		status = 0;
 	}
 
-	/* Does this device able to retrieve a retrieve a video ROM ? */
+	/* Does this device support retrieving a video ROM? */
 	if (video->cap._ROM) {
 		video->flags.rom = 1;
 		status = 0;
 	}
 
-	/* Does this device able to configure which video device to POST ? */
+	/* Does this device support configuring which video device to POST? */
 	if (video->cap._GPD && video->cap._SPD && video->cap._VPO) {
 		video->flags.post = 1;
 		status = 0;
@@ -654,8 +684,7 @@ static struct proc_dir_entry *acpi_video_dir;
 
 static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
 {
-	struct acpi_video_device *dev =
-	    (struct acpi_video_device *)seq->private;
+	struct acpi_video_device *dev = seq->private;
 
 
 	if (!dev)
@@ -669,6 +698,8 @@ static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
 		seq_printf(seq, "LCD\n");
 	else if (dev->flags.tvout)
 		seq_printf(seq, "TVOUT\n");
+	else if (dev->flags.dvi)
+		seq_printf(seq, "DVI\n");
 	else
 		seq_printf(seq, "UNKNOWN\n");
 
@@ -688,8 +719,7 @@ acpi_video_device_info_open_fs(struct inode *inode, struct file *file)
 static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset)
 {
 	int status;
-	struct acpi_video_device *dev =
-	    (struct acpi_video_device *)seq->private;
+	struct acpi_video_device *dev = seq->private;
 	unsigned long state;
 
 
@@ -727,8 +757,8 @@ acpi_video_device_write_state(struct file *file,
 			      size_t count, loff_t * data)
 {
 	int status;
-	struct seq_file *m = (struct seq_file *)file->private_data;
-	struct acpi_video_device *dev = (struct acpi_video_device *)m->private;
+	struct seq_file *m = file->private_data;
+	struct acpi_video_device *dev = m->private;
 	char str[12] = { 0 };
 	u32 state = 0;
 
@@ -754,8 +784,7 @@ acpi_video_device_write_state(struct file *file,
 static int
 acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
 {
-	struct acpi_video_device *dev =
-	    (struct acpi_video_device *)seq->private;
+	struct acpi_video_device *dev = seq->private;
 	int i;
 
 
@@ -784,9 +813,9 @@ acpi_video_device_write_brightness(struct file *file,
 				   const char __user * buffer,
 				   size_t count, loff_t * data)
 {
-	struct seq_file *m = (struct seq_file *)file->private_data;
-	struct acpi_video_device *dev = (struct acpi_video_device *)m->private;
-	char str[4] = { 0 };
+	struct seq_file *m = file->private_data;
+	struct acpi_video_device *dev = m->private;
+	char str[5] = { 0 };
 	unsigned int level = 0;
 	int i;
 
@@ -803,7 +832,7 @@ acpi_video_device_write_brightness(struct file *file,
 	if (level > 100)
 		return -EFAULT;
 
-	/* validate though the list of available levels */
+	/* validate through the list of available levels */
 	for (i = 0; i < dev->brightness->count; i++)
 		if (level == dev->brightness->levels[i]) {
 			if (ACPI_SUCCESS
@@ -817,8 +846,7 @@ acpi_video_device_write_brightness(struct file *file,
 
 static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
 {
-	struct acpi_video_device *dev =
-	    (struct acpi_video_device *)seq->private;
+	struct acpi_video_device *dev = seq->private;
 	int status;
 	int i;
 	union acpi_object *edid = NULL;
@@ -866,7 +894,7 @@ static int acpi_video_device_add_fs(struct acpi_device *device)
 	if (!device)
 		return -ENODEV;
 
-	vid_dev = (struct acpi_video_device *)acpi_driver_data(device);
+	vid_dev = acpi_driver_data(device);
 	if (!vid_dev)
 		return -ENODEV;
 
@@ -931,7 +959,7 @@ static int acpi_video_device_remove_fs(struct acpi_device *device)
 {
 	struct acpi_video_device *vid_dev;
 
-	vid_dev = (struct acpi_video_device *)acpi_driver_data(device);
+	vid_dev = acpi_driver_data(device);
 	if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
 		return -ENODEV;
 
@@ -950,7 +978,7 @@ static int acpi_video_device_remove_fs(struct acpi_device *device)
 /* video bus */
 static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset)
 {
-	struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
+	struct acpi_video_bus *video = seq->private;
 
 
 	if (!video)
@@ -975,7 +1003,7 @@ static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file)
 
 static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset)
 {
-	struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
+	struct acpi_video_bus *video = seq->private;
 
 
 	if (!video)
@@ -995,7 +1023,7 @@ static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file)
 
 static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
 {
-	struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
+	struct acpi_video_bus *video = seq->private;
 	unsigned long options;
 	int status;
 
@@ -1009,10 +1037,10 @@ static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
 			printk(KERN_WARNING PREFIX
 			       "The motherboard VGA device is not listed as a possible POST device.\n");
 			printk(KERN_WARNING PREFIX
-			       "This indicate a BIOS bug.  Please contact the manufacturer.\n");
+			       "This indicates a BIOS bug. Please contact the manufacturer.\n");
 		}
 		printk("%lx\n", options);
-		seq_printf(seq, "can POST: <intgrated video>");
+		seq_printf(seq, "can POST: <integrated video>");
 		if (options & 2)
 			seq_printf(seq, " <PCI video>");
 		if (options & 4)
@@ -1033,7 +1061,7 @@ acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file)
 
 static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
 {
-	struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
+	struct acpi_video_bus *video = seq->private;
 	int status;
 	unsigned long id;
 
@@ -1046,7 +1074,7 @@ static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
 		seq_printf(seq, "<not supported>\n");
 		goto end;
 	}
-	seq_printf(seq, "device posted is <%s>\n", device_decode[id & 3]);
+	seq_printf(seq, "device POSTed is <%s>\n", device_decode[id & 3]);
 
       end:
 	return 0;
@@ -1054,7 +1082,7 @@ static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
 
 static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset)
 {
-	struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
+	struct acpi_video_bus *video = seq->private;
 
 
 	seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting);
@@ -1079,8 +1107,8 @@ acpi_video_bus_write_POST(struct file *file,
 			  size_t count, loff_t * data)
 {
 	int status;
-	struct seq_file *m = (struct seq_file *)file->private_data;
-	struct acpi_video_bus *video = (struct acpi_video_bus *)m->private;
+	struct seq_file *m = file->private_data;
+	struct acpi_video_bus *video = m->private;
 	char str[12] = { 0 };
 	unsigned long opt, options;
 
@@ -1100,7 +1128,7 @@ acpi_video_bus_write_POST(struct file *file,
 	if (opt > 3)
 		return -EFAULT;
 
-	/* just in case an OEM 'forget' the motherboard... */
+	/* just in case an OEM 'forgot' the motherboard... */
 	options |= 1;
 
 	if (options & (1ul << opt)) {
@@ -1119,8 +1147,8 @@ acpi_video_bus_write_DOS(struct file *file,
 			 size_t count, loff_t * data)
 {
 	int status;
-	struct seq_file *m = (struct seq_file *)file->private_data;
-	struct acpi_video_bus *video = (struct acpi_video_bus *)m->private;
+	struct seq_file *m = file->private_data;
+	struct acpi_video_bus *video = m->private;
 	char str[12] = { 0 };
 	unsigned long opt;
 
@@ -1150,7 +1178,7 @@ static int acpi_video_bus_add_fs(struct acpi_device *device)
 	struct acpi_video_bus *video;
 
 
-	video = (struct acpi_video_bus *)acpi_driver_data(device);
+	video = acpi_driver_data(device);
 
 	if (!acpi_device_dir(device)) {
 		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
@@ -1226,7 +1254,7 @@ static int acpi_video_bus_remove_fs(struct acpi_device *device)
 	struct acpi_video_bus *video;
 
 
-	video = (struct acpi_video_bus *)acpi_driver_data(device);
+	video = acpi_driver_data(device);
 
 	if (acpi_device_dir(device)) {
 		remove_proc_entry("info", acpi_device_dir(device));
@@ -1246,6 +1274,16 @@ static int acpi_video_bus_remove_fs(struct acpi_device *device)
    -------------------------------------------------------------------------- */
 
 /* device interface */
+static struct acpi_video_device_attrib*
+acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id)
+{
+	int count;
+
+	for(count = 0; count < video->attached_count; count++)
+		if((video->attached_array[count].value.int_val & 0xffff) == device_id)
+			return &(video->attached_array[count].value.attrib);
+	return NULL;
+}
 
 static int
 acpi_video_bus_get_one_device(struct acpi_device *device,
@@ -1254,7 +1292,7 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
 	unsigned long device_id;
 	int status;
 	struct acpi_video_device *data;
-
+	struct acpi_video_device_attrib* attribute;
 
 	if (!device || !video)
 		return -EINVAL;
@@ -1263,12 +1301,10 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
 	    acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
 	if (ACPI_SUCCESS(status)) {
 
-		data = kmalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
+		data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
 		if (!data)
 			return -ENOMEM;
 
-		memset(data, 0, sizeof(struct acpi_video_device));
-
 		strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
 		strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
 		acpi_driver_data(device) = data;
@@ -1277,20 +1313,30 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
 		data->video = video;
 		data->dev = device;
 
-		switch (device_id & 0xffff) {
-		case 0x0100:
-			data->flags.crt = 1;
-			break;
-		case 0x0400:
-			data->flags.lcd = 1;
-			break;
-		case 0x0200:
-			data->flags.tvout = 1;
-			break;
-		default:
+		attribute = acpi_video_get_device_attr(video, device_id);
+
+		if((attribute != NULL) && attribute->device_id_scheme) {
+			switch (attribute->display_type) {
+			case ACPI_VIDEO_DISPLAY_CRT:
+				data->flags.crt = 1;
+				break;
+			case ACPI_VIDEO_DISPLAY_TV:
+				data->flags.tvout = 1;
+				break;
+			case ACPI_VIDEO_DISPLAY_DVI:
+				data->flags.dvi = 1;
+				break;
+			case ACPI_VIDEO_DISPLAY_LCD:
+				data->flags.lcd = 1;
+				break;
+			default:
+				data->flags.unknown = 1;
+				break;
+			}
+			if(attribute->bios_can_detect)
+				data->flags.bios = 1;
+		} else
 			data->flags.unknown = 1;
-			break;
-		}
 
 		acpi_video_device_bind(video, data);
 		acpi_video_device_find_cap(data);
@@ -1309,9 +1355,9 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
 			return -ENODEV;
 		}
 
-		down(&video->sem);
+		mutex_lock(&video->device_list_lock);
 		list_add_tail(&data->entry, &video->video_device_list);
-		up(&video->sem);
+		mutex_unlock(&video->device_list_lock);
 
 		acpi_video_device_add_fs(device);
 
@@ -1335,12 +1381,14 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
 
 static void acpi_video_device_rebind(struct acpi_video_bus *video)
 {
-	struct list_head *node, *next;
-	list_for_each_safe(node, next, &video->video_device_list) {
-		struct acpi_video_device *dev =
-		    container_of(node, struct acpi_video_device, entry);
+	struct acpi_video_device *dev;
+
+	mutex_lock(&video->device_list_lock);
+
+	list_for_each_entry(dev, &video->video_device_list, entry)
 		acpi_video_device_bind(video, dev);
-	}
+
+	mutex_unlock(&video->device_list_lock);
 }
 
 /*
@@ -1403,7 +1451,7 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video)
 		return status;
 	}
 
-	dod = (union acpi_object *)buffer.pointer;
+	dod = buffer.pointer;
 	if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
 		ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data"));
 		status = -EFAULT;
@@ -1426,7 +1474,7 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video)
 
 	count = 0;
 	for (i = 0; i < dod->package.count; i++) {
-		obj = (union acpi_object *)&dod->package.elements[i];
+		obj = &dod->package.elements[i];
 
 		if (obj->type != ACPI_TYPE_INTEGER) {
 			printk(KERN_ERR PREFIX "Invalid _DOD data\n");
@@ -1453,42 +1501,45 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video)
 /*
  *  Arg:
  *  	video	: video bus device 
- *  	event	: Nontify Event
+ *  	event	: notify event
  *
  *  Return:
  *  	< 0	: error
  *  
  *	1. Find out the current active output device.
- *	2. Identify the next output device to switch
+ *	2. Identify the next output device to switch to.
  *	3. call _DSS to do actual switch.
  */
 
 static int acpi_video_switch_output(struct acpi_video_bus *video, int event)
 {
-	struct list_head *node, *next;
+	struct list_head *node;
 	struct acpi_video_device *dev = NULL;
 	struct acpi_video_device *dev_next = NULL;
 	struct acpi_video_device *dev_prev = NULL;
 	unsigned long state;
 	int status = 0;
 
+	mutex_lock(&video->device_list_lock);
 
-	list_for_each_safe(node, next, &video->video_device_list) {
+	list_for_each(node, &video->video_device_list) {
 		dev = container_of(node, struct acpi_video_device, entry);
 		status = acpi_video_device_get_state(dev, &state);
 		if (state & 0x2) {
-			dev_next =
-			    container_of(node->next, struct acpi_video_device,
-					 entry);
-			dev_prev =
-			    container_of(node->prev, struct acpi_video_device,
-					 entry);
+			dev_next = container_of(node->next,
+					struct acpi_video_device, entry);
+			dev_prev = container_of(node->prev,
+					struct acpi_video_device, entry);
 			goto out;
 		}
 	}
+
 	dev_next = container_of(node->next, struct acpi_video_device, entry);
 	dev_prev = container_of(node->prev, struct acpi_video_device, entry);
-      out:
+
+ out:
+	mutex_unlock(&video->device_list_lock);
+
 	switch (event) {
 	case ACPI_VIDEO_NOTIFY_CYCLE:
 	case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:
@@ -1509,8 +1560,45 @@ static int
 acpi_video_get_next_level(struct acpi_video_device *device,
 			  u32 level_current, u32 event)
 {
-	/*Fix me */
-	return level_current;
+	int min, max, min_above, max_below, i, l, delta = 255;
+	max = max_below = 0;
+	min = min_above = 255;
+	/* Find closest level to level_current */
+	for (i = 0; i < device->brightness->count; i++) {
+		l = device->brightness->levels[i];
+		if (abs(l - level_current) < abs(delta)) {
+			delta = l - level_current;
+			if (!delta)
+				break;
+		}
+	}
+	/* Ajust level_current to closest available level */
+	level_current += delta;
+	for (i = 0; i < device->brightness->count; i++) {
+		l = device->brightness->levels[i];
+		if (l < min)
+			min = l;
+		if (l > max)
+			max = l;
+		if (l < min_above && l > level_current)
+			min_above = l;
+		if (l > max_below && l < level_current)
+			max_below = l;
+	}
+
+	switch (event) {
+	case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:
+		return (level_current < max) ? min_above : min;
+	case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:
+		return (level_current < max) ? min_above : max;
+	case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:
+		return (level_current > min) ? max_below : min;
+	case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:
+	case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:
+		return 0;
+	default:
+		return level_current;
+	}
 }
 
 static void
@@ -1527,24 +1615,17 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video,
 			   struct acpi_device *device)
 {
 	int status = 0;
-	struct list_head *node, *next;
-
+	struct acpi_device *dev;
 
 	acpi_video_device_enumerate(video);
 
-	list_for_each_safe(node, next, &device->children) {
-		struct acpi_device *dev =
-		    list_entry(node, struct acpi_device, node);
-
-		if (!dev)
-			continue;
+	list_for_each_entry(dev, &device->children, node) {
 
 		status = acpi_video_bus_get_one_device(dev, video);
 		if (ACPI_FAILURE(status)) {
 			ACPI_EXCEPTION((AE_INFO, status, "Cant attach device"));
 			continue;
 		}
-
 	}
 	return status;
 }
@@ -1560,14 +1641,13 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
 
 	video = device->video;
 
-	down(&video->sem);
-	list_del(&device->entry);
-	up(&video->sem);
 	acpi_video_device_remove_fs(device->dev);
 
 	status = acpi_remove_notify_handler(device->dev->handle,
 					    ACPI_DEVICE_NOTIFY,
 					    acpi_video_device_notify);
+	backlight_device_unregister(device->backlight);
+//	video_output_unregister(device->output_dev);
 
 	return 0;
 }
@@ -1575,26 +1655,27 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
 static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
 {
 	int status;
-	struct list_head *node, *next;
+	struct acpi_video_device *dev, *next;
 
+	mutex_lock(&video->device_list_lock);
 
-	list_for_each_safe(node, next, &video->video_device_list) {
-		struct acpi_video_device *data =
-		    list_entry(node, struct acpi_video_device, entry);
-		if (!data)
-			continue;
+	list_for_each_entry_safe(dev, next, &video->video_device_list, entry) {
 
-		status = acpi_video_bus_put_one_device(data);
+		status = acpi_video_bus_put_one_device(dev);
 		if (ACPI_FAILURE(status))
 			printk(KERN_WARNING PREFIX
 			       "hhuuhhuu bug in acpi video driver.\n");
 
-		if (data->brightness)
-			kfree(data->brightness->levels);
-		kfree(data->brightness);
-		kfree(data);
+		if (dev->brightness) {
+			kfree(dev->brightness->levels);
+			kfree(dev->brightness);
+		}
+		list_del(&dev->entry);
+		kfree(dev);
 	}
 
+	mutex_unlock(&video->device_list_lock);
+
 	return 0;
 }
 
@@ -1602,7 +1683,7 @@ static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
 
 static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
 {
-	return acpi_video_bus_DOS(video, 1, 0);
+	return acpi_video_bus_DOS(video, 0, 0);
 }
 
 static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
@@ -1612,94 +1693,138 @@ static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
 
 static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
 {
-	struct acpi_video_bus *video = (struct acpi_video_bus *)data;
+	struct acpi_video_bus *video = data;
 	struct acpi_device *device = NULL;
-
-	printk("video bus notify\n");
+	struct input_dev *input;
+	int keycode;
 
 	if (!video)
 		return;
 
 	device = video->device;
+	input = video->input;
 
 	switch (event) {
-	case ACPI_VIDEO_NOTIFY_SWITCH:	/* User request that a switch occur,
+	case ACPI_VIDEO_NOTIFY_SWITCH:	/* User requested a switch,
 					 * most likely via hotkey. */
 		acpi_bus_generate_event(device, event, 0);
+		keycode = KEY_SWITCHVIDEOMODE;
 		break;
 
-	case ACPI_VIDEO_NOTIFY_PROBE:	/* User plug or remove a video
+	case ACPI_VIDEO_NOTIFY_PROBE:	/* User plugged in or removed a video
 					 * connector. */
 		acpi_video_device_enumerate(video);
 		acpi_video_device_rebind(video);
 		acpi_video_switch_output(video, event);
 		acpi_bus_generate_event(device, event, 0);
+		keycode = KEY_SWITCHVIDEOMODE;
 		break;
 
 	case ACPI_VIDEO_NOTIFY_CYCLE:	/* Cycle Display output hotkey pressed. */
+		acpi_video_switch_output(video, event);
+		acpi_bus_generate_event(device, event, 0);
+		keycode = KEY_SWITCHVIDEOMODE;
+		break;
 	case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:	/* Next Display output hotkey pressed. */
+		acpi_video_switch_output(video, event);
+		acpi_bus_generate_event(device, event, 0);
+		keycode = KEY_VIDEO_NEXT;
+		break;
 	case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:	/* previous Display output hotkey pressed. */
 		acpi_video_switch_output(video, event);
 		acpi_bus_generate_event(device, event, 0);
+		keycode = KEY_VIDEO_PREV;
 		break;
 
 	default:
+		keycode = KEY_UNKNOWN;
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 				  "Unsupported event [0x%x]\n", event));
 		break;
 	}
 
+	input_report_key(input, keycode, 1);
+	input_sync(input);
+	input_report_key(input, keycode, 0);
+	input_sync(input);
+
 	return;
 }
 
 static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
 {
-	struct acpi_video_device *video_device =
-	    (struct acpi_video_device *)data;
+	struct acpi_video_device *video_device = data;
 	struct acpi_device *device = NULL;
+	struct acpi_video_bus *bus;
+	struct input_dev *input;
+	int keycode;
 
-
-	printk("video device notify\n");
 	if (!video_device)
 		return;
 
 	device = video_device->dev;
+	bus = video_device->video;
+	input = bus->input;
 
 	switch (event) {
-	case ACPI_VIDEO_NOTIFY_SWITCH:	/* change in status (cycle output device) */
-	case ACPI_VIDEO_NOTIFY_PROBE:	/* change in status (output device status) */
+	case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:	/* Cycle brightness */
+		acpi_video_switch_brightness(video_device, event);
 		acpi_bus_generate_event(device, event, 0);
+		keycode = KEY_BRIGHTNESS_CYCLE;
 		break;
-	case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:	/* Cycle brightness */
 	case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:	/* Increase brightness */
+		acpi_video_switch_brightness(video_device, event);
+		acpi_bus_generate_event(device, event, 0);
+		keycode = KEY_BRIGHTNESSUP;
+		break;
 	case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:	/* Decrease brightness */
+		acpi_video_switch_brightness(video_device, event);
+		acpi_bus_generate_event(device, event, 0);
+		keycode = KEY_BRIGHTNESSDOWN;
+		break;
 	case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:	/* zero brightnesss */
+		acpi_video_switch_brightness(video_device, event);
+		acpi_bus_generate_event(device, event, 0);
+		keycode = KEY_BRIGHTNESS_ZERO;
+		break;
 	case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:	/* display device off */
 		acpi_video_switch_brightness(video_device, event);
 		acpi_bus_generate_event(device, event, 0);
+		keycode = KEY_DISPLAY_OFF;
 		break;
 	default:
+		keycode = KEY_UNKNOWN;
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 				  "Unsupported event [0x%x]\n", event));
 		break;
 	}
+
+	input_report_key(input, keycode, 1);
+	input_sync(input);
+	input_report_key(input, keycode, 0);
+	input_sync(input);
+
 	return;
 }
 
+static int instance;
 static int acpi_video_bus_add(struct acpi_device *device)
 {
-	int result = 0;
-	acpi_status status = 0;
-	struct acpi_video_bus *video = NULL;
-
-
-	if (!device)
-		return -EINVAL;
+	acpi_status status;
+	struct acpi_video_bus *video;
+	struct input_dev *input;
+	int error;
 
-	video = kmalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
+	video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
 	if (!video)
 		return -ENOMEM;
-	memset(video, 0, sizeof(struct acpi_video_bus));
+
+	/* a hack to fix the duplicate name "VID" problem on T61 */
+	if (!strcmp(device->pnp.bus_id, "VID")) {
+		if (instance)
+			device->pnp.bus_id[3] = '0' + instance;
+		instance ++;
+	}
 
 	video->device = device;
 	strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
@@ -1707,15 +1832,15 @@ static int acpi_video_bus_add(struct acpi_device *device)
 	acpi_driver_data(device) = video;
 
 	acpi_video_bus_find_cap(video);
-	result = acpi_video_bus_check(video);
-	if (result)
-		goto end;
+	error = acpi_video_bus_check(video);
+	if (error)
+		goto err_free_video;
 
-	result = acpi_video_bus_add_fs(device);
-	if (result)
-		goto end;
+	error = acpi_video_bus_add_fs(device);
+	if (error)
+		goto err_free_video;
 
-	init_MUTEX(&video->sem);
+	mutex_init(&video->device_list_lock);
 	INIT_LIST_HEAD(&video->video_device_list);
 
 	acpi_video_bus_get_devices(video, device);
@@ -1727,25 +1852,61 @@ static int acpi_video_bus_add(struct acpi_device *device)
 	if (ACPI_FAILURE(status)) {
 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
 				  "Error installing notify handler\n"));
-		acpi_video_bus_stop_devices(video);
-		acpi_video_bus_put_devices(video);
-		kfree(video->attached_array);
-		acpi_video_bus_remove_fs(device);
-		result = -ENODEV;
-		goto end;
+		error = -ENODEV;
+		goto err_stop_video;
+	}
+
+	video->input = input = input_allocate_device();
+	if (!input) {
+		error = -ENOMEM;
+		goto err_uninstall_notify;
 	}
 
+	snprintf(video->phys, sizeof(video->phys),
+		"%s/video/input0", acpi_device_hid(video->device));
+
+	input->name = acpi_device_name(video->device);
+	input->phys = video->phys;
+	input->id.bustype = BUS_HOST;
+	input->id.product = 0x06;
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_SWITCHVIDEOMODE, input->keybit);
+	set_bit(KEY_VIDEO_NEXT, input->keybit);
+	set_bit(KEY_VIDEO_PREV, input->keybit);
+	set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit);
+	set_bit(KEY_BRIGHTNESSUP, input->keybit);
+	set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
+	set_bit(KEY_BRIGHTNESS_ZERO, input->keybit);
+	set_bit(KEY_DISPLAY_OFF, input->keybit);
+	set_bit(KEY_UNKNOWN, input->keybit);
+
+	error = input_register_device(input);
+	if (error)
+		goto err_free_input_dev;
+
 	printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s  rom: %s  post: %s)\n",
 	       ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
 	       video->flags.multihead ? "yes" : "no",
 	       video->flags.rom ? "yes" : "no",
 	       video->flags.post ? "yes" : "no");
 
-      end:
-	if (result)
-		kfree(video);
+	return 0;
+
+ err_free_input_dev:
+	input_free_device(input);
+ err_uninstall_notify:
+	acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+				   acpi_video_bus_notify);
+ err_stop_video:
+	acpi_video_bus_stop_devices(video);
+	acpi_video_bus_put_devices(video);
+	kfree(video->attached_array);
+	acpi_video_bus_remove_fs(device);
+ err_free_video:
+	kfree(video);
+	acpi_driver_data(device) = NULL;
 
-	return result;
+	return error;
 }
 
 static int acpi_video_bus_remove(struct acpi_device *device, int type)
@@ -1757,7 +1918,7 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type)
 	if (!device || !acpi_driver_data(device))
 		return -EINVAL;
 
-	video = (struct acpi_video_bus *)acpi_driver_data(device);
+	video = acpi_driver_data(device);
 
 	acpi_video_bus_stop_devices(video);
 
@@ -1768,6 +1929,7 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type)
 	acpi_video_bus_put_devices(video);
 	acpi_video_bus_remove_fs(device);
 
+	input_unregister_device(video->input);
 	kfree(video->attached_array);
 	kfree(video);
 
diff --git a/include/linux/input.h b/include/linux/input.h
index b3253ab..c899828 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -351,6 +351,12 @@ struct input_absinfo {
 
 #define KEY_UNKNOWN		240
 
+#define KEY_VIDEO_NEXT          241     /* drive next video source */
+#define KEY_VIDEO_PREV          242     /* drive previous video source */
+#define KEY_BRIGHTNESS_CYCLE    243     /* brightness up, after max is min */
+#define KEY_BRIGHTNESS_ZERO     244     /* brightness off, use ambient */
+#define KEY_DISPLAY_OFF         245     /* display device to off state */
+
 #define BTN_MISC		0x100
 #define BTN_0			0x100
 #define BTN_1			0x101