video.c 65.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
/*
 *  video.c - ACPI Video Driver ($Revision:$)
 *
 *  Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
 *  Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org>
6
 *  Copyright (C) 2006 Thomas Tuttle <linux-kernel@ttuttle.net>
L
Linus Torvalds 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or (at
 *  your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/list.h>
32
#include <linux/mutex.h>
L
Linus Torvalds 已提交
33 34
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
35
#include <linux/input.h>
36
#include <linux/backlight.h>
37
#include <linux/thermal.h>
38
#include <linux/video_output.h>
39
#include <linux/sort.h>
40 41
#include <linux/pci.h>
#include <linux/pci_ids.h>
42
#include <linux/slab.h>
L
Linus Torvalds 已提交
43
#include <asm/uaccess.h>
44
#include <linux/dmi.h>
L
Linus Torvalds 已提交
45 46
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
47
#include <linux/suspend.h>
48
#include <acpi/video.h>
L
Linus Torvalds 已提交
49

50 51
#define PREFIX "ACPI: "

L
Linus Torvalds 已提交
52 53 54 55 56 57 58 59 60
#define ACPI_VIDEO_CLASS		"video"
#define ACPI_VIDEO_BUS_NAME		"Video Bus"
#define ACPI_VIDEO_DEVICE_NAME		"Video Device"
#define ACPI_VIDEO_NOTIFY_SWITCH	0x80
#define ACPI_VIDEO_NOTIFY_PROBE		0x81
#define ACPI_VIDEO_NOTIFY_CYCLE		0x82
#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT	0x83
#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT	0x84

61 62 63 64 65
#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS	0x85
#define	ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS	0x86
#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS	0x87
#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS	0x88
#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF		0x89
L
Linus Torvalds 已提交
66

67
#define MAX_NAME_LEN	20
L
Linus Torvalds 已提交
68 69

#define _COMPONENT		ACPI_VIDEO_COMPONENT
70
ACPI_MODULE_NAME("video");
L
Linus Torvalds 已提交
71

72
MODULE_AUTHOR("Bruno Ducrot");
73
MODULE_DESCRIPTION("ACPI Video Driver");
L
Linus Torvalds 已提交
74 75
MODULE_LICENSE("GPL");

76 77 78
static int brightness_switch_enabled = 1;
module_param(brightness_switch_enabled, bool, 0644);

79 80 81 82 83 84 85
/*
 * By default, we don't allow duplicate ACPI video bus devices
 * under the same VGA controller
 */
static int allow_duplicates;
module_param(allow_duplicates, bool, 0644);

86
static int register_count = 0;
L
Len Brown 已提交
87 88
static int acpi_video_bus_add(struct acpi_device *device);
static int acpi_video_bus_remove(struct acpi_device *device, int type);
89
static void acpi_video_bus_notify(struct acpi_device *device, u32 event);
L
Linus Torvalds 已提交
90

91 92 93 94 95 96
static const struct acpi_device_id video_device_ids[] = {
	{ACPI_VIDEO_HID, 0},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, video_device_ids);

L
Linus Torvalds 已提交
97
static struct acpi_driver acpi_video_bus = {
L
Len Brown 已提交
98
	.name = "video",
L
Linus Torvalds 已提交
99
	.class = ACPI_VIDEO_CLASS,
100
	.ids = video_device_ids,
L
Linus Torvalds 已提交
101 102 103
	.ops = {
		.add = acpi_video_bus_add,
		.remove = acpi_video_bus_remove,
104
		.notify = acpi_video_bus_notify,
L
Len Brown 已提交
105
		},
L
Linus Torvalds 已提交
106 107 108
};

struct acpi_video_bus_flags {
L
Len Brown 已提交
109 110 111 112
	u8 multihead:1;		/* can switch video heads */
	u8 rom:1;		/* can retrieve a video rom */
	u8 post:1;		/* can configure the head to */
	u8 reserved:5;
L
Linus Torvalds 已提交
113 114 115
};

struct acpi_video_bus_cap {
L
Len Brown 已提交
116 117 118 119 120 121 122
	u8 _DOS:1;		/*Enable/Disable output switching */
	u8 _DOD:1;		/*Enumerate all devices attached to display adapter */
	u8 _ROM:1;		/*Get ROM Data */
	u8 _GPD:1;		/*Get POST Device */
	u8 _SPD:1;		/*Set POST Device */
	u8 _VPO:1;		/*Video POST Options */
	u8 reserved:2;
L
Linus Torvalds 已提交
123 124
};

L
Len Brown 已提交
125 126
struct acpi_video_device_attrib {
	u32 display_index:4;	/* A zero-based instance of the Display */
127
	u32 display_port_attachment:4;	/*This field differentiates the display type */
L
Len Brown 已提交
128
	u32 display_type:4;	/*Describe the specific type in use */
129
	u32 vendor_specific:4;	/*Chipset Vendor Specific */
L
Len Brown 已提交
130 131 132 133 134 135
	u32 bios_can_detect:1;	/*BIOS can detect the device */
	u32 depend_on_vga:1;	/*Non-VGA output device whose power is related to 
				   the VGA device. */
	u32 pipe_id:3;		/*For VGA multiple-head devices. */
	u32 reserved:10;	/*Must be 0 */
	u32 device_id_scheme:1;	/*Device ID Scheme */
L
Linus Torvalds 已提交
136 137 138 139 140
};

struct acpi_video_enumerated_device {
	union {
		u32 int_val;
L
Len Brown 已提交
141
		struct acpi_video_device_attrib attrib;
L
Linus Torvalds 已提交
142 143 144 145 146
	} value;
	struct acpi_video_device *bind_info;
};

struct acpi_video_bus {
147
	struct acpi_device *device;
L
Len Brown 已提交
148
	u8 dos_setting;
L
Linus Torvalds 已提交
149
	struct acpi_video_enumerated_device *attached_array;
L
Len Brown 已提交
150 151
	u8 attached_count;
	struct acpi_video_bus_cap cap;
L
Linus Torvalds 已提交
152
	struct acpi_video_bus_flags flags;
L
Len Brown 已提交
153
	struct list_head video_device_list;
154
	struct mutex device_list_lock;	/* protects video_device_list */
155
#ifdef CONFIG_ACPI_PROCFS
L
Len Brown 已提交
156
	struct proc_dir_entry *dir;
157
#endif
158 159
	struct input_dev *input;
	char phys[32];	/* for input device */
160
	struct notifier_block pm_nb;
L
Linus Torvalds 已提交
161 162 163
};

struct acpi_video_device_flags {
L
Len Brown 已提交
164 165 166
	u8 crt:1;
	u8 lcd:1;
	u8 tvout:1;
167
	u8 dvi:1;
L
Len Brown 已提交
168 169
	u8 bios:1;
	u8 unknown:1;
170
	u8 reserved:2;
L
Linus Torvalds 已提交
171 172 173
};

struct acpi_video_device_cap {
L
Len Brown 已提交
174 175 176
	u8 _ADR:1;		/*Return the unique ID */
	u8 _BCL:1;		/*Query list of brightness control levels supported */
	u8 _BCM:1;		/*Set the brightness level */
177
	u8 _BQC:1;		/* Get current brightness level */
178
	u8 _BCQ:1;		/* Some buggy BIOS uses _BCQ instead of _BQC */
L
Len Brown 已提交
179 180 181 182
	u8 _DDC:1;		/*Return the EDID for this device */
	u8 _DCS:1;		/*Return status of output device */
	u8 _DGS:1;		/*Query graphics state */
	u8 _DSS:1;		/*Device state set */
L
Linus Torvalds 已提交
183 184
};

185 186
struct acpi_video_brightness_flags {
	u8 _BCL_no_ac_battery_levels:1;	/* no AC/Battery levels in _BCL */
187
	u8 _BCL_reversed:1;		/* _BCL package is in a reversed order*/
188 189 190
	u8 _BCL_use_index:1;		/* levels in _BCL are index values */
	u8 _BCM_use_index:1;		/* input of _BCM is an index value */
	u8 _BQC_use_index:1;		/* _BQC returns an index value */
191 192
};

L
Linus Torvalds 已提交
193
struct acpi_video_device_brightness {
L
Len Brown 已提交
194 195 196
	int curr;
	int count;
	int *levels;
197
	struct acpi_video_brightness_flags flags;
L
Linus Torvalds 已提交
198 199 200
};

struct acpi_video_device {
L
Len Brown 已提交
201 202 203 204 205 206
	unsigned long device_id;
	struct acpi_video_device_flags flags;
	struct acpi_video_device_cap cap;
	struct list_head entry;
	struct acpi_video_bus *video;
	struct acpi_device *dev;
L
Linus Torvalds 已提交
207
	struct acpi_video_device_brightness *brightness;
208
	struct backlight_device *backlight;
209
	struct thermal_cooling_device *cooling_dev;
210
	struct output_device *output_dev;
L
Linus Torvalds 已提交
211 212
};

213
#ifdef CONFIG_ACPI_PROCFS
L
Linus Torvalds 已提交
214 215
/* bus */
static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file);
J
Jan Engelhardt 已提交
216
static const struct file_operations acpi_video_bus_info_fops = {
217
	.owner = THIS_MODULE,
L
Len Brown 已提交
218 219 220 221
	.open = acpi_video_bus_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
222 223 224
};

static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file);
J
Jan Engelhardt 已提交
225
static const struct file_operations acpi_video_bus_ROM_fops = {
226
	.owner = THIS_MODULE,
L
Len Brown 已提交
227 228 229 230
	.open = acpi_video_bus_ROM_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
231 232
};

L
Len Brown 已提交
233 234
static int acpi_video_bus_POST_info_open_fs(struct inode *inode,
					    struct file *file);
J
Jan Engelhardt 已提交
235
static const struct file_operations acpi_video_bus_POST_info_fops = {
236
	.owner = THIS_MODULE,
L
Len Brown 已提交
237 238 239 240
	.open = acpi_video_bus_POST_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
241 242 243
};

static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file);
L
Len Brown 已提交
244
static ssize_t acpi_video_bus_write_POST(struct file *file,
J
Jan Engelhardt 已提交
245 246
	const char __user *buffer, size_t count, loff_t *data);
static const struct file_operations acpi_video_bus_POST_fops = {
247
	.owner = THIS_MODULE,
L
Len Brown 已提交
248 249
	.open = acpi_video_bus_POST_open_fs,
	.read = seq_read,
J
Jan Engelhardt 已提交
250
	.write = acpi_video_bus_write_POST,
L
Len Brown 已提交
251 252
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
253 254 255
};

static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file);
L
Len Brown 已提交
256
static ssize_t acpi_video_bus_write_DOS(struct file *file,
J
Jan Engelhardt 已提交
257 258
	const char __user *buffer, size_t count, loff_t *data);
static const struct file_operations acpi_video_bus_DOS_fops = {
259
	.owner = THIS_MODULE,
L
Len Brown 已提交
260 261
	.open = acpi_video_bus_DOS_open_fs,
	.read = seq_read,
J
Jan Engelhardt 已提交
262
	.write = acpi_video_bus_write_DOS,
L
Len Brown 已提交
263 264
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
265 266 267
};

/* device */
L
Len Brown 已提交
268 269
static int acpi_video_device_info_open_fs(struct inode *inode,
					  struct file *file);
J
Jan Engelhardt 已提交
270
static const struct file_operations acpi_video_device_info_fops = {
271
	.owner = THIS_MODULE,
L
Len Brown 已提交
272 273 274 275
	.open = acpi_video_device_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
276 277
};

L
Len Brown 已提交
278 279
static int acpi_video_device_state_open_fs(struct inode *inode,
					   struct file *file);
L
Len Brown 已提交
280
static ssize_t acpi_video_device_write_state(struct file *file,
J
Jan Engelhardt 已提交
281 282
	const char __user *buffer, size_t count, loff_t *data);
static const struct file_operations acpi_video_device_state_fops = {
283
	.owner = THIS_MODULE,
L
Len Brown 已提交
284 285
	.open = acpi_video_device_state_open_fs,
	.read = seq_read,
J
Jan Engelhardt 已提交
286
	.write = acpi_video_device_write_state,
L
Len Brown 已提交
287 288
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
289 290
};

