Many Dell laptops have the DSDT coded to power down the display when the lid is closed, and leave it off when it is opened. http://bugzilla.kernel.org/show_bug.cgi?id=5155 Based on ideas from Len Brown and Dmitry Torokhov, this patch creates an input handler in the video driver which monitors for lid input events. When a lid open event is detected, the video driver reactivates the LCD. Signed-off-by: Daniel Drake <dsd@xxxxxxxxxx> Index: linux/drivers/acpi/button.c =================================================================== --- linux.orig/drivers/acpi/button.c +++ linux/drivers/acpi/button.c @@ -56,8 +56,6 @@ #define ACPI_BUTTON_SUBCLASS_LID "lid" #define ACPI_BUTTON_HID_LID "PNP0C0D" -#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" -#define ACPI_BUTTON_TYPE_LID 0x05 #define _COMPONENT ACPI_BUTTON_COMPONENT ACPI_MODULE_NAME("button"); Index: linux/drivers/acpi/video.c =================================================================== --- linux.orig/drivers/acpi/video.c +++ linux/drivers/acpi/video.c @@ -31,6 +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 <asm/uaccess.h> @@ -131,6 +132,8 @@ struct acpi_video_bus { struct semaphore sem; struct list_head video_device_list; struct proc_dir_entry *dir; + struct input_handler lid_handler; + struct input_handle *lid_handle; }; struct acpi_video_device_flags { @@ -1718,6 +1721,88 @@ static int acpi_video_bus_put_devices(st return 0; } +/* lid input monitoring */ + +static const struct input_device_id lid_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT(EV_SW) }, + .swbit = { BIT(SW_LID) }, + }, + { }, /* terminating entry */ +}; + +static int lid_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + struct acpi_video_bus *video = handler->private; + int r; + + if (dev->id.product != ACPI_BUTTON_TYPE_LID || + strcmp(dev->name, ACPI_BUTTON_DEVICE_NAME_LID) != 0) + return -ENODEV; + + if (video->lid_handle != NULL) { + printk(KERN_ERR PREFIX "trying to bind to multiple lids?\n"); + return -ENODEV; + } + + video->lid_handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!video->lid_handle) + return -ENOMEM; + video->lid_handle->dev = dev; + video->lid_handle->handler = handler; + video->lid_handle->private = video; + + r = input_register_handle(video->lid_handle); + if (r) + goto err; + + r = input_open_device(video->lid_handle); + if (r) + goto err_unregister; + + printk(KERN_INFO PREFIX "connected %s to lid switch\n", + acpi_device_bid(video->device)); + return 0; + +err_unregister: + input_unregister_handle(video->lid_handle); +err: + kfree(video->lid_handle); + return r; +} + +static void lid_disconnect(struct input_handle *handle) +{ + struct acpi_video_bus *video = handle->private; + input_unregister_handle(handle); + input_close_device(handle); + kfree(video->lid_handle); + video->lid_handle = NULL; +} + +static void lid_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + struct acpi_video_device *dev, *tmp; + struct acpi_video_bus *video = handle->private; + + if (type != EV_SW || value != 0) + return; + + list_for_each_entry_safe(dev, tmp, &video->video_device_list, entry) { + if (!dev->flags.lcd) + continue; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Waking up %s.%s on lid event\n", + acpi_device_bid(video->device), + acpi_device_bid(dev->dev))); + acpi_video_device_set_state(dev, 0x80000001); + } +} + /* acpi_video interface */ static int acpi_video_bus_start_devices(struct acpi_video_bus *video) @@ -1808,7 +1893,9 @@ static int acpi_video_bus_add(struct acp int result = 0; acpi_status status = 0; struct acpi_video_bus *video = NULL; - + char *name; + char *bid = acpi_device_bid(device); + int namelen; if (!device) return -EINVAL; @@ -1825,11 +1912,11 @@ static int acpi_video_bus_add(struct acp acpi_video_bus_find_cap(video); result = acpi_video_bus_check(video); if (result) - goto end; + goto err; result = acpi_video_bus_add_fs(device); if (result) - goto end; + goto err; init_MUTEX(&video->sem); INIT_LIST_HEAD(&video->video_device_list); @@ -1843,24 +1930,49 @@ static int acpi_video_bus_add(struct acp 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; + goto err_stop; + } + + namelen = 10 + strlen(bid); + name = kmalloc(namelen, GFP_KERNEL); + if (!name) { + result = -ENOMEM; + goto err_remove_notify; + } + snprintf(name, namelen, "ACPI-%s-LID", bid); + + video->lid_handler.event = lid_event; + video->lid_handler.connect = lid_connect; + video->lid_handler.disconnect = lid_disconnect; + video->lid_handler.name = name; + video->lid_handler.id_table = lid_ids; + video->lid_handler.private = video; + + result = input_register_handler(&video->lid_handler); + if (result) { + result = -ENODEV; + goto err_remove_notify; } printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", - ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), + ACPI_VIDEO_DEVICE_NAME, bid, video->flags.multihead ? "yes" : "no", video->flags.rom ? "yes" : "no", video->flags.post ? "yes" : "no"); - end: - if (result) - kfree(video); + return result; +err_remove_notify: + acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_video_bus_notify); +err_stop: + acpi_video_bus_stop_devices(video); + acpi_video_bus_put_devices(video); + kfree(video->attached_array); + acpi_video_bus_remove_fs(device); +err: + kfree(video); return result; } @@ -1875,6 +1987,9 @@ static int acpi_video_bus_remove(struct video = acpi_driver_data(device); + input_unregister_handler(&video->lid_handler); + kfree(video->lid_handler.name); + acpi_video_bus_stop_devices(video); status = acpi_remove_notify_handler(video->device->handle, Index: linux/include/acpi/acpi_drivers.h =================================================================== --- linux.orig/include/acpi/acpi_drivers.h +++ linux/include/acpi/acpi_drivers.h @@ -140,6 +140,14 @@ static inline void unregister_hotplug_do #endif /*-------------------------------------------------------------------------- + Button + -------------------------------------------------------------------------- */ + +/* definitions shared between button and video drivers */ +#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" +#define ACPI_BUTTON_TYPE_LID 0x05 + +/*-------------------------------------------------------------------------- Suspend/Resume -------------------------------------------------------------------------- */ #ifdef CONFIG_ACPI_SLEEP Index: linux/drivers/acpi/Kconfig =================================================================== --- linux.orig/drivers/acpi/Kconfig +++ linux/drivers/acpi/Kconfig @@ -124,13 +124,13 @@ config ACPI_BUTTON config ACPI_VIDEO tristate "Video" - depends on X86 && BACKLIGHT_CLASS_DEVICE + depends on X86 && BACKLIGHT_CLASS_DEVICE && INPUT help This driver implement the ACPI Extensions For Display Adapters for integrated graphics devices on motherboard, as specified in ACPI 2.0 Specification, Appendix B, allowing to perform some basic - control like defining the video POST device, retrieving EDID information - or to setup a video output, etc. + control like defining the video POST device, retrieving EDID + information or to setup a video output, etc. Note that this is an ref. implementation only. It may or may not work for your integrated video device. - To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html