Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux...
[pandora-kernel.git] / drivers / acpi / video.c
index 04ea697..bac956b 100644 (file)
@@ -31,7 +31,7 @@
 #include <linux/list.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-
+#include <linux/input.h>
 #include <linux/backlight.h>
 #include <linux/video_output.h>
 #include <asm/uaccess.h>
@@ -74,10 +74,16 @@ MODULE_LICENSE("GPL");
 static int acpi_video_bus_add(struct acpi_device *device);
 static int acpi_video_bus_remove(struct acpi_device *device, int type);
 
+static const struct acpi_device_id video_device_ids[] = {
+       {ACPI_VIDEO_HID, 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, video_device_ids);
+
 static struct acpi_driver acpi_video_bus = {
        .name = "video",
        .class = ACPI_VIDEO_CLASS,
-       .ids = ACPI_VIDEO_HID,
+       .ids = video_device_ids,
        .ops = {
                .add = acpi_video_bus_add,
                .remove = acpi_video_bus_remove,
@@ -132,6 +138,8 @@ struct acpi_video_bus {
        struct semaphore sem;
        struct list_head video_device_list;
        struct proc_dir_entry *dir;
+       struct input_dev *input;
+       char phys[32];  /* for input device */
 };
 
 struct acpi_video_device_flags {
@@ -308,7 +316,7 @@ static int acpi_video_output_get(struct output_device *od)
 {
        unsigned long state;
        struct acpi_video_device *vd =
-               (struct acpi_video_device *)class_get_devdata(&od->class_dev);
+               (struct acpi_video_device *)dev_get_drvdata(&od->dev);
        acpi_video_device_get_state(vd, &state);
        return (int)state;
 }
@@ -317,7 +325,7 @@ static int acpi_video_output_set(struct output_device *od)
 {
        unsigned long state = od->request_state;
        struct acpi_video_device *vd=
-               (struct acpi_video_device *)class_get_devdata(&od->class_dev);
+               (struct acpi_video_device *)dev_get_drvdata(&od->dev);
        return acpi_video_device_set_state(vd, state);
 }
 
@@ -401,15 +409,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;
 }
 
@@ -417,11 +427,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
@@ -1626,9 +1636,20 @@ static int
 acpi_video_get_next_level(struct acpi_video_device *device,
                          u32 level_current, u32 event)
 {
-       int min, max, min_above, max_below, i, l;
+       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)
@@ -1746,7 +1767,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)
@@ -1758,6 +1779,9 @@ static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
 {
        struct acpi_video_bus *video = data;
        struct acpi_device *device = NULL;
+       struct input_dev *input;
+       int keycode;
+
 
        printk("video bus notify\n");
 
@@ -1765,11 +1789,13 @@ static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
                return;
 
        device = video->device;
+       input = video->input;
 
        switch (event) {
        case ACPI_VIDEO_NOTIFY_SWITCH:  /* User requested a switch,
                                         * most likely via hotkey. */
-               acpi_bus_generate_event(device, event, 0);
+               acpi_bus_generate_proc_event(device, event, 0);
+               keycode = KEY_SWITCHVIDEOMODE;
                break;
 
        case ACPI_VIDEO_NOTIFY_PROBE:   /* User plugged in or removed a video
@@ -1777,22 +1803,38 @@ static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
                acpi_video_device_enumerate(video);
                acpi_video_device_rebind(video);
                acpi_video_switch_output(video, event);
-               acpi_bus_generate_event(device, event, 0);
+               acpi_bus_generate_proc_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_proc_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_proc_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);
+               acpi_bus_generate_proc_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;
 }
 
@@ -1800,38 +1842,65 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
 {
        struct acpi_video_device *video_device = data;
        struct acpi_device *device = NULL;
+       struct acpi_video_bus *bus;
+       struct input_dev *input;
+       int keycode;
 
        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) */
-               acpi_bus_generate_event(device, event, 0);
-               break;
        case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:        /* Cycle brightness */
+               acpi_video_switch_brightness(video_device, event);
+               acpi_bus_generate_proc_event(device, event, 0);
+               keycode = KEY_BRIGHTNESS_CYCLE;
+               break;
        case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:  /* Increase brightness */
+               acpi_video_switch_brightness(video_device, event);
+               acpi_bus_generate_proc_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_proc_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_proc_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);
+               acpi_bus_generate_proc_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;
+       struct input_dev *input;
 
 
        if (!device)
@@ -1841,6 +1910,13 @@ static int acpi_video_bus_add(struct acpi_device *device)
        if (!video)
                return -ENOMEM;
 
+       /* 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);
        strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
@@ -1875,6 +1951,39 @@ static int acpi_video_bus_add(struct acpi_device *device)
                goto end;
        }
 
+
+       video->input = input = input_allocate_device();
+
+       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);
+       result = input_register_device(input);
+       if (result) {
+               acpi_remove_notify_handler(video->device->handle,
+                                               ACPI_DEVICE_NOTIFY,
+                                               acpi_video_bus_notify);
+               acpi_video_bus_stop_devices(video);
+               acpi_video_bus_put_devices(video);
+               kfree(video->attached_array);
+               acpi_video_bus_remove_fs(device);
+               goto end;
+        }
+
+
        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",
@@ -1908,6 +2017,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);