diff options
Diffstat (limited to 'drivers/acpi/video.c')
-rw-r--r-- | drivers/acpi/video.c | 1989 |
1 files changed, 1989 insertions, 0 deletions
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c new file mode 100644 index 000000000000..71fa1011715f --- /dev/null +++ b/drivers/acpi/video.c | |||
@@ -0,0 +1,1989 @@ | |||
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> | ||
6 | * | ||
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or (at | ||
12 | * your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
21 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
22 | * | ||
23 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/list.h> | ||
31 | #include <linux/proc_fs.h> | ||
32 | #include <linux/seq_file.h> | ||
33 | |||
34 | #include <asm/uaccess.h> | ||
35 | |||
36 | #include <acpi/acpi_bus.h> | ||
37 | #include <acpi/acpi_drivers.h> | ||
38 | |||
39 | #define ACPI_VIDEO_COMPONENT 0x08000000 | ||
40 | #define ACPI_VIDEO_CLASS "video" | ||
41 | #define ACPI_VIDEO_DRIVER_NAME "ACPI Video Driver" | ||
42 | #define ACPI_VIDEO_BUS_NAME "Video Bus" | ||
43 | #define ACPI_VIDEO_DEVICE_NAME "Video Device" | ||
44 | #define ACPI_VIDEO_NOTIFY_SWITCH 0x80 | ||
45 | #define ACPI_VIDEO_NOTIFY_PROBE 0x81 | ||
46 | #define ACPI_VIDEO_NOTIFY_CYCLE 0x82 | ||
47 | #define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT 0x83 | ||
48 | #define ACPI_VIDEO_NOTIFY_PREV_OUTPUT 0x84 | ||
49 | |||
50 | #define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS 0x82 | ||
51 | #define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x83 | ||
52 | #define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x84 | ||
53 | #define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS 0x85 | ||
54 | #define ACPI_VIDEO_NOTIFY_DISPLAY_OFF 0x86 | ||
55 | |||
56 | |||
57 | #define ACPI_VIDEO_HEAD_INVALID (~0u - 1) | ||
58 | #define ACPI_VIDEO_HEAD_END (~0u) | ||
59 | |||
60 | |||
61 | #define _COMPONENT ACPI_VIDEO_COMPONENT | ||
62 | ACPI_MODULE_NAME ("acpi_video") | ||
63 | |||
64 | MODULE_AUTHOR("Bruno Ducrot"); | ||
65 | MODULE_DESCRIPTION(ACPI_VIDEO_DRIVER_NAME); | ||
66 | MODULE_LICENSE("GPL"); | ||
67 | |||
68 | static int acpi_video_bus_add (struct acpi_device *device); | ||
69 | static int acpi_video_bus_remove (struct acpi_device *device, int type); | ||
70 | static int acpi_video_bus_match (struct acpi_device *device, struct acpi_driver *driver); | ||
71 | |||
72 | static struct acpi_driver acpi_video_bus = { | ||
73 | .name = ACPI_VIDEO_DRIVER_NAME, | ||
74 | .class = ACPI_VIDEO_CLASS, | ||
75 | .ops = { | ||
76 | .add = acpi_video_bus_add, | ||
77 | .remove = acpi_video_bus_remove, | ||
78 | .match = acpi_video_bus_match, | ||
79 | }, | ||
80 | }; | ||
81 | |||
82 | struct acpi_video_bus_flags { | ||
83 | u8 multihead:1; /* can switch video heads */ | ||
84 | u8 rom:1; /* can retrieve a video rom */ | ||
85 | u8 post:1; /* can configure the head to */ | ||
86 | u8 reserved:5; | ||
87 | }; | ||
88 | |||
89 | struct acpi_video_bus_cap { | ||
90 | u8 _DOS:1; /*Enable/Disable output switching*/ | ||
91 | u8 _DOD:1; /*Enumerate all devices attached to display adapter*/ | ||
92 | u8 _ROM:1; /*Get ROM Data*/ | ||
93 | u8 _GPD:1; /*Get POST Device*/ | ||
94 | u8 _SPD:1; /*Set POST Device*/ | ||
95 | u8 _VPO:1; /*Video POST Options*/ | ||
96 | u8 reserved:2; | ||
97 | }; | ||
98 | |||
99 | struct acpi_video_device_attrib{ | ||
100 | u32 display_index:4; /* A zero-based instance of the Display*/ | ||
101 | u32 display_port_attachment:4; /*This field differenates displays type*/ | ||
102 | u32 display_type:4; /*Describe the specific type in use*/ | ||
103 | u32 vendor_specific:4; /*Chipset Vendor Specifi*/ | ||
104 | u32 bios_can_detect:1; /*BIOS can detect the device*/ | ||
105 | u32 depend_on_vga:1; /*Non-VGA output device whose power is related to | ||
106 | the VGA device.*/ | ||
107 | u32 pipe_id:3; /*For VGA multiple-head devices.*/ | ||
108 | u32 reserved:10; /*Must be 0*/ | ||
109 | u32 device_id_scheme:1; /*Device ID Scheme*/ | ||
110 | }; | ||
111 | |||
112 | struct acpi_video_enumerated_device { | ||
113 | union { | ||
114 | u32 int_val; | ||
115 | struct acpi_video_device_attrib attrib; | ||
116 | } value; | ||
117 | struct acpi_video_device *bind_info; | ||
118 | }; | ||
119 | |||
120 | struct acpi_video_bus { | ||
121 | acpi_handle handle; | ||
122 | u8 dos_setting; | ||
123 | struct acpi_video_enumerated_device *attached_array; | ||
124 | u8 attached_count; | ||
125 | struct acpi_video_bus_cap cap; | ||
126 | struct acpi_video_bus_flags flags; | ||
127 | struct semaphore sem; | ||
128 | struct list_head video_device_list; | ||
129 | struct proc_dir_entry *dir; | ||
130 | }; | ||
131 | |||
132 | struct acpi_video_device_flags { | ||
133 | u8 crt:1; | ||
134 | u8 lcd:1; | ||
135 | u8 tvout:1; | ||
136 | u8 bios:1; | ||
137 | u8 unknown:1; | ||
138 | u8 reserved:3; | ||
139 | }; | ||
140 | |||
141 | struct acpi_video_device_cap { | ||
142 | u8 _ADR:1; /*Return the unique ID */ | ||
143 | u8 _BCL:1; /*Query list of brightness control levels supported*/ | ||
144 | u8 _BCM:1; /*Set the brightness level*/ | ||
145 | u8 _DDC:1; /*Return the EDID for this device*/ | ||
146 | u8 _DCS:1; /*Return status of output device*/ | ||
147 | u8 _DGS:1; /*Query graphics state*/ | ||
148 | u8 _DSS:1; /*Device state set*/ | ||
149 | u8 _reserved:1; | ||
150 | }; | ||
151 | |||
152 | struct acpi_video_device_brightness { | ||
153 | int curr; | ||
154 | int count; | ||
155 | int *levels; | ||
156 | }; | ||
157 | |||
158 | struct acpi_video_device { | ||
159 | acpi_handle handle; | ||
160 | unsigned long device_id; | ||
161 | struct acpi_video_device_flags flags; | ||
162 | struct acpi_video_device_cap cap; | ||
163 | struct list_head entry; | ||
164 | struct acpi_video_bus *video; | ||
165 | struct acpi_device *dev; | ||
166 | struct acpi_video_device_brightness *brightness; | ||
167 | }; | ||
168 | |||
169 | |||
170 | /* bus */ | ||
171 | static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file); | ||
172 | static struct file_operations acpi_video_bus_info_fops = { | ||
173 | .open = acpi_video_bus_info_open_fs, | ||
174 | .read = seq_read, | ||
175 | .llseek = seq_lseek, | ||
176 | .release = single_release, | ||
177 | }; | ||
178 | |||
179 | static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file); | ||
180 | static struct file_operations acpi_video_bus_ROM_fops = { | ||
181 | .open = acpi_video_bus_ROM_open_fs, | ||
182 | .read = seq_read, | ||
183 | .llseek = seq_lseek, | ||
184 | .release = single_release, | ||
185 | }; | ||
186 | |||
187 | static int acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file); | ||
188 | static struct file_operations acpi_video_bus_POST_info_fops = { | ||
189 | .open = acpi_video_bus_POST_info_open_fs, | ||
190 | .read = seq_read, | ||
191 | .llseek = seq_lseek, | ||
192 | .release = single_release, | ||
193 | }; | ||
194 | |||
195 | static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file); | ||
196 | static struct file_operations acpi_video_bus_POST_fops = { | ||
197 | .open = acpi_video_bus_POST_open_fs, | ||
198 | .read = seq_read, | ||
199 | .llseek = seq_lseek, | ||
200 | .release = single_release, | ||
201 | }; | ||
202 | |||
203 | |||
204 | static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file); | ||
205 | static struct file_operations acpi_video_bus_DOS_fops = { | ||
206 | .open = acpi_video_bus_DOS_open_fs, | ||
207 | .read = seq_read, | ||
208 | .llseek = seq_lseek, | ||
209 | .release = single_release, | ||
210 | }; | ||
211 | |||
212 | /* device */ | ||
213 | static int acpi_video_device_info_open_fs(struct inode *inode, struct file *file); | ||
214 | static struct file_operations acpi_video_device_info_fops = { | ||
215 | .open = acpi_video_device_info_open_fs, | ||
216 | .read = seq_read, | ||
217 | .llseek = seq_lseek, | ||
218 | .release = single_release, | ||
219 | }; | ||
220 | |||
221 | static int acpi_video_device_state_open_fs(struct inode *inode, struct file *file); | ||
222 | static struct file_operations acpi_video_device_state_fops = { | ||
223 | .open = acpi_video_device_state_open_fs, | ||
224 | .read = seq_read, | ||
225 | .llseek = seq_lseek, | ||
226 | .release = single_release, | ||
227 | }; | ||
228 | |||
229 | static int acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file); | ||
230 | static struct file_operations acpi_video_device_brightness_fops = { | ||
231 | .open = acpi_video_device_brightness_open_fs, | ||
232 | .read = seq_read, | ||
233 | .llseek = seq_lseek, | ||
234 | .release = single_release, | ||
235 | }; | ||
236 | |||
237 | static int acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file); | ||
238 | static struct file_operations acpi_video_device_EDID_fops = { | ||
239 | .open = acpi_video_device_EDID_open_fs, | ||
240 | .read = seq_read, | ||
241 | .llseek = seq_lseek, | ||
242 | .release = single_release, | ||
243 | }; | ||
244 | |||
245 | static char device_decode[][30] = { | ||
246 | "motherboard VGA device", | ||
247 | "PCI VGA device", | ||
248 | "AGP VGA device", | ||
249 | "UNKNOWN", | ||
250 | }; | ||
251 | |||
252 | static void acpi_video_device_notify ( acpi_handle handle, u32 event, void *data); | ||
253 | static void acpi_video_device_rebind( struct acpi_video_bus *video); | ||
254 | static void acpi_video_device_bind( struct acpi_video_bus *video, struct acpi_video_device *device); | ||
255 | static int acpi_video_device_enumerate(struct acpi_video_bus *video); | ||
256 | static int acpi_video_switch_output( struct acpi_video_bus *video, int event); | ||
257 | static int acpi_video_get_next_level( struct acpi_video_device *device, u32 level_current,u32 event); | ||
258 | static void acpi_video_switch_brightness ( struct acpi_video_device *device, int event); | ||
259 | |||
260 | |||
261 | /* -------------------------------------------------------------------------- | ||
262 | Video Management | ||
263 | -------------------------------------------------------------------------- */ | ||
264 | |||
265 | /* device */ | ||
266 | |||
267 | static int | ||
268 | acpi_video_device_query ( | ||
269 | struct acpi_video_device *device, | ||
270 | unsigned long *state) | ||
271 | { | ||
272 | int status; | ||
273 | ACPI_FUNCTION_TRACE("acpi_video_device_query"); | ||
274 | status = acpi_evaluate_integer(device->handle, "_DGS", NULL, state); | ||
275 | |||
276 | return_VALUE(status); | ||
277 | } | ||
278 | |||
279 | static int | ||
280 | acpi_video_device_get_state ( | ||
281 | struct acpi_video_device *device, | ||
282 | unsigned long *state) | ||
283 | { | ||
284 | int status; | ||
285 | |||
286 | ACPI_FUNCTION_TRACE("acpi_video_device_get_state"); | ||
287 | |||
288 | status = acpi_evaluate_integer(device->handle, "_DCS", NULL, state); | ||
289 | |||
290 | return_VALUE(status); | ||
291 | } | ||
292 | |||
293 | static int | ||
294 | acpi_video_device_set_state ( | ||
295 | struct acpi_video_device *device, | ||
296 | int state) | ||
297 | { | ||
298 | int status; | ||
299 | union acpi_object arg0 = {ACPI_TYPE_INTEGER}; | ||
300 | struct acpi_object_list args = {1, &arg0}; | ||
301 | |||
302 | ACPI_FUNCTION_TRACE("acpi_video_device_set_state"); | ||
303 | |||
304 | arg0.integer.value = state; | ||
305 | status = acpi_evaluate_integer(device->handle, "_DSS", &args, NULL); | ||
306 | |||
307 | return_VALUE(status); | ||
308 | } | ||
309 | |||
310 | static int | ||
311 | acpi_video_device_lcd_query_levels ( | ||
312 | struct acpi_video_device *device, | ||
313 | union acpi_object **levels) | ||
314 | { | ||
315 | int status; | ||
316 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
317 | union acpi_object *obj; | ||
318 | |||
319 | |||
320 | ACPI_FUNCTION_TRACE("acpi_video_device_lcd_query_levels"); | ||
321 | |||
322 | *levels = NULL; | ||
323 | |||
324 | status = acpi_evaluate_object(device->handle, "_BCL", NULL, &buffer); | ||
325 | if (!ACPI_SUCCESS(status)) | ||
326 | return_VALUE(status); | ||
327 | obj = (union acpi_object *) buffer.pointer; | ||
328 | if (!obj && (obj->type != ACPI_TYPE_PACKAGE)) { | ||
329 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _BCL data\n")); | ||
330 | status = -EFAULT; | ||
331 | goto err; | ||
332 | } | ||
333 | |||
334 | *levels = obj; | ||
335 | |||
336 | return_VALUE(0); | ||
337 | |||
338 | err: | ||
339 | if (buffer.pointer) | ||
340 | kfree(buffer.pointer); | ||
341 | |||
342 | return_VALUE(status); | ||
343 | } | ||
344 | |||
345 | static int | ||
346 | acpi_video_device_lcd_set_level ( | ||
347 | struct acpi_video_device *device, | ||
348 | int level) | ||
349 | { | ||
350 | int status; | ||
351 | union acpi_object arg0 = {ACPI_TYPE_INTEGER}; | ||
352 | struct acpi_object_list args = {1, &arg0}; | ||
353 | |||
354 | ACPI_FUNCTION_TRACE("acpi_video_device_lcd_set_level"); | ||
355 | |||
356 | arg0.integer.value = level; | ||
357 | status = acpi_evaluate_object(device->handle, "_BCM", &args, NULL); | ||
358 | |||
359 | printk(KERN_DEBUG "set_level status: %x\n", status); | ||
360 | return_VALUE(status); | ||
361 | } | ||
362 | |||
363 | static int | ||
364 | acpi_video_device_lcd_get_level_current ( | ||
365 | struct acpi_video_device *device, | ||
366 | unsigned long *level) | ||
367 | { | ||
368 | int status; | ||
369 | ACPI_FUNCTION_TRACE("acpi_video_device_lcd_get_level_current"); | ||
370 | |||
371 | status = acpi_evaluate_integer(device->handle, "_BQC", NULL, level); | ||
372 | |||
373 | return_VALUE(status); | ||
374 | } | ||
375 | |||
376 | static int | ||
377 | acpi_video_device_EDID ( | ||
378 | struct acpi_video_device *device, | ||
379 | union acpi_object **edid, | ||
380 | ssize_t length) | ||
381 | { | ||
382 | int status; | ||
383 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
384 | union acpi_object *obj; | ||
385 | union acpi_object arg0 = {ACPI_TYPE_INTEGER}; | ||
386 | struct acpi_object_list args = {1, &arg0}; | ||
387 | |||
388 | ACPI_FUNCTION_TRACE("acpi_video_device_get_EDID"); | ||
389 | |||
390 | *edid = NULL; | ||
391 | |||
392 | if (!device) | ||
393 | return_VALUE(-ENODEV); | ||
394 | if (length == 128) | ||
395 | arg0.integer.value = 1; | ||
396 | else if (length == 256) | ||
397 | arg0.integer.value = 2; | ||
398 | else | ||
399 | return_VALUE(-EINVAL); | ||
400 | |||
401 | status = acpi_evaluate_object(device->handle, "_DDC", &args, &buffer); | ||
402 | if (ACPI_FAILURE(status)) | ||
403 | return_VALUE(-ENODEV); | ||
404 | |||
405 | obj = (union acpi_object *) buffer.pointer; | ||
406 | |||
407 | if (obj && obj->type == ACPI_TYPE_BUFFER) | ||
408 | *edid = obj; | ||
409 | else { | ||
410 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _DDC data\n")); | ||
411 | status = -EFAULT; | ||
412 | kfree(obj); | ||
413 | } | ||
414 | |||
415 | return_VALUE(status); | ||
416 | } | ||
417 | |||
418 | |||
419 | /* bus */ | ||
420 | |||
421 | static int | ||
422 | acpi_video_bus_set_POST ( | ||
423 | struct acpi_video_bus *video, | ||
424 | unsigned long option) | ||
425 | { | ||
426 | int status; | ||
427 | unsigned long tmp; | ||
428 | union acpi_object arg0 = {ACPI_TYPE_INTEGER}; | ||
429 | struct acpi_object_list args = {1, &arg0}; | ||
430 | |||
431 | ACPI_FUNCTION_TRACE("acpi_video_bus_set_POST"); | ||
432 | |||
433 | arg0.integer.value = option; | ||
434 | |||
435 | status = acpi_evaluate_integer(video->handle, "_SPD", &args, &tmp); | ||
436 | if (ACPI_SUCCESS(status)) | ||
437 | status = tmp ? (-EINVAL):(AE_OK); | ||
438 | |||
439 | return_VALUE(status); | ||
440 | } | ||
441 | |||
442 | static int | ||
443 | acpi_video_bus_get_POST ( | ||
444 | struct acpi_video_bus *video, | ||
445 | unsigned long *id) | ||
446 | { | ||
447 | int status; | ||
448 | |||
449 | ACPI_FUNCTION_TRACE("acpi_video_bus_get_POST"); | ||
450 | |||
451 | status = acpi_evaluate_integer(video->handle, "_GPD", NULL, id); | ||
452 | |||
453 | return_VALUE(status); | ||
454 | } | ||
455 | |||
456 | static int | ||
457 | acpi_video_bus_POST_options ( | ||
458 | struct acpi_video_bus *video, | ||
459 | unsigned long *options) | ||
460 | { | ||
461 | int status; | ||
462 | ACPI_FUNCTION_TRACE("acpi_video_bus_POST_options"); | ||
463 | |||
464 | status = acpi_evaluate_integer(video->handle, "_VPO", NULL, options); | ||
465 | *options &= 3; | ||
466 | |||
467 | return_VALUE(status); | ||
468 | } | ||
469 | |||
470 | /* | ||
471 | * Arg: | ||
472 | * video : video bus device pointer | ||
473 | * bios_flag : | ||
474 | * 0. The system BIOS should NOT automatically switch(toggle) | ||
475 | * the active display output. | ||
476 | * 1. The system BIOS should automatically switch (toggle) the | ||
477 | * active display output. No swich event. | ||
478 | * 2. The _DGS value should be locked. | ||
479 | * 3. The system BIOS should not automatically switch (toggle) the | ||
480 | * active display output, but instead generate the display switch | ||
481 | * event notify code. | ||
482 | * lcd_flag : | ||
483 | * 0. The system BIOS should automatically control the brightness level | ||
484 | * of the LCD, when the power changes from AC to DC | ||
485 | * 1. The system BIOS should NOT automatically control the brightness | ||
486 | * level of the LCD, when the power changes from AC to DC. | ||
487 | * Return Value: | ||
488 | * -1 wrong arg. | ||
489 | */ | ||
490 | |||
491 | static int | ||
492 | acpi_video_bus_DOS( | ||
493 | struct acpi_video_bus *video, | ||
494 | int bios_flag, | ||
495 | int lcd_flag) | ||
496 | { | ||
497 | acpi_integer status = 0; | ||
498 | union acpi_object arg0 = {ACPI_TYPE_INTEGER}; | ||
499 | struct acpi_object_list args = {1, &arg0}; | ||
500 | |||
501 | ACPI_FUNCTION_TRACE("acpi_video_bus_DOS"); | ||
502 | |||
503 | if (bios_flag < 0 || bios_flag >3 || lcd_flag < 0 || lcd_flag > 1){ | ||
504 | status = -1; | ||
505 | goto Failed; | ||
506 | } | ||
507 | arg0.integer.value = (lcd_flag << 2) | bios_flag; | ||
508 | video->dos_setting = arg0.integer.value; | ||
509 | acpi_evaluate_object(video->handle, "_DOS", &args, NULL); | ||
510 | |||
511 | Failed: | ||
512 | return_VALUE(status); | ||
513 | } | ||
514 | |||
515 | /* | ||
516 | * Arg: | ||
517 | * device : video output device (LCD, CRT, ..) | ||
518 | * | ||
519 | * Return Value: | ||
520 | * None | ||
521 | * | ||
522 | * Find out all required AML method defined under the output | ||
523 | * device. | ||
524 | */ | ||
525 | |||
526 | static void | ||
527 | acpi_video_device_find_cap (struct acpi_video_device *device) | ||
528 | { | ||
529 | acpi_integer status; | ||
530 | acpi_handle h_dummy1; | ||
531 | int i; | ||
532 | union acpi_object *obj = NULL; | ||
533 | struct acpi_video_device_brightness *br = NULL; | ||
534 | |||
535 | ACPI_FUNCTION_TRACE("acpi_video_device_find_cap"); | ||
536 | |||
537 | memset( &device->cap, 0, 4); | ||
538 | |||
539 | if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_ADR", &h_dummy1))) { | ||
540 | device->cap._ADR = 1; | ||
541 | } | ||
542 | if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_BCL", &h_dummy1))) { | ||
543 | device->cap._BCL= 1; | ||
544 | } | ||
545 | if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_BCM", &h_dummy1))) { | ||
546 | device->cap._BCM= 1; | ||
547 | } | ||
548 | if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DDC", &h_dummy1))) { | ||
549 | device->cap._DDC= 1; | ||
550 | } | ||
551 | if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DCS", &h_dummy1))) { | ||
552 | device->cap._DCS = 1; | ||
553 | } | ||
554 | if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DGS", &h_dummy1))) { | ||
555 | device->cap._DGS = 1; | ||
556 | } | ||
557 | if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DSS", &h_dummy1))) { | ||
558 | device->cap._DSS = 1; | ||
559 | } | ||
560 | |||
561 | status = acpi_video_device_lcd_query_levels(device, &obj); | ||
562 | |||
563 | if (obj && obj->type == ACPI_TYPE_PACKAGE && obj->package.count >= 2) { | ||
564 | int count = 0; | ||
565 | union acpi_object *o; | ||
566 | |||
567 | br = kmalloc(sizeof &br, GFP_KERNEL); | ||
568 | if (!br) { | ||
569 | printk(KERN_ERR "can't allocate memory\n"); | ||
570 | } else { | ||
571 | memset(br, 0, sizeof &br); | ||
572 | br->levels = kmalloc(obj->package.count * sizeof &br->levels, GFP_KERNEL); | ||
573 | if (!br->levels) | ||
574 | goto out; | ||
575 | |||
576 | for (i = 0; i < obj->package.count; i++) { | ||
577 | o = (union acpi_object *) &obj->package.elements[i]; | ||
578 | if (o->type != ACPI_TYPE_INTEGER) { | ||
579 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data\n")); | ||
580 | continue; | ||
581 | } | ||
582 | br->levels[count] = (u32) o->integer.value; | ||
583 | count++; | ||
584 | } | ||
585 | out: | ||
586 | if (count < 2) { | ||
587 | if (br->levels) | ||
588 | kfree(br->levels); | ||
589 | kfree(br); | ||
590 | } else { | ||
591 | br->count = count; | ||
592 | device->brightness = br; | ||
593 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "found %d brightness levels\n", count)); | ||
594 | } | ||
595 | } | ||
596 | } | ||
597 | |||
598 | if (obj) | ||
599 | kfree(obj); | ||
600 | |||
601 | return_VOID; | ||
602 | } | ||
603 | |||
604 | /* | ||
605 | * Arg: | ||
606 | * device : video output device (VGA) | ||
607 | * | ||
608 | * Return Value: | ||
609 | * None | ||
610 | * | ||
611 | * Find out all required AML method defined under the video bus device. | ||
612 | */ | ||
613 | |||
614 | static void | ||
615 | acpi_video_bus_find_cap (struct acpi_video_bus *video) | ||
616 | { | ||
617 | acpi_handle h_dummy1; | ||
618 | |||
619 | memset(&video->cap ,0, 4); | ||
620 | if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_DOS", &h_dummy1))) { | ||
621 | video->cap._DOS = 1; | ||
622 | } | ||
623 | if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_DOD", &h_dummy1))) { | ||
624 | video->cap._DOD = 1; | ||
625 | } | ||
626 | if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_ROM", &h_dummy1))) { | ||
627 | video->cap._ROM = 1; | ||
628 | } | ||
629 | if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_GPD", &h_dummy1))) { | ||
630 | video->cap._GPD = 1; | ||
631 | } | ||
632 | if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_SPD", &h_dummy1))) { | ||
633 | video->cap._SPD = 1; | ||
634 | } | ||
635 | if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_VPO", &h_dummy1))) { | ||
636 | video->cap._VPO = 1; | ||
637 | } | ||
638 | } | ||
639 | |||
640 | /* | ||
641 | * Check whether the video bus device has required AML method to | ||
642 | * support the desired features | ||
643 | */ | ||
644 | |||
645 | static int | ||
646 | acpi_video_bus_check ( | ||
647 | struct acpi_video_bus *video) | ||
648 | { | ||
649 | acpi_status status = -ENOENT; | ||
650 | |||
651 | |||
652 | ACPI_FUNCTION_TRACE("acpi_video_bus_check"); | ||
653 | |||
654 | if (!video) | ||
655 | return_VALUE(-EINVAL); | ||
656 | |||
657 | /* Since there is no HID, CID and so on for VGA driver, we have | ||
658 | * to check well known required nodes. | ||
659 | */ | ||
660 | |||
661 | /* Does this device able to support video switching ? */ | ||
662 | if(video->cap._DOS){ | ||
663 | video->flags.multihead = 1; | ||
664 | status = 0; | ||
665 | } | ||
666 | |||
667 | /* Does this device able to retrieve a retrieve a video ROM ? */ | ||
668 | if(video->cap._ROM){ | ||
669 | video->flags.rom = 1; | ||
670 | status = 0; | ||
671 | } | ||
672 | |||
673 | /* Does this device able to configure which video device to POST ? */ | ||
674 | if(video->cap._GPD && video->cap._SPD && video->cap._VPO){ | ||
675 | video->flags.post = 1; | ||
676 | status = 0; | ||
677 | } | ||
678 | |||
679 | return_VALUE(status); | ||
680 | } | ||
681 | |||
682 | /* -------------------------------------------------------------------------- | ||
683 | FS Interface (/proc) | ||
684 | -------------------------------------------------------------------------- */ | ||
685 | |||
686 | static struct proc_dir_entry *acpi_video_dir; | ||
687 | |||
688 | /* video devices */ | ||
689 | |||
690 | static int | ||
691 | acpi_video_device_info_seq_show ( | ||
692 | struct seq_file *seq, | ||
693 | void *offset) | ||
694 | { | ||
695 | struct acpi_video_device *dev = (struct acpi_video_device *) seq->private; | ||
696 | |||
697 | ACPI_FUNCTION_TRACE("acpi_video_device_info_seq_show"); | ||
698 | |||
699 | if (!dev) | ||
700 | goto end; | ||
701 | |||
702 | seq_printf(seq, "device_id: 0x%04x\n", (u32) dev->device_id); | ||
703 | seq_printf(seq, "type: "); | ||
704 | if (dev->flags.crt) | ||
705 | seq_printf(seq, "CRT\n"); | ||
706 | else if (dev->flags.lcd) | ||
707 | seq_printf(seq, "LCD\n"); | ||
708 | else if (dev->flags.tvout) | ||
709 | seq_printf(seq, "TVOUT\n"); | ||
710 | else | ||
711 | seq_printf(seq, "UNKNOWN\n"); | ||
712 | |||
713 | seq_printf(seq,"known by bios: %s\n", | ||
714 | dev->flags.bios ? "yes":"no"); | ||
715 | |||
716 | end: | ||
717 | return_VALUE(0); | ||
718 | } | ||
719 | |||
720 | static int | ||
721 | acpi_video_device_info_open_fs ( | ||
722 | struct inode *inode, | ||
723 | struct file *file) | ||
724 | { | ||
725 | return single_open(file, acpi_video_device_info_seq_show, | ||
726 | PDE(inode)->data); | ||
727 | } | ||
728 | |||
729 | static int | ||
730 | acpi_video_device_state_seq_show ( | ||
731 | struct seq_file *seq, | ||
732 | void *offset) | ||
733 | { | ||
734 | int status; | ||
735 | struct acpi_video_device *dev = (struct acpi_video_device *) seq->private; | ||
736 | unsigned long state; | ||
737 | |||
738 | ACPI_FUNCTION_TRACE("acpi_video_device_state_seq_show"); | ||
739 | |||
740 | if (!dev) | ||
741 | goto end; | ||
742 | |||
743 | status = acpi_video_device_get_state(dev, &state); | ||
744 | seq_printf(seq, "state: "); | ||
745 | if (ACPI_SUCCESS(status)) | ||
746 | seq_printf(seq, "0x%02lx\n", state); | ||
747 | else | ||
748 | seq_printf(seq, "<not supported>\n"); | ||
749 | |||
750 | status = acpi_video_device_query(dev, &state); | ||
751 | seq_printf(seq, "query: "); | ||
752 | if (ACPI_SUCCESS(status)) | ||
753 | seq_printf(seq, "0x%02lx\n", state); | ||
754 | else | ||
755 | seq_printf(seq, "<not supported>\n"); | ||
756 | |||
757 | end: | ||
758 | return_VALUE(0); | ||
759 | } | ||
760 | |||
761 | static int | ||
762 | acpi_video_device_state_open_fs ( | ||
763 | struct inode *inode, | ||
764 | struct file *file) | ||
765 | { | ||
766 | return single_open(file, acpi_video_device_state_seq_show, | ||
767 | PDE(inode)->data); | ||
768 | } | ||
769 | |||
770 | static ssize_t | ||
771 | acpi_video_device_write_state ( | ||
772 | struct file *file, | ||
773 | const char __user *buffer, | ||
774 | size_t count, | ||
775 | loff_t *data) | ||
776 | { | ||
777 | int status; | ||
778 | struct seq_file *m = (struct seq_file *) file->private_data; | ||
779 | struct acpi_video_device *dev = (struct acpi_video_device *) m->private; | ||
780 | char str[12] = {0}; | ||
781 | u32 state = 0; | ||
782 | |||
783 | ACPI_FUNCTION_TRACE("acpi_video_device_write_state"); | ||
784 | |||
785 | if (!dev || count + 1 > sizeof str) | ||
786 | return_VALUE(-EINVAL); | ||
787 | |||
788 | if (copy_from_user(str, buffer, count)) | ||
789 | return_VALUE(-EFAULT); | ||
790 | |||
791 | str[count] = 0; | ||
792 | state = simple_strtoul(str, NULL, 0); | ||
793 | state &= ((1ul<<31) | (1ul<<30) | (1ul<<0)); | ||
794 | |||
795 | status = acpi_video_device_set_state(dev, state); | ||
796 | |||
797 | if (status) | ||
798 | return_VALUE(-EFAULT); | ||
799 | |||
800 | return_VALUE(count); | ||
801 | } | ||
802 | |||
803 | static int | ||
804 | acpi_video_device_brightness_seq_show ( | ||
805 | struct seq_file *seq, | ||
806 | void *offset) | ||
807 | { | ||
808 | struct acpi_video_device *dev = (struct acpi_video_device *) seq->private; | ||
809 | int i; | ||
810 | |||
811 | ACPI_FUNCTION_TRACE("acpi_video_device_brightness_seq_show"); | ||
812 | |||
813 | if (!dev || !dev->brightness) { | ||
814 | seq_printf(seq, "<not supported>\n"); | ||
815 | return_VALUE(0); | ||
816 | } | ||
817 | |||
818 | seq_printf(seq, "levels: "); | ||
819 | for (i = 0; i < dev->brightness->count; i++) | ||
820 | seq_printf(seq, " %d", dev->brightness->levels[i]); | ||
821 | seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr); | ||
822 | |||
823 | return_VALUE(0); | ||
824 | } | ||
825 | |||
826 | static int | ||
827 | acpi_video_device_brightness_open_fs ( | ||
828 | struct inode *inode, | ||
829 | struct file *file) | ||
830 | { | ||
831 | return single_open(file, acpi_video_device_brightness_seq_show, | ||
832 | PDE(inode)->data); | ||
833 | } | ||
834 | |||
835 | static ssize_t | ||
836 | acpi_video_device_write_brightness ( | ||
837 | struct file *file, | ||
838 | const char __user *buffer, | ||
839 | size_t count, | ||
840 | loff_t *data) | ||
841 | { | ||
842 | struct seq_file *m = (struct seq_file *) file->private_data; | ||
843 | struct acpi_video_device *dev = (struct acpi_video_device *) m->private; | ||
844 | char str[4] = {0}; | ||
845 | unsigned int level = 0; | ||
846 | int i; | ||
847 | |||
848 | ACPI_FUNCTION_TRACE("acpi_video_device_write_brightness"); | ||
849 | |||
850 | if (!dev || count + 1 > sizeof str) | ||
851 | return_VALUE(-EINVAL); | ||
852 | |||
853 | if (copy_from_user(str, buffer, count)) | ||
854 | return_VALUE(-EFAULT); | ||
855 | |||
856 | str[count] = 0; | ||
857 | level = simple_strtoul(str, NULL, 0); | ||
858 | |||
859 | if (level > 100) | ||
860 | return_VALUE(-EFAULT); | ||
861 | |||
862 | /* validate though the list of available levels */ | ||
863 | for (i = 0; i < dev->brightness->count; i++) | ||
864 | if (level == dev->brightness->levels[i]) { | ||
865 | if (ACPI_SUCCESS(acpi_video_device_lcd_set_level(dev, level))) | ||
866 | dev->brightness->curr = level; | ||
867 | break; | ||
868 | } | ||
869 | |||
870 | return_VALUE(count); | ||
871 | } | ||
872 | |||
873 | static int | ||
874 | acpi_video_device_EDID_seq_show ( | ||
875 | struct seq_file *seq, | ||
876 | void *offset) | ||
877 | { | ||
878 | struct acpi_video_device *dev = (struct acpi_video_device *) seq->private; | ||
879 | int status; | ||
880 | int i; | ||
881 | union acpi_object *edid = NULL; | ||
882 | |||
883 | ACPI_FUNCTION_TRACE("acpi_video_device_EDID_seq_show"); | ||
884 | |||
885 | if (!dev) | ||
886 | goto out; | ||
887 | |||
888 | status = acpi_video_device_EDID (dev, &edid, 128); | ||
889 | if (ACPI_FAILURE(status)) { | ||
890 | status = acpi_video_device_EDID (dev, &edid, 256); | ||
891 | } | ||
892 | |||
893 | if (ACPI_FAILURE(status)) { | ||
894 | goto out; | ||
895 | } | ||
896 | |||
897 | if (edid && edid->type == ACPI_TYPE_BUFFER) { | ||
898 | for (i = 0; i < edid->buffer.length; i++) | ||
899 | seq_putc(seq, edid->buffer.pointer[i]); | ||
900 | } | ||
901 | |||
902 | out: | ||
903 | if (!edid) | ||
904 | seq_printf(seq, "<not supported>\n"); | ||
905 | else | ||
906 | kfree(edid); | ||
907 | |||
908 | return_VALUE(0); | ||
909 | } | ||
910 | |||
911 | static int | ||
912 | acpi_video_device_EDID_open_fs ( | ||
913 | struct inode *inode, | ||
914 | struct file *file) | ||
915 | { | ||
916 | return single_open(file, acpi_video_device_EDID_seq_show, | ||
917 | PDE(inode)->data); | ||
918 | } | ||
919 | |||
920 | |||
921 | static int | ||
922 | acpi_video_device_add_fs ( | ||
923 | struct acpi_device *device) | ||
924 | { | ||
925 | struct proc_dir_entry *entry = NULL; | ||
926 | struct acpi_video_device *vid_dev; | ||
927 | |||
928 | ACPI_FUNCTION_TRACE("acpi_video_device_add_fs"); | ||
929 | |||
930 | if (!device) | ||
931 | return_VALUE(-ENODEV); | ||
932 | |||
933 | vid_dev = (struct acpi_video_device *) acpi_driver_data(device); | ||
934 | if (!vid_dev) | ||
935 | return_VALUE(-ENODEV); | ||
936 | |||
937 | if (!acpi_device_dir(device)) { | ||
938 | acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), | ||
939 | vid_dev->video->dir); | ||
940 | if (!acpi_device_dir(device)) | ||
941 | return_VALUE(-ENODEV); | ||
942 | acpi_device_dir(device)->owner = THIS_MODULE; | ||
943 | } | ||
944 | |||
945 | /* 'info' [R] */ | ||
946 | entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device)); | ||
947 | if (!entry) | ||
948 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
949 | "Unable to create 'info' fs entry\n")); | ||
950 | else { | ||
951 | entry->proc_fops = &acpi_video_device_info_fops; | ||
952 | entry->data = acpi_driver_data(device); | ||
953 | entry->owner = THIS_MODULE; | ||
954 | } | ||
955 | |||
956 | /* 'state' [R/W] */ | ||
957 | entry = create_proc_entry("state", S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); | ||
958 | if (!entry) | ||
959 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
960 | "Unable to create 'state' fs entry\n")); | ||
961 | else { | ||
962 | entry->proc_fops = &acpi_video_device_state_fops; | ||
963 | entry->proc_fops->write = acpi_video_device_write_state; | ||
964 | entry->data = acpi_driver_data(device); | ||
965 | entry->owner = THIS_MODULE; | ||
966 | } | ||
967 | |||
968 | /* 'brightness' [R/W] */ | ||
969 | entry = create_proc_entry("brightness", S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); | ||
970 | if (!entry) | ||
971 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
972 | "Unable to create 'brightness' fs entry\n")); | ||
973 | else { | ||
974 | entry->proc_fops = &acpi_video_device_brightness_fops; | ||
975 | entry->proc_fops->write = acpi_video_device_write_brightness; | ||
976 | entry->data = acpi_driver_data(device); | ||
977 | entry->owner = THIS_MODULE; | ||
978 | } | ||
979 | |||
980 | /* 'EDID' [R] */ | ||
981 | entry = create_proc_entry("EDID", S_IRUGO, acpi_device_dir(device)); | ||
982 | if (!entry) | ||
983 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
984 | "Unable to create 'brightness' fs entry\n")); | ||
985 | else { | ||
986 | entry->proc_fops = &acpi_video_device_EDID_fops; | ||
987 | entry->data = acpi_driver_data(device); | ||
988 | entry->owner = THIS_MODULE; | ||
989 | } | ||
990 | |||
991 | return_VALUE(0); | ||
992 | } | ||
993 | |||
994 | static int | ||
995 | acpi_video_device_remove_fs ( | ||
996 | struct acpi_device *device) | ||
997 | { | ||
998 | struct acpi_video_device *vid_dev; | ||
999 | ACPI_FUNCTION_TRACE("acpi_video_device_remove_fs"); | ||
1000 | |||
1001 | vid_dev = (struct acpi_video_device *) acpi_driver_data(device); | ||
1002 | if (!vid_dev || !vid_dev->video || !vid_dev->video->dir) | ||
1003 | return_VALUE(-ENODEV); | ||
1004 | |||
1005 | if (acpi_device_dir(device)) { | ||
1006 | remove_proc_entry("info", acpi_device_dir(device)); | ||
1007 | remove_proc_entry("state", acpi_device_dir(device)); | ||
1008 | remove_proc_entry("brightness", acpi_device_dir(device)); | ||
1009 | remove_proc_entry("EDID", acpi_device_dir(device)); | ||
1010 | remove_proc_entry(acpi_device_bid(device), | ||
1011 | vid_dev->video->dir); | ||
1012 | acpi_device_dir(device) = NULL; | ||
1013 | } | ||
1014 | |||
1015 | return_VALUE(0); | ||
1016 | } | ||
1017 | |||
1018 | |||
1019 | /* video bus */ | ||
1020 | static int | ||
1021 | acpi_video_bus_info_seq_show ( | ||
1022 | struct seq_file *seq, | ||
1023 | void *offset) | ||
1024 | { | ||
1025 | struct acpi_video_bus *video = (struct acpi_video_bus *) seq->private; | ||
1026 | |||
1027 | ACPI_FUNCTION_TRACE("acpi_video_bus_info_seq_show"); | ||
1028 | |||
1029 | if (!video) | ||
1030 | goto end; | ||
1031 | |||
1032 | seq_printf(seq, "Switching heads: %s\n", | ||
1033 | video->flags.multihead ? "yes":"no"); | ||
1034 | seq_printf(seq, "Video ROM: %s\n", | ||
1035 | video->flags.rom ? "yes":"no"); | ||
1036 | seq_printf(seq, "Device to be POSTed on boot: %s\n", | ||
1037 | video->flags.post ? "yes":"no"); | ||
1038 | |||
1039 | end: | ||
1040 | return_VALUE(0); | ||
1041 | } | ||
1042 | |||
1043 | static int | ||
1044 | acpi_video_bus_info_open_fs ( | ||
1045 | struct inode *inode, | ||
1046 | struct file *file) | ||
1047 | { | ||
1048 | return single_open(file, acpi_video_bus_info_seq_show, PDE(inode)->data); | ||
1049 | } | ||
1050 | |||
1051 | static int | ||
1052 | acpi_video_bus_ROM_seq_show ( | ||
1053 | struct seq_file *seq, | ||
1054 | void *offset) | ||
1055 | { | ||
1056 | struct acpi_video_bus *video = (struct acpi_video_bus *) seq->private; | ||
1057 | |||
1058 | ACPI_FUNCTION_TRACE("acpi_video_bus_ROM_seq_show"); | ||
1059 | |||
1060 | if (!video) | ||
1061 | goto end; | ||
1062 | |||
1063 | printk(KERN_INFO PREFIX "Please implement %s\n", __FUNCTION__); | ||
1064 | seq_printf(seq, "<TODO>\n"); | ||
1065 | |||
1066 | end: | ||
1067 | return_VALUE(0); | ||
1068 | } | ||
1069 | |||
1070 | static int | ||
1071 | acpi_video_bus_ROM_open_fs ( | ||
1072 | struct inode *inode, | ||
1073 | struct file *file) | ||
1074 | { | ||
1075 | return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data); | ||
1076 | } | ||
1077 | |||
1078 | static int | ||
1079 | acpi_video_bus_POST_info_seq_show ( | ||
1080 | struct seq_file *seq, | ||
1081 | void *offset) | ||
1082 | { | ||
1083 | struct acpi_video_bus *video = (struct acpi_video_bus *) seq->private; | ||
1084 | unsigned long options; | ||
1085 | int status; | ||
1086 | |||
1087 | ACPI_FUNCTION_TRACE("acpi_video_bus_POST_info_seq_show"); | ||
1088 | |||
1089 | if (!video) | ||
1090 | goto end; | ||
1091 | |||
1092 | status = acpi_video_bus_POST_options(video, &options); | ||
1093 | if (ACPI_SUCCESS(status)) { | ||
1094 | if (!(options & 1)) { | ||
1095 | printk(KERN_WARNING PREFIX "The motherboard VGA device is not listed as a possible POST device.\n"); | ||
1096 | printk(KERN_WARNING PREFIX "This indicate a BIOS bug. Please contact the manufacturer.\n"); | ||
1097 | } | ||
1098 | printk("%lx\n", options); | ||
1099 | seq_printf(seq, "can POST: <intgrated video>"); | ||
1100 | if (options & 2) | ||
1101 | seq_printf(seq, " <PCI video>"); | ||
1102 | if (options & 4) | ||
1103 | seq_printf(seq, " <AGP video>"); | ||
1104 | seq_putc(seq, '\n'); | ||
1105 | } else | ||
1106 | seq_printf(seq, "<not supported>\n"); | ||
1107 | end: | ||
1108 | return_VALUE(0); | ||
1109 | } | ||
1110 | |||
1111 | static int | ||
1112 | acpi_video_bus_POST_info_open_fs ( | ||
1113 | struct inode *inode, | ||
1114 | struct file *file) | ||
1115 | { | ||
1116 | return single_open(file, acpi_video_bus_POST_info_seq_show, PDE(inode)->data); | ||
1117 | } | ||
1118 | |||
1119 | static int | ||
1120 | acpi_video_bus_POST_seq_show ( | ||
1121 | struct seq_file *seq, | ||
1122 | void *offset) | ||
1123 | { | ||
1124 | struct acpi_video_bus *video = (struct acpi_video_bus *) seq->private; | ||
1125 | int status; | ||
1126 | unsigned long id; | ||
1127 | |||
1128 | ACPI_FUNCTION_TRACE("acpi_video_bus_POST_seq_show"); | ||
1129 | |||
1130 | if (!video) | ||
1131 | goto end; | ||
1132 | |||
1133 | status = acpi_video_bus_get_POST (video, &id); | ||
1134 | if (!ACPI_SUCCESS(status)) { | ||
1135 | seq_printf(seq, "<not supported>\n"); | ||
1136 | goto end; | ||
1137 | } | ||
1138 | seq_printf(seq, "device posted is <%s>\n", device_decode[id & 3]); | ||
1139 | |||
1140 | end: | ||
1141 | return_VALUE(0); | ||
1142 | } | ||
1143 | |||
1144 | static int | ||
1145 | acpi_video_bus_DOS_seq_show ( | ||
1146 | struct seq_file *seq, | ||
1147 | void *offset) | ||
1148 | { | ||
1149 | struct acpi_video_bus *video = (struct acpi_video_bus *) seq->private; | ||
1150 | |||
1151 | ACPI_FUNCTION_TRACE("acpi_video_bus_DOS_seq_show"); | ||
1152 | |||
1153 | seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting ); | ||
1154 | |||
1155 | return_VALUE(0); | ||
1156 | } | ||
1157 | |||
1158 | static int | ||
1159 | acpi_video_bus_POST_open_fs ( | ||
1160 | struct inode *inode, | ||
1161 | struct file *file) | ||
1162 | { | ||
1163 | return single_open(file, acpi_video_bus_POST_seq_show, PDE(inode)->data); | ||
1164 | } | ||
1165 | |||
1166 | static int | ||
1167 | acpi_video_bus_DOS_open_fs ( | ||
1168 | struct inode *inode, | ||
1169 | struct file *file) | ||
1170 | { | ||
1171 | return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data); | ||
1172 | } | ||
1173 | |||
1174 | static ssize_t | ||
1175 | acpi_video_bus_write_POST ( | ||
1176 | struct file *file, | ||
1177 | const char __user *buffer, | ||
1178 | size_t count, | ||
1179 | loff_t *data) | ||
1180 | { | ||
1181 | int status; | ||
1182 | struct seq_file *m = (struct seq_file *) file->private_data; | ||
1183 | struct acpi_video_bus *video = (struct acpi_video_bus *) m->private; | ||
1184 | char str[12] = {0}; | ||
1185 | unsigned long opt, options; | ||
1186 | |||
1187 | ACPI_FUNCTION_TRACE("acpi_video_bus_write_POST"); | ||
1188 | |||
1189 | |||
1190 | if (!video || count + 1 > sizeof str) | ||
1191 | return_VALUE(-EINVAL); | ||
1192 | |||
1193 | status = acpi_video_bus_POST_options(video, &options); | ||
1194 | if (!ACPI_SUCCESS(status)) | ||
1195 | return_VALUE(-EINVAL); | ||
1196 | |||
1197 | if (copy_from_user(str, buffer, count)) | ||
1198 | return_VALUE(-EFAULT); | ||
1199 | |||
1200 | str[count] = 0; | ||
1201 | opt = strtoul(str, NULL, 0); | ||
1202 | if (opt > 3) | ||
1203 | return_VALUE(-EFAULT); | ||
1204 | |||
1205 | /* just in case an OEM 'forget' the motherboard... */ | ||
1206 | options |= 1; | ||
1207 | |||
1208 | if (options & (1ul << opt)) { | ||
1209 | status = acpi_video_bus_set_POST (video, opt); | ||
1210 | if (!ACPI_SUCCESS(status)) | ||
1211 | return_VALUE(-EFAULT); | ||
1212 | |||
1213 | } | ||
1214 | |||
1215 | |||
1216 | return_VALUE(count); | ||
1217 | } | ||
1218 | |||
1219 | static ssize_t | ||
1220 | acpi_video_bus_write_DOS ( | ||
1221 | struct file *file, | ||
1222 | const char __user *buffer, | ||
1223 | size_t count, | ||
1224 | loff_t *data) | ||
1225 | { | ||
1226 | int status; | ||
1227 | struct seq_file *m = (struct seq_file *) file->private_data; | ||
1228 | struct acpi_video_bus *video = (struct acpi_video_bus *) m->private; | ||
1229 | char str[12] = {0}; | ||
1230 | unsigned long opt; | ||
1231 | |||
1232 | ACPI_FUNCTION_TRACE("acpi_video_bus_write_DOS"); | ||
1233 | |||
1234 | |||
1235 | if (!video || count + 1 > sizeof str) | ||
1236 | return_VALUE(-EINVAL); | ||
1237 | |||
1238 | if (copy_from_user(str, buffer, count)) | ||
1239 | return_VALUE(-EFAULT); | ||
1240 | |||
1241 | str[count] = 0; | ||
1242 | opt = strtoul(str, NULL, 0); | ||
1243 | if (opt > 7) | ||
1244 | return_VALUE(-EFAULT); | ||
1245 | |||
1246 | status = acpi_video_bus_DOS (video, opt & 0x3, (opt & 0x4)>>2); | ||
1247 | |||
1248 | if (!ACPI_SUCCESS(status)) | ||
1249 | return_VALUE(-EFAULT); | ||
1250 | |||
1251 | return_VALUE(count); | ||
1252 | } | ||
1253 | |||
1254 | static int | ||
1255 | acpi_video_bus_add_fs ( | ||
1256 | struct acpi_device *device) | ||
1257 | { | ||
1258 | struct proc_dir_entry *entry = NULL; | ||
1259 | struct acpi_video_bus *video; | ||
1260 | |||
1261 | ACPI_FUNCTION_TRACE("acpi_video_bus_add_fs"); | ||
1262 | |||
1263 | video = (struct acpi_video_bus *) acpi_driver_data(device); | ||
1264 | |||
1265 | if (!acpi_device_dir(device)) { | ||
1266 | acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), | ||
1267 | acpi_video_dir); | ||
1268 | if (!acpi_device_dir(device)) | ||
1269 | return_VALUE(-ENODEV); | ||
1270 | video->dir = acpi_device_dir(device); | ||
1271 | acpi_device_dir(device)->owner = THIS_MODULE; | ||
1272 | } | ||
1273 | |||
1274 | /* 'info' [R] */ | ||
1275 | entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device)); | ||
1276 | if (!entry) | ||
1277 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'info' fs entry\n")); | ||
1278 | else { | ||
1279 | entry->proc_fops = &acpi_video_bus_info_fops; | ||
1280 | entry->data = acpi_driver_data(device); | ||
1281 | entry->owner = THIS_MODULE; | ||
1282 | } | ||
1283 | |||
1284 | /* 'ROM' [R] */ | ||
1285 | entry = create_proc_entry("ROM", S_IRUGO, acpi_device_dir(device)); | ||
1286 | if (!entry) | ||
1287 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'ROM' fs entry\n")); | ||
1288 | else { | ||
1289 | entry->proc_fops = &acpi_video_bus_ROM_fops; | ||
1290 | entry->data = acpi_driver_data(device); | ||
1291 | entry->owner = THIS_MODULE; | ||
1292 | } | ||
1293 | |||
1294 | /* 'POST_info' [R] */ | ||
1295 | entry = create_proc_entry("POST_info", S_IRUGO, acpi_device_dir(device)); | ||
1296 | if (!entry) | ||
1297 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'POST_info' fs entry\n")); | ||
1298 | else { | ||
1299 | entry->proc_fops = &acpi_video_bus_POST_info_fops; | ||
1300 | entry->data = acpi_driver_data(device); | ||
1301 | entry->owner = THIS_MODULE; | ||
1302 | } | ||
1303 | |||
1304 | /* 'POST' [R/W] */ | ||
1305 | entry = create_proc_entry("POST", S_IFREG|S_IRUGO|S_IRUSR, acpi_device_dir(device)); | ||
1306 | if (!entry) | ||
1307 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'POST' fs entry\n")); | ||
1308 | else { | ||
1309 | entry->proc_fops = &acpi_video_bus_POST_fops; | ||
1310 | entry->proc_fops->write = acpi_video_bus_write_POST; | ||
1311 | entry->data = acpi_driver_data(device); | ||
1312 | entry->owner = THIS_MODULE; | ||
1313 | } | ||
1314 | |||
1315 | /* 'DOS' [R/W] */ | ||
1316 | entry = create_proc_entry("DOS", S_IFREG|S_IRUGO|S_IRUSR, acpi_device_dir(device)); | ||
1317 | if (!entry) | ||
1318 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'DOS' fs entry\n")); | ||
1319 | else { | ||
1320 | entry->proc_fops = &acpi_video_bus_DOS_fops; | ||
1321 | entry->proc_fops->write = acpi_video_bus_write_DOS; | ||
1322 | entry->data = acpi_driver_data(device); | ||
1323 | entry->owner = THIS_MODULE; | ||
1324 | } | ||
1325 | |||
1326 | return_VALUE(0); | ||
1327 | } | ||
1328 | |||
1329 | static int | ||
1330 | acpi_video_bus_remove_fs ( | ||
1331 | struct acpi_device *device) | ||
1332 | { | ||
1333 | struct acpi_video_bus *video; | ||
1334 | |||
1335 | ACPI_FUNCTION_TRACE("acpi_video_bus_remove_fs"); | ||
1336 | |||
1337 | video = (struct acpi_video_bus *) acpi_driver_data(device); | ||
1338 | |||
1339 | if (acpi_device_dir(device)) { | ||
1340 | remove_proc_entry("info", acpi_device_dir(device)); | ||
1341 | remove_proc_entry("ROM", acpi_device_dir(device)); | ||
1342 | remove_proc_entry("POST_info", acpi_device_dir(device)); | ||
1343 | remove_proc_entry("POST", acpi_device_dir(device)); | ||
1344 | remove_proc_entry("DOS", acpi_device_dir(device)); | ||
1345 | remove_proc_entry(acpi_device_bid(device), | ||
1346 | acpi_video_dir); | ||
1347 | acpi_device_dir(device) = NULL; | ||
1348 | } | ||
1349 | |||
1350 | return_VALUE(0); | ||
1351 | } | ||
1352 | |||
1353 | /* -------------------------------------------------------------------------- | ||
1354 | Driver Interface | ||
1355 | -------------------------------------------------------------------------- */ | ||
1356 | |||
1357 | /* device interface */ | ||
1358 | |||
1359 | static int | ||
1360 | acpi_video_bus_get_one_device ( | ||
1361 | struct acpi_device *device, | ||
1362 | struct acpi_video_bus *video) | ||
1363 | { | ||
1364 | unsigned long device_id; | ||
1365 | int status, result; | ||
1366 | struct acpi_video_device *data; | ||
1367 | |||
1368 | ACPI_FUNCTION_TRACE("acpi_video_bus_get_one_device"); | ||
1369 | |||
1370 | if (!device || !video) | ||
1371 | return_VALUE(-EINVAL); | ||
1372 | |||
1373 | status = acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); | ||
1374 | if (ACPI_SUCCESS(status)) { | ||
1375 | |||
1376 | data = kmalloc(sizeof(struct acpi_video_device), GFP_KERNEL); | ||
1377 | if (!data) | ||
1378 | return_VALUE(-ENOMEM); | ||
1379 | |||
1380 | memset(data, 0, sizeof(struct acpi_video_device)); | ||
1381 | |||
1382 | data->handle = device->handle; | ||
1383 | strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); | ||
1384 | strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); | ||
1385 | acpi_driver_data(device) = data; | ||
1386 | |||
1387 | data->device_id = device_id; | ||
1388 | data->video = video; | ||
1389 | data->dev = device; | ||
1390 | |||
1391 | switch (device_id & 0xffff) { | ||
1392 | case 0x0100: | ||
1393 | data->flags.crt = 1; | ||
1394 | break; | ||
1395 | case 0x0400: | ||
1396 | data->flags.lcd = 1; | ||
1397 | break; | ||
1398 | case 0x0200: | ||
1399 | data->flags.tvout = 1; | ||
1400 | break; | ||
1401 | default: | ||
1402 | data->flags.unknown = 1; | ||
1403 | break; | ||
1404 | } | ||
1405 | |||
1406 | acpi_video_device_bind(video, data); | ||
1407 | acpi_video_device_find_cap(data); | ||
1408 | |||
1409 | status = acpi_install_notify_handler(data->handle, | ||
1410 | ACPI_DEVICE_NOTIFY, acpi_video_device_notify, data); | ||
1411 | if (ACPI_FAILURE(status)) { | ||
1412 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1413 | "Error installing notify handler\n")); | ||
1414 | result = -ENODEV; | ||
1415 | goto end; | ||
1416 | } | ||
1417 | |||
1418 | down(&video->sem); | ||
1419 | list_add_tail(&data->entry, &video->video_device_list); | ||
1420 | up(&video->sem); | ||
1421 | |||
1422 | acpi_video_device_add_fs(device); | ||
1423 | |||
1424 | return_VALUE(0); | ||
1425 | } | ||
1426 | |||
1427 | end: | ||
1428 | return_VALUE(-ENOENT); | ||
1429 | } | ||
1430 | |||
1431 | /* | ||
1432 | * Arg: | ||
1433 | * video : video bus device | ||
1434 | * | ||
1435 | * Return: | ||
1436 | * none | ||
1437 | * | ||
1438 | * Enumerate the video device list of the video bus, | ||
1439 | * bind the ids with the corresponding video devices | ||
1440 | * under the video bus. | ||
1441 | */ | ||
1442 | |||
1443 | static void | ||
1444 | acpi_video_device_rebind( struct acpi_video_bus *video) | ||
1445 | { | ||
1446 | struct list_head * node, * next; | ||
1447 | list_for_each_safe(node, next, &video->video_device_list) { | ||
1448 | struct acpi_video_device * dev = container_of(node, struct acpi_video_device, entry); | ||
1449 | acpi_video_device_bind( video, dev); | ||
1450 | } | ||
1451 | } | ||
1452 | |||
1453 | /* | ||
1454 | * Arg: | ||
1455 | * video : video bus device | ||
1456 | * device : video output device under the video | ||
1457 | * bus | ||
1458 | * | ||
1459 | * Return: | ||
1460 | * none | ||
1461 | * | ||
1462 | * Bind the ids with the corresponding video devices | ||
1463 | * under the video bus. | ||
1464 | */ | ||
1465 | |||
1466 | static void | ||
1467 | acpi_video_device_bind( struct acpi_video_bus *video, | ||
1468 | struct acpi_video_device *device) | ||
1469 | { | ||
1470 | int i; | ||
1471 | ACPI_FUNCTION_TRACE("acpi_video_device_bind"); | ||
1472 | |||
1473 | #define IDS_VAL(i) video->attached_array[i].value.int_val | ||
1474 | #define IDS_BIND(i) video->attached_array[i].bind_info | ||
1475 | |||
1476 | for (i = 0; IDS_VAL(i) != ACPI_VIDEO_HEAD_INVALID && | ||
1477 | i < video->attached_count; i++) { | ||
1478 | if (device->device_id == (IDS_VAL(i)& 0xffff)) { | ||
1479 | IDS_BIND(i) = device; | ||
1480 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i)); | ||
1481 | } | ||
1482 | } | ||
1483 | #undef IDS_VAL | ||
1484 | #undef IDS_BIND | ||
1485 | } | ||
1486 | |||
1487 | /* | ||
1488 | * Arg: | ||
1489 | * video : video bus device | ||
1490 | * | ||
1491 | * Return: | ||
1492 | * < 0 : error | ||
1493 | * | ||
1494 | * Call _DOD to enumerate all devices attached to display adapter | ||
1495 | * | ||
1496 | */ | ||
1497 | |||
1498 | static int acpi_video_device_enumerate(struct acpi_video_bus *video) | ||
1499 | { | ||
1500 | int status; | ||
1501 | int count; | ||
1502 | int i; | ||
1503 | struct acpi_video_enumerated_device *active_device_list; | ||
1504 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
1505 | union acpi_object *dod = NULL; | ||
1506 | union acpi_object *obj; | ||
1507 | |||
1508 | ACPI_FUNCTION_TRACE("acpi_video_device_enumerate"); | ||
1509 | |||
1510 | status = acpi_evaluate_object(video->handle, "_DOD", NULL, &buffer); | ||
1511 | if (!ACPI_SUCCESS(status)) { | ||
1512 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _DOD\n")); | ||
1513 | return_VALUE(status); | ||
1514 | } | ||
1515 | |||
1516 | dod = (union acpi_object *) buffer.pointer; | ||
1517 | if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) { | ||
1518 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _DOD data\n")); | ||
1519 | status = -EFAULT; | ||
1520 | goto out; | ||
1521 | } | ||
1522 | |||
1523 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n", | ||
1524 | dod->package.count)); | ||
1525 | |||
1526 | active_device_list= kmalloc( | ||
1527 | (1+dod->package.count)*sizeof(struct acpi_video_enumerated_device), | ||
1528 | GFP_KERNEL); | ||
1529 | |||
1530 | if (!active_device_list) { | ||
1531 | status = -ENOMEM; | ||
1532 | goto out; | ||
1533 | } | ||
1534 | |||
1535 | count = 0; | ||
1536 | for (i = 0; i < dod->package.count; i++) { | ||
1537 | obj = (union acpi_object *) &dod->package.elements[i]; | ||
1538 | |||
1539 | if (obj->type != ACPI_TYPE_INTEGER) { | ||
1540 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _DOD data\n")); | ||
1541 | active_device_list[i].value.int_val = ACPI_VIDEO_HEAD_INVALID; | ||
1542 | } | ||
1543 | active_device_list[i].value.int_val = obj->integer.value; | ||
1544 | active_device_list[i].bind_info = NULL; | ||
1545 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i, (int) obj->integer.value)); | ||
1546 | count++; | ||
1547 | } | ||
1548 | active_device_list[count].value.int_val = ACPI_VIDEO_HEAD_END; | ||
1549 | |||
1550 | if(video->attached_array) | ||
1551 | kfree(video->attached_array); | ||
1552 | |||
1553 | video->attached_array = active_device_list; | ||
1554 | video->attached_count = count; | ||
1555 | out: | ||
1556 | acpi_os_free(buffer.pointer); | ||
1557 | return_VALUE(status); | ||
1558 | } | ||
1559 | |||
1560 | /* | ||
1561 | * Arg: | ||
1562 | * video : video bus device | ||
1563 | * event : Nontify Event | ||
1564 | * | ||
1565 | * Return: | ||
1566 | * < 0 : error | ||
1567 | * | ||
1568 | * 1. Find out the current active output device. | ||
1569 | * 2. Identify the next output device to switch | ||
1570 | * 3. call _DSS to do actual switch. | ||
1571 | */ | ||
1572 | |||
1573 | static int | ||
1574 | acpi_video_switch_output( | ||
1575 | struct acpi_video_bus *video, | ||
1576 | int event) | ||
1577 | { | ||
1578 | struct list_head * node, * next; | ||
1579 | struct acpi_video_device *dev=NULL; | ||
1580 | struct acpi_video_device *dev_next=NULL; | ||
1581 | struct acpi_video_device *dev_prev=NULL; | ||
1582 | unsigned long state; | ||
1583 | int status = 0; | ||
1584 | |||
1585 | ACPI_FUNCTION_TRACE("acpi_video_switch_output"); | ||
1586 | |||
1587 | list_for_each_safe(node, next, &video->video_device_list) { | ||
1588 | struct acpi_video_device * dev = container_of(node, struct acpi_video_device, entry); | ||
1589 | status = acpi_video_device_get_state(dev, &state); | ||
1590 | if (state & 0x2){ | ||
1591 | dev_next = container_of(node->next, struct acpi_video_device, entry); | ||
1592 | dev_prev = container_of(node->prev, struct acpi_video_device, entry); | ||
1593 | goto out; | ||
1594 | } | ||
1595 | } | ||
1596 | dev_next = container_of(node->next, struct acpi_video_device, entry); | ||
1597 | dev_prev = container_of(node->prev, struct acpi_video_device, entry); | ||
1598 | out: | ||
1599 | switch (event) { | ||
1600 | case ACPI_VIDEO_NOTIFY_CYCLE: | ||
1601 | case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: | ||
1602 | acpi_video_device_set_state(dev, 0); | ||
1603 | acpi_video_device_set_state(dev_next, 0x80000001); | ||
1604 | break; | ||
1605 | case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: | ||
1606 | acpi_video_device_set_state(dev, 0); | ||
1607 | acpi_video_device_set_state(dev_prev, 0x80000001); | ||
1608 | default: | ||
1609 | break; | ||
1610 | } | ||
1611 | |||
1612 | return_VALUE(status); | ||
1613 | } | ||
1614 | |||
1615 | static int | ||
1616 | acpi_video_get_next_level( | ||
1617 | struct acpi_video_device *device, | ||
1618 | u32 level_current, | ||
1619 | u32 event) | ||
1620 | { | ||
1621 | /*Fix me*/ | ||
1622 | return level_current; | ||
1623 | } | ||
1624 | |||
1625 | |||
1626 | static void | ||
1627 | acpi_video_switch_brightness ( | ||
1628 | struct acpi_video_device *device, | ||
1629 | int event) | ||
1630 | { | ||
1631 | unsigned long level_current, level_next; | ||
1632 | acpi_video_device_lcd_get_level_current(device, &level_current); | ||
1633 | level_next = acpi_video_get_next_level(device, level_current, event); | ||
1634 | acpi_video_device_lcd_set_level(device, level_next); | ||
1635 | } | ||
1636 | |||
1637 | static int | ||
1638 | acpi_video_bus_get_devices ( | ||
1639 | struct acpi_video_bus *video, | ||
1640 | struct acpi_device *device) | ||
1641 | { | ||
1642 | int status = 0; | ||
1643 | struct list_head *node, *next; | ||
1644 | |||
1645 | ACPI_FUNCTION_TRACE("acpi_video_get_devices"); | ||
1646 | |||
1647 | acpi_video_device_enumerate(video); | ||
1648 | |||
1649 | list_for_each_safe(node, next, &device->children) { | ||
1650 | struct acpi_device *dev = list_entry(node, struct acpi_device, node); | ||
1651 | |||
1652 | if (!dev) | ||
1653 | continue; | ||
1654 | |||
1655 | status = acpi_video_bus_get_one_device(dev, video); | ||
1656 | if (ACPI_FAILURE(status)) { | ||
1657 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Cant attach device\n")); | ||
1658 | continue; | ||
1659 | } | ||
1660 | |||
1661 | } | ||
1662 | return_VALUE(status); | ||
1663 | } | ||
1664 | |||
1665 | static int | ||
1666 | acpi_video_bus_put_one_device( | ||
1667 | struct acpi_video_device *device) | ||
1668 | { | ||
1669 | struct acpi_video_bus *video; | ||
1670 | |||
1671 | ACPI_FUNCTION_TRACE("acpi_video_bus_put_one_device"); | ||
1672 | |||
1673 | if (!device || !device->video) | ||
1674 | return_VALUE(-ENOENT); | ||
1675 | |||
1676 | video = device->video; | ||
1677 | |||
1678 | down(&video->sem); | ||
1679 | list_del(&device->entry); | ||
1680 | up(&video->sem); | ||
1681 | acpi_video_device_remove_fs(device->dev); | ||
1682 | |||
1683 | return_VALUE(0); | ||
1684 | } | ||
1685 | |||
1686 | static int | ||
1687 | acpi_video_bus_put_devices ( | ||
1688 | struct acpi_video_bus *video) | ||
1689 | { | ||
1690 | int status; | ||
1691 | struct list_head *node, *next; | ||
1692 | |||
1693 | ACPI_FUNCTION_TRACE("acpi_video_bus_put_devices"); | ||
1694 | |||
1695 | list_for_each_safe(node, next, &video->video_device_list) { | ||
1696 | struct acpi_video_device *data = list_entry(node, struct acpi_video_device, entry); | ||
1697 | if (!data) | ||
1698 | continue; | ||
1699 | |||
1700 | status = acpi_video_bus_put_one_device(data); | ||
1701 | if(ACPI_FAILURE(status)) | ||
1702 | printk(KERN_WARNING PREFIX "hhuuhhuu bug in acpi video driver.\n"); | ||
1703 | |||
1704 | if (data->brightness) | ||
1705 | kfree(data->brightness); | ||
1706 | |||
1707 | kfree(data); | ||
1708 | } | ||
1709 | |||
1710 | return_VALUE(0); | ||
1711 | } | ||
1712 | |||
1713 | /* acpi_video interface */ | ||
1714 | |||
1715 | static int | ||
1716 | acpi_video_bus_start_devices( | ||
1717 | struct acpi_video_bus *video) | ||
1718 | { | ||
1719 | return acpi_video_bus_DOS(video, 1, 0); | ||
1720 | } | ||
1721 | |||
1722 | static int | ||
1723 | acpi_video_bus_stop_devices( | ||
1724 | struct acpi_video_bus *video) | ||
1725 | { | ||
1726 | return acpi_video_bus_DOS(video, 0, 1); | ||
1727 | } | ||
1728 | |||
1729 | static void | ||
1730 | acpi_video_bus_notify ( | ||
1731 | acpi_handle handle, | ||
1732 | u32 event, | ||
1733 | void *data) | ||
1734 | { | ||
1735 | struct acpi_video_bus *video = (struct acpi_video_bus *) data; | ||
1736 | struct acpi_device *device = NULL; | ||
1737 | |||
1738 | ACPI_FUNCTION_TRACE("acpi_video_bus_notify"); | ||
1739 | printk("video bus notify\n"); | ||
1740 | |||
1741 | if (!video) | ||
1742 | return_VOID; | ||
1743 | |||
1744 | if (acpi_bus_get_device(handle, &device)) | ||
1745 | return_VOID; | ||
1746 | |||
1747 | switch (event) { | ||
1748 | case ACPI_VIDEO_NOTIFY_SWITCH: /* User request that a switch occur, | ||
1749 | * most likely via hotkey. */ | ||
1750 | acpi_bus_generate_event(device, event, 0); | ||
1751 | break; | ||
1752 | |||
1753 | case ACPI_VIDEO_NOTIFY_PROBE: /* User plug or remove a video | ||
1754 | * connector. */ | ||
1755 | acpi_video_device_enumerate(video); | ||
1756 | acpi_video_device_rebind(video); | ||
1757 | acpi_video_switch_output(video, event); | ||
1758 | acpi_bus_generate_event(device, event, 0); | ||
1759 | break; | ||
1760 | |||
1761 | case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed.*/ | ||
1762 | case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */ | ||
1763 | case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */ | ||
1764 | acpi_video_switch_output(video, event); | ||
1765 | acpi_bus_generate_event(device, event, 0); | ||
1766 | break; | ||
1767 | |||
1768 | default: | ||
1769 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
1770 | "Unsupported event [0x%x]\n", event)); | ||
1771 | break; | ||
1772 | } | ||
1773 | |||
1774 | return_VOID; | ||
1775 | } | ||
1776 | |||
1777 | static void | ||
1778 | acpi_video_device_notify ( | ||
1779 | acpi_handle handle, | ||
1780 | u32 event, | ||
1781 | void *data) | ||
1782 | { | ||
1783 | struct acpi_video_device *video_device = (struct acpi_video_device *) data; | ||
1784 | struct acpi_device *device = NULL; | ||
1785 | |||
1786 | ACPI_FUNCTION_TRACE("acpi_video_device_notify"); | ||
1787 | |||
1788 | printk("video device notify\n"); | ||
1789 | if (!video_device) | ||
1790 | return_VOID; | ||
1791 | |||
1792 | if (acpi_bus_get_device(handle, &device)) | ||
1793 | return_VOID; | ||
1794 | |||
1795 | switch (event) { | ||
1796 | case ACPI_VIDEO_NOTIFY_SWITCH: /* change in status (cycle output device) */ | ||
1797 | case ACPI_VIDEO_NOTIFY_PROBE: /* change in status (output device status) */ | ||
1798 | acpi_bus_generate_event(device, event, 0); | ||
1799 | break; | ||
1800 | case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */ | ||
1801 | case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */ | ||
1802 | case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */ | ||
1803 | case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightnesss */ | ||
1804 | case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */ | ||
1805 | acpi_video_switch_brightness (video_device, event); | ||
1806 | acpi_bus_generate_event(device, event, 0); | ||
1807 | break; | ||
1808 | default: | ||
1809 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
1810 | "Unsupported event [0x%x]\n", event)); | ||
1811 | break; | ||
1812 | } | ||
1813 | return_VOID; | ||
1814 | } | ||
1815 | |||
1816 | static int | ||
1817 | acpi_video_bus_add ( | ||
1818 | struct acpi_device *device) | ||
1819 | { | ||
1820 | int result = 0; | ||
1821 | acpi_status status = 0; | ||
1822 | struct acpi_video_bus *video = NULL; | ||
1823 | |||
1824 | ACPI_FUNCTION_TRACE("acpi_video_bus_add"); | ||
1825 | |||
1826 | if (!device) | ||
1827 | return_VALUE(-EINVAL); | ||
1828 | |||
1829 | video = kmalloc(sizeof(struct acpi_video_bus), GFP_KERNEL); | ||
1830 | if (!video) | ||
1831 | return_VALUE(-ENOMEM); | ||
1832 | memset(video, 0, sizeof(struct acpi_video_bus)); | ||
1833 | |||
1834 | video->handle = device->handle; | ||
1835 | strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); | ||
1836 | strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); | ||
1837 | acpi_driver_data(device) = video; | ||
1838 | |||
1839 | acpi_video_bus_find_cap(video); | ||
1840 | result = acpi_video_bus_check(video); | ||
1841 | if (result) | ||
1842 | goto end; | ||
1843 | |||
1844 | result = acpi_video_bus_add_fs(device); | ||
1845 | if (result) | ||
1846 | goto end; | ||
1847 | |||
1848 | init_MUTEX(&video->sem); | ||
1849 | INIT_LIST_HEAD(&video->video_device_list); | ||
1850 | |||
1851 | acpi_video_bus_get_devices(video, device); | ||
1852 | acpi_video_bus_start_devices(video); | ||
1853 | |||
1854 | status = acpi_install_notify_handler(video->handle, | ||
1855 | ACPI_DEVICE_NOTIFY, acpi_video_bus_notify, video); | ||
1856 | if (ACPI_FAILURE(status)) { | ||
1857 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1858 | "Error installing notify handler\n")); | ||
1859 | result = -ENODEV; | ||
1860 | goto end; | ||
1861 | } | ||
1862 | |||
1863 | printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", | ||
1864 | ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), | ||
1865 | video->flags.multihead ? "yes":"no", | ||
1866 | video->flags.rom ? "yes":"no", | ||
1867 | video->flags.post ? "yes":"no"); | ||
1868 | |||
1869 | end: | ||
1870 | if (result) { | ||
1871 | acpi_video_bus_remove_fs(device); | ||
1872 | kfree(video); | ||
1873 | } | ||
1874 | |||
1875 | return_VALUE(result); | ||
1876 | } | ||
1877 | |||
1878 | static int | ||
1879 | acpi_video_bus_remove ( | ||
1880 | struct acpi_device *device, | ||
1881 | int type) | ||
1882 | { | ||
1883 | acpi_status status = 0; | ||
1884 | struct acpi_video_bus *video = NULL; | ||
1885 | |||
1886 | ACPI_FUNCTION_TRACE("acpi_video_bus_remove"); | ||
1887 | |||
1888 | if (!device || !acpi_driver_data(device)) | ||
1889 | return_VALUE(-EINVAL); | ||
1890 | |||
1891 | video = (struct acpi_video_bus *) acpi_driver_data(device); | ||
1892 | |||
1893 | acpi_video_bus_stop_devices(video); | ||
1894 | |||
1895 | status = acpi_remove_notify_handler(video->handle, | ||
1896 | ACPI_DEVICE_NOTIFY, acpi_video_bus_notify); | ||
1897 | if (ACPI_FAILURE(status)) | ||
1898 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1899 | "Error removing notify handler\n")); | ||
1900 | |||
1901 | acpi_video_bus_put_devices(video); | ||
1902 | acpi_video_bus_remove_fs(device); | ||
1903 | |||
1904 | if (video->attached_array) | ||
1905 | kfree(video->attached_array); | ||
1906 | kfree(video); | ||
1907 | |||
1908 | return_VALUE(0); | ||
1909 | } | ||
1910 | |||
1911 | |||
1912 | static int | ||
1913 | acpi_video_bus_match ( | ||
1914 | struct acpi_device *device, | ||
1915 | struct acpi_driver *driver) | ||
1916 | { | ||
1917 | acpi_handle h_dummy1; | ||
1918 | acpi_handle h_dummy2; | ||
1919 | acpi_handle h_dummy3; | ||
1920 | |||
1921 | ACPI_FUNCTION_TRACE("acpi_video_bus_match"); | ||
1922 | |||
1923 | if (!device || !driver) | ||
1924 | return_VALUE(-EINVAL); | ||
1925 | |||
1926 | /* Since there is no HID, CID for ACPI Video drivers, we have | ||
1927 | * to check well known required nodes for each feature we support. | ||
1928 | */ | ||
1929 | |||
1930 | /* Does this device able to support video switching ? */ | ||
1931 | if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy1)) && | ||
1932 | ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy2))) | ||
1933 | return_VALUE(0); | ||
1934 | |||
1935 | /* Does this device able to retrieve a video ROM ? */ | ||
1936 | if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy1))) | ||
1937 | return_VALUE(0); | ||
1938 | |||
1939 | /* Does this device able to configure which video head to be POSTed ? */ | ||
1940 | if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy1)) && | ||
1941 | ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy2)) && | ||
1942 | ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy3))) | ||
1943 | return_VALUE(0); | ||
1944 | |||
1945 | |||
1946 | return_VALUE(-ENODEV); | ||
1947 | } | ||
1948 | |||
1949 | |||
1950 | static int __init | ||
1951 | acpi_video_init (void) | ||
1952 | { | ||
1953 | int result = 0; | ||
1954 | |||
1955 | ACPI_FUNCTION_TRACE("acpi_video_init"); | ||
1956 | |||
1957 | /* | ||
1958 | acpi_dbg_level = 0xFFFFFFFF; | ||
1959 | acpi_dbg_layer = 0x08000000; | ||
1960 | */ | ||
1961 | |||
1962 | acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir); | ||
1963 | if (!acpi_video_dir) | ||
1964 | return_VALUE(-ENODEV); | ||
1965 | acpi_video_dir->owner = THIS_MODULE; | ||
1966 | |||
1967 | result = acpi_bus_register_driver(&acpi_video_bus); | ||
1968 | if (result < 0) { | ||
1969 | remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); | ||
1970 | return_VALUE(-ENODEV); | ||
1971 | } | ||
1972 | |||
1973 | return_VALUE(0); | ||
1974 | } | ||
1975 | |||
1976 | static void __exit | ||
1977 | acpi_video_exit (void) | ||
1978 | { | ||
1979 | ACPI_FUNCTION_TRACE("acpi_video_exit"); | ||
1980 | |||
1981 | acpi_bus_unregister_driver(&acpi_video_bus); | ||
1982 | |||
1983 | remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); | ||
1984 | |||
1985 | return_VOID; | ||
1986 | } | ||
1987 | |||
1988 | module_init(acpi_video_init); | ||
1989 | module_exit(acpi_video_exit); | ||