blob: e0b97add8c6355274cb220b04352c9927263b5d2 [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
Rui Zhang82cae992007-01-03 23:40:53 -050062#define ACPI_VIDEO_DISPLAY_CRT 1
63#define ACPI_VIDEO_DISPLAY_TV 2
64#define ACPI_VIDEO_DISPLAY_DVI 3
65#define ACPI_VIDEO_DISPLAY_LCD 4
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067#define _COMPONENT ACPI_VIDEO_COMPONENT
Len Brown4be44fc2005-08-05 00:44:28 -040068ACPI_MODULE_NAME("acpi_video")
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
Len Brown4be44fc2005-08-05 00:44:28 -040070 MODULE_AUTHOR("Bruno Ducrot");
Linus Torvalds1da177e2005-04-16 15:20:36 -070071MODULE_DESCRIPTION(ACPI_VIDEO_DRIVER_NAME);
72MODULE_LICENSE("GPL");
73
Len Brown4be44fc2005-08-05 00:44:28 -040074static int acpi_video_bus_add(struct acpi_device *device);
75static int acpi_video_bus_remove(struct acpi_device *device, int type);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
77static struct acpi_driver acpi_video_bus = {
78 .name = ACPI_VIDEO_DRIVER_NAME,
79 .class = ACPI_VIDEO_CLASS,
Zhang Ruiae843332006-12-07 20:57:10 +080080 .ids = ACPI_VIDEO_HID,
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 .ops = {
82 .add = acpi_video_bus_add,
83 .remove = acpi_video_bus_remove,
Len Brown4be44fc2005-08-05 00:44:28 -040084 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070085};
86
87struct acpi_video_bus_flags {
Len Brown4be44fc2005-08-05 00:44:28 -040088 u8 multihead:1; /* can switch video heads */
89 u8 rom:1; /* can retrieve a video rom */
90 u8 post:1; /* can configure the head to */
91 u8 reserved:5;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092};
93
94struct acpi_video_bus_cap {
Len Brown4be44fc2005-08-05 00:44:28 -040095 u8 _DOS:1; /*Enable/Disable output switching */
96 u8 _DOD:1; /*Enumerate all devices attached to display adapter */
97 u8 _ROM:1; /*Get ROM Data */
98 u8 _GPD:1; /*Get POST Device */
99 u8 _SPD:1; /*Set POST Device */
100 u8 _VPO:1; /*Video POST Options */
101 u8 reserved:2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102};
103
Len Brown4be44fc2005-08-05 00:44:28 -0400104struct acpi_video_device_attrib {
105 u32 display_index:4; /* A zero-based instance of the Display */
106 u32 display_port_attachment:4; /*This field differenates displays type */
107 u32 display_type:4; /*Describe the specific type in use */
108 u32 vendor_specific:4; /*Chipset Vendor Specifi */
109 u32 bios_can_detect:1; /*BIOS can detect the device */
110 u32 depend_on_vga:1; /*Non-VGA output device whose power is related to
111 the VGA device. */
112 u32 pipe_id:3; /*For VGA multiple-head devices. */
113 u32 reserved:10; /*Must be 0 */
114 u32 device_id_scheme:1; /*Device ID Scheme */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115};
116
117struct acpi_video_enumerated_device {
118 union {
119 u32 int_val;
Len Brown4be44fc2005-08-05 00:44:28 -0400120 struct acpi_video_device_attrib attrib;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 } value;
122 struct acpi_video_device *bind_info;
123};
124
125struct acpi_video_bus {
Patrick Mochele6afa0d2006-05-19 16:54:40 -0400126 struct acpi_device *device;
Len Brown4be44fc2005-08-05 00:44:28 -0400127 u8 dos_setting;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 struct acpi_video_enumerated_device *attached_array;
Len Brown4be44fc2005-08-05 00:44:28 -0400129 u8 attached_count;
130 struct acpi_video_bus_cap cap;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 struct acpi_video_bus_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400132 struct semaphore sem;
133 struct list_head video_device_list;
134 struct proc_dir_entry *dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135};
136
137struct acpi_video_device_flags {
Len Brown4be44fc2005-08-05 00:44:28 -0400138 u8 crt:1;
139 u8 lcd:1;
140 u8 tvout:1;
Rui Zhang82cae992007-01-03 23:40:53 -0500141 u8 dvi:1;
Len Brown4be44fc2005-08-05 00:44:28 -0400142 u8 bios:1;
143 u8 unknown:1;
Rui Zhang82cae992007-01-03 23:40:53 -0500144 u8 reserved:2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145};
146
147struct acpi_video_device_cap {
Len Brown4be44fc2005-08-05 00:44:28 -0400148 u8 _ADR:1; /*Return the unique ID */
149 u8 _BCL:1; /*Query list of brightness control levels supported */
150 u8 _BCM:1; /*Set the brightness level */
Yu Luming2f3d0002006-11-11 02:40:34 +0800151 u8 _BQC:1; /* Get current brightness level */
Len Brown4be44fc2005-08-05 00:44:28 -0400152 u8 _DDC:1; /*Return the EDID for this device */
153 u8 _DCS:1; /*Return status of output device */
154 u8 _DGS:1; /*Query graphics state */
155 u8 _DSS:1; /*Device state set */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156};
157
158struct acpi_video_device_brightness {
Len Brown4be44fc2005-08-05 00:44:28 -0400159 int curr;
160 int count;
161 int *levels;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162};
163
164struct acpi_video_device {
Len Brown4be44fc2005-08-05 00:44:28 -0400165 unsigned long device_id;
166 struct acpi_video_device_flags flags;
167 struct acpi_video_device_cap cap;
168 struct list_head entry;
169 struct acpi_video_bus *video;
170 struct acpi_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 struct acpi_video_device_brightness *brightness;
Yu Luming2f3d0002006-11-11 02:40:34 +0800172 struct backlight_device *backlight;
173 struct backlight_properties *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174};
175
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176/* bus */
177static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file);
178static struct file_operations acpi_video_bus_info_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400179 .open = acpi_video_bus_info_open_fs,
180 .read = seq_read,
181 .llseek = seq_lseek,
182 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183};
184
185static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file);
186static struct file_operations acpi_video_bus_ROM_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400187 .open = acpi_video_bus_ROM_open_fs,
188 .read = seq_read,
189 .llseek = seq_lseek,
190 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191};
192
Len Brown4be44fc2005-08-05 00:44:28 -0400193static int acpi_video_bus_POST_info_open_fs(struct inode *inode,
194 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195static struct file_operations acpi_video_bus_POST_info_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400196 .open = acpi_video_bus_POST_info_open_fs,
197 .read = seq_read,
198 .llseek = seq_lseek,
199 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200};
201
202static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file);
203static struct file_operations acpi_video_bus_POST_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400204 .open = acpi_video_bus_POST_open_fs,
205 .read = seq_read,
206 .llseek = seq_lseek,
207 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208};
209
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file);
211static struct file_operations acpi_video_bus_DOS_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400212 .open = acpi_video_bus_DOS_open_fs,
213 .read = seq_read,
214 .llseek = seq_lseek,
215 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216};
217
218/* device */
Len Brown4be44fc2005-08-05 00:44:28 -0400219static int acpi_video_device_info_open_fs(struct inode *inode,
220 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221static struct file_operations acpi_video_device_info_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400222 .open = acpi_video_device_info_open_fs,
223 .read = seq_read,
224 .llseek = seq_lseek,
225 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226};
227
Len Brown4be44fc2005-08-05 00:44:28 -0400228static int acpi_video_device_state_open_fs(struct inode *inode,
229 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230static struct file_operations acpi_video_device_state_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400231 .open = acpi_video_device_state_open_fs,
232 .read = seq_read,
233 .llseek = seq_lseek,
234 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235};
236
Len Brown4be44fc2005-08-05 00:44:28 -0400237static int acpi_video_device_brightness_open_fs(struct inode *inode,
238 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239static struct file_operations acpi_video_device_brightness_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400240 .open = acpi_video_device_brightness_open_fs,
241 .read = seq_read,
242 .llseek = seq_lseek,
243 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244};
245
Len Brown4be44fc2005-08-05 00:44:28 -0400246static int acpi_video_device_EDID_open_fs(struct inode *inode,
247 struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248static struct file_operations acpi_video_device_EDID_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400249 .open = acpi_video_device_EDID_open_fs,
250 .read = seq_read,
251 .llseek = seq_lseek,
252 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253};
254
Len Brown4be44fc2005-08-05 00:44:28 -0400255static char device_decode[][30] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 "motherboard VGA device",
257 "PCI VGA device",
258 "AGP VGA device",
259 "UNKNOWN",
260};
261
Len Brown4be44fc2005-08-05 00:44:28 -0400262static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data);
263static void acpi_video_device_rebind(struct acpi_video_bus *video);
264static void acpi_video_device_bind(struct acpi_video_bus *video,
265 struct acpi_video_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266static int acpi_video_device_enumerate(struct acpi_video_bus *video);
Len Brown4be44fc2005-08-05 00:44:28 -0400267static int acpi_video_switch_output(struct acpi_video_bus *video, int event);
Yu Luming2f3d0002006-11-11 02:40:34 +0800268static int acpi_video_device_lcd_set_level(struct acpi_video_device *device,
269 int level);
270static int acpi_video_device_lcd_get_level_current(
271 struct acpi_video_device *device,
272 unsigned long *level);
Len Brown4be44fc2005-08-05 00:44:28 -0400273static int acpi_video_get_next_level(struct acpi_video_device *device,
274 u32 level_current, u32 event);
275static void acpi_video_switch_brightness(struct acpi_video_device *device,
276 int event);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
Yu Luming2f3d0002006-11-11 02:40:34 +0800278/*backlight device sysfs support*/
279static int acpi_video_get_brightness(struct backlight_device *bd)
280{
281 unsigned long cur_level;
282 struct acpi_video_device *vd =
283 (struct acpi_video_device *)class_get_devdata(&bd->class_dev);
284 acpi_video_device_lcd_get_level_current(vd, &cur_level);
285 return (int) cur_level;
286}
287
288static int acpi_video_set_brightness(struct backlight_device *bd)
289{
290 int request_level = bd->props->brightness;
291 struct acpi_video_device *vd =
292 (struct acpi_video_device *)class_get_devdata(&bd->class_dev);
293 acpi_video_device_lcd_set_level(vd, request_level);
294 return 0;
295}
296
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297/* --------------------------------------------------------------------------
298 Video Management
299 -------------------------------------------------------------------------- */
300
301/* device */
302
303static int
Len Brown4be44fc2005-08-05 00:44:28 -0400304acpi_video_device_query(struct acpi_video_device *device, unsigned long *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305{
Len Brown4be44fc2005-08-05 00:44:28 -0400306 int status;
Patrick Mochel90130262006-05-19 16:54:48 -0400307
308 status = acpi_evaluate_integer(device->dev->handle, "_DGS", NULL, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
Patrick Mocheld550d982006-06-27 00:41:40 -0400310 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311}
312
313static int
Len Brown4be44fc2005-08-05 00:44:28 -0400314acpi_video_device_get_state(struct acpi_video_device *device,
315 unsigned long *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316{
Len Brown4be44fc2005-08-05 00:44:28 -0400317 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
Patrick Mochel90130262006-05-19 16:54:48 -0400319 status = acpi_evaluate_integer(device->dev->handle, "_DCS", NULL, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320
Patrick Mocheld550d982006-06-27 00:41:40 -0400321 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322}
323
324static int
Len Brown4be44fc2005-08-05 00:44:28 -0400325acpi_video_device_set_state(struct acpi_video_device *device, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326{
Len Brown4be44fc2005-08-05 00:44:28 -0400327 int status;
328 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
329 struct acpi_object_list args = { 1, &arg0 };
Luming Yu824b5582005-08-21 19:17:00 -0400330 unsigned long ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
333 arg0.integer.value = state;
Patrick Mochel90130262006-05-19 16:54:48 -0400334 status = acpi_evaluate_integer(device->dev->handle, "_DSS", &args, &ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
Patrick Mocheld550d982006-06-27 00:41:40 -0400336 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337}
338
339static int
Len Brown4be44fc2005-08-05 00:44:28 -0400340acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
341 union acpi_object **levels)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342{
Len Brown4be44fc2005-08-05 00:44:28 -0400343 int status;
344 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
345 union acpi_object *obj;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347
348 *levels = NULL;
349
Patrick Mochel90130262006-05-19 16:54:48 -0400350 status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400352 return status;
Len Brown4be44fc2005-08-05 00:44:28 -0400353 obj = (union acpi_object *)buffer.pointer;
Adrian Bunk6665bda2006-03-11 10:12:00 -0500354 if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
Len Brown64684632006-06-26 23:41:38 -0400355 printk(KERN_ERR PREFIX "Invalid _BCL data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 status = -EFAULT;
357 goto err;
358 }
359
360 *levels = obj;
361
Patrick Mocheld550d982006-06-27 00:41:40 -0400362 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
Len Brown4be44fc2005-08-05 00:44:28 -0400364 err:
Jesper Juhl6044ec82005-11-07 01:01:32 -0800365 kfree(buffer.pointer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
Patrick Mocheld550d982006-06-27 00:41:40 -0400367 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368}
369
370static int
Len Brown4be44fc2005-08-05 00:44:28 -0400371acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372{
Len Brown4be44fc2005-08-05 00:44:28 -0400373 int status;
374 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
375 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
378 arg0.integer.value = level;
Patrick Mochel90130262006-05-19 16:54:48 -0400379 status = acpi_evaluate_object(device->dev->handle, "_BCM", &args, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
381 printk(KERN_DEBUG "set_level status: %x\n", status);
Patrick Mocheld550d982006-06-27 00:41:40 -0400382 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383}
384
385static int
Len Brown4be44fc2005-08-05 00:44:28 -0400386acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
387 unsigned long *level)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388{
Len Brown4be44fc2005-08-05 00:44:28 -0400389 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
Patrick Mochel90130262006-05-19 16:54:48 -0400391 status = acpi_evaluate_integer(device->dev->handle, "_BQC", NULL, level);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
Patrick Mocheld550d982006-06-27 00:41:40 -0400393 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394}
395
396static int
Len Brown4be44fc2005-08-05 00:44:28 -0400397acpi_video_device_EDID(struct acpi_video_device *device,
398 union acpi_object **edid, ssize_t length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399{
Len Brown4be44fc2005-08-05 00:44:28 -0400400 int status;
401 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
402 union acpi_object *obj;
403 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
404 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
407 *edid = NULL;
408
409 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400410 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 if (length == 128)
412 arg0.integer.value = 1;
413 else if (length == 256)
414 arg0.integer.value = 2;
415 else
Patrick Mocheld550d982006-06-27 00:41:40 -0400416 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
Patrick Mochel90130262006-05-19 16:54:48 -0400418 status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400420 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200422 obj = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423
424 if (obj && obj->type == ACPI_TYPE_BUFFER)
425 *edid = obj;
426 else {
Len Brown64684632006-06-26 23:41:38 -0400427 printk(KERN_ERR PREFIX "Invalid _DDC data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 status = -EFAULT;
429 kfree(obj);
430 }
431
Patrick Mocheld550d982006-06-27 00:41:40 -0400432 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433}
434
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435/* bus */
436
437static int
Len Brown4be44fc2005-08-05 00:44:28 -0400438acpi_video_bus_set_POST(struct acpi_video_bus *video, unsigned long option)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439{
Len Brown4be44fc2005-08-05 00:44:28 -0400440 int status;
441 unsigned long tmp;
442 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
443 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445
446 arg0.integer.value = option;
447
Patrick Mochel90130262006-05-19 16:54:48 -0400448 status = acpi_evaluate_integer(video->device->handle, "_SPD", &args, &tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 if (ACPI_SUCCESS(status))
Len Brown4be44fc2005-08-05 00:44:28 -0400450 status = tmp ? (-EINVAL) : (AE_OK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
Patrick Mocheld550d982006-06-27 00:41:40 -0400452 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453}
454
455static int
Len Brown4be44fc2005-08-05 00:44:28 -0400456acpi_video_bus_get_POST(struct acpi_video_bus *video, unsigned long *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457{
458 int status;
459
Patrick Mochel90130262006-05-19 16:54:48 -0400460 status = acpi_evaluate_integer(video->device->handle, "_GPD", NULL, id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
Patrick Mocheld550d982006-06-27 00:41:40 -0400462 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463}
464
465static int
Len Brown4be44fc2005-08-05 00:44:28 -0400466acpi_video_bus_POST_options(struct acpi_video_bus *video,
467 unsigned long *options)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468{
Len Brown4be44fc2005-08-05 00:44:28 -0400469 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
Patrick Mochel90130262006-05-19 16:54:48 -0400471 status = acpi_evaluate_integer(video->device->handle, "_VPO", NULL, options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 *options &= 3;
473
Patrick Mocheld550d982006-06-27 00:41:40 -0400474 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475}
476
477/*
478 * Arg:
479 * video : video bus device pointer
480 * bios_flag :
481 * 0. The system BIOS should NOT automatically switch(toggle)
482 * the active display output.
483 * 1. The system BIOS should automatically switch (toggle) the
484 * active display output. No swich event.
485 * 2. The _DGS value should be locked.
486 * 3. The system BIOS should not automatically switch (toggle) the
487 * active display output, but instead generate the display switch
488 * event notify code.
489 * lcd_flag :
490 * 0. The system BIOS should automatically control the brightness level
491 * of the LCD, when the power changes from AC to DC
492 * 1. The system BIOS should NOT automatically control the brightness
493 * level of the LCD, when the power changes from AC to DC.
494 * Return Value:
495 * -1 wrong arg.
496 */
497
498static int
Len Brown4be44fc2005-08-05 00:44:28 -0400499acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500{
Len Brown4be44fc2005-08-05 00:44:28 -0400501 acpi_integer status = 0;
502 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
503 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
Len Brown4be44fc2005-08-05 00:44:28 -0400506 if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 status = -1;
508 goto Failed;
509 }
510 arg0.integer.value = (lcd_flag << 2) | bios_flag;
511 video->dos_setting = arg0.integer.value;
Patrick Mochel90130262006-05-19 16:54:48 -0400512 acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
Len Brown4be44fc2005-08-05 00:44:28 -0400514 Failed:
Patrick Mocheld550d982006-06-27 00:41:40 -0400515 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516}
517
518/*
519 * Arg:
520 * device : video output device (LCD, CRT, ..)
521 *
522 * Return Value:
523 * None
524 *
525 * Find out all required AML method defined under the output
526 * device.
527 */
528
Len Brown4be44fc2005-08-05 00:44:28 -0400529static void acpi_video_device_find_cap(struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530{
Len Brown4be44fc2005-08-05 00:44:28 -0400531 acpi_integer status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 acpi_handle h_dummy1;
533 int i;
Yu Luming2f3d0002006-11-11 02:40:34 +0800534 u32 max_level = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 union acpi_object *obj = NULL;
536 struct acpi_video_device_brightness *br = NULL;
537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
Len Brown4be44fc2005-08-05 00:44:28 -0400539 memset(&device->cap, 0, 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
Patrick Mochel90130262006-05-19 16:54:48 -0400541 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 device->cap._ADR = 1;
543 }
Patrick Mochel90130262006-05-19 16:54:48 -0400544 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400545 device->cap._BCL = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 }
Patrick Mochel90130262006-05-19 16:54:48 -0400547 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400548 device->cap._BCM = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 }
Yu Luming2f3d0002006-11-11 02:40:34 +0800550 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
551 device->cap._BQC = 1;
Patrick Mochel90130262006-05-19 16:54:48 -0400552 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400553 device->cap._DDC = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 }
Patrick Mochel90130262006-05-19 16:54:48 -0400555 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DCS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 device->cap._DCS = 1;
557 }
Patrick Mochel90130262006-05-19 16:54:48 -0400558 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DGS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 device->cap._DGS = 1;
560 }
Patrick Mochel90130262006-05-19 16:54:48 -0400561 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DSS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 device->cap._DSS = 1;
563 }
564
565 status = acpi_video_device_lcd_query_levels(device, &obj);
566
567 if (obj && obj->type == ACPI_TYPE_PACKAGE && obj->package.count >= 2) {
568 int count = 0;
569 union acpi_object *o;
Len Brown4be44fc2005-08-05 00:44:28 -0400570
Burman Yan36bcbec2006-12-19 12:56:11 -0800571 br = kzalloc(sizeof(*br), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 if (!br) {
573 printk(KERN_ERR "can't allocate memory\n");
574 } else {
Paulo Marquesd1dd0c22005-03-30 22:39:49 -0500575 br->levels = kmalloc(obj->package.count *
Len Brown4be44fc2005-08-05 00:44:28 -0400576 sizeof *(br->levels), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 if (!br->levels)
578 goto out;
579
580 for (i = 0; i < obj->package.count; i++) {
Len Brown4be44fc2005-08-05 00:44:28 -0400581 o = (union acpi_object *)&obj->package.
582 elements[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 if (o->type != ACPI_TYPE_INTEGER) {
Len Brown64684632006-06-26 23:41:38 -0400584 printk(KERN_ERR PREFIX "Invalid data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 continue;
586 }
587 br->levels[count] = (u32) o->integer.value;
Yu Luming2f3d0002006-11-11 02:40:34 +0800588 if (br->levels[count] > max_level)
589 max_level = br->levels[count];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 count++;
591 }
Len Brown4be44fc2005-08-05 00:44:28 -0400592 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 if (count < 2) {
Paulo Marquesd1dd0c22005-03-30 22:39:49 -0500594 kfree(br->levels);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 kfree(br);
596 } else {
597 br->count = count;
598 device->brightness = br;
Len Brown4be44fc2005-08-05 00:44:28 -0400599 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
600 "found %d brightness levels\n",
601 count));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 }
603 }
604 }
605
Paulo Marquesd1dd0c22005-03-30 22:39:49 -0500606 kfree(obj);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
Yu Luming2f3d0002006-11-11 02:40:34 +0800608 if (device->cap._BCL && device->cap._BCM && device->cap._BQC){
609 unsigned long tmp;
610 static int count = 0;
611 char *name;
612 struct backlight_properties *acpi_video_data;
613
614 name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
615 if (!name)
616 return;
617
618 acpi_video_data = kzalloc(
619 sizeof(struct backlight_properties),
620 GFP_KERNEL);
621 if (!acpi_video_data){
622 kfree(name);
623 return;
624 }
625 acpi_video_data->owner = THIS_MODULE;
626 acpi_video_data->get_brightness =
627 acpi_video_get_brightness;
628 acpi_video_data->update_status =
629 acpi_video_set_brightness;
630 sprintf(name, "acpi_video%d", count++);
631 device->data = acpi_video_data;
632 acpi_video_data->max_brightness = max_level;
633 acpi_video_device_lcd_get_level_current(device, &tmp);
634 acpi_video_data->brightness = (int)tmp;
635 device->backlight = backlight_device_register(name,
636 NULL, device, acpi_video_data);
637 kfree(name);
638 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400639 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640}
641
642/*
643 * Arg:
644 * device : video output device (VGA)
645 *
646 * Return Value:
647 * None
648 *
649 * Find out all required AML method defined under the video bus device.
650 */
651
Len Brown4be44fc2005-08-05 00:44:28 -0400652static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653{
Len Brown4be44fc2005-08-05 00:44:28 -0400654 acpi_handle h_dummy1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655
Len Brown4be44fc2005-08-05 00:44:28 -0400656 memset(&video->cap, 0, 4);
Patrick Mochel90130262006-05-19 16:54:48 -0400657 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 video->cap._DOS = 1;
659 }
Patrick Mochel90130262006-05-19 16:54:48 -0400660 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 video->cap._DOD = 1;
662 }
Patrick Mochel90130262006-05-19 16:54:48 -0400663 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 video->cap._ROM = 1;
665 }
Patrick Mochel90130262006-05-19 16:54:48 -0400666 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 video->cap._GPD = 1;
668 }
Patrick Mochel90130262006-05-19 16:54:48 -0400669 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 video->cap._SPD = 1;
671 }
Patrick Mochel90130262006-05-19 16:54:48 -0400672 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 video->cap._VPO = 1;
674 }
675}
676
677/*
678 * Check whether the video bus device has required AML method to
679 * support the desired features
680 */
681
Len Brown4be44fc2005-08-05 00:44:28 -0400682static int acpi_video_bus_check(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683{
Len Brown4be44fc2005-08-05 00:44:28 -0400684 acpi_status status = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
687 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -0400688 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689
690 /* Since there is no HID, CID and so on for VGA driver, we have
691 * to check well known required nodes.
692 */
693
694 /* Does this device able to support video switching ? */
Len Brown4be44fc2005-08-05 00:44:28 -0400695 if (video->cap._DOS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 video->flags.multihead = 1;
697 status = 0;
698 }
699
700 /* Does this device able to retrieve a retrieve a video ROM ? */
Len Brown4be44fc2005-08-05 00:44:28 -0400701 if (video->cap._ROM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 video->flags.rom = 1;
703 status = 0;
704 }
705
706 /* Does this device able to configure which video device to POST ? */
Len Brown4be44fc2005-08-05 00:44:28 -0400707 if (video->cap._GPD && video->cap._SPD && video->cap._VPO) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 video->flags.post = 1;
709 status = 0;
710 }
711
Patrick Mocheld550d982006-06-27 00:41:40 -0400712 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713}
714
715/* --------------------------------------------------------------------------
716 FS Interface (/proc)
717 -------------------------------------------------------------------------- */
718
Len Brown4be44fc2005-08-05 00:44:28 -0400719static struct proc_dir_entry *acpi_video_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
721/* video devices */
722
Len Brown4be44fc2005-08-05 00:44:28 -0400723static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200725 struct acpi_video_device *dev = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
728 if (!dev)
729 goto end;
730
731 seq_printf(seq, "device_id: 0x%04x\n", (u32) dev->device_id);
732 seq_printf(seq, "type: ");
733 if (dev->flags.crt)
734 seq_printf(seq, "CRT\n");
735 else if (dev->flags.lcd)
736 seq_printf(seq, "LCD\n");
737 else if (dev->flags.tvout)
738 seq_printf(seq, "TVOUT\n");
Rui Zhang82cae992007-01-03 23:40:53 -0500739 else if (dev->flags.dvi)
740 seq_printf(seq, "DVI\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 else
742 seq_printf(seq, "UNKNOWN\n");
743
Len Brown4be44fc2005-08-05 00:44:28 -0400744 seq_printf(seq, "known by bios: %s\n", dev->flags.bios ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
Len Brown4be44fc2005-08-05 00:44:28 -0400746 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400747 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748}
749
750static int
Len Brown4be44fc2005-08-05 00:44:28 -0400751acpi_video_device_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752{
753 return single_open(file, acpi_video_device_info_seq_show,
754 PDE(inode)->data);
755}
756
Len Brown4be44fc2005-08-05 00:44:28 -0400757static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758{
Len Brown4be44fc2005-08-05 00:44:28 -0400759 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200760 struct acpi_video_device *dev = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400761 unsigned long state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
764 if (!dev)
765 goto end;
766
767 status = acpi_video_device_get_state(dev, &state);
768 seq_printf(seq, "state: ");
769 if (ACPI_SUCCESS(status))
770 seq_printf(seq, "0x%02lx\n", state);
771 else
772 seq_printf(seq, "<not supported>\n");
773
774 status = acpi_video_device_query(dev, &state);
775 seq_printf(seq, "query: ");
776 if (ACPI_SUCCESS(status))
777 seq_printf(seq, "0x%02lx\n", state);
778 else
779 seq_printf(seq, "<not supported>\n");
780
Len Brown4be44fc2005-08-05 00:44:28 -0400781 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400782 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783}
784
785static int
Len Brown4be44fc2005-08-05 00:44:28 -0400786acpi_video_device_state_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787{
788 return single_open(file, acpi_video_device_state_seq_show,
789 PDE(inode)->data);
790}
791
792static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400793acpi_video_device_write_state(struct file *file,
794 const char __user * buffer,
795 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796{
Len Brown4be44fc2005-08-05 00:44:28 -0400797 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200798 struct seq_file *m = file->private_data;
799 struct acpi_video_device *dev = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400800 char str[12] = { 0 };
801 u32 state = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803
804 if (!dev || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -0400805 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
807 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -0400808 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
810 str[count] = 0;
811 state = simple_strtoul(str, NULL, 0);
Len Brown4be44fc2005-08-05 00:44:28 -0400812 state &= ((1ul << 31) | (1ul << 30) | (1ul << 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
814 status = acpi_video_device_set_state(dev, state);
815
816 if (status)
Patrick Mocheld550d982006-06-27 00:41:40 -0400817 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Patrick Mocheld550d982006-06-27 00:41:40 -0400819 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820}
821
822static int
Len Brown4be44fc2005-08-05 00:44:28 -0400823acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200825 struct acpi_video_device *dev = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400826 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
829 if (!dev || !dev->brightness) {
830 seq_printf(seq, "<not supported>\n");
Patrick Mocheld550d982006-06-27 00:41:40 -0400831 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 }
833
834 seq_printf(seq, "levels: ");
835 for (i = 0; i < dev->brightness->count; i++)
836 seq_printf(seq, " %d", dev->brightness->levels[i]);
837 seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr);
838
Patrick Mocheld550d982006-06-27 00:41:40 -0400839 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840}
841
842static int
Len Brown4be44fc2005-08-05 00:44:28 -0400843acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844{
845 return single_open(file, acpi_video_device_brightness_seq_show,
846 PDE(inode)->data);
847}
848
849static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400850acpi_video_device_write_brightness(struct file *file,
851 const char __user * buffer,
852 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200854 struct seq_file *m = file->private_data;
855 struct acpi_video_device *dev = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400856 char str[4] = { 0 };
857 unsigned int level = 0;
858 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
Thomas Renninger59d399d2005-11-08 05:27:00 -0500861 if (!dev || !dev->brightness || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -0400862 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
864 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -0400865 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
867 str[count] = 0;
868 level = simple_strtoul(str, NULL, 0);
Len Brown4be44fc2005-08-05 00:44:28 -0400869
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 if (level > 100)
Patrick Mocheld550d982006-06-27 00:41:40 -0400871 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
873 /* validate though the list of available levels */
874 for (i = 0; i < dev->brightness->count; i++)
875 if (level == dev->brightness->levels[i]) {
Len Brown4be44fc2005-08-05 00:44:28 -0400876 if (ACPI_SUCCESS
877 (acpi_video_device_lcd_set_level(dev, level)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 dev->brightness->curr = level;
879 break;
880 }
881
Patrick Mocheld550d982006-06-27 00:41:40 -0400882 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883}
884
Len Brown4be44fc2005-08-05 00:44:28 -0400885static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200887 struct acpi_video_device *dev = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400888 int status;
889 int i;
890 union acpi_object *edid = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892
893 if (!dev)
894 goto out;
895
Len Brown4be44fc2005-08-05 00:44:28 -0400896 status = acpi_video_device_EDID(dev, &edid, 128);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 if (ACPI_FAILURE(status)) {
Len Brown4be44fc2005-08-05 00:44:28 -0400898 status = acpi_video_device_EDID(dev, &edid, 256);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 }
900
901 if (ACPI_FAILURE(status)) {
902 goto out;
903 }
904
905 if (edid && edid->type == ACPI_TYPE_BUFFER) {
906 for (i = 0; i < edid->buffer.length; i++)
907 seq_putc(seq, edid->buffer.pointer[i]);
908 }
909
Len Brown4be44fc2005-08-05 00:44:28 -0400910 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 if (!edid)
912 seq_printf(seq, "<not supported>\n");
913 else
914 kfree(edid);
915
Patrick Mocheld550d982006-06-27 00:41:40 -0400916 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917}
918
919static int
Len Brown4be44fc2005-08-05 00:44:28 -0400920acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921{
922 return single_open(file, acpi_video_device_EDID_seq_show,
923 PDE(inode)->data);
924}
925
Len Brown4be44fc2005-08-05 00:44:28 -0400926static int acpi_video_device_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927{
Len Brown4be44fc2005-08-05 00:44:28 -0400928 struct proc_dir_entry *entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 struct acpi_video_device *vid_dev;
930
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
932 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400933 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200935 vid_dev = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 if (!vid_dev)
Patrick Mocheld550d982006-06-27 00:41:40 -0400937 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
939 if (!acpi_device_dir(device)) {
940 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -0400941 vid_dev->video->dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400943 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 acpi_device_dir(device)->owner = THIS_MODULE;
945 }
946
947 /* 'info' [R] */
948 entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device));
949 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400950 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 else {
952 entry->proc_fops = &acpi_video_device_info_fops;
953 entry->data = acpi_driver_data(device);
954 entry->owner = THIS_MODULE;
955 }
956
957 /* 'state' [R/W] */
Len Brown4be44fc2005-08-05 00:44:28 -0400958 entry =
959 create_proc_entry("state", S_IFREG | S_IRUGO | S_IWUSR,
960 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400962 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 else {
Arjan van de Vend479e902006-01-06 16:47:00 -0500964 acpi_video_device_state_fops.write = acpi_video_device_write_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 entry->proc_fops = &acpi_video_device_state_fops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 entry->data = acpi_driver_data(device);
967 entry->owner = THIS_MODULE;
968 }
969
970 /* 'brightness' [R/W] */
Len Brown4be44fc2005-08-05 00:44:28 -0400971 entry =
972 create_proc_entry("brightness", S_IFREG | S_IRUGO | S_IWUSR,
973 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400975 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 else {
Arjan van de Vend479e902006-01-06 16:47:00 -0500977 acpi_video_device_brightness_fops.write = acpi_video_device_write_brightness;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 entry->proc_fops = &acpi_video_device_brightness_fops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 entry->data = acpi_driver_data(device);
980 entry->owner = THIS_MODULE;
981 }
982
983 /* 'EDID' [R] */
984 entry = create_proc_entry("EDID", S_IRUGO, acpi_device_dir(device));
985 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400986 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 else {
988 entry->proc_fops = &acpi_video_device_EDID_fops;
989 entry->data = acpi_driver_data(device);
990 entry->owner = THIS_MODULE;
991 }
992
Patrick Mocheld550d982006-06-27 00:41:40 -0400993 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994}
995
Len Brown4be44fc2005-08-05 00:44:28 -0400996static int acpi_video_device_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997{
998 struct acpi_video_device *vid_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001000 vid_dev = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001002 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
1004 if (acpi_device_dir(device)) {
1005 remove_proc_entry("info", acpi_device_dir(device));
1006 remove_proc_entry("state", acpi_device_dir(device));
1007 remove_proc_entry("brightness", acpi_device_dir(device));
1008 remove_proc_entry("EDID", acpi_device_dir(device));
Len Brown4be44fc2005-08-05 00:44:28 -04001009 remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 acpi_device_dir(device) = NULL;
1011 }
1012
Patrick Mocheld550d982006-06-27 00:41:40 -04001013 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014}
1015
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016/* video bus */
Len Brown4be44fc2005-08-05 00:44:28 -04001017static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001019 struct acpi_video_bus *video = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021
1022 if (!video)
1023 goto end;
1024
1025 seq_printf(seq, "Switching heads: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001026 video->flags.multihead ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 seq_printf(seq, "Video ROM: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001028 video->flags.rom ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 seq_printf(seq, "Device to be POSTed on boot: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001030 video->flags.post ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031
Len Brown4be44fc2005-08-05 00:44:28 -04001032 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001033 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034}
1035
Len Brown4be44fc2005-08-05 00:44:28 -04001036static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037{
Len Brown4be44fc2005-08-05 00:44:28 -04001038 return single_open(file, acpi_video_bus_info_seq_show,
1039 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040}
1041
Len Brown4be44fc2005-08-05 00:44:28 -04001042static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001044 struct acpi_video_bus *video = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046
1047 if (!video)
1048 goto end;
1049
1050 printk(KERN_INFO PREFIX "Please implement %s\n", __FUNCTION__);
1051 seq_printf(seq, "<TODO>\n");
1052
Len Brown4be44fc2005-08-05 00:44:28 -04001053 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001054 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055}
1056
Len Brown4be44fc2005-08-05 00:44:28 -04001057static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058{
1059 return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data);
1060}
1061
Len Brown4be44fc2005-08-05 00:44:28 -04001062static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001064 struct acpi_video_bus *video = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001065 unsigned long options;
1066 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068
1069 if (!video)
1070 goto end;
1071
1072 status = acpi_video_bus_POST_options(video, &options);
1073 if (ACPI_SUCCESS(status)) {
1074 if (!(options & 1)) {
Len Brown4be44fc2005-08-05 00:44:28 -04001075 printk(KERN_WARNING PREFIX
1076 "The motherboard VGA device is not listed as a possible POST device.\n");
1077 printk(KERN_WARNING PREFIX
1078 "This indicate a BIOS bug. Please contact the manufacturer.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 }
1080 printk("%lx\n", options);
1081 seq_printf(seq, "can POST: <intgrated video>");
1082 if (options & 2)
1083 seq_printf(seq, " <PCI video>");
1084 if (options & 4)
1085 seq_printf(seq, " <AGP video>");
1086 seq_putc(seq, '\n');
1087 } else
1088 seq_printf(seq, "<not supported>\n");
Len Brown4be44fc2005-08-05 00:44:28 -04001089 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001090 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091}
1092
1093static int
Len Brown4be44fc2005-08-05 00:44:28 -04001094acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095{
Len Brown4be44fc2005-08-05 00:44:28 -04001096 return single_open(file, acpi_video_bus_POST_info_seq_show,
1097 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098}
1099
Len Brown4be44fc2005-08-05 00:44:28 -04001100static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001102 struct acpi_video_bus *video = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001103 int status;
1104 unsigned long id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106
1107 if (!video)
1108 goto end;
1109
Len Brown4be44fc2005-08-05 00:44:28 -04001110 status = acpi_video_bus_get_POST(video, &id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 if (!ACPI_SUCCESS(status)) {
1112 seq_printf(seq, "<not supported>\n");
1113 goto end;
1114 }
Len Brown4be44fc2005-08-05 00:44:28 -04001115 seq_printf(seq, "device posted is <%s>\n", device_decode[id & 3]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116
Len Brown4be44fc2005-08-05 00:44:28 -04001117 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001118 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119}
1120
Len Brown4be44fc2005-08-05 00:44:28 -04001121static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001123 struct acpi_video_bus *video = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125
Len Brown4be44fc2005-08-05 00:44:28 -04001126 seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127
Patrick Mocheld550d982006-06-27 00:41:40 -04001128 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129}
1130
Len Brown4be44fc2005-08-05 00:44:28 -04001131static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132{
Len Brown4be44fc2005-08-05 00:44:28 -04001133 return single_open(file, acpi_video_bus_POST_seq_show,
1134 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135}
1136
Len Brown4be44fc2005-08-05 00:44:28 -04001137static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138{
1139 return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data);
1140}
1141
1142static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001143acpi_video_bus_write_POST(struct file *file,
1144 const char __user * buffer,
1145 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146{
Len Brown4be44fc2005-08-05 00:44:28 -04001147 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001148 struct seq_file *m = file->private_data;
1149 struct acpi_video_bus *video = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001150 char str[12] = { 0 };
1151 unsigned long opt, options;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 if (!video || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -04001155 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156
1157 status = acpi_video_bus_POST_options(video, &options);
1158 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001159 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
1161 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001162 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163
1164 str[count] = 0;
1165 opt = strtoul(str, NULL, 0);
1166 if (opt > 3)
Patrick Mocheld550d982006-06-27 00:41:40 -04001167 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168
1169 /* just in case an OEM 'forget' the motherboard... */
1170 options |= 1;
1171
1172 if (options & (1ul << opt)) {
Len Brown4be44fc2005-08-05 00:44:28 -04001173 status = acpi_video_bus_set_POST(video, opt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001175 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176
1177 }
1178
Patrick Mocheld550d982006-06-27 00:41:40 -04001179 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180}
1181
1182static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001183acpi_video_bus_write_DOS(struct file *file,
1184 const char __user * buffer,
1185 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186{
Len Brown4be44fc2005-08-05 00:44:28 -04001187 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001188 struct seq_file *m = file->private_data;
1189 struct acpi_video_bus *video = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001190 char str[12] = { 0 };
1191 unsigned long opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 if (!video || count + 1 > sizeof str)
Patrick Mocheld550d982006-06-27 00:41:40 -04001195 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196
1197 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001198 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199
1200 str[count] = 0;
1201 opt = strtoul(str, NULL, 0);
1202 if (opt > 7)
Patrick Mocheld550d982006-06-27 00:41:40 -04001203 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204
Len Brown4be44fc2005-08-05 00:44:28 -04001205 status = acpi_video_bus_DOS(video, opt & 0x3, (opt & 0x4) >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206
1207 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001208 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209
Patrick Mocheld550d982006-06-27 00:41:40 -04001210 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211}
1212
Len Brown4be44fc2005-08-05 00:44:28 -04001213static int acpi_video_bus_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214{
Len Brown4be44fc2005-08-05 00:44:28 -04001215 struct proc_dir_entry *entry = NULL;
1216 struct acpi_video_bus *video;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001219 video = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220
1221 if (!acpi_device_dir(device)) {
1222 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -04001223 acpi_video_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001225 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 video->dir = acpi_device_dir(device);
1227 acpi_device_dir(device)->owner = THIS_MODULE;
1228 }
1229
1230 /* 'info' [R] */
1231 entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device));
1232 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001233 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 else {
1235 entry->proc_fops = &acpi_video_bus_info_fops;
1236 entry->data = acpi_driver_data(device);
1237 entry->owner = THIS_MODULE;
1238 }
1239
1240 /* 'ROM' [R] */
1241 entry = create_proc_entry("ROM", S_IRUGO, acpi_device_dir(device));
1242 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001243 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 else {
1245 entry->proc_fops = &acpi_video_bus_ROM_fops;
1246 entry->data = acpi_driver_data(device);
1247 entry->owner = THIS_MODULE;
1248 }
1249
1250 /* 'POST_info' [R] */
Len Brown4be44fc2005-08-05 00:44:28 -04001251 entry =
1252 create_proc_entry("POST_info", S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001254 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 else {
1256 entry->proc_fops = &acpi_video_bus_POST_info_fops;
1257 entry->data = acpi_driver_data(device);
1258 entry->owner = THIS_MODULE;
1259 }
1260
1261 /* 'POST' [R/W] */
Len Brown4be44fc2005-08-05 00:44:28 -04001262 entry =
1263 create_proc_entry("POST", S_IFREG | S_IRUGO | S_IRUSR,
1264 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001266 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 else {
Arjan van de Vend479e902006-01-06 16:47:00 -05001268 acpi_video_bus_POST_fops.write = acpi_video_bus_write_POST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 entry->proc_fops = &acpi_video_bus_POST_fops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 entry->data = acpi_driver_data(device);
1271 entry->owner = THIS_MODULE;
1272 }
1273
1274 /* 'DOS' [R/W] */
Len Brown4be44fc2005-08-05 00:44:28 -04001275 entry =
1276 create_proc_entry("DOS", S_IFREG | S_IRUGO | S_IRUSR,
1277 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001279 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 else {
Arjan van de Vend479e902006-01-06 16:47:00 -05001281 acpi_video_bus_DOS_fops.write = acpi_video_bus_write_DOS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 entry->proc_fops = &acpi_video_bus_DOS_fops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 entry->data = acpi_driver_data(device);
1284 entry->owner = THIS_MODULE;
1285 }
1286
Patrick Mocheld550d982006-06-27 00:41:40 -04001287 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288}
1289
Len Brown4be44fc2005-08-05 00:44:28 -04001290static int acpi_video_bus_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291{
Len Brown4be44fc2005-08-05 00:44:28 -04001292 struct acpi_video_bus *video;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001295 video = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296
1297 if (acpi_device_dir(device)) {
1298 remove_proc_entry("info", acpi_device_dir(device));
1299 remove_proc_entry("ROM", acpi_device_dir(device));
1300 remove_proc_entry("POST_info", acpi_device_dir(device));
1301 remove_proc_entry("POST", acpi_device_dir(device));
1302 remove_proc_entry("DOS", acpi_device_dir(device));
Len Brown4be44fc2005-08-05 00:44:28 -04001303 remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 acpi_device_dir(device) = NULL;
1305 }
1306
Patrick Mocheld550d982006-06-27 00:41:40 -04001307 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308}
1309
1310/* --------------------------------------------------------------------------
1311 Driver Interface
1312 -------------------------------------------------------------------------- */
1313
1314/* device interface */
Rui Zhang82cae992007-01-03 23:40:53 -05001315static struct acpi_video_device_attrib*
1316acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id)
1317{
1318 int count;
1319
1320 for(count = 0; count < video->attached_count; count++)
1321 if((video->attached_array[count].value.int_val & 0xffff) == device_id)
1322 return &(video->attached_array[count].value.attrib);
1323 return NULL;
1324}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325
1326static int
Len Brown4be44fc2005-08-05 00:44:28 -04001327acpi_video_bus_get_one_device(struct acpi_device *device,
1328 struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329{
Len Brown4be44fc2005-08-05 00:44:28 -04001330 unsigned long device_id;
Yu, Luming973bf492006-04-27 05:25:00 -04001331 int status;
Len Brown4be44fc2005-08-05 00:44:28 -04001332 struct acpi_video_device *data;
Rui Zhang82cae992007-01-03 23:40:53 -05001333 struct acpi_video_device_attrib* attribute;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334
1335 if (!device || !video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001336 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337
Len Brown4be44fc2005-08-05 00:44:28 -04001338 status =
1339 acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 if (ACPI_SUCCESS(status)) {
1341
Burman Yan36bcbec2006-12-19 12:56:11 -08001342 data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 if (!data)
Patrick Mocheld550d982006-06-27 00:41:40 -04001344 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
1347 strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
1348 acpi_driver_data(device) = data;
1349
1350 data->device_id = device_id;
1351 data->video = video;
1352 data->dev = device;
1353
Rui Zhang82cae992007-01-03 23:40:53 -05001354 attribute = acpi_video_get_device_attr(video, device_id);
1355
1356 if((attribute != NULL) && attribute->device_id_scheme) {
1357 switch (attribute->display_type) {
1358 case ACPI_VIDEO_DISPLAY_CRT:
1359 data->flags.crt = 1;
1360 break;
1361 case ACPI_VIDEO_DISPLAY_TV:
1362 data->flags.tvout = 1;
1363 break;
1364 case ACPI_VIDEO_DISPLAY_DVI:
1365 data->flags.dvi = 1;
1366 break;
1367 case ACPI_VIDEO_DISPLAY_LCD:
1368 data->flags.lcd = 1;
1369 break;
1370 default:
1371 data->flags.unknown = 1;
1372 break;
1373 }
1374 if(attribute->bios_can_detect)
1375 data->flags.bios = 1;
1376 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 data->flags.unknown = 1;
Len Brown4be44fc2005-08-05 00:44:28 -04001378
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 acpi_video_device_bind(video, data);
1380 acpi_video_device_find_cap(data);
1381
Patrick Mochel90130262006-05-19 16:54:48 -04001382 status = acpi_install_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001383 ACPI_DEVICE_NOTIFY,
1384 acpi_video_device_notify,
1385 data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 if (ACPI_FAILURE(status)) {
1387 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
Len Brown4be44fc2005-08-05 00:44:28 -04001388 "Error installing notify handler\n"));
Yu, Luming973bf492006-04-27 05:25:00 -04001389 if(data->brightness)
1390 kfree(data->brightness->levels);
1391 kfree(data->brightness);
1392 kfree(data);
1393 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 }
1395
1396 down(&video->sem);
1397 list_add_tail(&data->entry, &video->video_device_list);
1398 up(&video->sem);
1399
1400 acpi_video_device_add_fs(device);
1401
Patrick Mocheld550d982006-06-27 00:41:40 -04001402 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 }
1404
Patrick Mocheld550d982006-06-27 00:41:40 -04001405 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406}
1407
1408/*
1409 * Arg:
1410 * video : video bus device
1411 *
1412 * Return:
1413 * none
1414 *
1415 * Enumerate the video device list of the video bus,
1416 * bind the ids with the corresponding video devices
1417 * under the video bus.
Len Brown4be44fc2005-08-05 00:44:28 -04001418 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419
Len Brown4be44fc2005-08-05 00:44:28 -04001420static void acpi_video_device_rebind(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421{
Len Brown4be44fc2005-08-05 00:44:28 -04001422 struct list_head *node, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 list_for_each_safe(node, next, &video->video_device_list) {
Len Brown4be44fc2005-08-05 00:44:28 -04001424 struct acpi_video_device *dev =
1425 container_of(node, struct acpi_video_device, entry);
1426 acpi_video_device_bind(video, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 }
1428}
1429
1430/*
1431 * Arg:
1432 * video : video bus device
1433 * device : video output device under the video
1434 * bus
1435 *
1436 * Return:
1437 * none
1438 *
1439 * Bind the ids with the corresponding video devices
1440 * under the video bus.
Len Brown4be44fc2005-08-05 00:44:28 -04001441 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442
1443static void
Len Brown4be44fc2005-08-05 00:44:28 -04001444acpi_video_device_bind(struct acpi_video_bus *video,
1445 struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446{
Len Brown4be44fc2005-08-05 00:44:28 -04001447 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448
1449#define IDS_VAL(i) video->attached_array[i].value.int_val
1450#define IDS_BIND(i) video->attached_array[i].bind_info
Len Brown4be44fc2005-08-05 00:44:28 -04001451
1452 for (i = 0; IDS_VAL(i) != ACPI_VIDEO_HEAD_INVALID &&
1453 i < video->attached_count; i++) {
1454 if (device->device_id == (IDS_VAL(i) & 0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 IDS_BIND(i) = device;
1456 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i));
1457 }
1458 }
1459#undef IDS_VAL
1460#undef IDS_BIND
1461}
1462
1463/*
1464 * Arg:
1465 * video : video bus device
1466 *
1467 * Return:
1468 * < 0 : error
1469 *
1470 * Call _DOD to enumerate all devices attached to display adapter
1471 *
Len Brown4be44fc2005-08-05 00:44:28 -04001472 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473
1474static int acpi_video_device_enumerate(struct acpi_video_bus *video)
1475{
Len Brown4be44fc2005-08-05 00:44:28 -04001476 int status;
1477 int count;
1478 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 struct acpi_video_enumerated_device *active_device_list;
Len Brown4be44fc2005-08-05 00:44:28 -04001480 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1481 union acpi_object *dod = NULL;
1482 union acpi_object *obj;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483
Patrick Mochel90130262006-05-19 16:54:48 -04001484 status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 if (!ACPI_SUCCESS(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001486 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD"));
Patrick Mocheld550d982006-06-27 00:41:40 -04001487 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 }
1489
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001490 dod = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001492 ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 status = -EFAULT;
1494 goto out;
1495 }
1496
1497 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001498 dod->package.count));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499
Len Brown4be44fc2005-08-05 00:44:28 -04001500 active_device_list = kmalloc((1 +
1501 dod->package.count) *
1502 sizeof(struct
1503 acpi_video_enumerated_device),
1504 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
1506 if (!active_device_list) {
1507 status = -ENOMEM;
1508 goto out;
1509 }
1510
1511 count = 0;
1512 for (i = 0; i < dod->package.count; i++) {
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001513 obj = &dod->package.elements[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514
1515 if (obj->type != ACPI_TYPE_INTEGER) {
Len Brown64684632006-06-26 23:41:38 -04001516 printk(KERN_ERR PREFIX "Invalid _DOD data\n");
Len Brown4be44fc2005-08-05 00:44:28 -04001517 active_device_list[i].value.int_val =
1518 ACPI_VIDEO_HEAD_INVALID;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 }
1520 active_device_list[i].value.int_val = obj->integer.value;
1521 active_device_list[i].bind_info = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -04001522 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i,
1523 (int)obj->integer.value));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 count++;
1525 }
1526 active_device_list[count].value.int_val = ACPI_VIDEO_HEAD_END;
1527
Jesper Juhl6044ec82005-11-07 01:01:32 -08001528 kfree(video->attached_array);
Len Brown4be44fc2005-08-05 00:44:28 -04001529
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 video->attached_array = active_device_list;
1531 video->attached_count = count;
Len Brown4be44fc2005-08-05 00:44:28 -04001532 out:
Len Brown02438d82006-06-30 03:19:10 -04001533 kfree(buffer.pointer);
Patrick Mocheld550d982006-06-27 00:41:40 -04001534 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535}
1536
1537/*
1538 * Arg:
1539 * video : video bus device
1540 * event : Nontify Event
1541 *
1542 * Return:
1543 * < 0 : error
1544 *
1545 * 1. Find out the current active output device.
1546 * 2. Identify the next output device to switch
1547 * 3. call _DSS to do actual switch.
Len Brown4be44fc2005-08-05 00:44:28 -04001548 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
Len Brown4be44fc2005-08-05 00:44:28 -04001550static int acpi_video_switch_output(struct acpi_video_bus *video, int event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551{
Len Brown4be44fc2005-08-05 00:44:28 -04001552 struct list_head *node, *next;
1553 struct acpi_video_device *dev = NULL;
1554 struct acpi_video_device *dev_next = NULL;
1555 struct acpi_video_device *dev_prev = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 unsigned long state;
1557 int status = 0;
1558
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
1560 list_for_each_safe(node, next, &video->video_device_list) {
Adrian Bunk73345712005-03-30 22:31:35 -05001561 dev = container_of(node, struct acpi_video_device, entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 status = acpi_video_device_get_state(dev, &state);
Len Brown4be44fc2005-08-05 00:44:28 -04001563 if (state & 0x2) {
1564 dev_next =
1565 container_of(node->next, struct acpi_video_device,
1566 entry);
1567 dev_prev =
1568 container_of(node->prev, struct acpi_video_device,
1569 entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 goto out;
1571 }
1572 }
1573 dev_next = container_of(node->next, struct acpi_video_device, entry);
1574 dev_prev = container_of(node->prev, struct acpi_video_device, entry);
Len Brown4be44fc2005-08-05 00:44:28 -04001575 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 switch (event) {
1577 case ACPI_VIDEO_NOTIFY_CYCLE:
1578 case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:
1579 acpi_video_device_set_state(dev, 0);
1580 acpi_video_device_set_state(dev_next, 0x80000001);
1581 break;
1582 case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:
1583 acpi_video_device_set_state(dev, 0);
1584 acpi_video_device_set_state(dev_prev, 0x80000001);
1585 default:
1586 break;
1587 }
1588
Patrick Mocheld550d982006-06-27 00:41:40 -04001589 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590}
1591
Len Brown4be44fc2005-08-05 00:44:28 -04001592static int
1593acpi_video_get_next_level(struct acpi_video_device *device,
1594 u32 level_current, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595{
Thomas Tuttlef4715182006-12-19 12:56:14 -08001596 int min, max, min_above, max_below, i, l;
1597 max = max_below = 0;
1598 min = min_above = 255;
1599 for (i = 0; i < device->brightness->count; i++) {
1600 l = device->brightness->levels[i];
1601 if (l < min)
1602 min = l;
1603 if (l > max)
1604 max = l;
1605 if (l < min_above && l > level_current)
1606 min_above = l;
1607 if (l > max_below && l < level_current)
1608 max_below = l;
1609 }
1610
1611 switch (event) {
1612 case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:
1613 return (level_current < max) ? min_above : min;
1614 case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:
1615 return (level_current < max) ? min_above : max;
1616 case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:
1617 return (level_current > min) ? max_below : min;
1618 case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:
1619 case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:
1620 return 0;
1621 default:
1622 return level_current;
1623 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624}
1625
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626static void
Len Brown4be44fc2005-08-05 00:44:28 -04001627acpi_video_switch_brightness(struct acpi_video_device *device, int event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628{
1629 unsigned long level_current, level_next;
1630 acpi_video_device_lcd_get_level_current(device, &level_current);
1631 level_next = acpi_video_get_next_level(device, level_current, event);
1632 acpi_video_device_lcd_set_level(device, level_next);
1633}
1634
1635static int
Len Brown4be44fc2005-08-05 00:44:28 -04001636acpi_video_bus_get_devices(struct acpi_video_bus *video,
1637 struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638{
Len Brown4be44fc2005-08-05 00:44:28 -04001639 int status = 0;
1640 struct list_head *node, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642
1643 acpi_video_device_enumerate(video);
1644
1645 list_for_each_safe(node, next, &device->children) {
Len Brown4be44fc2005-08-05 00:44:28 -04001646 struct acpi_device *dev =
1647 list_entry(node, struct acpi_device, node);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
1649 if (!dev)
1650 continue;
1651
1652 status = acpi_video_bus_get_one_device(dev, video);
1653 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001654 ACPI_EXCEPTION((AE_INFO, status, "Cant attach device"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 continue;
1656 }
1657
1658 }
Patrick Mocheld550d982006-06-27 00:41:40 -04001659 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660}
1661
Len Brown4be44fc2005-08-05 00:44:28 -04001662static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663{
Karol Kozimor031ec772005-07-30 04:18:00 -04001664 acpi_status status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 struct acpi_video_bus *video;
1666
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667
1668 if (!device || !device->video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001669 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670
1671 video = device->video;
1672
1673 down(&video->sem);
1674 list_del(&device->entry);
1675 up(&video->sem);
1676 acpi_video_device_remove_fs(device->dev);
1677
Patrick Mochel90130262006-05-19 16:54:48 -04001678 status = acpi_remove_notify_handler(device->dev->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001679 ACPI_DEVICE_NOTIFY,
1680 acpi_video_device_notify);
Yu Luming2f3d0002006-11-11 02:40:34 +08001681 if (device->backlight){
1682 backlight_device_unregister(device->backlight);
1683 kfree(device->data);
1684 }
Patrick Mocheld550d982006-06-27 00:41:40 -04001685 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686}
1687
Len Brown4be44fc2005-08-05 00:44:28 -04001688static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689{
Len Brown4be44fc2005-08-05 00:44:28 -04001690 int status;
1691 struct list_head *node, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693
1694 list_for_each_safe(node, next, &video->video_device_list) {
Len Brown4be44fc2005-08-05 00:44:28 -04001695 struct acpi_video_device *data =
1696 list_entry(node, struct acpi_video_device, entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 if (!data)
1698 continue;
1699
1700 status = acpi_video_bus_put_one_device(data);
Len Brown4be44fc2005-08-05 00:44:28 -04001701 if (ACPI_FAILURE(status))
1702 printk(KERN_WARNING PREFIX
1703 "hhuuhhuu bug in acpi video driver.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704
Dave Jonesd384ea62006-06-24 00:33:08 -04001705 if (data->brightness)
Yu, Luming973bf492006-04-27 05:25:00 -04001706 kfree(data->brightness->levels);
Jesper Juhl6044ec82005-11-07 01:01:32 -08001707 kfree(data->brightness);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 kfree(data);
1709 }
1710
Patrick Mocheld550d982006-06-27 00:41:40 -04001711 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712}
1713
1714/* acpi_video interface */
1715
Len Brown4be44fc2005-08-05 00:44:28 -04001716static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717{
1718 return acpi_video_bus_DOS(video, 1, 0);
1719}
1720
Len Brown4be44fc2005-08-05 00:44:28 -04001721static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722{
1723 return acpi_video_bus_DOS(video, 0, 1);
1724}
1725
Len Brown4be44fc2005-08-05 00:44:28 -04001726static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001728 struct acpi_video_bus *video = data;
Len Brown4be44fc2005-08-05 00:44:28 -04001729 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731 printk("video bus notify\n");
1732
1733 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001734 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735
Patrick Mochele6afa0d2006-05-19 16:54:40 -04001736 device = video->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737
1738 switch (event) {
1739 case ACPI_VIDEO_NOTIFY_SWITCH: /* User request that a switch occur,
1740 * most likely via hotkey. */
1741 acpi_bus_generate_event(device, event, 0);
1742 break;
1743
1744 case ACPI_VIDEO_NOTIFY_PROBE: /* User plug or remove a video
1745 * connector. */
1746 acpi_video_device_enumerate(video);
1747 acpi_video_device_rebind(video);
1748 acpi_video_switch_output(video, event);
1749 acpi_bus_generate_event(device, event, 0);
1750 break;
1751
Len Brown4be44fc2005-08-05 00:44:28 -04001752 case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */
1753 case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */
1754 case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 acpi_video_switch_output(video, event);
1756 acpi_bus_generate_event(device, event, 0);
1757 break;
1758
1759 default:
1760 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -04001761 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 break;
1763 }
1764
Patrick Mocheld550d982006-06-27 00:41:40 -04001765 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766}
1767
Len Brown4be44fc2005-08-05 00:44:28 -04001768static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001770 struct acpi_video_device *video_device = data;
Len Brown4be44fc2005-08-05 00:44:28 -04001771 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 if (!video_device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001774 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775
Patrick Mochele6afa0d2006-05-19 16:54:40 -04001776 device = video_device->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
1778 switch (event) {
Len Brown4be44fc2005-08-05 00:44:28 -04001779 case ACPI_VIDEO_NOTIFY_SWITCH: /* change in status (cycle output device) */
1780 case ACPI_VIDEO_NOTIFY_PROBE: /* change in status (output device status) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781 acpi_bus_generate_event(device, event, 0);
1782 break;
Len Brown4be44fc2005-08-05 00:44:28 -04001783 case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */
1784 case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */
1785 case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */
1786 case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightnesss */
1787 case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */
1788 acpi_video_switch_brightness(video_device, event);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789 acpi_bus_generate_event(device, event, 0);
1790 break;
1791 default:
1792 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -04001793 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794 break;
1795 }
Patrick Mocheld550d982006-06-27 00:41:40 -04001796 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797}
1798
Len Brown4be44fc2005-08-05 00:44:28 -04001799static int acpi_video_bus_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800{
Len Brown4be44fc2005-08-05 00:44:28 -04001801 int result = 0;
1802 acpi_status status = 0;
1803 struct acpi_video_bus *video = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804
Len Brown4be44fc2005-08-05 00:44:28 -04001805
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001807 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808
Burman Yan36bcbec2006-12-19 12:56:11 -08001809 video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001811 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812
Patrick Mochele6afa0d2006-05-19 16:54:40 -04001813 video->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814 strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
1815 strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
1816 acpi_driver_data(device) = video;
1817
1818 acpi_video_bus_find_cap(video);
1819 result = acpi_video_bus_check(video);
1820 if (result)
1821 goto end;
1822
1823 result = acpi_video_bus_add_fs(device);
1824 if (result)
1825 goto end;
1826
1827 init_MUTEX(&video->sem);
1828 INIT_LIST_HEAD(&video->video_device_list);
1829
1830 acpi_video_bus_get_devices(video, device);
1831 acpi_video_bus_start_devices(video);
1832
Patrick Mochel90130262006-05-19 16:54:48 -04001833 status = acpi_install_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001834 ACPI_DEVICE_NOTIFY,
1835 acpi_video_bus_notify, video);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001836 if (ACPI_FAILURE(status)) {
1837 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
Len Brown4be44fc2005-08-05 00:44:28 -04001838 "Error installing notify handler\n"));
Yu, Luming973bf492006-04-27 05:25:00 -04001839 acpi_video_bus_stop_devices(video);
1840 acpi_video_bus_put_devices(video);
1841 kfree(video->attached_array);
1842 acpi_video_bus_remove_fs(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843 result = -ENODEV;
1844 goto end;
1845 }
1846
1847 printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001848 ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
1849 video->flags.multihead ? "yes" : "no",
1850 video->flags.rom ? "yes" : "no",
1851 video->flags.post ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852
Len Brown4be44fc2005-08-05 00:44:28 -04001853 end:
Yu, Luming973bf492006-04-27 05:25:00 -04001854 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855 kfree(video);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856
Patrick Mocheld550d982006-06-27 00:41:40 -04001857 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858}
1859
Len Brown4be44fc2005-08-05 00:44:28 -04001860static int acpi_video_bus_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861{
Len Brown4be44fc2005-08-05 00:44:28 -04001862 acpi_status status = 0;
1863 struct acpi_video_bus *video = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865
1866 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001867 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001869 video = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870
1871 acpi_video_bus_stop_devices(video);
1872
Patrick Mochel90130262006-05-19 16:54:48 -04001873 status = acpi_remove_notify_handler(video->device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001874 ACPI_DEVICE_NOTIFY,
1875 acpi_video_bus_notify);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876
1877 acpi_video_bus_put_devices(video);
1878 acpi_video_bus_remove_fs(device);
1879
Jesper Juhl6044ec82005-11-07 01:01:32 -08001880 kfree(video->attached_array);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 kfree(video);
1882
Patrick Mocheld550d982006-06-27 00:41:40 -04001883 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884}
1885
Len Brown4be44fc2005-08-05 00:44:28 -04001886static int __init acpi_video_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887{
Len Brown4be44fc2005-08-05 00:44:28 -04001888 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890
1891 /*
Len Brown4be44fc2005-08-05 00:44:28 -04001892 acpi_dbg_level = 0xFFFFFFFF;
1893 acpi_dbg_layer = 0x08000000;
1894 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895
1896 acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
1897 if (!acpi_video_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001898 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 acpi_video_dir->owner = THIS_MODULE;
1900
1901 result = acpi_bus_register_driver(&acpi_video_bus);
1902 if (result < 0) {
1903 remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -04001904 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905 }
1906
Patrick Mocheld550d982006-06-27 00:41:40 -04001907 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908}
1909
Len Brown4be44fc2005-08-05 00:44:28 -04001910static void __exit acpi_video_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912
1913 acpi_bus_unregister_driver(&acpi_video_bus);
1914
1915 remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
1916
Patrick Mocheld550d982006-06-27 00:41:40 -04001917 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918}
1919
1920module_init(acpi_video_init);
1921module_exit(acpi_video_exit);