L
Len Brown 已提交
291 292
static int acpi_video_device_brightness_open_fs(struct inode *inode,
						struct file *file);
L
Len Brown 已提交
293
static ssize_t acpi_video_device_write_brightness(struct file *file,
J
Jan Engelhardt 已提交
294
	const char __user *buffer, size_t count, loff_t *data);
295
static const struct file_operations acpi_video_device_brightness_fops = {
296
	.owner = THIS_MODULE,
L
Len Brown 已提交
297 298
	.open = acpi_video_device_brightness_open_fs,
	.read = seq_read,
J
Jan Engelhardt 已提交
299
	.write = acpi_video_device_write_brightness,
L
Len Brown 已提交
300 301
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
302 303
};

L
Len Brown 已提交
304 305
static int acpi_video_device_EDID_open_fs(struct inode *inode,
					  struct file *file);
J
Jan Engelhardt 已提交
306
static const struct file_operations acpi_video_device_EDID_fops = {
307
	.owner = THIS_MODULE,
L
Len Brown 已提交
308 309 310 311
	.open = acpi_video_device_EDID_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
312
};
313
#endif /* CONFIG_ACPI_PROCFS */
L
Linus Torvalds 已提交
314

J
Jan Engelhardt 已提交
315
static const char device_decode[][30] = {
L
Linus Torvalds 已提交
316 317 318 319 320 321
	"motherboard VGA device",
	"PCI VGA device",
	"AGP VGA device",
	"UNKNOWN",
};

L
Len Brown 已提交
322 323 324 325
static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data);
static void acpi_video_device_rebind(struct acpi_video_bus *video);
static void acpi_video_device_bind(struct acpi_video_bus *video,
				   struct acpi_video_device *device);
L
Linus Torvalds 已提交
326
static int acpi_video_device_enumerate(struct acpi_video_bus *video);
327 328 329 330
static int acpi_video_device_lcd_set_level(struct acpi_video_device *device,
			int level);
static int acpi_video_device_lcd_get_level_current(
			struct acpi_video_device *device,
331
			unsigned long long *level, int init);
L
Len Brown 已提交
332 333
static int acpi_video_get_next_level(struct acpi_video_device *device,
				     u32 level_current, u32 event);
334
static int acpi_video_switch_brightness(struct acpi_video_device *device,
L
Len Brown 已提交
335
					 int event);
336
static int acpi_video_device_get_state(struct acpi_video_device *device,
337
			    unsigned long long *state);
338 339
static int acpi_video_output_get(struct output_device *od);
static int acpi_video_device_set_state(struct acpi_video_device *device, int state);
L
Linus Torvalds 已提交
340

341 342 343
/*backlight device sysfs support*/
static int acpi_video_get_brightness(struct backlight_device *bd)
{
344
	unsigned long long cur_level;
345
	int i;
346
	struct acpi_video_device *vd =
347
		(struct acpi_video_device *)bl_get_data(bd);
348

349
	if (acpi_video_device_lcd_get_level_current(vd, &cur_level, 0))
350
		return -EINVAL;
351 352 353 354 355 356 357
	for (i = 2; i < vd->brightness->count; i++) {
		if (vd->brightness->levels[i] == cur_level)
			/* The first two entries are special - see page 575
			   of the ACPI spec 3.0 */
			return i-2;
	}
	return 0;
358 359 360 361
}

static int acpi_video_set_brightness(struct backlight_device *bd)
{
362
	int request_level = bd->props.brightness + 2;
363
	struct acpi_video_device *vd =
364
		(struct acpi_video_device *)bl_get_data(bd);
365 366 367

	return acpi_video_device_lcd_set_level(vd,
				vd->brightness->levels[request_level]);
368 369
}

370 371 372 373 374
static struct backlight_ops acpi_backlight_ops = {
	.get_brightness = acpi_video_get_brightness,
	.update_status  = acpi_video_set_brightness,
};

375 376 377
/*video output device sysfs support*/
static int acpi_video_output_get(struct output_device *od)
{
378
	unsigned long long state;
379
	struct acpi_video_device *vd =
380
		(struct acpi_video_device *)dev_get_drvdata(&od->dev);
381 382 383 384 385 386 387 388
	acpi_video_device_get_state(vd, &state);
	return (int)state;
}

static int acpi_video_output_set(struct output_device *od)
{
	unsigned long state = od->request_state;
	struct acpi_video_device *vd=
389
		(struct acpi_video_device *)dev_get_drvdata(&od->dev);
390 391 392 393 394 395 396
	return acpi_video_device_set_state(vd, state);
}

static struct output_properties acpi_output_properties = {
	.set_state = acpi_video_output_set,
	.get_status = acpi_video_output_get,
};
397 398 399


/* thermal cooling device callbacks */
400
static int video_get_max_state(struct thermal_cooling_device *cooling_dev, unsigned
401
			       long *state)
402
{
403
	struct acpi_device *device = cooling_dev->devdata;
404 405
	struct acpi_video_device *video = acpi_driver_data(device);

406 407
	*state = video->brightness->count - 3;
	return 0;
408 409
}

410
static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, unsigned
411
			       long *state)
412
{
413
	struct acpi_device *device = cooling_dev->devdata;
414
	struct acpi_video_device *video = acpi_driver_data(device);
415
	unsigned long long level;
416
	int offset;
417

418
	if (acpi_video_device_lcd_get_level_current(video, &level, 0))
419
		return -EINVAL;
420 421 422 423 424
	for (offset = 2; offset < video->brightness->count; offset++)
		if (level == video->brightness->levels[offset]) {
			*state = video->brightness->count - offset - 1;
			return 0;
		}
425 426 427 428 429

	return -EINVAL;
}

static int
430
video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state)
431
{
432
	struct acpi_device *device = cooling_dev->devdata;
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
	struct acpi_video_device *video = acpi_driver_data(device);
	int level;

	if ( state >= video->brightness->count - 2)
		return -EINVAL;

	state = video->brightness->count - state;
	level = video->brightness->levels[state -1];
	return acpi_video_device_lcd_set_level(video, level);
}

static struct thermal_cooling_device_ops video_cooling_ops = {
	.get_max_state = video_get_max_state,
	.get_cur_state = video_get_cur_state,
	.set_cur_state = video_set_cur_state,
};

L
Linus Torvalds 已提交
450 451 452 453 454 455 456
/* --------------------------------------------------------------------------
                               Video Management
   -------------------------------------------------------------------------- */

/* device */

static int
L
Len Brown 已提交
457
acpi_video_device_get_state(struct acpi_video_device *device,
458
			    unsigned long long *state)
L
Linus Torvalds 已提交
459
{
L
Len Brown 已提交
460
	int status;
L
Linus Torvalds 已提交
461

462
	status = acpi_evaluate_integer(device->dev->handle, "_DCS", NULL, state);
L
Linus Torvalds 已提交
463

464
	return status;
L
Linus Torvalds 已提交
465 466 467
}

static int
L
Len Brown 已提交
468
acpi_video_device_set_state(struct acpi_video_device *device, int state)
L
Linus Torvalds 已提交
469
{
L
Len Brown 已提交
470 471 472
	int status;
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
473
	unsigned long long ret;
L
Linus Torvalds 已提交
474 475 476


	arg0.integer.value = state;
477
	status = acpi_evaluate_integer(device->dev->handle, "_DSS", &args, &ret);
L
Linus Torvalds 已提交
478

479
	return status;
L
Linus Torvalds 已提交
480 481 482
}

static int
L
Len Brown 已提交
483 484
acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
				   union acpi_object **levels)
L
Linus Torvalds 已提交
485
{
L
Len Brown 已提交
486 487 488
	int status;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *obj;
L
Linus Torvalds 已提交
489 490 491 492


	*levels = NULL;

493
	status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer);
L
Linus Torvalds 已提交
494
	if (!ACPI_SUCCESS(status))
495
		return status;
L
Len Brown 已提交
496
	obj = (union acpi_object *)buffer.pointer;
497
	if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
498
		printk(KERN_ERR PREFIX "Invalid _BCL data\n");
L
Linus Torvalds 已提交
499 500 501 502 503 504
		status = -EFAULT;
		goto err;
	}

	*levels = obj;

505
	return 0;
L
Linus Torvalds 已提交
506

L
Len Brown 已提交
507
      err:
508
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
509

510
	return status;
L
Linus Torvalds 已提交
511 512 513
}

static int
L
Len Brown 已提交
514
acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
L
Linus Torvalds 已提交
515
{
516
	int status;
L
Len Brown 已提交
517 518
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
519
	int state;
L
Linus Torvalds 已提交
520 521 522

	arg0.integer.value = level;

523 524 525 526 527 528 529
	status = acpi_evaluate_object(device->dev->handle, "_BCM",
				      &args, NULL);
	if (ACPI_FAILURE(status)) {
		ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));
		return -EIO;
	}

530
	device->brightness->curr = level;
531
	for (state = 2; state < device->brightness->count; state++)
532
		if (level == device->brightness->levels[state]) {
533 534
			if (device->backlight)
				device->backlight->props.brightness = state - 2;
535 536
			return 0;
		}
537

538 539
	ACPI_ERROR((AE_INFO, "Current brightness invalid"));
	return -EINVAL;
L
Linus Torvalds 已提交
540 541
}

542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
/*
 * For some buggy _BQC methods, we need to add a constant value to
 * the _BQC return value to get the actual current brightness level
 */

static int bqc_offset_aml_bug_workaround;
static int __init video_set_bqc_offset(const struct dmi_system_id *d)
{
	bqc_offset_aml_bug_workaround = 9;
	return 0;
}

static struct dmi_system_id video_dmi_table[] __initdata = {
	/*
	 * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121
	 */
	{
	 .callback = video_set_bqc_offset,
	 .ident = "Acer Aspire 5720",
	 .matches = {
		DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"),
		},
	},
566 567 568 569 570 571 572 573
	{
	 .callback = video_set_bqc_offset,
	 .ident = "Acer Aspire 5710Z",
	 .matches = {
		DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710Z"),
		},
	},
574 575 576 577 578 579 580 581
	{
	 .callback = video_set_bqc_offset,
	 .ident = "eMachines E510",
	 .matches = {
		DMI_MATCH(DMI_BOARD_VENDOR, "EMACHINES"),
		DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"),
		},
	},
582 583 584 585 586 587 588 589
	{
	 .callback = video_set_bqc_offset,
	 .ident = "Acer Aspire 5315",
	 .matches = {
		DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"),
		},
	},
590 591 592 593 594 595 596 597
	{
	 .callback = video_set_bqc_offset,
	 .ident = "Acer Aspire 7720",
	 .matches = {
		DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),
		},
	},
598 599 600
	{}
};

L
Linus Torvalds 已提交
601
static int
L
Len Brown 已提交
602
acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
603
					unsigned long long *level, int init)
L
Linus Torvalds 已提交
604
{
605
	acpi_status status = AE_OK;
606
	int i;
607

608 609 610 611
	if (device->cap._BQC || device->cap._BCQ) {
		char *buf = device->cap._BQC ? "_BQC" : "_BCQ";

		status = acpi_evaluate_integer(device->dev->handle, buf,
612 613
						NULL, level);
		if (ACPI_SUCCESS(status)) {
614 615 616 617 618 619 620
			if (device->brightness->flags._BQC_use_index) {
				if (device->brightness->flags._BCL_reversed)
					*level = device->brightness->count
								 - 3 - (*level);
				*level = device->brightness->levels[*level + 2];

			}
621
			*level += bqc_offset_aml_bug_workaround;
622 623 624 625 626
			for (i = 2; i < device->brightness->count; i++)
				if (device->brightness->levels[i] == *level) {
					device->brightness->curr = *level;
					return 0;
			}
627 628 629 630 631 632 633 634 635 636
			if (!init) {
				/*
				 * BQC returned an invalid level.
				 * Stop using it.
				 */
				ACPI_WARNING((AE_INFO,
					      "%s returned an invalid level",
					      buf));
				device->cap._BQC = device->cap._BCQ = 0;
			}
637 638 639 640 641 642 643 644
		} else {
			/* Fixme:
			 * should we return an error or ignore this failure?
			 * dev->brightness->curr is a cached value which stores
			 * the correct current backlight level in most cases.
			 * ACPI video backlight still works w/ buggy _BQC.
			 * http://bugzilla.kernel.org/show_bug.cgi?id=12233
			 */
645 646
			ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf));
			device->cap._BQC = device->cap._BCQ = 0;
647 648 649
		}
	}

