]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/acpi/video.c
ACPI: thermal: use .notify method instead of installing handler directly
[mirror_ubuntu-artful-kernel.git] / drivers / acpi / video.c
CommitLineData
1da177e4
LT
1/*
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>
f4715189 6 * Copyright (C) 2006 Thomas Tuttle <linux-kernel@ttuttle.net>
1da177e4
LT
7 *
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>
bbac81f5 32#include <linux/mutex.h>
1da177e4
LT
33#include <linux/proc_fs.h>
34#include <linux/seq_file.h>
e9dab196 35#include <linux/input.h>
2f3d000a 36#include <linux/backlight.h>
702ed512 37#include <linux/thermal.h>
23b0f015 38#include <linux/video_output.h>
935e5f29 39#include <linux/sort.h>
74a365b3
MG
40#include <linux/pci.h>
41#include <linux/pci_ids.h>
1da177e4
LT
42#include <asm/uaccess.h>
43
44#include <acpi/acpi_bus.h>
45#include <acpi/acpi_drivers.h>
46
1da177e4 47#define ACPI_VIDEO_CLASS "video"
1da177e4
LT
48#define ACPI_VIDEO_BUS_NAME "Video Bus"
49#define ACPI_VIDEO_DEVICE_NAME "Video Device"
50#define ACPI_VIDEO_NOTIFY_SWITCH 0x80
51#define ACPI_VIDEO_NOTIFY_PROBE 0x81
52#define ACPI_VIDEO_NOTIFY_CYCLE 0x82
53#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT 0x83
54#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT 0x84
55
f4715189
TT
56#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS 0x85
57#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
58#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
59#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS 0x88
60#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF 0x89
1da177e4 61
2f3d000a 62#define MAX_NAME_LEN 20
1da177e4 63
82cae999
RZ
64#define ACPI_VIDEO_DISPLAY_CRT 1
65#define ACPI_VIDEO_DISPLAY_TV 2
66#define ACPI_VIDEO_DISPLAY_DVI 3
67#define ACPI_VIDEO_DISPLAY_LCD 4
68
1da177e4 69#define _COMPONENT ACPI_VIDEO_COMPONENT
f52fd66d 70ACPI_MODULE_NAME("video");
1da177e4 71
f52fd66d 72MODULE_AUTHOR("Bruno Ducrot");
7cda93e0 73MODULE_DESCRIPTION("ACPI Video Driver");
1da177e4
LT
74MODULE_LICENSE("GPL");
75
8a681a4d
ZR
76static int brightness_switch_enabled = 1;
77module_param(brightness_switch_enabled, bool, 0644);
78
4be44fcd
LB
79static int acpi_video_bus_add(struct acpi_device *device);
80static int acpi_video_bus_remove(struct acpi_device *device, int type);
863c1490 81static int acpi_video_resume(struct acpi_device *device);
1da177e4 82
1ba90e3a
TR
83static const struct acpi_device_id video_device_ids[] = {
84 {ACPI_VIDEO_HID, 0},
85 {"", 0},
86};
87MODULE_DEVICE_TABLE(acpi, video_device_ids);
88
1da177e4 89static struct acpi_driver acpi_video_bus = {
c2b6705b 90 .name = "video",
1da177e4 91 .class = ACPI_VIDEO_CLASS,
1ba90e3a 92 .ids = video_device_ids,
1da177e4
LT
93 .ops = {
94 .add = acpi_video_bus_add,
95 .remove = acpi_video_bus_remove,
863c1490 96 .resume = acpi_video_resume,
4be44fcd 97 },
1da177e4
LT
98};
99
100struct acpi_video_bus_flags {
4be44fcd
LB
101 u8 multihead:1; /* can switch video heads */
102 u8 rom:1; /* can retrieve a video rom */
103 u8 post:1; /* can configure the head to */
104 u8 reserved:5;
1da177e4
LT
105};
106
107struct acpi_video_bus_cap {
4be44fcd
LB
108 u8 _DOS:1; /*Enable/Disable output switching */
109 u8 _DOD:1; /*Enumerate all devices attached to display adapter */
110 u8 _ROM:1; /*Get ROM Data */
111 u8 _GPD:1; /*Get POST Device */
112 u8 _SPD:1; /*Set POST Device */
113 u8 _VPO:1; /*Video POST Options */
114 u8 reserved:2;
1da177e4
LT
115};
116
4be44fcd
LB
117struct acpi_video_device_attrib {
118 u32 display_index:4; /* A zero-based instance of the Display */
98fb8fe1 119 u32 display_port_attachment:4; /*This field differentiates the display type */
4be44fcd 120 u32 display_type:4; /*Describe the specific type in use */
98fb8fe1 121 u32 vendor_specific:4; /*Chipset Vendor Specific */
4be44fcd
LB
122 u32 bios_can_detect:1; /*BIOS can detect the device */
123 u32 depend_on_vga:1; /*Non-VGA output device whose power is related to
124 the VGA device. */
125 u32 pipe_id:3; /*For VGA multiple-head devices. */
126 u32 reserved:10; /*Must be 0 */
127 u32 device_id_scheme:1; /*Device ID Scheme */
1da177e4
LT
128};
129
130struct acpi_video_enumerated_device {
131 union {
132 u32 int_val;
4be44fcd 133 struct acpi_video_device_attrib attrib;
1da177e4
LT
134 } value;
135 struct acpi_video_device *bind_info;
136};
137
138struct acpi_video_bus {
e6afa0de 139 struct acpi_device *device;
4be44fcd 140 u8 dos_setting;
1da177e4 141 struct acpi_video_enumerated_device *attached_array;
4be44fcd
LB
142 u8 attached_count;
143 struct acpi_video_bus_cap cap;
1da177e4 144 struct acpi_video_bus_flags flags;
4be44fcd 145 struct list_head video_device_list;
bbac81f5 146 struct mutex device_list_lock; /* protects video_device_list */
4be44fcd 147 struct proc_dir_entry *dir;
e9dab196
LY
148 struct input_dev *input;
149 char phys[32]; /* for input device */
1da177e4
LT
150};
151
152struct acpi_video_device_flags {
4be44fcd
LB
153 u8 crt:1;
154 u8 lcd:1;
155 u8 tvout:1;
82cae999 156 u8 dvi:1;
4be44fcd
LB
157 u8 bios:1;
158 u8 unknown:1;
82cae999 159 u8 reserved:2;
1da177e4
LT
160};
161
162struct acpi_video_device_cap {
4be44fcd
LB
163 u8 _ADR:1; /*Return the unique ID */
164 u8 _BCL:1; /*Query list of brightness control levels supported */
165 u8 _BCM:1; /*Set the brightness level */
2f3d000a 166 u8 _BQC:1; /* Get current brightness level */
c60d638e 167 u8 _BCQ:1; /* Some buggy BIOS uses _BCQ instead of _BQC */
4be44fcd
LB
168 u8 _DDC:1; /*Return the EDID for this device */
169 u8 _DCS:1; /*Return status of output device */
170 u8 _DGS:1; /*Query graphics state */
171 u8 _DSS:1; /*Device state set */
1da177e4
LT
172};
173
d32f6947
ZR
174struct acpi_video_brightness_flags {
175 u8 _BCL_no_ac_battery_levels:1; /* no AC/Battery levels in _BCL */
d80fb99f 176 u8 _BCL_reversed:1; /* _BCL package is in a reversed order*/
1a7c618a
ZR
177 u8 _BCL_use_index:1; /* levels in _BCL are index values */
178 u8 _BCM_use_index:1; /* input of _BCM is an index value */
179 u8 _BQC_use_index:1; /* _BQC returns an index value */
d32f6947
ZR
180};
181
1da177e4 182struct acpi_video_device_brightness {
4be44fcd
LB
183 int curr;
184 int count;
185 int *levels;
d32f6947 186 struct acpi_video_brightness_flags flags;
1da177e4
LT
187};
188
189struct acpi_video_device {
4be44fcd
LB
190 unsigned long device_id;
191 struct acpi_video_device_flags flags;
192 struct acpi_video_device_cap cap;
193 struct list_head entry;
194 struct acpi_video_bus *video;
195 struct acpi_device *dev;
1da177e4 196 struct acpi_video_device_brightness *brightness;
2f3d000a 197 struct backlight_device *backlight;
702ed512 198 struct thermal_cooling_device *cdev;
23b0f015 199 struct output_device *output_dev;
1da177e4
LT
200};
201
1da177e4
LT
202/* bus */
203static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file);
070d8eb1 204static const struct file_operations acpi_video_bus_info_fops = {
cf7acfab 205 .owner = THIS_MODULE,
4be44fcd
LB
206 .open = acpi_video_bus_info_open_fs,
207 .read = seq_read,
208 .llseek = seq_lseek,
209 .release = single_release,
1da177e4
LT
210};
211
212static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file);
070d8eb1 213static const struct file_operations acpi_video_bus_ROM_fops = {
cf7acfab 214 .owner = THIS_MODULE,
4be44fcd
LB
215 .open = acpi_video_bus_ROM_open_fs,
216 .read = seq_read,
217 .llseek = seq_lseek,
218 .release = single_release,
1da177e4
LT
219};
220
4be44fcd
LB
221static int acpi_video_bus_POST_info_open_fs(struct inode *inode,
222 struct file *file);
070d8eb1 223static const struct file_operations acpi_video_bus_POST_info_fops = {
cf7acfab 224 .owner = THIS_MODULE,
4be44fcd
LB
225 .open = acpi_video_bus_POST_info_open_fs,
226 .read = seq_read,
227 .llseek = seq_lseek,
228 .release = single_release,
1da177e4
LT
229};
230
231static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file);
c07c9a78 232static ssize_t acpi_video_bus_write_POST(struct file *file,
b7171ae7
JE
233 const char __user *buffer, size_t count, loff_t *data);
234static const struct file_operations acpi_video_bus_POST_fops = {
cf7acfab 235 .owner = THIS_MODULE,
4be44fcd
LB
236 .open = acpi_video_bus_POST_open_fs,
237 .read = seq_read,
b7171ae7 238 .write = acpi_video_bus_write_POST,
4be44fcd
LB
239 .llseek = seq_lseek,
240 .release = single_release,
1da177e4
LT
241};
242
1da177e4 243static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file);
c07c9a78 244static ssize_t acpi_video_bus_write_DOS(struct file *file,
b7171ae7
JE
245 const char __user *buffer, size_t count, loff_t *data);
246static const struct file_operations acpi_video_bus_DOS_fops = {
cf7acfab 247 .owner = THIS_MODULE,
4be44fcd
LB
248 .open = acpi_video_bus_DOS_open_fs,
249 .read = seq_read,
b7171ae7 250 .write = acpi_video_bus_write_DOS,
4be44fcd
LB
251 .llseek = seq_lseek,
252 .release = single_release,
1da177e4
LT
253};
254
255/* device */
4be44fcd
LB
256static int acpi_video_device_info_open_fs(struct inode *inode,
257 struct file *file);
070d8eb1 258static const struct file_operations acpi_video_device_info_fops = {
cf7acfab 259 .owner = THIS_MODULE,
4be44fcd
LB
260 .open = acpi_video_device_info_open_fs,
261 .read = seq_read,
262 .llseek = seq_lseek,
263 .release = single_release,
1da177e4
LT
264};
265
4be44fcd
LB
266static int acpi_video_device_state_open_fs(struct inode *inode,
267 struct file *file);
c07c9a78 268static ssize_t acpi_video_device_write_state(struct file *file,
b7171ae7
JE
269 const char __user *buffer, size_t count, loff_t *data);
270static const struct file_operations acpi_video_device_state_fops = {
cf7acfab 271 .owner = THIS_MODULE,
4be44fcd
LB
272 .open = acpi_video_device_state_open_fs,
273 .read = seq_read,
b7171ae7 274 .write = acpi_video_device_write_state,
4be44fcd
LB
275 .llseek = seq_lseek,
276 .release = single_release,
1da177e4
LT
277};
278
4be44fcd
LB
279static int acpi_video_device_brightness_open_fs(struct inode *inode,
280 struct file *file);
c07c9a78 281static ssize_t acpi_video_device_write_brightness(struct file *file,
b7171ae7 282 const char __user *buffer, size_t count, loff_t *data);
1da177e4 283static struct file_operations acpi_video_device_brightness_fops = {
cf7acfab 284 .owner = THIS_MODULE,
4be44fcd
LB
285 .open = acpi_video_device_brightness_open_fs,
286 .read = seq_read,
b7171ae7 287 .write = acpi_video_device_write_brightness,
4be44fcd
LB
288 .llseek = seq_lseek,
289 .release = single_release,
1da177e4
LT
290};
291
4be44fcd
LB
292static int acpi_video_device_EDID_open_fs(struct inode *inode,
293 struct file *file);
070d8eb1 294static const struct file_operations acpi_video_device_EDID_fops = {
cf7acfab 295 .owner = THIS_MODULE,
4be44fcd
LB
296 .open = acpi_video_device_EDID_open_fs,
297 .read = seq_read,
298 .llseek = seq_lseek,
299 .release = single_release,
1da177e4
LT
300};
301
b7171ae7 302static const char device_decode[][30] = {
1da177e4
LT
303 "motherboard VGA device",
304 "PCI VGA device",
305 "AGP VGA device",
306 "UNKNOWN",
307};
308
4be44fcd
LB
309static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data);
310static void acpi_video_device_rebind(struct acpi_video_bus *video);
311static void acpi_video_device_bind(struct acpi_video_bus *video,
312 struct acpi_video_device *device);
1da177e4 313static int acpi_video_device_enumerate(struct acpi_video_bus *video);
2f3d000a
YL
314static int acpi_video_device_lcd_set_level(struct acpi_video_device *device,
315 int level);
316static int acpi_video_device_lcd_get_level_current(
317 struct acpi_video_device *device,
27663c58 318 unsigned long long *level);
4be44fcd
LB
319static int acpi_video_get_next_level(struct acpi_video_device *device,
320 u32 level_current, u32 event);
c8890f90 321static int acpi_video_switch_brightness(struct acpi_video_device *device,
4be44fcd 322 int event);
23b0f015 323static int acpi_video_device_get_state(struct acpi_video_device *device,
27663c58 324 unsigned long long *state);
23b0f015
LY
325static int acpi_video_output_get(struct output_device *od);
326static int acpi_video_device_set_state(struct acpi_video_device *device, int state);
1da177e4 327
2f3d000a
YL
328/*backlight device sysfs support*/
329static int acpi_video_get_brightness(struct backlight_device *bd)
330{
27663c58 331 unsigned long long cur_level;
38531e6f 332 int i;
2f3d000a 333 struct acpi_video_device *vd =
655bfd7a 334 (struct acpi_video_device *)bl_get_data(bd);
c8890f90
ZR
335
336 if (acpi_video_device_lcd_get_level_current(vd, &cur_level))
337 return -EINVAL;
38531e6f
MG
338 for (i = 2; i < vd->brightness->count; i++) {
339 if (vd->brightness->levels[i] == cur_level)
340 /* The first two entries are special - see page 575
341 of the ACPI spec 3.0 */
342 return i-2;
343 }
344 return 0;
2f3d000a
YL
345}
346
347static int acpi_video_set_brightness(struct backlight_device *bd)
348{
24450c7a 349 int request_level = bd->props.brightness + 2;
2f3d000a 350 struct acpi_video_device *vd =
655bfd7a 351 (struct acpi_video_device *)bl_get_data(bd);
24450c7a
ZR
352
353 return acpi_video_device_lcd_set_level(vd,
354 vd->brightness->levels[request_level]);
2f3d000a
YL
355}
356
599a52d1
RP
357static struct backlight_ops acpi_backlight_ops = {
358 .get_brightness = acpi_video_get_brightness,
359 .update_status = acpi_video_set_brightness,
360};
361
23b0f015
LY
362/*video output device sysfs support*/
363static int acpi_video_output_get(struct output_device *od)
364{
27663c58 365 unsigned long long state;
23b0f015 366 struct acpi_video_device *vd =
60043428 367 (struct acpi_video_device *)dev_get_drvdata(&od->dev);
23b0f015
LY
368 acpi_video_device_get_state(vd, &state);
369 return (int)state;
370}
371
372static int acpi_video_output_set(struct output_device *od)
373{
374 unsigned long state = od->request_state;
375 struct acpi_video_device *vd=
60043428 376 (struct acpi_video_device *)dev_get_drvdata(&od->dev);
23b0f015
LY
377 return acpi_video_device_set_state(vd, state);
378}
379
380static struct output_properties acpi_output_properties = {
381 .set_state = acpi_video_output_set,
382 .get_status = acpi_video_output_get,
383};
702ed512
ZR
384
385
386/* thermal cooling device callbacks */
6503e5df
MG
387static int video_get_max_state(struct thermal_cooling_device *cdev, unsigned
388 long *state)
702ed512
ZR
389{
390 struct acpi_device *device = cdev->devdata;
391 struct acpi_video_device *video = acpi_driver_data(device);
392
6503e5df
MG
393 *state = video->brightness->count - 3;
394 return 0;
702ed512
ZR
395}
396
6503e5df
MG
397static int video_get_cur_state(struct thermal_cooling_device *cdev, unsigned
398 long *state)
702ed512
ZR
399{
400 struct acpi_device *device = cdev->devdata;
401 struct acpi_video_device *video = acpi_driver_data(device);
27663c58 402 unsigned long long level;
6503e5df 403 int offset;
702ed512 404
c8890f90
ZR
405 if (acpi_video_device_lcd_get_level_current(video, &level))
406 return -EINVAL;
6503e5df
MG
407 for (offset = 2; offset < video->brightness->count; offset++)
408 if (level == video->brightness->levels[offset]) {
409 *state = video->brightness->count - offset - 1;
410 return 0;
411 }
702ed512
ZR
412
413 return -EINVAL;
414}
415
416static int
6503e5df 417video_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
702ed512
ZR
418{
419 struct acpi_device *device = cdev->devdata;
420 struct acpi_video_device *video = acpi_driver_data(device);
421 int level;
422
423 if ( state >= video->brightness->count - 2)
424 return -EINVAL;
425
426 state = video->brightness->count - state;
427 level = video->brightness->levels[state -1];
428 return acpi_video_device_lcd_set_level(video, level);
429}
430
431static struct thermal_cooling_device_ops video_cooling_ops = {
432 .get_max_state = video_get_max_state,
433 .get_cur_state = video_get_cur_state,
434 .set_cur_state = video_set_cur_state,
435};
436
1da177e4
LT
437/* --------------------------------------------------------------------------
438 Video Management
439 -------------------------------------------------------------------------- */
440
441/* device */
442
443static int
27663c58 444acpi_video_device_query(struct acpi_video_device *device, unsigned long long *state)
1da177e4 445{
4be44fcd 446 int status;
90130268
PM
447
448 status = acpi_evaluate_integer(device->dev->handle, "_DGS", NULL, state);
1da177e4 449
d550d98d 450 return status;
1da177e4
LT
451}
452
453static int
4be44fcd 454acpi_video_device_get_state(struct acpi_video_device *device,
27663c58 455 unsigned long long *state)
1da177e4 456{
4be44fcd 457 int status;
1da177e4 458
90130268 459 status = acpi_evaluate_integer(device->dev->handle, "_DCS", NULL, state);
1da177e4 460
d550d98d 461 return status;
1da177e4
LT
462}
463
464static int
4be44fcd 465acpi_video_device_set_state(struct acpi_video_device *device, int state)
1da177e4 466{
4be44fcd
LB
467 int status;
468 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
469 struct acpi_object_list args = { 1, &arg0 };
27663c58 470 unsigned long long ret;
1da177e4 471
1da177e4
LT
472
473 arg0.integer.value = state;
90130268 474 status = acpi_evaluate_integer(device->dev->handle, "_DSS", &args, &ret);
1da177e4 475
d550d98d 476 return status;
1da177e4
LT
477}
478
479static int
4be44fcd
LB
480acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
481 union acpi_object **levels)
1da177e4 482{
4be44fcd
LB
483 int status;
484 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
485 union acpi_object *obj;
1da177e4 486
1da177e4
LT
487
488 *levels = NULL;
489
90130268 490 status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer);
1da177e4 491 if (!ACPI_SUCCESS(status))
d550d98d 492 return status;
4be44fcd 493 obj = (union acpi_object *)buffer.pointer;
6665bda7 494 if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
6468463a 495 printk(KERN_ERR PREFIX "Invalid _BCL data\n");
1da177e4
LT
496 status = -EFAULT;
497 goto err;
498 }
499
500 *levels = obj;
501
d550d98d 502 return 0;
1da177e4 503
4be44fcd 504 err:
6044ec88 505 kfree(buffer.pointer);
1da177e4 506
d550d98d 507 return status;
1da177e4
LT
508}
509
510static int
4be44fcd 511acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
1da177e4 512{
24450c7a 513 int status;
4be44fcd
LB
514 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
515 struct acpi_object_list args = { 1, &arg0 };
9e6dada9 516 int state;
1da177e4 517
1da177e4 518 arg0.integer.value = level;
1da177e4 519
24450c7a
ZR
520 status = acpi_evaluate_object(device->dev->handle, "_BCM",
521 &args, NULL);
522 if (ACPI_FAILURE(status)) {
523 ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));
524 return -EIO;
525 }
526
4500ca8e 527 device->brightness->curr = level;
9e6dada9 528 for (state = 2; state < device->brightness->count; state++)
24450c7a 529 if (level == device->brightness->levels[state]) {
1a7c618a
ZR
530 if (device->backlight)
531 device->backlight->props.brightness = state - 2;
24450c7a
ZR
532 return 0;
533 }
9e6dada9 534
24450c7a
ZR
535 ACPI_ERROR((AE_INFO, "Current brightness invalid"));
536 return -EINVAL;
1da177e4
LT
537}
538
539static int
4be44fcd 540acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
27663c58 541 unsigned long long *level)
1da177e4 542{
c8890f90
ZR
543 acpi_status status = AE_OK;
544
c60d638e
ZR
545 if (device->cap._BQC || device->cap._BCQ) {
546 char *buf = device->cap._BQC ? "_BQC" : "_BCQ";
547
548 status = acpi_evaluate_integer(device->dev->handle, buf,
c8890f90
ZR
549 NULL, level);
550 if (ACPI_SUCCESS(status)) {
1a7c618a
ZR
551 if (device->brightness->flags._BQC_use_index) {
552 if (device->brightness->flags._BCL_reversed)
553 *level = device->brightness->count
554 - 3 - (*level);
555 *level = device->brightness->levels[*level + 2];
556
557 }
c8890f90
ZR
558 device->brightness->curr = *level;
559 return 0;
560 } else {
561 /* Fixme:
562 * should we return an error or ignore this failure?
563 * dev->brightness->curr is a cached value which stores
564 * the correct current backlight level in most cases.
565 * ACPI video backlight still works w/ buggy _BQC.
566 * http://bugzilla.kernel.org/show_bug.cgi?id=12233
567 */
c60d638e
ZR
568 ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf));
569 device->cap._BQC = device->cap._BCQ = 0;
c8890f90
ZR
570 }
571 }
572
4500ca8e 573 *level = device->brightness->curr;
c8890f90 574 return 0;
1da177e4
LT
575}
576
577static int
4be44fcd
LB
578acpi_video_device_EDID(struct acpi_video_device *device,
579 union acpi_object **edid, ssize_t length)
1da177e4 580{
4be44fcd
LB
581 int status;
582 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
583 union acpi_object *obj;
584 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
585 struct acpi_object_list args = { 1, &arg0 };
1da177e4 586
1da177e4
LT
587
588 *edid = NULL;
589
590 if (!device)
d550d98d 591 return -ENODEV;
1da177e4
LT
592 if (length == 128)
593 arg0.integer.value = 1;
594 else if (length == 256)
595 arg0.integer.value = 2;
596 else
d550d98d 597 return -EINVAL;
1da177e4 598
90130268 599 status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer);
1da177e4 600 if (ACPI_FAILURE(status))
d550d98d 601 return -ENODEV;
1da177e4 602
50dd0969 603 obj = buffer.pointer;
1da177e4
LT
604
605 if (obj && obj->type == ACPI_TYPE_BUFFER)
606 *edid = obj;
607 else {
6468463a 608 printk(KERN_ERR PREFIX "Invalid _DDC data\n");
1da177e4
LT
609 status = -EFAULT;
610 kfree(obj);
611 }
612
d550d98d 613 return status;
1da177e4
LT
614}
615
1da177e4
LT
616/* bus */
617
618static int
4be44fcd 619acpi_video_bus_set_POST(struct acpi_video_bus *video, unsigned long option)
1da177e4 620{
4be44fcd 621 int status;
27663c58 622 unsigned long long tmp;
4be44fcd
LB
623 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
624 struct acpi_object_list args = { 1, &arg0 };
1da177e4 625
1da177e4
LT
626
627 arg0.integer.value = option;
628
90130268 629 status = acpi_evaluate_integer(video->device->handle, "_SPD", &args, &tmp);
1da177e4 630 if (ACPI_SUCCESS(status))
4be44fcd 631 status = tmp ? (-EINVAL) : (AE_OK);
1da177e4 632
d550d98d 633 return status;
1da177e4
LT
634}
635
636static int
27663c58 637acpi_video_bus_get_POST(struct acpi_video_bus *video, unsigned long long *id)
1da177e4
LT
638{
639 int status;
640
90130268 641 status = acpi_evaluate_integer(video->device->handle, "_GPD", NULL, id);
1da177e4 642
d550d98d 643 return status;
1da177e4
LT
644}
645
646static int
4be44fcd 647acpi_video_bus_POST_options(struct acpi_video_bus *video,
27663c58 648 unsigned long long *options)
1da177e4 649{
4be44fcd 650 int status;
1da177e4 651
90130268 652 status = acpi_evaluate_integer(video->device->handle, "_VPO", NULL, options);
1da177e4
LT
653 *options &= 3;
654
d550d98d 655 return status;
1da177e4
LT
656}
657
658/*
659 * Arg:
660 * video : video bus device pointer
661 * bios_flag :
662 * 0. The system BIOS should NOT automatically switch(toggle)
663 * the active display output.
664 * 1. The system BIOS should automatically switch (toggle) the
98fb8fe1 665 * active display output. No switch event.
1da177e4
LT
666 * 2. The _DGS value should be locked.
667 * 3. The system BIOS should not automatically switch (toggle) the
668 * active display output, but instead generate the display switch
669 * event notify code.
670 * lcd_flag :
671 * 0. The system BIOS should automatically control the brightness level
98fb8fe1 672 * of the LCD when the power changes from AC to DC
1da177e4 673 * 1. The system BIOS should NOT automatically control the brightness
98fb8fe1 674 * level of the LCD when the power changes from AC to DC.
1da177e4
LT
675 * Return Value:
676 * -1 wrong arg.
677 */
678
679static int
4be44fcd 680acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
1da177e4 681{
4be44fcd
LB
682 acpi_integer status = 0;
683 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
684 struct acpi_object_list args = { 1, &arg0 };
1da177e4 685
1da177e4 686
4be44fcd 687 if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) {
1da177e4
LT
688 status = -1;
689 goto Failed;
690 }
691 arg0.integer.value = (lcd_flag << 2) | bios_flag;
692 video->dos_setting = arg0.integer.value;
90130268 693 acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL);
1da177e4 694
4be44fcd 695 Failed:
d550d98d 696 return status;
1da177e4
LT
697}
698
935e5f29
ZR
699/*
700 * Simple comparison function used to sort backlight levels.
701 */
702
703static int
704acpi_video_cmp_level(const void *a, const void *b)
705{
706 return *(int *)a - *(int *)b;
707}
708
1da177e4
LT
709/*
710 * Arg:
711 * device : video output device (LCD, CRT, ..)
712 *
713 * Return Value:
469778c1
JJ
714 * Maximum brightness level
715 *
716 * Allocate and initialize device->brightness.
717 */
718
719static int
720acpi_video_init_brightness(struct acpi_video_device *device)
721{
722 union acpi_object *obj = NULL;
d32f6947 723 int i, max_level = 0, count = 0, level_ac_battery = 0;
1a7c618a 724 unsigned long long level, level_old;
469778c1
JJ
725 union acpi_object *o;
726 struct acpi_video_device_brightness *br = NULL;
1a7c618a 727 int result = -EINVAL;
469778c1
JJ
728
729 if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) {
730 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available "
731 "LCD brightness level\n"));
732 goto out;
733 }
734
735 if (obj->package.count < 2)
736 goto out;
737
738 br = kzalloc(sizeof(*br), GFP_KERNEL);
739 if (!br) {
740 printk(KERN_ERR "can't allocate memory\n");
1a7c618a 741 result = -ENOMEM;
469778c1
JJ
742 goto out;
743 }
744
d32f6947 745 br->levels = kmalloc((obj->package.count + 2) * sizeof *(br->levels),
469778c1 746 GFP_KERNEL);
1a7c618a
ZR
747 if (!br->levels) {
748 result = -ENOMEM;
469778c1 749 goto out_free;
1a7c618a 750 }
469778c1
JJ
751
752 for (i = 0; i < obj->package.count; i++) {
753 o = (union acpi_object *)&obj->package.elements[i];
754 if (o->type != ACPI_TYPE_INTEGER) {
755 printk(KERN_ERR PREFIX "Invalid data\n");
756 continue;
757 }
758 br->levels[count] = (u32) o->integer.value;
759
760 if (br->levels[count] > max_level)
761 max_level = br->levels[count];
762 count++;
763 }
764
d32f6947
ZR
765 /*
766 * some buggy BIOS don't export the levels
767 * when machine is on AC/Battery in _BCL package.
768 * In this case, the first two elements in _BCL packages
769 * are also supported brightness levels that OS should take care of.
770 */
771 for (i = 2; i < count; i++)
772 if (br->levels[i] == br->levels[0] ||
773 br->levels[i] == br->levels[1])
774 level_ac_battery++;
775
776 if (level_ac_battery < 2) {
777 level_ac_battery = 2 - level_ac_battery;
778 br->flags._BCL_no_ac_battery_levels = 1;
779 for (i = (count - 1 + level_ac_battery); i >= 2; i--)
780 br->levels[i] = br->levels[i - level_ac_battery];
781 count += level_ac_battery;
782 } else if (level_ac_battery > 2)
783 ACPI_ERROR((AE_INFO, "Too many duplicates in _BCL package\n"));
784
d80fb99f
ZR
785 /* Check if the _BCL package is in a reversed order */
786 if (max_level == br->levels[2]) {
787 br->flags._BCL_reversed = 1;
788 sort(&br->levels[2], count - 2, sizeof(br->levels[2]),
789 acpi_video_cmp_level, NULL);
790 } else if (max_level != br->levels[count - 1])
791 ACPI_ERROR((AE_INFO,
792 "Found unordered _BCL package\n"));
469778c1
JJ
793
794 br->count = count;
795 device->brightness = br;
1a7c618a
ZR
796
797 /* Check the input/output of _BQC/_BCL/_BCM */
798 if ((max_level < 100) && (max_level <= (count - 2)))
799 br->flags._BCL_use_index = 1;
800
801 /*
802 * _BCM is always consistent with _BCL,
803 * at least for all the laptops we have ever seen.
804 */
805 br->flags._BCM_use_index = br->flags._BCL_use_index;
806
807 /* _BQC uses INDEX while _BCL uses VALUE in some laptops */
808 br->curr = max_level;
809 result = acpi_video_device_lcd_get_level_current(device, &level_old);
810 if (result)
811 goto out_free_levels;
812
813 result = acpi_video_device_lcd_set_level(device, br->curr);
814 if (result)
815 goto out_free_levels;
816
817 result = acpi_video_device_lcd_get_level_current(device, &level);
818 if (result)
819 goto out_free_levels;
820
821 if ((level != level_old) && !br->flags._BCM_use_index) {
822 /* Note:
823 * This piece of code does not work correctly if the current
824 * brightness levels is 0.
825 * But I guess boxes that boot with such a dark screen are rare
826 * and no more code is needed to cover this specifial case.
827 */
828
829 if (level_ac_battery != 2) {
830 /*
831 * For now, we don't support the _BCL like this:
832 * 16, 15, 0, 1, 2, 3, ..., 14, 15, 16
833 * because we may mess up the index returned by _BQC.
834 * Plus: we have not got a box like this.
835 */
836 ACPI_ERROR((AE_INFO, "_BCL not supported\n"));
837 }
838 br->flags._BQC_use_index = 1;
839 }
840
d32f6947
ZR
841 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
842 "found %d brightness levels\n", count - 2));
469778c1 843 kfree(obj);
1a7c618a 844 return result;
469778c1
JJ
845
846out_free_levels:
847 kfree(br->levels);
848out_free:
849 kfree(br);
850out:
851 device->brightness = NULL;
852 kfree(obj);
1a7c618a 853 return result;
469778c1
JJ
854}
855
856/*
857 * Arg:
858 * device : video output device (LCD, CRT, ..)
859 *
860 * Return Value:
1da177e4
LT
861 * None
862 *
98fb8fe1 863 * Find out all required AML methods defined under the output
1da177e4
LT
864 * device.
865 */
866
4be44fcd 867static void acpi_video_device_find_cap(struct acpi_video_device *device)
1da177e4 868{
1da177e4 869 acpi_handle h_dummy1;
1da177e4 870
1da177e4 871
98934def 872 memset(&device->cap, 0, sizeof(device->cap));
1da177e4 873
90130268 874 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
1da177e4
LT
875 device->cap._ADR = 1;
876 }
90130268 877 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) {
4be44fcd 878 device->cap._BCL = 1;
1da177e4 879 }
90130268 880 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
4be44fcd 881 device->cap._BCM = 1;
1da177e4 882 }
2f3d000a
YL
883 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
884 device->cap._BQC = 1;
c60d638e
ZR
885 else if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCQ",
886 &h_dummy1))) {
887 printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n");
888 device->cap._BCQ = 1;
889 }
890
90130268 891 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
4be44fcd 892 device->cap._DDC = 1;
1da177e4 893 }
90130268 894 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DCS", &h_dummy1))) {
1da177e4
LT
895 device->cap._DCS = 1;
896 }
90130268 897 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DGS", &h_dummy1))) {
1da177e4
LT
898 device->cap._DGS = 1;
899 }
90130268 900 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DSS", &h_dummy1))) {
1da177e4
LT
901 device->cap._DSS = 1;
902 }
903
1a7c618a 904 if (acpi_video_backlight_support()) {
702ed512 905 int result;
2f3d000a
YL
906 static int count = 0;
907 char *name;
1a7c618a
ZR
908
909 result = acpi_video_init_brightness(device);
910 if (result)
911 return;
2f3d000a
YL
912 name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
913 if (!name)
914 return;
915
2f3d000a 916 sprintf(name, "acpi_video%d", count++);
2f3d000a 917 device->backlight = backlight_device_register(name,
599a52d1 918 NULL, device, &acpi_backlight_ops);
38531e6f 919 device->backlight->props.max_brightness = device->brightness->count-3;
2f3d000a 920 kfree(name);
702ed512
ZR
921
922 device->cdev = thermal_cooling_device_register("LCD",
923 device->dev, &video_cooling_ops);
43ff39f2
TS
924 if (IS_ERR(device->cdev))
925 return;
926
fc3a8828
GKH
927 dev_info(&device->dev->dev, "registered as cooling_device%d\n",
928 device->cdev->id);
9030062f
JL
929 result = sysfs_create_link(&device->dev->dev.kobj,
930 &device->cdev->device.kobj,
931 "thermal_cooling");
932 if (result)
933 printk(KERN_ERR PREFIX "Create sysfs link\n");
934 result = sysfs_create_link(&device->cdev->device.kobj,
935 &device->dev->dev.kobj, "device");
936 if (result)
937 printk(KERN_ERR PREFIX "Create sysfs link\n");
938
2f3d000a 939 }
c3d6de69
TR
940
941 if (acpi_video_display_switch_support()) {
942
943 if (device->cap._DCS && device->cap._DSS) {
944 static int count;
945 char *name;
946 name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
947 if (!name)
948 return;
949 sprintf(name, "acpi_video%d", count++);
950 device->output_dev = video_output_register(name,
951 NULL, device, &acpi_output_properties);
952 kfree(name);
953 }
23b0f015 954 }
1da177e4
LT
955}
956
957/*
958 * Arg:
959 * device : video output device (VGA)
960 *
961 * Return Value:
962 * None
963 *
98fb8fe1 964 * Find out all required AML methods defined under the video bus device.
1da177e4
LT
965 */
966
4be44fcd 967static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
1da177e4 968{
4be44fcd 969 acpi_handle h_dummy1;
1da177e4 970
98934def 971 memset(&video->cap, 0, sizeof(video->cap));
90130268 972 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
1da177e4
LT
973 video->cap._DOS = 1;
974 }
90130268 975 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) {
1da177e4
LT
976 video->cap._DOD = 1;
977 }
90130268 978 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) {
1da177e4
LT
979 video->cap._ROM = 1;
980 }
90130268 981 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) {
1da177e4
LT
982 video->cap._GPD = 1;
983 }
90130268 984 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) {
1da177e4
LT
985 video->cap._SPD = 1;
986 }
90130268 987 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) {
1da177e4
LT
988 video->cap._VPO = 1;
989 }
990}
991
992/*
993 * Check whether the video bus device has required AML method to
994 * support the desired features
995 */
996
4be44fcd 997static int acpi_video_bus_check(struct acpi_video_bus *video)
1da177e4 998{
4be44fcd 999 acpi_status status = -ENOENT;
22c13f9d 1000 struct device *dev;
1da177e4
LT
1001
1002 if (!video)
d550d98d 1003 return -EINVAL;
1da177e4 1004
22c13f9d
TR
1005 dev = acpi_get_physical_pci_device(video->device->handle);
1006 if (!dev)
1007 return -ENODEV;
1008 put_device(dev);
1009
1da177e4
LT
1010 /* Since there is no HID, CID and so on for VGA driver, we have
1011 * to check well known required nodes.
1012 */
1013
98fb8fe1 1014 /* Does this device support video switching? */
4be44fcd 1015 if (video->cap._DOS) {
1da177e4
LT
1016 video->flags.multihead = 1;
1017 status = 0;
1018 }
1019
98fb8fe1 1020 /* Does this device support retrieving a video ROM? */
4be44fcd 1021 if (video->cap._ROM) {
1da177e4
LT
1022 video->flags.rom = 1;
1023 status = 0;
1024 }
1025
98fb8fe1 1026 /* Does this device support configuring which video device to POST? */
4be44fcd 1027 if (video->cap._GPD && video->cap._SPD && video->cap._VPO) {
1da177e4
LT
1028 video->flags.post = 1;
1029 status = 0;
1030 }
1031
d550d98d 1032 return status;
1da177e4
LT
1033}
1034
1035/* --------------------------------------------------------------------------
1036 FS Interface (/proc)
1037 -------------------------------------------------------------------------- */
1038
4be44fcd 1039static struct proc_dir_entry *acpi_video_dir;
1da177e4
LT
1040
1041/* video devices */
1042
4be44fcd 1043static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
1da177e4 1044{
50dd0969 1045 struct acpi_video_device *dev = seq->private;
1da177e4 1046
1da177e4
LT
1047
1048 if (!dev)
1049 goto end;
1050
1051 seq_printf(seq, "device_id: 0x%04x\n", (u32) dev->device_id);
1052 seq_printf(seq, "type: ");
1053 if (dev->flags.crt)
1054 seq_printf(seq, "CRT\n");
1055 else if (dev->flags.lcd)
1056 seq_printf(seq, "LCD\n");
1057 else if (dev->flags.tvout)
1058 seq_printf(seq, "TVOUT\n");
82cae999
RZ
1059 else if (dev->flags.dvi)
1060 seq_printf(seq, "DVI\n");
1da177e4
LT
1061 else
1062 seq_printf(seq, "UNKNOWN\n");
1063
4be44fcd 1064 seq_printf(seq, "known by bios: %s\n", dev->flags.bios ? "yes" : "no");
1da177e4 1065
4be44fcd 1066 end:
d550d98d 1067 return 0;
1da177e4
LT
1068}
1069
1070static int
4be44fcd 1071acpi_video_device_info_open_fs(struct inode *inode, struct file *file)
1da177e4
LT
1072{
1073 return single_open(file, acpi_video_device_info_seq_show,
1074 PDE(inode)->data);
1075}
1076
4be44fcd 1077static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset)
1da177e4 1078{
4be44fcd 1079 int status;
50dd0969 1080 struct acpi_video_device *dev = seq->private;
27663c58 1081 unsigned long long state;
1da177e4 1082
1da177e4
LT
1083
1084 if (!dev)
1085 goto end;
1086
1087 status = acpi_video_device_get_state(dev, &state);
1088 seq_printf(seq, "state: ");
1089 if (ACPI_SUCCESS(status))
27663c58 1090 seq_printf(seq, "0x%02llx\n", state);
1da177e4
LT
1091 else
1092 seq_printf(seq, "<not supported>\n");
1093
1094 status = acpi_video_device_query(dev, &state);
1095 seq_printf(seq, "query: ");
1096 if (ACPI_SUCCESS(status))
27663c58 1097 seq_printf(seq, "0x%02llx\n", state);
1da177e4
LT
1098 else
1099 seq_printf(seq, "<not supported>\n");
1100
4be44fcd 1101 end:
d550d98d 1102 return 0;
1da177e4
LT
1103}
1104
1105static int
4be44fcd 1106acpi_video_device_state_open_fs(struct inode *inode, struct file *file)
1da177e4
LT
1107{
1108 return single_open(file, acpi_video_device_state_seq_show,
1109 PDE(inode)->data);
1110}
1111
1112static ssize_t
4be44fcd
LB
1113acpi_video_device_write_state(struct file *file,
1114 const char __user * buffer,
1115 size_t count, loff_t * data)
1da177e4 1116{
4be44fcd 1117 int status;
50dd0969
JE
1118 struct seq_file *m = file->private_data;
1119 struct acpi_video_device *dev = m->private;
4be44fcd
LB
1120 char str[12] = { 0 };
1121 u32 state = 0;
1da177e4 1122
1da177e4
LT
1123
1124 if (!dev || count + 1 > sizeof str)
d550d98d 1125 return -EINVAL;
1da177e4
LT
1126
1127 if (copy_from_user(str, buffer, count))
d550d98d 1128 return -EFAULT;
1da177e4
LT
1129
1130 str[count] = 0;
1131 state = simple_strtoul(str, NULL, 0);
4be44fcd 1132 state &= ((1ul << 31) | (1ul << 30) | (1ul << 0));
1da177e4
LT
1133
1134 status = acpi_video_device_set_state(dev, state);
1135
1136 if (status)
d550d98d 1137 return -EFAULT;
1da177e4 1138
d550d98d 1139 return count;
1da177e4
LT
1140}
1141
1142static int
4be44fcd 1143acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
1da177e4 1144{
50dd0969 1145 struct acpi_video_device *dev = seq->private;
4be44fcd 1146 int i;
1da177e4 1147
1da177e4
LT
1148
1149 if (!dev || !dev->brightness) {
1150 seq_printf(seq, "<not supported>\n");
d550d98d 1151 return 0;
1da177e4
LT
1152 }
1153
1154 seq_printf(seq, "levels: ");
0a3db1ce 1155 for (i = 2; i < dev->brightness->count; i++)
1da177e4
LT
1156 seq_printf(seq, " %d", dev->brightness->levels[i]);
1157 seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr);
1158
d550d98d 1159 return 0;
1da177e4
LT
1160}
1161
1162static int
4be44fcd 1163acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file)
1da177e4
LT
1164{
1165 return single_open(file, acpi_video_device_brightness_seq_show,
1166 PDE(inode)->data);
1167}
1168
1169static ssize_t
4be44fcd
LB
1170acpi_video_device_write_brightness(struct file *file,
1171 const char __user * buffer,
1172 size_t count, loff_t * data)
1da177e4 1173{
50dd0969
JE
1174 struct seq_file *m = file->private_data;
1175 struct acpi_video_device *dev = m->private;
c88c5786 1176 char str[5] = { 0 };
4be44fcd
LB
1177 unsigned int level = 0;
1178 int i;
1da177e4 1179
1da177e4 1180
59d399d3 1181 if (!dev || !dev->brightness || count + 1 > sizeof str)
d550d98d 1182 return -EINVAL;
1da177e4
LT
1183
1184 if (copy_from_user(str, buffer, count))
d550d98d 1185 return -EFAULT;
1da177e4
LT
1186
1187 str[count] = 0;
1188 level = simple_strtoul(str, NULL, 0);
4be44fcd 1189
1da177e4 1190 if (level > 100)
d550d98d 1191 return -EFAULT;
1da177e4 1192
98fb8fe1 1193 /* validate through the list of available levels */
0a3db1ce 1194 for (i = 2; i < dev->brightness->count; i++)
1da177e4 1195 if (level == dev->brightness->levels[i]) {
24450c7a
ZR
1196 if (!acpi_video_device_lcd_set_level(dev, level))
1197 return count;
1da177e4
LT
1198 break;
1199 }
1200
24450c7a 1201 return -EINVAL;
1da177e4
LT
1202}
1203
4be44fcd 1204static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
1da177e4 1205{
50dd0969 1206 struct acpi_video_device *dev = seq->private;
4be44fcd
LB
1207 int status;
1208 int i;
1209 union acpi_object *edid = NULL;
1da177e4 1210
1da177e4
LT
1211
1212 if (!dev)
1213 goto out;
1214
4be44fcd 1215 status = acpi_video_device_EDID(dev, &edid, 128);
1da177e4 1216 if (ACPI_FAILURE(status)) {
4be44fcd 1217 status = acpi_video_device_EDID(dev, &edid, 256);
1da177e4
LT
1218 }
1219
1220 if (ACPI_FAILURE(status)) {
1221 goto out;
1222 }
1223
1224 if (edid && edid->type == ACPI_TYPE_BUFFER) {
1225 for (i = 0; i < edid->buffer.length; i++)
1226 seq_putc(seq, edid->buffer.pointer[i]);
1227 }
1228
4be44fcd 1229 out:
1da177e4
LT
1230 if (!edid)
1231 seq_printf(seq, "<not supported>\n");
1232 else
1233 kfree(edid);
1234
d550d98d 1235 return 0;
1da177e4
LT
1236}
1237
1238static int
4be44fcd 1239acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file)
1da177e4
LT
1240{
1241 return single_open(file, acpi_video_device_EDID_seq_show,
1242 PDE(inode)->data);
1243}
1244
4be44fcd 1245static int acpi_video_device_add_fs(struct acpi_device *device)
1da177e4 1246{
251cb0bc 1247 struct proc_dir_entry *entry, *device_dir;
1da177e4
LT
1248 struct acpi_video_device *vid_dev;
1249
50dd0969 1250 vid_dev = acpi_driver_data(device);
1da177e4 1251 if (!vid_dev)
d550d98d 1252 return -ENODEV;
1da177e4 1253
251cb0bc
DT
1254 device_dir = proc_mkdir(acpi_device_bid(device),
1255 vid_dev->video->dir);
1256 if (!device_dir)
1257 return -ENOMEM;
1258
1da177e4 1259 /* 'info' [R] */
e0066c4e 1260 entry = proc_create_data("info", S_IRUGO, device_dir,
cf7acfab 1261 &acpi_video_device_info_fops, acpi_driver_data(device));
1da177e4 1262 if (!entry)
251cb0bc 1263 goto err_remove_dir;
1da177e4
LT
1264
1265 /* 'state' [R/W] */
cf7acfab 1266 entry = proc_create_data("state", S_IFREG | S_IRUGO | S_IWUSR,
e0066c4e 1267 device_dir,
cf7acfab
DL
1268 &acpi_video_device_state_fops,
1269 acpi_driver_data(device));
1da177e4 1270 if (!entry)
251cb0bc 1271 goto err_remove_info;
1da177e4
LT
1272
1273 /* 'brightness' [R/W] */
cf7acfab 1274 entry = proc_create_data("brightness", S_IFREG | S_IRUGO | S_IWUSR,
e0066c4e 1275 device_dir,
cf7acfab
DL
1276 &acpi_video_device_brightness_fops,
1277 acpi_driver_data(device));
1da177e4 1278 if (!entry)
251cb0bc 1279 goto err_remove_state;
1da177e4
LT
1280
1281 /* 'EDID' [R] */
e0066c4e 1282 entry = proc_create_data("EDID", S_IRUGO, device_dir,
cf7acfab
DL
1283 &acpi_video_device_EDID_fops,
1284 acpi_driver_data(device));
1da177e4 1285 if (!entry)
251cb0bc 1286 goto err_remove_brightness;
1da177e4 1287
e0066c4e
AD
1288 acpi_device_dir(device) = device_dir;
1289
d550d98d 1290 return 0;
251cb0bc
DT
1291
1292 err_remove_brightness:
1293 remove_proc_entry("brightness", device_dir);
1294 err_remove_state:
1295 remove_proc_entry("state", device_dir);
1296 err_remove_info:
1297 remove_proc_entry("info", device_dir);
1298 err_remove_dir:
1299 remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
1300 return -ENOMEM;
1da177e4
LT
1301}
1302
4be44fcd 1303static int acpi_video_device_remove_fs(struct acpi_device *device)
1da177e4
LT
1304{
1305 struct acpi_video_device *vid_dev;
251cb0bc 1306 struct proc_dir_entry *device_dir;
1da177e4 1307
50dd0969 1308 vid_dev = acpi_driver_data(device);
1da177e4 1309 if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
d550d98d 1310 return -ENODEV;
1da177e4 1311
251cb0bc
DT
1312 device_dir = acpi_device_dir(device);
1313 if (device_dir) {
1314 remove_proc_entry("info", device_dir);
1315 remove_proc_entry("state", device_dir);
1316 remove_proc_entry("brightness", device_dir);
1317 remove_proc_entry("EDID", device_dir);
4be44fcd 1318 remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
1da177e4
LT
1319 acpi_device_dir(device) = NULL;
1320 }
1321
d550d98d 1322 return 0;
1da177e4
LT
1323}
1324
1da177e4 1325/* video bus */
4be44fcd 1326static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset)
1da177e4 1327{
50dd0969 1328 struct acpi_video_bus *video = seq->private;
1da177e4 1329
1da177e4
LT
1330
1331 if (!video)
1332 goto end;
1333
1334 seq_printf(seq, "Switching heads: %s\n",
4be44fcd 1335 video->flags.multihead ? "yes" : "no");
1da177e4 1336 seq_printf(seq, "Video ROM: %s\n",
4be44fcd 1337 video->flags.rom ? "yes" : "no");
1da177e4 1338 seq_printf(seq, "Device to be POSTed on boot: %s\n",
4be44fcd 1339 video->flags.post ? "yes" : "no");
1da177e4 1340
4be44fcd 1341 end:
d550d98d 1342 return 0;
1da177e4
LT
1343}
1344
4be44fcd 1345static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file)
1da177e4 1346{
4be44fcd
LB
1347 return single_open(file, acpi_video_bus_info_seq_show,
1348 PDE(inode)->data);
1da177e4
LT
1349}
1350
4be44fcd 1351static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset)
1da177e4 1352{
50dd0969 1353 struct acpi_video_bus *video = seq->private;
1da177e4 1354
1da177e4
LT
1355
1356 if (!video)
1357 goto end;
1358
96b2dd1f 1359 printk(KERN_INFO PREFIX "Please implement %s\n", __func__);
1da177e4
LT
1360 seq_printf(seq, "<TODO>\n");
1361
4be44fcd 1362 end:
d550d98d 1363 return 0;
1da177e4
LT
1364}
1365
4be44fcd 1366static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file)
1da177e4
LT
1367{
1368 return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data);
1369}
1370
4be44fcd 1371static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
1da177e4 1372{
50dd0969 1373 struct acpi_video_bus *video = seq->private;
27663c58 1374 unsigned long long options;
4be44fcd 1375 int status;
1da177e4 1376
1da177e4
LT
1377
1378 if (!video)
1379 goto end;
1380
1381 status = acpi_video_bus_POST_options(video, &options);
1382 if (ACPI_SUCCESS(status)) {
1383 if (!(options & 1)) {
4be44fcd
LB
1384 printk(KERN_WARNING PREFIX
1385 "The motherboard VGA device is not listed as a possible POST device.\n");
1386 printk(KERN_WARNING PREFIX
98fb8fe1 1387 "This indicates a BIOS bug. Please contact the manufacturer.\n");
1da177e4 1388 }
4d939155 1389 printk(KERN_WARNING "%llx\n", options);
98fb8fe1 1390 seq_printf(seq, "can POST: <integrated video>");
1da177e4
LT
1391 if (options & 2)
1392 seq_printf(seq, " <PCI video>");
1393 if (options & 4)
1394 seq_printf(seq, " <AGP video>");
1395 seq_putc(seq, '\n');
1396 } else
1397 seq_printf(seq, "<not supported>\n");
4be44fcd 1398 end:
d550d98d 1399 return 0;
1da177e4
LT
1400}
1401
1402static int
4be44fcd 1403acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file)
1da177e4 1404{
4be44fcd
LB
1405 return single_open(file, acpi_video_bus_POST_info_seq_show,
1406 PDE(inode)->data);
1da177e4
LT
1407}
1408
4be44fcd 1409static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
1da177e4 1410{
50dd0969 1411 struct acpi_video_bus *video = seq->private;
4be44fcd 1412 int status;
27663c58 1413 unsigned long long id;
1da177e4 1414
1da177e4
LT
1415
1416 if (!video)
1417 goto end;
1418
4be44fcd 1419 status = acpi_video_bus_get_POST(video, &id);
1da177e4
LT
1420 if (!ACPI_SUCCESS(status)) {
1421 seq_printf(seq, "<not supported>\n");
1422 goto end;
1423 }
98fb8fe1 1424 seq_printf(seq, "device POSTed is <%s>\n", device_decode[id & 3]);
1da177e4 1425
4be44fcd 1426 end:
d550d98d 1427 return 0;
1da177e4
LT
1428}
1429
4be44fcd 1430static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset)
1da177e4 1431{
50dd0969 1432 struct acpi_video_bus *video = seq->private;
1da177e4 1433
1da177e4 1434
4be44fcd 1435 seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting);
1da177e4 1436
d550d98d 1437 return 0;
1da177e4
LT
1438}
1439
4be44fcd 1440static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file)
1da177e4 1441{
4be44fcd
LB
1442 return single_open(file, acpi_video_bus_POST_seq_show,
1443 PDE(inode)->data);
1da177e4
LT
1444}
1445
4be44fcd 1446static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file)
1da177e4
LT
1447{
1448 return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data);
1449}
1450
1451static ssize_t
4be44fcd
LB
1452acpi_video_bus_write_POST(struct file *file,
1453 const char __user * buffer,
1454 size_t count, loff_t * data)
1da177e4 1455{
4be44fcd 1456 int status;
50dd0969
JE
1457 struct seq_file *m = file->private_data;
1458 struct acpi_video_bus *video = m->private;
4be44fcd 1459 char str[12] = { 0 };
27663c58 1460 unsigned long long opt, options;
1da177e4 1461
1da177e4 1462
1da177e4 1463 if (!video || count + 1 > sizeof str)
d550d98d 1464 return -EINVAL;
1da177e4
LT
1465
1466 status = acpi_video_bus_POST_options(video, &options);
1467 if (!ACPI_SUCCESS(status))
d550d98d 1468 return -EINVAL;
1da177e4
LT
1469
1470 if (copy_from_user(str, buffer, count))
d550d98d 1471 return -EFAULT;
1da177e4
LT
1472
1473 str[count] = 0;
1474 opt = strtoul(str, NULL, 0);
1475 if (opt > 3)
d550d98d 1476 return -EFAULT;
1da177e4 1477
98fb8fe1 1478 /* just in case an OEM 'forgot' the motherboard... */
1da177e4
LT
1479 options |= 1;
1480
1481 if (options & (1ul << opt)) {
4be44fcd 1482 status = acpi_video_bus_set_POST(video, opt);
1da177e4 1483 if (!ACPI_SUCCESS(status))
d550d98d 1484 return -EFAULT;
1da177e4
LT
1485
1486 }
1487
d550d98d 1488 return count;
1da177e4
LT
1489}
1490
1491static ssize_t
4be44fcd
LB
1492acpi_video_bus_write_DOS(struct file *file,
1493 const char __user * buffer,
1494 size_t count, loff_t * data)
1da177e4 1495{
4be44fcd 1496 int status;
50dd0969
JE
1497 struct seq_file *m = file->private_data;
1498 struct acpi_video_bus *video = m->private;
4be44fcd
LB
1499 char str[12] = { 0 };
1500 unsigned long opt;
1da177e4 1501
1da177e4 1502
1da177e4 1503 if (!video || count + 1 > sizeof str)
d550d98d 1504 return -EINVAL;
1da177e4
LT
1505
1506 if (copy_from_user(str, buffer, count))
d550d98d 1507 return -EFAULT;
1da177e4
LT
1508
1509 str[count] = 0;
1510 opt = strtoul(str, NULL, 0);
1511 if (opt > 7)
d550d98d 1512 return -EFAULT;
1da177e4 1513
4be44fcd 1514 status = acpi_video_bus_DOS(video, opt & 0x3, (opt & 0x4) >> 2);
1da177e4
LT
1515
1516 if (!ACPI_SUCCESS(status))
d550d98d 1517 return -EFAULT;
1da177e4 1518
d550d98d 1519 return count;
1da177e4
LT
1520}
1521
4be44fcd 1522static int acpi_video_bus_add_fs(struct acpi_device *device)
1da177e4 1523{
251cb0bc
DT
1524 struct acpi_video_bus *video = acpi_driver_data(device);
1525 struct proc_dir_entry *device_dir;
1526 struct proc_dir_entry *entry;
1da177e4 1527
251cb0bc
DT
1528 device_dir = proc_mkdir(acpi_device_bid(device), acpi_video_dir);
1529 if (!device_dir)
1530 return -ENOMEM;
1da177e4 1531
1da177e4 1532 /* 'info' [R] */
e0066c4e 1533 entry = proc_create_data("info", S_IRUGO, device_dir,
cf7acfab
DL
1534 &acpi_video_bus_info_fops,
1535 acpi_driver_data(device));
1da177e4 1536 if (!entry)
251cb0bc 1537 goto err_remove_dir;
1da177e4
LT
1538
1539 /* 'ROM' [R] */
e0066c4e 1540 entry = proc_create_data("ROM", S_IRUGO, device_dir,
cf7acfab
DL
1541 &acpi_video_bus_ROM_fops,
1542 acpi_driver_data(device));
1da177e4 1543 if (!entry)
251cb0bc 1544 goto err_remove_info;
1da177e4
LT
1545
1546 /* 'POST_info' [R] */
e0066c4e 1547 entry = proc_create_data("POST_info", S_IRUGO, device_dir,
cf7acfab
DL
1548 &acpi_video_bus_POST_info_fops,
1549 acpi_driver_data(device));
1da177e4 1550 if (!entry)
251cb0bc 1551 goto err_remove_rom;
1da177e4
LT
1552
1553 /* 'POST' [R/W] */
08acd4f8 1554 entry = proc_create_data("POST", S_IFREG | S_IRUGO | S_IWUSR,
e0066c4e 1555 device_dir,
cf7acfab
DL
1556 &acpi_video_bus_POST_fops,
1557 acpi_driver_data(device));
1da177e4 1558 if (!entry)
251cb0bc 1559 goto err_remove_post_info;
1da177e4
LT
1560
1561 /* 'DOS' [R/W] */
08acd4f8 1562 entry = proc_create_data("DOS", S_IFREG | S_IRUGO | S_IWUSR,
e0066c4e 1563 device_dir,
cf7acfab
DL
1564 &acpi_video_bus_DOS_fops,
1565 acpi_driver_data(device));
1da177e4 1566 if (!entry)
251cb0bc 1567 goto err_remove_post;
1da177e4 1568
251cb0bc 1569 video->dir = acpi_device_dir(device) = device_dir;
d550d98d 1570 return 0;
251cb0bc
DT
1571
1572 err_remove_post:
1573 remove_proc_entry("POST", device_dir);
1574 err_remove_post_info:
1575 remove_proc_entry("POST_info", device_dir);
1576 err_remove_rom:
1577 remove_proc_entry("ROM", device_dir);
1578 err_remove_info:
1579 remove_proc_entry("info", device_dir);
1580 err_remove_dir:
1581 remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
1582 return -ENOMEM;
1da177e4
LT
1583}
1584
4be44fcd 1585static int acpi_video_bus_remove_fs(struct acpi_device *device)
1da177e4 1586{
251cb0bc
DT
1587 struct proc_dir_entry *device_dir = acpi_device_dir(device);
1588
1589 if (device_dir) {
1590 remove_proc_entry("info", device_dir);
1591 remove_proc_entry("ROM", device_dir);
1592 remove_proc_entry("POST_info", device_dir);
1593 remove_proc_entry("POST", device_dir);
1594 remove_proc_entry("DOS", device_dir);
4be44fcd 1595 remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
1da177e4
LT
1596 acpi_device_dir(device) = NULL;
1597 }
1598
d550d98d 1599 return 0;
1da177e4
LT
1600}
1601
1602/* --------------------------------------------------------------------------
1603 Driver Interface
1604 -------------------------------------------------------------------------- */
1605
1606/* device interface */
82cae999
RZ
1607static struct acpi_video_device_attrib*
1608acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id)
1609{
78eed028
DT
1610 struct acpi_video_enumerated_device *ids;
1611 int i;
1612
1613 for (i = 0; i < video->attached_count; i++) {
1614 ids = &video->attached_array[i];
1615 if ((ids->value.int_val & 0xffff) == device_id)
1616 return &ids->value.attrib;
1617 }
82cae999 1618
82cae999
RZ
1619 return NULL;
1620}
1da177e4
LT
1621
1622static int
4be44fcd
LB
1623acpi_video_bus_get_one_device(struct acpi_device *device,
1624 struct acpi_video_bus *video)
1da177e4 1625{
27663c58 1626 unsigned long long device_id;
973bf491 1627 int status;
4be44fcd 1628 struct acpi_video_device *data;
82cae999 1629 struct acpi_video_device_attrib* attribute;
1da177e4
LT
1630
1631 if (!device || !video)
d550d98d 1632 return -EINVAL;
1da177e4 1633
4be44fcd
LB
1634 status =
1635 acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
1da177e4
LT
1636 if (ACPI_SUCCESS(status)) {
1637
36bcbec7 1638 data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
1da177e4 1639 if (!data)
d550d98d 1640 return -ENOMEM;
1da177e4 1641
1da177e4
LT
1642 strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
1643 strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
db89b4f0 1644 device->driver_data = data;
1da177e4
LT
1645
1646 data->device_id = device_id;
1647 data->video = video;
1648 data->dev = device;
1649
82cae999
RZ
1650 attribute = acpi_video_get_device_attr(video, device_id);
1651
1652 if((attribute != NULL) && attribute->device_id_scheme) {
1653 switch (attribute->display_type) {
1654 case ACPI_VIDEO_DISPLAY_CRT:
1655 data->flags.crt = 1;
1656 break;
1657 case ACPI_VIDEO_DISPLAY_TV:
1658 data->flags.tvout = 1;
1659 break;
1660 case ACPI_VIDEO_DISPLAY_DVI:
1661 data->flags.dvi = 1;
1662 break;
1663 case ACPI_VIDEO_DISPLAY_LCD:
1664 data->flags.lcd = 1;
1665 break;
1666 default:
1667 data->flags.unknown = 1;
1668 break;
1669 }
1670 if(attribute->bios_can_detect)
1671 data->flags.bios = 1;
1672 } else
1da177e4 1673 data->flags.unknown = 1;
4be44fcd 1674
1da177e4
LT
1675 acpi_video_device_bind(video, data);
1676 acpi_video_device_find_cap(data);
1677
90130268 1678 status = acpi_install_notify_handler(device->handle,
4be44fcd
LB
1679 ACPI_DEVICE_NOTIFY,
1680 acpi_video_device_notify,
1681 data);
1da177e4 1682 if (ACPI_FAILURE(status)) {
55ac9a01
LM
1683 printk(KERN_ERR PREFIX
1684 "Error installing notify handler\n");
973bf491
YL
1685 if(data->brightness)
1686 kfree(data->brightness->levels);
1687 kfree(data->brightness);
1688 kfree(data);
1689 return -ENODEV;
1da177e4
LT
1690 }
1691
bbac81f5 1692 mutex_lock(&video->device_list_lock);
1da177e4 1693 list_add_tail(&data->entry, &video->video_device_list);
bbac81f5 1694 mutex_unlock(&video->device_list_lock);
1da177e4
LT
1695
1696 acpi_video_device_add_fs(device);
1697
d550d98d 1698 return 0;
1da177e4
LT
1699 }
1700
d550d98d 1701 return -ENOENT;
1da177e4
LT
1702}
1703
1704/*
1705 * Arg:
1706 * video : video bus device
1707 *
1708 * Return:
1709 * none
1710 *
1711 * Enumerate the video device list of the video bus,
1712 * bind the ids with the corresponding video devices
1713 * under the video bus.
4be44fcd 1714 */
1da177e4 1715
4be44fcd 1716static void acpi_video_device_rebind(struct acpi_video_bus *video)
1da177e4 1717{
ff102ea9
DT
1718 struct acpi_video_device *dev;
1719
bbac81f5 1720 mutex_lock(&video->device_list_lock);
ff102ea9
DT
1721
1722 list_for_each_entry(dev, &video->video_device_list, entry)
4be44fcd 1723 acpi_video_device_bind(video, dev);
ff102ea9 1724
bbac81f5 1725 mutex_unlock(&video->device_list_lock);
1da177e4
LT
1726}
1727
1728/*
1729 * Arg:
1730 * video : video bus device
1731 * device : video output device under the video
1732 * bus
1733 *
1734 * Return:
1735 * none
1736 *
1737 * Bind the ids with the corresponding video devices
1738 * under the video bus.
4be44fcd 1739 */
1da177e4
LT
1740
1741static void
4be44fcd
LB
1742acpi_video_device_bind(struct acpi_video_bus *video,
1743 struct acpi_video_device *device)
1da177e4 1744{
78eed028 1745 struct acpi_video_enumerated_device *ids;
4be44fcd 1746 int i;
1da177e4 1747
78eed028
DT
1748 for (i = 0; i < video->attached_count; i++) {
1749 ids = &video->attached_array[i];
1750 if (device->device_id == (ids->value.int_val & 0xffff)) {
1751 ids->bind_info = device;
1da177e4
LT
1752 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i));
1753 }
1754 }
1da177e4
LT
1755}
1756
1757/*
1758 * Arg:
1759 * video : video bus device
1760 *
1761 * Return:
1762 * < 0 : error
1763 *
1764 * Call _DOD to enumerate all devices attached to display adapter
1765 *
4be44fcd 1766 */
1da177e4
LT
1767
1768static int acpi_video_device_enumerate(struct acpi_video_bus *video)
1769{
4be44fcd
LB
1770 int status;
1771 int count;
1772 int i;
78eed028 1773 struct acpi_video_enumerated_device *active_list;
4be44fcd
LB
1774 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1775 union acpi_object *dod = NULL;
1776 union acpi_object *obj;
1da177e4 1777
90130268 1778 status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
1da177e4 1779 if (!ACPI_SUCCESS(status)) {
a6fc6720 1780 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD"));
d550d98d 1781 return status;
1da177e4
LT
1782 }
1783
50dd0969 1784 dod = buffer.pointer;
1da177e4 1785 if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
a6fc6720 1786 ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data"));
1da177e4
LT
1787 status = -EFAULT;
1788 goto out;
1789 }
1790
1791 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n",
4be44fcd 1792 dod->package.count));
1da177e4 1793
78eed028
DT
1794 active_list = kcalloc(1 + dod->package.count,
1795 sizeof(struct acpi_video_enumerated_device),
1796 GFP_KERNEL);
1797 if (!active_list) {
1da177e4
LT
1798 status = -ENOMEM;
1799 goto out;
1800 }
1801
1802 count = 0;
1803 for (i = 0; i < dod->package.count; i++) {
50dd0969 1804 obj = &dod->package.elements[i];
1da177e4
LT
1805
1806 if (obj->type != ACPI_TYPE_INTEGER) {
78eed028
DT
1807 printk(KERN_ERR PREFIX
1808 "Invalid _DOD data in element %d\n", i);
1809 continue;
1da177e4 1810 }
78eed028
DT
1811
1812 active_list[count].value.int_val = obj->integer.value;
1813 active_list[count].bind_info = NULL;
4be44fcd
LB
1814 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i,
1815 (int)obj->integer.value));
1da177e4
LT
1816 count++;
1817 }
1da177e4 1818
6044ec88 1819 kfree(video->attached_array);
4be44fcd 1820
78eed028 1821 video->attached_array = active_list;
1da177e4 1822 video->attached_count = count;
78eed028
DT
1823
1824 out:
02438d87 1825 kfree(buffer.pointer);
d550d98d 1826 return status;
1da177e4
LT
1827}
1828
4be44fcd
LB
1829static int
1830acpi_video_get_next_level(struct acpi_video_device *device,
1831 u32 level_current, u32 event)
1da177e4 1832{
63f0edfc 1833 int min, max, min_above, max_below, i, l, delta = 255;
f4715189
TT
1834 max = max_below = 0;
1835 min = min_above = 255;
63f0edfc 1836 /* Find closest level to level_current */
0a3db1ce 1837 for (i = 2; i < device->brightness->count; i++) {
63f0edfc
AS
1838 l = device->brightness->levels[i];
1839 if (abs(l - level_current) < abs(delta)) {
1840 delta = l - level_current;
1841 if (!delta)
1842 break;
1843 }
1844 }
1845 /* Ajust level_current to closest available level */
1846 level_current += delta;
0a3db1ce 1847 for (i = 2; i < device->brightness->count; i++) {
f4715189
TT
1848 l = device->brightness->levels[i];
1849 if (l < min)
1850 min = l;
1851 if (l > max)
1852 max = l;
1853 if (l < min_above && l > level_current)
1854 min_above = l;
1855 if (l > max_below && l < level_current)
1856 max_below = l;
1857 }
1858
1859 switch (event) {
1860 case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:
1861 return (level_current < max) ? min_above : min;
1862 case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:
1863 return (level_current < max) ? min_above : max;
1864 case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:
1865 return (level_current > min) ? max_below : min;
1866 case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:
1867 case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:
1868 return 0;
1869 default:
1870 return level_current;
1871 }
1da177e4
LT
1872}
1873
c8890f90 1874static int
4be44fcd 1875acpi_video_switch_brightness(struct acpi_video_device *device, int event)
1da177e4 1876{
27663c58 1877 unsigned long long level_current, level_next;
c8890f90
ZR
1878 int result = -EINVAL;
1879
469778c1 1880 if (!device->brightness)
c8890f90
ZR
1881 goto out;
1882
1883 result = acpi_video_device_lcd_get_level_current(device,
1884 &level_current);
1885 if (result)
1886 goto out;
1887
1da177e4 1888 level_next = acpi_video_get_next_level(device, level_current, event);
c8890f90 1889
24450c7a 1890 result = acpi_video_device_lcd_set_level(device, level_next);
c8890f90
ZR
1891
1892out:
1893 if (result)
1894 printk(KERN_ERR PREFIX "Failed to switch the brightness\n");
1895
1896 return result;
1da177e4
LT
1897}
1898
1899static int
4be44fcd
LB
1900acpi_video_bus_get_devices(struct acpi_video_bus *video,
1901 struct acpi_device *device)
1da177e4 1902{
4be44fcd 1903 int status = 0;
ff102ea9 1904 struct acpi_device *dev;
1da177e4
LT
1905
1906 acpi_video_device_enumerate(video);
1907
ff102ea9 1908 list_for_each_entry(dev, &device->children, node) {
1da177e4
LT
1909
1910 status = acpi_video_bus_get_one_device(dev, video);
1911 if (ACPI_FAILURE(status)) {
55ac9a01
LM
1912 printk(KERN_WARNING PREFIX
1913 "Cant attach device");
1da177e4
LT
1914 continue;
1915 }
1da177e4 1916 }
d550d98d 1917 return status;
1da177e4
LT
1918}
1919
4be44fcd 1920static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
1da177e4 1921{
031ec77b 1922 acpi_status status;
1da177e4
LT
1923 struct acpi_video_bus *video;
1924
1da177e4
LT
1925
1926 if (!device || !device->video)
d550d98d 1927 return -ENOENT;
1da177e4
LT
1928
1929 video = device->video;
1930
1da177e4
LT
1931 acpi_video_device_remove_fs(device->dev);
1932
90130268 1933 status = acpi_remove_notify_handler(device->dev->handle,
4be44fcd
LB
1934 ACPI_DEVICE_NOTIFY,
1935 acpi_video_device_notify);
599a52d1 1936 backlight_device_unregister(device->backlight);
702ed512
ZR
1937 if (device->cdev) {
1938 sysfs_remove_link(&device->dev->dev.kobj,
1939 "thermal_cooling");
1940 sysfs_remove_link(&device->cdev->device.kobj,
1941 "device");
1942 thermal_cooling_device_unregister(device->cdev);
1943 device->cdev = NULL;
1944 }
23b0f015 1945 video_output_unregister(device->output_dev);
ff102ea9 1946
d550d98d 1947 return 0;
1da177e4
LT
1948}
1949
4be44fcd 1950static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
1da177e4 1951{
4be44fcd 1952 int status;
ff102ea9 1953 struct acpi_video_device *dev, *next;
1da177e4 1954
bbac81f5 1955 mutex_lock(&video->device_list_lock);
1da177e4 1956
ff102ea9 1957 list_for_each_entry_safe(dev, next, &video->video_device_list, entry) {
1da177e4 1958
ff102ea9 1959 status = acpi_video_bus_put_one_device(dev);
4be44fcd
LB
1960 if (ACPI_FAILURE(status))
1961 printk(KERN_WARNING PREFIX
1962 "hhuuhhuu bug in acpi video driver.\n");
1da177e4 1963
ff102ea9
DT
1964 if (dev->brightness) {
1965 kfree(dev->brightness->levels);
1966 kfree(dev->brightness);
1967 }
1968 list_del(&dev->entry);
1969 kfree(dev);
1da177e4
LT
1970 }
1971
bbac81f5 1972 mutex_unlock(&video->device_list_lock);
ff102ea9 1973
d550d98d 1974 return 0;
1da177e4
LT
1975}
1976
1977/* acpi_video interface */
1978
4be44fcd 1979static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
1da177e4 1980{
a21101c4 1981 return acpi_video_bus_DOS(video, 0, 0);
1da177e4
LT
1982}
1983
4be44fcd 1984static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
1da177e4
LT
1985{
1986 return acpi_video_bus_DOS(video, 0, 1);
1987}
1988
4be44fcd 1989static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
1da177e4 1990{
50dd0969 1991 struct acpi_video_bus *video = data;
4be44fcd 1992 struct acpi_device *device = NULL;
e9dab196
LY
1993 struct input_dev *input;
1994 int keycode;
1995
1da177e4 1996 if (!video)
d550d98d 1997 return;
1da177e4 1998
e6afa0de 1999 device = video->device;
e9dab196 2000 input = video->input;
1da177e4
LT
2001
2002 switch (event) {
98fb8fe1 2003 case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch,
1da177e4 2004 * most likely via hotkey. */
14e04fb3 2005 acpi_bus_generate_proc_event(device, event, 0);
e9dab196 2006 keycode = KEY_SWITCHVIDEOMODE;
1da177e4
LT
2007 break;
2008
98fb8fe1 2009 case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video
1da177e4
LT
2010 * connector. */
2011 acpi_video_device_enumerate(video);
2012 acpi_video_device_rebind(video);
14e04fb3 2013 acpi_bus_generate_proc_event(device, event, 0);
e9dab196 2014 keycode = KEY_SWITCHVIDEOMODE;
1da177e4
LT
2015 break;
2016
4be44fcd 2017 case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */
25c87f7f 2018 acpi_bus_generate_proc_event(device, event, 0);
e9dab196
LY
2019 keycode = KEY_SWITCHVIDEOMODE;
2020 break;
4be44fcd 2021 case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */
25c87f7f 2022 acpi_bus_generate_proc_event(device, event, 0);
e9dab196
LY
2023 keycode = KEY_VIDEO_NEXT;
2024 break;
4be44fcd 2025 case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */
14e04fb3 2026 acpi_bus_generate_proc_event(device, event, 0);
e9dab196 2027 keycode = KEY_VIDEO_PREV;
1da177e4
LT
2028 break;
2029
2030 default:
e9dab196 2031 keycode = KEY_UNKNOWN;
1da177e4 2032 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
4be44fcd 2033 "Unsupported event [0x%x]\n", event));
1da177e4
LT
2034 break;
2035 }
2036
7761f638 2037 acpi_notifier_call_chain(device, event, 0);
e9dab196
LY
2038 input_report_key(input, keycode, 1);
2039 input_sync(input);
2040 input_report_key(input, keycode, 0);
2041 input_sync(input);
2042
d550d98d 2043 return;
1da177e4
LT
2044}
2045
4be44fcd 2046static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
1da177e4 2047{
50dd0969 2048 struct acpi_video_device *video_device = data;
4be44fcd 2049 struct acpi_device *device = NULL;
e9dab196
LY
2050 struct acpi_video_bus *bus;
2051 struct input_dev *input;
2052 int keycode;
1da177e4 2053
1da177e4 2054 if (!video_device)
d550d98d 2055 return;
1da177e4 2056
e6afa0de 2057 device = video_device->dev;
e9dab196
LY
2058 bus = video_device->video;
2059 input = bus->input;
1da177e4
LT
2060
2061 switch (event) {
4be44fcd 2062 case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */
8a681a4d
ZR
2063 if (brightness_switch_enabled)
2064 acpi_video_switch_brightness(video_device, event);
14e04fb3 2065 acpi_bus_generate_proc_event(device, event, 0);
e9dab196
LY
2066 keycode = KEY_BRIGHTNESS_CYCLE;
2067 break;
4be44fcd 2068 case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */
8a681a4d
ZR
2069 if (brightness_switch_enabled)
2070 acpi_video_switch_brightness(video_device, event);
25c87f7f 2071 acpi_bus_generate_proc_event(device, event, 0);
e9dab196
LY
2072 keycode = KEY_BRIGHTNESSUP;
2073 break;
4be44fcd 2074 case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */
8a681a4d
ZR
2075 if (brightness_switch_enabled)
2076 acpi_video_switch_brightness(video_device, event);
25c87f7f 2077 acpi_bus_generate_proc_event(device, event, 0);
e9dab196
LY
2078 keycode = KEY_BRIGHTNESSDOWN;
2079 break;
4be44fcd 2080 case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightnesss */
8a681a4d
ZR
2081 if (brightness_switch_enabled)
2082 acpi_video_switch_brightness(video_device, event);
25c87f7f 2083 acpi_bus_generate_proc_event(device, event, 0);
e9dab196
LY
2084 keycode = KEY_BRIGHTNESS_ZERO;
2085 break;
4be44fcd 2086 case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */
8a681a4d
ZR
2087 if (brightness_switch_enabled)
2088 acpi_video_switch_brightness(video_device, event);
14e04fb3 2089 acpi_bus_generate_proc_event(device, event, 0);
e9dab196 2090 keycode = KEY_DISPLAY_OFF;
1da177e4
LT
2091 break;
2092 default:
e9dab196 2093 keycode = KEY_UNKNOWN;
1da177e4 2094 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
4be44fcd 2095 "Unsupported event [0x%x]\n", event));
1da177e4
LT
2096 break;
2097 }
e9dab196 2098
7761f638 2099 acpi_notifier_call_chain(device, event, 0);
e9dab196
LY
2100 input_report_key(input, keycode, 1);
2101 input_sync(input);
2102 input_report_key(input, keycode, 0);
2103 input_sync(input);
2104
d550d98d 2105 return;
1da177e4
LT
2106}
2107
e6d9da1d 2108static int instance;
863c1490
MG
2109static int acpi_video_resume(struct acpi_device *device)
2110{
2111 struct acpi_video_bus *video;
2112 struct acpi_video_device *video_device;
2113 int i;
2114
2115 if (!device || !acpi_driver_data(device))
2116 return -EINVAL;
2117
2118 video = acpi_driver_data(device);
2119
2120 for (i = 0; i < video->attached_count; i++) {
2121 video_device = video->attached_array[i].bind_info;
2122 if (video_device && video_device->backlight)
2123 acpi_video_set_brightness(video_device->backlight);
2124 }
2125 return AE_OK;
2126}
2127
4be44fcd 2128static int acpi_video_bus_add(struct acpi_device *device)
1da177e4 2129{
f51e8391
DT
2130 acpi_status status;
2131 struct acpi_video_bus *video;
e9dab196 2132 struct input_dev *input;
f51e8391 2133 int error;
1da177e4 2134
36bcbec7 2135 video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
1da177e4 2136 if (!video)
d550d98d 2137 return -ENOMEM;
1da177e4 2138
e6d9da1d
ZR
2139 /* a hack to fix the duplicate name "VID" problem on T61 */
2140 if (!strcmp(device->pnp.bus_id, "VID")) {
2141 if (instance)
2142 device->pnp.bus_id[3] = '0' + instance;
2143 instance ++;
2144 }
f3b39f13
ZY
2145 /* a hack to fix the duplicate name "VGA" problem on Pa 3553 */
2146 if (!strcmp(device->pnp.bus_id, "VGA")) {
2147 if (instance)
2148 device->pnp.bus_id[3] = '0' + instance;
2149 instance++;
2150 }
e6d9da1d 2151
e6afa0de 2152 video->device = device;
1da177e4
LT
2153 strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
2154 strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
db89b4f0 2155 device->driver_data = video;
1da177e4
LT
2156
2157 acpi_video_bus_find_cap(video);
f51e8391
DT
2158 error = acpi_video_bus_check(video);
2159 if (error)
2160 goto err_free_video;
1da177e4 2161
f51e8391
DT
2162 error = acpi_video_bus_add_fs(device);
2163 if (error)
2164 goto err_free_video;
1da177e4 2165
bbac81f5 2166 mutex_init(&video->device_list_lock);
1da177e4
LT
2167 INIT_LIST_HEAD(&video->video_device_list);
2168
2169 acpi_video_bus_get_devices(video, device);
2170 acpi_video_bus_start_devices(video);
2171
90130268 2172 status = acpi_install_notify_handler(device->handle,
4be44fcd
LB
2173 ACPI_DEVICE_NOTIFY,
2174 acpi_video_bus_notify, video);
1da177e4 2175 if (ACPI_FAILURE(status)) {
55ac9a01
LM
2176 printk(KERN_ERR PREFIX
2177 "Error installing notify handler\n");
f51e8391
DT
2178 error = -ENODEV;
2179 goto err_stop_video;
1da177e4
LT
2180 }
2181
e9dab196 2182 video->input = input = input_allocate_device();
f51e8391
DT
2183 if (!input) {
2184 error = -ENOMEM;
2185 goto err_uninstall_notify;
2186 }
e9dab196
LY
2187
2188 snprintf(video->phys, sizeof(video->phys),
2189 "%s/video/input0", acpi_device_hid(video->device));
2190
2191 input->name = acpi_device_name(video->device);
2192 input->phys = video->phys;
2193 input->id.bustype = BUS_HOST;
2194 input->id.product = 0x06;
91c05c66 2195 input->dev.parent = &device->dev;
e9dab196
LY
2196 input->evbit[0] = BIT(EV_KEY);
2197 set_bit(KEY_SWITCHVIDEOMODE, input->keybit);
2198 set_bit(KEY_VIDEO_NEXT, input->keybit);
2199 set_bit(KEY_VIDEO_PREV, input->keybit);
2200 set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit);
2201 set_bit(KEY_BRIGHTNESSUP, input->keybit);
2202 set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
2203 set_bit(KEY_BRIGHTNESS_ZERO, input->keybit);
2204 set_bit(KEY_DISPLAY_OFF, input->keybit);
2205 set_bit(KEY_UNKNOWN, input->keybit);
e9dab196 2206
f51e8391
DT
2207 error = input_register_device(input);
2208 if (error)
2209 goto err_free_input_dev;
e9dab196 2210
1da177e4 2211 printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n",
4be44fcd
LB
2212 ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
2213 video->flags.multihead ? "yes" : "no",
2214 video->flags.rom ? "yes" : "no",
2215 video->flags.post ? "yes" : "no");
1da177e4 2216
f51e8391
DT
2217 return 0;
2218
2219 err_free_input_dev:
2220 input_free_device(input);
2221 err_uninstall_notify:
2222 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
2223 acpi_video_bus_notify);
2224 err_stop_video:
2225 acpi_video_bus_stop_devices(video);
2226 acpi_video_bus_put_devices(video);
2227 kfree(video->attached_array);
2228 acpi_video_bus_remove_fs(device);
2229 err_free_video:
2230 kfree(video);
db89b4f0 2231 device->driver_data = NULL;
1da177e4 2232
f51e8391 2233 return error;
1da177e4
LT
2234}
2235
4be44fcd 2236static int acpi_video_bus_remove(struct acpi_device *device, int type)
1da177e4 2237{
4be44fcd
LB
2238 acpi_status status = 0;
2239 struct acpi_video_bus *video = NULL;
1da177e4 2240
1da177e4
LT
2241
2242 if (!device || !acpi_driver_data(device))
d550d98d 2243 return -EINVAL;
1da177e4 2244
50dd0969 2245 video = acpi_driver_data(device);
1da177e4
LT
2246
2247 acpi_video_bus_stop_devices(video);
2248
90130268 2249 status = acpi_remove_notify_handler(video->device->handle,
4be44fcd
LB
2250 ACPI_DEVICE_NOTIFY,
2251 acpi_video_bus_notify);
1da177e4
LT
2252
2253 acpi_video_bus_put_devices(video);
2254 acpi_video_bus_remove_fs(device);
2255
e9dab196 2256 input_unregister_device(video->input);
6044ec88 2257 kfree(video->attached_array);
1da177e4
LT
2258 kfree(video);
2259
d550d98d 2260 return 0;
1da177e4
LT
2261}
2262
74a365b3
MG
2263static int __init intel_opregion_present(void)
2264{
2265#if defined(CONFIG_DRM_I915) || defined(CONFIG_DRM_I915_MODULE)
2266 struct pci_dev *dev = NULL;
2267 u32 address;
2268
2269 for_each_pci_dev(dev) {
2270 if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
2271 continue;
2272 if (dev->vendor != PCI_VENDOR_ID_INTEL)
2273 continue;
2274 pci_read_config_dword(dev, 0xfc, &address);
2275 if (!address)
2276 continue;
2277 return 1;
2278 }
2279#endif
2280 return 0;
2281}
2282
2283int acpi_video_register(void)
1da177e4 2284{
4be44fcd 2285 int result = 0;
1da177e4 2286
1da177e4
LT
2287 acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
2288 if (!acpi_video_dir)
d550d98d 2289 return -ENODEV;
1da177e4
LT
2290
2291 result = acpi_bus_register_driver(&acpi_video_bus);
2292 if (result < 0) {
2293 remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
d550d98d 2294 return -ENODEV;
1da177e4
LT
2295 }
2296
d550d98d 2297 return 0;
1da177e4 2298}
74a365b3
MG
2299EXPORT_SYMBOL(acpi_video_register);
2300
2301/*
2302 * This is kind of nasty. Hardware using Intel chipsets may require
2303 * the video opregion code to be run first in order to initialise
2304 * state before any ACPI video calls are made. To handle this we defer
2305 * registration of the video class until the opregion code has run.
2306 */
2307
2308static int __init acpi_video_init(void)
2309{
2310 if (intel_opregion_present())
2311 return 0;
2312
2313 return acpi_video_register();
2314}
1da177e4 2315
4be44fcd 2316static void __exit acpi_video_exit(void)
1da177e4 2317{
1da177e4
LT
2318
2319 acpi_bus_unregister_driver(&acpi_video_bus);
2320
2321 remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
2322
d550d98d 2323 return;
1da177e4
LT
2324}
2325
2326module_init(acpi_video_init);
2327module_exit(acpi_video_exit);