blob: 66a610d0e2f5f14705cd88dbb305efd8e1786fa5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * video.c - ACPI Video Driver ($Revision:$)
3 *
4 * Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
5 * Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org>
Thomas Tuttlef4715182006-12-19 12:56:14 -08006 * Copyright (C) 2006 Thomas Tuttle <linux-kernel@ttuttle.net>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or (at
13 * your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
23 *
24 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 */
26
27#include <linux/kernel.h>
28#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/types.h>
31#include <linux/list.h>
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -050032#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/proc_fs.h>
34#include <linux/seq_file.h>
Luming Yue9dab192007-08-20 18:23:53 +080035#include <linux/input.h>
Yu Luming2f3d0002006-11-11 02:40:34 +080036#include <linux/backlight.h>
Zhang Rui702ed512008-01-17 15:51:22 +080037#include <linux/thermal.h>
Luming Yu23b0f012007-05-09 21:07:05 +080038#include <linux/video_output.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <asm/uaccess.h>
40
41#include <acpi/acpi_bus.h>
42#include <acpi/acpi_drivers.h>
43
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#define ACPI_VIDEO_CLASS "video"
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#define ACPI_VIDEO_BUS_NAME "Video Bus"
46#define ACPI_VIDEO_DEVICE_NAME "Video Device"
47#define ACPI_VIDEO_NOTIFY_SWITCH 0x80
48#define ACPI_VIDEO_NOTIFY_PROBE 0x81
49#define ACPI_VIDEO_NOTIFY_CYCLE 0x82
50#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT 0x83
51#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT 0x84
52
Thomas Tuttlef4715182006-12-19 12:56:14 -080053#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS 0x85
54#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
55#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
56#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS 0x88
57#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF 0x89
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
Yu Luming2f3d0002006-11-11 02:40:34 +080059#define MAX_NAME_LEN 20
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
Rui Zhang82cae992007-01-03 23:40:53 -050061#define ACPI_VIDEO_DISPLAY_CRT 1
62#define ACPI_VIDEO_DISPLAY_TV 2
63#define ACPI_VIDEO_DISPLAY_DVI 3
64#define ACPI_VIDEO_DISPLAY_LCD 4
65
Linus Torvalds1da177e2005-04-16 15:20:36 -070066#define _COMPONENT ACPI_VIDEO_COMPONENT
Len Brownf52fd662007-02-12 22:42:12 -050067ACPI_MODULE_NAME("video");
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Len Brownf52fd662007-02-12 22:42:12 -050069MODULE_AUTHOR("Bruno Ducrot");
Len Brown7cda93e2007-02-12 23:50:02 -050070MODULE_DESCRIPTION("ACPI Video Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070071MODULE_LICENSE("GPL");
72
Zhang Rui8a681a42008-01-25 14:47:49 +080073static int brightness_switch_enabled = 1;
74module_param(brightness_switch_enabled, bool, 0644);
75
Len Brown4be44fc2005-08-05 00:44:28 -040076static int acpi_video_bus_add(struct acpi_device *device);
77static int acpi_video_bus_remove(struct acpi_device *device, int type);
Matthew Garrett863c1492008-02-04 23:31:24 -080078static int acpi_video_resume(struct acpi_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
Thomas Renninger1ba90e32007-07-23 14:44:41 +020080static const struct acpi_device_id video_device_ids[] = {
81 {ACPI_VIDEO_HID, 0},
82 {"", 0},
83};
84MODULE_DEVICE_TABLE(acpi, video_device_ids);
85
Linus Torvalds1da177e2005-04-16 15:20:36 -070086static struct acpi_driver acpi_video_bus = {
Len Brownc2b6705b2007-02-12 23:33:40 -050087 .name = "video",
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 .class = ACPI_VIDEO_CLASS,
Thomas Renninger1ba90e32007-07-23 14:44:41 +020089 .ids = video_device_ids,
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 .ops = {
91 .add = acpi_video_bus_add,
92 .remove = acpi_video_bus_remove,
Matthew Garrett863c1492008-02-04 23:31:24 -080093 .resume = acpi_video_resume,
Len Brown4be44fc2005-08-05 00:44:28 -040094 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070095};
96
97struct acpi_video_bus_flags {
Len Brown4be44fc2005-08-05 00:44:28 -040098 u8 multihead:1; /* can switch video heads */
99 u8 rom:1; /* can retrieve a video rom */
100 u8 post:1; /* can configure the head to */
101 u8 reserved:5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102};
103
104struct acpi_video_bus_cap {
Len Brown4be44fc2005-08-05 00:44:28 -0400105 u8 _DOS:1; /*Enable/Disable output switching */
106 u8 _DOD:1; /*Enumerate all devices attached to display adapter */
107 u8 _ROM:1; /*Get ROM Data */
108 u8 _GPD:1; /*Get POST Device */
109 u8 _SPD:1; /*Set POST Device */
110 u8 _VPO:1; /*Video POST Options */
111 u8 reserved:2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112};
113
Len Brown4be44fc2005-08-05 00:44:28 -0400114struct acpi_video_device_attrib {
115 u32 display_index:4; /* A zero-based instance of the Display */
Julius Volz98fb8fe2007-02-20 16:38:40 +0100116 u32 display_port_attachment:4; /*This field differentiates the display type */
Len Brown4be44fc2005-08-05 00:44:28 -0400117 u32 display_type:4; /*Describe the specific type in use */
Julius Volz98fb8fe2007-02-20 16:38:40 +0100118 u32 vendor_specific:4; /*Chipset Vendor Specific */
Len Brown4be44fc2005-08-05 00:44:28 -0400119 u32 bios_can_detect:1; /*BIOS can detect the device */
120 u32 depend_on_vga:1; /*Non-VGA output device whose power is related to
121 the VGA device. */
122 u32 pipe_id:3; /*For VGA multiple-head devices. */
123 u32 reserved:10; /*Must be 0 */
124 u32 device_id_scheme:1; /*Device ID Scheme */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125};
126
127struct acpi_video_enumerated_device {
128 union {
129 u32 int_val;
Len Brown4be44fc2005-08-05 00:44:28 -0400130 struct acpi_video_device_attrib attrib;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 } value;
132 struct acpi_video_device *bind_info;
133};
134
135struct acpi_video_bus {
Patrick Mochele6afa0d2006-05-19 16:54:40 -0400136 struct acpi_device *device;
Len Brown4be44fc2005-08-05 00:44:28 -0400137 u8 dos_setting;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 struct acpi_video_enumerated_device *attached_array;
Len Brown4be44fc2005-08-05 00:44:28 -0400139 u8 attached_count;
140 struct acpi_video_bus_cap cap;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 struct acpi_video_bus_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400142 struct list_head video_device_list;
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -0500143 struct mutex device_list_lock; /* protects video_device_list */
Len Brown4be44fc2005-08-05 00:44:28 -0400144 struct proc_dir_entry *dir;
Luming Yue9dab192007-08-20 18:23:53 +0800145 struct input_dev *input;
146 char phys[32]; /* for input device */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147};
148
149struct acpi_video_device_flags {
Len Brown4be44fc2005-08-05 00:44:28 -0400150 u8 crt:1;
151 u8 lcd:1;
152 u8 tvout:1;
Rui Zhang82cae992007-01-03 23:40:53 -0500153 u8 dvi:1;
Len Brown4be44fc2005-08-05 00:44:28 -0400154 u8 bios:1;
155 u8 unknown:1;
Rui Zhang82cae992007-01-03 23:40:53 -0500156 u8 reserved:2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157};
158
159struct acpi_video_device_cap {
Len Brown4be44fc2005-08-05 00:44:28 -0400160 u8 _ADR:1; /*Return the unique ID */
161 u8 _BCL:1; /*Query list of brightness control levels supported */
162 u8 _BCM:1; /*Set the brightness level */
Yu Luming2f3d0002006-11-11 02:40:34 +0800163 u8 _BQC:1; /* Get current brightness level */
Len Brown4be44fc2005-08-05 00:44:28 -0400164 u8 _DDC:1; /*Return the EDID for this device */
165 u8 _DCS:1; /*Return status of output device */
166 u8 _DGS:1; /*Query graphics state */
167 u8 _DSS:1; /*Device state set */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168};
169
170struct acpi_video_device_brightness {
Len Brown4be44fc2005-08-05 00:44:28 -0400171 int curr;
172 int count;
173 int *levels;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174};
175
176struct acpi_video_device {
Len Brown4be44fc2005-08-05 00:44:28 -0400177 unsigned long device_id;
178 struct acpi_video_device_flags flags;
179 struct acpi_video_device_cap cap;
180 struct list_head entry;
181 struct acpi_video_bus *video;
182 struct acpi_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 struct acpi_video_device_brightness *brightness;
Yu Luming2f3d0002006-11-11 02:40:34 +0800184 struct backlight_device *backlight;
Zhang Rui702ed512008-01-17 15:51:22 +0800185 struct thermal_cooling_device *cdev;
Luming Yu23b0f012007-05-09 21:07:05 +0800186 struct output_device *output_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187};
188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189/* bus */
190static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file);
191static struct file_operations acpi_video_bus_info_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700192 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400193 .open = acpi_video_bus_info_open_fs,
194 .read = seq_read,
195 .llseek = seq_lseek,
196 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197};
198
199static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file);
200static struct file_operations acpi_video_bus_ROM_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700201 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400202 .open = acpi_video_bus_ROM_open_fs,
203 .read = seq_read,
204 .llseek = seq_lseek,
205 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206};
207
Len Brown4be44fc2005-08-05 00:44:28 -0400208static int acpi_video_bus_POST_info_open_fs(struct inode *inode,
209 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210static struct file_operations acpi_video_bus_POST_info_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700211 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400212 .open = acpi_video_bus_POST_info_open_fs,
213 .read = seq_read,
214 .llseek = seq_lseek,
215 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216};
217
218static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file);
219static struct file_operations acpi_video_bus_POST_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700220 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400221 .open = acpi_video_bus_POST_open_fs,
222 .read = seq_read,
223 .llseek = seq_lseek,
224 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225};
226
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file);
228static struct file_operations acpi_video_bus_DOS_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700229 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400230 .open = acpi_video_bus_DOS_open_fs,
231 .read = seq_read,
232 .llseek = seq_lseek,
233 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234};
235
236/* device */
Len Brown4be44fc2005-08-05 00:44:28 -0400237static int acpi_video_device_info_open_fs(struct inode *inode,
238 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239static struct file_operations acpi_video_device_info_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700240 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400241 .open = acpi_video_device_info_open_fs,
242 .read = seq_read,
243 .llseek = seq_lseek,
244 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245};
246
Len Brown4be44fc2005-08-05 00:44:28 -0400247static int acpi_video_device_state_open_fs(struct inode *inode,
248 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249static struct file_operations acpi_video_device_state_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700250 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400251 .open = acpi_video_device_state_open_fs,
252 .read = seq_read,
253 .llseek = seq_lseek,
254 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255};
256
Len Brown4be44fc2005-08-05 00:44:28 -0400257static int acpi_video_device_brightness_open_fs(struct inode *inode,
258 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259static struct file_operations acpi_video_device_brightness_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700260 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400261 .open = acpi_video_device_brightness_open_fs,
262 .read = seq_read,
263 .llseek = seq_lseek,
264 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265};
266
Len Brown4be44fc2005-08-05 00:44:28 -0400267static int acpi_video_device_EDID_open_fs(struct inode *inode,
268 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269static struct file_operations acpi_video_device_EDID_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700270 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400271 .open = acpi_video_device_EDID_open_fs,
272 .read = seq_read,
273 .llseek = seq_lseek,
274 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275};
276
Len Brown4be44fc2005-08-05 00:44:28 -0400277static char device_decode[][30] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 "motherboard VGA device",
279 "PCI VGA device",
280 "AGP VGA device",
281 "UNKNOWN",
282};
283
Len Brown4be44fc2005-08-05 00:44:28 -0400284static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data);
285static void acpi_video_device_rebind(struct acpi_video_bus *video);
286static void acpi_video_device_bind(struct acpi_video_bus *video,
287 struct acpi_video_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288static int acpi_video_device_enumerate(struct acpi_video_bus *video);
Yu Luming2f3d0002006-11-11 02:40:34 +0800289static int acpi_video_device_lcd_set_level(struct acpi_video_device *device,
290 int level);
291static int acpi_video_device_lcd_get_level_current(
292 struct acpi_video_device *device,
Matthew Wilcox27663c52008-10-10 02:22:59 -0400293 unsigned long long *level);
Len Brown4be44fc2005-08-05 00:44:28 -0400294static int acpi_video_get_next_level(struct acpi_video_device *device,
295 u32 level_current, u32 event);
296static void acpi_video_switch_brightness(struct acpi_video_device *device,
297 int event);
Luming Yu23b0f012007-05-09 21:07:05 +0800298static int acpi_video_device_get_state(struct acpi_video_device *device,
Matthew Wilcox27663c52008-10-10 02:22:59 -0400299 unsigned long long *state);
Luming Yu23b0f012007-05-09 21:07:05 +0800300static int acpi_video_output_get(struct output_device *od);
301static int acpi_video_device_set_state(struct acpi_video_device *device, int state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302
Yu Luming2f3d0002006-11-11 02:40:34 +0800303/*backlight device sysfs support*/
304static int acpi_video_get_brightness(struct backlight_device *bd)
305{
Matthew Wilcox27663c52008-10-10 02:22:59 -0400306 unsigned long long cur_level;
Matthew Garrett38531e62007-12-26 02:03:26 +0000307 int i;
Yu Luming2f3d0002006-11-11 02:40:34 +0800308 struct acpi_video_device *vd =
Richard Purdie655bfd72007-07-09 12:17:24 +0100309 (struct acpi_video_device *)bl_get_data(bd);
Yu Luming2f3d0002006-11-11 02:40:34 +0800310 acpi_video_device_lcd_get_level_current(vd, &cur_level);
Matthew Garrett38531e62007-12-26 02:03:26 +0000311 for (i = 2; i < vd->brightness->count; i++) {
312 if (vd->brightness->levels[i] == cur_level)
313 /* The first two entries are special - see page 575
314 of the ACPI spec 3.0 */
315 return i-2;
316 }
317 return 0;
Yu Luming2f3d0002006-11-11 02:40:34 +0800318}
319
320static int acpi_video_set_brightness(struct backlight_device *bd)
321{
Matthew Garrett38531e62007-12-26 02:03:26 +0000322 int request_level = bd->props.brightness+2;
Yu Luming2f3d0002006-11-11 02:40:34 +0800323 struct acpi_video_device *vd =
Richard Purdie655bfd72007-07-09 12:17:24 +0100324 (struct acpi_video_device *)bl_get_data(bd);
Matthew Garrett38531e62007-12-26 02:03:26 +0000325 acpi_video_device_lcd_set_level(vd,
326 vd->brightness->levels[request_level]);
Yu Luming2f3d0002006-11-11 02:40:34 +0800327 return 0;
328}
329
Richard Purdie599a52d2007-02-10 23:07:48 +0000330static struct backlight_ops acpi_backlight_ops = {
331 .get_brightness = acpi_video_get_brightness,
332 .update_status = acpi_video_set_brightness,
333};
334
Luming Yu23b0f012007-05-09 21:07:05 +0800335/*video output device sysfs support*/
336static int acpi_video_output_get(struct output_device *od)
337{
Matthew Wilcox27663c52008-10-10 02:22:59 -0400338 unsigned long long state;
Luming Yu23b0f012007-05-09 21:07:05 +0800339 struct acpi_video_device *vd =
tonyj@suse.de60043422007-08-07 22:28:47 -0700340 (struct acpi_video_device *)dev_get_drvdata(&od->dev);
Luming Yu23b0f012007-05-09 21:07:05 +0800341 acpi_video_device_get_state(vd, &state);
342 return (int)state;
343}
344
345static int acpi_video_output_set(struct output_device *od)
346{
347 unsigned long state = od->request_state;
348 struct acpi_video_device *vd=
tonyj@suse.de60043422007-08-07 22:28:47 -0700349 (struct acpi_video_device *)dev_get_drvdata(&od->dev);
Luming Yu23b0f012007-05-09 21:07:05 +0800350 return acpi_video_device_set_state(vd, state);
351}
352
353static struct output_properties acpi_output_properties = {
354 .set_state = acpi_video_output_set,
355 .get_status = acpi_video_output_get,
356};
Zhang Rui702ed512008-01-17 15:51:22 +0800357
358
359/* thermal cooling device callbacks */
360static int video_get_max_state(struct thermal_cooling_device *cdev, char *buf)
361{
362 struct acpi_device *device = cdev->devdata;
363 struct acpi_video_device *video = acpi_driver_data(device);
364
365 return sprintf(buf, "%d\n", video->brightness->count - 3);
366}
367
368static int video_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
369{
370 struct acpi_device *device = cdev->devdata;
371 struct acpi_video_device *video = acpi_driver_data(device);
Matthew Wilcox27663c52008-10-10 02:22:59 -0400372 unsigned long long level;
Zhang Rui702ed512008-01-17 15:51:22 +0800373 int state;
374
375 acpi_video_device_lcd_get_level_current(video, &level);
376 for (state = 2; state < video->brightness->count; state++)
377 if (level == video->brightness->levels[state])
378 return sprintf(buf, "%d\n",
379 video->brightness->count - state - 1);
380
381 return -EINVAL;
382}
383
384static int
385video_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
386{
387 struct acpi_device *device = cdev->devdata;
388 struct acpi_video_device *video = acpi_driver_data(device);
389 int level;
390
391 if ( state >= video->brightness->count - 2)
392 return -EINVAL;
393
394 state = video->brightness->count - state;
395 level = video->brightness->levels[state -1];
396 return acpi_video_device_lcd_set_level(video, level);
397}
398
399static struct thermal_cooling_device_ops video_cooling_ops = {
400 .get_max_state = video_get_max_state,
401 .get_cur_state = video_get_cur_state,
402 .set_cur_state = video_set_cur_state,
403};
404
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405/* --------------------------------------------------------------------------
406 Video Management
407 -------------------------------------------------------------------------- */
408
409/* device */
410
411static int
Matthew Wilcox27663c52008-10-10 02:22:59 -0400412acpi_video_device_query(struct acpi_video_device *device, unsigned long long *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413{
Len Brown4be44fc2005-08-05 00:44:28 -0400414 int status;
Patrick Mochel90130262006-05-19 16:54:48 -0400415
416 status = acpi_evaluate_integer(device->dev->handle, "_DGS", NULL, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
Patrick Mocheld550d982006-06-27 00:41:40 -0400418 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419}
420
421static int
Len Brown4be44fc2005-08-05 00:44:28 -0400422acpi_video_device_get_state(struct acpi_video_device *device,
Matthew Wilcox27663c52008-10-10 02:22:59 -0400423 unsigned long long *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424{
Len Brown4be44fc2005-08-05 00:44:28 -0400425 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
Patrick Mochel90130262006-05-19 16:54:48 -0400427 status = acpi_evaluate_integer(device->dev->handle, "_DCS", NULL, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428
Patrick Mocheld550d982006-06-27 00:41:40 -0400429 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430}
431
432static int
Len Brown4be44fc2005-08-05 00:44:28 -0400433acpi_video_device_set_state(struct acpi_video_device *device, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
Len Brown4be44fc2005-08-05 00:44:28 -0400435 int status;
436 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
437 struct acpi_object_list args = { 1, &arg0 };
Matthew Wilcox27663c52008-10-10 02:22:59 -0400438 unsigned long long ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440
441 arg0.integer.value = state;
Patrick Mochel90130262006-05-19 16:54:48 -0400442 status = acpi_evaluate_integer(device->dev->handle, "_DSS", &args, &ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443
Patrick Mocheld550d982006-06-27 00:41:40 -0400444 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445}
446
447static int
Len Brown4be44fc2005-08-05 00:44:28 -0400448acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
449 union acpi_object **levels)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450{
Len Brown4be44fc2005-08-05 00:44:28 -0400451 int status;
452 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
453 union acpi_object *obj;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
456 *levels = NULL;
457
Patrick Mochel90130262006-05-19 16:54:48 -0400458 status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400460 return status;
Len Brown4be44fc2005-08-05 00:44:28 -0400461 obj = (union acpi_object *)buffer.pointer;
Adrian Bunk6665bda2006-03-11 10:12:00 -0500462 if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
Len Brown64684632006-06-26 23:41:38 -0400463 printk(KERN_ERR PREFIX "Invalid _BCL data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 status = -EFAULT;
465 goto err;
466 }
467
468 *levels = obj;
469
Patrick Mocheld550d982006-06-27 00:41:40 -0400470 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471
Len Brown4be44fc2005-08-05 00:44:28 -0400472 err:
Jesper Juhl6044ec82005-11-07 01:01:32 -0800473 kfree(buffer.pointer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474
Patrick Mocheld550d982006-06-27 00:41:40 -0400475 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476}
477
478static int
Len Brown4be44fc2005-08-05 00:44:28 -0400479acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480{
Alexey Starikovskiy4500ca82007-09-03 16:29:58 +0400481 int status = AE_OK;
Len Brown4be44fc2005-08-05 00:44:28 -0400482 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
483 struct acpi_object_list args = { 1, &arg0 };
Zhang Rui9e6dada2008-12-31 10:58:48 +0800484 int state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
487 arg0.integer.value = level;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488
Alexey Starikovskiy4500ca82007-09-03 16:29:58 +0400489 if (device->cap._BCM)
490 status = acpi_evaluate_object(device->dev->handle, "_BCM",
491 &args, NULL);
492 device->brightness->curr = level;
Zhang Rui9e6dada2008-12-31 10:58:48 +0800493 for (state = 2; state < device->brightness->count; state++)
494 if (level == device->brightness->levels[state])
495 device->backlight->props.brightness = state - 2;
496
Patrick Mocheld550d982006-06-27 00:41:40 -0400497 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498}
499
500static int
Len Brown4be44fc2005-08-05 00:44:28 -0400501acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
Matthew Wilcox27663c52008-10-10 02:22:59 -0400502 unsigned long long *level)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503{
Alexey Starikovskiy4500ca82007-09-03 16:29:58 +0400504 if (device->cap._BQC)
505 return acpi_evaluate_integer(device->dev->handle, "_BQC", NULL,
506 level);
507 *level = device->brightness->curr;
508 return AE_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509}
510
511static int
Len Brown4be44fc2005-08-05 00:44:28 -0400512acpi_video_device_EDID(struct acpi_video_device *device,
513 union acpi_object **edid, ssize_t length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514{
Len Brown4be44fc2005-08-05 00:44:28 -0400515 int status;
516 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
517 union acpi_object *obj;
518 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
519 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
522 *edid = NULL;
523
524 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400525 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 if (length == 128)
527 arg0.integer.value = 1;
528 else if (length == 256)
529 arg0.integer.value = 2;
530 else
Patrick Mocheld550d982006-06-27 00:41:40 -0400531 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532
Patrick Mochel90130262006-05-19 16:54:48 -0400533 status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400535 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200537 obj = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
539 if (obj && obj->type == ACPI_TYPE_BUFFER)
540 *edid = obj;
541 else {
Len Brown64684632006-06-26 23:41:38 -0400542 printk(KERN_ERR PREFIX "Invalid _DDC data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 status = -EFAULT;
544 kfree(obj);
545 }
546
Patrick Mocheld550d982006-06-27 00:41:40 -0400547 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548}
549
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550/* bus */
551
552static int
Len Brown4be44fc2005-08-05 00:44:28 -0400553acpi_video_bus_set_POST(struct acpi_video_bus *video, unsigned long option)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554{
Len Brown4be44fc2005-08-05 00:44:28 -0400555 int status;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400556 unsigned long long tmp;
Len Brown4be44fc2005-08-05 00:44:28 -0400557 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
558 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
561 arg0.integer.value = option;
562
Patrick Mochel90130262006-05-19 16:54:48 -0400563 status = acpi_evaluate_integer(video->device->handle, "_SPD", &args, &tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 if (ACPI_SUCCESS(status))
Len Brown4be44fc2005-08-05 00:44:28 -0400565 status = tmp ? (-EINVAL) : (AE_OK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
Patrick Mocheld550d982006-06-27 00:41:40 -0400567 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568}
569
570static int
Matthew Wilcox27663c52008-10-10 02:22:59 -0400571acpi_video_bus_get_POST(struct acpi_video_bus *video, unsigned long long *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572{
573 int status;
574
Patrick Mochel90130262006-05-19 16:54:48 -0400575 status = acpi_evaluate_integer(video->device->handle, "_GPD", NULL, id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Patrick Mocheld550d982006-06-27 00:41:40 -0400577 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578}
579
580static int
Len Brown4be44fc2005-08-05 00:44:28 -0400581acpi_video_bus_POST_options(struct acpi_video_bus *video,
Matthew Wilcox27663c52008-10-10 02:22:59 -0400582 unsigned long long *options)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583{
Len Brown4be44fc2005-08-05 00:44:28 -0400584 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
Patrick Mochel90130262006-05-19 16:54:48 -0400586 status = acpi_evaluate_integer(video->device->handle, "_VPO", NULL, options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 *options &= 3;
588
Patrick Mocheld550d982006-06-27 00:41:40 -0400589 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590}
591
592/*
593 * Arg:
594 * video : video bus device pointer
595 * bios_flag :
596 * 0. The system BIOS should NOT automatically switch(toggle)
597 * the active display output.
598 * 1. The system BIOS should automatically switch (toggle) the
Julius Volz98fb8fe2007-02-20 16:38:40 +0100599 * active display output. No switch event.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 * 2. The _DGS value should be locked.
601 * 3. The system BIOS should not automatically switch (toggle) the
602 * active display output, but instead generate the display switch
603 * event notify code.
604 * lcd_flag :
605 * 0. The system BIOS should automatically control the brightness level
Julius Volz98fb8fe2007-02-20 16:38:40 +0100606 * of the LCD when the power changes from AC to DC
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 * 1. The system BIOS should NOT automatically control the brightness
Julius Volz98fb8fe2007-02-20 16:38:40 +0100608 * level of the LCD when the power changes from AC to DC.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 * Return Value:
610 * -1 wrong arg.
611 */
612
613static int
Len Brown4be44fc2005-08-05 00:44:28 -0400614acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615{
Len Brown4be44fc2005-08-05 00:44:28 -0400616 acpi_integer status = 0;
617 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
618 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620
Len Brown4be44fc2005-08-05 00:44:28 -0400621 if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 status = -1;
623 goto Failed;
624 }
625 arg0.integer.value = (lcd_flag << 2) | bios_flag;
626 video->dos_setting = arg0.integer.value;
Patrick Mochel90130262006-05-19 16:54:48 -0400627 acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
Len Brown4be44fc2005-08-05 00:44:28 -0400629 Failed:
Patrick Mocheld550d982006-06-27 00:41:40 -0400630 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631}
632
633/*
634 * Arg:
635 * device : video output device (LCD, CRT, ..)
636 *
637 * Return Value:
Julia Jomantaite469778c2008-06-23 22:50:42 +0100638 * Maximum brightness level
639 *
640 * Allocate and initialize device->brightness.
641 */
642
643static int
644acpi_video_init_brightness(struct acpi_video_device *device)
645{
646 union acpi_object *obj = NULL;
647 int i, max_level = 0, count = 0;
648 union acpi_object *o;
649 struct acpi_video_device_brightness *br = NULL;
650
651 if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) {
652 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available "
653 "LCD brightness level\n"));
654 goto out;
655 }
656
657 if (obj->package.count < 2)
658 goto out;
659
660 br = kzalloc(sizeof(*br), GFP_KERNEL);
661 if (!br) {
662 printk(KERN_ERR "can't allocate memory\n");
663 goto out;
664 }
665
666 br->levels = kmalloc(obj->package.count * sizeof *(br->levels),
667 GFP_KERNEL);
668 if (!br->levels)
669 goto out_free;
670
671 for (i = 0; i < obj->package.count; i++) {
672 o = (union acpi_object *)&obj->package.elements[i];
673 if (o->type != ACPI_TYPE_INTEGER) {
674 printk(KERN_ERR PREFIX "Invalid data\n");
675 continue;
676 }
677 br->levels[count] = (u32) o->integer.value;
678
679 if (br->levels[count] > max_level)
680 max_level = br->levels[count];
681 count++;
682 }
683
684 if (count < 2)
685 goto out_free_levels;
686
687 br->count = count;
688 device->brightness = br;
689 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "found %d brightness levels\n", count));
690 kfree(obj);
691 return max_level;
692
693out_free_levels:
694 kfree(br->levels);
695out_free:
696 kfree(br);
697out:
698 device->brightness = NULL;
699 kfree(obj);
700 return 0;
701}
702
703/*
704 * Arg:
705 * device : video output device (LCD, CRT, ..)
706 *
707 * Return Value:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 * None
709 *
Julius Volz98fb8fe2007-02-20 16:38:40 +0100710 * Find out all required AML methods defined under the output
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 * device.
712 */
713
Len Brown4be44fc2005-08-05 00:44:28 -0400714static void acpi_video_device_find_cap(struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 acpi_handle h_dummy1;
Yu Luming2f3d0002006-11-11 02:40:34 +0800717 u32 max_level = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719
William Lee Irwin III98934de2007-12-12 03:56:55 -0800720 memset(&device->cap, 0, sizeof(device->cap));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
Patrick Mochel90130262006-05-19 16:54:48 -0400722 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 device->cap._ADR = 1;
724 }
Patrick Mochel90130262006-05-19 16:54:48 -0400725 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400726 device->cap._BCL = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 }
Patrick Mochel90130262006-05-19 16:54:48 -0400728 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400729 device->cap._BCM = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 }
Yu Luming2f3d0002006-11-11 02:40:34 +0800731 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
732 device->cap._BQC = 1;
Patrick Mochel90130262006-05-19 16:54:48 -0400733 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400734 device->cap._DDC = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 }
Patrick Mochel90130262006-05-19 16:54:48 -0400736 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DCS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 device->cap._DCS = 1;
738 }
Patrick Mochel90130262006-05-19 16:54:48 -0400739 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DGS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 device->cap._DGS = 1;
741 }
Patrick Mochel90130262006-05-19 16:54:48 -0400742 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DSS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 device->cap._DSS = 1;
744 }
745
Thomas Renningerc3d6de62008-08-01 17:37:55 +0200746 if (acpi_video_backlight_support())
747 max_level = acpi_video_init_brightness(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
Zhao Yakuic2c78902008-07-17 10:46:05 +0800749 if (device->cap._BCL && device->cap._BCM && max_level > 0) {
Zhang Rui702ed512008-01-17 15:51:22 +0800750 int result;
Yu Luming2f3d0002006-11-11 02:40:34 +0800751 static int count = 0;
752 char *name;
Yu Luming2f3d0002006-11-11 02:40:34 +0800753 name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
754 if (!name)
755 return;
756
Yu Luming2f3d0002006-11-11 02:40:34 +0800757 sprintf(name, "acpi_video%d", count++);
Yu Luming2f3d0002006-11-11 02:40:34 +0800758 device->backlight = backlight_device_register(name,
Richard Purdie599a52d2007-02-10 23:07:48 +0000759 NULL, device, &acpi_backlight_ops);
Matthew Garrett38531e62007-12-26 02:03:26 +0000760 device->backlight->props.max_brightness = device->brightness->count-3;
Zhao Yakuic2c78902008-07-17 10:46:05 +0800761 /*
762 * If there exists the _BQC object, the _BQC object will be
763 * called to get the current backlight brightness. Otherwise
764 * the brightness will be set to the maximum.
765 */
766 if (device->cap._BQC)
767 device->backlight->props.brightness =
768 acpi_video_get_brightness(device->backlight);
769 else
770 device->backlight->props.brightness =
771 device->backlight->props.max_brightness;
Richard Purdie599a52d2007-02-10 23:07:48 +0000772 backlight_update_status(device->backlight);
Yu Luming2f3d0002006-11-11 02:40:34 +0800773 kfree(name);
Zhang Rui702ed512008-01-17 15:51:22 +0800774
775 device->cdev = thermal_cooling_device_register("LCD",
776 device->dev, &video_cooling_ops);
Thomas Sujith43ff39f2008-02-15 18:29:18 -0500777 if (IS_ERR(device->cdev))
778 return;
779
Greg Kroah-Hartmanfc3a8822008-05-02 06:02:41 +0200780 dev_info(&device->dev->dev, "registered as cooling_device%d\n",
781 device->cdev->id);
Julia Lawall90300622008-04-11 10:09:24 +0800782 result = sysfs_create_link(&device->dev->dev.kobj,
783 &device->cdev->device.kobj,
784 "thermal_cooling");
785 if (result)
786 printk(KERN_ERR PREFIX "Create sysfs link\n");
787 result = sysfs_create_link(&device->cdev->device.kobj,
788 &device->dev->dev.kobj, "device");
789 if (result)
790 printk(KERN_ERR PREFIX "Create sysfs link\n");
791
Yu Luming2f3d0002006-11-11 02:40:34 +0800792 }
Thomas Renningerc3d6de62008-08-01 17:37:55 +0200793
794 if (acpi_video_display_switch_support()) {
795
796 if (device->cap._DCS && device->cap._DSS) {
797 static int count;
798 char *name;
799 name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
800 if (!name)
801 return;
802 sprintf(name, "acpi_video%d", count++);
803 device->output_dev = video_output_register(name,
804 NULL, device, &acpi_output_properties);
805 kfree(name);
806 }
Luming Yu23b0f012007-05-09 21:07:05 +0800807 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808}
809
810/*
811 * Arg:
812 * device : video output device (VGA)
813 *
814 * Return Value:
815 * None
816 *
Julius Volz98fb8fe2007-02-20 16:38:40 +0100817 * Find out all required AML methods defined under the video bus device.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 */
819
Len Brown4be44fc2005-08-05 00:44:28 -0400820static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821{
Len Brown4be44fc2005-08-05 00:44:28 -0400822 acpi_handle h_dummy1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
William Lee Irwin III98934de2007-12-12 03:56:55 -0800824 memset(&video->cap, 0, sizeof(video->cap));
Patrick Mochel90130262006-05-19 16:54:48 -0400825 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 video->cap._DOS = 1;
827 }
Patrick Mochel90130262006-05-19 16:54:48 -0400828 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 video->cap._DOD = 1;
830 }
Patrick Mochel90130262006-05-19 16:54:48 -0400831 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 video->cap._ROM = 1;
833 }
Patrick Mochel90130262006-05-19 16:54:48 -0400834 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 video->cap._GPD = 1;
836 }
Patrick Mochel90130262006-05-19 16:54:48 -0400837 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 video->cap._SPD = 1;
839 }
Patrick Mochel90130262006-05-19 16:54:48 -0400840 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 video->cap._VPO = 1;
842 }
843}
844
845/*
846 * Check whether the video bus device has required AML method to
847 * support the desired features
848 */
849
Len Brown4be44fc2005-08-05 00:44:28 -0400850static int acpi_video_bus_check(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851{
Len Brown4be44fc2005-08-05 00:44:28 -0400852 acpi_status status = -ENOENT;
Thomas Renninger22c13f92008-08-01 17:37:54 +0200853 struct device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
855 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -0400856 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
Thomas Renninger22c13f92008-08-01 17:37:54 +0200858 dev = acpi_get_physical_pci_device(video->device->handle);
859 if (!dev)
860 return -ENODEV;
861 put_device(dev);
862
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 /* Since there is no HID, CID and so on for VGA driver, we have
864 * to check well known required nodes.
865 */
866
Julius Volz98fb8fe2007-02-20 16:38:40 +0100867 /* Does this device support video switching? */
Len Brown4be44fc2005-08-05 00:44:28 -0400868 if (video->cap._DOS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 video->flags.multihead = 1;
870 status = 0;
871 }
872
Julius Volz98fb8fe2007-02-20 16:38:40 +0100873 /* Does this device support retrieving a video ROM? */
Len Brown4be44fc2005-08-05 00:44:28 -0400874 if (video->cap._ROM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 video->flags.rom = 1;
876 status = 0;
877 }
878
Julius Volz98fb8fe2007-02-20 16:38:40 +0100879 /* Does this device support configuring which video device to POST? */
Len Brown4be44fc2005-08-05 00:44:28 -0400880 if (video->cap._GPD && video->cap._SPD && video->cap._VPO) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 video->flags.post = 1;
882 status = 0;
883 }
884
Patrick Mocheld550d982006-06-27 00:41:40 -0400885 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886}
887
888/* --------------------------------------------------------------------------
889 FS Interface (/proc)
890 -------------------------------------------------------------------------- */
891
Len Brown4be44fc2005-08-05 00:44:28 -0400892static struct proc_dir_entry *acpi_video_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893
894/* video devices */
895
Len Brown4be44fc2005-08-05 00:44:28 -0400896static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200898 struct acpi_video_device *dev = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
901 if (!dev)
902 goto end;
903
904 seq_printf(seq, "device_id: 0x%04x\n", (u32) dev->device_id);
905 seq_printf(seq, "type: ");
906 if (dev->flags.crt)
907 seq_printf(seq, "CRT\n");
908 else if (dev->flags.lcd)
909 seq_printf(seq, "LCD\n");
910 else if (dev->flags.tvout)
911 seq_printf(seq, "TVOUT\n");
Rui Zhang82cae992007-01-03 23:40:53 -0500912 else if (dev->flags.dvi)
913 seq_printf(seq, "DVI\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 else
915 seq_printf(seq, "UNKNOWN\n");
916
Len Brown4be44fc2005-08-05 00:44:28 -0400917 seq_printf(seq, "known by bios: %s\n", dev->flags.bios ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
Len Brown4be44fc2005-08-05 00:44:28 -0400919 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400920 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921}
922
923static int
Len Brown4be44fc2005-08-05 00:44:28 -0400924acpi_video_device_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925{
926 return single_open(file, acpi_video_device_info_seq_show,
927 PDE(inode)->data);
928}
929
Len Brown4be44fc2005-08-05 00:44:28 -0400930static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931{
Len Brown4be44fc2005-08-05 00:44:28 -0400932 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200933 struct acpi_video_device *dev = seq->private;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400934 unsigned long long state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
937 if (!dev)
938 goto end;
939
940 status = acpi_video_device_get_state(dev, &state);
941 seq_printf(seq, "state: ");
942 if (ACPI_SUCCESS(status))
Matthew Wilcox27663c52008-10-10 02:22:59 -0400943 seq_printf(seq, "0x%02llx\n", state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 else
945 seq_printf(seq, "<not supported>\n");
946
947 status = acpi_video_device_query(dev, &state);
948 seq_printf(seq, "query: ");
949 if (ACPI_SUCCESS(status))
Matthew Wilcox27663c52008-10-10 02:22:59 -0400950 seq_printf(seq, "0x%02llx\n", state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 else
952 seq_printf(seq, "<not supported>\n");
953
Len Brown4be44fc2005-08-05 00:44:28 -0400954 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400955 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956}
957
958static int
Len Brown4be44fc2005-08-05 00:44:28 -0400959acpi_video_device_state_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960{
961 return single_open(file, acpi_video_device_state_seq_show,
962 PDE(inode)->data);
963}
964
965static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400966acpi_video_device_write_state(struct file *file,
967 const char __user * buffer,
968 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969{
Len Brown4be44fc2005-08-05 00:44:28 -0400970 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200971 struct seq_file *m = file->private_data;
972 struct acpi_video_device *dev = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400973 char str[12] = { 0 };
974 u32 state = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976
977 if (!dev || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -0400978 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979
980 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -0400981 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982
983 str[count] = 0;
984 state = simple_strtoul(str, NULL, 0);
Len Brown4be44fc2005-08-05 00:44:28 -0400985 state &= ((1ul << 31) | (1ul << 30) | (1ul << 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
987 status = acpi_video_device_set_state(dev, state);
988
989 if (status)
Patrick Mocheld550d982006-06-27 00:41:40 -0400990 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991
Patrick Mocheld550d982006-06-27 00:41:40 -0400992 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993}
994
995static int
Len Brown4be44fc2005-08-05 00:44:28 -0400996acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200998 struct acpi_video_device *dev = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400999 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001
1002 if (!dev || !dev->brightness) {
1003 seq_printf(seq, "<not supported>\n");
Patrick Mocheld550d982006-06-27 00:41:40 -04001004 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 }
1006
1007 seq_printf(seq, "levels: ");
1008 for (i = 0; i < dev->brightness->count; i++)
1009 seq_printf(seq, " %d", dev->brightness->levels[i]);
1010 seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr);
1011
Patrick Mocheld550d982006-06-27 00:41:40 -04001012 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013}
1014
1015static int
Len Brown4be44fc2005-08-05 00:44:28 -04001016acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017{
1018 return single_open(file, acpi_video_device_brightness_seq_show,
1019 PDE(inode)->data);
1020}
1021
1022static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001023acpi_video_device_write_brightness(struct file *file,
1024 const char __user * buffer,
1025 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001027 struct seq_file *m = file->private_data;
1028 struct acpi_video_device *dev = m->private;
Danny Baumannc88c5782007-11-02 13:47:53 +01001029 char str[5] = { 0 };
Len Brown4be44fc2005-08-05 00:44:28 -04001030 unsigned int level = 0;
1031 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033
Thomas Renninger59d399d2005-11-08 05:27:00 -05001034 if (!dev || !dev->brightness || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -04001035 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
1037 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001038 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039
1040 str[count] = 0;
1041 level = simple_strtoul(str, NULL, 0);
Len Brown4be44fc2005-08-05 00:44:28 -04001042
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 if (level > 100)
Patrick Mocheld550d982006-06-27 00:41:40 -04001044 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045
Julius Volz98fb8fe2007-02-20 16:38:40 +01001046 /* validate through the list of available levels */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 for (i = 0; i < dev->brightness->count; i++)
1048 if (level == dev->brightness->levels[i]) {
Len Brown4be44fc2005-08-05 00:44:28 -04001049 if (ACPI_SUCCESS
1050 (acpi_video_device_lcd_set_level(dev, level)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 dev->brightness->curr = level;
1052 break;
1053 }
1054
Patrick Mocheld550d982006-06-27 00:41:40 -04001055 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056}
1057
Len Brown4be44fc2005-08-05 00:44:28 -04001058static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001060 struct acpi_video_device *dev = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001061 int status;
1062 int i;
1063 union acpi_object *edid = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065
1066 if (!dev)
1067 goto out;
1068
Len Brown4be44fc2005-08-05 00:44:28 -04001069 status = acpi_video_device_EDID(dev, &edid, 128);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 if (ACPI_FAILURE(status)) {
Len Brown4be44fc2005-08-05 00:44:28 -04001071 status = acpi_video_device_EDID(dev, &edid, 256);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 }
1073
1074 if (ACPI_FAILURE(status)) {
1075 goto out;
1076 }
1077
1078 if (edid && edid->type == ACPI_TYPE_BUFFER) {
1079 for (i = 0; i < edid->buffer.length; i++)
1080 seq_putc(seq, edid->buffer.pointer[i]);
1081 }
1082
Len Brown4be44fc2005-08-05 00:44:28 -04001083 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 if (!edid)
1085 seq_printf(seq, "<not supported>\n");
1086 else
1087 kfree(edid);
1088
Patrick Mocheld550d982006-06-27 00:41:40 -04001089 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090}
1091
1092static int
Len Brown4be44fc2005-08-05 00:44:28 -04001093acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094{
1095 return single_open(file, acpi_video_device_EDID_seq_show,
1096 PDE(inode)->data);
1097}
1098
Len Brown4be44fc2005-08-05 00:44:28 -04001099static int acpi_video_device_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100{
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001101 struct proc_dir_entry *entry, *device_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 struct acpi_video_device *vid_dev;
1103
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001104 vid_dev = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 if (!vid_dev)
Patrick Mocheld550d982006-06-27 00:41:40 -04001106 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001108 device_dir = proc_mkdir(acpi_device_bid(device),
1109 vid_dev->video->dir);
1110 if (!device_dir)
1111 return -ENOMEM;
1112
1113 device_dir->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114
1115 /* 'info' [R] */
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001116 entry = proc_create_data("info", S_IRUGO, device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001117 &acpi_video_device_info_fops, acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001119 goto err_remove_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120
1121 /* 'state' [R/W] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001122 acpi_video_device_state_fops.write = acpi_video_device_write_state;
1123 entry = proc_create_data("state", S_IFREG | S_IRUGO | S_IWUSR,
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001124 device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001125 &acpi_video_device_state_fops,
1126 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001128 goto err_remove_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129
1130 /* 'brightness' [R/W] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001131 acpi_video_device_brightness_fops.write =
1132 acpi_video_device_write_brightness;
1133 entry = proc_create_data("brightness", S_IFREG | S_IRUGO | S_IWUSR,
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001134 device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001135 &acpi_video_device_brightness_fops,
1136 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001138 goto err_remove_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139
1140 /* 'EDID' [R] */
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001141 entry = proc_create_data("EDID", S_IRUGO, device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001142 &acpi_video_device_EDID_fops,
1143 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001145 goto err_remove_brightness;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001147 acpi_device_dir(device) = device_dir;
1148
Patrick Mocheld550d982006-06-27 00:41:40 -04001149 return 0;
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001150
1151 err_remove_brightness:
1152 remove_proc_entry("brightness", device_dir);
1153 err_remove_state:
1154 remove_proc_entry("state", device_dir);
1155 err_remove_info:
1156 remove_proc_entry("info", device_dir);
1157 err_remove_dir:
1158 remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
1159 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160}
1161
Len Brown4be44fc2005-08-05 00:44:28 -04001162static int acpi_video_device_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163{
1164 struct acpi_video_device *vid_dev;
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001165 struct proc_dir_entry *device_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001167 vid_dev = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001169 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001171 device_dir = acpi_device_dir(device);
1172 if (device_dir) {
1173 remove_proc_entry("info", device_dir);
1174 remove_proc_entry("state", device_dir);
1175 remove_proc_entry("brightness", device_dir);
1176 remove_proc_entry("EDID", device_dir);
Len Brown4be44fc2005-08-05 00:44:28 -04001177 remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 acpi_device_dir(device) = NULL;
1179 }
1180
Patrick Mocheld550d982006-06-27 00:41:40 -04001181 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182}
1183
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184/* video bus */
Len Brown4be44fc2005-08-05 00:44:28 -04001185static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001187 struct acpi_video_bus *video = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189
1190 if (!video)
1191 goto end;
1192
1193 seq_printf(seq, "Switching heads: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001194 video->flags.multihead ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 seq_printf(seq, "Video ROM: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001196 video->flags.rom ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 seq_printf(seq, "Device to be POSTed on boot: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001198 video->flags.post ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199
Len Brown4be44fc2005-08-05 00:44:28 -04001200 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001201 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202}
1203
Len Brown4be44fc2005-08-05 00:44:28 -04001204static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205{
Len Brown4be44fc2005-08-05 00:44:28 -04001206 return single_open(file, acpi_video_bus_info_seq_show,
1207 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208}
1209
Len Brown4be44fc2005-08-05 00:44:28 -04001210static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001212 struct acpi_video_bus *video = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214
1215 if (!video)
1216 goto end;
1217
Harvey Harrison96b2dd12008-03-05 18:24:51 -08001218 printk(KERN_INFO PREFIX "Please implement %s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 seq_printf(seq, "<TODO>\n");
1220
Len Brown4be44fc2005-08-05 00:44:28 -04001221 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001222 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223}
1224
Len Brown4be44fc2005-08-05 00:44:28 -04001225static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226{
1227 return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data);
1228}
1229
Len Brown4be44fc2005-08-05 00:44:28 -04001230static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001232 struct acpi_video_bus *video = seq->private;
Matthew Wilcox27663c52008-10-10 02:22:59 -04001233 unsigned long long options;
Len Brown4be44fc2005-08-05 00:44:28 -04001234 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
1237 if (!video)
1238 goto end;
1239
1240 status = acpi_video_bus_POST_options(video, &options);
1241 if (ACPI_SUCCESS(status)) {
1242 if (!(options & 1)) {
Len Brown4be44fc2005-08-05 00:44:28 -04001243 printk(KERN_WARNING PREFIX
1244 "The motherboard VGA device is not listed as a possible POST device.\n");
1245 printk(KERN_WARNING PREFIX
Julius Volz98fb8fe2007-02-20 16:38:40 +01001246 "This indicates a BIOS bug. Please contact the manufacturer.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 }
Matthew Wilcox27663c52008-10-10 02:22:59 -04001248 printk("%llx\n", options);
Julius Volz98fb8fe2007-02-20 16:38:40 +01001249 seq_printf(seq, "can POST: <integrated video>");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 if (options & 2)
1251 seq_printf(seq, " <PCI video>");
1252 if (options & 4)
1253 seq_printf(seq, " <AGP video>");
1254 seq_putc(seq, '\n');
1255 } else
1256 seq_printf(seq, "<not supported>\n");
Len Brown4be44fc2005-08-05 00:44:28 -04001257 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001258 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259}
1260
1261static int
Len Brown4be44fc2005-08-05 00:44:28 -04001262acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263{
Len Brown4be44fc2005-08-05 00:44:28 -04001264 return single_open(file, acpi_video_bus_POST_info_seq_show,
1265 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266}
1267
Len Brown4be44fc2005-08-05 00:44:28 -04001268static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001270 struct acpi_video_bus *video = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001271 int status;
Matthew Wilcox27663c52008-10-10 02:22:59 -04001272 unsigned long long id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
1275 if (!video)
1276 goto end;
1277
Len Brown4be44fc2005-08-05 00:44:28 -04001278 status = acpi_video_bus_get_POST(video, &id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 if (!ACPI_SUCCESS(status)) {
1280 seq_printf(seq, "<not supported>\n");
1281 goto end;
1282 }
Julius Volz98fb8fe2007-02-20 16:38:40 +01001283 seq_printf(seq, "device POSTed is <%s>\n", device_decode[id & 3]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284
Len Brown4be44fc2005-08-05 00:44:28 -04001285 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001286 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287}
1288
Len Brown4be44fc2005-08-05 00:44:28 -04001289static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001291 struct acpi_video_bus *video = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293
Len Brown4be44fc2005-08-05 00:44:28 -04001294 seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295
Patrick Mocheld550d982006-06-27 00:41:40 -04001296 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297}
1298
Len Brown4be44fc2005-08-05 00:44:28 -04001299static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300{
Len Brown4be44fc2005-08-05 00:44:28 -04001301 return single_open(file, acpi_video_bus_POST_seq_show,
1302 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303}
1304
Len Brown4be44fc2005-08-05 00:44:28 -04001305static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306{
1307 return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data);
1308}
1309
1310static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001311acpi_video_bus_write_POST(struct file *file,
1312 const char __user * buffer,
1313 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314{
Len Brown4be44fc2005-08-05 00:44:28 -04001315 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001316 struct seq_file *m = file->private_data;
1317 struct acpi_video_bus *video = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001318 char str[12] = { 0 };
Matthew Wilcox27663c52008-10-10 02:22:59 -04001319 unsigned long long opt, options;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 if (!video || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -04001323 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324
1325 status = acpi_video_bus_POST_options(video, &options);
1326 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001327 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
1329 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001330 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331
1332 str[count] = 0;
1333 opt = strtoul(str, NULL, 0);
1334 if (opt > 3)
Patrick Mocheld550d982006-06-27 00:41:40 -04001335 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336
Julius Volz98fb8fe2007-02-20 16:38:40 +01001337 /* just in case an OEM 'forgot' the motherboard... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 options |= 1;
1339
1340 if (options & (1ul << opt)) {
Len Brown4be44fc2005-08-05 00:44:28 -04001341 status = acpi_video_bus_set_POST(video, opt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001343 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
1345 }
1346
Patrick Mocheld550d982006-06-27 00:41:40 -04001347 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348}
1349
1350static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001351acpi_video_bus_write_DOS(struct file *file,
1352 const char __user * buffer,
1353 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354{
Len Brown4be44fc2005-08-05 00:44:28 -04001355 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001356 struct seq_file *m = file->private_data;
1357 struct acpi_video_bus *video = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001358 char str[12] = { 0 };
1359 unsigned long opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 if (!video || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -04001363 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
1365 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001366 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
1368 str[count] = 0;
1369 opt = strtoul(str, NULL, 0);
1370 if (opt > 7)
Patrick Mocheld550d982006-06-27 00:41:40 -04001371 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
Len Brown4be44fc2005-08-05 00:44:28 -04001373 status = acpi_video_bus_DOS(video, opt & 0x3, (opt & 0x4) >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374
1375 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001376 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Patrick Mocheld550d982006-06-27 00:41:40 -04001378 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379}
1380
Len Brown4be44fc2005-08-05 00:44:28 -04001381static int acpi_video_bus_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382{
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001383 struct acpi_video_bus *video = acpi_driver_data(device);
1384 struct proc_dir_entry *device_dir;
1385 struct proc_dir_entry *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001387 device_dir = proc_mkdir(acpi_device_bid(device), acpi_video_dir);
1388 if (!device_dir)
1389 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001391 device_dir->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392
1393 /* 'info' [R] */
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001394 entry = proc_create_data("info", S_IRUGO, device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001395 &acpi_video_bus_info_fops,
1396 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001398 goto err_remove_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399
1400 /* 'ROM' [R] */
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001401 entry = proc_create_data("ROM", S_IRUGO, device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001402 &acpi_video_bus_ROM_fops,
1403 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001405 goto err_remove_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406
1407 /* 'POST_info' [R] */
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001408 entry = proc_create_data("POST_info", S_IRUGO, device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001409 &acpi_video_bus_POST_info_fops,
1410 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001412 goto err_remove_rom;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413
1414 /* 'POST' [R/W] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001415 acpi_video_bus_POST_fops.write = acpi_video_bus_write_POST;
Linus Torvalds08acd4f2008-04-30 11:52:52 -07001416 entry = proc_create_data("POST", S_IFREG | S_IRUGO | S_IWUSR,
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001417 device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001418 &acpi_video_bus_POST_fops,
1419 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001421 goto err_remove_post_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422
1423 /* 'DOS' [R/W] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001424 acpi_video_bus_DOS_fops.write = acpi_video_bus_write_DOS;
Linus Torvalds08acd4f2008-04-30 11:52:52 -07001425 entry = proc_create_data("DOS", S_IFREG | S_IRUGO | S_IWUSR,
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001426 device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001427 &acpi_video_bus_DOS_fops,
1428 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001430 goto err_remove_post;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001432 video->dir = acpi_device_dir(device) = device_dir;
Patrick Mocheld550d982006-06-27 00:41:40 -04001433 return 0;
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001434
1435 err_remove_post:
1436 remove_proc_entry("POST", device_dir);
1437 err_remove_post_info:
1438 remove_proc_entry("POST_info", device_dir);
1439 err_remove_rom:
1440 remove_proc_entry("ROM", device_dir);
1441 err_remove_info:
1442 remove_proc_entry("info", device_dir);
1443 err_remove_dir:
1444 remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
1445 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446}
1447
Len Brown4be44fc2005-08-05 00:44:28 -04001448static int acpi_video_bus_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449{
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001450 struct proc_dir_entry *device_dir = acpi_device_dir(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001452 if (device_dir) {
1453 remove_proc_entry("info", device_dir);
1454 remove_proc_entry("ROM", device_dir);
1455 remove_proc_entry("POST_info", device_dir);
1456 remove_proc_entry("POST", device_dir);
1457 remove_proc_entry("DOS", device_dir);
Len Brown4be44fc2005-08-05 00:44:28 -04001458 remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 acpi_device_dir(device) = NULL;
1460 }
1461
Patrick Mocheld550d982006-06-27 00:41:40 -04001462 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463}
1464
1465/* --------------------------------------------------------------------------
1466 Driver Interface
1467 -------------------------------------------------------------------------- */
1468
1469/* device interface */
Rui Zhang82cae992007-01-03 23:40:53 -05001470static struct acpi_video_device_attrib*
1471acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id)
1472{
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001473 struct acpi_video_enumerated_device *ids;
1474 int i;
Rui Zhang82cae992007-01-03 23:40:53 -05001475
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001476 for (i = 0; i < video->attached_count; i++) {
1477 ids = &video->attached_array[i];
1478 if ((ids->value.int_val & 0xffff) == device_id)
1479 return &ids->value.attrib;
1480 }
1481
Rui Zhang82cae992007-01-03 23:40:53 -05001482 return NULL;
1483}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484
1485static int
Len Brown4be44fc2005-08-05 00:44:28 -04001486acpi_video_bus_get_one_device(struct acpi_device *device,
1487 struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488{
Matthew Wilcox27663c52008-10-10 02:22:59 -04001489 unsigned long long device_id;
Yu, Luming973bf492006-04-27 05:25:00 -04001490 int status;
Len Brown4be44fc2005-08-05 00:44:28 -04001491 struct acpi_video_device *data;
Rui Zhang82cae992007-01-03 23:40:53 -05001492 struct acpi_video_device_attrib* attribute;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493
1494 if (!device || !video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001495 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
Len Brown4be44fc2005-08-05 00:44:28 -04001497 status =
1498 acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 if (ACPI_SUCCESS(status)) {
1500
Burman Yan36bcbec2006-12-19 12:56:11 -08001501 data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 if (!data)
Patrick Mocheld550d982006-06-27 00:41:40 -04001503 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
1506 strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -07001507 device->driver_data = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
1509 data->device_id = device_id;
1510 data->video = video;
1511 data->dev = device;
1512
Rui Zhang82cae992007-01-03 23:40:53 -05001513 attribute = acpi_video_get_device_attr(video, device_id);
1514
1515 if((attribute != NULL) && attribute->device_id_scheme) {
1516 switch (attribute->display_type) {
1517 case ACPI_VIDEO_DISPLAY_CRT:
1518 data->flags.crt = 1;
1519 break;
1520 case ACPI_VIDEO_DISPLAY_TV:
1521 data->flags.tvout = 1;
1522 break;
1523 case ACPI_VIDEO_DISPLAY_DVI:
1524 data->flags.dvi = 1;
1525 break;
1526 case ACPI_VIDEO_DISPLAY_LCD:
1527 data->flags.lcd = 1;
1528 break;
1529 default:
1530 data->flags.unknown = 1;
1531 break;
1532 }
1533 if(attribute->bios_can_detect)
1534 data->flags.bios = 1;
1535 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 data->flags.unknown = 1;
Len Brown4be44fc2005-08-05 00:44:28 -04001537
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 acpi_video_device_bind(video, data);
1539 acpi_video_device_find_cap(data);
1540
Patrick Mochel90130262006-05-19 16:54:48 -04001541 status = acpi_install_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001542 ACPI_DEVICE_NOTIFY,
1543 acpi_video_device_notify,
1544 data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 if (ACPI_FAILURE(status)) {
Lin Ming55ac9a02008-09-28 14:51:56 +08001546 printk(KERN_ERR PREFIX
1547 "Error installing notify handler\n");
Yu, Luming973bf492006-04-27 05:25:00 -04001548 if(data->brightness)
1549 kfree(data->brightness->levels);
1550 kfree(data->brightness);
1551 kfree(data);
1552 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 }
1554
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05001555 mutex_lock(&video->device_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 list_add_tail(&data->entry, &video->video_device_list);
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05001557 mutex_unlock(&video->device_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558
1559 acpi_video_device_add_fs(device);
1560
Patrick Mocheld550d982006-06-27 00:41:40 -04001561 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 }
1563
Patrick Mocheld550d982006-06-27 00:41:40 -04001564 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565}
1566
1567/*
1568 * Arg:
1569 * video : video bus device
1570 *
1571 * Return:
1572 * none
1573 *
1574 * Enumerate the video device list of the video bus,
1575 * bind the ids with the corresponding video devices
1576 * under the video bus.
Len Brown4be44fc2005-08-05 00:44:28 -04001577 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578
Len Brown4be44fc2005-08-05 00:44:28 -04001579static void acpi_video_device_rebind(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580{
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001581 struct acpi_video_device *dev;
1582
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05001583 mutex_lock(&video->device_list_lock);
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001584
1585 list_for_each_entry(dev, &video->video_device_list, entry)
Len Brown4be44fc2005-08-05 00:44:28 -04001586 acpi_video_device_bind(video, dev);
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001587
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05001588 mutex_unlock(&video->device_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589}
1590
1591/*
1592 * Arg:
1593 * video : video bus device
1594 * device : video output device under the video
1595 * bus
1596 *
1597 * Return:
1598 * none
1599 *
1600 * Bind the ids with the corresponding video devices
1601 * under the video bus.
Len Brown4be44fc2005-08-05 00:44:28 -04001602 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603
1604static void
Len Brown4be44fc2005-08-05 00:44:28 -04001605acpi_video_device_bind(struct acpi_video_bus *video,
1606 struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607{
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001608 struct acpi_video_enumerated_device *ids;
Len Brown4be44fc2005-08-05 00:44:28 -04001609 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001611 for (i = 0; i < video->attached_count; i++) {
1612 ids = &video->attached_array[i];
1613 if (device->device_id == (ids->value.int_val & 0xffff)) {
1614 ids->bind_info = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i));
1616 }
1617 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618}
1619
1620/*
1621 * Arg:
1622 * video : video bus device
1623 *
1624 * Return:
1625 * < 0 : error
1626 *
1627 * Call _DOD to enumerate all devices attached to display adapter
1628 *
Len Brown4be44fc2005-08-05 00:44:28 -04001629 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630
1631static int acpi_video_device_enumerate(struct acpi_video_bus *video)
1632{
Len Brown4be44fc2005-08-05 00:44:28 -04001633 int status;
1634 int count;
1635 int i;
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001636 struct acpi_video_enumerated_device *active_list;
Len Brown4be44fc2005-08-05 00:44:28 -04001637 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1638 union acpi_object *dod = NULL;
1639 union acpi_object *obj;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
Patrick Mochel90130262006-05-19 16:54:48 -04001641 status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 if (!ACPI_SUCCESS(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001643 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD"));
Patrick Mocheld550d982006-06-27 00:41:40 -04001644 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 }
1646
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001647 dod = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001649 ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 status = -EFAULT;
1651 goto out;
1652 }
1653
1654 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001655 dod->package.count));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001657 active_list = kcalloc(1 + dod->package.count,
1658 sizeof(struct acpi_video_enumerated_device),
1659 GFP_KERNEL);
1660 if (!active_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 status = -ENOMEM;
1662 goto out;
1663 }
1664
1665 count = 0;
1666 for (i = 0; i < dod->package.count; i++) {
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001667 obj = &dod->package.elements[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668
1669 if (obj->type != ACPI_TYPE_INTEGER) {
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001670 printk(KERN_ERR PREFIX
1671 "Invalid _DOD data in element %d\n", i);
1672 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 }
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001674
1675 active_list[count].value.int_val = obj->integer.value;
1676 active_list[count].bind_info = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -04001677 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i,
1678 (int)obj->integer.value));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 count++;
1680 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681
Jesper Juhl6044ec82005-11-07 01:01:32 -08001682 kfree(video->attached_array);
Len Brown4be44fc2005-08-05 00:44:28 -04001683
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001684 video->attached_array = active_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 video->attached_count = count;
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001686
1687 out:
Len Brown02438d82006-06-30 03:19:10 -04001688 kfree(buffer.pointer);
Patrick Mocheld550d982006-06-27 00:41:40 -04001689 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690}
1691
Len Brown4be44fc2005-08-05 00:44:28 -04001692static int
1693acpi_video_get_next_level(struct acpi_video_device *device,
1694 u32 level_current, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695{
Alexey Starikovskiy63f0edf2007-09-03 16:30:08 +04001696 int min, max, min_above, max_below, i, l, delta = 255;
Thomas Tuttlef4715182006-12-19 12:56:14 -08001697 max = max_below = 0;
1698 min = min_above = 255;
Alexey Starikovskiy63f0edf2007-09-03 16:30:08 +04001699 /* Find closest level to level_current */
1700 for (i = 0; i < device->brightness->count; i++) {
1701 l = device->brightness->levels[i];
1702 if (abs(l - level_current) < abs(delta)) {
1703 delta = l - level_current;
1704 if (!delta)
1705 break;
1706 }
1707 }
1708 /* Ajust level_current to closest available level */
1709 level_current += delta;
Thomas Tuttlef4715182006-12-19 12:56:14 -08001710 for (i = 0; i < device->brightness->count; i++) {
1711 l = device->brightness->levels[i];
1712 if (l < min)
1713 min = l;
1714 if (l > max)
1715 max = l;
1716 if (l < min_above && l > level_current)
1717 min_above = l;
1718 if (l > max_below && l < level_current)
1719 max_below = l;
1720 }
1721
1722 switch (event) {
1723 case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:
1724 return (level_current < max) ? min_above : min;
1725 case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:
1726 return (level_current < max) ? min_above : max;
1727 case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:
1728 return (level_current > min) ? max_below : min;
1729 case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:
1730 case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:
1731 return 0;
1732 default:
1733 return level_current;
1734 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735}
1736
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737static void
Len Brown4be44fc2005-08-05 00:44:28 -04001738acpi_video_switch_brightness(struct acpi_video_device *device, int event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739{
Matthew Wilcox27663c52008-10-10 02:22:59 -04001740 unsigned long long level_current, level_next;
Julia Jomantaite469778c2008-06-23 22:50:42 +01001741 if (!device->brightness)
1742 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 acpi_video_device_lcd_get_level_current(device, &level_current);
1744 level_next = acpi_video_get_next_level(device, level_current, event);
1745 acpi_video_device_lcd_set_level(device, level_next);
1746}
1747
1748static int
Len Brown4be44fc2005-08-05 00:44:28 -04001749acpi_video_bus_get_devices(struct acpi_video_bus *video,
1750 struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751{
Len Brown4be44fc2005-08-05 00:44:28 -04001752 int status = 0;
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001753 struct acpi_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754
1755 acpi_video_device_enumerate(video);
1756
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001757 list_for_each_entry(dev, &device->children, node) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758
1759 status = acpi_video_bus_get_one_device(dev, video);
1760 if (ACPI_FAILURE(status)) {
Lin Ming55ac9a02008-09-28 14:51:56 +08001761 printk(KERN_WARNING PREFIX
1762 "Cant attach device");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 continue;
1764 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765 }
Patrick Mocheld550d982006-06-27 00:41:40 -04001766 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767}
1768
Len Brown4be44fc2005-08-05 00:44:28 -04001769static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770{
Karol Kozimor031ec772005-07-30 04:18:00 -04001771 acpi_status status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 struct acpi_video_bus *video;
1773
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774
1775 if (!device || !device->video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001776 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
1778 video = device->video;
1779
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780 acpi_video_device_remove_fs(device->dev);
1781
Patrick Mochel90130262006-05-19 16:54:48 -04001782 status = acpi_remove_notify_handler(device->dev->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001783 ACPI_DEVICE_NOTIFY,
1784 acpi_video_device_notify);
Richard Purdie599a52d2007-02-10 23:07:48 +00001785 backlight_device_unregister(device->backlight);
Zhang Rui702ed512008-01-17 15:51:22 +08001786 if (device->cdev) {
1787 sysfs_remove_link(&device->dev->dev.kobj,
1788 "thermal_cooling");
1789 sysfs_remove_link(&device->cdev->device.kobj,
1790 "device");
1791 thermal_cooling_device_unregister(device->cdev);
1792 device->cdev = NULL;
1793 }
Luming Yu23b0f012007-05-09 21:07:05 +08001794 video_output_unregister(device->output_dev);
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001795
Patrick Mocheld550d982006-06-27 00:41:40 -04001796 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797}
1798
Len Brown4be44fc2005-08-05 00:44:28 -04001799static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800{
Len Brown4be44fc2005-08-05 00:44:28 -04001801 int status;
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001802 struct acpi_video_device *dev, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05001804 mutex_lock(&video->device_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001806 list_for_each_entry_safe(dev, next, &video->video_device_list, entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001808 status = acpi_video_bus_put_one_device(dev);
Len Brown4be44fc2005-08-05 00:44:28 -04001809 if (ACPI_FAILURE(status))
1810 printk(KERN_WARNING PREFIX
1811 "hhuuhhuu bug in acpi video driver.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001813 if (dev->brightness) {
1814 kfree(dev->brightness->levels);
1815 kfree(dev->brightness);
1816 }
1817 list_del(&dev->entry);
1818 kfree(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 }
1820
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05001821 mutex_unlock(&video->device_list_lock);
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001822
Patrick Mocheld550d982006-06-27 00:41:40 -04001823 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824}
1825
1826/* acpi_video interface */
1827
Len Brown4be44fc2005-08-05 00:44:28 -04001828static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829{
Zhang Ruia21101c2007-09-14 11:46:22 +08001830 return acpi_video_bus_DOS(video, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831}
1832
Len Brown4be44fc2005-08-05 00:44:28 -04001833static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834{
1835 return acpi_video_bus_DOS(video, 0, 1);
1836}
1837
Len Brown4be44fc2005-08-05 00:44:28 -04001838static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001840 struct acpi_video_bus *video = data;
Len Brown4be44fc2005-08-05 00:44:28 -04001841 struct acpi_device *device = NULL;
Luming Yue9dab192007-08-20 18:23:53 +08001842 struct input_dev *input;
1843 int keycode;
1844
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001846 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847
Patrick Mochele6afa0d2006-05-19 16:54:40 -04001848 device = video->device;
Luming Yue9dab192007-08-20 18:23:53 +08001849 input = video->input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850
1851 switch (event) {
Julius Volz98fb8fe2007-02-20 16:38:40 +01001852 case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853 * most likely via hotkey. */
Len Brown14e04fb2007-08-23 15:20:26 -04001854 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001855 keycode = KEY_SWITCHVIDEOMODE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856 break;
1857
Julius Volz98fb8fe2007-02-20 16:38:40 +01001858 case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859 * connector. */
1860 acpi_video_device_enumerate(video);
1861 acpi_video_device_rebind(video);
Len Brown14e04fb2007-08-23 15:20:26 -04001862 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001863 keycode = KEY_SWITCHVIDEOMODE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864 break;
1865
Len Brown4be44fc2005-08-05 00:44:28 -04001866 case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */
Len Brown25c87f72007-08-25 01:44:01 -04001867 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001868 keycode = KEY_SWITCHVIDEOMODE;
1869 break;
Len Brown4be44fc2005-08-05 00:44:28 -04001870 case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */
Len Brown25c87f72007-08-25 01:44:01 -04001871 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001872 keycode = KEY_VIDEO_NEXT;
1873 break;
Len Brown4be44fc2005-08-05 00:44:28 -04001874 case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */
Len Brown14e04fb2007-08-23 15:20:26 -04001875 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001876 keycode = KEY_VIDEO_PREV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 break;
1878
1879 default:
Luming Yue9dab192007-08-20 18:23:53 +08001880 keycode = KEY_UNKNOWN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -04001882 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883 break;
1884 }
1885
Zhang Rui7761f632008-01-25 14:48:12 +08001886 acpi_notifier_call_chain(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001887 input_report_key(input, keycode, 1);
1888 input_sync(input);
1889 input_report_key(input, keycode, 0);
1890 input_sync(input);
1891
Patrick Mocheld550d982006-06-27 00:41:40 -04001892 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893}
1894
Len Brown4be44fc2005-08-05 00:44:28 -04001895static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001897 struct acpi_video_device *video_device = data;
Len Brown4be44fc2005-08-05 00:44:28 -04001898 struct acpi_device *device = NULL;
Luming Yue9dab192007-08-20 18:23:53 +08001899 struct acpi_video_bus *bus;
1900 struct input_dev *input;
1901 int keycode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903 if (!video_device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001904 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905
Patrick Mochele6afa0d2006-05-19 16:54:40 -04001906 device = video_device->dev;
Luming Yue9dab192007-08-20 18:23:53 +08001907 bus = video_device->video;
1908 input = bus->input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909
1910 switch (event) {
Len Brown4be44fc2005-08-05 00:44:28 -04001911 case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */
Zhang Rui8a681a42008-01-25 14:47:49 +08001912 if (brightness_switch_enabled)
1913 acpi_video_switch_brightness(video_device, event);
Len Brown25c87f72007-08-25 01:44:01 -04001914 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001915 keycode = KEY_BRIGHTNESS_CYCLE;
1916 break;
Len Brown4be44fc2005-08-05 00:44:28 -04001917 case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */
Zhang Rui8a681a42008-01-25 14:47:49 +08001918 if (brightness_switch_enabled)
1919 acpi_video_switch_brightness(video_device, event);
Len Brown25c87f72007-08-25 01:44:01 -04001920 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001921 keycode = KEY_BRIGHTNESSUP;
1922 break;
Len Brown4be44fc2005-08-05 00:44:28 -04001923 case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */
Zhang Rui8a681a42008-01-25 14:47:49 +08001924 if (brightness_switch_enabled)
1925 acpi_video_switch_brightness(video_device, event);
Len Brown25c87f72007-08-25 01:44:01 -04001926 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001927 keycode = KEY_BRIGHTNESSDOWN;
1928 break;
Len Brown4be44fc2005-08-05 00:44:28 -04001929 case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightnesss */
Zhang Rui8a681a42008-01-25 14:47:49 +08001930 if (brightness_switch_enabled)
1931 acpi_video_switch_brightness(video_device, event);
Len Brown25c87f72007-08-25 01:44:01 -04001932 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001933 keycode = KEY_BRIGHTNESS_ZERO;
1934 break;
Len Brown4be44fc2005-08-05 00:44:28 -04001935 case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */
Zhang Rui8a681a42008-01-25 14:47:49 +08001936 if (brightness_switch_enabled)
1937 acpi_video_switch_brightness(video_device, event);
Len Brown14e04fb2007-08-23 15:20:26 -04001938 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001939 keycode = KEY_DISPLAY_OFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940 break;
1941 default:
Luming Yue9dab192007-08-20 18:23:53 +08001942 keycode = KEY_UNKNOWN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -04001944 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945 break;
1946 }
Luming Yue9dab192007-08-20 18:23:53 +08001947
Zhang Rui7761f632008-01-25 14:48:12 +08001948 acpi_notifier_call_chain(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08001949 input_report_key(input, keycode, 1);
1950 input_sync(input);
1951 input_report_key(input, keycode, 0);
1952 input_sync(input);
1953
Patrick Mocheld550d982006-06-27 00:41:40 -04001954 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955}
1956
Zhang Ruie6d9da12007-08-25 02:23:31 -04001957static int instance;
Matthew Garrett863c1492008-02-04 23:31:24 -08001958static int acpi_video_resume(struct acpi_device *device)
1959{
1960 struct acpi_video_bus *video;
1961 struct acpi_video_device *video_device;
1962 int i;
1963
1964 if (!device || !acpi_driver_data(device))
1965 return -EINVAL;
1966
1967 video = acpi_driver_data(device);
1968
1969 for (i = 0; i < video->attached_count; i++) {
1970 video_device = video->attached_array[i].bind_info;
1971 if (video_device && video_device->backlight)
1972 acpi_video_set_brightness(video_device->backlight);
1973 }
1974 return AE_OK;
1975}
1976
Len Brown4be44fc2005-08-05 00:44:28 -04001977static int acpi_video_bus_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978{
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05001979 acpi_status status;
1980 struct acpi_video_bus *video;
Luming Yue9dab192007-08-20 18:23:53 +08001981 struct input_dev *input;
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05001982 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983
Burman Yan36bcbec2006-12-19 12:56:11 -08001984 video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001986 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987
Zhang Ruie6d9da12007-08-25 02:23:31 -04001988 /* a hack to fix the duplicate name "VID" problem on T61 */
1989 if (!strcmp(device->pnp.bus_id, "VID")) {
1990 if (instance)
1991 device->pnp.bus_id[3] = '0' + instance;
1992 instance ++;
1993 }
1994
Patrick Mochele6afa0d2006-05-19 16:54:40 -04001995 video->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
1997 strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -07001998 device->driver_data = video;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999
2000 acpi_video_bus_find_cap(video);
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002001 error = acpi_video_bus_check(video);
2002 if (error)
2003 goto err_free_video;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002005 error = acpi_video_bus_add_fs(device);
2006 if (error)
2007 goto err_free_video;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05002009 mutex_init(&video->device_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 INIT_LIST_HEAD(&video->video_device_list);
2011
2012 acpi_video_bus_get_devices(video, device);
2013 acpi_video_bus_start_devices(video);
2014
Patrick Mochel90130262006-05-19 16:54:48 -04002015 status = acpi_install_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04002016 ACPI_DEVICE_NOTIFY,
2017 acpi_video_bus_notify, video);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 if (ACPI_FAILURE(status)) {
Lin Ming55ac9a02008-09-28 14:51:56 +08002019 printk(KERN_ERR PREFIX
2020 "Error installing notify handler\n");
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002021 error = -ENODEV;
2022 goto err_stop_video;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 }
2024
Luming Yue9dab192007-08-20 18:23:53 +08002025 video->input = input = input_allocate_device();
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002026 if (!input) {
2027 error = -ENOMEM;
2028 goto err_uninstall_notify;
2029 }
Luming Yue9dab192007-08-20 18:23:53 +08002030
2031 snprintf(video->phys, sizeof(video->phys),
2032 "%s/video/input0", acpi_device_hid(video->device));
2033
2034 input->name = acpi_device_name(video->device);
2035 input->phys = video->phys;
2036 input->id.bustype = BUS_HOST;
2037 input->id.product = 0x06;
Dmitry Torokhov91c05c62007-11-05 11:43:29 -05002038 input->dev.parent = &device->dev;
Luming Yue9dab192007-08-20 18:23:53 +08002039 input->evbit[0] = BIT(EV_KEY);
2040 set_bit(KEY_SWITCHVIDEOMODE, input->keybit);
2041 set_bit(KEY_VIDEO_NEXT, input->keybit);
2042 set_bit(KEY_VIDEO_PREV, input->keybit);
2043 set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit);
2044 set_bit(KEY_BRIGHTNESSUP, input->keybit);
2045 set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
2046 set_bit(KEY_BRIGHTNESS_ZERO, input->keybit);
2047 set_bit(KEY_DISPLAY_OFF, input->keybit);
2048 set_bit(KEY_UNKNOWN, input->keybit);
Luming Yue9dab192007-08-20 18:23:53 +08002049
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002050 error = input_register_device(input);
2051 if (error)
2052 goto err_free_input_dev;
Luming Yue9dab192007-08-20 18:23:53 +08002053
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n",
Len Brown4be44fc2005-08-05 00:44:28 -04002055 ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
2056 video->flags.multihead ? "yes" : "no",
2057 video->flags.rom ? "yes" : "no",
2058 video->flags.post ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002060 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002062 err_free_input_dev:
2063 input_free_device(input);
2064 err_uninstall_notify:
2065 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
2066 acpi_video_bus_notify);
2067 err_stop_video:
2068 acpi_video_bus_stop_devices(video);
2069 acpi_video_bus_put_devices(video);
2070 kfree(video->attached_array);
2071 acpi_video_bus_remove_fs(device);
2072 err_free_video:
2073 kfree(video);
Pavel Machekdb89b4f2008-09-22 14:37:34 -07002074 device->driver_data = NULL;
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002075
2076 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077}
2078
Len Brown4be44fc2005-08-05 00:44:28 -04002079static int acpi_video_bus_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080{
Len Brown4be44fc2005-08-05 00:44:28 -04002081 acpi_status status = 0;
2082 struct acpi_video_bus *video = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084
2085 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04002086 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087
Jan Engelhardt50dd0962006-10-01 00:28:50 +02002088 video = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089
2090 acpi_video_bus_stop_devices(video);
2091
Patrick Mochel90130262006-05-19 16:54:48 -04002092 status = acpi_remove_notify_handler(video->device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04002093 ACPI_DEVICE_NOTIFY,
2094 acpi_video_bus_notify);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095
2096 acpi_video_bus_put_devices(video);
2097 acpi_video_bus_remove_fs(device);
2098
Luming Yue9dab192007-08-20 18:23:53 +08002099 input_unregister_device(video->input);
Jesper Juhl6044ec82005-11-07 01:01:32 -08002100 kfree(video->attached_array);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 kfree(video);
2102
Patrick Mocheld550d982006-06-27 00:41:40 -04002103 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104}
2105
Len Brown4be44fc2005-08-05 00:44:28 -04002106static int __init acpi_video_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107{
Len Brown4be44fc2005-08-05 00:44:28 -04002108 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
2111 if (!acpi_video_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04002112 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113 acpi_video_dir->owner = THIS_MODULE;
2114
2115 result = acpi_bus_register_driver(&acpi_video_bus);
2116 if (result < 0) {
2117 remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -04002118 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 }
2120
Patrick Mocheld550d982006-06-27 00:41:40 -04002121 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122}
2123
Len Brown4be44fc2005-08-05 00:44:28 -04002124static void __exit acpi_video_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126
2127 acpi_bus_unregister_driver(&acpi_video_bus);
2128
2129 remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
2130
Patrick Mocheld550d982006-06-27 00:41:40 -04002131 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132}
2133
2134module_init(acpi_video_init);
2135module_exit(acpi_video_exit);