650
	*level = device->brightness->curr;
651
	return 0;
L
Linus Torvalds 已提交
652 653 654
}

static int
L
Len Brown 已提交
655 656
acpi_video_device_EDID(struct acpi_video_device *device,
		       union acpi_object **edid, ssize_t length)
L
Linus Torvalds 已提交
657
{
L
Len Brown 已提交
658 659 660 661 662
	int status;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *obj;
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
L
Linus Torvalds 已提交
663 664 665 666 667


	*edid = NULL;

	if (!device)
668
		return -ENODEV;
L
Linus Torvalds 已提交
669 670 671 672 673
	if (length == 128)
		arg0.integer.value = 1;
	else if (length == 256)
		arg0.integer.value = 2;
	else
674
		return -EINVAL;
L
Linus Torvalds 已提交
675

676
	status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer);
L
Linus Torvalds 已提交
677
	if (ACPI_FAILURE(status))
678
		return -ENODEV;
L
Linus Torvalds 已提交
679

680
	obj = buffer.pointer;
L
Linus Torvalds 已提交
681 682 683 684

	if (obj && obj->type == ACPI_TYPE_BUFFER)
		*edid = obj;
	else {
685
		printk(KERN_ERR PREFIX "Invalid _DDC data\n");
L
Linus Torvalds 已提交
686 687 688 689
		status = -EFAULT;
		kfree(obj);
	}

690
	return status;
L
Linus Torvalds 已提交
691 692 693 694 695 696 697 698 699 700 701
}

/* bus */

/*
 *  Arg:
 *  	video		: video bus device pointer
 *	bios_flag	: 
 *		0.	The system BIOS should NOT automatically switch(toggle)
 *			the active display output.
 *		1.	The system BIOS should automatically switch (toggle) the
702
 *			active display output. No switch event.
L
Linus Torvalds 已提交
703 704 705 706 707 708
 *		2.	The _DGS value should be locked.
 *		3.	The system BIOS should not automatically switch (toggle) the
 *			active display output, but instead generate the display switch
 *			event notify code.
 *	lcd_flag	:
 *		0.	The system BIOS should automatically control the brightness level
709
 *			of the LCD when the power changes from AC to DC
L
Linus Torvalds 已提交
710
 *		1. 	The system BIOS should NOT automatically control the brightness 
711
 *			level of the LCD when the power changes from AC to DC.
L
Linus Torvalds 已提交
712 713 714 715 716
 * Return Value:
 * 		-1	wrong arg.
 */

static int
L
Len Brown 已提交
717
acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
L
Linus Torvalds 已提交
718
{
L
Lin Ming 已提交
719
	u64 status = 0;
L
Len Brown 已提交
720 721
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
L
Linus Torvalds 已提交
722 723


L
Len Brown 已提交
724
	if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) {
L
Linus Torvalds 已提交
725 726 727 728 729
		status = -1;
		goto Failed;
	}
	arg0.integer.value = (lcd_flag << 2) | bios_flag;
	video->dos_setting = arg0.integer.value;
730
	acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL);
L
Linus Torvalds 已提交
731

L
Len Brown 已提交
732
      Failed:
733
	return status;
L
Linus Torvalds 已提交
734 735
}

736 737 738 739 740 741 742 743 744 745
/*
 * Simple comparison function used to sort backlight levels.
 */

static int
acpi_video_cmp_level(const void *a, const void *b)
{
	return *(int *)a - *(int *)b;
}

L
Linus Torvalds 已提交
746 747 748 749 750
/*
 *  Arg:	
 *  	device	: video output device (LCD, CRT, ..)
 *
 *  Return Value:
751 752 753 754 755 756 757 758 759
 *	Maximum brightness level
 *
 *  Allocate and initialize device->brightness.
 */

static int
acpi_video_init_brightness(struct acpi_video_device *device)
{
	union acpi_object *obj = NULL;
760
	int i, max_level = 0, count = 0, level_ac_battery = 0;
761
	unsigned long long level, level_old;
762 763
	union acpi_object *o;
	struct acpi_video_device_brightness *br = NULL;
764
	int result = -EINVAL;
765 766 767 768 769 770 771 772 773 774 775 776 777

	if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available "
						"LCD brightness level\n"));
		goto out;
	}

	if (obj->package.count < 2)
		goto out;

	br = kzalloc(sizeof(*br), GFP_KERNEL);
	if (!br) {
		printk(KERN_ERR "can't allocate memory\n");
778
		result = -ENOMEM;
779 780 781
		goto out;
	}

782
	br->levels = kmalloc((obj->package.count + 2) * sizeof *(br->levels),
783
				GFP_KERNEL);
784 785
	if (!br->levels) {
		result = -ENOMEM;
786
		goto out_free;
787
	}
788 789 790 791 792 793 794 795 796 797 798 799 800 801

	for (i = 0; i < obj->package.count; i++) {
		o = (union acpi_object *)&obj->package.elements[i];
		if (o->type != ACPI_TYPE_INTEGER) {
			printk(KERN_ERR PREFIX "Invalid data\n");
			continue;
		}
		br->levels[count] = (u32) o->integer.value;

		if (br->levels[count] > max_level)
			max_level = br->levels[count];
		count++;
	}

802 803 804 805 806 807
	/*
	 * some buggy BIOS don't export the levels
	 * when machine is on AC/Battery in _BCL package.
	 * In this case, the first two elements in _BCL packages
	 * are also supported brightness levels that OS should take care of.
	 */
808 809
	for (i = 2; i < count; i++) {
		if (br->levels[i] == br->levels[0])
810
			level_ac_battery++;
811 812 813
		if (br->levels[i] == br->levels[1])
			level_ac_battery++;
	}
814 815 816 817 818 819 820 821 822 823

	if (level_ac_battery < 2) {
		level_ac_battery = 2 - level_ac_battery;
		br->flags._BCL_no_ac_battery_levels = 1;
		for (i = (count - 1 + level_ac_battery); i >= 2; i--)
			br->levels[i] = br->levels[i - level_ac_battery];
		count += level_ac_battery;
	} else if (level_ac_battery > 2)
		ACPI_ERROR((AE_INFO, "Too many duplicates in _BCL package\n"));

824 825 826 827 828 829 830 831
	/* Check if the _BCL package is in a reversed order */
	if (max_level == br->levels[2]) {
		br->flags._BCL_reversed = 1;
		sort(&br->levels[2], count - 2, sizeof(br->levels[2]),
			acpi_video_cmp_level, NULL);
	} else if (max_level != br->levels[count - 1])
		ACPI_ERROR((AE_INFO,
			    "Found unordered _BCL package\n"));
832 833 834

	br->count = count;
	device->brightness = br;
835 836 837 838 839 840 841 842 843 844 845 846

	/* Check the input/output of _BQC/_BCL/_BCM */
	if ((max_level < 100) && (max_level <= (count - 2)))
		br->flags._BCL_use_index = 1;

	/*
	 * _BCM is always consistent with _BCL,
	 * at least for all the laptops we have ever seen.
	 */
	br->flags._BCM_use_index = br->flags._BCL_use_index;

	/* _BQC uses INDEX while _BCL uses VALUE in some laptops */
847
	br->curr = level = max_level;
848 849 850 851

	if (!device->cap._BQC)
		goto set_level;

852
	result = acpi_video_device_lcd_get_level_current(device, &level_old, 1);
853 854 855
	if (result)
		goto out_free_levels;

856 857 858 859
	/*
	 * Set the level to maximum and check if _BQC uses indexed value
	 */
	result = acpi_video_device_lcd_set_level(device, max_level);
860 861 862
	if (result)
		goto out_free_levels;

863
	result = acpi_video_device_lcd_get_level_current(device, &level, 0);
864 865 866
	if (result)
		goto out_free_levels;

867 868
	br->flags._BQC_use_index = (level == max_level ? 0 : 1);

869 870 871 872 873 874 875 876 877 878
	if (!br->flags._BQC_use_index) {
		/*
		 * Set the backlight to the initial state.
		 * On some buggy laptops, _BQC returns an uninitialized value
		 * when invoked for the first time, i.e. level_old is invalid.
		 * set the backlight to max_level in this case
		 */
		for (i = 2; i < br->count; i++)
			if (level_old == br->levels[i])
				level = level_old;
879
		goto set_level;
880
	}
881 882 883

	if (br->flags._BCL_reversed)
		level_old = (br->count - 1) - level_old;
884
	level = br->levels[level_old];
885 886

set_level:
887
	result = acpi_video_device_lcd_set_level(device, level);
888 889
	if (result)
		goto out_free_levels;
890

891 892
	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
			  "found %d brightness levels\n", count - 2));
893
	kfree(obj);
894
	return result;
895 896 897 898 899 900 901 902

out_free_levels:
	kfree(br->levels);
out_free:
	kfree(br);
out:
	device->brightness = NULL;
	kfree(obj);
903
	return result;
904 905 906 907 908 909 910
}

/*
 *  Arg:
 *	device	: video output device (LCD, CRT, ..)
 *
 *  Return Value:
L
Linus Torvalds 已提交
911 912
 *  	None
 *
913
 *  Find out all required AML methods defined under the output
L
Linus Torvalds 已提交
914 915 916
 *  device.
 */

L
Len Brown 已提交
917
static void acpi_video_device_find_cap(struct acpi_video_device *device)
L
Linus Torvalds 已提交
918 919 920
{
	acpi_handle h_dummy1;

921
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
L
Linus Torvalds 已提交
922 923
		device->cap._ADR = 1;
	}
924
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) {
L
Len Brown 已提交
925
		device->cap._BCL = 1;
L
Linus Torvalds 已提交
926
	}
927
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
L
Len Brown 已提交
928
		device->cap._BCM = 1;
L
Linus Torvalds 已提交
929
	}
930 931
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
		device->cap._BQC = 1;
932 933 934 935 936 937
	else if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCQ",
				&h_dummy1))) {
		printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n");
		device->cap._BCQ = 1;
	}

938
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
L
Len Brown 已提交
939
		device->cap._DDC = 1;
L
Linus Torvalds 已提交
940
	}
941
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DCS", &h_dummy1))) {
L
Linus Torvalds 已提交
942 943
		device->cap._DCS = 1;
	}
944
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DGS", &h_dummy1))) {
L
Linus Torvalds 已提交
945 946
		device->cap._DGS = 1;
	}
947
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DSS", &h_dummy1))) {
L
Linus Torvalds 已提交
948 949 950
		device->cap._DSS = 1;
	}

951
	if (acpi_video_backlight_support()) {
952
		struct backlight_properties props;
953
		int result;
954 955
		static int count = 0;
		char *name;
956 957 958 959

		result = acpi_video_init_brightness(device);
		if (result)
			return;
J
Julia Lawall 已提交
960
		name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
961 962
		if (!name)
			return;
J
Julia Lawall 已提交
963
		count++;
964

965 966 967 968 969
		memset(&props, 0, sizeof(struct backlight_properties));
		props.max_brightness = device->brightness->count - 3;
		device->backlight = backlight_device_register(name, NULL, device,
							      &acpi_backlight_ops,
							      &props);
970
		kfree(name);
971 972
		if (IS_ERR(device->backlight))
			return;
973

974 975 976 977 978 979
		/*
		 * Save current brightness level in case we have to restore it
		 * before acpi_video_device_lcd_set_level() is called next time.
		 */
		device->backlight->props.brightness =
				acpi_video_get_brightness(device->backlight);
980

981 982 983 984 985
		result = sysfs_create_link(&device->backlight->dev.kobj,
					   &device->dev->dev.kobj, "device");
		if (result)
			printk(KERN_ERR PREFIX "Create sysfs link\n");

986
		device->cooling_dev = thermal_cooling_device_register("LCD",
987
					device->dev, &video_cooling_ops);
988
		if (IS_ERR(device->cooling_dev)) {
989
			/*
990
			 * Set cooling_dev to NULL so we don't crash trying to
991 992 993 994 995 996
			 * free it.
			 * Also, why the hell we are returning early and
			 * not attempt to register video output if cooling
			 * device registration failed?
			 * -- dtor
			 */
997
			device->cooling_dev = NULL;
998
			return;
999
		}
1000

1001
		dev_info(&device->dev->dev, "registered as cooling_device%d\n",
1002
			 device->cooling_dev->id);
1003
		result = sysfs_create_link(&device->dev->dev.kobj,
1004
				&device->cooling_dev->device.kobj,
1005 1006 1007
				"thermal_cooling");
		if (result)
			printk(KERN_ERR PREFIX "Create sysfs link\n");
1008
		result = sysfs_create_link(&device->cooling_dev->device.kobj,
1009 1010 1011 1012
				&device->dev->dev.kobj, "device");
		if (result)
			printk(KERN_ERR PREFIX "Create sysfs link\n");

1013
	}
