blob: 4b09a021b434046232563d5e0573ad7bbbd5d7eb [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * video.c - ACPI Video Driver ($Revision:$)
3 *
4 * Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
5 * Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org>
Thomas Tuttlef4715182006-12-19 12:56:14 -08006 * Copyright (C) 2006 Thomas Tuttle <linux-kernel@ttuttle.net>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or (at
13 * your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
23 *
24 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 */
26
27#include <linux/kernel.h>
28#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/types.h>
31#include <linux/list.h>
32#include <linux/proc_fs.h>
33#include <linux/seq_file.h>
34
Yu Luming2f3d0002006-11-11 02:40:34 +080035#include <linux/backlight.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <asm/uaccess.h>
37
38#include <acpi/acpi_bus.h>
39#include <acpi/acpi_drivers.h>
40
41#define ACPI_VIDEO_COMPONENT 0x08000000
42#define ACPI_VIDEO_CLASS "video"
43#define ACPI_VIDEO_DRIVER_NAME "ACPI Video Driver"
44#define ACPI_VIDEO_BUS_NAME "Video Bus"
45#define ACPI_VIDEO_DEVICE_NAME "Video Device"
46#define ACPI_VIDEO_NOTIFY_SWITCH 0x80
47#define ACPI_VIDEO_NOTIFY_PROBE 0x81
48#define ACPI_VIDEO_NOTIFY_CYCLE 0x82
49#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT 0x83
50#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT 0x84
51
Thomas Tuttlef4715182006-12-19 12:56:14 -080052#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS 0x85
53#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
54#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
55#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS 0x88
56#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF 0x89
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
Linus Torvalds1da177e2005-04-16 15:20:36 -070058#define ACPI_VIDEO_HEAD_INVALID (~0u - 1)
59#define ACPI_VIDEO_HEAD_END (~0u)
Yu Luming2f3d0002006-11-11 02:40:34 +080060#define MAX_NAME_LEN 20
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#define _COMPONENT ACPI_VIDEO_COMPONENT
Len Brown4be44fc2005-08-05 00:44:28 -040063ACPI_MODULE_NAME("acpi_video")
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
Len Brown4be44fc2005-08-05 00:44:28 -040065 MODULE_AUTHOR("Bruno Ducrot");
Linus Torvalds1da177e2005-04-16 15:20:36 -070066MODULE_DESCRIPTION(ACPI_VIDEO_DRIVER_NAME);
67MODULE_LICENSE("GPL");
68
Len Brown4be44fc2005-08-05 00:44:28 -040069static int acpi_video_bus_add(struct acpi_device *device);
70static int acpi_video_bus_remove(struct acpi_device *device, int type);
71static int acpi_video_bus_match(struct acpi_device *device,
72 struct acpi_driver *driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74static struct acpi_driver acpi_video_bus = {
75 .name = ACPI_VIDEO_DRIVER_NAME,
76 .class = ACPI_VIDEO_CLASS,
77 .ops = {
78 .add = acpi_video_bus_add,
79 .remove = acpi_video_bus_remove,
80 .match = acpi_video_bus_match,
Len Brown4be44fc2005-08-05 00:44:28 -040081 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070082};
83
84struct acpi_video_bus_flags {
Len Brown4be44fc2005-08-05 00:44:28 -040085 u8 multihead:1; /* can switch video heads */
86 u8 rom:1; /* can retrieve a video rom */
87 u8 post:1; /* can configure the head to */
88 u8 reserved:5;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089};
90
91struct acpi_video_bus_cap {
Len Brown4be44fc2005-08-05 00:44:28 -040092 u8 _DOS:1; /*Enable/Disable output switching */
93 u8 _DOD:1; /*Enumerate all devices attached to display adapter */
94 u8 _ROM:1; /*Get ROM Data */
95 u8 _GPD:1; /*Get POST Device */
96 u8 _SPD:1; /*Set POST Device */
97 u8 _VPO:1; /*Video POST Options */
98 u8 reserved:2;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099};
100
Len Brown4be44fc2005-08-05 00:44:28 -0400101struct acpi_video_device_attrib {
102 u32 display_index:4; /* A zero-based instance of the Display */
103 u32 display_port_attachment:4; /*This field differenates displays type */
104 u32 display_type:4; /*Describe the specific type in use */
105 u32 vendor_specific:4; /*Chipset Vendor Specifi */
106 u32 bios_can_detect:1; /*BIOS can detect the device */
107 u32 depend_on_vga:1; /*Non-VGA output device whose power is related to
108 the VGA device. */
109 u32 pipe_id:3; /*For VGA multiple-head devices. */
110 u32 reserved:10; /*Must be 0 */
111 u32 device_id_scheme:1; /*Device ID Scheme */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112};
113
114struct acpi_video_enumerated_device {
115 union {
116 u32 int_val;
Len Brown4be44fc2005-08-05 00:44:28 -0400117 struct acpi_video_device_attrib attrib;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 } value;
119 struct acpi_video_device *bind_info;
120};
121
122struct acpi_video_bus {
Patrick Mochele6afa0d2006-05-19 16:54:40 -0400123 struct acpi_device *device;
Len Brown4be44fc2005-08-05 00:44:28 -0400124 u8 dos_setting;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 struct acpi_video_enumerated_device *attached_array;
Len Brown4be44fc2005-08-05 00:44:28 -0400126 u8 attached_count;
127 struct acpi_video_bus_cap cap;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 struct acpi_video_bus_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400129 struct semaphore sem;
130 struct list_head video_device_list;
131 struct proc_dir_entry *dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132};
133
134struct acpi_video_device_flags {
Len Brown4be44fc2005-08-05 00:44:28 -0400135 u8 crt:1;
136 u8 lcd:1;
137 u8 tvout:1;
138 u8 bios:1;
139 u8 unknown:1;
140 u8 reserved:3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141};
142
143struct acpi_video_device_cap {
Len Brown4be44fc2005-08-05 00:44:28 -0400144 u8 _ADR:1; /*Return the unique ID */
145 u8 _BCL:1; /*Query list of brightness control levels supported */
146 u8 _BCM:1; /*Set the brightness level */
Yu Luming2f3d0002006-11-11 02:40:34 +0800147 u8 _BQC:1; /* Get current brightness level */
Len Brown4be44fc2005-08-05 00:44:28 -0400148 u8 _DDC:1; /*Return the EDID for this device */
149 u8 _DCS:1; /*Return status of output device */
150 u8 _DGS:1; /*Query graphics state */
151 u8 _DSS:1; /*Device state set */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152};
153
154struct acpi_video_device_brightness {
Len Brown4be44fc2005-08-05 00:44:28 -0400155 int curr;
156 int count;
157 int *levels;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158};
159
160struct acpi_video_device {
Len Brown4be44fc2005-08-05 00:44:28 -0400161 unsigned long device_id;
162 struct acpi_video_device_flags flags;
163 struct acpi_video_device_cap cap;
164 struct list_head entry;
165 struct acpi_video_bus *video;
166 struct acpi_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 struct acpi_video_device_brightness *brightness;
Yu Luming2f3d0002006-11-11 02:40:34 +0800168 struct backlight_device *backlight;
169 struct backlight_properties *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170};
171
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172/* bus */
173static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file);
174static struct file_operations acpi_video_bus_info_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400175 .open = acpi_video_bus_info_open_fs,
176 .read = seq_read,
177 .llseek = seq_lseek,
178 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179};
180
181static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file);
182static struct file_operations acpi_video_bus_ROM_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400183 .open = acpi_video_bus_ROM_open_fs,
184 .read = seq_read,
185 .llseek = seq_lseek,
186 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187};
188
Len Brown4be44fc2005-08-05 00:44:28 -0400189static int acpi_video_bus_POST_info_open_fs(struct inode *inode,
190 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191static struct file_operations acpi_video_bus_POST_info_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400192 .open = acpi_video_bus_POST_info_open_fs,
193 .read = seq_read,
194 .llseek = seq_lseek,
195 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196};
197
198static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file);
199static struct file_operations acpi_video_bus_POST_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400200 .open = acpi_video_bus_POST_open_fs,
201 .read = seq_read,
202 .llseek = seq_lseek,
203 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204};
205
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file);
207static struct file_operations acpi_video_bus_DOS_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400208 .open = acpi_video_bus_DOS_open_fs,
209 .read = seq_read,
210 .llseek = seq_lseek,
211 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212};
213
214/* device */
Len Brown4be44fc2005-08-05 00:44:28 -0400215static int acpi_video_device_info_open_fs(struct inode *inode,
216 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217static struct file_operations acpi_video_device_info_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400218 .open = acpi_video_device_info_open_fs,
219 .read = seq_read,
220 .llseek = seq_lseek,
221 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222};
223
Len Brown4be44fc2005-08-05 00:44:28 -0400224static int acpi_video_device_state_open_fs(struct inode *inode,
225 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226static struct file_operations acpi_video_device_state_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400227 .open = acpi_video_device_state_open_fs,
228 .read = seq_read,
229 .llseek = seq_lseek,
230 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231};
232
Len Brown4be44fc2005-08-05 00:44:28 -0400233static int acpi_video_device_brightness_open_fs(struct inode *inode,
234 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235static struct file_operations acpi_video_device_brightness_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400236 .open = acpi_video_device_brightness_open_fs,
237 .read = seq_read,
238 .llseek = seq_lseek,
239 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240};
241
Len Brown4be44fc2005-08-05 00:44:28 -0400242static int acpi_video_device_EDID_open_fs(struct inode *inode,
243 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244static struct file_operations acpi_video_device_EDID_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400245 .open = acpi_video_device_EDID_open_fs,
246 .read = seq_read,
247 .llseek = seq_lseek,
248 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249};
250
Len Brown4be44fc2005-08-05 00:44:28 -0400251static char device_decode[][30] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 "motherboard VGA device",
253 "PCI VGA device",
254 "AGP VGA device",
255 "UNKNOWN",
256};
257
Len Brown4be44fc2005-08-05 00:44:28 -0400258static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data);
259static void acpi_video_device_rebind(struct acpi_video_bus *video);
260static void acpi_video_device_bind(struct acpi_video_bus *video,
261 struct acpi_video_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262static int acpi_video_device_enumerate(struct acpi_video_bus *video);
Len Brown4be44fc2005-08-05 00:44:28 -0400263static int acpi_video_switch_output(struct acpi_video_bus *video, int event);
Yu Luming2f3d0002006-11-11 02:40:34 +0800264static int acpi_video_device_lcd_set_level(struct acpi_video_device *device,
265 int level);
266static int acpi_video_device_lcd_get_level_current(
267 struct acpi_video_device *device,
268 unsigned long *level);
Len Brown4be44fc2005-08-05 00:44:28 -0400269static int acpi_video_get_next_level(struct acpi_video_device *device,
270 u32 level_current, u32 event);
271static void acpi_video_switch_brightness(struct acpi_video_device *device,
272 int event);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273
Yu Luming2f3d0002006-11-11 02:40:34 +0800274/*backlight device sysfs support*/
275static int acpi_video_get_brightness(struct backlight_device *bd)
276{
277 unsigned long cur_level;
278 struct acpi_video_device *vd =
279 (struct acpi_video_device *)class_get_devdata(&bd->class_dev);
280 acpi_video_device_lcd_get_level_current(vd, &cur_level);
281 return (int) cur_level;
282}
283
284static int acpi_video_set_brightness(struct backlight_device *bd)
285{
286 int request_level = bd->props->brightness;
287 struct acpi_video_device *vd =
288 (struct acpi_video_device *)class_get_devdata(&bd->class_dev);
289 acpi_video_device_lcd_set_level(vd, request_level);
290 return 0;
291}
292
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293/* --------------------------------------------------------------------------
294 Video Management
295 -------------------------------------------------------------------------- */
296
297/* device */
298
299static int
Len Brown4be44fc2005-08-05 00:44:28 -0400300acpi_video_device_query(struct acpi_video_device *device, unsigned long *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301{
Len Brown4be44fc2005-08-05 00:44:28 -0400302 int status;
Patrick Mochel90130262006-05-19 16:54:48 -0400303
304 status = acpi_evaluate_integer(device->dev->handle, "_DGS", NULL, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
Patrick Mocheld550d982006-06-27 00:41:40 -0400306 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307}
308
309static int
Len Brown4be44fc2005-08-05 00:44:28 -0400310acpi_video_device_get_state(struct acpi_video_device *device,
311 unsigned long *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312{
Len Brown4be44fc2005-08-05 00:44:28 -0400313 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
Patrick Mochel90130262006-05-19 16:54:48 -0400315 status = acpi_evaluate_integer(device->dev->handle, "_DCS", NULL, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
Patrick Mocheld550d982006-06-27 00:41:40 -0400317 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318}
319
320static int
Len Brown4be44fc2005-08-05 00:44:28 -0400321acpi_video_device_set_state(struct acpi_video_device *device, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322{
Len Brown4be44fc2005-08-05 00:44:28 -0400323 int status;
324 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
325 struct acpi_object_list args = { 1, &arg0 };
Luming Yu824b5582005-08-21 19:17:00 -0400326 unsigned long ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328
329 arg0.integer.value = state;
Patrick Mochel90130262006-05-19 16:54:48 -0400330 status = acpi_evaluate_integer(device->dev->handle, "_DSS", &args, &ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331
Patrick Mocheld550d982006-06-27 00:41:40 -0400332 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333}
334
335static int
Len Brown4be44fc2005-08-05 00:44:28 -0400336acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
337 union acpi_object **levels)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338{
Len Brown4be44fc2005-08-05 00:44:28 -0400339 int status;
340 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
341 union acpi_object *obj;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
344 *levels = NULL;
345
Patrick Mochel90130262006-05-19 16:54:48 -0400346 status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400348 return status;
Len Brown4be44fc2005-08-05 00:44:28 -0400349 obj = (union acpi_object *)buffer.pointer;
Adrian Bunk6665bda2006-03-11 10:12:00 -0500350 if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
Len Brown64684632006-06-26 23:41:38 -0400351 printk(KERN_ERR PREFIX "Invalid _BCL data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 status = -EFAULT;
353 goto err;
354 }
355
356 *levels = obj;
357
Patrick Mocheld550d982006-06-27 00:41:40 -0400358 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
Len Brown4be44fc2005-08-05 00:44:28 -0400360 err:
Jesper Juhl6044ec82005-11-07 01:01:32 -0800361 kfree(buffer.pointer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
Patrick Mocheld550d982006-06-27 00:41:40 -0400363 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364}
365
366static int
Len Brown4be44fc2005-08-05 00:44:28 -0400367acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368{
Len Brown4be44fc2005-08-05 00:44:28 -0400369 int status;
370 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
371 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
374 arg0.integer.value = level;
Patrick Mochel90130262006-05-19 16:54:48 -0400375 status = acpi_evaluate_object(device->dev->handle, "_BCM", &args, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
377 printk(KERN_DEBUG "set_level status: %x\n", status);
Patrick Mocheld550d982006-06-27 00:41:40 -0400378 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379}
380
381static int
Len Brown4be44fc2005-08-05 00:44:28 -0400382acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
383 unsigned long *level)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384{
Len Brown4be44fc2005-08-05 00:44:28 -0400385 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
Patrick Mochel90130262006-05-19 16:54:48 -0400387 status = acpi_evaluate_integer(device->dev->handle, "_BQC", NULL, level);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Patrick Mocheld550d982006-06-27 00:41:40 -0400389 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390}
391
392static int
Len Brown4be44fc2005-08-05 00:44:28 -0400393acpi_video_device_EDID(struct acpi_video_device *device,
394 union acpi_object **edid, ssize_t length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395{
Len Brown4be44fc2005-08-05 00:44:28 -0400396 int status;
397 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
398 union acpi_object *obj;
399 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
400 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402
403 *edid = NULL;
404
405 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400406 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 if (length == 128)
408 arg0.integer.value = 1;
409 else if (length == 256)
410 arg0.integer.value = 2;
411 else
Patrick Mocheld550d982006-06-27 00:41:40 -0400412 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
Patrick Mochel90130262006-05-19 16:54:48 -0400414 status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400416 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
Len Brown4be44fc2005-08-05 00:44:28 -0400418 obj = (union acpi_object *)buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
420 if (obj && obj->type == ACPI_TYPE_BUFFER)
421 *edid = obj;
422 else {
Len Brown64684632006-06-26 23:41:38 -0400423 printk(KERN_ERR PREFIX "Invalid _DDC data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 status = -EFAULT;
425 kfree(obj);
426 }
427
Patrick Mocheld550d982006-06-27 00:41:40 -0400428 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429}
430
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431/* bus */
432
433static int
Len Brown4be44fc2005-08-05 00:44:28 -0400434acpi_video_bus_set_POST(struct acpi_video_bus *video, unsigned long option)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435{
Len Brown4be44fc2005-08-05 00:44:28 -0400436 int status;
437 unsigned long tmp;
438 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
439 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 arg0.integer.value = option;
443
Patrick Mochel90130262006-05-19 16:54:48 -0400444 status = acpi_evaluate_integer(video->device->handle, "_SPD", &args, &tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 if (ACPI_SUCCESS(status))
Len Brown4be44fc2005-08-05 00:44:28 -0400446 status = tmp ? (-EINVAL) : (AE_OK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447
Patrick Mocheld550d982006-06-27 00:41:40 -0400448 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449}
450
451static int
Len Brown4be44fc2005-08-05 00:44:28 -0400452acpi_video_bus_get_POST(struct acpi_video_bus *video, unsigned long *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453{
454 int status;
455
Patrick Mochel90130262006-05-19 16:54:48 -0400456 status = acpi_evaluate_integer(video->device->handle, "_GPD", NULL, id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
Patrick Mocheld550d982006-06-27 00:41:40 -0400458 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459}
460
461static int
Len Brown4be44fc2005-08-05 00:44:28 -0400462acpi_video_bus_POST_options(struct acpi_video_bus *video,
463 unsigned long *options)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
Len Brown4be44fc2005-08-05 00:44:28 -0400465 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
Patrick Mochel90130262006-05-19 16:54:48 -0400467 status = acpi_evaluate_integer(video->device->handle, "_VPO", NULL, options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 *options &= 3;
469
Patrick Mocheld550d982006-06-27 00:41:40 -0400470 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471}
472
473/*
474 * Arg:
475 * video : video bus device pointer
476 * bios_flag :
477 * 0. The system BIOS should NOT automatically switch(toggle)
478 * the active display output.
479 * 1. The system BIOS should automatically switch (toggle) the
480 * active display output. No swich event.
481 * 2. The _DGS value should be locked.
482 * 3. The system BIOS should not automatically switch (toggle) the
483 * active display output, but instead generate the display switch
484 * event notify code.
485 * lcd_flag :
486 * 0. The system BIOS should automatically control the brightness level
487 * of the LCD, when the power changes from AC to DC
488 * 1. The system BIOS should NOT automatically control the brightness
489 * level of the LCD, when the power changes from AC to DC.
490 * Return Value:
491 * -1 wrong arg.
492 */
493
494static int
Len Brown4be44fc2005-08-05 00:44:28 -0400495acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496{
Len Brown4be44fc2005-08-05 00:44:28 -0400497 acpi_integer status = 0;
498 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
499 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Len Brown4be44fc2005-08-05 00:44:28 -0400502 if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 status = -1;
504 goto Failed;
505 }
506 arg0.integer.value = (lcd_flag << 2) | bios_flag;
507 video->dos_setting = arg0.integer.value;
Patrick Mochel90130262006-05-19 16:54:48 -0400508 acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
Len Brown4be44fc2005-08-05 00:44:28 -0400510 Failed:
Patrick Mocheld550d982006-06-27 00:41:40 -0400511 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512}
513
514/*
515 * Arg:
516 * device : video output device (LCD, CRT, ..)
517 *
518 * Return Value:
519 * None
520 *
521 * Find out all required AML method defined under the output
522 * device.
523 */
524
Len Brown4be44fc2005-08-05 00:44:28 -0400525static void acpi_video_device_find_cap(struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526{
Len Brown4be44fc2005-08-05 00:44:28 -0400527 acpi_integer status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 acpi_handle h_dummy1;
529 int i;
Yu Luming2f3d0002006-11-11 02:40:34 +0800530 u32 max_level = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 union acpi_object *obj = NULL;
532 struct acpi_video_device_brightness *br = NULL;
533
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
Len Brown4be44fc2005-08-05 00:44:28 -0400535 memset(&device->cap, 0, 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Patrick Mochel90130262006-05-19 16:54:48 -0400537 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 device->cap._ADR = 1;
539 }
Patrick Mochel90130262006-05-19 16:54:48 -0400540 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400541 device->cap._BCL = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 }
Patrick Mochel90130262006-05-19 16:54:48 -0400543 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400544 device->cap._BCM = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 }
Yu Luming2f3d0002006-11-11 02:40:34 +0800546 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
547 device->cap._BQC = 1;
Patrick Mochel90130262006-05-19 16:54:48 -0400548 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400549 device->cap._DDC = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 }
Patrick Mochel90130262006-05-19 16:54:48 -0400551 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DCS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 device->cap._DCS = 1;
553 }
Patrick Mochel90130262006-05-19 16:54:48 -0400554 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DGS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 device->cap._DGS = 1;
556 }
Patrick Mochel90130262006-05-19 16:54:48 -0400557 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DSS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 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;
Len Brown4be44fc2005-08-05 00:44:28 -0400566
Paulo Marquesd1dd0c22005-03-30 22:39:49 -0500567 br = kmalloc(sizeof(*br), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 if (!br) {
569 printk(KERN_ERR "can't allocate memory\n");
570 } else {
Paulo Marquesd1dd0c22005-03-30 22:39:49 -0500571 memset(br, 0, sizeof(*br));
572 br->levels = kmalloc(obj->package.count *
Len Brown4be44fc2005-08-05 00:44:28 -0400573 sizeof *(br->levels), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 if (!br->levels)
575 goto out;
576
577 for (i = 0; i < obj->package.count; i++) {
Len Brown4be44fc2005-08-05 00:44:28 -0400578 o = (union acpi_object *)&obj->package.
579 elements[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 if (o->type != ACPI_TYPE_INTEGER) {
Len Brown64684632006-06-26 23:41:38 -0400581 printk(KERN_ERR PREFIX "Invalid data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 continue;
583 }
584 br->levels[count] = (u32) o->integer.value;
Yu Luming2f3d0002006-11-11 02:40:34 +0800585 if (br->levels[count] > max_level)
586 max_level = br->levels[count];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 count++;
588 }
Len Brown4be44fc2005-08-05 00:44:28 -0400589 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 if (count < 2) {
Paulo Marquesd1dd0c22005-03-30 22:39:49 -0500591 kfree(br->levels);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 kfree(br);
593 } else {
594 br->count = count;
595 device->brightness = br;
Len Brown4be44fc2005-08-05 00:44:28 -0400596 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
597 "found %d brightness levels\n",
598 count));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 }
600 }
601 }
602
Paulo Marquesd1dd0c22005-03-30 22:39:49 -0500603 kfree(obj);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604
Yu Luming2f3d0002006-11-11 02:40:34 +0800605 if (device->cap._BCL && device->cap._BCM && device->cap._BQC){
606 unsigned long tmp;
607 static int count = 0;
608 char *name;
609 struct backlight_properties *acpi_video_data;
610
611 name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
612 if (!name)
613 return;
614
615 acpi_video_data = kzalloc(
616 sizeof(struct backlight_properties),
617 GFP_KERNEL);
618 if (!acpi_video_data){
619 kfree(name);
620 return;
621 }
622 acpi_video_data->owner = THIS_MODULE;
623 acpi_video_data->get_brightness =
624 acpi_video_get_brightness;
625 acpi_video_data->update_status =
626 acpi_video_set_brightness;
627 sprintf(name, "acpi_video%d", count++);
628 device->data = acpi_video_data;
629 acpi_video_data->max_brightness = max_level;
630 acpi_video_device_lcd_get_level_current(device, &tmp);
631 acpi_video_data->brightness = (int)tmp;
632 device->backlight = backlight_device_register(name,
633 NULL, device, acpi_video_data);
634 kfree(name);
635 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400636 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637}
638
639/*
640 * Arg:
641 * device : video output device (VGA)
642 *
643 * Return Value:
644 * None
645 *
646 * Find out all required AML method defined under the video bus device.
647 */
648
Len Brown4be44fc2005-08-05 00:44:28 -0400649static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650{
Len Brown4be44fc2005-08-05 00:44:28 -0400651 acpi_handle h_dummy1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Len Brown4be44fc2005-08-05 00:44:28 -0400653 memset(&video->cap, 0, 4);
Patrick Mochel90130262006-05-19 16:54:48 -0400654 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 video->cap._DOS = 1;
656 }
Patrick Mochel90130262006-05-19 16:54:48 -0400657 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 video->cap._DOD = 1;
659 }
Patrick Mochel90130262006-05-19 16:54:48 -0400660 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 video->cap._ROM = 1;
662 }
Patrick Mochel90130262006-05-19 16:54:48 -0400663 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 video->cap._GPD = 1;
665 }
Patrick Mochel90130262006-05-19 16:54:48 -0400666 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 video->cap._SPD = 1;
668 }
Patrick Mochel90130262006-05-19 16:54:48 -0400669 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 video->cap._VPO = 1;
671 }
672}
673
674/*
675 * Check whether the video bus device has required AML method to
676 * support the desired features
677 */
678
Len Brown4be44fc2005-08-05 00:44:28 -0400679static int acpi_video_bus_check(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680{
Len Brown4be44fc2005-08-05 00:44:28 -0400681 acpi_status status = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683
684 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -0400685 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
687 /* Since there is no HID, CID and so on for VGA driver, we have
688 * to check well known required nodes.
689 */
690
691 /* Does this device able to support video switching ? */
Len Brown4be44fc2005-08-05 00:44:28 -0400692 if (video->cap._DOS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 video->flags.multihead = 1;
694 status = 0;
695 }
696
697 /* Does this device able to retrieve a retrieve a video ROM ? */
Len Brown4be44fc2005-08-05 00:44:28 -0400698 if (video->cap._ROM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 video->flags.rom = 1;
700 status = 0;
701 }
702
703 /* Does this device able to configure which video device to POST ? */
Len Brown4be44fc2005-08-05 00:44:28 -0400704 if (video->cap._GPD && video->cap._SPD && video->cap._VPO) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 video->flags.post = 1;
706 status = 0;
707 }
708
Patrick Mocheld550d982006-06-27 00:41:40 -0400709 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710}
711
712/* --------------------------------------------------------------------------
713 FS Interface (/proc)
714 -------------------------------------------------------------------------- */
715
Len Brown4be44fc2005-08-05 00:44:28 -0400716static struct proc_dir_entry *acpi_video_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
718/* video devices */
719
Len Brown4be44fc2005-08-05 00:44:28 -0400720static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721{
Len Brown4be44fc2005-08-05 00:44:28 -0400722 struct acpi_video_device *dev =
723 (struct acpi_video_device *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725
726 if (!dev)
727 goto end;
728
729 seq_printf(seq, "device_id: 0x%04x\n", (u32) dev->device_id);
730 seq_printf(seq, "type: ");
731 if (dev->flags.crt)
732 seq_printf(seq, "CRT\n");
733 else if (dev->flags.lcd)
734 seq_printf(seq, "LCD\n");
735 else if (dev->flags.tvout)
736 seq_printf(seq, "TVOUT\n");
737 else
738 seq_printf(seq, "UNKNOWN\n");
739
Len Brown4be44fc2005-08-05 00:44:28 -0400740 seq_printf(seq, "known by bios: %s\n", dev->flags.bios ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Len Brown4be44fc2005-08-05 00:44:28 -0400742 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400743 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744}
745
746static int
Len Brown4be44fc2005-08-05 00:44:28 -0400747acpi_video_device_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748{
749 return single_open(file, acpi_video_device_info_seq_show,
750 PDE(inode)->data);
751}
752
Len Brown4be44fc2005-08-05 00:44:28 -0400753static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754{
Len Brown4be44fc2005-08-05 00:44:28 -0400755 int status;
756 struct acpi_video_device *dev =
757 (struct acpi_video_device *)seq->private;
758 unsigned long state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
761 if (!dev)
762 goto end;
763
764 status = acpi_video_device_get_state(dev, &state);
765 seq_printf(seq, "state: ");
766 if (ACPI_SUCCESS(status))
767 seq_printf(seq, "0x%02lx\n", state);
768 else
769 seq_printf(seq, "<not supported>\n");
770
771 status = acpi_video_device_query(dev, &state);
772 seq_printf(seq, "query: ");
773 if (ACPI_SUCCESS(status))
774 seq_printf(seq, "0x%02lx\n", state);
775 else
776 seq_printf(seq, "<not supported>\n");
777
Len Brown4be44fc2005-08-05 00:44:28 -0400778 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400779 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780}
781
782static int
Len Brown4be44fc2005-08-05 00:44:28 -0400783acpi_video_device_state_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784{
785 return single_open(file, acpi_video_device_state_seq_show,
786 PDE(inode)->data);
787}
788
789static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400790acpi_video_device_write_state(struct file *file,
791 const char __user * buffer,
792 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793{
Len Brown4be44fc2005-08-05 00:44:28 -0400794 int status;
795 struct seq_file *m = (struct seq_file *)file->private_data;
796 struct acpi_video_device *dev = (struct acpi_video_device *)m->private;
797 char str[12] = { 0 };
798 u32 state = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
801 if (!dev || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -0400802 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803
804 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -0400805 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
807 str[count] = 0;
808 state = simple_strtoul(str, NULL, 0);
Len Brown4be44fc2005-08-05 00:44:28 -0400809 state &= ((1ul << 31) | (1ul << 30) | (1ul << 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
811 status = acpi_video_device_set_state(dev, state);
812
813 if (status)
Patrick Mocheld550d982006-06-27 00:41:40 -0400814 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
Patrick Mocheld550d982006-06-27 00:41:40 -0400816 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817}
818
819static int
Len Brown4be44fc2005-08-05 00:44:28 -0400820acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821{
Len Brown4be44fc2005-08-05 00:44:28 -0400822 struct acpi_video_device *dev =
823 (struct acpi_video_device *)seq->private;
824 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
827 if (!dev || !dev->brightness) {
828 seq_printf(seq, "<not supported>\n");
Patrick Mocheld550d982006-06-27 00:41:40 -0400829 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 }
831
832 seq_printf(seq, "levels: ");
833 for (i = 0; i < dev->brightness->count; i++)
834 seq_printf(seq, " %d", dev->brightness->levels[i]);
835 seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr);
836
Patrick Mocheld550d982006-06-27 00:41:40 -0400837 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838}
839
840static int
Len Brown4be44fc2005-08-05 00:44:28 -0400841acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842{
843 return single_open(file, acpi_video_device_brightness_seq_show,
844 PDE(inode)->data);
845}
846
847static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400848acpi_video_device_write_brightness(struct file *file,
849 const char __user * buffer,
850 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851{
Len Brown4be44fc2005-08-05 00:44:28 -0400852 struct seq_file *m = (struct seq_file *)file->private_data;
853 struct acpi_video_device *dev = (struct acpi_video_device *)m->private;
854 char str[4] = { 0 };
855 unsigned int level = 0;
856 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
Thomas Renninger59d399d2005-11-08 05:27:00 -0500859 if (!dev || !dev->brightness || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -0400860 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
862 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -0400863 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
865 str[count] = 0;
866 level = simple_strtoul(str, NULL, 0);
Len Brown4be44fc2005-08-05 00:44:28 -0400867
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 if (level > 100)
Patrick Mocheld550d982006-06-27 00:41:40 -0400869 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870
871 /* validate though the list of available levels */
872 for (i = 0; i < dev->brightness->count; i++)
873 if (level == dev->brightness->levels[i]) {
Len Brown4be44fc2005-08-05 00:44:28 -0400874 if (ACPI_SUCCESS
875 (acpi_video_device_lcd_set_level(dev, level)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 dev->brightness->curr = level;
877 break;
878 }
879
Patrick Mocheld550d982006-06-27 00:41:40 -0400880 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881}
882
Len Brown4be44fc2005-08-05 00:44:28 -0400883static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884{
Len Brown4be44fc2005-08-05 00:44:28 -0400885 struct acpi_video_device *dev =
886 (struct acpi_video_device *)seq->private;
887 int status;
888 int i;
889 union acpi_object *edid = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
892 if (!dev)
893 goto out;
894
Len Brown4be44fc2005-08-05 00:44:28 -0400895 status = acpi_video_device_EDID(dev, &edid, 128);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 if (ACPI_FAILURE(status)) {
Len Brown4be44fc2005-08-05 00:44:28 -0400897 status = acpi_video_device_EDID(dev, &edid, 256);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 }
899
900 if (ACPI_FAILURE(status)) {
901 goto out;
902 }
903
904 if (edid && edid->type == ACPI_TYPE_BUFFER) {
905 for (i = 0; i < edid->buffer.length; i++)
906 seq_putc(seq, edid->buffer.pointer[i]);
907 }
908
Len Brown4be44fc2005-08-05 00:44:28 -0400909 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 if (!edid)
911 seq_printf(seq, "<not supported>\n");
912 else
913 kfree(edid);
914
Patrick Mocheld550d982006-06-27 00:41:40 -0400915 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916}
917
918static int
Len Brown4be44fc2005-08-05 00:44:28 -0400919acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920{
921 return single_open(file, acpi_video_device_EDID_seq_show,
922 PDE(inode)->data);
923}
924
Len Brown4be44fc2005-08-05 00:44:28 -0400925static int acpi_video_device_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926{
Len Brown4be44fc2005-08-05 00:44:28 -0400927 struct proc_dir_entry *entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 struct acpi_video_device *vid_dev;
929
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
931 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400932 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933
Len Brown4be44fc2005-08-05 00:44:28 -0400934 vid_dev = (struct acpi_video_device *)acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 if (!vid_dev)
Patrick Mocheld550d982006-06-27 00:41:40 -0400936 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
938 if (!acpi_device_dir(device)) {
939 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -0400940 vid_dev->video->dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400942 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 acpi_device_dir(device)->owner = THIS_MODULE;
944 }
945
946 /* 'info' [R] */
947 entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device));
948 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400949 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 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] */
Len Brown4be44fc2005-08-05 00:44:28 -0400957 entry =
958 create_proc_entry("state", S_IFREG | S_IRUGO | S_IWUSR,
959 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400961 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 else {
Arjan van de Vend479e902006-01-06 16:47:00 -0500963 acpi_video_device_state_fops.write = acpi_video_device_write_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 entry->proc_fops = &acpi_video_device_state_fops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 entry->data = acpi_driver_data(device);
966 entry->owner = THIS_MODULE;
967 }
968
969 /* 'brightness' [R/W] */
Len Brown4be44fc2005-08-05 00:44:28 -0400970 entry =
971 create_proc_entry("brightness", S_IFREG | S_IRUGO | S_IWUSR,
972 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400974 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 else {
Arjan van de Vend479e902006-01-06 16:47:00 -0500976 acpi_video_device_brightness_fops.write = acpi_video_device_write_brightness;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 entry->proc_fops = &acpi_video_device_brightness_fops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 entry->data = acpi_driver_data(device);
979 entry->owner = THIS_MODULE;
980 }
981
982 /* 'EDID' [R] */
983 entry = create_proc_entry("EDID", S_IRUGO, acpi_device_dir(device));
984 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400985 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 else {
987 entry->proc_fops = &acpi_video_device_EDID_fops;
988 entry->data = acpi_driver_data(device);
989 entry->owner = THIS_MODULE;
990 }
991
Patrick Mocheld550d982006-06-27 00:41:40 -0400992 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993}
994
Len Brown4be44fc2005-08-05 00:44:28 -0400995static int acpi_video_device_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996{
997 struct acpi_video_device *vid_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998
Len Brown4be44fc2005-08-05 00:44:28 -0400999 vid_dev = (struct acpi_video_device *)acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001001 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
1003 if (acpi_device_dir(device)) {
1004 remove_proc_entry("info", acpi_device_dir(device));
1005 remove_proc_entry("state", acpi_device_dir(device));
1006 remove_proc_entry("brightness", acpi_device_dir(device));
1007 remove_proc_entry("EDID", acpi_device_dir(device));
Len Brown4be44fc2005-08-05 00:44:28 -04001008 remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 acpi_device_dir(device) = NULL;
1010 }
1011
Patrick Mocheld550d982006-06-27 00:41:40 -04001012 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013}
1014
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015/* video bus */
Len Brown4be44fc2005-08-05 00:44:28 -04001016static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017{
Len Brown4be44fc2005-08-05 00:44:28 -04001018 struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020
1021 if (!video)
1022 goto end;
1023
1024 seq_printf(seq, "Switching heads: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001025 video->flags.multihead ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 seq_printf(seq, "Video ROM: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001027 video->flags.rom ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 seq_printf(seq, "Device to be POSTed on boot: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001029 video->flags.post ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030
Len Brown4be44fc2005-08-05 00:44:28 -04001031 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001032 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033}
1034
Len Brown4be44fc2005-08-05 00:44:28 -04001035static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036{
Len Brown4be44fc2005-08-05 00:44:28 -04001037 return single_open(file, acpi_video_bus_info_seq_show,
1038 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039}
1040
Len Brown4be44fc2005-08-05 00:44:28 -04001041static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042{
Len Brown4be44fc2005-08-05 00:44:28 -04001043 struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045
1046 if (!video)
1047 goto end;
1048
1049 printk(KERN_INFO PREFIX "Please implement %s\n", __FUNCTION__);
1050 seq_printf(seq, "<TODO>\n");
1051
Len Brown4be44fc2005-08-05 00:44:28 -04001052 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001053 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054}
1055
Len Brown4be44fc2005-08-05 00:44:28 -04001056static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057{
1058 return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data);
1059}
1060
Len Brown4be44fc2005-08-05 00:44:28 -04001061static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062{
Len Brown4be44fc2005-08-05 00:44:28 -04001063 struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
1064 unsigned long options;
1065 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
1068 if (!video)
1069 goto end;
1070
1071 status = acpi_video_bus_POST_options(video, &options);
1072 if (ACPI_SUCCESS(status)) {
1073 if (!(options & 1)) {
Len Brown4be44fc2005-08-05 00:44:28 -04001074 printk(KERN_WARNING PREFIX
1075 "The motherboard VGA device is not listed as a possible POST device.\n");
1076 printk(KERN_WARNING PREFIX
1077 "This indicate a BIOS bug. Please contact the manufacturer.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 }
1079 printk("%lx\n", options);
1080 seq_printf(seq, "can POST: <intgrated video>");
1081 if (options & 2)
1082 seq_printf(seq, " <PCI video>");
1083 if (options & 4)
1084 seq_printf(seq, " <AGP video>");
1085 seq_putc(seq, '\n');
1086 } else
1087 seq_printf(seq, "<not supported>\n");
Len Brown4be44fc2005-08-05 00:44:28 -04001088 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001089 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090}
1091
1092static int
Len Brown4be44fc2005-08-05 00:44:28 -04001093acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094{
Len Brown4be44fc2005-08-05 00:44:28 -04001095 return single_open(file, acpi_video_bus_POST_info_seq_show,
1096 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097}
1098
Len Brown4be44fc2005-08-05 00:44:28 -04001099static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100{
Len Brown4be44fc2005-08-05 00:44:28 -04001101 struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
1102 int status;
1103 unsigned long id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105
1106 if (!video)
1107 goto end;
1108
Len Brown4be44fc2005-08-05 00:44:28 -04001109 status = acpi_video_bus_get_POST(video, &id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 if (!ACPI_SUCCESS(status)) {
1111 seq_printf(seq, "<not supported>\n");
1112 goto end;
1113 }
Len Brown4be44fc2005-08-05 00:44:28 -04001114 seq_printf(seq, "device posted is <%s>\n", device_decode[id & 3]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115
Len Brown4be44fc2005-08-05 00:44:28 -04001116 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001117 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118}
1119
Len Brown4be44fc2005-08-05 00:44:28 -04001120static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121{
Len Brown4be44fc2005-08-05 00:44:28 -04001122 struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124
Len Brown4be44fc2005-08-05 00:44:28 -04001125 seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126
Patrick Mocheld550d982006-06-27 00:41:40 -04001127 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128}
1129
Len Brown4be44fc2005-08-05 00:44:28 -04001130static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131{
Len Brown4be44fc2005-08-05 00:44:28 -04001132 return single_open(file, acpi_video_bus_POST_seq_show,
1133 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134}
1135
Len Brown4be44fc2005-08-05 00:44:28 -04001136static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137{
1138 return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data);
1139}
1140
1141static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001142acpi_video_bus_write_POST(struct file *file,
1143 const char __user * buffer,
1144 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145{
Len Brown4be44fc2005-08-05 00:44:28 -04001146 int status;
1147 struct seq_file *m = (struct seq_file *)file->private_data;
1148 struct acpi_video_bus *video = (struct acpi_video_bus *)m->private;
1149 char str[12] = { 0 };
1150 unsigned long opt, options;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 if (!video || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -04001154 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155
1156 status = acpi_video_bus_POST_options(video, &options);
1157 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001158 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159
1160 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001161 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162
1163 str[count] = 0;
1164 opt = strtoul(str, NULL, 0);
1165 if (opt > 3)
Patrick Mocheld550d982006-06-27 00:41:40 -04001166 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167
1168 /* just in case an OEM 'forget' the motherboard... */
1169 options |= 1;
1170
1171 if (options & (1ul << opt)) {
Len Brown4be44fc2005-08-05 00:44:28 -04001172 status = acpi_video_bus_set_POST(video, opt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001174 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175
1176 }
1177
Patrick Mocheld550d982006-06-27 00:41:40 -04001178 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179}
1180
1181static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001182acpi_video_bus_write_DOS(struct file *file,
1183 const char __user * buffer,
1184 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185{
Len Brown4be44fc2005-08-05 00:44:28 -04001186 int status;
1187 struct seq_file *m = (struct seq_file *)file->private_data;
1188 struct acpi_video_bus *video = (struct acpi_video_bus *)m->private;
1189 char str[12] = { 0 };
1190 unsigned long opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 if (!video || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -04001194 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195
1196 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001197 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198
1199 str[count] = 0;
1200 opt = strtoul(str, NULL, 0);
1201 if (opt > 7)
Patrick Mocheld550d982006-06-27 00:41:40 -04001202 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203
Len Brown4be44fc2005-08-05 00:44:28 -04001204 status = acpi_video_bus_DOS(video, opt & 0x3, (opt & 0x4) >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205
1206 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001207 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208
Patrick Mocheld550d982006-06-27 00:41:40 -04001209 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210}
1211
Len Brown4be44fc2005-08-05 00:44:28 -04001212static int acpi_video_bus_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213{
Len Brown4be44fc2005-08-05 00:44:28 -04001214 struct proc_dir_entry *entry = NULL;
1215 struct acpi_video_bus *video;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
Len Brown4be44fc2005-08-05 00:44:28 -04001218 video = (struct acpi_video_bus *)acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219
1220 if (!acpi_device_dir(device)) {
1221 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -04001222 acpi_video_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001224 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 video->dir = acpi_device_dir(device);
1226 acpi_device_dir(device)->owner = THIS_MODULE;
1227 }
1228
1229 /* 'info' [R] */
1230 entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device));
1231 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001232 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 else {
1234 entry->proc_fops = &acpi_video_bus_info_fops;
1235 entry->data = acpi_driver_data(device);
1236 entry->owner = THIS_MODULE;
1237 }
1238
1239 /* 'ROM' [R] */
1240 entry = create_proc_entry("ROM", S_IRUGO, acpi_device_dir(device));
1241 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001242 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 else {
1244 entry->proc_fops = &acpi_video_bus_ROM_fops;
1245 entry->data = acpi_driver_data(device);
1246 entry->owner = THIS_MODULE;
1247 }
1248
1249 /* 'POST_info' [R] */
Len Brown4be44fc2005-08-05 00:44:28 -04001250 entry =
1251 create_proc_entry("POST_info", S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001253 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 else {
1255 entry->proc_fops = &acpi_video_bus_POST_info_fops;
1256 entry->data = acpi_driver_data(device);
1257 entry->owner = THIS_MODULE;
1258 }
1259
1260 /* 'POST' [R/W] */
Len Brown4be44fc2005-08-05 00:44:28 -04001261 entry =
1262 create_proc_entry("POST", S_IFREG | S_IRUGO | S_IRUSR,
1263 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001265 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 else {
Arjan van de Vend479e902006-01-06 16:47:00 -05001267 acpi_video_bus_POST_fops.write = acpi_video_bus_write_POST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 entry->proc_fops = &acpi_video_bus_POST_fops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 entry->data = acpi_driver_data(device);
1270 entry->owner = THIS_MODULE;
1271 }
1272
1273 /* 'DOS' [R/W] */
Len Brown4be44fc2005-08-05 00:44:28 -04001274 entry =
1275 create_proc_entry("DOS", S_IFREG | S_IRUGO | S_IRUSR,
1276 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001278 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 else {
Arjan van de Vend479e902006-01-06 16:47:00 -05001280 acpi_video_bus_DOS_fops.write = acpi_video_bus_write_DOS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 entry->proc_fops = &acpi_video_bus_DOS_fops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 entry->data = acpi_driver_data(device);
1283 entry->owner = THIS_MODULE;
1284 }
1285
Patrick Mocheld550d982006-06-27 00:41:40 -04001286 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287}
1288
Len Brown4be44fc2005-08-05 00:44:28 -04001289static int acpi_video_bus_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290{
Len Brown4be44fc2005-08-05 00:44:28 -04001291 struct acpi_video_bus *video;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293
Len Brown4be44fc2005-08-05 00:44:28 -04001294 video = (struct acpi_video_bus *)acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295
1296 if (acpi_device_dir(device)) {
1297 remove_proc_entry("info", acpi_device_dir(device));
1298 remove_proc_entry("ROM", acpi_device_dir(device));
1299 remove_proc_entry("POST_info", acpi_device_dir(device));
1300 remove_proc_entry("POST", acpi_device_dir(device));
1301 remove_proc_entry("DOS", acpi_device_dir(device));
Len Brown4be44fc2005-08-05 00:44:28 -04001302 remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 acpi_device_dir(device) = NULL;
1304 }
1305
Patrick Mocheld550d982006-06-27 00:41:40 -04001306 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307}
1308
1309/* --------------------------------------------------------------------------
1310 Driver Interface
1311 -------------------------------------------------------------------------- */
1312
1313/* device interface */
1314
1315static int
Len Brown4be44fc2005-08-05 00:44:28 -04001316acpi_video_bus_get_one_device(struct acpi_device *device,
1317 struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318{
Len Brown4be44fc2005-08-05 00:44:28 -04001319 unsigned long device_id;
Yu, Luming973bf492006-04-27 05:25:00 -04001320 int status;
Len Brown4be44fc2005-08-05 00:44:28 -04001321 struct acpi_video_device *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323
1324 if (!device || !video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001325 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326
Len Brown4be44fc2005-08-05 00:44:28 -04001327 status =
1328 acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 if (ACPI_SUCCESS(status)) {
1330
1331 data = kmalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
1332 if (!data)
Patrick Mocheld550d982006-06-27 00:41:40 -04001333 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334
1335 memset(data, 0, sizeof(struct acpi_video_device));
1336
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
1338 strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
1339 acpi_driver_data(device) = data;
1340
1341 data->device_id = device_id;
1342 data->video = video;
1343 data->dev = device;
1344
1345 switch (device_id & 0xffff) {
1346 case 0x0100:
1347 data->flags.crt = 1;
1348 break;
1349 case 0x0400:
1350 data->flags.lcd = 1;
1351 break;
1352 case 0x0200:
1353 data->flags.tvout = 1;
1354 break;
1355 default:
1356 data->flags.unknown = 1;
1357 break;
1358 }
Len Brown4be44fc2005-08-05 00:44:28 -04001359
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 acpi_video_device_bind(video, data);
1361 acpi_video_device_find_cap(data);
1362
Patrick Mochel90130262006-05-19 16:54:48 -04001363 status = acpi_install_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001364 ACPI_DEVICE_NOTIFY,
1365 acpi_video_device_notify,
1366 data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 if (ACPI_FAILURE(status)) {
1368 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
Len Brown4be44fc2005-08-05 00:44:28 -04001369 "Error installing notify handler\n"));
Yu, Luming973bf492006-04-27 05:25:00 -04001370 if(data->brightness)
1371 kfree(data->brightness->levels);
1372 kfree(data->brightness);
1373 kfree(data);
1374 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 }
1376
1377 down(&video->sem);
1378 list_add_tail(&data->entry, &video->video_device_list);
1379 up(&video->sem);
1380
1381 acpi_video_device_add_fs(device);
1382
Patrick Mocheld550d982006-06-27 00:41:40 -04001383 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 }
1385
Patrick Mocheld550d982006-06-27 00:41:40 -04001386 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387}
1388
1389/*
1390 * Arg:
1391 * video : video bus device
1392 *
1393 * Return:
1394 * none
1395 *
1396 * Enumerate the video device list of the video bus,
1397 * bind the ids with the corresponding video devices
1398 * under the video bus.
Len Brown4be44fc2005-08-05 00:44:28 -04001399 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400
Len Brown4be44fc2005-08-05 00:44:28 -04001401static void acpi_video_device_rebind(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402{
Len Brown4be44fc2005-08-05 00:44:28 -04001403 struct list_head *node, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 list_for_each_safe(node, next, &video->video_device_list) {
Len Brown4be44fc2005-08-05 00:44:28 -04001405 struct acpi_video_device *dev =
1406 container_of(node, struct acpi_video_device, entry);
1407 acpi_video_device_bind(video, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 }
1409}
1410
1411/*
1412 * Arg:
1413 * video : video bus device
1414 * device : video output device under the video
1415 * bus
1416 *
1417 * Return:
1418 * none
1419 *
1420 * Bind the ids with the corresponding video devices
1421 * under the video bus.
Len Brown4be44fc2005-08-05 00:44:28 -04001422 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423
1424static void
Len Brown4be44fc2005-08-05 00:44:28 -04001425acpi_video_device_bind(struct acpi_video_bus *video,
1426 struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427{
Len Brown4be44fc2005-08-05 00:44:28 -04001428 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429
1430#define IDS_VAL(i) video->attached_array[i].value.int_val
1431#define IDS_BIND(i) video->attached_array[i].bind_info
Len Brown4be44fc2005-08-05 00:44:28 -04001432
1433 for (i = 0; IDS_VAL(i) != ACPI_VIDEO_HEAD_INVALID &&
1434 i < video->attached_count; i++) {
1435 if (device->device_id == (IDS_VAL(i) & 0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 IDS_BIND(i) = device;
1437 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i));
1438 }
1439 }
1440#undef IDS_VAL
1441#undef IDS_BIND
1442}
1443
1444/*
1445 * Arg:
1446 * video : video bus device
1447 *
1448 * Return:
1449 * < 0 : error
1450 *
1451 * Call _DOD to enumerate all devices attached to display adapter
1452 *
Len Brown4be44fc2005-08-05 00:44:28 -04001453 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454
1455static int acpi_video_device_enumerate(struct acpi_video_bus *video)
1456{
Len Brown4be44fc2005-08-05 00:44:28 -04001457 int status;
1458 int count;
1459 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 struct acpi_video_enumerated_device *active_device_list;
Len Brown4be44fc2005-08-05 00:44:28 -04001461 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1462 union acpi_object *dod = NULL;
1463 union acpi_object *obj;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464
Patrick Mochel90130262006-05-19 16:54:48 -04001465 status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 if (!ACPI_SUCCESS(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001467 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD"));
Patrick Mocheld550d982006-06-27 00:41:40 -04001468 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 }
1470
Len Brown4be44fc2005-08-05 00:44:28 -04001471 dod = (union acpi_object *)buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001473 ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 status = -EFAULT;
1475 goto out;
1476 }
1477
1478 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001479 dod->package.count));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480
Len Brown4be44fc2005-08-05 00:44:28 -04001481 active_device_list = kmalloc((1 +
1482 dod->package.count) *
1483 sizeof(struct
1484 acpi_video_enumerated_device),
1485 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486
1487 if (!active_device_list) {
1488 status = -ENOMEM;
1489 goto out;
1490 }
1491
1492 count = 0;
1493 for (i = 0; i < dod->package.count; i++) {
Len Brown4be44fc2005-08-05 00:44:28 -04001494 obj = (union acpi_object *)&dod->package.elements[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495
1496 if (obj->type != ACPI_TYPE_INTEGER) {
Len Brown64684632006-06-26 23:41:38 -04001497 printk(KERN_ERR PREFIX "Invalid _DOD data\n");
Len Brown4be44fc2005-08-05 00:44:28 -04001498 active_device_list[i].value.int_val =
1499 ACPI_VIDEO_HEAD_INVALID;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 }
1501 active_device_list[i].value.int_val = obj->integer.value;
1502 active_device_list[i].bind_info = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -04001503 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i,
1504 (int)obj->integer.value));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 count++;
1506 }
1507 active_device_list[count].value.int_val = ACPI_VIDEO_HEAD_END;
1508
Jesper Juhl6044ec82005-11-07 01:01:32 -08001509 kfree(video->attached_array);
Len Brown4be44fc2005-08-05 00:44:28 -04001510
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 video->attached_array = active_device_list;
1512 video->attached_count = count;
Len Brown4be44fc2005-08-05 00:44:28 -04001513 out:
Len Brown02438d82006-06-30 03:19:10 -04001514 kfree(buffer.pointer);
Patrick Mocheld550d982006-06-27 00:41:40 -04001515 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516}
1517
1518/*
1519 * Arg:
1520 * video : video bus device
1521 * event : Nontify Event
1522 *
1523 * Return:
1524 * < 0 : error
1525 *
1526 * 1. Find out the current active output device.
1527 * 2. Identify the next output device to switch
1528 * 3. call _DSS to do actual switch.
Len Brown4be44fc2005-08-05 00:44:28 -04001529 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530
Len Brown4be44fc2005-08-05 00:44:28 -04001531static int acpi_video_switch_output(struct acpi_video_bus *video, int event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532{
Len Brown4be44fc2005-08-05 00:44:28 -04001533 struct list_head *node, *next;
1534 struct acpi_video_device *dev = NULL;
1535 struct acpi_video_device *dev_next = NULL;
1536 struct acpi_video_device *dev_prev = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 unsigned long state;
1538 int status = 0;
1539
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540
1541 list_for_each_safe(node, next, &video->video_device_list) {
Adrian Bunk73345712005-03-30 22:31:35 -05001542 dev = container_of(node, struct acpi_video_device, entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 status = acpi_video_device_get_state(dev, &state);
Len Brown4be44fc2005-08-05 00:44:28 -04001544 if (state & 0x2) {
1545 dev_next =
1546 container_of(node->next, struct acpi_video_device,
1547 entry);
1548 dev_prev =
1549 container_of(node->prev, struct acpi_video_device,
1550 entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 goto out;
1552 }
1553 }
1554 dev_next = container_of(node->next, struct acpi_video_device, entry);
1555 dev_prev = container_of(node->prev, struct acpi_video_device, entry);
Len Brown4be44fc2005-08-05 00:44:28 -04001556 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 switch (event) {
1558 case ACPI_VIDEO_NOTIFY_CYCLE:
1559 case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:
1560 acpi_video_device_set_state(dev, 0);
1561 acpi_video_device_set_state(dev_next, 0x80000001);
1562 break;
1563 case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:
1564 acpi_video_device_set_state(dev, 0);
1565 acpi_video_device_set_state(dev_prev, 0x80000001);
1566 default:
1567 break;
1568 }
1569
Patrick Mocheld550d982006-06-27 00:41:40 -04001570 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571}
1572
Len Brown4be44fc2005-08-05 00:44:28 -04001573static int
1574acpi_video_get_next_level(struct acpi_video_device *device,
1575 u32 level_current, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576{
Thomas Tuttlef4715182006-12-19 12:56:14 -08001577 int min, max, min_above, max_below, i, l;
1578 max = max_below = 0;
1579 min = min_above = 255;
1580 for (i = 0; i < device->brightness->count; i++) {
1581 l = device->brightness->levels[i];
1582 if (l < min)
1583 min = l;
1584 if (l > max)
1585 max = l;
1586 if (l < min_above && l > level_current)
1587 min_above = l;
1588 if (l > max_below && l < level_current)
1589 max_below = l;
1590 }
1591
1592 switch (event) {
1593 case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:
1594 return (level_current < max) ? min_above : min;
1595 case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:
1596 return (level_current < max) ? min_above : max;
1597 case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:
1598 return (level_current > min) ? max_below : min;
1599 case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:
1600 case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:
1601 return 0;
1602 default:
1603 return level_current;
1604 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605}
1606
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607static void
Len Brown4be44fc2005-08-05 00:44:28 -04001608acpi_video_switch_brightness(struct acpi_video_device *device, int event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609{
1610 unsigned long level_current, level_next;
1611 acpi_video_device_lcd_get_level_current(device, &level_current);
1612 level_next = acpi_video_get_next_level(device, level_current, event);
1613 acpi_video_device_lcd_set_level(device, level_next);
1614}
1615
1616static int
Len Brown4be44fc2005-08-05 00:44:28 -04001617acpi_video_bus_get_devices(struct acpi_video_bus *video,
1618 struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619{
Len Brown4be44fc2005-08-05 00:44:28 -04001620 int status = 0;
1621 struct list_head *node, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623
1624 acpi_video_device_enumerate(video);
1625
1626 list_for_each_safe(node, next, &device->children) {
Len Brown4be44fc2005-08-05 00:44:28 -04001627 struct acpi_device *dev =
1628 list_entry(node, struct acpi_device, node);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629
1630 if (!dev)
1631 continue;
1632
1633 status = acpi_video_bus_get_one_device(dev, video);
1634 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001635 ACPI_EXCEPTION((AE_INFO, status, "Cant attach device"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 continue;
1637 }
1638
1639 }
Patrick Mocheld550d982006-06-27 00:41:40 -04001640 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641}
1642
Len Brown4be44fc2005-08-05 00:44:28 -04001643static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644{
Karol Kozimor031ec772005-07-30 04:18:00 -04001645 acpi_status status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 struct acpi_video_bus *video;
1647
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
1649 if (!device || !device->video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001650 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
1652 video = device->video;
1653
1654 down(&video->sem);
1655 list_del(&device->entry);
1656 up(&video->sem);
1657 acpi_video_device_remove_fs(device->dev);
1658
Patrick Mochel90130262006-05-19 16:54:48 -04001659 status = acpi_remove_notify_handler(device->dev->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001660 ACPI_DEVICE_NOTIFY,
1661 acpi_video_device_notify);
Yu Luming2f3d0002006-11-11 02:40:34 +08001662 if (device->backlight){
1663 backlight_device_unregister(device->backlight);
1664 kfree(device->data);
1665 }
Patrick Mocheld550d982006-06-27 00:41:40 -04001666 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667}
1668
Len Brown4be44fc2005-08-05 00:44:28 -04001669static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670{
Len Brown4be44fc2005-08-05 00:44:28 -04001671 int status;
1672 struct list_head *node, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674
1675 list_for_each_safe(node, next, &video->video_device_list) {
Len Brown4be44fc2005-08-05 00:44:28 -04001676 struct acpi_video_device *data =
1677 list_entry(node, struct acpi_video_device, entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678 if (!data)
1679 continue;
1680
1681 status = acpi_video_bus_put_one_device(data);
Len Brown4be44fc2005-08-05 00:44:28 -04001682 if (ACPI_FAILURE(status))
1683 printk(KERN_WARNING PREFIX
1684 "hhuuhhuu bug in acpi video driver.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685
Dave Jonesd384ea62006-06-24 00:33:08 -04001686 if (data->brightness)
Yu, Luming973bf492006-04-27 05:25:00 -04001687 kfree(data->brightness->levels);
Jesper Juhl6044ec82005-11-07 01:01:32 -08001688 kfree(data->brightness);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 kfree(data);
1690 }
1691
Patrick Mocheld550d982006-06-27 00:41:40 -04001692 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693}
1694
1695/* acpi_video interface */
1696
Len Brown4be44fc2005-08-05 00:44:28 -04001697static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698{
1699 return acpi_video_bus_DOS(video, 1, 0);
1700}
1701
Len Brown4be44fc2005-08-05 00:44:28 -04001702static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703{
1704 return acpi_video_bus_DOS(video, 0, 1);
1705}
1706
Len Brown4be44fc2005-08-05 00:44:28 -04001707static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708{
Len Brown4be44fc2005-08-05 00:44:28 -04001709 struct acpi_video_bus *video = (struct acpi_video_bus *)data;
1710 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 printk("video bus notify\n");
1713
1714 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001715 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716
Patrick Mochele6afa0d2006-05-19 16:54:40 -04001717 device = video->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718
1719 switch (event) {
1720 case ACPI_VIDEO_NOTIFY_SWITCH: /* User request that a switch occur,
1721 * most likely via hotkey. */
1722 acpi_bus_generate_event(device, event, 0);
1723 break;
1724
1725 case ACPI_VIDEO_NOTIFY_PROBE: /* User plug or remove a video
1726 * connector. */
1727 acpi_video_device_enumerate(video);
1728 acpi_video_device_rebind(video);
1729 acpi_video_switch_output(video, event);
1730 acpi_bus_generate_event(device, event, 0);
1731 break;
1732
Len Brown4be44fc2005-08-05 00:44:28 -04001733 case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */
1734 case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */
1735 case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 acpi_video_switch_output(video, event);
1737 acpi_bus_generate_event(device, event, 0);
1738 break;
1739
1740 default:
1741 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -04001742 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 break;
1744 }
1745
Patrick Mocheld550d982006-06-27 00:41:40 -04001746 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747}
1748
Len Brown4be44fc2005-08-05 00:44:28 -04001749static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750{
Len Brown4be44fc2005-08-05 00:44:28 -04001751 struct acpi_video_device *video_device =
1752 (struct acpi_video_device *)data;
1753 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755
1756 printk("video device notify\n");
1757 if (!video_device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001758 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759
Patrick Mochele6afa0d2006-05-19 16:54:40 -04001760 device = video_device->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761
1762 switch (event) {
Len Brown4be44fc2005-08-05 00:44:28 -04001763 case ACPI_VIDEO_NOTIFY_SWITCH: /* change in status (cycle output device) */
1764 case ACPI_VIDEO_NOTIFY_PROBE: /* change in status (output device status) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765 acpi_bus_generate_event(device, event, 0);
1766 break;
Len Brown4be44fc2005-08-05 00:44:28 -04001767 case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */
1768 case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */
1769 case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */
1770 case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightnesss */
1771 case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */
1772 acpi_video_switch_brightness(video_device, event);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 acpi_bus_generate_event(device, event, 0);
1774 break;
1775 default:
1776 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -04001777 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 break;
1779 }
Patrick Mocheld550d982006-06-27 00:41:40 -04001780 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781}
1782
Len Brown4be44fc2005-08-05 00:44:28 -04001783static int acpi_video_bus_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784{
Len Brown4be44fc2005-08-05 00:44:28 -04001785 int result = 0;
1786 acpi_status status = 0;
1787 struct acpi_video_bus *video = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788
Len Brown4be44fc2005-08-05 00:44:28 -04001789
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001791 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792
1793 video = kmalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
1794 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001795 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 memset(video, 0, sizeof(struct acpi_video_bus));
1797
Patrick Mochele6afa0d2006-05-19 16:54:40 -04001798 video->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
1800 strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
1801 acpi_driver_data(device) = video;
1802
1803 acpi_video_bus_find_cap(video);
1804 result = acpi_video_bus_check(video);
1805 if (result)
1806 goto end;
1807
1808 result = acpi_video_bus_add_fs(device);
1809 if (result)
1810 goto end;
1811
1812 init_MUTEX(&video->sem);
1813 INIT_LIST_HEAD(&video->video_device_list);
1814
1815 acpi_video_bus_get_devices(video, device);
1816 acpi_video_bus_start_devices(video);
1817
Patrick Mochel90130262006-05-19 16:54:48 -04001818 status = acpi_install_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001819 ACPI_DEVICE_NOTIFY,
1820 acpi_video_bus_notify, video);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821 if (ACPI_FAILURE(status)) {
1822 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
Len Brown4be44fc2005-08-05 00:44:28 -04001823 "Error installing notify handler\n"));
Yu, Luming973bf492006-04-27 05:25:00 -04001824 acpi_video_bus_stop_devices(video);
1825 acpi_video_bus_put_devices(video);
1826 kfree(video->attached_array);
1827 acpi_video_bus_remove_fs(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828 result = -ENODEV;
1829 goto end;
1830 }
1831
1832 printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001833 ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
1834 video->flags.multihead ? "yes" : "no",
1835 video->flags.rom ? "yes" : "no",
1836 video->flags.post ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837
Len Brown4be44fc2005-08-05 00:44:28 -04001838 end:
Yu, Luming973bf492006-04-27 05:25:00 -04001839 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840 kfree(video);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841
Patrick Mocheld550d982006-06-27 00:41:40 -04001842 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843}
1844
Len Brown4be44fc2005-08-05 00:44:28 -04001845static int acpi_video_bus_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846{
Len Brown4be44fc2005-08-05 00:44:28 -04001847 acpi_status status = 0;
1848 struct acpi_video_bus *video = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850
1851 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001852 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853
Len Brown4be44fc2005-08-05 00:44:28 -04001854 video = (struct acpi_video_bus *)acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855
1856 acpi_video_bus_stop_devices(video);
1857
Patrick Mochel90130262006-05-19 16:54:48 -04001858 status = acpi_remove_notify_handler(video->device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001859 ACPI_DEVICE_NOTIFY,
1860 acpi_video_bus_notify);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861
1862 acpi_video_bus_put_devices(video);
1863 acpi_video_bus_remove_fs(device);
1864
Jesper Juhl6044ec82005-11-07 01:01:32 -08001865 kfree(video->attached_array);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866 kfree(video);
1867
Patrick Mocheld550d982006-06-27 00:41:40 -04001868 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869}
1870
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871static int
Len Brown4be44fc2005-08-05 00:44:28 -04001872acpi_video_bus_match(struct acpi_device *device, struct acpi_driver *driver)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873{
Len Brown4be44fc2005-08-05 00:44:28 -04001874 acpi_handle h_dummy1;
1875 acpi_handle h_dummy2;
1876 acpi_handle h_dummy3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878
1879 if (!device || !driver)
Patrick Mocheld550d982006-06-27 00:41:40 -04001880 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881
1882 /* Since there is no HID, CID for ACPI Video drivers, we have
1883 * to check well known required nodes for each feature we support.
1884 */
1885
1886 /* Does this device able to support video switching ? */
1887 if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy1)) &&
1888 ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy2)))
Patrick Mocheld550d982006-06-27 00:41:40 -04001889 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890
1891 /* Does this device able to retrieve a video ROM ? */
1892 if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy1)))
Patrick Mocheld550d982006-06-27 00:41:40 -04001893 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894
1895 /* Does this device able to configure which video head to be POSTed ? */
1896 if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy1)) &&
1897 ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy2)) &&
1898 ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy3)))
Patrick Mocheld550d982006-06-27 00:41:40 -04001899 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900
Patrick Mocheld550d982006-06-27 00:41:40 -04001901 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902}
1903
Len Brown4be44fc2005-08-05 00:44:28 -04001904static int __init acpi_video_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905{
Len Brown4be44fc2005-08-05 00:44:28 -04001906 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908
1909 /*
Len Brown4be44fc2005-08-05 00:44:28 -04001910 acpi_dbg_level = 0xFFFFFFFF;
1911 acpi_dbg_layer = 0x08000000;
1912 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913
1914 acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
1915 if (!acpi_video_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001916 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917 acpi_video_dir->owner = THIS_MODULE;
1918
1919 result = acpi_bus_register_driver(&acpi_video_bus);
1920 if (result < 0) {
1921 remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -04001922 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 }
1924
Patrick Mocheld550d982006-06-27 00:41:40 -04001925 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926}
1927
Len Brown4be44fc2005-08-05 00:44:28 -04001928static void __exit acpi_video_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930
1931 acpi_bus_unregister_driver(&acpi_video_bus);
1932
1933 remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
1934
Patrick Mocheld550d982006-06-27 00:41:40 -04001935 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936}
1937
1938module_init(acpi_video_init);
1939module_exit(acpi_video_exit);