blob: 67dec0c675aae4d59e71c689ccc450ff5896c5c6 [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>
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -050032#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/proc_fs.h>
34#include <linux/seq_file.h>
Luming Yue9dab192007-08-20 18:23:53 +080035#include <linux/input.h>
Yu Luming2f3d0002006-11-11 02:40:34 +080036#include <linux/backlight.h>
Zhang Rui702ed512008-01-17 15:51:22 +080037#include <linux/thermal.h>
Luming Yu23b0f012007-05-09 21:07:05 +080038#include <linux/video_output.h>
Zhang Rui935e5f22008-12-11 16:24:52 -050039#include <linux/sort.h>
Matthew Garrett74a365b2009-03-19 21:35:39 +000040#include <linux/pci.h>
41#include <linux/pci_ids.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090042#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <asm/uaccess.h>
Len Browneb27cae2009-07-06 23:40:19 -040044#include <linux/dmi.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#include <acpi/acpi_bus.h>
46#include <acpi/acpi_drivers.h>
Rafael J. Wysockiac7729d2010-04-05 01:43:51 +020047#include <linux/suspend.h>
Matthew Garrette92a7162010-01-12 14:17:03 -050048#include <acpi/video.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
Len Browna192a952009-07-28 16:45:54 -040050#define PREFIX "ACPI: "
51
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#define ACPI_VIDEO_CLASS "video"
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#define ACPI_VIDEO_BUS_NAME "Video Bus"
54#define ACPI_VIDEO_DEVICE_NAME "Video Device"
55#define ACPI_VIDEO_NOTIFY_SWITCH 0x80
56#define ACPI_VIDEO_NOTIFY_PROBE 0x81
57#define ACPI_VIDEO_NOTIFY_CYCLE 0x82
58#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT 0x83
59#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT 0x84
60
Thomas Tuttlef4715182006-12-19 12:56:14 -080061#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS 0x85
62#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
63#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
64#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS 0x88
65#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF 0x89
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Yu Luming2f3d0002006-11-11 02:40:34 +080067#define MAX_NAME_LEN 20
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Linus Torvalds1da177e2005-04-16 15:20:36 -070069#define _COMPONENT ACPI_VIDEO_COMPONENT
Len Brownf52fd662007-02-12 22:42:12 -050070ACPI_MODULE_NAME("video");
Linus Torvalds1da177e2005-04-16 15:20:36 -070071
Len Brownf52fd662007-02-12 22:42:12 -050072MODULE_AUTHOR("Bruno Ducrot");
Len Brown7cda93e2007-02-12 23:50:02 -050073MODULE_DESCRIPTION("ACPI Video Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070074MODULE_LICENSE("GPL");
75
Zhang Rui8a681a42008-01-25 14:47:49 +080076static int brightness_switch_enabled = 1;
77module_param(brightness_switch_enabled, bool, 0644);
78
Zhang Ruic504f8c2009-12-30 15:59:23 +080079/*
80 * By default, we don't allow duplicate ACPI video bus devices
81 * under the same VGA controller
82 */
83static int allow_duplicates;
84module_param(allow_duplicates, bool, 0644);
85
Zhao Yakui86e437f2009-06-16 11:23:13 +080086static int register_count = 0;
Len Brown4be44fc2005-08-05 00:44:28 -040087static int acpi_video_bus_add(struct acpi_device *device);
88static int acpi_video_bus_remove(struct acpi_device *device, int type);
Bjorn Helgaas70155582009-04-07 15:37:11 +000089static void acpi_video_bus_notify(struct acpi_device *device, u32 event);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
Thomas Renninger1ba90e32007-07-23 14:44:41 +020091static const struct acpi_device_id video_device_ids[] = {
92 {ACPI_VIDEO_HID, 0},
93 {"", 0},
94};
95MODULE_DEVICE_TABLE(acpi, video_device_ids);
96
Linus Torvalds1da177e2005-04-16 15:20:36 -070097static struct acpi_driver acpi_video_bus = {
Len Brownc2b67052007-02-12 23:33:40 -050098 .name = "video",
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 .class = ACPI_VIDEO_CLASS,
Thomas Renninger1ba90e32007-07-23 14:44:41 +0200100 .ids = video_device_ids,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 .ops = {
102 .add = acpi_video_bus_add,
103 .remove = acpi_video_bus_remove,
Bjorn Helgaas70155582009-04-07 15:37:11 +0000104 .notify = acpi_video_bus_notify,
Len Brown4be44fc2005-08-05 00:44:28 -0400105 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106};
107
108struct acpi_video_bus_flags {
Len Brown4be44fc2005-08-05 00:44:28 -0400109 u8 multihead:1; /* can switch video heads */
110 u8 rom:1; /* can retrieve a video rom */
111 u8 post:1; /* can configure the head to */
112 u8 reserved:5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113};
114
115struct acpi_video_bus_cap {
Len Brown4be44fc2005-08-05 00:44:28 -0400116 u8 _DOS:1; /*Enable/Disable output switching */
117 u8 _DOD:1; /*Enumerate all devices attached to display adapter */
118 u8 _ROM:1; /*Get ROM Data */
119 u8 _GPD:1; /*Get POST Device */
120 u8 _SPD:1; /*Set POST Device */
121 u8 _VPO:1; /*Video POST Options */
122 u8 reserved:2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123};
124
Len Brown4be44fc2005-08-05 00:44:28 -0400125struct acpi_video_device_attrib {
126 u32 display_index:4; /* A zero-based instance of the Display */
Julius Volz98fb8fe2007-02-20 16:38:40 +0100127 u32 display_port_attachment:4; /*This field differentiates the display type */
Len Brown4be44fc2005-08-05 00:44:28 -0400128 u32 display_type:4; /*Describe the specific type in use */
Julius Volz98fb8fe2007-02-20 16:38:40 +0100129 u32 vendor_specific:4; /*Chipset Vendor Specific */
Len Brown4be44fc2005-08-05 00:44:28 -0400130 u32 bios_can_detect:1; /*BIOS can detect the device */
131 u32 depend_on_vga:1; /*Non-VGA output device whose power is related to
132 the VGA device. */
133 u32 pipe_id:3; /*For VGA multiple-head devices. */
134 u32 reserved:10; /*Must be 0 */
135 u32 device_id_scheme:1; /*Device ID Scheme */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136};
137
138struct acpi_video_enumerated_device {
139 union {
140 u32 int_val;
Len Brown4be44fc2005-08-05 00:44:28 -0400141 struct acpi_video_device_attrib attrib;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 } value;
143 struct acpi_video_device *bind_info;
144};
145
146struct acpi_video_bus {
Patrick Mochele6afa0d2006-05-19 16:54:40 -0400147 struct acpi_device *device;
Len Brown4be44fc2005-08-05 00:44:28 -0400148 u8 dos_setting;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 struct acpi_video_enumerated_device *attached_array;
Len Brown4be44fc2005-08-05 00:44:28 -0400150 u8 attached_count;
151 struct acpi_video_bus_cap cap;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 struct acpi_video_bus_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400153 struct list_head video_device_list;
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -0500154 struct mutex device_list_lock; /* protects video_device_list */
Zhang Rui6e37c652010-07-15 10:46:43 +0800155#ifdef CONFIG_ACPI_PROCFS
Len Brown4be44fc2005-08-05 00:44:28 -0400156 struct proc_dir_entry *dir;
Zhang Rui6e37c652010-07-15 10:46:43 +0800157#endif
Luming Yue9dab192007-08-20 18:23:53 +0800158 struct input_dev *input;
159 char phys[32]; /* for input device */
Rafael J. Wysockiac7729d2010-04-05 01:43:51 +0200160 struct notifier_block pm_nb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161};
162
163struct acpi_video_device_flags {
Len Brown4be44fc2005-08-05 00:44:28 -0400164 u8 crt:1;
165 u8 lcd:1;
166 u8 tvout:1;
Rui Zhang82cae992007-01-03 23:40:53 -0500167 u8 dvi:1;
Len Brown4be44fc2005-08-05 00:44:28 -0400168 u8 bios:1;
169 u8 unknown:1;
Rui Zhang82cae992007-01-03 23:40:53 -0500170 u8 reserved:2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171};
172
173struct acpi_video_device_cap {
Len Brown4be44fc2005-08-05 00:44:28 -0400174 u8 _ADR:1; /*Return the unique ID */
175 u8 _BCL:1; /*Query list of brightness control levels supported */
176 u8 _BCM:1; /*Set the brightness level */
Yu Luming2f3d0002006-11-11 02:40:34 +0800177 u8 _BQC:1; /* Get current brightness level */
Zhang Ruic60d6382009-03-18 16:27:18 +0800178 u8 _BCQ:1; /* Some buggy BIOS uses _BCQ instead of _BQC */
Len Brown4be44fc2005-08-05 00:44:28 -0400179 u8 _DDC:1; /*Return the EDID for this device */
180 u8 _DCS:1; /*Return status of output device */
181 u8 _DGS:1; /*Query graphics state */
182 u8 _DSS:1; /*Device state set */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183};
184
Zhang Ruid32f6942009-03-18 16:27:12 +0800185struct acpi_video_brightness_flags {
186 u8 _BCL_no_ac_battery_levels:1; /* no AC/Battery levels in _BCL */
Zhang Ruid80fb992009-03-18 16:27:14 +0800187 u8 _BCL_reversed:1; /* _BCL package is in a reversed order*/
Zhang Rui1a7c6182009-03-18 16:27:16 +0800188 u8 _BCL_use_index:1; /* levels in _BCL are index values */
189 u8 _BCM_use_index:1; /* input of _BCM is an index value */
190 u8 _BQC_use_index:1; /* _BQC returns an index value */
Zhang Ruid32f6942009-03-18 16:27:12 +0800191};
192
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193struct acpi_video_device_brightness {
Len Brown4be44fc2005-08-05 00:44:28 -0400194 int curr;
195 int count;
196 int *levels;
Zhang Ruid32f6942009-03-18 16:27:12 +0800197 struct acpi_video_brightness_flags flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198};
199
200struct acpi_video_device {
Len Brown4be44fc2005-08-05 00:44:28 -0400201 unsigned long device_id;
202 struct acpi_video_device_flags flags;
203 struct acpi_video_device_cap cap;
204 struct list_head entry;
205 struct acpi_video_bus *video;
206 struct acpi_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 struct acpi_video_device_brightness *brightness;
Yu Luming2f3d0002006-11-11 02:40:34 +0800208 struct backlight_device *backlight;
Dmitry Torokhov4a703a82009-08-29 23:03:16 -0400209 struct thermal_cooling_device *cooling_dev;
Luming Yu23b0f012007-05-09 21:07:05 +0800210 struct output_device *output_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211};
212
Zhang Rui6e37c652010-07-15 10:46:43 +0800213#ifdef CONFIG_ACPI_PROCFS
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214/* bus */
215static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file);
Jan Engelhardt070d8eb2009-01-12 00:07:55 +0100216static const struct file_operations acpi_video_bus_info_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700217 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400218 .open = acpi_video_bus_info_open_fs,
219 .read = seq_read,
220 .llseek = seq_lseek,
221 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222};
223
224static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file);
Jan Engelhardt070d8eb2009-01-12 00:07:55 +0100225static const struct file_operations acpi_video_bus_ROM_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700226 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400227 .open = acpi_video_bus_ROM_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_bus_POST_info_open_fs(struct inode *inode,
234 struct file *file);
Jan Engelhardt070d8eb2009-01-12 00:07:55 +0100235static const struct file_operations acpi_video_bus_POST_info_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700236 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400237 .open = acpi_video_bus_POST_info_open_fs,
238 .read = seq_read,
239 .llseek = seq_lseek,
240 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241};
242
243static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file);
Len Brownc07c9a72009-04-04 03:33:45 -0400244static ssize_t acpi_video_bus_write_POST(struct file *file,
Jan Engelhardtb7171ae2009-01-12 00:08:19 +0100245 const char __user *buffer, size_t count, loff_t *data);
246static const struct file_operations acpi_video_bus_POST_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700247 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400248 .open = acpi_video_bus_POST_open_fs,
249 .read = seq_read,
Jan Engelhardtb7171ae2009-01-12 00:08:19 +0100250 .write = acpi_video_bus_write_POST,
Len Brown4be44fc2005-08-05 00:44:28 -0400251 .llseek = seq_lseek,
252 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253};
254
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file);
Len Brownc07c9a72009-04-04 03:33:45 -0400256static ssize_t acpi_video_bus_write_DOS(struct file *file,
Jan Engelhardtb7171ae2009-01-12 00:08:19 +0100257 const char __user *buffer, size_t count, loff_t *data);
258static const struct file_operations acpi_video_bus_DOS_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700259 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400260 .open = acpi_video_bus_DOS_open_fs,
261 .read = seq_read,
Jan Engelhardtb7171ae2009-01-12 00:08:19 +0100262 .write = acpi_video_bus_write_DOS,
Len Brown4be44fc2005-08-05 00:44:28 -0400263 .llseek = seq_lseek,
264 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265};
266
267/* device */
Len Brown4be44fc2005-08-05 00:44:28 -0400268static int acpi_video_device_info_open_fs(struct inode *inode,
269 struct file *file);
Jan Engelhardt070d8eb2009-01-12 00:07:55 +0100270static const struct file_operations acpi_video_device_info_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700271 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400272 .open = acpi_video_device_info_open_fs,
273 .read = seq_read,
274 .llseek = seq_lseek,
275 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276};
277
Len Brown4be44fc2005-08-05 00:44:28 -0400278static int acpi_video_device_state_open_fs(struct inode *inode,
279 struct file *file);
Len Brownc07c9a72009-04-04 03:33:45 -0400280static ssize_t acpi_video_device_write_state(struct file *file,
Jan Engelhardtb7171ae2009-01-12 00:08:19 +0100281 const char __user *buffer, size_t count, loff_t *data);
282static const struct file_operations acpi_video_device_state_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700283 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400284 .open = acpi_video_device_state_open_fs,
285 .read = seq_read,
Jan Engelhardtb7171ae2009-01-12 00:08:19 +0100286 .write = acpi_video_device_write_state,
Len Brown4be44fc2005-08-05 00:44:28 -0400287 .llseek = seq_lseek,
288 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289};
290
Len Brown4be44fc2005-08-05 00:44:28 -0400291static int acpi_video_device_brightness_open_fs(struct inode *inode,
292 struct file *file);
Len Brownc07c9a72009-04-04 03:33:45 -0400293static ssize_t acpi_video_device_write_brightness(struct file *file,
Jan Engelhardtb7171ae2009-01-12 00:08:19 +0100294 const char __user *buffer, size_t count, loff_t *data);
Alexey Dobriyan828c0952009-10-01 15:43:56 -0700295static const struct file_operations acpi_video_device_brightness_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700296 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400297 .open = acpi_video_device_brightness_open_fs,
298 .read = seq_read,
Jan Engelhardtb7171ae2009-01-12 00:08:19 +0100299 .write = acpi_video_device_write_brightness,
Len Brown4be44fc2005-08-05 00:44:28 -0400300 .llseek = seq_lseek,
301 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302};
303
Len Brown4be44fc2005-08-05 00:44:28 -0400304static int acpi_video_device_EDID_open_fs(struct inode *inode,
305 struct file *file);
Jan Engelhardt070d8eb2009-01-12 00:07:55 +0100306static const struct file_operations acpi_video_device_EDID_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700307 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400308 .open = acpi_video_device_EDID_open_fs,
309 .read = seq_read,
310 .llseek = seq_lseek,
311 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312};
Zhang Rui6e37c652010-07-15 10:46:43 +0800313#endif /* CONFIG_ACPI_PROCFS */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
Jan Engelhardtb7171ae2009-01-12 00:08:19 +0100315static const char device_decode[][30] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 "motherboard VGA device",
317 "PCI VGA device",
318 "AGP VGA device",
319 "UNKNOWN",
320};
321
Len Brown4be44fc2005-08-05 00:44:28 -0400322static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data);
323static void acpi_video_device_rebind(struct acpi_video_bus *video);
324static void acpi_video_device_bind(struct acpi_video_bus *video,
325 struct acpi_video_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326static int acpi_video_device_enumerate(struct acpi_video_bus *video);
Yu Luming2f3d0002006-11-11 02:40:34 +0800327static int acpi_video_device_lcd_set_level(struct acpi_video_device *device,
328 int level);
329static int acpi_video_device_lcd_get_level_current(
330 struct acpi_video_device *device,
Matthew Garrett70287db2010-02-16 16:53:50 -0500331 unsigned long long *level, int init);
Len Brown4be44fc2005-08-05 00:44:28 -0400332static int acpi_video_get_next_level(struct acpi_video_device *device,
333 u32 level_current, u32 event);
Zhang Ruic8890f92009-03-18 16:27:08 +0800334static int acpi_video_switch_brightness(struct acpi_video_device *device,
Len Brown4be44fc2005-08-05 00:44:28 -0400335 int event);
Luming Yu23b0f012007-05-09 21:07:05 +0800336static int acpi_video_device_get_state(struct acpi_video_device *device,
Matthew Wilcox27663c52008-10-10 02:22:59 -0400337 unsigned long long *state);
Luming Yu23b0f012007-05-09 21:07:05 +0800338static int acpi_video_output_get(struct output_device *od);
339static int acpi_video_device_set_state(struct acpi_video_device *device, int state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
Yu Luming2f3d0002006-11-11 02:40:34 +0800341/*backlight device sysfs support*/
342static int acpi_video_get_brightness(struct backlight_device *bd)
343{
Matthew Wilcox27663c52008-10-10 02:22:59 -0400344 unsigned long long cur_level;
Matthew Garrett38531e62007-12-26 02:03:26 +0000345 int i;
Yu Luming2f3d0002006-11-11 02:40:34 +0800346 struct acpi_video_device *vd =
Richard Purdie655bfd72007-07-09 12:17:24 +0100347 (struct acpi_video_device *)bl_get_data(bd);
Zhang Ruic8890f92009-03-18 16:27:08 +0800348
Matthew Garrett70287db2010-02-16 16:53:50 -0500349 if (acpi_video_device_lcd_get_level_current(vd, &cur_level, 0))
Zhang Ruic8890f92009-03-18 16:27:08 +0800350 return -EINVAL;
Matthew Garrett38531e62007-12-26 02:03:26 +0000351 for (i = 2; i < vd->brightness->count; i++) {
352 if (vd->brightness->levels[i] == cur_level)
353 /* The first two entries are special - see page 575
354 of the ACPI spec 3.0 */
355 return i-2;
356 }
357 return 0;
Yu Luming2f3d0002006-11-11 02:40:34 +0800358}
359
360static int acpi_video_set_brightness(struct backlight_device *bd)
361{
Zhang Rui24450c72009-03-18 16:27:10 +0800362 int request_level = bd->props.brightness + 2;
Yu Luming2f3d0002006-11-11 02:40:34 +0800363 struct acpi_video_device *vd =
Richard Purdie655bfd72007-07-09 12:17:24 +0100364 (struct acpi_video_device *)bl_get_data(bd);
Zhang Rui24450c72009-03-18 16:27:10 +0800365
366 return acpi_video_device_lcd_set_level(vd,
367 vd->brightness->levels[request_level]);
Yu Luming2f3d0002006-11-11 02:40:34 +0800368}
369
Richard Purdie599a52d2007-02-10 23:07:48 +0000370static struct backlight_ops acpi_backlight_ops = {
371 .get_brightness = acpi_video_get_brightness,
372 .update_status = acpi_video_set_brightness,
373};
374
Luming Yu23b0f012007-05-09 21:07:05 +0800375/*video output device sysfs support*/
376static int acpi_video_output_get(struct output_device *od)
377{
Matthew Wilcox27663c52008-10-10 02:22:59 -0400378 unsigned long long state;
Luming Yu23b0f012007-05-09 21:07:05 +0800379 struct acpi_video_device *vd =
tonyj@suse.de60043422007-08-07 22:28:47 -0700380 (struct acpi_video_device *)dev_get_drvdata(&od->dev);
Luming Yu23b0f012007-05-09 21:07:05 +0800381 acpi_video_device_get_state(vd, &state);
382 return (int)state;
383}
384
385static int acpi_video_output_set(struct output_device *od)
386{
387 unsigned long state = od->request_state;
388 struct acpi_video_device *vd=
tonyj@suse.de60043422007-08-07 22:28:47 -0700389 (struct acpi_video_device *)dev_get_drvdata(&od->dev);
Luming Yu23b0f012007-05-09 21:07:05 +0800390 return acpi_video_device_set_state(vd, state);
391}
392
393static struct output_properties acpi_output_properties = {
394 .set_state = acpi_video_output_set,
395 .get_status = acpi_video_output_get,
396};
Zhang Rui702ed512008-01-17 15:51:22 +0800397
398
399/* thermal cooling device callbacks */
Dmitry Torokhov4a703a82009-08-29 23:03:16 -0400400static int video_get_max_state(struct thermal_cooling_device *cooling_dev, unsigned
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000401 long *state)
Zhang Rui702ed512008-01-17 15:51:22 +0800402{
Dmitry Torokhov4a703a82009-08-29 23:03:16 -0400403 struct acpi_device *device = cooling_dev->devdata;
Zhang Rui702ed512008-01-17 15:51:22 +0800404 struct acpi_video_device *video = acpi_driver_data(device);
405
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000406 *state = video->brightness->count - 3;
407 return 0;
Zhang Rui702ed512008-01-17 15:51:22 +0800408}
409
Dmitry Torokhov4a703a82009-08-29 23:03:16 -0400410static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, unsigned
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000411 long *state)
Zhang Rui702ed512008-01-17 15:51:22 +0800412{
Dmitry Torokhov4a703a82009-08-29 23:03:16 -0400413 struct acpi_device *device = cooling_dev->devdata;
Zhang Rui702ed512008-01-17 15:51:22 +0800414 struct acpi_video_device *video = acpi_driver_data(device);
Matthew Wilcox27663c52008-10-10 02:22:59 -0400415 unsigned long long level;
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000416 int offset;
Zhang Rui702ed512008-01-17 15:51:22 +0800417
Matthew Garrett70287db2010-02-16 16:53:50 -0500418 if (acpi_video_device_lcd_get_level_current(video, &level, 0))
Zhang Ruic8890f92009-03-18 16:27:08 +0800419 return -EINVAL;
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000420 for (offset = 2; offset < video->brightness->count; offset++)
421 if (level == video->brightness->levels[offset]) {
422 *state = video->brightness->count - offset - 1;
423 return 0;
424 }
Zhang Rui702ed512008-01-17 15:51:22 +0800425
426 return -EINVAL;
427}
428
429static int
Dmitry Torokhov4a703a82009-08-29 23:03:16 -0400430video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state)
Zhang Rui702ed512008-01-17 15:51:22 +0800431{
Dmitry Torokhov4a703a82009-08-29 23:03:16 -0400432 struct acpi_device *device = cooling_dev->devdata;
Zhang Rui702ed512008-01-17 15:51:22 +0800433 struct acpi_video_device *video = acpi_driver_data(device);
434 int level;
435
436 if ( state >= video->brightness->count - 2)
437 return -EINVAL;
438
439 state = video->brightness->count - state;
440 level = video->brightness->levels[state -1];
441 return acpi_video_device_lcd_set_level(video, level);
442}
443
444static struct thermal_cooling_device_ops video_cooling_ops = {
445 .get_max_state = video_get_max_state,
446 .get_cur_state = video_get_cur_state,
447 .set_cur_state = video_set_cur_state,
448};
449
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450/* --------------------------------------------------------------------------
451 Video Management
452 -------------------------------------------------------------------------- */
453
454/* device */
455
456static int
Len Brown4be44fc2005-08-05 00:44:28 -0400457acpi_video_device_get_state(struct acpi_video_device *device,
Matthew Wilcox27663c52008-10-10 02:22:59 -0400458 unsigned long long *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459{
Len Brown4be44fc2005-08-05 00:44:28 -0400460 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
Patrick Mochel90130262006-05-19 16:54:48 -0400462 status = acpi_evaluate_integer(device->dev->handle, "_DCS", NULL, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463
Patrick Mocheld550d982006-06-27 00:41:40 -0400464 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465}
466
467static int
Len Brown4be44fc2005-08-05 00:44:28 -0400468acpi_video_device_set_state(struct acpi_video_device *device, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469{
Len Brown4be44fc2005-08-05 00:44:28 -0400470 int status;
471 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
472 struct acpi_object_list args = { 1, &arg0 };
Matthew Wilcox27663c52008-10-10 02:22:59 -0400473 unsigned long long ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475
476 arg0.integer.value = state;
Patrick Mochel90130262006-05-19 16:54:48 -0400477 status = acpi_evaluate_integer(device->dev->handle, "_DSS", &args, &ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478
Patrick Mocheld550d982006-06-27 00:41:40 -0400479 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480}
481
482static int
Len Brown4be44fc2005-08-05 00:44:28 -0400483acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
484 union acpi_object **levels)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485{
Len Brown4be44fc2005-08-05 00:44:28 -0400486 int status;
487 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
488 union acpi_object *obj;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
491 *levels = NULL;
492
Patrick Mochel90130262006-05-19 16:54:48 -0400493 status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400495 return status;
Len Brown4be44fc2005-08-05 00:44:28 -0400496 obj = (union acpi_object *)buffer.pointer;
Adrian Bunk6665bda2006-03-11 10:12:00 -0500497 if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
Len Brown64684632006-06-26 23:41:38 -0400498 printk(KERN_ERR PREFIX "Invalid _BCL data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 status = -EFAULT;
500 goto err;
501 }
502
503 *levels = obj;
504
Patrick Mocheld550d982006-06-27 00:41:40 -0400505 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
Len Brown4be44fc2005-08-05 00:44:28 -0400507 err:
Jesper Juhl6044ec82005-11-07 01:01:32 -0800508 kfree(buffer.pointer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
Patrick Mocheld550d982006-06-27 00:41:40 -0400510 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511}
512
513static int
Len Brown4be44fc2005-08-05 00:44:28 -0400514acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515{
Zhang Rui24450c72009-03-18 16:27:10 +0800516 int status;
Len Brown4be44fc2005-08-05 00:44:28 -0400517 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
518 struct acpi_object_list args = { 1, &arg0 };
Zhang Rui9e6dada2008-12-31 10:58:48 +0800519 int state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 arg0.integer.value = level;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522
Zhang Rui24450c72009-03-18 16:27:10 +0800523 status = acpi_evaluate_object(device->dev->handle, "_BCM",
524 &args, NULL);
525 if (ACPI_FAILURE(status)) {
526 ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));
527 return -EIO;
528 }
529
Alexey Starikovskiy4500ca82007-09-03 16:29:58 +0400530 device->brightness->curr = level;
Zhang Rui9e6dada2008-12-31 10:58:48 +0800531 for (state = 2; state < device->brightness->count; state++)
Zhang Rui24450c72009-03-18 16:27:10 +0800532 if (level == device->brightness->levels[state]) {
Zhang Rui1a7c6182009-03-18 16:27:16 +0800533 if (device->backlight)
534 device->backlight->props.brightness = state - 2;
Zhang Rui24450c72009-03-18 16:27:10 +0800535 return 0;
536 }
Zhang Rui9e6dada2008-12-31 10:58:48 +0800537
Zhang Rui24450c72009-03-18 16:27:10 +0800538 ACPI_ERROR((AE_INFO, "Current brightness invalid"));
539 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540}
541
Zhang Rui45cb50e2009-04-24 12:13:18 -0400542/*
543 * For some buggy _BQC methods, we need to add a constant value to
544 * the _BQC return value to get the actual current brightness level
545 */
546
547static int bqc_offset_aml_bug_workaround;
548static int __init video_set_bqc_offset(const struct dmi_system_id *d)
549{
550 bqc_offset_aml_bug_workaround = 9;
551 return 0;
552}
553
554static struct dmi_system_id video_dmi_table[] __initdata = {
555 /*
556 * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121
557 */
558 {
559 .callback = video_set_bqc_offset,
560 .ident = "Acer Aspire 5720",
561 .matches = {
562 DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
563 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"),
564 },
565 },
Len Brown5afc4ab2009-05-07 21:11:56 -0400566 {
567 .callback = video_set_bqc_offset,
568 .ident = "Acer Aspire 5710Z",
569 .matches = {
570 DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
571 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710Z"),
572 },
573 },
Zhang Rui34ac2722009-05-26 23:35:34 -0400574 {
575 .callback = video_set_bqc_offset,
576 .ident = "eMachines E510",
577 .matches = {
578 DMI_MATCH(DMI_BOARD_VENDOR, "EMACHINES"),
579 DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"),
580 },
581 },
Zhang Rui93bcece2009-05-19 15:08:41 -0400582 {
583 .callback = video_set_bqc_offset,
584 .ident = "Acer Aspire 5315",
585 .matches = {
586 DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
587 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"),
588 },
589 },
Zhang Rui152a4e62009-06-22 11:31:18 +0800590 {
591 .callback = video_set_bqc_offset,
592 .ident = "Acer Aspire 7720",
593 .matches = {
594 DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
595 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),
596 },
597 },
Zhang Rui45cb50e2009-04-24 12:13:18 -0400598 {}
599};
600
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601static int
Len Brown4be44fc2005-08-05 00:44:28 -0400602acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
Matthew Garrett70287db2010-02-16 16:53:50 -0500603 unsigned long long *level, int init)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604{
Zhang Ruic8890f92009-03-18 16:27:08 +0800605 acpi_status status = AE_OK;
Vladimir Serbinenko4e231fa2009-06-24 15:17:36 +0800606 int i;
Zhang Ruic8890f92009-03-18 16:27:08 +0800607
Zhang Ruic60d6382009-03-18 16:27:18 +0800608 if (device->cap._BQC || device->cap._BCQ) {
609 char *buf = device->cap._BQC ? "_BQC" : "_BCQ";
610
611 status = acpi_evaluate_integer(device->dev->handle, buf,
Zhang Ruic8890f92009-03-18 16:27:08 +0800612 NULL, level);
613 if (ACPI_SUCCESS(status)) {
Zhang Rui1a7c6182009-03-18 16:27:16 +0800614 if (device->brightness->flags._BQC_use_index) {
615 if (device->brightness->flags._BCL_reversed)
616 *level = device->brightness->count
617 - 3 - (*level);
618 *level = device->brightness->levels[*level + 2];
619
620 }
Zhang Rui45cb50e2009-04-24 12:13:18 -0400621 *level += bqc_offset_aml_bug_workaround;
Vladimir Serbinenko4e231fa2009-06-24 15:17:36 +0800622 for (i = 2; i < device->brightness->count; i++)
623 if (device->brightness->levels[i] == *level) {
624 device->brightness->curr = *level;
625 return 0;
626 }
Matthew Garrett70287db2010-02-16 16:53:50 -0500627 if (!init) {
628 /*
629 * BQC returned an invalid level.
630 * Stop using it.
631 */
632 ACPI_WARNING((AE_INFO,
633 "%s returned an invalid level",
634 buf));
635 device->cap._BQC = device->cap._BCQ = 0;
636 }
Zhang Ruic8890f92009-03-18 16:27:08 +0800637 } else {
638 /* Fixme:
639 * should we return an error or ignore this failure?
640 * dev->brightness->curr is a cached value which stores
641 * the correct current backlight level in most cases.
642 * ACPI video backlight still works w/ buggy _BQC.
643 * http://bugzilla.kernel.org/show_bug.cgi?id=12233
644 */
Zhang Ruic60d6382009-03-18 16:27:18 +0800645 ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf));
646 device->cap._BQC = device->cap._BCQ = 0;
Zhang Ruic8890f92009-03-18 16:27:08 +0800647 }
648 }
649
Alexey Starikovskiy4500ca82007-09-03 16:29:58 +0400650 *level = device->brightness->curr;
Zhang Ruic8890f92009-03-18 16:27:08 +0800651 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652}
653
654static int
Len Brown4be44fc2005-08-05 00:44:28 -0400655acpi_video_device_EDID(struct acpi_video_device *device,
656 union acpi_object **edid, ssize_t length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657{
Len Brown4be44fc2005-08-05 00:44:28 -0400658 int status;
659 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
660 union acpi_object *obj;
661 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
662 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
665 *edid = NULL;
666
667 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400668 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 if (length == 128)
670 arg0.integer.value = 1;
671 else if (length == 256)
672 arg0.integer.value = 2;
673 else
Patrick Mocheld550d982006-06-27 00:41:40 -0400674 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
Patrick Mochel90130262006-05-19 16:54:48 -0400676 status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400678 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200680 obj = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681
682 if (obj && obj->type == ACPI_TYPE_BUFFER)
683 *edid = obj;
684 else {
Len Brown64684632006-06-26 23:41:38 -0400685 printk(KERN_ERR PREFIX "Invalid _DDC data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 status = -EFAULT;
687 kfree(obj);
688 }
689
Patrick Mocheld550d982006-06-27 00:41:40 -0400690 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691}
692
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693/* bus */
694
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695/*
696 * Arg:
697 * video : video bus device pointer
698 * bios_flag :
699 * 0. The system BIOS should NOT automatically switch(toggle)
700 * the active display output.
701 * 1. The system BIOS should automatically switch (toggle) the
Julius Volz98fb8fe2007-02-20 16:38:40 +0100702 * active display output. No switch event.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 * 2. The _DGS value should be locked.
704 * 3. The system BIOS should not automatically switch (toggle) the
705 * active display output, but instead generate the display switch
706 * event notify code.
707 * lcd_flag :
708 * 0. The system BIOS should automatically control the brightness level
Julius Volz98fb8fe2007-02-20 16:38:40 +0100709 * of the LCD when the power changes from AC to DC
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 * 1. The system BIOS should NOT automatically control the brightness
Julius Volz98fb8fe2007-02-20 16:38:40 +0100711 * level of the LCD when the power changes from AC to DC.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 * Return Value:
713 * -1 wrong arg.
714 */
715
716static int
Len Brown4be44fc2005-08-05 00:44:28 -0400717acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718{
Lin Ming439913f2010-01-28 10:53:19 +0800719 u64 status = 0;
Len Brown4be44fc2005-08-05 00:44:28 -0400720 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
721 struct acpi_object_list args = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
Len Brown4be44fc2005-08-05 00:44:28 -0400724 if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 status = -1;
726 goto Failed;
727 }
728 arg0.integer.value = (lcd_flag << 2) | bios_flag;
729 video->dos_setting = arg0.integer.value;
Patrick Mochel90130262006-05-19 16:54:48 -0400730 acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731
Len Brown4be44fc2005-08-05 00:44:28 -0400732 Failed:
Patrick Mocheld550d982006-06-27 00:41:40 -0400733 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734}
735
736/*
Zhang Rui935e5f22008-12-11 16:24:52 -0500737 * Simple comparison function used to sort backlight levels.
738 */
739
740static int
741acpi_video_cmp_level(const void *a, const void *b)
742{
743 return *(int *)a - *(int *)b;
744}
745
746/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 * Arg:
748 * device : video output device (LCD, CRT, ..)
749 *
750 * Return Value:
Julia Jomantaite469778c2008-06-23 22:50:42 +0100751 * Maximum brightness level
752 *
753 * Allocate and initialize device->brightness.
754 */
755
756static int
757acpi_video_init_brightness(struct acpi_video_device *device)
758{
759 union acpi_object *obj = NULL;
Zhang Ruid32f6942009-03-18 16:27:12 +0800760 int i, max_level = 0, count = 0, level_ac_battery = 0;
Zhang Rui1a7c6182009-03-18 16:27:16 +0800761 unsigned long long level, level_old;
Julia Jomantaite469778c2008-06-23 22:50:42 +0100762 union acpi_object *o;
763 struct acpi_video_device_brightness *br = NULL;
Zhang Rui1a7c6182009-03-18 16:27:16 +0800764 int result = -EINVAL;
Julia Jomantaite469778c2008-06-23 22:50:42 +0100765
766 if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) {
767 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available "
768 "LCD brightness level\n"));
769 goto out;
770 }
771
772 if (obj->package.count < 2)
773 goto out;
774
775 br = kzalloc(sizeof(*br), GFP_KERNEL);
776 if (!br) {
777 printk(KERN_ERR "can't allocate memory\n");
Zhang Rui1a7c6182009-03-18 16:27:16 +0800778 result = -ENOMEM;
Julia Jomantaite469778c2008-06-23 22:50:42 +0100779 goto out;
780 }
781
Zhang Ruid32f6942009-03-18 16:27:12 +0800782 br->levels = kmalloc((obj->package.count + 2) * sizeof *(br->levels),
Julia Jomantaite469778c2008-06-23 22:50:42 +0100783 GFP_KERNEL);
Zhang Rui1a7c6182009-03-18 16:27:16 +0800784 if (!br->levels) {
785 result = -ENOMEM;
Julia Jomantaite469778c2008-06-23 22:50:42 +0100786 goto out_free;
Zhang Rui1a7c6182009-03-18 16:27:16 +0800787 }
Julia Jomantaite469778c2008-06-23 22:50:42 +0100788
789 for (i = 0; i < obj->package.count; i++) {
790 o = (union acpi_object *)&obj->package.elements[i];
791 if (o->type != ACPI_TYPE_INTEGER) {
792 printk(KERN_ERR PREFIX "Invalid data\n");
793 continue;
794 }
795 br->levels[count] = (u32) o->integer.value;
796
797 if (br->levels[count] > max_level)
798 max_level = br->levels[count];
799 count++;
800 }
801
Zhang Ruid32f6942009-03-18 16:27:12 +0800802 /*
803 * some buggy BIOS don't export the levels
804 * when machine is on AC/Battery in _BCL package.
805 * In this case, the first two elements in _BCL packages
806 * are also supported brightness levels that OS should take care of.
807 */
Zhang Rui90af2cf2009-04-14 11:02:18 +0800808 for (i = 2; i < count; i++) {
809 if (br->levels[i] == br->levels[0])
Zhang Ruid32f6942009-03-18 16:27:12 +0800810 level_ac_battery++;
Zhang Rui90af2cf2009-04-14 11:02:18 +0800811 if (br->levels[i] == br->levels[1])
812 level_ac_battery++;
813 }
Zhang Rui935e5f22008-12-11 16:24:52 -0500814
Zhang Ruid32f6942009-03-18 16:27:12 +0800815 if (level_ac_battery < 2) {
816 level_ac_battery = 2 - level_ac_battery;
817 br->flags._BCL_no_ac_battery_levels = 1;
818 for (i = (count - 1 + level_ac_battery); i >= 2; i--)
819 br->levels[i] = br->levels[i - level_ac_battery];
820 count += level_ac_battery;
821 } else if (level_ac_battery > 2)
822 ACPI_ERROR((AE_INFO, "Too many duplicates in _BCL package\n"));
823
Zhang Ruid80fb992009-03-18 16:27:14 +0800824 /* Check if the _BCL package is in a reversed order */
825 if (max_level == br->levels[2]) {
826 br->flags._BCL_reversed = 1;
827 sort(&br->levels[2], count - 2, sizeof(br->levels[2]),
828 acpi_video_cmp_level, NULL);
829 } else if (max_level != br->levels[count - 1])
830 ACPI_ERROR((AE_INFO,
831 "Found unordered _BCL package\n"));
Julia Jomantaite469778c2008-06-23 22:50:42 +0100832
833 br->count = count;
834 device->brightness = br;
Zhang Rui1a7c6182009-03-18 16:27:16 +0800835
836 /* Check the input/output of _BQC/_BCL/_BCM */
837 if ((max_level < 100) && (max_level <= (count - 2)))
838 br->flags._BCL_use_index = 1;
839
840 /*
841 * _BCM is always consistent with _BCL,
842 * at least for all the laptops we have ever seen.
843 */
844 br->flags._BCM_use_index = br->flags._BCL_use_index;
845
846 /* _BQC uses INDEX while _BCL uses VALUE in some laptops */
Zhang Rui90c53ca2009-08-31 12:39:54 -0400847 br->curr = level = max_level;
Zhang Ruie047cca2009-04-09 14:24:35 +0800848
849 if (!device->cap._BQC)
850 goto set_level;
851
Matthew Garrett70287db2010-02-16 16:53:50 -0500852 result = acpi_video_device_lcd_get_level_current(device, &level_old, 1);
Zhang Rui1a7c6182009-03-18 16:27:16 +0800853 if (result)
854 goto out_free_levels;
855
Zhang Ruie047cca2009-04-09 14:24:35 +0800856 /*
857 * Set the level to maximum and check if _BQC uses indexed value
858 */
859 result = acpi_video_device_lcd_set_level(device, max_level);
Zhang Rui1a7c6182009-03-18 16:27:16 +0800860 if (result)
861 goto out_free_levels;
862
Matthew Garrett70287db2010-02-16 16:53:50 -0500863 result = acpi_video_device_lcd_get_level_current(device, &level, 0);
Zhang Rui1a7c6182009-03-18 16:27:16 +0800864 if (result)
865 goto out_free_levels;
866
Zhang Ruie047cca2009-04-09 14:24:35 +0800867 br->flags._BQC_use_index = (level == max_level ? 0 : 1);
Zhang Rui1a7c6182009-03-18 16:27:16 +0800868
Zhang Rui90c53ca2009-08-31 12:39:54 -0400869 if (!br->flags._BQC_use_index) {
870 /*
871 * Set the backlight to the initial state.
872 * On some buggy laptops, _BQC returns an uninitialized value
873 * when invoked for the first time, i.e. level_old is invalid.
874 * set the backlight to max_level in this case
875 */
876 for (i = 2; i < br->count; i++)
877 if (level_old == br->levels[i])
878 level = level_old;
Zhang Ruie047cca2009-04-09 14:24:35 +0800879 goto set_level;
Zhang Rui90c53ca2009-08-31 12:39:54 -0400880 }
Zhang Ruie047cca2009-04-09 14:24:35 +0800881
882 if (br->flags._BCL_reversed)
883 level_old = (br->count - 1) - level_old;
Zhang Rui90c53ca2009-08-31 12:39:54 -0400884 level = br->levels[level_old];
Zhang Ruie047cca2009-04-09 14:24:35 +0800885
886set_level:
Zhang Rui90c53ca2009-08-31 12:39:54 -0400887 result = acpi_video_device_lcd_set_level(device, level);
Zhang Ruie047cca2009-04-09 14:24:35 +0800888 if (result)
889 goto out_free_levels;
Zhang Rui1a7c6182009-03-18 16:27:16 +0800890
Zhang Ruid32f6942009-03-18 16:27:12 +0800891 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
892 "found %d brightness levels\n", count - 2));
Julia Jomantaite469778c2008-06-23 22:50:42 +0100893 kfree(obj);
Zhang Rui1a7c6182009-03-18 16:27:16 +0800894 return result;
Julia Jomantaite469778c2008-06-23 22:50:42 +0100895
896out_free_levels:
897 kfree(br->levels);
898out_free:
899 kfree(br);
900out:
901 device->brightness = NULL;
902 kfree(obj);
Zhang Rui1a7c6182009-03-18 16:27:16 +0800903 return result;
Julia Jomantaite469778c2008-06-23 22:50:42 +0100904}
905
906/*
907 * Arg:
908 * device : video output device (LCD, CRT, ..)
909 *
910 * Return Value:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 * None
912 *
Julius Volz98fb8fe2007-02-20 16:38:40 +0100913 * Find out all required AML methods defined under the output
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 * device.
915 */
916
Len Brown4be44fc2005-08-05 00:44:28 -0400917static void acpi_video_device_find_cap(struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 acpi_handle h_dummy1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
Patrick Mochel90130262006-05-19 16:54:48 -0400921 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 device->cap._ADR = 1;
923 }
Patrick Mochel90130262006-05-19 16:54:48 -0400924 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400925 device->cap._BCL = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 }
Patrick Mochel90130262006-05-19 16:54:48 -0400927 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400928 device->cap._BCM = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 }
Yu Luming2f3d0002006-11-11 02:40:34 +0800930 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
931 device->cap._BQC = 1;
Zhang Ruic60d6382009-03-18 16:27:18 +0800932 else if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCQ",
933 &h_dummy1))) {
934 printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n");
935 device->cap._BCQ = 1;
936 }
937
Patrick Mochel90130262006-05-19 16:54:48 -0400938 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
Len Brown4be44fc2005-08-05 00:44:28 -0400939 device->cap._DDC = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 }
Patrick Mochel90130262006-05-19 16:54:48 -0400941 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DCS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 device->cap._DCS = 1;
943 }
Patrick Mochel90130262006-05-19 16:54:48 -0400944 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DGS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 device->cap._DGS = 1;
946 }
Patrick Mochel90130262006-05-19 16:54:48 -0400947 if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DSS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 device->cap._DSS = 1;
949 }
950
Zhang Rui1a7c6182009-03-18 16:27:16 +0800951 if (acpi_video_backlight_support()) {
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500952 struct backlight_properties props;
Zhang Rui702ed512008-01-17 15:51:22 +0800953 int result;
Yu Luming2f3d0002006-11-11 02:40:34 +0800954 static int count = 0;
955 char *name;
Zhang Rui1a7c6182009-03-18 16:27:16 +0800956
957 result = acpi_video_init_brightness(device);
958 if (result)
959 return;
Julia Lawallaeb834d2010-04-27 14:06:04 -0700960 name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
Yu Luming2f3d0002006-11-11 02:40:34 +0800961 if (!name)
962 return;
Julia Lawallaeb834d2010-04-27 14:06:04 -0700963 count++;
Yu Luming2f3d0002006-11-11 02:40:34 +0800964
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500965 memset(&props, 0, sizeof(struct backlight_properties));
966 props.max_brightness = device->brightness->count - 3;
967 device->backlight = backlight_device_register(name, NULL, device,
968 &acpi_backlight_ops,
969 &props);
Yu Luming2f3d0002006-11-11 02:40:34 +0800970 kfree(name);
Zhang Ruie01ce792009-07-29 08:53:29 +0800971 if (IS_ERR(device->backlight))
972 return;
Zhang Rui702ed512008-01-17 15:51:22 +0800973
Rafael J. Wysockiac7729d2010-04-05 01:43:51 +0200974 /*
975 * Save current brightness level in case we have to restore it
976 * before acpi_video_device_lcd_set_level() is called next time.
977 */
978 device->backlight->props.brightness =
979 acpi_video_get_brightness(device->backlight);
Zhang Rui702ed512008-01-17 15:51:22 +0800980
Zhang Rui056c3082009-06-22 11:31:14 +0800981 result = sysfs_create_link(&device->backlight->dev.kobj,
982 &device->dev->dev.kobj, "device");
983 if (result)
984 printk(KERN_ERR PREFIX "Create sysfs link\n");
985
Dmitry Torokhov4a703a82009-08-29 23:03:16 -0400986 device->cooling_dev = thermal_cooling_device_register("LCD",
Zhang Rui702ed512008-01-17 15:51:22 +0800987 device->dev, &video_cooling_ops);
Dmitry Torokhov4a703a82009-08-29 23:03:16 -0400988 if (IS_ERR(device->cooling_dev)) {
Dmitry Torokhov4b4fe3b2009-08-08 00:26:25 -0700989 /*
Dmitry Torokhov4a703a82009-08-29 23:03:16 -0400990 * Set cooling_dev to NULL so we don't crash trying to
Dmitry Torokhov4b4fe3b2009-08-08 00:26:25 -0700991 * free it.
992 * Also, why the hell we are returning early and
993 * not attempt to register video output if cooling
994 * device registration failed?
995 * -- dtor
996 */
Dmitry Torokhov4a703a82009-08-29 23:03:16 -0400997 device->cooling_dev = NULL;
Thomas Sujith43ff39f2008-02-15 18:29:18 -0500998 return;
Dmitry Torokhov4b4fe3b2009-08-08 00:26:25 -0700999 }
Thomas Sujith43ff39f2008-02-15 18:29:18 -05001000
Greg Kroah-Hartmanfc3a8822008-05-02 06:02:41 +02001001 dev_info(&device->dev->dev, "registered as cooling_device%d\n",
Dmitry Torokhov4a703a82009-08-29 23:03:16 -04001002 device->cooling_dev->id);
Julia Lawall90300622008-04-11 10:09:24 +08001003 result = sysfs_create_link(&device->dev->dev.kobj,
Dmitry Torokhov4a703a82009-08-29 23:03:16 -04001004 &device->cooling_dev->device.kobj,
Julia Lawall90300622008-04-11 10:09:24 +08001005 "thermal_cooling");
1006 if (result)
1007 printk(KERN_ERR PREFIX "Create sysfs link\n");
Dmitry Torokhov4a703a82009-08-29 23:03:16 -04001008 result = sysfs_create_link(&device->cooling_dev->device.kobj,
Julia Lawall90300622008-04-11 10:09:24 +08001009 &device->dev->dev.kobj, "device");
1010 if (result)
1011 printk(KERN_ERR PREFIX "Create sysfs link\n");
1012
Yu Luming2f3d0002006-11-11 02:40:34 +08001013 }
Thomas Renningerc3d6de62008-08-01 17:37:55 +02001014
1015 if (acpi_video_display_switch_support()) {
1016
1017 if (device->cap._DCS && device->cap._DSS) {
1018 static int count;
1019 char *name;
Julia Lawallaeb834d2010-04-27 14:06:04 -07001020 name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
Thomas Renningerc3d6de62008-08-01 17:37:55 +02001021 if (!name)
1022 return;
Julia Lawallaeb834d2010-04-27 14:06:04 -07001023 count++;
Thomas Renningerc3d6de62008-08-01 17:37:55 +02001024 device->output_dev = video_output_register(name,
1025 NULL, device, &acpi_output_properties);
1026 kfree(name);
1027 }
Luming Yu23b0f012007-05-09 21:07:05 +08001028 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029}
1030
1031/*
1032 * Arg:
1033 * device : video output device (VGA)
1034 *
1035 * Return Value:
1036 * None
1037 *
Julius Volz98fb8fe2007-02-20 16:38:40 +01001038 * Find out all required AML methods defined under the video bus device.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 */
1040
Len Brown4be44fc2005-08-05 00:44:28 -04001041static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042{
Len Brown4be44fc2005-08-05 00:44:28 -04001043 acpi_handle h_dummy1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
Patrick Mochel90130262006-05-19 16:54:48 -04001045 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 video->cap._DOS = 1;
1047 }
Patrick Mochel90130262006-05-19 16:54:48 -04001048 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 video->cap._DOD = 1;
1050 }
Patrick Mochel90130262006-05-19 16:54:48 -04001051 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 video->cap._ROM = 1;
1053 }
Patrick Mochel90130262006-05-19 16:54:48 -04001054 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 video->cap._GPD = 1;
1056 }
Patrick Mochel90130262006-05-19 16:54:48 -04001057 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 video->cap._SPD = 1;
1059 }
Patrick Mochel90130262006-05-19 16:54:48 -04001060 if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 video->cap._VPO = 1;
1062 }
1063}
1064
1065/*
1066 * Check whether the video bus device has required AML method to
1067 * support the desired features
1068 */
1069
Len Brown4be44fc2005-08-05 00:44:28 -04001070static int acpi_video_bus_check(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071{
Len Brown4be44fc2005-08-05 00:44:28 -04001072 acpi_status status = -ENOENT;
Alexander Chiang1e4cffe2009-06-10 19:56:00 +00001073 struct pci_dev *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
1075 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001076 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077
Alexander Chiang1e4cffe2009-06-10 19:56:00 +00001078 dev = acpi_get_pci_dev(video->device->handle);
Thomas Renninger22c13f92008-08-01 17:37:54 +02001079 if (!dev)
1080 return -ENODEV;
Alexander Chiang1e4cffe2009-06-10 19:56:00 +00001081 pci_dev_put(dev);
Thomas Renninger22c13f92008-08-01 17:37:54 +02001082
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 /* Since there is no HID, CID and so on for VGA driver, we have
1084 * to check well known required nodes.
1085 */
1086
Julius Volz98fb8fe2007-02-20 16:38:40 +01001087 /* Does this device support video switching? */
Stefan Bader3a1151e2009-08-21 11:03:05 +02001088 if (video->cap._DOS || video->cap._DOD) {
1089 if (!video->cap._DOS) {
1090 printk(KERN_WARNING FW_BUG
1091 "ACPI(%s) defines _DOD but not _DOS\n",
1092 acpi_device_bid(video->device));
1093 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 video->flags.multihead = 1;
1095 status = 0;
1096 }
1097
Julius Volz98fb8fe2007-02-20 16:38:40 +01001098 /* Does this device support retrieving a video ROM? */
Len Brown4be44fc2005-08-05 00:44:28 -04001099 if (video->cap._ROM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 video->flags.rom = 1;
1101 status = 0;
1102 }
1103
Julius Volz98fb8fe2007-02-20 16:38:40 +01001104 /* Does this device support configuring which video device to POST? */
Len Brown4be44fc2005-08-05 00:44:28 -04001105 if (video->cap._GPD && video->cap._SPD && video->cap._VPO) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 video->flags.post = 1;
1107 status = 0;
1108 }
1109
Patrick Mocheld550d982006-06-27 00:41:40 -04001110 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111}
1112
1113/* --------------------------------------------------------------------------
1114 FS Interface (/proc)
1115 -------------------------------------------------------------------------- */
Zhang Rui6e37c652010-07-15 10:46:43 +08001116#ifdef CONFIG_ACPI_PROCFS
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117
Len Brown4be44fc2005-08-05 00:44:28 -04001118static struct proc_dir_entry *acpi_video_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119
1120/* video devices */
1121
Len Brown4be44fc2005-08-05 00:44:28 -04001122static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001124 struct acpi_video_device *dev = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126
1127 if (!dev)
1128 goto end;
1129
1130 seq_printf(seq, "device_id: 0x%04x\n", (u32) dev->device_id);
1131 seq_printf(seq, "type: ");
1132 if (dev->flags.crt)
1133 seq_printf(seq, "CRT\n");
1134 else if (dev->flags.lcd)
1135 seq_printf(seq, "LCD\n");
1136 else if (dev->flags.tvout)
1137 seq_printf(seq, "TVOUT\n");
Rui Zhang82cae992007-01-03 23:40:53 -05001138 else if (dev->flags.dvi)
1139 seq_printf(seq, "DVI\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 else
1141 seq_printf(seq, "UNKNOWN\n");
1142
Len Brown4be44fc2005-08-05 00:44:28 -04001143 seq_printf(seq, "known by bios: %s\n", dev->flags.bios ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144
Len Brown4be44fc2005-08-05 00:44:28 -04001145 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001146 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147}
1148
1149static int
Len Brown4be44fc2005-08-05 00:44:28 -04001150acpi_video_device_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151{
1152 return single_open(file, acpi_video_device_info_seq_show,
1153 PDE(inode)->data);
1154}
1155
Zhang Rui6e37c652010-07-15 10:46:43 +08001156static int
1157acpi_video_device_query(struct acpi_video_device *device,
1158 unsigned long long *state)
1159{
1160 int status;
1161
1162 status = acpi_evaluate_integer(device->dev->handle, "_DGS",
1163 NULL, state);
1164
1165 return status;
1166}
1167
Len Brown4be44fc2005-08-05 00:44:28 -04001168static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169{
Len Brown4be44fc2005-08-05 00:44:28 -04001170 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001171 struct acpi_video_device *dev = seq->private;
Matthew Wilcox27663c52008-10-10 02:22:59 -04001172 unsigned long long state;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174
1175 if (!dev)
1176 goto end;
1177
1178 status = acpi_video_device_get_state(dev, &state);
1179 seq_printf(seq, "state: ");
1180 if (ACPI_SUCCESS(status))
Matthew Wilcox27663c52008-10-10 02:22:59 -04001181 seq_printf(seq, "0x%02llx\n", state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 else
1183 seq_printf(seq, "<not supported>\n");
1184
1185 status = acpi_video_device_query(dev, &state);
1186 seq_printf(seq, "query: ");
1187 if (ACPI_SUCCESS(status))
Matthew Wilcox27663c52008-10-10 02:22:59 -04001188 seq_printf(seq, "0x%02llx\n", state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 else
1190 seq_printf(seq, "<not supported>\n");
1191
Len Brown4be44fc2005-08-05 00:44:28 -04001192 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001193 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194}
1195
1196static int
Len Brown4be44fc2005-08-05 00:44:28 -04001197acpi_video_device_state_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198{
1199 return single_open(file, acpi_video_device_state_seq_show,
1200 PDE(inode)->data);
1201}
1202
1203static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001204acpi_video_device_write_state(struct file *file,
1205 const char __user * buffer,
1206 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207{
Len Brown4be44fc2005-08-05 00:44:28 -04001208 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001209 struct seq_file *m = file->private_data;
1210 struct acpi_video_device *dev = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001211 char str[12] = { 0 };
1212 u32 state = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214
Arjan van de Ven52a2b112009-10-01 15:48:40 -07001215 if (!dev || count >= sizeof(str))
Patrick Mocheld550d982006-06-27 00:41:40 -04001216 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
1218 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001219 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220
1221 str[count] = 0;
1222 state = simple_strtoul(str, NULL, 0);
Len Brown4be44fc2005-08-05 00:44:28 -04001223 state &= ((1ul << 31) | (1ul << 30) | (1ul << 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224
1225 status = acpi_video_device_set_state(dev, state);
1226
1227 if (status)
Patrick Mocheld550d982006-06-27 00:41:40 -04001228 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229
Patrick Mocheld550d982006-06-27 00:41:40 -04001230 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231}
1232
1233static int
Len Brown4be44fc2005-08-05 00:44:28 -04001234acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001236 struct acpi_video_device *dev = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001237 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239
1240 if (!dev || !dev->brightness) {
1241 seq_printf(seq, "<not supported>\n");
Patrick Mocheld550d982006-06-27 00:41:40 -04001242 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 }
1244
1245 seq_printf(seq, "levels: ");
Zhao Yakui0a3db1c2009-02-02 11:33:41 +08001246 for (i = 2; i < dev->brightness->count; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 seq_printf(seq, " %d", dev->brightness->levels[i]);
1248 seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr);
1249
Patrick Mocheld550d982006-06-27 00:41:40 -04001250 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251}
1252
1253static int
Len Brown4be44fc2005-08-05 00:44:28 -04001254acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255{
1256 return single_open(file, acpi_video_device_brightness_seq_show,
1257 PDE(inode)->data);
1258}
1259
1260static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001261acpi_video_device_write_brightness(struct file *file,
1262 const char __user * buffer,
1263 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001265 struct seq_file *m = file->private_data;
1266 struct acpi_video_device *dev = m->private;
Danny Baumannc88c5782007-11-02 13:47:53 +01001267 char str[5] = { 0 };
Len Brown4be44fc2005-08-05 00:44:28 -04001268 unsigned int level = 0;
1269 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271
Arjan van de Ven52a2b112009-10-01 15:48:40 -07001272 if (!dev || !dev->brightness || count >= sizeof(str))
Patrick Mocheld550d982006-06-27 00:41:40 -04001273 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
1275 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001276 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277
1278 str[count] = 0;
1279 level = simple_strtoul(str, NULL, 0);
Len Brown4be44fc2005-08-05 00:44:28 -04001280
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 if (level > 100)
Patrick Mocheld550d982006-06-27 00:41:40 -04001282 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
Julius Volz98fb8fe2007-02-20 16:38:40 +01001284 /* validate through the list of available levels */
Zhao Yakui0a3db1c2009-02-02 11:33:41 +08001285 for (i = 2; i < dev->brightness->count; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 if (level == dev->brightness->levels[i]) {
Zhang Rui24450c72009-03-18 16:27:10 +08001287 if (!acpi_video_device_lcd_set_level(dev, level))
1288 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 break;
1290 }
1291
Zhang Rui24450c72009-03-18 16:27:10 +08001292 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293}
1294
Len Brown4be44fc2005-08-05 00:44:28 -04001295static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001297 struct acpi_video_device *dev = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001298 int status;
1299 int i;
1300 union acpi_object *edid = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302
1303 if (!dev)
1304 goto out;
1305
Len Brown4be44fc2005-08-05 00:44:28 -04001306 status = acpi_video_device_EDID(dev, &edid, 128);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 if (ACPI_FAILURE(status)) {
Len Brown4be44fc2005-08-05 00:44:28 -04001308 status = acpi_video_device_EDID(dev, &edid, 256);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 }
1310
1311 if (ACPI_FAILURE(status)) {
1312 goto out;
1313 }
1314
1315 if (edid && edid->type == ACPI_TYPE_BUFFER) {
1316 for (i = 0; i < edid->buffer.length; i++)
1317 seq_putc(seq, edid->buffer.pointer[i]);
1318 }
1319
Len Brown4be44fc2005-08-05 00:44:28 -04001320 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 if (!edid)
1322 seq_printf(seq, "<not supported>\n");
1323 else
1324 kfree(edid);
1325
Patrick Mocheld550d982006-06-27 00:41:40 -04001326 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327}
1328
1329static int
Len Brown4be44fc2005-08-05 00:44:28 -04001330acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331{
1332 return single_open(file, acpi_video_device_EDID_seq_show,
1333 PDE(inode)->data);
1334}
1335
Len Brown4be44fc2005-08-05 00:44:28 -04001336static int acpi_video_device_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337{
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001338 struct proc_dir_entry *entry, *device_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 struct acpi_video_device *vid_dev;
1340
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001341 vid_dev = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 if (!vid_dev)
Patrick Mocheld550d982006-06-27 00:41:40 -04001343 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001345 device_dir = proc_mkdir(acpi_device_bid(device),
1346 vid_dev->video->dir);
1347 if (!device_dir)
1348 return -ENOMEM;
1349
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 /* 'info' [R] */
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001351 entry = proc_create_data("info", S_IRUGO, device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001352 &acpi_video_device_info_fops, acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001354 goto err_remove_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
1356 /* 'state' [R/W] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001357 entry = proc_create_data("state", S_IFREG | S_IRUGO | S_IWUSR,
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001358 device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001359 &acpi_video_device_state_fops,
1360 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001362 goto err_remove_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
1364 /* 'brightness' [R/W] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001365 entry = proc_create_data("brightness", S_IFREG | S_IRUGO | S_IWUSR,
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001366 device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001367 &acpi_video_device_brightness_fops,
1368 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001370 goto err_remove_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371
1372 /* 'EDID' [R] */
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001373 entry = proc_create_data("EDID", S_IRUGO, device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001374 &acpi_video_device_EDID_fops,
1375 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001377 goto err_remove_brightness;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001379 acpi_device_dir(device) = device_dir;
1380
Patrick Mocheld550d982006-06-27 00:41:40 -04001381 return 0;
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001382
1383 err_remove_brightness:
1384 remove_proc_entry("brightness", device_dir);
1385 err_remove_state:
1386 remove_proc_entry("state", device_dir);
1387 err_remove_info:
1388 remove_proc_entry("info", device_dir);
1389 err_remove_dir:
1390 remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
1391 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392}
1393
Len Brown4be44fc2005-08-05 00:44:28 -04001394static int acpi_video_device_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395{
1396 struct acpi_video_device *vid_dev;
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001397 struct proc_dir_entry *device_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001399 vid_dev = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001401 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001403 device_dir = acpi_device_dir(device);
1404 if (device_dir) {
1405 remove_proc_entry("info", device_dir);
1406 remove_proc_entry("state", device_dir);
1407 remove_proc_entry("brightness", device_dir);
1408 remove_proc_entry("EDID", device_dir);
Len Brown4be44fc2005-08-05 00:44:28 -04001409 remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 acpi_device_dir(device) = NULL;
1411 }
1412
Patrick Mocheld550d982006-06-27 00:41:40 -04001413 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414}
1415
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416/* video bus */
Len Brown4be44fc2005-08-05 00:44:28 -04001417static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001419 struct acpi_video_bus *video = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421
1422 if (!video)
1423 goto end;
1424
1425 seq_printf(seq, "Switching heads: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001426 video->flags.multihead ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 seq_printf(seq, "Video ROM: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001428 video->flags.rom ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 seq_printf(seq, "Device to be POSTed on boot: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001430 video->flags.post ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431
Len Brown4be44fc2005-08-05 00:44:28 -04001432 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001433 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434}
1435
Len Brown4be44fc2005-08-05 00:44:28 -04001436static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437{
Len Brown4be44fc2005-08-05 00:44:28 -04001438 return single_open(file, acpi_video_bus_info_seq_show,
1439 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440}
1441
Len Brown4be44fc2005-08-05 00:44:28 -04001442static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001444 struct acpi_video_bus *video = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446
1447 if (!video)
1448 goto end;
1449
Harvey Harrison96b2dd12008-03-05 18:24:51 -08001450 printk(KERN_INFO PREFIX "Please implement %s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 seq_printf(seq, "<TODO>\n");
1452
Len Brown4be44fc2005-08-05 00:44:28 -04001453 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001454 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455}
1456
Len Brown4be44fc2005-08-05 00:44:28 -04001457static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458{
1459 return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data);
1460}
1461
Zhang Rui6e37c652010-07-15 10:46:43 +08001462static int
1463acpi_video_bus_POST_options(struct acpi_video_bus *video,
1464 unsigned long long *options)
1465{
1466 int status;
1467
1468 status = acpi_evaluate_integer(video->device->handle, "_VPO",
1469 NULL, options);
1470 *options &= 3;
1471
1472 return status;
1473}
1474
Len Brown4be44fc2005-08-05 00:44:28 -04001475static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001477 struct acpi_video_bus *video = seq->private;
Matthew Wilcox27663c52008-10-10 02:22:59 -04001478 unsigned long long options;
Len Brown4be44fc2005-08-05 00:44:28 -04001479 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481
1482 if (!video)
1483 goto end;
1484
1485 status = acpi_video_bus_POST_options(video, &options);
1486 if (ACPI_SUCCESS(status)) {
1487 if (!(options & 1)) {
Len Brown4be44fc2005-08-05 00:44:28 -04001488 printk(KERN_WARNING PREFIX
1489 "The motherboard VGA device is not listed as a possible POST device.\n");
1490 printk(KERN_WARNING PREFIX
Julius Volz98fb8fe2007-02-20 16:38:40 +01001491 "This indicates a BIOS bug. Please contact the manufacturer.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 }
Frank Seidel4d939152009-02-04 17:03:07 +01001493 printk(KERN_WARNING "%llx\n", options);
Julius Volz98fb8fe2007-02-20 16:38:40 +01001494 seq_printf(seq, "can POST: <integrated video>");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 if (options & 2)
1496 seq_printf(seq, " <PCI video>");
1497 if (options & 4)
1498 seq_printf(seq, " <AGP video>");
1499 seq_putc(seq, '\n');
1500 } else
1501 seq_printf(seq, "<not supported>\n");
Len Brown4be44fc2005-08-05 00:44:28 -04001502 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001503 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504}
1505
1506static int
Len Brown4be44fc2005-08-05 00:44:28 -04001507acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508{
Len Brown4be44fc2005-08-05 00:44:28 -04001509 return single_open(file, acpi_video_bus_POST_info_seq_show,
1510 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511}
1512
Zhang Rui6e37c652010-07-15 10:46:43 +08001513static int
1514acpi_video_bus_get_POST(struct acpi_video_bus *video, unsigned long long *id)
1515{
1516 int status;
1517
1518 status = acpi_evaluate_integer(video->device->handle, "_GPD", NULL, id);
1519
1520 return status;
1521}
1522
Len Brown4be44fc2005-08-05 00:44:28 -04001523static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001525 struct acpi_video_bus *video = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001526 int status;
Matthew Wilcox27663c52008-10-10 02:22:59 -04001527 unsigned long long id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
1530 if (!video)
1531 goto end;
1532
Len Brown4be44fc2005-08-05 00:44:28 -04001533 status = acpi_video_bus_get_POST(video, &id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 if (!ACPI_SUCCESS(status)) {
1535 seq_printf(seq, "<not supported>\n");
1536 goto end;
1537 }
Julius Volz98fb8fe2007-02-20 16:38:40 +01001538 seq_printf(seq, "device POSTed is <%s>\n", device_decode[id & 3]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539
Len Brown4be44fc2005-08-05 00:44:28 -04001540 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001541 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542}
1543
Len Brown4be44fc2005-08-05 00:44:28 -04001544static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001546 struct acpi_video_bus *video = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548
Len Brown4be44fc2005-08-05 00:44:28 -04001549 seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550
Patrick Mocheld550d982006-06-27 00:41:40 -04001551 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552}
1553
Len Brown4be44fc2005-08-05 00:44:28 -04001554static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555{
Len Brown4be44fc2005-08-05 00:44:28 -04001556 return single_open(file, acpi_video_bus_POST_seq_show,
1557 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558}
1559
Len Brown4be44fc2005-08-05 00:44:28 -04001560static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561{
1562 return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data);
1563}
1564
Zhang Rui6e37c652010-07-15 10:46:43 +08001565static int
1566acpi_video_bus_set_POST(struct acpi_video_bus *video, unsigned long option)
1567{
1568 int status;
1569 unsigned long long tmp;
1570 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
1571 struct acpi_object_list args = { 1, &arg0 };
1572
1573
1574 arg0.integer.value = option;
1575
1576 status = acpi_evaluate_integer(video->device->handle, "_SPD",
1577 &args, &tmp);
1578 if (ACPI_SUCCESS(status))
1579 status = tmp ? (-EINVAL) : (AE_OK);
1580
1581 return status;
1582}
1583
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001585acpi_video_bus_write_POST(struct file *file,
1586 const char __user * buffer,
1587 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588{
Len Brown4be44fc2005-08-05 00:44:28 -04001589 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001590 struct seq_file *m = file->private_data;
1591 struct acpi_video_bus *video = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001592 char str[12] = { 0 };
Matthew Wilcox27663c52008-10-10 02:22:59 -04001593 unsigned long long opt, options;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595
Arjan van de Ven52a2b112009-10-01 15:48:40 -07001596 if (!video || count >= sizeof(str))
Patrick Mocheld550d982006-06-27 00:41:40 -04001597 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
1599 status = acpi_video_bus_POST_options(video, &options);
1600 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001601 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
1603 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001604 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605
1606 str[count] = 0;
1607 opt = strtoul(str, NULL, 0);
1608 if (opt > 3)
Patrick Mocheld550d982006-06-27 00:41:40 -04001609 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610
Julius Volz98fb8fe2007-02-20 16:38:40 +01001611 /* just in case an OEM 'forgot' the motherboard... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 options |= 1;
1613
1614 if (options & (1ul << opt)) {
Len Brown4be44fc2005-08-05 00:44:28 -04001615 status = acpi_video_bus_set_POST(video, opt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001617 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618
1619 }
1620
Patrick Mocheld550d982006-06-27 00:41:40 -04001621 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622}
1623
1624static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001625acpi_video_bus_write_DOS(struct file *file,
1626 const char __user * buffer,
1627 size_t count, loff_t * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628{
Len Brown4be44fc2005-08-05 00:44:28 -04001629 int status;
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001630 struct seq_file *m = file->private_data;
1631 struct acpi_video_bus *video = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001632 char str[12] = { 0 };
1633 unsigned long opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635
Arjan van de Ven52a2b112009-10-01 15:48:40 -07001636 if (!video || count >= sizeof(str))
Patrick Mocheld550d982006-06-27 00:41:40 -04001637 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638
1639 if (copy_from_user(str, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001640 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
1642 str[count] = 0;
1643 opt = strtoul(str, NULL, 0);
1644 if (opt > 7)
Patrick Mocheld550d982006-06-27 00:41:40 -04001645 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646
Len Brown4be44fc2005-08-05 00:44:28 -04001647 status = acpi_video_bus_DOS(video, opt & 0x3, (opt & 0x4) >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
1649 if (!ACPI_SUCCESS(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001650 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
Patrick Mocheld550d982006-06-27 00:41:40 -04001652 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653}
1654
Len Brown4be44fc2005-08-05 00:44:28 -04001655static int acpi_video_bus_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656{
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001657 struct acpi_video_bus *video = acpi_driver_data(device);
1658 struct proc_dir_entry *device_dir;
1659 struct proc_dir_entry *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001661 device_dir = proc_mkdir(acpi_device_bid(device), acpi_video_dir);
1662 if (!device_dir)
1663 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 /* 'info' [R] */
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001666 entry = proc_create_data("info", S_IRUGO, device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001667 &acpi_video_bus_info_fops,
1668 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001670 goto err_remove_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671
1672 /* 'ROM' [R] */
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001673 entry = proc_create_data("ROM", S_IRUGO, device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001674 &acpi_video_bus_ROM_fops,
1675 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001677 goto err_remove_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678
1679 /* 'POST_info' [R] */
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001680 entry = proc_create_data("POST_info", S_IRUGO, device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001681 &acpi_video_bus_POST_info_fops,
1682 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001684 goto err_remove_rom;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685
1686 /* 'POST' [R/W] */
Linus Torvalds08acd4f82008-04-30 11:52:52 -07001687 entry = proc_create_data("POST", S_IFREG | S_IRUGO | S_IWUSR,
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001688 device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001689 &acpi_video_bus_POST_fops,
1690 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001692 goto err_remove_post_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693
1694 /* 'DOS' [R/W] */
Linus Torvalds08acd4f82008-04-30 11:52:52 -07001695 entry = proc_create_data("DOS", S_IFREG | S_IRUGO | S_IWUSR,
Alexey Dobriyane0066c4e2008-05-01 04:10:02 +04001696 device_dir,
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001697 &acpi_video_bus_DOS_fops,
1698 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 if (!entry)
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001700 goto err_remove_post;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001702 video->dir = acpi_device_dir(device) = device_dir;
Patrick Mocheld550d982006-06-27 00:41:40 -04001703 return 0;
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001704
1705 err_remove_post:
1706 remove_proc_entry("POST", device_dir);
1707 err_remove_post_info:
1708 remove_proc_entry("POST_info", device_dir);
1709 err_remove_rom:
1710 remove_proc_entry("ROM", device_dir);
1711 err_remove_info:
1712 remove_proc_entry("info", device_dir);
1713 err_remove_dir:
1714 remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
1715 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716}
1717
Len Brown4be44fc2005-08-05 00:44:28 -04001718static int acpi_video_bus_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719{
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001720 struct proc_dir_entry *device_dir = acpi_device_dir(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721
Dmitry Torokhov251cb0b2007-11-05 11:43:34 -05001722 if (device_dir) {
1723 remove_proc_entry("info", device_dir);
1724 remove_proc_entry("ROM", device_dir);
1725 remove_proc_entry("POST_info", device_dir);
1726 remove_proc_entry("POST", device_dir);
1727 remove_proc_entry("DOS", device_dir);
Len Brown4be44fc2005-08-05 00:44:28 -04001728 remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729 acpi_device_dir(device) = NULL;
1730 }
1731
Patrick Mocheld550d982006-06-27 00:41:40 -04001732 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733}
Zhang Rui6e37c652010-07-15 10:46:43 +08001734#else
1735static inline int acpi_video_device_add_fs(struct acpi_device *device)
1736{
1737 return 0;
1738}
1739static inline int acpi_video_device_remove_fs(struct acpi_device *device)
1740{
1741 return 0;
1742}
1743static inline int acpi_video_bus_add_fs(struct acpi_device *device)
1744{
1745 return 0;
1746}
1747static inline int acpi_video_bus_remove_fs(struct acpi_device *device)
1748{
1749 return 0;
1750}
1751#endif /* CONFIG_ACPI_PROCFS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752
1753/* --------------------------------------------------------------------------
1754 Driver Interface
1755 -------------------------------------------------------------------------- */
1756
1757/* device interface */
Rui Zhang82cae992007-01-03 23:40:53 -05001758static struct acpi_video_device_attrib*
1759acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id)
1760{
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001761 struct acpi_video_enumerated_device *ids;
1762 int i;
Rui Zhang82cae992007-01-03 23:40:53 -05001763
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001764 for (i = 0; i < video->attached_count; i++) {
1765 ids = &video->attached_array[i];
1766 if ((ids->value.int_val & 0xffff) == device_id)
1767 return &ids->value.attrib;
1768 }
1769
Rui Zhang82cae992007-01-03 23:40:53 -05001770 return NULL;
1771}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772
1773static int
Matthew Garrette92a7162010-01-12 14:17:03 -05001774acpi_video_get_device_type(struct acpi_video_bus *video,
1775 unsigned long device_id)
1776{
1777 struct acpi_video_enumerated_device *ids;
1778 int i;
1779
1780 for (i = 0; i < video->attached_count; i++) {
1781 ids = &video->attached_array[i];
1782 if ((ids->value.int_val & 0xffff) == device_id)
1783 return ids->value.int_val;
1784 }
1785
1786 return 0;
1787}
1788
1789static int
Len Brown4be44fc2005-08-05 00:44:28 -04001790acpi_video_bus_get_one_device(struct acpi_device *device,
1791 struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792{
Matthew Wilcox27663c52008-10-10 02:22:59 -04001793 unsigned long long device_id;
Matthew Garrette92a7162010-01-12 14:17:03 -05001794 int status, device_type;
Len Brown4be44fc2005-08-05 00:44:28 -04001795 struct acpi_video_device *data;
Rui Zhang82cae992007-01-03 23:40:53 -05001796 struct acpi_video_device_attrib* attribute;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797
1798 if (!device || !video)
Patrick Mocheld550d982006-06-27 00:41:40 -04001799 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800
Len Brown4be44fc2005-08-05 00:44:28 -04001801 status =
1802 acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803 if (ACPI_SUCCESS(status)) {
1804
Burman Yan36bcbec2006-12-19 12:56:11 -08001805 data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 if (!data)
Patrick Mocheld550d982006-06-27 00:41:40 -04001807 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
1810 strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -07001811 device->driver_data = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812
1813 data->device_id = device_id;
1814 data->video = video;
1815 data->dev = device;
1816
Rui Zhang82cae992007-01-03 23:40:53 -05001817 attribute = acpi_video_get_device_attr(video, device_id);
1818
1819 if((attribute != NULL) && attribute->device_id_scheme) {
1820 switch (attribute->display_type) {
1821 case ACPI_VIDEO_DISPLAY_CRT:
1822 data->flags.crt = 1;
1823 break;
1824 case ACPI_VIDEO_DISPLAY_TV:
1825 data->flags.tvout = 1;
1826 break;
1827 case ACPI_VIDEO_DISPLAY_DVI:
1828 data->flags.dvi = 1;
1829 break;
1830 case ACPI_VIDEO_DISPLAY_LCD:
1831 data->flags.lcd = 1;
1832 break;
1833 default:
1834 data->flags.unknown = 1;
1835 break;
1836 }
1837 if(attribute->bios_can_detect)
1838 data->flags.bios = 1;
Matthew Garrette92a7162010-01-12 14:17:03 -05001839 } else {
1840 /* Check for legacy IDs */
1841 device_type = acpi_video_get_device_type(video,
1842 device_id);
1843 /* Ignore bits 16 and 18-20 */
1844 switch (device_type & 0xffe2ffff) {
1845 case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR:
1846 data->flags.crt = 1;
1847 break;
1848 case ACPI_VIDEO_DISPLAY_LEGACY_PANEL:
1849 data->flags.lcd = 1;
1850 break;
1851 case ACPI_VIDEO_DISPLAY_LEGACY_TV:
1852 data->flags.tvout = 1;
1853 break;
1854 default:
1855 data->flags.unknown = 1;
1856 }
1857 }
Len Brown4be44fc2005-08-05 00:44:28 -04001858
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859 acpi_video_device_bind(video, data);
1860 acpi_video_device_find_cap(data);
1861
Patrick Mochel90130262006-05-19 16:54:48 -04001862 status = acpi_install_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001863 ACPI_DEVICE_NOTIFY,
1864 acpi_video_device_notify,
1865 data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866 if (ACPI_FAILURE(status)) {
Lin Ming55ac9a02008-09-28 14:51:56 +08001867 printk(KERN_ERR PREFIX
1868 "Error installing notify handler\n");
Yu, Luming973bf492006-04-27 05:25:00 -04001869 if(data->brightness)
1870 kfree(data->brightness->levels);
1871 kfree(data->brightness);
1872 kfree(data);
1873 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 }
1875
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05001876 mutex_lock(&video->device_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 list_add_tail(&data->entry, &video->video_device_list);
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05001878 mutex_unlock(&video->device_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879
1880 acpi_video_device_add_fs(device);
1881
Patrick Mocheld550d982006-06-27 00:41:40 -04001882 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883 }
1884
Patrick Mocheld550d982006-06-27 00:41:40 -04001885 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886}
1887
1888/*
1889 * Arg:
1890 * video : video bus device
1891 *
1892 * Return:
1893 * none
1894 *
1895 * Enumerate the video device list of the video bus,
1896 * bind the ids with the corresponding video devices
1897 * under the video bus.
Len Brown4be44fc2005-08-05 00:44:28 -04001898 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899
Len Brown4be44fc2005-08-05 00:44:28 -04001900static void acpi_video_device_rebind(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901{
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001902 struct acpi_video_device *dev;
1903
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05001904 mutex_lock(&video->device_list_lock);
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001905
1906 list_for_each_entry(dev, &video->video_device_list, entry)
Len Brown4be44fc2005-08-05 00:44:28 -04001907 acpi_video_device_bind(video, dev);
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05001908
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05001909 mutex_unlock(&video->device_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910}
1911
1912/*
1913 * Arg:
1914 * video : video bus device
1915 * device : video output device under the video
1916 * bus
1917 *
1918 * Return:
1919 * none
1920 *
1921 * Bind the ids with the corresponding video devices
1922 * under the video bus.
Len Brown4be44fc2005-08-05 00:44:28 -04001923 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924
1925static void
Len Brown4be44fc2005-08-05 00:44:28 -04001926acpi_video_device_bind(struct acpi_video_bus *video,
1927 struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928{
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001929 struct acpi_video_enumerated_device *ids;
Len Brown4be44fc2005-08-05 00:44:28 -04001930 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001932 for (i = 0; i < video->attached_count; i++) {
1933 ids = &video->attached_array[i];
1934 if (device->device_id == (ids->value.int_val & 0xffff)) {
1935 ids->bind_info = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i));
1937 }
1938 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939}
1940
1941/*
1942 * Arg:
1943 * video : video bus device
1944 *
1945 * Return:
1946 * < 0 : error
1947 *
1948 * Call _DOD to enumerate all devices attached to display adapter
1949 *
Len Brown4be44fc2005-08-05 00:44:28 -04001950 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951
1952static int acpi_video_device_enumerate(struct acpi_video_bus *video)
1953{
Len Brown4be44fc2005-08-05 00:44:28 -04001954 int status;
1955 int count;
1956 int i;
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001957 struct acpi_video_enumerated_device *active_list;
Len Brown4be44fc2005-08-05 00:44:28 -04001958 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1959 union acpi_object *dod = NULL;
1960 union acpi_object *obj;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961
Patrick Mochel90130262006-05-19 16:54:48 -04001962 status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963 if (!ACPI_SUCCESS(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001964 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD"));
Patrick Mocheld550d982006-06-27 00:41:40 -04001965 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966 }
1967
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001968 dod = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969 if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001970 ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971 status = -EFAULT;
1972 goto out;
1973 }
1974
1975 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001976 dod->package.count));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001978 active_list = kcalloc(1 + dod->package.count,
1979 sizeof(struct acpi_video_enumerated_device),
1980 GFP_KERNEL);
1981 if (!active_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 status = -ENOMEM;
1983 goto out;
1984 }
1985
1986 count = 0;
1987 for (i = 0; i < dod->package.count; i++) {
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001988 obj = &dod->package.elements[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989
1990 if (obj->type != ACPI_TYPE_INTEGER) {
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001991 printk(KERN_ERR PREFIX
1992 "Invalid _DOD data in element %d\n", i);
1993 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 }
Dmitry Torokhov78eed022007-11-05 11:43:33 -05001995
1996 active_list[count].value.int_val = obj->integer.value;
1997 active_list[count].bind_info = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -04001998 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i,
1999 (int)obj->integer.value));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 count++;
2001 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002
Jesper Juhl6044ec82005-11-07 01:01:32 -08002003 kfree(video->attached_array);
Len Brown4be44fc2005-08-05 00:44:28 -04002004
Dmitry Torokhov78eed022007-11-05 11:43:33 -05002005 video->attached_array = active_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 video->attached_count = count;
Dmitry Torokhov78eed022007-11-05 11:43:33 -05002007
2008 out:
Len Brown02438d82006-06-30 03:19:10 -04002009 kfree(buffer.pointer);
Patrick Mocheld550d982006-06-27 00:41:40 -04002010 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011}
2012
Len Brown4be44fc2005-08-05 00:44:28 -04002013static int
2014acpi_video_get_next_level(struct acpi_video_device *device,
2015 u32 level_current, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016{
Alexey Starikovskiy63f0edf2007-09-03 16:30:08 +04002017 int min, max, min_above, max_below, i, l, delta = 255;
Thomas Tuttlef4715182006-12-19 12:56:14 -08002018 max = max_below = 0;
2019 min = min_above = 255;
Alexey Starikovskiy63f0edf2007-09-03 16:30:08 +04002020 /* Find closest level to level_current */
Zhao Yakui0a3db1c2009-02-02 11:33:41 +08002021 for (i = 2; i < device->brightness->count; i++) {
Alexey Starikovskiy63f0edf2007-09-03 16:30:08 +04002022 l = device->brightness->levels[i];
2023 if (abs(l - level_current) < abs(delta)) {
2024 delta = l - level_current;
2025 if (!delta)
2026 break;
2027 }
2028 }
2029 /* Ajust level_current to closest available level */
2030 level_current += delta;
Zhao Yakui0a3db1c2009-02-02 11:33:41 +08002031 for (i = 2; i < device->brightness->count; i++) {
Thomas Tuttlef4715182006-12-19 12:56:14 -08002032 l = device->brightness->levels[i];
2033 if (l < min)
2034 min = l;
2035 if (l > max)
2036 max = l;
2037 if (l < min_above && l > level_current)
2038 min_above = l;
2039 if (l > max_below && l < level_current)
2040 max_below = l;
2041 }
2042
2043 switch (event) {
2044 case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:
2045 return (level_current < max) ? min_above : min;
2046 case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:
2047 return (level_current < max) ? min_above : max;
2048 case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:
2049 return (level_current > min) ? max_below : min;
2050 case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:
2051 case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:
2052 return 0;
2053 default:
2054 return level_current;
2055 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056}
2057
Zhang Ruic8890f92009-03-18 16:27:08 +08002058static int
Len Brown4be44fc2005-08-05 00:44:28 -04002059acpi_video_switch_brightness(struct acpi_video_device *device, int event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060{
Matthew Wilcox27663c52008-10-10 02:22:59 -04002061 unsigned long long level_current, level_next;
Zhang Ruic8890f92009-03-18 16:27:08 +08002062 int result = -EINVAL;
2063
Zhang Rui28c32e92009-07-13 10:33:24 +08002064 /* no warning message if acpi_backlight=vendor is used */
2065 if (!acpi_video_backlight_support())
2066 return 0;
2067
Julia Jomantaite469778c2008-06-23 22:50:42 +01002068 if (!device->brightness)
Zhang Ruic8890f92009-03-18 16:27:08 +08002069 goto out;
2070
2071 result = acpi_video_device_lcd_get_level_current(device,
Matthew Garrett70287db2010-02-16 16:53:50 -05002072 &level_current, 0);
Zhang Ruic8890f92009-03-18 16:27:08 +08002073 if (result)
2074 goto out;
2075
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 level_next = acpi_video_get_next_level(device, level_current, event);
Zhang Ruic8890f92009-03-18 16:27:08 +08002077
Zhang Rui24450c72009-03-18 16:27:10 +08002078 result = acpi_video_device_lcd_set_level(device, level_next);
Zhang Ruic8890f92009-03-18 16:27:08 +08002079
Matthew Garrett36342742009-07-14 17:06:03 +01002080 if (!result)
2081 backlight_force_update(device->backlight,
2082 BACKLIGHT_UPDATE_HOTKEY);
2083
Zhang Ruic8890f92009-03-18 16:27:08 +08002084out:
2085 if (result)
2086 printk(KERN_ERR PREFIX "Failed to switch the brightness\n");
2087
2088 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089}
2090
Matthew Garrette92a7162010-01-12 14:17:03 -05002091int acpi_video_get_edid(struct acpi_device *device, int type, int device_id,
2092 void **edid)
2093{
2094 struct acpi_video_bus *video;
2095 struct acpi_video_device *video_device;
2096 union acpi_object *buffer = NULL;
2097 acpi_status status;
2098 int i, length;
2099
2100 if (!device || !acpi_driver_data(device))
2101 return -EINVAL;
2102
2103 video = acpi_driver_data(device);
2104
2105 for (i = 0; i < video->attached_count; i++) {
2106 video_device = video->attached_array[i].bind_info;
2107 length = 256;
2108
2109 if (!video_device)
2110 continue;
2111
2112 if (type) {
2113 switch (type) {
2114 case ACPI_VIDEO_DISPLAY_CRT:
2115 if (!video_device->flags.crt)
2116 continue;
2117 break;
2118 case ACPI_VIDEO_DISPLAY_TV:
2119 if (!video_device->flags.tvout)
2120 continue;
2121 break;
2122 case ACPI_VIDEO_DISPLAY_DVI:
2123 if (!video_device->flags.dvi)
2124 continue;
2125 break;
2126 case ACPI_VIDEO_DISPLAY_LCD:
2127 if (!video_device->flags.lcd)
2128 continue;
2129 break;
2130 }
2131 } else if (video_device->device_id != device_id) {
2132 continue;
2133 }
2134
2135 status = acpi_video_device_EDID(video_device, &buffer, length);
2136
2137 if (ACPI_FAILURE(status) || !buffer ||
2138 buffer->type != ACPI_TYPE_BUFFER) {
2139 length = 128;
2140 status = acpi_video_device_EDID(video_device, &buffer,
2141 length);
2142 if (ACPI_FAILURE(status) || !buffer ||
2143 buffer->type != ACPI_TYPE_BUFFER) {
2144 continue;
2145 }
2146 }
2147
2148 *edid = buffer->buffer.pointer;
2149 return length;
2150 }
2151
2152 return -ENODEV;
2153}
2154EXPORT_SYMBOL(acpi_video_get_edid);
2155
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156static int
Len Brown4be44fc2005-08-05 00:44:28 -04002157acpi_video_bus_get_devices(struct acpi_video_bus *video,
2158 struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159{
Len Brown4be44fc2005-08-05 00:44:28 -04002160 int status = 0;
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05002161 struct acpi_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162
2163 acpi_video_device_enumerate(video);
2164
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05002165 list_for_each_entry(dev, &device->children, node) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166
2167 status = acpi_video_bus_get_one_device(dev, video);
2168 if (ACPI_FAILURE(status)) {
Lin Ming55ac9a02008-09-28 14:51:56 +08002169 printk(KERN_WARNING PREFIX
Andi Kleencfa806f2010-07-20 15:18:36 -07002170 "Cant attach device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 continue;
2172 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 }
Patrick Mocheld550d982006-06-27 00:41:40 -04002174 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175}
2176
Len Brown4be44fc2005-08-05 00:44:28 -04002177static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178{
Karol Kozimor031ec772005-07-30 04:18:00 -04002179 acpi_status status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180
2181 if (!device || !device->video)
Patrick Mocheld550d982006-06-27 00:41:40 -04002182 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184 acpi_video_device_remove_fs(device->dev);
2185
Patrick Mochel90130262006-05-19 16:54:48 -04002186 status = acpi_remove_notify_handler(device->dev->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04002187 ACPI_DEVICE_NOTIFY,
2188 acpi_video_device_notify);
Andi Kleencfa806f2010-07-20 15:18:36 -07002189 if (ACPI_FAILURE(status)) {
2190 printk(KERN_WARNING PREFIX
2191 "Cant remove video notify handler\n");
2192 }
Keith Packarde29b3ee2009-08-06 15:57:54 -07002193 if (device->backlight) {
2194 sysfs_remove_link(&device->backlight->dev.kobj, "device");
2195 backlight_device_unregister(device->backlight);
2196 device->backlight = NULL;
2197 }
Dmitry Torokhov4a703a82009-08-29 23:03:16 -04002198 if (device->cooling_dev) {
Zhang Rui702ed512008-01-17 15:51:22 +08002199 sysfs_remove_link(&device->dev->dev.kobj,
2200 "thermal_cooling");
Dmitry Torokhov4a703a82009-08-29 23:03:16 -04002201 sysfs_remove_link(&device->cooling_dev->device.kobj,
Zhang Rui702ed512008-01-17 15:51:22 +08002202 "device");
Dmitry Torokhov4a703a82009-08-29 23:03:16 -04002203 thermal_cooling_device_unregister(device->cooling_dev);
2204 device->cooling_dev = NULL;
Zhang Rui702ed512008-01-17 15:51:22 +08002205 }
Luming Yu23b0f012007-05-09 21:07:05 +08002206 video_output_unregister(device->output_dev);
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05002207
Patrick Mocheld550d982006-06-27 00:41:40 -04002208 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209}
2210
Len Brown4be44fc2005-08-05 00:44:28 -04002211static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212{
Len Brown4be44fc2005-08-05 00:44:28 -04002213 int status;
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05002214 struct acpi_video_device *dev, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05002216 mutex_lock(&video->device_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05002218 list_for_each_entry_safe(dev, next, &video->video_device_list, entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05002220 status = acpi_video_bus_put_one_device(dev);
Len Brown4be44fc2005-08-05 00:44:28 -04002221 if (ACPI_FAILURE(status))
2222 printk(KERN_WARNING PREFIX
2223 "hhuuhhuu bug in acpi video driver.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05002225 if (dev->brightness) {
2226 kfree(dev->brightness->levels);
2227 kfree(dev->brightness);
2228 }
2229 list_del(&dev->entry);
2230 kfree(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231 }
2232
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05002233 mutex_unlock(&video->device_list_lock);
Dmitry Torokhovff102ea2007-11-05 11:43:31 -05002234
Patrick Mocheld550d982006-06-27 00:41:40 -04002235 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236}
2237
2238/* acpi_video interface */
2239
Len Brown4be44fc2005-08-05 00:44:28 -04002240static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241{
Zhang Ruia21101c2007-09-14 11:46:22 +08002242 return acpi_video_bus_DOS(video, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243}
2244
Len Brown4be44fc2005-08-05 00:44:28 -04002245static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246{
2247 return acpi_video_bus_DOS(video, 0, 1);
2248}
2249
Bjorn Helgaas70155582009-04-07 15:37:11 +00002250static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251{
Bjorn Helgaas70155582009-04-07 15:37:11 +00002252 struct acpi_video_bus *video = acpi_driver_data(device);
Luming Yue9dab192007-08-20 18:23:53 +08002253 struct input_dev *input;
Matthew Garrett17c452f2009-12-11 17:40:46 -05002254 int keycode = 0;
Luming Yue9dab192007-08-20 18:23:53 +08002255
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -04002257 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258
Luming Yue9dab192007-08-20 18:23:53 +08002259 input = video->input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260
2261 switch (event) {
Julius Volz98fb8fe2007-02-20 16:38:40 +01002262 case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 * most likely via hotkey. */
Len Brown14e04fb2007-08-23 15:20:26 -04002264 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08002265 keycode = KEY_SWITCHVIDEOMODE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 break;
2267
Julius Volz98fb8fe2007-02-20 16:38:40 +01002268 case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 * connector. */
2270 acpi_video_device_enumerate(video);
2271 acpi_video_device_rebind(video);
Len Brown14e04fb2007-08-23 15:20:26 -04002272 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08002273 keycode = KEY_SWITCHVIDEOMODE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 break;
2275
Len Brown4be44fc2005-08-05 00:44:28 -04002276 case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */
Len Brown25c87f72007-08-25 01:44:01 -04002277 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08002278 keycode = KEY_SWITCHVIDEOMODE;
2279 break;
Len Brown4be44fc2005-08-05 00:44:28 -04002280 case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */
Len Brown25c87f72007-08-25 01:44:01 -04002281 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08002282 keycode = KEY_VIDEO_NEXT;
2283 break;
Len Brown4be44fc2005-08-05 00:44:28 -04002284 case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */
Len Brown14e04fb2007-08-23 15:20:26 -04002285 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08002286 keycode = KEY_VIDEO_PREV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287 break;
2288
2289 default:
2290 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -04002291 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292 break;
2293 }
2294
Zhang Rui7761f632008-01-25 14:48:12 +08002295 acpi_notifier_call_chain(device, event, 0);
Matthew Garrett17c452f2009-12-11 17:40:46 -05002296
2297 if (keycode) {
2298 input_report_key(input, keycode, 1);
2299 input_sync(input);
2300 input_report_key(input, keycode, 0);
2301 input_sync(input);
2302 }
Luming Yue9dab192007-08-20 18:23:53 +08002303
Patrick Mocheld550d982006-06-27 00:41:40 -04002304 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305}
2306
Len Brown4be44fc2005-08-05 00:44:28 -04002307static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02002309 struct acpi_video_device *video_device = data;
Len Brown4be44fc2005-08-05 00:44:28 -04002310 struct acpi_device *device = NULL;
Luming Yue9dab192007-08-20 18:23:53 +08002311 struct acpi_video_bus *bus;
2312 struct input_dev *input;
Matthew Garrett17c452f2009-12-11 17:40:46 -05002313 int keycode = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002314
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315 if (!video_device)
Patrick Mocheld550d982006-06-27 00:41:40 -04002316 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317
Patrick Mochele6afa0d2006-05-19 16:54:40 -04002318 device = video_device->dev;
Luming Yue9dab192007-08-20 18:23:53 +08002319 bus = video_device->video;
2320 input = bus->input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321
2322 switch (event) {
Len Brown4be44fc2005-08-05 00:44:28 -04002323 case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */
Zhang Rui8a681a42008-01-25 14:47:49 +08002324 if (brightness_switch_enabled)
2325 acpi_video_switch_brightness(video_device, event);
Len Brown25c87f72007-08-25 01:44:01 -04002326 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08002327 keycode = KEY_BRIGHTNESS_CYCLE;
2328 break;
Len Brown4be44fc2005-08-05 00:44:28 -04002329 case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */
Zhang Rui8a681a42008-01-25 14:47:49 +08002330 if (brightness_switch_enabled)
2331 acpi_video_switch_brightness(video_device, event);
Len Brown25c87f72007-08-25 01:44:01 -04002332 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08002333 keycode = KEY_BRIGHTNESSUP;
2334 break;
Len Brown4be44fc2005-08-05 00:44:28 -04002335 case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */
Zhang Rui8a681a42008-01-25 14:47:49 +08002336 if (brightness_switch_enabled)
2337 acpi_video_switch_brightness(video_device, event);
Len Brown25c87f72007-08-25 01:44:01 -04002338 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08002339 keycode = KEY_BRIGHTNESSDOWN;
2340 break;
Len Brown4be44fc2005-08-05 00:44:28 -04002341 case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightnesss */
Zhang Rui8a681a42008-01-25 14:47:49 +08002342 if (brightness_switch_enabled)
2343 acpi_video_switch_brightness(video_device, event);
Len Brown25c87f72007-08-25 01:44:01 -04002344 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08002345 keycode = KEY_BRIGHTNESS_ZERO;
2346 break;
Len Brown4be44fc2005-08-05 00:44:28 -04002347 case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */
Zhang Rui8a681a42008-01-25 14:47:49 +08002348 if (brightness_switch_enabled)
2349 acpi_video_switch_brightness(video_device, event);
Len Brown14e04fb2007-08-23 15:20:26 -04002350 acpi_bus_generate_proc_event(device, event, 0);
Luming Yue9dab192007-08-20 18:23:53 +08002351 keycode = KEY_DISPLAY_OFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002352 break;
2353 default:
2354 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -04002355 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356 break;
2357 }
Luming Yue9dab192007-08-20 18:23:53 +08002358
Zhang Rui7761f632008-01-25 14:48:12 +08002359 acpi_notifier_call_chain(device, event, 0);
Matthew Garrett17c452f2009-12-11 17:40:46 -05002360
2361 if (keycode) {
2362 input_report_key(input, keycode, 1);
2363 input_sync(input);
2364 input_report_key(input, keycode, 0);
2365 input_sync(input);
2366 }
Luming Yue9dab192007-08-20 18:23:53 +08002367
Patrick Mocheld550d982006-06-27 00:41:40 -04002368 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369}
2370
Rafael J. Wysockiac7729d2010-04-05 01:43:51 +02002371static int acpi_video_resume(struct notifier_block *nb,
2372 unsigned long val, void *ign)
Matthew Garrett863c1492008-02-04 23:31:24 -08002373{
2374 struct acpi_video_bus *video;
2375 struct acpi_video_device *video_device;
2376 int i;
2377
Rafael J. Wysockiac7729d2010-04-05 01:43:51 +02002378 switch (val) {
2379 case PM_HIBERNATION_PREPARE:
2380 case PM_SUSPEND_PREPARE:
2381 case PM_RESTORE_PREPARE:
2382 return NOTIFY_DONE;
2383 }
Matthew Garrett863c1492008-02-04 23:31:24 -08002384
Rafael J. Wysockiac7729d2010-04-05 01:43:51 +02002385 video = container_of(nb, struct acpi_video_bus, pm_nb);
2386
2387 dev_info(&video->device->dev, "Restoring backlight state\n");
Matthew Garrett863c1492008-02-04 23:31:24 -08002388
2389 for (i = 0; i < video->attached_count; i++) {
2390 video_device = video->attached_array[i].bind_info;
2391 if (video_device && video_device->backlight)
2392 acpi_video_set_brightness(video_device->backlight);
2393 }
Rafael J. Wysockiac7729d2010-04-05 01:43:51 +02002394
2395 return NOTIFY_OK;
Matthew Garrett863c1492008-02-04 23:31:24 -08002396}
2397
Zhang Ruic504f8c2009-12-30 15:59:23 +08002398static acpi_status
2399acpi_video_bus_match(acpi_handle handle, u32 level, void *context,
2400 void **return_value)
2401{
2402 struct acpi_device *device = context;
2403 struct acpi_device *sibling;
2404 int result;
2405
2406 if (handle == device->handle)
2407 return AE_CTRL_TERMINATE;
2408
2409 result = acpi_bus_get_device(handle, &sibling);
2410 if (result)
2411 return AE_OK;
2412
2413 if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME))
2414 return AE_ALREADY_EXISTS;
2415
2416 return AE_OK;
2417}
2418
Rafael J. Wysockiac7729d2010-04-05 01:43:51 +02002419static int instance;
2420
Len Brown4be44fc2005-08-05 00:44:28 -04002421static int acpi_video_bus_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422{
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002423 struct acpi_video_bus *video;
Luming Yue9dab192007-08-20 18:23:53 +08002424 struct input_dev *input;
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002425 int error;
Zhang Ruic504f8c2009-12-30 15:59:23 +08002426 acpi_status status;
2427
2428 status = acpi_walk_namespace(ACPI_TYPE_DEVICE,
2429 device->parent->handle, 1,
2430 acpi_video_bus_match, NULL,
2431 device, NULL);
2432 if (status == AE_ALREADY_EXISTS) {
2433 printk(KERN_WARNING FW_BUG
2434 "Duplicate ACPI video bus devices for the"
2435 " same VGA controller, please try module "
2436 "parameter \"video.allow_duplicates=1\""
2437 "if the current driver doesn't work.\n");
2438 if (!allow_duplicates)
2439 return -ENODEV;
2440 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441
Burman Yan36bcbec2006-12-19 12:56:11 -08002442 video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443 if (!video)
Patrick Mocheld550d982006-06-27 00:41:40 -04002444 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445
Zhang Ruie6d9da12007-08-25 02:23:31 -04002446 /* a hack to fix the duplicate name "VID" problem on T61 */
2447 if (!strcmp(device->pnp.bus_id, "VID")) {
2448 if (instance)
2449 device->pnp.bus_id[3] = '0' + instance;
2450 instance ++;
2451 }
Zhao Yakuif3b39f12009-02-02 22:55:01 -05002452 /* a hack to fix the duplicate name "VGA" problem on Pa 3553 */
2453 if (!strcmp(device->pnp.bus_id, "VGA")) {
2454 if (instance)
2455 device->pnp.bus_id[3] = '0' + instance;
2456 instance++;
2457 }
Zhang Ruie6d9da12007-08-25 02:23:31 -04002458
Patrick Mochele6afa0d2006-05-19 16:54:40 -04002459 video->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460 strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
2461 strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -07002462 device->driver_data = video;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463
2464 acpi_video_bus_find_cap(video);
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002465 error = acpi_video_bus_check(video);
2466 if (error)
2467 goto err_free_video;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002469 error = acpi_video_bus_add_fs(device);
2470 if (error)
2471 goto err_free_video;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472
Dmitry Torokhovbbac81f2007-11-05 11:43:32 -05002473 mutex_init(&video->device_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474 INIT_LIST_HEAD(&video->video_device_list);
2475
2476 acpi_video_bus_get_devices(video, device);
2477 acpi_video_bus_start_devices(video);
2478
Luming Yue9dab192007-08-20 18:23:53 +08002479 video->input = input = input_allocate_device();
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002480 if (!input) {
2481 error = -ENOMEM;
Bjorn Helgaas70155582009-04-07 15:37:11 +00002482 goto err_stop_video;
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002483 }
Luming Yue9dab192007-08-20 18:23:53 +08002484
2485 snprintf(video->phys, sizeof(video->phys),
2486 "%s/video/input0", acpi_device_hid(video->device));
2487
2488 input->name = acpi_device_name(video->device);
2489 input->phys = video->phys;
2490 input->id.bustype = BUS_HOST;
2491 input->id.product = 0x06;
Dmitry Torokhov91c05c62007-11-05 11:43:29 -05002492 input->dev.parent = &device->dev;
Luming Yue9dab192007-08-20 18:23:53 +08002493 input->evbit[0] = BIT(EV_KEY);
2494 set_bit(KEY_SWITCHVIDEOMODE, input->keybit);
2495 set_bit(KEY_VIDEO_NEXT, input->keybit);
2496 set_bit(KEY_VIDEO_PREV, input->keybit);
2497 set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit);
2498 set_bit(KEY_BRIGHTNESSUP, input->keybit);
2499 set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
2500 set_bit(KEY_BRIGHTNESS_ZERO, input->keybit);
2501 set_bit(KEY_DISPLAY_OFF, input->keybit);
Luming Yue9dab192007-08-20 18:23:53 +08002502
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002503 error = input_register_device(input);
2504 if (error)
2505 goto err_free_input_dev;
Luming Yue9dab192007-08-20 18:23:53 +08002506
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n",
Len Brown4be44fc2005-08-05 00:44:28 -04002508 ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
2509 video->flags.multihead ? "yes" : "no",
2510 video->flags.rom ? "yes" : "no",
2511 video->flags.post ? "yes" : "no");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512
Rafael J. Wysockiac7729d2010-04-05 01:43:51 +02002513 video->pm_nb.notifier_call = acpi_video_resume;
2514 video->pm_nb.priority = 0;
2515 register_pm_notifier(&video->pm_nb);
2516
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002517 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002519 err_free_input_dev:
2520 input_free_device(input);
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002521 err_stop_video:
2522 acpi_video_bus_stop_devices(video);
2523 acpi_video_bus_put_devices(video);
2524 kfree(video->attached_array);
2525 acpi_video_bus_remove_fs(device);
2526 err_free_video:
2527 kfree(video);
Pavel Machekdb89b4f2008-09-22 14:37:34 -07002528 device->driver_data = NULL;
Dmitry Torokhovf51e8392007-11-05 11:43:30 -05002529
2530 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531}
2532
Len Brown4be44fc2005-08-05 00:44:28 -04002533static int acpi_video_bus_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534{
Len Brown4be44fc2005-08-05 00:44:28 -04002535 struct acpi_video_bus *video = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537
2538 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04002539 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002540
Jan Engelhardt50dd0962006-10-01 00:28:50 +02002541 video = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542
Rafael J. Wysockiac7729d2010-04-05 01:43:51 +02002543 unregister_pm_notifier(&video->pm_nb);
2544
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545 acpi_video_bus_stop_devices(video);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 acpi_video_bus_put_devices(video);
2547 acpi_video_bus_remove_fs(device);
2548
Luming Yue9dab192007-08-20 18:23:53 +08002549 input_unregister_device(video->input);
Jesper Juhl6044ec82005-11-07 01:01:32 -08002550 kfree(video->attached_array);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551 kfree(video);
2552
Patrick Mocheld550d982006-06-27 00:41:40 -04002553 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554}
2555
Matthew Garrett74a365b2009-03-19 21:35:39 +00002556static int __init intel_opregion_present(void)
2557{
2558#if defined(CONFIG_DRM_I915) || defined(CONFIG_DRM_I915_MODULE)
2559 struct pci_dev *dev = NULL;
2560 u32 address;
2561
2562 for_each_pci_dev(dev) {
2563 if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
2564 continue;
2565 if (dev->vendor != PCI_VENDOR_ID_INTEL)
2566 continue;
2567 pci_read_config_dword(dev, 0xfc, &address);
2568 if (!address)
2569 continue;
2570 return 1;
2571 }
2572#endif
2573 return 0;
2574}
2575
2576int acpi_video_register(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577{
Len Brown4be44fc2005-08-05 00:44:28 -04002578 int result = 0;
Zhao Yakui86e437f2009-06-16 11:23:13 +08002579 if (register_count) {
2580 /*
2581 * if the function of acpi_video_register is already called,
2582 * don't register the acpi_vide_bus again and return no error.
2583 */
2584 return 0;
2585 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586
Zhang Rui6e37c652010-07-15 10:46:43 +08002587#ifdef CONFIG_ACPI_PROCFS
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
2589 if (!acpi_video_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04002590 return -ENODEV;
Zhang Rui6e37c652010-07-15 10:46:43 +08002591#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592
2593 result = acpi_bus_register_driver(&acpi_video_bus);
2594 if (result < 0) {
2595 remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -04002596 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597 }
2598
Zhao Yakui86e437f2009-06-16 11:23:13 +08002599 /*
2600 * When the acpi_video_bus is loaded successfully, increase
2601 * the counter reference.
2602 */
2603 register_count = 1;
2604
Patrick Mocheld550d982006-06-27 00:41:40 -04002605 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606}
Matthew Garrett74a365b2009-03-19 21:35:39 +00002607EXPORT_SYMBOL(acpi_video_register);
2608
Zhao Yakui86e437f2009-06-16 11:23:13 +08002609void acpi_video_unregister(void)
2610{
2611 if (!register_count) {
2612 /*
2613 * If the acpi video bus is already unloaded, don't
2614 * unload it again and return directly.
2615 */
2616 return;
2617 }
2618 acpi_bus_unregister_driver(&acpi_video_bus);
2619
Zhang Rui6e37c652010-07-15 10:46:43 +08002620#ifdef CONFIG_ACPI_PROCFS
Zhao Yakui86e437f2009-06-16 11:23:13 +08002621 remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
Zhang Rui6e37c652010-07-15 10:46:43 +08002622#endif
Zhao Yakui86e437f2009-06-16 11:23:13 +08002623
2624 register_count = 0;
2625
2626 return;
2627}
2628EXPORT_SYMBOL(acpi_video_unregister);
2629
Matthew Garrett74a365b2009-03-19 21:35:39 +00002630/*
2631 * This is kind of nasty. Hardware using Intel chipsets may require
2632 * the video opregion code to be run first in order to initialise
2633 * state before any ACPI video calls are made. To handle this we defer
2634 * registration of the video class until the opregion code has run.
2635 */
2636
2637static int __init acpi_video_init(void)
2638{
Zhang Rui45cb50e2009-04-24 12:13:18 -04002639 dmi_check_system(video_dmi_table);
2640
Matthew Garrett74a365b2009-03-19 21:35:39 +00002641 if (intel_opregion_present())
2642 return 0;
2643
2644 return acpi_video_register();
2645}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002646
Zhao Yakui86e437f2009-06-16 11:23:13 +08002647static void __exit acpi_video_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648{
Zhao Yakui86e437f2009-06-16 11:23:13 +08002649 acpi_video_unregister();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650
Patrick Mocheld550d982006-06-27 00:41:40 -04002651 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002652}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653
2654module_init(acpi_video_init);
2655module_exit(acpi_video_exit);