1014 1015 1016 1017 1018 1019

	if (acpi_video_display_switch_support()) {

		if (device->cap._DCS && device->cap._DSS) {
			static int count;
			char *name;
J
Julia Lawall 已提交
1020
			name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
1021 1022
			if (!name)
				return;
J
Julia Lawall 已提交
1023
			count++;
1024 1025 1026 1027
			device->output_dev = video_output_register(name,
					NULL, device, &acpi_output_properties);
			kfree(name);
		}
1028
	}
L
Linus Torvalds 已提交
1029 1030 1031 1032 1033 1034 1035 1036 1037
}

/*
 *  Arg:	
 *  	device	: video output device (VGA)
 *
 *  Return Value:
 *  	None
 *
1038
 *  Find out all required AML methods defined under the video bus device.
L
Linus Torvalds 已提交
1039 1040
 */

L
Len Brown 已提交
1041
static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1042
{
L
Len Brown 已提交
1043
	acpi_handle h_dummy1;
L
Linus Torvalds 已提交
1044

1045
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
L
Linus Torvalds 已提交
1046 1047
		video->cap._DOS = 1;
	}
1048
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) {
L
Linus Torvalds 已提交
1049 1050
		video->cap._DOD = 1;
	}
1051
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) {
L
Linus Torvalds 已提交
1052 1053
		video->cap._ROM = 1;
	}
1054
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) {
L
Linus Torvalds 已提交
1055 1056
		video->cap._GPD = 1;
	}
1057
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) {
L
Linus Torvalds 已提交
1058 1059
		video->cap._SPD = 1;
	}
1060
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) {
L
Linus Torvalds 已提交
1061 1062 1063 1064 1065 1066 1067 1068 1069
		video->cap._VPO = 1;
	}
}

/*
 * Check whether the video bus device has required AML method to
 * support the desired features
 */

L
Len Brown 已提交
1070
static int acpi_video_bus_check(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1071
{
L
Len Brown 已提交
1072
	acpi_status status = -ENOENT;
1073
	struct pci_dev *dev;
L
Linus Torvalds 已提交
1074 1075

	if (!video)
1076
		return -EINVAL;
L
Linus Torvalds 已提交
1077

1078
	dev = acpi_get_pci_dev(video->device->handle);
1079 1080
	if (!dev)
		return -ENODEV;
1081
	pci_dev_put(dev);
1082

L
Linus Torvalds 已提交
1083 1084 1085 1086
	/* Since there is no HID, CID and so on for VGA driver, we have
	 * to check well known required nodes.
	 */

1087
	/* Does this device support video switching? */
1088 1089 1090 1091 1092 1093
	if (video->cap._DOS || video->cap._DOD) {
		if (!video->cap._DOS) {
			printk(KERN_WARNING FW_BUG
				"ACPI(%s) defines _DOD but not _DOS\n",
				acpi_device_bid(video->device));
		}
L
Linus Torvalds 已提交
1094 1095 1096 1097
		video->flags.multihead = 1;
		status = 0;
	}

1098
	/* Does this device support retrieving a video ROM? */
L
Len Brown 已提交
1099
	if (video->cap._ROM) {
L
Linus Torvalds 已提交
1100 1101 1102 1103
		video->flags.rom = 1;
		status = 0;
	}

1104
	/* Does this device support configuring which video device to POST? */
L
Len Brown 已提交
1105
	if (video->cap._GPD && video->cap._SPD && video->cap._VPO) {
L
Linus Torvalds 已提交
1106 1107 1108 1109
		video->flags.post = 1;
		status = 0;
	}

1110
	return status;
L
Linus Torvalds 已提交
1111 1112 1113 1114 1115
}

/* --------------------------------------------------------------------------
                              FS Interface (/proc)
   -------------------------------------------------------------------------- */
1116
#ifdef CONFIG_ACPI_PROCFS
L
Linus Torvalds 已提交
1117

L
Len Brown 已提交
1118
static struct proc_dir_entry *acpi_video_dir;
L
Linus Torvalds 已提交
1119 1120 1121

/* video devices */

L
Len Brown 已提交
1122
static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1123
{
1124
	struct acpi_video_device *dev = seq->private;
L
Linus Torvalds 已提交
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137


	if (!dev)
		goto end;

	seq_printf(seq, "device_id:    0x%04x\n", (u32) dev->device_id);
	seq_printf(seq, "type:         ");
	if (dev->flags.crt)
		seq_printf(seq, "CRT\n");
	else if (dev->flags.lcd)
		seq_printf(seq, "LCD\n");
	else if (dev->flags.tvout)
		seq_printf(seq, "TVOUT\n");
1138 1139
	else if (dev->flags.dvi)
		seq_printf(seq, "DVI\n");
L
Linus Torvalds 已提交
1140 1141 1142
	else
		seq_printf(seq, "UNKNOWN\n");

L
Len Brown 已提交
1143
	seq_printf(seq, "known by bios: %s\n", dev->flags.bios ? "yes" : "no");
L
Linus Torvalds 已提交
1144

L
Len Brown 已提交
1145
      end:
1146
	return 0;
L
Linus Torvalds 已提交
1147 1148 1149
}

static int
L
Len Brown 已提交
1150
acpi_video_device_info_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1151 1152 1153 1154 1155
{
	return single_open(file, acpi_video_device_info_seq_show,
			   PDE(inode)->data);
}

1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167
static int
acpi_video_device_query(struct acpi_video_device *device,
			unsigned long long *state)
{
	int status;

	status = acpi_evaluate_integer(device->dev->handle, "_DGS",
				       NULL, state);

	return status;
}

L
Len Brown 已提交
1168
static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1169
{
L
Len Brown 已提交
1170
	int status;
1171
	struct acpi_video_device *dev = seq->private;
1172
	unsigned long long state;
L
Linus Torvalds 已提交
1173 1174 1175 1176 1177 1178 1179 1180


	if (!dev)
		goto end;

	status = acpi_video_device_get_state(dev, &state);
	seq_printf(seq, "state:     ");
	if (ACPI_SUCCESS(status))
1181
		seq_printf(seq, "0x%02llx\n", state);
L
Linus Torvalds 已提交
1182 1183 1184 1185 1186 1187
	else
		seq_printf(seq, "<not supported>\n");

	status = acpi_video_device_query(dev, &state);
	seq_printf(seq, "query:     ");
	if (ACPI_SUCCESS(status))
1188
		seq_printf(seq, "0x%02llx\n", state);
L
Linus Torvalds 已提交
1189 1190 1191
	else
		seq_printf(seq, "<not supported>\n");

L
Len Brown 已提交
1192
      end:
1193
	return 0;
L
Linus Torvalds 已提交
1194 1195 1196
}

static int
L
Len Brown 已提交
1197
acpi_video_device_state_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1198 1199 1200 1201 1202 1203
{
	return single_open(file, acpi_video_device_state_seq_show,
			   PDE(inode)->data);
}

static ssize_t
L
Len Brown 已提交
1204 1205 1206
acpi_video_device_write_state(struct file *file,
			      const char __user * buffer,
			      size_t count, loff_t * data)
L
Linus Torvalds 已提交
1207
{
L
Len Brown 已提交
1208
	int status;
1209 1210
	struct seq_file *m = file->private_data;
	struct acpi_video_device *dev = m->private;
L
Len Brown 已提交
1211 1212
	char str[12] = { 0 };
	u32 state = 0;
L
Linus Torvalds 已提交
1213 1214


1215
	if (!dev || count >= sizeof(str))
1216
		return -EINVAL;
L
Linus Torvalds 已提交
1217 1218

	if (copy_from_user(str, buffer, count))
1219
		return -EFAULT;
L
Linus Torvalds 已提交
1220 1221 1222

	str[count] = 0;
	state = simple_strtoul(str, NULL, 0);
L
Len Brown 已提交
1223
	state &= ((1ul << 31) | (1ul << 30) | (1ul << 0));
L
Linus Torvalds 已提交
1224 1225 1226 1227

	status = acpi_video_device_set_state(dev, state);

	if (status)
1228
		return -EFAULT;
L
Linus Torvalds 已提交
1229

1230
	return count;
L
Linus Torvalds 已提交
1231 1232 1233
}

static int
L
Len Brown 已提交
1234
acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1235
{
1236
	struct acpi_video_device *dev = seq->private;
L
Len Brown 已提交
1237
	int i;
L
Linus Torvalds 已提交
1238 1239 1240 1241


	if (!dev || !dev->brightness) {
		seq_printf(seq, "<not supported>\n");
1242
		return 0;
L
Linus Torvalds 已提交
1243 1244 1245
	}

	seq_printf(seq, "levels: ");
1246
	for (i = 2; i < dev->brightness->count; i++)
L
Linus Torvalds 已提交
1247 1248 1249
		seq_printf(seq, " %d", dev->brightness->levels[i]);
	seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr);

1250
	return 0;
L
Linus Torvalds 已提交
1251 1252 1253
}

static int
L
Len Brown 已提交
1254
acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1255 1256 1257 1258 1259 1260
{
	return single_open(file, acpi_video_device_brightness_seq_show,
			   PDE(inode)->data);
}

static ssize_t
L
Len Brown 已提交
1261 1262 1263
acpi_video_device_write_brightness(struct file *file,
				   const char __user * buffer,
				   size_t count, loff_t * data)
L
Linus Torvalds 已提交
1264
{
1265 1266
	struct seq_file *m = file->private_data;
	struct acpi_video_device *dev = m->private;
1267
	char str[5] = { 0 };
L
Len Brown 已提交
1268 1269
	unsigned int level = 0;
	int i;
L
Linus Torvalds 已提交
1270 1271


1272
	if (!dev || !dev->brightness || count >= sizeof(str))
1273
		return -EINVAL;
L
Linus Torvalds 已提交
1274 1275

	if (copy_from_user(str, buffer, count))
1276
		return -EFAULT;
L
Linus Torvalds 已提交
1277 1278 1279

	str[count] = 0;
	level = simple_strtoul(str, NULL, 0);
L
Len Brown 已提交
1280

L
Linus Torvalds 已提交
1281
	if (level > 100)
1282
		return -EFAULT;
L
Linus Torvalds 已提交
1283

1284
	/* validate through the list of available levels */
1285
	for (i = 2; i < dev->brightness->count; i++)
L
Linus Torvalds 已提交
1286
		if (level == dev->brightness->levels[i]) {
1287 1288
			if (!acpi_video_device_lcd_set_level(dev, level))
				return count;
L
Linus Torvalds 已提交
1289 1290 1291
			break;
		}

1292
	return -EINVAL;
L
Linus Torvalds 已提交
1293 1294
}

L
Len Brown 已提交
1295
static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1296
{
1297
	struct acpi_video_device *dev = seq->private;
L
Len Brown 已提交
1298 1299 1300
	int status;
	int i;
	union acpi_object *edid = NULL;
L
Linus Torvalds 已提交
1301 1302 1303 1304 1305


	if (!dev)
		goto out;

L
Len Brown 已提交
1306
	status = acpi_video_device_EDID(dev, &edid, 128);
L
Linus Torvalds 已提交
1307
	if (ACPI_FAILURE(status)) {
L
Len Brown 已提交
1308
		status = acpi_video_device_EDID(dev, &edid, 256);
L
Linus Torvalds 已提交
1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319
	}

	if (ACPI_FAILURE(status)) {
		goto out;
	}

	if (edid && edid->type == ACPI_TYPE_BUFFER) {
		for (i = 0; i < edid->buffer.length; i++)
			seq_putc(seq, edid->buffer.pointer[i]);
	}

L
Len Brown 已提交
1320
      out:
L
Linus Torvalds 已提交
1321 1322 1323 1324 1325
	if (!edid)
		seq_printf(seq, "<not supported>\n");
	else
		kfree(edid);

1326
	return 0;
L
Linus Torvalds 已提交
1327 1328 1329
}

