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