diff options
Diffstat (limited to 'drivers/acpi/video.c')
-rw-r--r-- | drivers/acpi/video.c | 278 |
1 files changed, 177 insertions, 101 deletions
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 56666a982476..e0b97add8c63 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> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -31,6 +32,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/backlight.h> #include <asm/uaccess.h> #include <acpi/acpi_bus.h> @@ -47,14 +49,20 @@ #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") @@ -65,16 +73,14 @@ 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 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, .class = ACPI_VIDEO_CLASS, + .ids = ACPI_VIDEO_HID, .ops = { .add = acpi_video_bus_add, .remove = acpi_video_bus_remove, - .match = acpi_video_bus_match, }, }; @@ -132,20 +138,21 @@ 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 +169,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 backlight_properties *data; }; /* bus */ @@ -256,11 +265,35 @@ 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); +/*backlight device sysfs support*/ +static int acpi_video_get_brightness(struct backlight_device *bd) +{ + unsigned long cur_level; + struct acpi_video_device *vd = + (struct acpi_video_device *)class_get_devdata(&bd->class_dev); + acpi_video_device_lcd_get_level_current(vd, &cur_level); + return (int) cur_level; +} + +static int acpi_video_set_brightness(struct backlight_device *bd) +{ + int request_level = bd->props->brightness; + struct acpi_video_device *vd = + (struct acpi_video_device *)class_get_devdata(&bd->class_dev); + acpi_video_device_lcd_set_level(vd, request_level); + return 0; +} + /* -------------------------------------------------------------------------- Video Management -------------------------------------------------------------------------- */ @@ -386,7 +419,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; @@ -498,6 +531,7 @@ 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; @@ -513,6 +547,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; } @@ -532,11 +568,10 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) int count = 0; union acpi_object *o; - br = kmalloc(sizeof(*br), GFP_KERNEL); + br = kzalloc(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) @@ -550,6 +585,8 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) continue; } br->levels[count] = (u32) o->integer.value; + if (br->levels[count] > max_level) + max_level = br->levels[count]; count++; } out: @@ -568,6 +605,37 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) kfree(obj); + if (device->cap._BCL && device->cap._BCM && device->cap._BQC){ + unsigned long tmp; + static int count = 0; + char *name; + struct backlight_properties *acpi_video_data; + + name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); + if (!name) + return; + + acpi_video_data = kzalloc( + sizeof(struct backlight_properties), + GFP_KERNEL); + if (!acpi_video_data){ + kfree(name); + return; + } + acpi_video_data->owner = THIS_MODULE; + acpi_video_data->get_brightness = + acpi_video_get_brightness; + acpi_video_data->update_status = + acpi_video_set_brightness; + sprintf(name, "acpi_video%d", count++); + device->data = acpi_video_data; + acpi_video_data->max_brightness = max_level; + acpi_video_device_lcd_get_level_current(device, &tmp); + acpi_video_data->brightness = (int)tmp; + device->backlight = backlight_device_register(name, + NULL, device, acpi_video_data); + kfree(name); + } return; } @@ -654,8 +722,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 +736,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 +757,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 +795,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 +822,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,8 +851,8 @@ 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; + struct seq_file *m = file->private_data; + struct acpi_video_device *dev = m->private; char str[4] = { 0 }; unsigned int level = 0; int i; @@ -817,8 +884,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 +932,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 +997,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 +1016,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 +1041,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 +1061,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; @@ -1033,7 +1099,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; @@ -1054,7 +1120,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 +1145,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; @@ -1119,8 +1185,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 +1216,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 +1292,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 +1312,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 +1330,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 +1339,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 +1351,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); @@ -1403,7 +1487,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 +1510,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"); @@ -1509,8 +1593,34 @@ 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; + max = max_below = 0; + min = min_above = 255; + 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 @@ -1568,7 +1678,10 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) status = acpi_remove_notify_handler(device->dev->handle, ACPI_DEVICE_NOTIFY, acpi_video_device_notify); - + if (device->backlight){ + backlight_device_unregister(device->backlight); + kfree(device->data); + } return 0; } @@ -1612,7 +1725,7 @@ 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"); @@ -1654,12 +1767,9 @@ static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data) 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; - - printk("video device notify\n"); if (!video_device) return; @@ -1696,10 +1806,9 @@ static int acpi_video_bus_add(struct acpi_device *device) if (!device) return -EINVAL; - 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)); video->device = device; strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); @@ -1757,7 +1866,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); @@ -1774,39 +1883,6 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type) return 0; } -static int -acpi_video_bus_match(struct acpi_device *device, struct acpi_driver *driver) -{ - acpi_handle h_dummy1; - acpi_handle h_dummy2; - acpi_handle h_dummy3; - - - if (!device || !driver) - return -EINVAL; - - /* Since there is no HID, CID for ACPI Video drivers, we have - * to check well known required nodes for each feature we support. - */ - - /* Does this device able to support video switching ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy1)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy2))) - return 0; - - /* Does this device able to retrieve a video ROM ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy1))) - return 0; - - /* Does this device able to configure which video head to be POSTed ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy1)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy2)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy3))) - return 0; - - return -ENODEV; -} - static int __init acpi_video_init(void) { int result = 0; |