static int
L
Len Brown 已提交
1330
acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1331 1332 1333 1334 1335
{
	return single_open(file, acpi_video_device_EDID_seq_show,
			   PDE(inode)->data);
}

L
Len Brown 已提交
1336
static int acpi_video_device_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1337
{
1338
	struct proc_dir_entry *entry, *device_dir;
L
Linus Torvalds 已提交
1339 1340
	struct acpi_video_device *vid_dev;

1341
	vid_dev = acpi_driver_data(device);
L
Linus Torvalds 已提交
1342
	if (!vid_dev)
1343
		return -ENODEV;
L
Linus Torvalds 已提交
1344

1345 1346 1347 1348 1349
	device_dir = proc_mkdir(acpi_device_bid(device),
				vid_dev->video->dir);
	if (!device_dir)
		return -ENOMEM;

L
Linus Torvalds 已提交
1350
	/* 'info' [R] */
1351
	entry = proc_create_data("info", S_IRUGO, device_dir,
1352
			&acpi_video_device_info_fops, acpi_driver_data(device));
L
Linus Torvalds 已提交
1353
	if (!entry)
1354
		goto err_remove_dir;
L
Linus Torvalds 已提交
1355 1356

	/* 'state' [R/W] */
1357
	entry = proc_create_data("state", S_IFREG | S_IRUGO | S_IWUSR,
1358
				 device_dir,
1359 1360
				 &acpi_video_device_state_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1361
	if (!entry)
1362
		goto err_remove_info;
L
Linus Torvalds 已提交
1363 1364

	/* 'brightness' [R/W] */
1365
	entry = proc_create_data("brightness", S_IFREG | S_IRUGO | S_IWUSR,
1366
				 device_dir,
1367 1368
				 &acpi_video_device_brightness_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1369
	if (!entry)
1370
		goto err_remove_state;
L
Linus Torvalds 已提交
1371 1372

	/* 'EDID' [R] */
1373
	entry = proc_create_data("EDID", S_IRUGO, device_dir,
1374 1375
				 &acpi_video_device_EDID_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1376
	if (!entry)
1377
		goto err_remove_brightness;
L
Linus Torvalds 已提交
1378

1379 1380
	acpi_device_dir(device) = device_dir;

1381
	return 0;
1382 1383 1384 1385 1386 1387 1388 1389 1390 1391

 err_remove_brightness:
	remove_proc_entry("brightness", device_dir);
 err_remove_state:
	remove_proc_entry("state", device_dir);
 err_remove_info:
	remove_proc_entry("info", device_dir);
 err_remove_dir:
	remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
	return -ENOMEM;
L
Linus Torvalds 已提交
1392 1393
}

L
Len Brown 已提交
1394
static int acpi_video_device_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1395 1396
{
	struct acpi_video_device *vid_dev;
1397
	struct proc_dir_entry *device_dir;
L
Linus Torvalds 已提交
1398

1399
	vid_dev = acpi_driver_data(device);
L
Linus Torvalds 已提交
1400
	if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
1401
		return -ENODEV;
L
Linus Torvalds 已提交
1402

1403 1404 1405 1406 1407 1408
	device_dir = acpi_device_dir(device);
	if (device_dir) {
		remove_proc_entry("info", device_dir);
		remove_proc_entry("state", device_dir);
		remove_proc_entry("brightness", device_dir);
		remove_proc_entry("EDID", device_dir);
L
Len Brown 已提交
1409
		remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
L
Linus Torvalds 已提交
1410 1411 1412
		acpi_device_dir(device) = NULL;
	}

1413
	return 0;
L
Linus Torvalds 已提交
1414 1415 1416
}

/* video bus */
L
Len Brown 已提交
1417
static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1418
{
1419
	struct acpi_video_bus *video = seq->private;
L
Linus Torvalds 已提交
1420 1421 1422 1423 1424 1425


	if (!video)
		goto end;

	seq_printf(seq, "Switching heads:              %s\n",
L
Len Brown 已提交
1426
		   video->flags.multihead ? "yes" : "no");
L
Linus Torvalds 已提交
1427
	seq_printf(seq, "Video ROM:                    %s\n",
L
Len Brown 已提交
1428
		   video->flags.rom ? "yes" : "no");
L
Linus Torvalds 已提交
1429
	seq_printf(seq, "Device to be POSTed on boot:  %s\n",
L
Len Brown 已提交
1430
		   video->flags.post ? "yes" : "no");
L
Linus Torvalds 已提交
1431

L
Len Brown 已提交
1432
      end:
1433
	return 0;
L
Linus Torvalds 已提交
1434 1435
}

L
Len Brown 已提交
1436
static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1437
{
L
Len Brown 已提交
1438 1439
	return single_open(file, acpi_video_bus_info_seq_show,
			   PDE(inode)->data);
L
Linus Torvalds 已提交
1440 1441
}

L
Len Brown 已提交
1442
static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1443
{
1444
	struct acpi_video_bus *video = seq->private;
L
Linus Torvalds 已提交
1445 1446 1447 1448 1449


	if (!video)
		goto end;

1450
	printk(KERN_INFO PREFIX "Please implement %s\n", __func__);
L
Linus Torvalds 已提交
1451 1452
	seq_printf(seq, "<TODO>\n");

L
Len Brown 已提交
1453
      end:
1454
	return 0;
L
Linus Torvalds 已提交
1455 1456
}

L
Len Brown 已提交
1457
static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1458 1459 1460 1461
{
	return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data);
}

1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474
static int
acpi_video_bus_POST_options(struct acpi_video_bus *video,
			    unsigned long long *options)
{
	int status;

	status = acpi_evaluate_integer(video->device->handle, "_VPO",
				       NULL, options);
	*options &= 3;

	return status;
}

L
Len Brown 已提交
1475
static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1476
{
1477
	struct acpi_video_bus *video = seq->private;
1478
	unsigned long long options;
L
Len Brown 已提交
1479
	int status;
L
Linus Torvalds 已提交
1480 1481 1482 1483 1484 1485 1486 1487


	if (!video)
		goto end;

	status = acpi_video_bus_POST_options(video, &options);
	if (ACPI_SUCCESS(status)) {
		if (!(options & 1)) {
L
Len Brown 已提交
1488 1489 1490
			printk(KERN_WARNING PREFIX
			       "The motherboard VGA device is not listed as a possible POST device.\n");
			printk(KERN_WARNING PREFIX
1491
			       "This indicates a BIOS bug. Please contact the manufacturer.\n");
L
Linus Torvalds 已提交
1492
		}
1493
		printk(KERN_WARNING "%llx\n", options);
1494
		seq_printf(seq, "can POST: <integrated video>");
L
Linus Torvalds 已提交
1495 1496 1497 1498 1499 1500 1501
		if (options & 2)
			seq_printf(seq, " <PCI video>");
		if (options & 4)
			seq_printf(seq, " <AGP video>");
		seq_putc(seq, '\n');
	} else
		seq_printf(seq, "<not supported>\n");
L
Len Brown 已提交
1502
      end:
1503
	return 0;
L
Linus Torvalds 已提交
1504 1505 1506
}

static int
L
Len Brown 已提交
1507
acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1508
{
L
Len Brown 已提交
1509 1510
	return single_open(file, acpi_video_bus_POST_info_seq_show,
			   PDE(inode)->data);
L
Linus Torvalds 已提交
1511 1512
}

1513 1514 1515 1516 1517 1518 1519 1520 1521 1522
static int
acpi_video_bus_get_POST(struct acpi_video_bus *video, unsigned long long *id)
{
	int status;

	status = acpi_evaluate_integer(video->device->handle, "_GPD", NULL, id);

	return status;
}

L
Len Brown 已提交
1523
static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1524
{
1525
	struct acpi_video_bus *video = seq->private;
L
Len Brown 已提交
1526
	int status;
1527
	unsigned long long id;
L
Linus Torvalds 已提交
1528 1529 1530 1531 1532


	if (!video)
		goto end;

L
Len Brown 已提交
1533
	status = acpi_video_bus_get_POST(video, &id);
L
Linus Torvalds 已提交
1534 1535 1536 1537
	if (!ACPI_SUCCESS(status)) {
		seq_printf(seq, "<not supported>\n");
		goto end;
	}
1538
	seq_printf(seq, "device POSTed is <%s>\n", device_decode[id & 3]);
L
Linus Torvalds 已提交
1539

L
Len Brown 已提交
1540
      end:
1541
	return 0;
L
Linus Torvalds 已提交
1542 1543
}

L
Len Brown 已提交
1544
static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1545
{
1546
	struct acpi_video_bus *video = seq->private;
L
Linus Torvalds 已提交
1547 1548


L
Len Brown 已提交
1549
	seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting);
L
Linus Torvalds 已提交
1550

1551
	return 0;
L
Linus Torvalds 已提交
1552 1553
}

L
Len Brown 已提交
1554
static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1555
{
L
Len Brown 已提交
1556 1557
	return single_open(file, acpi_video_bus_POST_seq_show,
			   PDE(inode)->data);
L
Linus Torvalds 已提交
1558 1559
}

L
Len Brown 已提交
1560
static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1561 1562 1563 1564
{
	return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data);
}

1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583
static int
acpi_video_bus_set_POST(struct acpi_video_bus *video, unsigned long option)
{
	int status;
	unsigned long long tmp;
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };


	arg0.integer.value = option;

	status = acpi_evaluate_integer(video->device->handle, "_SPD",
				       &args, &tmp);
	if (ACPI_SUCCESS(status))
		status = tmp ? (-EINVAL) : (AE_OK);

	return status;
}

L
Linus Torvalds 已提交
1584
static ssize_t
L
Len Brown 已提交
1585 1586 1587
acpi_video_bus_write_POST(struct file *file,
			  const char __user * buffer,
			  size_t count, loff_t * data)
L
Linus Torvalds 已提交
1588
{
L
Len Brown 已提交
1589
	int status;
1590 1591
	struct seq_file *m = file->private_data;
	struct acpi_video_bus *video = m->private;
L
Len Brown 已提交
1592
	char str[12] = { 0 };
1593
	unsigned long long opt, options;
L
Linus Torvalds 已提交
1594 1595


1596
	if (!video || count >= sizeof(str))
1597
		return -EINVAL;
L
Linus Torvalds 已提交
1598 1599 1600

	status = acpi_video_bus_POST_options(video, &options);
	if (!ACPI_SUCCESS(status))
1601
		return -EINVAL;
L
Linus Torvalds 已提交
1602 1603

	if (copy_from_user(str, buffer, count))
1604
		return -EFAULT;
L
Linus Torvalds 已提交
1605 1606 1607 1608

	str[count] = 0;
	opt = strtoul(str, NULL, 0);
	if (opt > 3)
1609
		return -EFAULT;
L
Linus Torvalds 已提交
1610

1611
	/* just in case an OEM 'forgot' the motherboard... */
L
Linus Torvalds 已提交
1612 1613 1614
	options |= 1;

	if (options & (1ul << opt)) {
L
Len Brown 已提交
1615
		status = acpi_video_bus_set_POST(video, opt);
L
Linus Torvalds 已提交
1616
		if (!ACPI_SUCCESS(status))
1617
			return -EFAULT;
L
Linus Torvalds 已提交
1618 1619 1620

	}

1621
	return count;
L
Linus Torvalds 已提交
1622 1623 1624
}

static ssize_t
L
Len Brown 已提交
1625 1626 1627
acpi_video_bus_write_DOS(struct file *file,
			 const char __user * buffer,
			 size_t count, loff_t * data)
L
Linus Torvalds 已提交
1628
{
L
Len Brown 已提交
1629
	int status;
1630 1631
	struct seq_file *m = file->private_data;
	struct acpi_video_bus *video = m->private;
L
Len Brown 已提交
1632 1633
	char str[12] = { 0 };
	unsigned long opt;
L
Linus Torvalds 已提交
1634 1635


1636
	if (!video || count >= sizeof(str))
1637
		return -EINVAL;
L
Linus Torvalds 已提交
1638 1639

	if (copy_from_user(str, buffer, count))
1640
		return -EFAULT;
L
Linus Torvalds 已提交
1641 1642 1643 1644

	str[count] = 0;
	opt = strtoul(str, NULL, 0);
	if (opt > 7)
1645
		return -EFAULT;
L
Linus Torvalds 已提交
1646

L
Len Brown 已提交
1647
	status = acpi_video_bus_DOS(video, opt & 0x3, (opt & 0x4) >> 2);
L
Linus Torvalds 已提交
1648 1649

	if (!ACPI_SUCCESS(status))
1650
		return -EFAULT;
L
Linus Torvalds 已提交
1651

1652
	return count;
L
Linus Torvalds 已提交
1653 1654
}

L
Len Brown 已提交
1655
static int acpi_video_bus_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1656
{
1657 1658 1659
	struct acpi_video_bus *video = acpi_driver_data(device);
	struct proc_dir_entry *device_dir;
	struct proc_dir_entry *entry;
L
Linus Torvalds 已提交
1660

1661 1662 1663
	device_dir = proc_mkdir(acpi_device_bid(device), acpi_video_dir);
	if (!device_dir)
		return -ENOMEM;
L
Linus Torvalds 已提交
1664 1665

	/* 'info' [R] */
1666
	entry = proc_create_data("info", S_IRUGO, device_dir,
1667 1668
				 &acpi_video_bus_info_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1669
	if (!entry)
1670
		goto err_remove_dir;
L
Linus Torvalds 已提交
1671 1672

	/* 'ROM' [R] */
1673
	entry = proc_create_data("ROM", S_IRUGO, device_dir,
1674 1675
				 &acpi_video_bus_ROM_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1676
	if (!entry)
1677
		goto err_remove_info;
L
Linus Torvalds 已提交
1678 1679

	/* 'POST_info' [R] */
1680
	entry = proc_create_data("POST_info", S_IRUGO, device_dir,
1681 1682
				 &acpi_video_bus_POST_info_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1683
	if (!entry)
1684
		goto err_remove_rom;
L
Linus Torvalds 已提交
1685 1686

	/* 'POST' [R/W] */
1687
	entry = proc_create_data("POST", S_IFREG | S_IRUGO | S_IWUSR,
1688
				 device_dir,
1689 1690
				 &acpi_video_bus_POST_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1691
	if (!entry)
1692
		goto err_remove_post_info;
L
Linus Torvalds 已提交
1693 1694

	/* 'DOS' [R/W] */
1695
	entry = proc_create_data("DOS", S_IFREG | S_IRUGO | S_IWUSR,
1696
				 device_dir,
1697 1698
				 &acpi_video_bus_DOS_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1699
	if (!entry)
1700
		goto err_remove_post;
L
Linus Torvalds 已提交
1701

1702
	video->dir = acpi_device_dir(device) = device_dir;
1703
	return 0;
1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715

 err_remove_post:
	remove_proc_entry("POST", device_dir);
 err_remove_post_info:
	remove_proc_entry("POST_info", device_dir);
 err_remove_rom:
	remove_proc_entry("ROM", device_dir);
 err_remove_info:
	remove_proc_entry("info", device_dir);
 err_remove_dir:
	remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
	return -ENOMEM;
L
Linus Torvalds 已提交
1716 1717
}

L
Len Brown 已提交
1718
static int acpi_video_bus_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1719
{
1720 1721 1722 1723 1724 1725 1726 1727
	struct proc_dir_entry *device_dir = acpi_device_dir(device);

	if (device_dir) {
		remove_proc_entry("info", device_dir);
		remove_proc_entry("ROM", device_dir);
		remove_proc_entry("POST_info", device_dir);
		remove_proc_entry("POST", device_dir);
		remove_proc_entry("DOS", device_dir);
L
Len Brown 已提交
1728
		remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
L
Linus Torvalds 已提交
1729 1730 1731
		acpi_device_dir(device) = NULL;
	}

1732
	return 0;
L
Linus Torvalds 已提交
1733
}
1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751
#else
static inline int acpi_video_device_add_fs(struct acpi_device *device)
{
	return 0;
}
static inline int acpi_video_device_remove_fs(struct acpi_device *device)
{
	return 0;
}
static inline int acpi_video_bus_add_fs(struct acpi_device *device)
{
	return 0;
}
static inline int acpi_video_bus_remove_fs(struct acpi_device *device)
{
	return 0;
}
#endif /* CONFIG_ACPI_PROCFS */
L
Linus Torvalds 已提交
1752 1753 1754 1755 1756 1757

/* --------------------------------------------------------------------------
                                 Driver Interface
   -------------------------------------------------------------------------- */

/* device interface */
1758 1759 1760
static struct acpi_video_device_attrib*
acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id)
{
1761 1762 1763 1764 1765 1766 1767 1768
	struct acpi_video_enumerated_device *ids;
	int i;

	for (i = 0; i < video->attached_count; i++) {
		ids = &video->attached_array[i];
		if ((ids->value.int_val & 0xffff) == device_id)
			return &ids->value.attrib;
	}
1769 1770 1771

	return NULL;
}
L
Linus Torvalds 已提交
1772

1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788
static int
acpi_video_get_device_type(struct acpi_video_bus *video,
			   unsigned long device_id)
{
	struct acpi_video_enumerated_device *ids;
	int i;

	for (i = 0; i < video->attached_count; i++) {
		ids = &video->attached_array[i];
		if ((ids->value.int_val & 0xffff) == device_id)
			return ids->value.int_val;
	}

	return 0;
}

L
Linus Torvalds 已提交
1789
static int
L
Len Brown 已提交
1790 1791
acpi_video_bus_get_one_device(struct acpi_device *device,
			      struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1792
{
1793
	unsigned long long device_id;
1794
	int status, device_type;
L
Len Brown 已提交
1795
	struct acpi_video_device *data;
1796
	struct acpi_video_device_attrib* attribute;
L
Linus Torvalds 已提交
1797 1798

	if (!device || !video)
1799
		return -EINVAL;
L
Linus Torvalds 已提交
1800

L
Len Brown 已提交
1801 1802
	status =
	    acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
L
Linus Torvalds 已提交
1803 1804
	if (ACPI_SUCCESS(status)) {

1805
		data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
L
Linus Torvalds 已提交
1806
		if (!data)
1807
			return -ENOMEM;
L
Linus Torvalds 已提交
1808 1809 1810

		strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
		strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
1811
		device->driver_data = data;
L
Linus Torvalds 已提交
1812 1813 1814 1815 1816

		data->device_id = device_id;
		data->video = video;
		data->dev = device;

1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838
		attribute = acpi_video_get_device_attr(video, device_id);

		if((attribute != NULL) && attribute->device_id_scheme) {
			switch (attribute->display_type) {
			case ACPI_VIDEO_DISPLAY_CRT:
				data->flags.crt = 1;
				break;
			case ACPI_VIDEO_DISPLAY_TV:
				data->flags.tvout = 1;
				break;
			case ACPI_VIDEO_DISPLAY_DVI:
				data->flags.dvi = 1;
				break;
			case ACPI_VIDEO_DISPLAY_LCD:
				data->flags.lcd = 1;
				break;
			default:
				data->flags.unknown = 1;
				break;
			}
			if(attribute->bios_can_detect)
				data->flags.bios = 1;
1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857
		} else {
			/* Check for legacy IDs */
			device_type = acpi_video_get_device_type(video,
								 device_id);
			/* Ignore bits 16 and 18-20 */
			switch (device_type & 0xffe2ffff) {
			case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR:
				data->flags.crt = 1;
				break;
			case ACPI_VIDEO_DISPLAY_LEGACY_PANEL:
				data->flags.lcd = 1;
				break;
			case ACPI_VIDEO_DISPLAY_LEGACY_TV:
				data->flags.tvout = 1;
				break;
			default:
				data->flags.unknown = 1;
			}
		}
L
Len Brown 已提交
1858

L
Linus Torvalds 已提交
1859 1860 1861
		acpi_video_device_bind(video, data);
		acpi_video_device_find_cap(data);

1862
		status = acpi_install_notify_handler(device->handle,
L
Len Brown 已提交
1863 1864 1865
						     ACPI_DEVICE_NOTIFY,
						     acpi_video_device_notify,
						     data);
L
Linus Torvalds 已提交
1866
		if (ACPI_FAILURE(status)) {
1867 1868
			printk(KERN_ERR PREFIX
					  "Error installing notify handler\n");
1869 1870 1871 1872 1873
			if(data->brightness)
				kfree(data->brightness->levels);
			kfree(data->brightness);
			kfree(data);
			return -ENODEV;
L
Linus Torvalds 已提交
1874 1875
		}

1876
		mutex_lock(&video->device_list_lock);
L
Linus Torvalds 已提交
1877
		list_add_tail(&data->entry, &video->video_device_list);
1878
		mutex_unlock(&video->device_list_lock);
L
Linus Torvalds 已提交
1879 1880 1881

		acpi_video_device_add_fs(device);

1882
		return 0;
L
Linus Torvalds 已提交
1883 1884
	}

1885
	return -ENOENT;
L
Linus Torvalds 已提交
1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897
}

/*
 *  Arg:
 *  	video	: video bus device 
 *
 *  Return:
 *  	none
 *  
 *  Enumerate the video device list of the video bus, 
 *  bind the ids with the corresponding video devices
 *  under the video bus.
L
Len Brown 已提交
1898
 */
L
Linus Torvalds 已提交
1899

L
Len Brown 已提交
1900
static void acpi_video_device_rebind(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1901
{
1902 1903
	struct acpi_video_device *dev;

1904
	mutex_lock(&video->device_list_lock);
1905 1906

	list_for_each_entry(dev, &video->video_device_list, entry)
L
Len Brown 已提交
1907
		acpi_video_device_bind(video, dev);
1908

1909
	mutex_unlock(&video->device_list_lock);
L
Linus Torvalds 已提交
1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922
}

/*
 *  Arg:
 *  	video	: video bus device 
 *  	device	: video output device under the video 
 *  		bus
 *
 *  Return:
 *  	none
 *  
 *  Bind the ids with the corresponding video devices
 *  under the video bus.
L
Len Brown 已提交
1923
 */
L
Linus Torvalds 已提交
1924 1925

static void
L
Len Brown 已提交
1926 1927
acpi_video_device_bind(struct acpi_video_bus *video,
		       struct acpi_video_device *device)
L
Linus Torvalds 已提交
1928
{
1929
	struct acpi_video_enumerated_device *ids;
L
Len Brown 已提交
1930
	int i;
L
Linus Torvalds 已提交
1931

1932 1933 1934 1935
	for (i = 0; i < video->attached_count; i++) {
		ids = &video->attached_array[i];
		if (device->device_id == (ids->value.int_val & 0xffff)) {
			ids->bind_info = device;
L
Linus Torvalds 已提交
1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949
			ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i));
		}
	}
}

/*
 *  Arg:
 *  	video	: video bus device 
 *
 *  Return:
 *  	< 0	: error
 *  
 *  Call _DOD to enumerate all devices attached to display adapter
 *
L
Len Brown 已提交
1950
 */
L
Linus Torvalds 已提交
1951 1952 1953

static int acpi_video_device_enumerate(struct acpi_video_bus *video)
{
L
Len Brown 已提交
1954 1955 1956
	int status;
	int count;
	int i;
1957
	struct acpi_video_enumerated_device *active_list;
L
Len Brown 已提交
1958 1959 1960
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *dod = NULL;
	union acpi_object *obj;
L
Linus Torvalds 已提交
1961

1962
	status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
L
Linus Torvalds 已提交
1963
	if (!ACPI_SUCCESS(status)) {
1964
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD"));
1965
		return status;
L
Linus Torvalds 已提交
1966 1967
	}

1968
	dod = buffer.pointer;
L
Linus Torvalds 已提交
1969
	if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
1970
		ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data"));
L
Linus Torvalds 已提交
1971 1972 1973 1974 1975
		status = -EFAULT;
		goto out;
	}

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n",
L
Len Brown 已提交
1976
			  dod->package.count));
L
Linus Torvalds 已提交
1977

1978 1979 1980 1981
	active_list = kcalloc(1 + dod->package.count,
			      sizeof(struct acpi_video_enumerated_device),
			      GFP_KERNEL);
	if (!active_list) {
L
Linus Torvalds 已提交
1982 1983 1984 1985 1986 1987
		status = -ENOMEM;
		goto out;
	}

	count = 0;
	for (i = 0; i < dod->package.count; i++) {
1988
		obj = &dod->package.elements[i];
L
Linus Torvalds 已提交
1989 1990

		if (obj->type != ACPI_TYPE_INTEGER) {
1991 1992 1993
			printk(KERN_ERR PREFIX
				"Invalid _DOD data in element %d\n", i);
			continue;
L
Linus Torvalds 已提交
1994
		}
1995 1996 1997

		active_list[count].value.int_val = obj->integer.value;
		active_list[count].bind_info = NULL;
L
Len Brown 已提交
1998 1999
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i,
				  (int)obj->integer.value));
L
Linus Torvalds 已提交
2000 2001 2002
		count++;
	}

2003
	kfree(video->attached_array);
L
Len Brown 已提交
2004

2005
	video->attached_array = active_list;
L
Linus Torvalds 已提交
2006
	video->attached_count = count;
2007 2008

 out:
2009
	kfree(buffer.pointer);
2010
	return status;
L
Linus Torvalds 已提交
2011 2012
}

L
Len Brown 已提交
2013 2014 2015
static int
acpi_video_get_next_level(struct acpi_video_device *device,
			  u32 level_current, u32 event)
L
Linus Torvalds 已提交
2016
{
2017
	int min, max, min_above, max_below, i, l, delta = 255;
2018 2019
	max = max_below = 0;
	min = min_above = 255;
2020
	/* Find closest level to level_current */
2021
	for (i = 2; i < device->brightness->count; i++) {
2022 2023 2024 2025 2026 2027 2028 2029 2030
		l = device->brightness->levels[i];
		if (abs(l - level_current) < abs(delta)) {
			delta = l - level_current;
			if (!delta)
				break;
		}
	}
	/* Ajust level_current to closest available level */
	level_current += delta;
2031
	for (i = 2; i < device->brightness->count; i++) {
2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055
		l = device->brightness->levels[i];
		if (l < min)
			min = l;
		if (l > max)
			max = l;
		if (l < min_above && l > level_current)
			min_above = l;
		if (l > max_below && l < level_current)
			max_below = l;
	}

	switch (event) {
	case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:
		return (level_current < max) ? min_above : min;
	case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:
		return (level_current < max) ? min_above : max;
	case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:
		return (level_current > min) ? max_below : min;
	case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:
	case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:
		return 0;
	default:
		return level_current;
	}
L
Linus Torvalds 已提交
2056 2057
}

2058
static int
L
Len Brown 已提交
2059
acpi_video_switch_brightness(struct acpi_video_device *device, int event)
L
Linus Torvalds 已提交
2060
{
2061
	unsigned long long level_current, level_next;
2062 2063
	int result = -EINVAL;

2064 2065 2066 2067
	/* no warning message if acpi_backlight=vendor is used */
	if (!acpi_video_backlight_support())
		return 0;

2068
	if (!device->brightness)
2069 2070 2071
		goto out;

	result = acpi_video_device_lcd_get_level_current(device,
2072
							 &level_current, 0);
2073 2074 2075
	if (result)
		goto out;

L
Linus Torvalds 已提交
2076
	level_next = acpi_video_get_next_level(device, level_current, event);
2077

2078
	result = acpi_video_device_lcd_set_level(device, level_next);
2079

2080 2081 2082 2083
	if (!result)
		backlight_force_update(device->backlight,
				       BACKLIGHT_UPDATE_HOTKEY);

2084 2085 2086 2087 2088
out:
	if (result)
		printk(KERN_ERR PREFIX "Failed to switch the brightness\n");

	return result;
L
Linus Torvalds 已提交
2089 2090
}

2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155
int acpi_video_get_edid(struct acpi_device *device, int type, int device_id,
			void **edid)
{
	struct acpi_video_bus *video;
	struct acpi_video_device *video_device;
	union acpi_object *buffer = NULL;
	acpi_status status;
	int i, length;

	if (!device || !acpi_driver_data(device))
		return -EINVAL;

	video = acpi_driver_data(device);

	for (i = 0; i < video->attached_count; i++) {
		video_device = video->attached_array[i].bind_info;
		length = 256;

		if (!video_device)
			continue;

		if (type) {
			switch (type) {
			case ACPI_VIDEO_DISPLAY_CRT:
				if (!video_device->flags.crt)
					continue;
				break;
			case ACPI_VIDEO_DISPLAY_TV:
				if (!video_device->flags.tvout)
					continue;
				break;
			case ACPI_VIDEO_DISPLAY_DVI:
				if (!video_device->flags.dvi)
					continue;
				break;
			case ACPI_VIDEO_DISPLAY_LCD:
				if (!video_device->flags.lcd)
					continue;
				break;
			}
		} else if (video_device->device_id != device_id) {
			continue;
		}

		status = acpi_video_device_EDID(video_device, &buffer, length);

		if (ACPI_FAILURE(status) || !buffer ||
		    buffer->type != ACPI_TYPE_BUFFER) {
			length = 128;
			status = acpi_video_device_EDID(video_device, &buffer,
							length);
			if (ACPI_FAILURE(status) || !buffer ||
			    buffer->type != ACPI_TYPE_BUFFER) {
				continue;
			}
		}

		*edid = buffer->buffer.pointer;
		return length;
	}

	return -ENODEV;
}
EXPORT_SYMBOL(acpi_video_get_edid);

L
Linus Torvalds 已提交
2156
static int
L
Len Brown 已提交
2157 2158
acpi_video_bus_get_devices(struct acpi_video_bus *video,
			   struct acpi_device *device)
L
Linus Torvalds 已提交
2159
{
L
Len Brown 已提交
2160
	int status = 0;
2161
	struct acpi_device *dev;
L
Linus Torvalds 已提交
2162 2163 2164

	acpi_video_device_enumerate(video);

2165
	list_for_each_entry(dev, &device->children, node) {
L
Linus Torvalds 已提交
2166 2167 2168

		status = acpi_video_bus_get_one_device(dev, video);
		if (ACPI_FAILURE(status)) {
2169 2170
			printk(KERN_WARNING PREFIX
					"Cant attach device");
L
Linus Torvalds 已提交
2171 2172 2173
			continue;
		}
	}
2174
	return status;
L
Linus Torvalds 已提交
2175 2176
}

L
Len Brown 已提交
2177
static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
L
Linus Torvalds 已提交
2178
{
2179
	acpi_status status;
L
Linus Torvalds 已提交
2180 2181 2182 2183
	struct acpi_video_bus *video;


	if (!device || !device->video)
2184
		return -ENOENT;
L
Linus Torvalds 已提交
2185 2186 2187 2188 2189

	video = device->video;

	acpi_video_device_remove_fs(device->dev);

2190
	status = acpi_remove_notify_handler(device->dev->handle,
L
Len Brown 已提交
2191 2192
					    ACPI_DEVICE_NOTIFY,
					    acpi_video_device_notify);
2193 2194 2195 2196 2197
	if (device->backlight) {
		sysfs_remove_link(&device->backlight->dev.kobj, "device");
		backlight_device_unregister(device->backlight);
		device->backlight = NULL;
	}
2198
	if (device->cooling_dev) {
2199 2200
		sysfs_remove_link(&device->dev->dev.kobj,
				  "thermal_cooling");
2201
		sysfs_remove_link(&device->cooling_dev->device.kobj,
2202
				  "device");
2203 2204
		thermal_cooling_device_unregister(device->cooling_dev);
		device->cooling_dev = NULL;
2205
	}
2206
	video_output_unregister(device->output_dev);
2207

2208
	return 0;
L
Linus Torvalds 已提交
2209 2210
}

L
Len Brown 已提交
2211
static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
2212
{
L
Len Brown 已提交
2213
	int status;
2214
	struct acpi_video_device *dev, *next;
L
Linus Torvalds 已提交
2215

2216
	mutex_lock(&video->device_list_lock);
L
Linus Torvalds 已提交
2217

2218
	list_for_each_entry_safe(dev, next, &video->video_device_list, entry) {
L
Linus Torvalds 已提交
2219

2220
		status = acpi_video_bus_put_one_device(dev);
L
Len Brown 已提交
2221 2222 2223
		if (ACPI_FAILURE(status))
			printk(KERN_WARNING PREFIX
			       "hhuuhhuu bug in acpi video driver.\n");
L
Linus Torvalds 已提交
2224

2225 2226 2227 2228 2229 2230
		if (dev->brightness) {
			kfree(dev->brightness->levels);
			kfree(dev->brightness);
		}
		list_del(&dev->entry);
		kfree(dev);
L
Linus Torvalds 已提交
2231 2232
	}

2233
	mutex_unlock(&video->device_list_lock);
2234

2235
	return 0;
L
Linus Torvalds 已提交
2236 2237 2238 2239
}

/* acpi_video interface */

L
Len Brown 已提交
2240
static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
2241
{
2242
	return acpi_video_bus_DOS(video, 0, 0);
L
Linus Torvalds 已提交
2243 2244
}

L
Len Brown 已提交
2245
static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
2246 2247 2248 2249
{
	return acpi_video_bus_DOS(video, 0, 1);
}

2250
static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
L
Linus Torvalds 已提交
2251
{
2252
	struct acpi_video_bus *video = acpi_driver_data(device);
2253
	struct input_dev *input;
2254
	int keycode = 0;
2255

L
Linus Torvalds 已提交
2256
	if (!video)
2257
		return;
L
Linus Torvalds 已提交
2258

2259
	input = video->input;
L
Linus Torvalds 已提交
2260 2261

	switch (event) {
2262
	case ACPI_VIDEO_NOTIFY_SWITCH:	/* User requested a switch,
L
Linus Torvalds 已提交
2263
					 * most likely via hotkey. */
2264
		acpi_bus_generate_proc_event(device, event, 0);
2265
		keycode = KEY_SWITCHVIDEOMODE;
L
Linus Torvalds 已提交
2266 2267
		break;

2268
	case ACPI_VIDEO_NOTIFY_PROBE:	/* User plugged in or removed a video
L
Linus Torvalds 已提交
2269 2270 2271
					 * connector. */
		acpi_video_device_enumerate(video);
		acpi_video_device_rebind(video);
2272
		acpi_bus_generate_proc_event(device, event, 0);
2273
		keycode = KEY_SWITCHVIDEOMODE;
L
Linus Torvalds 已提交
2274 2275
		break;

L
Len Brown 已提交
2276
	case ACPI_VIDEO_NOTIFY_CYCLE:	/* Cycle Display output hotkey pressed. */
L
Len Brown 已提交
2277
		acpi_bus_generate_proc_event(device, event, 0);
2278 2279
		keycode = KEY_SWITCHVIDEOMODE;
		break;
L
Len Brown 已提交
2280
	case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:	/* Next Display output hotkey pressed. */
L
Len Brown 已提交
2281
		acpi_bus_generate_proc_event(device, event, 0);
2282 2283
		keycode = KEY_VIDEO_NEXT;
		break;
L
Len Brown 已提交
2284
	case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:	/* previous Display output hotkey pressed. */
2285
		acpi_bus_generate_proc_event(device, event, 0);
2286
		keycode = KEY_VIDEO_PREV;
L
Linus Torvalds 已提交
2287 2288 2289 2290
		break;

	default:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
2291
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
2292 2293 2294
		break;
	}

2295
	acpi_notifier_call_chain(device, event, 0);
2296 2297 2298 2299 2300 2301 2302

	if (keycode) {
		input_report_key(input, keycode, 1);
		input_sync(input);
		input_report_key(input, keycode, 0);
		input_sync(input);
	}
2303

2304
	return;
L
Linus Torvalds 已提交
2305 2306
}

L
Len Brown 已提交
2307
static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
L
Linus Torvalds 已提交
2308
{
2309
	struct acpi_video_device *video_device = data;
L
Len Brown 已提交
2310
	struct acpi_device *device = NULL;
2311 2312
	struct acpi_video_bus *bus;
	struct input_dev *input;
2313
	int keycode = 0;
L
Linus Torvalds 已提交
2314 2315

	if (!video_device)
2316
		return;
L
Linus Torvalds 已提交
2317

2318
	device = video_device->dev;
2319 2320
	bus = video_device->video;
	input = bus->input;
L
Linus Torvalds 已提交
2321 2322

	switch (event) {
L
Len Brown 已提交
2323
	case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:	/* Cycle brightness */
2324 2325
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
2326
		acpi_bus_generate_proc_event(device, event, 0);
2327 2328
		keycode = KEY_BRIGHTNESS_CYCLE;
		break;
L
Len Brown 已提交
2329
	case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:	/* Increase brightness */
2330 2331
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
L
Len Brown 已提交
2332
		acpi_bus_generate_proc_event(device, event, 0);
2333 2334
		keycode = KEY_BRIGHTNESSUP;
		break;
L
Len Brown 已提交
2335
	case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:	/* Decrease brightness */
2336 2337
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
L
Len Brown 已提交
2338
		acpi_bus_generate_proc_event(device, event, 0);
2339 2340
		keycode = KEY_BRIGHTNESSDOWN;
		break;
L
Len Brown 已提交
2341
	case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:	/* zero brightnesss */
2342 2343
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
L
Len Brown 已提交
2344
		acpi_bus_generate_proc_event(device, event, 0);
2345 2346
		keycode = KEY_BRIGHTNESS_ZERO;
		break;
L
Len Brown 已提交
2347
	case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:	/* display device off */
2348 2349
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
2350
		acpi_bus_generate_proc_event(device, event, 0);
2351
		keycode = KEY_DISPLAY_OFF;
L
Linus Torvalds 已提交
2352 2353 2354
		break;
	default:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
2355
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
2356 2357
		break;
	}
2358

2359
	acpi_notifier_call_chain(device, event, 0);
2360 2361 2362 2363 2364 2365 2366

	if (keycode) {
		input_report_key(input, keycode, 1);
		input_sync(input);
		input_report_key(input, keycode, 0);
		input_sync(input);
	}
2367

2368
	return;
L
Linus Torvalds 已提交
2369 2370
}

2371 2372
static int acpi_video_resume(struct notifier_block *nb,
				unsigned long val, void *ign)
2373 2374 2375 2376 2377
{
	struct acpi_video_bus *video;
	struct acpi_video_device *video_device;
	int i;

2378 2379 2380 2381 2382 2383
	switch (val) {
	case PM_HIBERNATION_PREPARE:
	case PM_SUSPEND_PREPARE:
	case PM_RESTORE_PREPARE:
		return NOTIFY_DONE;
	}
2384

2385 2386 2387
	video = container_of(nb, struct acpi_video_bus, pm_nb);

	dev_info(&video->device->dev, "Restoring backlight state\n");
2388 2389 2390 2391 2392 2393

	for (i = 0; i < video->attached_count; i++) {
		video_device = video->attached_array[i].bind_info;
		if (video_device && video_device->backlight)
			acpi_video_set_brightness(video_device->backlight);
	}
2394 2395

	return NOTIFY_OK;
2396 2397
}

2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418
static acpi_status
acpi_video_bus_match(acpi_handle handle, u32 level, void *context,
			void **return_value)
{
	struct acpi_device *device = context;
	struct acpi_device *sibling;
	int result;

	if (handle == device->handle)
		return AE_CTRL_TERMINATE;

	result = acpi_bus_get_device(handle, &sibling);
	if (result)
		return AE_OK;

	if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME))
			return AE_ALREADY_EXISTS;

	return AE_OK;
}

2419 2420
static int instance;

L
Len Brown 已提交
2421
static int acpi_video_bus_add(struct acpi_device *device)
L
Linus Torvalds 已提交
2422
{
2423
	struct acpi_video_bus *video;
2424
	struct input_dev *input;
2425
	int error;
2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440
	acpi_status status;

	status = acpi_walk_namespace(ACPI_TYPE_DEVICE,
				device->parent->handle, 1,
				acpi_video_bus_match, NULL,
				device, NULL);
	if (status == AE_ALREADY_EXISTS) {
		printk(KERN_WARNING FW_BUG
			"Duplicate ACPI video bus devices for the"
			" same VGA controller, please try module "
			"parameter \"video.allow_duplicates=1\""
			"if the current driver doesn't work.\n");
		if (!allow_duplicates)
			return -ENODEV;
	}
L
Linus Torvalds 已提交
2441

2442
	video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
L
Linus Torvalds 已提交
2443
	if (!video)
2444
		return -ENOMEM;
L
Linus Torvalds 已提交
2445

2446 2447 2448 2449 2450 2451
	/* a hack to fix the duplicate name "VID" problem on T61 */
	if (!strcmp(device->pnp.bus_id, "VID")) {
		if (instance)
			device->pnp.bus_id[3] = '0' + instance;
		instance ++;
	}
2452 2453 2454 2455 2456 2457
	/* a hack to fix the duplicate name "VGA" problem on Pa 3553 */
	if (!strcmp(device->pnp.bus_id, "VGA")) {
		if (instance)
			device->pnp.bus_id[3] = '0' + instance;
		instance++;
	}
2458

2459
	video->device = device;
L
Linus Torvalds 已提交
2460 2461
	strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
	strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
2462
	device->driver_data = video;
L
Linus Torvalds 已提交
2463 2464

	acpi_video_bus_find_cap(video);
2465 2466 2467
	error = acpi_video_bus_check(video);
	if (error)
		goto err_free_video;
L
Linus Torvalds 已提交
2468

2469 2470 2471
	error = acpi_video_bus_add_fs(device);
	if (error)
		goto err_free_video;
L
Linus Torvalds 已提交
2472

2473
	mutex_init(&video->device_list_lock);
L
Linus Torvalds 已提交
2474 2475 2476 2477 2478
	INIT_LIST_HEAD(&video->video_device_list);

	acpi_video_bus_get_devices(video, device);
	acpi_video_bus_start_devices(video);

2479
	video->input = input = input_allocate_device();
2480 2481
	if (!input) {
		error = -ENOMEM;
2482
		goto err_stop_video;
2483
	}
2484 2485 2486 2487 2488 2489 2490 2491

	snprintf(video->phys, sizeof(video->phys),
		"%s/video/input0", acpi_device_hid(video->device));

	input->name = acpi_device_name(video->device);
	input->phys = video->phys;
	input->id.bustype = BUS_HOST;
	input->id.product = 0x06;
2492
	input->dev.parent = &device->dev;
2493 2494 2495 2496 2497 2498 2499 2500 2501 2502
	input->evbit[0] = BIT(EV_KEY);
	set_bit(KEY_SWITCHVIDEOMODE, input->keybit);
	set_bit(KEY_VIDEO_NEXT, input->keybit);
	set_bit(KEY_VIDEO_PREV, input->keybit);
	set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit);
	set_bit(KEY_BRIGHTNESSUP, input->keybit);
	set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
	set_bit(KEY_BRIGHTNESS_ZERO, input->keybit);
	set_bit(KEY_DISPLAY_OFF, input->keybit);

2503 2504 2505
	error = input_register_device(input);
	if (error)
		goto err_free_input_dev;
2506

L
Linus Torvalds 已提交
2507
	printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s  rom: %s  post: %s)\n",
L
Len Brown 已提交
2508 2509 2510 2511
	       ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
	       video->flags.multihead ? "yes" : "no",
	       video->flags.rom ? "yes" : "no",
	       video->flags.post ? "yes" : "no");
L
Linus Torvalds 已提交
2512

2513 2514 2515 2516
	video->pm_nb.notifier_call = acpi_video_resume;
	video->pm_nb.priority = 0;
	register_pm_notifier(&video->pm_nb);

2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527
	return 0;

 err_free_input_dev:
	input_free_device(input);
 err_stop_video:
	acpi_video_bus_stop_devices(video);
	acpi_video_bus_put_devices(video);
	kfree(video->attached_array);
	acpi_video_bus_remove_fs(device);
 err_free_video:
	kfree(video);
2528
	device->driver_data = NULL;
L
Linus Torvalds 已提交
2529

2530
	return error;
L
Linus Torvalds 已提交
2531 2532
}

L
Len Brown 已提交
2533
static int acpi_video_bus_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
2534
{
L
Len Brown 已提交
2535
	struct acpi_video_bus *video = NULL;
L
Linus Torvalds 已提交
2536 2537 2538


	if (!device || !acpi_driver_data(device))
2539
		return -EINVAL;
L
Linus Torvalds 已提交
2540

2541
	video = acpi_driver_data(device);
L
Linus Torvalds 已提交
2542

2543 2544
	unregister_pm_notifier(&video->pm_nb);

L
Linus Torvalds 已提交
2545 2546 2547 2548
	acpi_video_bus_stop_devices(video);
	acpi_video_bus_put_devices(video);
	acpi_video_bus_remove_fs(device);

2549
	input_unregister_device(video->input);
2550
	kfree(video->attached_array);
L
Linus Torvalds 已提交
2551 2552
	kfree(video);

2553
	return 0;
L
Linus Torvalds 已提交
2554 2555
}

2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576
static int __init intel_opregion_present(void)
{
#if defined(CONFIG_DRM_I915) || defined(CONFIG_DRM_I915_MODULE)
	struct pci_dev *dev = NULL;
	u32 address;

	for_each_pci_dev(dev) {
		if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
			continue;
		if (dev->vendor != PCI_VENDOR_ID_INTEL)
			continue;
		pci_read_config_dword(dev, 0xfc, &address);
		if (!address)
			continue;
		return 1;
	}
#endif
	return 0;
}

int acpi_video_register(void)
L
Linus Torvalds 已提交
2577
{
L
Len Brown 已提交
2578
	int result = 0;
2579 2580 2581 2582 2583 2584 2585
	if (register_count) {
		/*
		 * if the function of acpi_video_register is already called,
		 * don't register the acpi_vide_bus again and return no error.
		 */
		return 0;
	}
L
Linus Torvalds 已提交
2586

2587
#ifdef CONFIG_ACPI_PROCFS
L
Linus Torvalds 已提交
2588 2589
	acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
	if (!acpi_video_dir)
2590
		return -ENODEV;
2591
#endif
L
Linus Torvalds 已提交
2592 2593 2594 2595

	result = acpi_bus_register_driver(&acpi_video_bus);
	if (result < 0) {
		remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
2596
		return -ENODEV;
L
Linus Torvalds 已提交
2597 2598
	}

2599 2600 2601 2602 2603 2604
	/*
	 * When the acpi_video_bus is loaded successfully, increase
	 * the counter reference.
	 */
	register_count = 1;

2605
	return 0;
L
Linus Torvalds 已提交
2606
}
2607 2608
EXPORT_SYMBOL(acpi_video_register);

2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619
void acpi_video_unregister(void)
{
	if (!register_count) {
		/*
		 * If the acpi video bus is already unloaded, don't
		 * unload it again and return directly.
		 */
		return;
	}
	acpi_bus_unregister_driver(&acpi_video_bus);

2620
#ifdef CONFIG_ACPI_PROCFS
2621
	remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
2622
#endif
2623 2624 2625 2626 2627 2628 2629

	register_count = 0;

	return;
}
EXPORT_SYMBOL(acpi_video_unregister);

2630 2631 2632 2633 2634 2635 2636 2637 2638
/*
 * This is kind of nasty. Hardware using Intel chipsets may require
 * the video opregion code to be run first in order to initialise
 * state before any ACPI video calls are made. To handle this we defer
 * registration of the video class until the opregion code has run.
 */

static int __init acpi_video_init(void)
{
2639 2640
	dmi_check_system(video_dmi_table);

2641 2642 2643 2644 2645
	if (intel_opregion_present())
		return 0;

	return acpi_video_register();
}
L
Linus Torvalds 已提交
2646

2647
static void __exit acpi_video_exit(void)
L
Linus Torvalds 已提交
2648
{
2649
	acpi_video_unregister();
L
Linus Torvalds 已提交
2650

2651
	return;
L
Linus Torvalds 已提交
2652 2653 2654 2655
}

module_init(acpi_video_init);
module_exit(acpi_video_exit);