video.c 55.6 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>
L
Linus Torvalds 已提交
40 41 42 43 44 45 46 47 48 49 50 51 52 53
#include <asm/uaccess.h>

#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>

#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

54 55 56 57 58
#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 已提交
59

60
#define MAX_NAME_LEN	20
L
Linus Torvalds 已提交
61

62 63 64 65 66
#define ACPI_VIDEO_DISPLAY_CRT	1
#define ACPI_VIDEO_DISPLAY_TV	2
#define ACPI_VIDEO_DISPLAY_DVI	3
#define ACPI_VIDEO_DISPLAY_LCD	4

L
Linus Torvalds 已提交
67
#define _COMPONENT		ACPI_VIDEO_COMPONENT
68
ACPI_MODULE_NAME("video");
L
Linus Torvalds 已提交
69

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

74 75 76
static int brightness_switch_enabled = 1;
module_param(brightness_switch_enabled, bool, 0644);

L
Len Brown 已提交
77 78
static int acpi_video_bus_add(struct acpi_device *device);
static int acpi_video_bus_remove(struct acpi_device *device, int type);
79
static int acpi_video_resume(struct acpi_device *device);
L
Linus Torvalds 已提交
80

81 82 83 84 85 86
static const struct acpi_device_id video_device_ids[] = {
	{ACPI_VIDEO_HID, 0},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, video_device_ids);

L
Linus Torvalds 已提交
87
static struct acpi_driver acpi_video_bus = {
L
Len Brown 已提交
88
	.name = "video",
L
Linus Torvalds 已提交
89
	.class = ACPI_VIDEO_CLASS,
90
	.ids = video_device_ids,
L
Linus Torvalds 已提交
91 92 93
	.ops = {
		.add = acpi_video_bus_add,
		.remove = acpi_video_bus_remove,
94
		.resume = acpi_video_resume,
L
Len Brown 已提交
95
		},
L
Linus Torvalds 已提交
96 97 98
};

struct acpi_video_bus_flags {
L
Len Brown 已提交
99 100 101 102
	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 已提交
103 104 105
};

struct acpi_video_bus_cap {
L
Len Brown 已提交
106 107 108 109 110 111 112
	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 已提交
113 114
};

L
Len Brown 已提交
115 116
struct acpi_video_device_attrib {
	u32 display_index:4;	/* A zero-based instance of the Display */
117
	u32 display_port_attachment:4;	/*This field differentiates the display type */
L
Len Brown 已提交
118
	u32 display_type:4;	/*Describe the specific type in use */
119
	u32 vendor_specific:4;	/*Chipset Vendor Specific */
L
Len Brown 已提交
120 121 122 123 124 125
	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 已提交
126 127 128 129 130
};

struct acpi_video_enumerated_device {
	union {
		u32 int_val;
L
Len Brown 已提交
131
		struct acpi_video_device_attrib attrib;
L
Linus Torvalds 已提交
132 133 134 135 136
	} value;
	struct acpi_video_device *bind_info;
};

struct acpi_video_bus {
137
	struct acpi_device *device;
L
Len Brown 已提交
138
	u8 dos_setting;
L
Linus Torvalds 已提交
139
	struct acpi_video_enumerated_device *attached_array;
L
Len Brown 已提交
140 141
	u8 attached_count;
	struct acpi_video_bus_cap cap;
L
Linus Torvalds 已提交
142
	struct acpi_video_bus_flags flags;
L
Len Brown 已提交
143
	struct list_head video_device_list;
144
	struct mutex device_list_lock;	/* protects video_device_list */
L
Len Brown 已提交
145
	struct proc_dir_entry *dir;
146 147
	struct input_dev *input;
	char phys[32];	/* for input device */
L
Linus Torvalds 已提交
148 149 150
};

struct acpi_video_device_flags {
L
Len Brown 已提交
151 152 153
	u8 crt:1;
	u8 lcd:1;
	u8 tvout:1;
154
	u8 dvi:1;
L
Len Brown 已提交
155 156
	u8 bios:1;
	u8 unknown:1;
157
	u8 reserved:2;
L
Linus Torvalds 已提交
158 159 160
};

struct acpi_video_device_cap {
L
Len Brown 已提交
161 162 163
	u8 _ADR:1;		/*Return the unique ID */
	u8 _BCL:1;		/*Query list of brightness control levels supported */
	u8 _BCM:1;		/*Set the brightness level */
164
	u8 _BQC:1;		/* Get current brightness level */
L
Len Brown 已提交
165 166 167 168
	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 已提交
169 170
};

171 172
struct acpi_video_brightness_flags {
	u8 _BCL_no_ac_battery_levels:1;	/* no AC/Battery levels in _BCL */
173
	u8 _BCL_reversed:1;		/* _BCL package is in a reversed order*/
174 175
};

L
Linus Torvalds 已提交
176
struct acpi_video_device_brightness {
L
Len Brown 已提交
177 178 179
	int curr;
	int count;
	int *levels;
180
	struct acpi_video_brightness_flags flags;
L
Linus Torvalds 已提交
181 182 183
};

struct acpi_video_device {
L
Len Brown 已提交
184 185 186 187 188 189
	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 已提交
190
	struct acpi_video_device_brightness *brightness;
191
	struct backlight_device *backlight;
192
	struct thermal_cooling_device *cdev;
193
	struct output_device *output_dev;
L
Linus Torvalds 已提交
194 195 196 197 198
};

/* bus */
static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file);
static struct file_operations acpi_video_bus_info_fops = {
199
	.owner = THIS_MODULE,
L
Len Brown 已提交
200 201 202 203
	.open = acpi_video_bus_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
204 205 206 207
};

static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file);
static struct file_operations acpi_video_bus_ROM_fops = {
208
	.owner = THIS_MODULE,
L
Len Brown 已提交
209 210 211 212
	.open = acpi_video_bus_ROM_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
213 214
};

L
Len Brown 已提交
215 216
static int acpi_video_bus_POST_info_open_fs(struct inode *inode,
					    struct file *file);
L
Linus Torvalds 已提交
217
static struct file_operations acpi_video_bus_POST_info_fops = {
218
	.owner = THIS_MODULE,
L
Len Brown 已提交
219 220 221 222
	.open = acpi_video_bus_POST_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
223 224 225 226
};

static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file);
static struct file_operations acpi_video_bus_POST_fops = {
227
	.owner = THIS_MODULE,
L
Len Brown 已提交
228 229 230 231
	.open = acpi_video_bus_POST_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
232 233 234 235
};

static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file);
static struct file_operations acpi_video_bus_DOS_fops = {
236
	.owner = THIS_MODULE,
L
Len Brown 已提交
237 238 239 240
	.open = acpi_video_bus_DOS_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
241 242 243
};

/* device */
L
Len Brown 已提交
244 245
static int acpi_video_device_info_open_fs(struct inode *inode,
					  struct file *file);
L
Linus Torvalds 已提交
246
static struct file_operations acpi_video_device_info_fops = {
247
	.owner = THIS_MODULE,
L
Len Brown 已提交
248 249 250 251
	.open = acpi_video_device_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
252 253
};

L
Len Brown 已提交
254 255
static int acpi_video_device_state_open_fs(struct inode *inode,
					   struct file *file);
L
Linus Torvalds 已提交
256
static struct file_operations acpi_video_device_state_fops = {
257
	.owner = THIS_MODULE,
L
Len Brown 已提交
258 259 260 261
	.open = acpi_video_device_state_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
262 263
};

L
Len Brown 已提交
264 265
static int acpi_video_device_brightness_open_fs(struct inode *inode,
						struct file *file);
L
Linus Torvalds 已提交
266
static struct file_operations acpi_video_device_brightness_fops = {
267
	.owner = THIS_MODULE,
L
Len Brown 已提交
268 269 270 271
	.open = acpi_video_device_brightness_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
272 273
};

L
Len Brown 已提交
274 275
static int acpi_video_device_EDID_open_fs(struct inode *inode,
					  struct file *file);
L
Linus Torvalds 已提交
276
static struct file_operations acpi_video_device_EDID_fops = {
277
	.owner = THIS_MODULE,
L
Len Brown 已提交
278 279 280 281
	.open = acpi_video_device_EDID_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
282 283
};

L
Len Brown 已提交
284
static char device_decode[][30] = {
L
Linus Torvalds 已提交
285 286 287 288 289 290
	"motherboard VGA device",
	"PCI VGA device",
	"AGP VGA device",
	"UNKNOWN",
};

L
Len Brown 已提交
291 292 293 294
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 已提交
295
static int acpi_video_device_enumerate(struct acpi_video_bus *video);
296 297 298 299
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,
300
			unsigned long long *level);
L
Len Brown 已提交
301 302
static int acpi_video_get_next_level(struct acpi_video_device *device,
				     u32 level_current, u32 event);
303
static int acpi_video_switch_brightness(struct acpi_video_device *device,
L
Len Brown 已提交
304
					 int event);
305
static int acpi_video_device_get_state(struct acpi_video_device *device,
306
			    unsigned long long *state);
307 308
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 已提交
309

310 311 312
/*backlight device sysfs support*/
static int acpi_video_get_brightness(struct backlight_device *bd)
{
313
	unsigned long long cur_level;
314
	int i;
315
	struct acpi_video_device *vd =
316
		(struct acpi_video_device *)bl_get_data(bd);
317 318 319

	if (acpi_video_device_lcd_get_level_current(vd, &cur_level))
		return -EINVAL;
320 321 322 323 324 325 326
	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;
327 328 329 330
}

static int acpi_video_set_brightness(struct backlight_device *bd)
{
331
	int request_level = bd->props.brightness + 2;
332
	struct acpi_video_device *vd =
333
		(struct acpi_video_device *)bl_get_data(bd);
334 335 336

	return acpi_video_device_lcd_set_level(vd,
				vd->brightness->levels[request_level]);
337 338
}

339 340 341 342 343
static struct backlight_ops acpi_backlight_ops = {
	.get_brightness = acpi_video_get_brightness,
	.update_status  = acpi_video_set_brightness,
};

344 345 346
/*video output device sysfs support*/
static int acpi_video_output_get(struct output_device *od)
{
347
	unsigned long long state;
348
	struct acpi_video_device *vd =
349
		(struct acpi_video_device *)dev_get_drvdata(&od->dev);
350 351 352 353 354 355 356 357
	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=
358
		(struct acpi_video_device *)dev_get_drvdata(&od->dev);
359 360 361 362 363 364 365
	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,
};
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380


/* thermal cooling device callbacks */
static int video_get_max_state(struct thermal_cooling_device *cdev, char *buf)
{
	struct acpi_device *device = cdev->devdata;
	struct acpi_video_device *video = acpi_driver_data(device);

	return sprintf(buf, "%d\n", video->brightness->count - 3);
}

static int video_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
{
	struct acpi_device *device = cdev->devdata;
	struct acpi_video_device *video = acpi_driver_data(device);
381
	unsigned long long level;
382 383
	int state;

384 385
	if (acpi_video_device_lcd_get_level_current(video, &level))
		return -EINVAL;
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
	for (state = 2; state < video->brightness->count; state++)
		if (level == video->brightness->levels[state])
			return sprintf(buf, "%d\n",
				       video->brightness->count - state - 1);

	return -EINVAL;
}

static int
video_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
{
	struct acpi_device *device = cdev->devdata;
	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 已提交
415 416 417 418 419 420 421
/* --------------------------------------------------------------------------
                               Video Management
   -------------------------------------------------------------------------- */

/* device */

static int
422
acpi_video_device_query(struct acpi_video_device *device, unsigned long long *state)
L
Linus Torvalds 已提交
423
{
L
Len Brown 已提交
424
	int status;
425 426

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

428
	return status;
L
Linus Torvalds 已提交
429 430 431
}

static int
L
Len Brown 已提交
432
acpi_video_device_get_state(struct acpi_video_device *device,
433
			    unsigned long long *state)
L
Linus Torvalds 已提交
434
{
L
Len Brown 已提交
435
	int status;
L
Linus Torvalds 已提交
436

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

439
	return status;
L
Linus Torvalds 已提交
440 441 442
}

static int
L
Len Brown 已提交
443
acpi_video_device_set_state(struct acpi_video_device *device, int state)
L
Linus Torvalds 已提交
444
{
L
Len Brown 已提交
445 446 447
	int status;
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
448
	unsigned long long ret;
L
Linus Torvalds 已提交
449 450 451


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

454
	return status;
L
Linus Torvalds 已提交
455 456 457
}

static int
L
Len Brown 已提交
458 459
acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
				   union acpi_object **levels)
L
Linus Torvalds 已提交
460
{
L
Len Brown 已提交
461 462 463
	int status;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *obj;
L
Linus Torvalds 已提交
464 465 466 467


	*levels = NULL;

468
	status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer);
L
Linus Torvalds 已提交
469
	if (!ACPI_SUCCESS(status))
470
		return status;
L
Len Brown 已提交
471
	obj = (union acpi_object *)buffer.pointer;
472
	if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
473
		printk(KERN_ERR PREFIX "Invalid _BCL data\n");
L
Linus Torvalds 已提交
474 475 476 477 478 479
		status = -EFAULT;
		goto err;
	}

	*levels = obj;

480
	return 0;
L
Linus Torvalds 已提交
481

L
Len Brown 已提交
482
      err:
483
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
484

485
	return status;
L
Linus Torvalds 已提交
486 487 488
}

static int
L
Len Brown 已提交
489
acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
L
Linus Torvalds 已提交
490
{
491
	int status;
L
Len Brown 已提交
492 493
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
494
	int state;
L
Linus Torvalds 已提交
495 496 497

	arg0.integer.value = level;

498 499 500 501 502 503 504
	status = acpi_evaluate_object(device->dev->handle, "_BCM",
				      &args, NULL);
	if (ACPI_FAILURE(status)) {
		ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));
		return -EIO;
	}

505
	device->brightness->curr = level;
506
	for (state = 2; state < device->brightness->count; state++)
507
		if (level == device->brightness->levels[state]) {
508
			device->backlight->props.brightness = state - 2;
509 510
			return 0;
		}
511

512 513
	ACPI_ERROR((AE_INFO, "Current brightness invalid"));
	return -EINVAL;
L
Linus Torvalds 已提交
514 515 516
}

static int
L
Len Brown 已提交
517
acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
518
					unsigned long long *level)
L
Linus Torvalds 已提交
519
{
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
	acpi_status status = AE_OK;

	if (device->cap._BQC) {
		status = acpi_evaluate_integer(device->dev->handle, "_BQC",
						NULL, level);
		if (ACPI_SUCCESS(status)) {
			device->brightness->curr = *level;
			return 0;
		} 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
			 */
			ACPI_WARNING((AE_INFO, "Evaluating _BQC failed"));
			device->cap._BQC = 0;
		}
	}

541
	*level = device->brightness->curr;
542
	return 0;
L
Linus Torvalds 已提交
543 544 545
}

static int
L
Len Brown 已提交
546 547
acpi_video_device_EDID(struct acpi_video_device *device,
		       union acpi_object **edid, ssize_t length)
L
Linus Torvalds 已提交
548
{
L
Len Brown 已提交
549 550 551 552 553
	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 已提交
554 555 556 557 558


	*edid = NULL;

	if (!device)
559
		return -ENODEV;
L
Linus Torvalds 已提交
560 561 562 563 564
	if (length == 128)
		arg0.integer.value = 1;
	else if (length == 256)
		arg0.integer.value = 2;
	else
565
		return -EINVAL;
L
Linus Torvalds 已提交
566

567
	status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer);
L
Linus Torvalds 已提交
568
	if (ACPI_FAILURE(status))
569
		return -ENODEV;
L
Linus Torvalds 已提交
570

571
	obj = buffer.pointer;
L
Linus Torvalds 已提交
572 573 574 575

	if (obj && obj->type == ACPI_TYPE_BUFFER)
		*edid = obj;
	else {
576
		printk(KERN_ERR PREFIX "Invalid _DDC data\n");
L
Linus Torvalds 已提交
577 578 579 580
		status = -EFAULT;
		kfree(obj);
	}

581
	return status;
L
Linus Torvalds 已提交
582 583 584 585 586
}

/* bus */

static int
L
Len Brown 已提交
587
acpi_video_bus_set_POST(struct acpi_video_bus *video, unsigned long option)
L
Linus Torvalds 已提交
588
{
L
Len Brown 已提交
589
	int status;
590
	unsigned long long tmp;
L
Len Brown 已提交
591 592
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
L
Linus Torvalds 已提交
593 594 595 596


	arg0.integer.value = option;

597
	status = acpi_evaluate_integer(video->device->handle, "_SPD", &args, &tmp);
L
Linus Torvalds 已提交
598
	if (ACPI_SUCCESS(status))
L
Len Brown 已提交
599
		status = tmp ? (-EINVAL) : (AE_OK);
L
Linus Torvalds 已提交
600

601
	return status;
L
Linus Torvalds 已提交
602 603 604
}

static int
605
acpi_video_bus_get_POST(struct acpi_video_bus *video, unsigned long long *id)
L
Linus Torvalds 已提交
606 607 608
{
	int status;

609
	status = acpi_evaluate_integer(video->device->handle, "_GPD", NULL, id);
L
Linus Torvalds 已提交
610

611
	return status;
L
Linus Torvalds 已提交
612 613 614
}

static int
L
Len Brown 已提交
615
acpi_video_bus_POST_options(struct acpi_video_bus *video,
616
			    unsigned long long *options)
L
Linus Torvalds 已提交
617
{
L
Len Brown 已提交
618
	int status;
L
Linus Torvalds 已提交
619

620
	status = acpi_evaluate_integer(video->device->handle, "_VPO", NULL, options);
L
Linus Torvalds 已提交
621 622
	*options &= 3;

623
	return status;
L
Linus Torvalds 已提交
624 625 626 627 628 629 630 631 632
}

/*
 *  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
633
 *			active display output. No switch event.
L
Linus Torvalds 已提交
634 635 636 637 638 639
 *		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
640
 *			of the LCD when the power changes from AC to DC
L
Linus Torvalds 已提交
641
 *		1. 	The system BIOS should NOT automatically control the brightness 
642
 *			level of the LCD when the power changes from AC to DC.
L
Linus Torvalds 已提交
643 644 645 646 647
 * Return Value:
 * 		-1	wrong arg.
 */

static int
L
Len Brown 已提交
648
acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
L
Linus Torvalds 已提交
649
{
L
Len Brown 已提交
650 651 652
	acpi_integer status = 0;
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
L
Linus Torvalds 已提交
653 654


L
Len Brown 已提交
655
	if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) {
L
Linus Torvalds 已提交
656 657 658 659 660
		status = -1;
		goto Failed;
	}
	arg0.integer.value = (lcd_flag << 2) | bios_flag;
	video->dos_setting = arg0.integer.value;
661
	acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL);
L
Linus Torvalds 已提交
662

L
Len Brown 已提交
663
      Failed:
664
	return status;
L
Linus Torvalds 已提交
665 666
}

667 668 669 670 671 672 673 674 675 676
/*
 * 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 已提交
677 678 679 680 681
/*
 *  Arg:	
 *  	device	: video output device (LCD, CRT, ..)
 *
 *  Return Value:
682 683 684 685 686 687 688 689 690
 *	Maximum brightness level
 *
 *  Allocate and initialize device->brightness.
 */

static int
acpi_video_init_brightness(struct acpi_video_device *device)
{
	union acpi_object *obj = NULL;
691
	int i, max_level = 0, count = 0, level_ac_battery = 0;
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
	union acpi_object *o;
	struct acpi_video_device_brightness *br = NULL;

	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");
		goto out;
	}

710
	br->levels = kmalloc((obj->package.count + 2) * sizeof *(br->levels),
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
				GFP_KERNEL);
	if (!br->levels)
		goto out_free;

	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++;
	}

728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
	/*
	 * 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.
	 */
	for (i = 2; i < count; i++)
		if (br->levels[i] == br->levels[0] ||
		    br->levels[i] == br->levels[1])
			level_ac_battery++;

	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"));

748 749 750 751 752 753 754 755
	/* 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"));
756

757 758
	br->count = count;
	device->brightness = br;
759 760
	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
			  "found %d brightness levels\n", count - 2));
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
	kfree(obj);
	return max_level;

out_free_levels:
	kfree(br->levels);
out_free:
	kfree(br);
out:
	device->brightness = NULL;
	kfree(obj);
	return 0;
}

/*
 *  Arg:
 *	device	: video output device (LCD, CRT, ..)
 *
 *  Return Value:
L
Linus Torvalds 已提交
779 780
 *  	None
 *
781
 *  Find out all required AML methods defined under the output
L
Linus Torvalds 已提交
782 783 784
 *  device.
 */

L
Len Brown 已提交
785
static void acpi_video_device_find_cap(struct acpi_video_device *device)
L
Linus Torvalds 已提交
786 787
{
	acpi_handle h_dummy1;
788
	u32 max_level = 0;
L
Linus Torvalds 已提交
789 790


791
	memset(&device->cap, 0, sizeof(device->cap));
L
Linus Torvalds 已提交
792

793
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
L
Linus Torvalds 已提交
794 795
		device->cap._ADR = 1;
	}
796
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) {
L
Len Brown 已提交
797
		device->cap._BCL = 1;
L
Linus Torvalds 已提交
798
	}
799
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
L
Len Brown 已提交
800
		device->cap._BCM = 1;
L
Linus Torvalds 已提交
801
	}
802 803
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
		device->cap._BQC = 1;
804
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
L
Len Brown 已提交
805
		device->cap._DDC = 1;
L
Linus Torvalds 已提交
806
	}
807
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DCS", &h_dummy1))) {
L
Linus Torvalds 已提交
808 809
		device->cap._DCS = 1;
	}
810
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DGS", &h_dummy1))) {
L
Linus Torvalds 已提交
811 812
		device->cap._DGS = 1;
	}
813
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DSS", &h_dummy1))) {
L
Linus Torvalds 已提交
814 815 816
		device->cap._DSS = 1;
	}

817 818
	if (acpi_video_backlight_support())
		max_level = acpi_video_init_brightness(device);
L
Linus Torvalds 已提交
819

820
	if (device->cap._BCL && device->cap._BCM && max_level > 0) {
821
		int result;
822 823 824 825 826 827 828 829
		static int count = 0;
		char *name;
		name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
		if (!name)
			return;

		sprintf(name, "acpi_video%d", count++);
		device->backlight = backlight_device_register(name,
830
			NULL, device, &acpi_backlight_ops);
831
		device->backlight->props.max_brightness = device->brightness->count-3;
832 833 834 835 836 837 838 839 840 841 842
		/*
		 * If there exists the _BQC object, the _BQC object will be
		 * called to get the current backlight brightness. Otherwise
		 * the brightness will be set to the maximum.
		 */
		if (device->cap._BQC)
			device->backlight->props.brightness =
				acpi_video_get_brightness(device->backlight);
		else
			device->backlight->props.brightness =
				device->backlight->props.max_brightness;
843
		backlight_update_status(device->backlight);
844
		kfree(name);
845 846 847

		device->cdev = thermal_cooling_device_register("LCD",
					device->dev, &video_cooling_ops);
848 849 850
		if (IS_ERR(device->cdev))
			return;

851 852
		dev_info(&device->dev->dev, "registered as cooling_device%d\n",
			 device->cdev->id);
853 854 855 856 857 858 859 860 861 862
		result = sysfs_create_link(&device->dev->dev.kobj,
				&device->cdev->device.kobj,
				"thermal_cooling");
		if (result)
			printk(KERN_ERR PREFIX "Create sysfs link\n");
		result = sysfs_create_link(&device->cdev->device.kobj,
				&device->dev->dev.kobj, "device");
		if (result)
			printk(KERN_ERR PREFIX "Create sysfs link\n");

863
	}
864 865 866 867 868 869 870 871 872 873 874 875 876 877

	if (acpi_video_display_switch_support()) {

		if (device->cap._DCS && device->cap._DSS) {
			static int count;
			char *name;
			name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
			if (!name)
				return;
			sprintf(name, "acpi_video%d", count++);
			device->output_dev = video_output_register(name,
					NULL, device, &acpi_output_properties);
			kfree(name);
		}
878
	}
L
Linus Torvalds 已提交
879 880 881 882 883 884 885 886 887
}

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

L
Len Brown 已提交
891
static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
892
{
L
Len Brown 已提交
893
	acpi_handle h_dummy1;
L
Linus Torvalds 已提交
894

895
	memset(&video->cap, 0, sizeof(video->cap));
896
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
L
Linus Torvalds 已提交
897 898
		video->cap._DOS = 1;
	}
899
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) {
L
Linus Torvalds 已提交
900 901
		video->cap._DOD = 1;
	}
902
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) {
L
Linus Torvalds 已提交
903 904
		video->cap._ROM = 1;
	}
905
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) {
L
Linus Torvalds 已提交
906 907
		video->cap._GPD = 1;
	}
908
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) {
L
Linus Torvalds 已提交
909 910
		video->cap._SPD = 1;
	}
911
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) {
L
Linus Torvalds 已提交
912 913 914 915 916 917 918 919 920
		video->cap._VPO = 1;
	}
}

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

L
Len Brown 已提交
921
static int acpi_video_bus_check(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
922
{
L
Len Brown 已提交
923
	acpi_status status = -ENOENT;
924
	struct device *dev;
L
Linus Torvalds 已提交
925 926

	if (!video)
927
		return -EINVAL;
L
Linus Torvalds 已提交
928

929 930 931 932 933
	dev = acpi_get_physical_pci_device(video->device->handle);
	if (!dev)
		return -ENODEV;
	put_device(dev);

L
Linus Torvalds 已提交
934 935 936 937
	/* Since there is no HID, CID and so on for VGA driver, we have
	 * to check well known required nodes.
	 */

938
	/* Does this device support video switching? */
L
Len Brown 已提交
939
	if (video->cap._DOS) {
L
Linus Torvalds 已提交
940 941 942 943
		video->flags.multihead = 1;
		status = 0;
	}

944
	/* Does this device support retrieving a video ROM? */
L
Len Brown 已提交
945
	if (video->cap._ROM) {
L
Linus Torvalds 已提交
946 947 948 949
		video->flags.rom = 1;
		status = 0;
	}

950
	/* Does this device support configuring which video device to POST? */
L
Len Brown 已提交
951
	if (video->cap._GPD && video->cap._SPD && video->cap._VPO) {
L
Linus Torvalds 已提交
952 953 954 955
		video->flags.post = 1;
		status = 0;
	}

956
	return status;
L
Linus Torvalds 已提交
957 958 959 960 961 962
}

/* --------------------------------------------------------------------------
                              FS Interface (/proc)
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
963
static struct proc_dir_entry *acpi_video_dir;
L
Linus Torvalds 已提交
964 965 966

/* video devices */

L
Len Brown 已提交
967
static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
968
{
969
	struct acpi_video_device *dev = seq->private;
L
Linus Torvalds 已提交
970 971 972 973 974 975 976 977 978 979 980 981 982


	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");
983 984
	else if (dev->flags.dvi)
		seq_printf(seq, "DVI\n");
L
Linus Torvalds 已提交
985 986 987
	else
		seq_printf(seq, "UNKNOWN\n");

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

L
Len Brown 已提交
990
      end:
991
	return 0;
L
Linus Torvalds 已提交
992 993 994
}

static int
L
Len Brown 已提交
995
acpi_video_device_info_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
996 997 998 999 1000
{
	return single_open(file, acpi_video_device_info_seq_show,
			   PDE(inode)->data);
}

L
Len Brown 已提交
1001
static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1002
{
L
Len Brown 已提交
1003
	int status;
1004
	struct acpi_video_device *dev = seq->private;
1005
	unsigned long long state;
L
Linus Torvalds 已提交
1006 1007 1008 1009 1010 1011 1012 1013


	if (!dev)
		goto end;

	status = acpi_video_device_get_state(dev, &state);
	seq_printf(seq, "state:     ");
	if (ACPI_SUCCESS(status))
1014
		seq_printf(seq, "0x%02llx\n", state);
L
Linus Torvalds 已提交
1015 1016 1017 1018 1019 1020
	else
		seq_printf(seq, "<not supported>\n");

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

L
Len Brown 已提交
1025
      end:
1026
	return 0;
L
Linus Torvalds 已提交
1027 1028 1029
}

static int
L
Len Brown 已提交
1030
acpi_video_device_state_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1031 1032 1033 1034 1035 1036
{
	return single_open(file, acpi_video_device_state_seq_show,
			   PDE(inode)->data);
}

static ssize_t
L
Len Brown 已提交
1037 1038 1039
acpi_video_device_write_state(struct file *file,
			      const char __user * buffer,
			      size_t count, loff_t * data)
L
Linus Torvalds 已提交
1040
{
L
Len Brown 已提交
1041
	int status;
1042 1043
	struct seq_file *m = file->private_data;
	struct acpi_video_device *dev = m->private;
L
Len Brown 已提交
1044 1045
	char str[12] = { 0 };
	u32 state = 0;
L
Linus Torvalds 已提交
1046 1047 1048


	if (!dev || count + 1 > sizeof str)
1049
		return -EINVAL;
L
Linus Torvalds 已提交
1050 1051

	if (copy_from_user(str, buffer, count))
1052
		return -EFAULT;
L
Linus Torvalds 已提交
1053 1054 1055

	str[count] = 0;
	state = simple_strtoul(str, NULL, 0);
L
Len Brown 已提交
1056
	state &= ((1ul << 31) | (1ul << 30) | (1ul << 0));
L
Linus Torvalds 已提交
1057 1058 1059 1060

	status = acpi_video_device_set_state(dev, state);

	if (status)
1061
		return -EFAULT;
L
Linus Torvalds 已提交
1062

1063
	return count;
L
Linus Torvalds 已提交
1064 1065 1066
}

static int
L
Len Brown 已提交
1067
acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1068
{
1069
	struct acpi_video_device *dev = seq->private;
L
Len Brown 已提交
1070
	int i;
L
Linus Torvalds 已提交
1071 1072 1073 1074


	if (!dev || !dev->brightness) {
		seq_printf(seq, "<not supported>\n");
1075
		return 0;
L
Linus Torvalds 已提交
1076 1077 1078
	}

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

1083
	return 0;
L
Linus Torvalds 已提交
1084 1085 1086
}

static int
L
Len Brown 已提交
1087
acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1088 1089 1090 1091 1092 1093
{
	return single_open(file, acpi_video_device_brightness_seq_show,
			   PDE(inode)->data);
}

static ssize_t
L
Len Brown 已提交
1094 1095 1096
acpi_video_device_write_brightness(struct file *file,
				   const char __user * buffer,
				   size_t count, loff_t * data)
L
Linus Torvalds 已提交
1097
{
1098 1099
	struct seq_file *m = file->private_data;
	struct acpi_video_device *dev = m->private;
1100
	char str[5] = { 0 };
L
Len Brown 已提交
1101 1102
	unsigned int level = 0;
	int i;
L
Linus Torvalds 已提交
1103 1104


1105
	if (!dev || !dev->brightness || count + 1 > sizeof str)
1106
		return -EINVAL;
L
Linus Torvalds 已提交
1107 1108

	if (copy_from_user(str, buffer, count))
1109
		return -EFAULT;
L
Linus Torvalds 已提交
1110 1111 1112

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

L
Linus Torvalds 已提交
1114
	if (level > 100)
1115
		return -EFAULT;
L
Linus Torvalds 已提交
1116

1117
	/* validate through the list of available levels */
1118
	for (i = 2; i < dev->brightness->count; i++)
L
Linus Torvalds 已提交
1119
		if (level == dev->brightness->levels[i]) {
1120 1121
			if (!acpi_video_device_lcd_set_level(dev, level))
				return count;
L
Linus Torvalds 已提交
1122 1123 1124
			break;
		}

1125
	return -EINVAL;
L
Linus Torvalds 已提交
1126 1127
}

L
Len Brown 已提交
1128
static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1129
{
1130
	struct acpi_video_device *dev = seq->private;
L
Len Brown 已提交
1131 1132 1133
	int status;
	int i;
	union acpi_object *edid = NULL;
L
Linus Torvalds 已提交
1134 1135 1136 1137 1138


	if (!dev)
		goto out;

L
Len Brown 已提交
1139
	status = acpi_video_device_EDID(dev, &edid, 128);
L
Linus Torvalds 已提交
1140
	if (ACPI_FAILURE(status)) {
L
Len Brown 已提交
1141
		status = acpi_video_device_EDID(dev, &edid, 256);
L
Linus Torvalds 已提交
1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
	}

	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 已提交
1153
      out:
L
Linus Torvalds 已提交
1154 1155 1156 1157 1158
	if (!edid)
		seq_printf(seq, "<not supported>\n");
	else
		kfree(edid);

1159
	return 0;
L
Linus Torvalds 已提交
1160 1161 1162
}

static int
L
Len Brown 已提交
1163
acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1164 1165 1166 1167 1168
{
	return single_open(file, acpi_video_device_EDID_seq_show,
			   PDE(inode)->data);
}

L
Len Brown 已提交
1169
static int acpi_video_device_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1170
{
1171
	struct proc_dir_entry *entry, *device_dir;
L
Linus Torvalds 已提交
1172 1173
	struct acpi_video_device *vid_dev;

1174
	vid_dev = acpi_driver_data(device);
L
Linus Torvalds 已提交
1175
	if (!vid_dev)
1176
		return -ENODEV;
L
Linus Torvalds 已提交
1177

1178 1179 1180 1181 1182 1183
	device_dir = proc_mkdir(acpi_device_bid(device),
				vid_dev->video->dir);
	if (!device_dir)
		return -ENOMEM;

	device_dir->owner = THIS_MODULE;
L
Linus Torvalds 已提交
1184 1185

	/* 'info' [R] */
1186
	entry = proc_create_data("info", S_IRUGO, device_dir,
1187
			&acpi_video_device_info_fops, acpi_driver_data(device));
L
Linus Torvalds 已提交
1188
	if (!entry)
1189
		goto err_remove_dir;
L
Linus Torvalds 已提交
1190 1191

	/* 'state' [R/W] */
1192 1193
	acpi_video_device_state_fops.write = acpi_video_device_write_state;
	entry = proc_create_data("state", S_IFREG | S_IRUGO | S_IWUSR,
1194
				 device_dir,
1195 1196
				 &acpi_video_device_state_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1197
	if (!entry)
1198
		goto err_remove_info;
L
Linus Torvalds 已提交
1199 1200

	/* 'brightness' [R/W] */
1201 1202 1203
	acpi_video_device_brightness_fops.write =
		acpi_video_device_write_brightness;
	entry = proc_create_data("brightness", S_IFREG | S_IRUGO | S_IWUSR,
1204
				 device_dir,
1205 1206
				 &acpi_video_device_brightness_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1207
	if (!entry)
1208
		goto err_remove_state;
L
Linus Torvalds 已提交
1209 1210

	/* 'EDID' [R] */
1211
	entry = proc_create_data("EDID", S_IRUGO, device_dir,
1212 1213
				 &acpi_video_device_EDID_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1214
	if (!entry)
1215
		goto err_remove_brightness;
L
Linus Torvalds 已提交
1216

1217 1218
	acpi_device_dir(device) = device_dir;

1219
	return 0;
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229

 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 已提交
1230 1231
}

L
Len Brown 已提交
1232
static int acpi_video_device_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1233 1234
{
	struct acpi_video_device *vid_dev;
1235
	struct proc_dir_entry *device_dir;
L
Linus Torvalds 已提交
1236

1237
	vid_dev = acpi_driver_data(device);
L
Linus Torvalds 已提交
1238
	if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
1239
		return -ENODEV;
L
Linus Torvalds 已提交
1240

1241 1242 1243 1244 1245 1246
	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 已提交
1247
		remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
L
Linus Torvalds 已提交
1248 1249 1250
		acpi_device_dir(device) = NULL;
	}

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

/* video bus */
L
Len Brown 已提交
1255
static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1256
{
1257
	struct acpi_video_bus *video = seq->private;
L
Linus Torvalds 已提交
1258 1259 1260 1261 1262 1263


	if (!video)
		goto end;

	seq_printf(seq, "Switching heads:              %s\n",
L
Len Brown 已提交
1264
		   video->flags.multihead ? "yes" : "no");
L
Linus Torvalds 已提交
1265
	seq_printf(seq, "Video ROM:                    %s\n",
L
Len Brown 已提交
1266
		   video->flags.rom ? "yes" : "no");
L
Linus Torvalds 已提交
1267
	seq_printf(seq, "Device to be POSTed on boot:  %s\n",
L
Len Brown 已提交
1268
		   video->flags.post ? "yes" : "no");
L
Linus Torvalds 已提交
1269

L
Len Brown 已提交
1270
      end:
1271
	return 0;
L
Linus Torvalds 已提交
1272 1273
}

L
Len Brown 已提交
1274
static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1275
{
L
Len Brown 已提交
1276 1277
	return single_open(file, acpi_video_bus_info_seq_show,
			   PDE(inode)->data);
L
Linus Torvalds 已提交
1278 1279
}

L
Len Brown 已提交
1280
static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1281
{
1282
	struct acpi_video_bus *video = seq->private;
L
Linus Torvalds 已提交
1283 1284 1285 1286 1287


	if (!video)
		goto end;

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

L
Len Brown 已提交
1291
      end:
1292
	return 0;
L
Linus Torvalds 已提交
1293 1294
}

L
Len Brown 已提交
1295
static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1296 1297 1298 1299
{
	return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data);
}

L
Len Brown 已提交
1300
static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1301
{
1302
	struct acpi_video_bus *video = seq->private;
1303
	unsigned long long options;
L
Len Brown 已提交
1304
	int status;
L
Linus Torvalds 已提交
1305 1306 1307 1308 1309 1310 1311 1312


	if (!video)
		goto end;

	status = acpi_video_bus_POST_options(video, &options);
	if (ACPI_SUCCESS(status)) {
		if (!(options & 1)) {
L
Len Brown 已提交
1313 1314 1315
			printk(KERN_WARNING PREFIX
			       "The motherboard VGA device is not listed as a possible POST device.\n");
			printk(KERN_WARNING PREFIX
1316
			       "This indicates a BIOS bug. Please contact the manufacturer.\n");
L
Linus Torvalds 已提交
1317
		}
1318
		printk(KERN_WARNING "%llx\n", options);
1319
		seq_printf(seq, "can POST: <integrated video>");
L
Linus Torvalds 已提交
1320 1321 1322 1323 1324 1325 1326
		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 已提交
1327
      end:
1328
	return 0;
L
Linus Torvalds 已提交
1329 1330 1331
}

static int
L
Len Brown 已提交
1332
acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1333
{
L
Len Brown 已提交
1334 1335
	return single_open(file, acpi_video_bus_POST_info_seq_show,
			   PDE(inode)->data);
L
Linus Torvalds 已提交
1336 1337
}

L
Len Brown 已提交
1338
static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1339
{
1340
	struct acpi_video_bus *video = seq->private;
L
Len Brown 已提交
1341
	int status;
1342
	unsigned long long id;
L
Linus Torvalds 已提交
1343 1344 1345 1346 1347


	if (!video)
		goto end;

L
Len Brown 已提交
1348
	status = acpi_video_bus_get_POST(video, &id);
L
Linus Torvalds 已提交
1349 1350 1351 1352
	if (!ACPI_SUCCESS(status)) {
		seq_printf(seq, "<not supported>\n");
		goto end;
	}
1353
	seq_printf(seq, "device POSTed is <%s>\n", device_decode[id & 3]);
L
Linus Torvalds 已提交
1354

L
Len Brown 已提交
1355
      end:
1356
	return 0;
L
Linus Torvalds 已提交
1357 1358
}

L
Len Brown 已提交
1359
static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1360
{
1361
	struct acpi_video_bus *video = seq->private;
L
Linus Torvalds 已提交
1362 1363


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

1366
	return 0;
L
Linus Torvalds 已提交
1367 1368
}

L
Len Brown 已提交
1369
static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1370
{
L
Len Brown 已提交
1371 1372
	return single_open(file, acpi_video_bus_POST_seq_show,
			   PDE(inode)->data);
L
Linus Torvalds 已提交
1373 1374
}

L
Len Brown 已提交
1375
static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1376 1377 1378 1379 1380
{
	return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data);
}

static ssize_t
L
Len Brown 已提交
1381 1382 1383
acpi_video_bus_write_POST(struct file *file,
			  const char __user * buffer,
			  size_t count, loff_t * data)
L
Linus Torvalds 已提交
1384
{
L
Len Brown 已提交
1385
	int status;
1386 1387
	struct seq_file *m = file->private_data;
	struct acpi_video_bus *video = m->private;
L
Len Brown 已提交
1388
	char str[12] = { 0 };
1389
	unsigned long long opt, options;
L
Linus Torvalds 已提交
1390 1391 1392


	if (!video || count + 1 > sizeof str)
1393
		return -EINVAL;
L
Linus Torvalds 已提交
1394 1395 1396

	status = acpi_video_bus_POST_options(video, &options);
	if (!ACPI_SUCCESS(status))
1397
		return -EINVAL;
L
Linus Torvalds 已提交
1398 1399

	if (copy_from_user(str, buffer, count))
1400
		return -EFAULT;
L
Linus Torvalds 已提交
1401 1402 1403 1404

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

1407
	/* just in case an OEM 'forgot' the motherboard... */
L
Linus Torvalds 已提交
1408 1409 1410
	options |= 1;

	if (options & (1ul << opt)) {
L
Len Brown 已提交
1411
		status = acpi_video_bus_set_POST(video, opt);
L
Linus Torvalds 已提交
1412
		if (!ACPI_SUCCESS(status))
1413
			return -EFAULT;
L
Linus Torvalds 已提交
1414 1415 1416

	}

1417
	return count;
L
Linus Torvalds 已提交
1418 1419 1420
}

static ssize_t
L
Len Brown 已提交
1421 1422 1423
acpi_video_bus_write_DOS(struct file *file,
			 const char __user * buffer,
			 size_t count, loff_t * data)
L
Linus Torvalds 已提交
1424
{
L
Len Brown 已提交
1425
	int status;
1426 1427
	struct seq_file *m = file->private_data;
	struct acpi_video_bus *video = m->private;
L
Len Brown 已提交
1428 1429
	char str[12] = { 0 };
	unsigned long opt;
L
Linus Torvalds 已提交
1430 1431 1432


	if (!video || count + 1 > sizeof str)
1433
		return -EINVAL;
L
Linus Torvalds 已提交
1434 1435

	if (copy_from_user(str, buffer, count))
1436
		return -EFAULT;
L
Linus Torvalds 已提交
1437 1438 1439 1440

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

L
Len Brown 已提交
1443
	status = acpi_video_bus_DOS(video, opt & 0x3, (opt & 0x4) >> 2);
L
Linus Torvalds 已提交
1444 1445

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

1448
	return count;
L
Linus Torvalds 已提交
1449 1450
}

L
Len Brown 已提交
1451
static int acpi_video_bus_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1452
{
1453 1454 1455
	struct acpi_video_bus *video = acpi_driver_data(device);
	struct proc_dir_entry *device_dir;
	struct proc_dir_entry *entry;
L
Linus Torvalds 已提交
1456

1457 1458 1459
	device_dir = proc_mkdir(acpi_device_bid(device), acpi_video_dir);
	if (!device_dir)
		return -ENOMEM;
L
Linus Torvalds 已提交
1460

1461
	device_dir->owner = THIS_MODULE;
L
Linus Torvalds 已提交
1462 1463

	/* 'info' [R] */
1464
	entry = proc_create_data("info", S_IRUGO, device_dir,
1465 1466
				 &acpi_video_bus_info_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1467
	if (!entry)
1468
		goto err_remove_dir;
L
Linus Torvalds 已提交
1469 1470

	/* 'ROM' [R] */
1471
	entry = proc_create_data("ROM", S_IRUGO, device_dir,
1472 1473
				 &acpi_video_bus_ROM_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1474
	if (!entry)
1475
		goto err_remove_info;
L
Linus Torvalds 已提交
1476 1477

	/* 'POST_info' [R] */
1478
	entry = proc_create_data("POST_info", S_IRUGO, device_dir,
1479 1480
				 &acpi_video_bus_POST_info_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1481
	if (!entry)
1482
		goto err_remove_rom;
L
Linus Torvalds 已提交
1483 1484

	/* 'POST' [R/W] */
1485
	acpi_video_bus_POST_fops.write = acpi_video_bus_write_POST;
1486
	entry = proc_create_data("POST", S_IFREG | S_IRUGO | S_IWUSR,
1487
				 device_dir,
1488 1489
				 &acpi_video_bus_POST_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1490
	if (!entry)
1491
		goto err_remove_post_info;
L
Linus Torvalds 已提交
1492 1493

	/* 'DOS' [R/W] */
1494
	acpi_video_bus_DOS_fops.write = acpi_video_bus_write_DOS;
1495
	entry = proc_create_data("DOS", S_IFREG | S_IRUGO | S_IWUSR,
1496
				 device_dir,
1497 1498
				 &acpi_video_bus_DOS_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1499
	if (!entry)
1500
		goto err_remove_post;
L
Linus Torvalds 已提交
1501

1502
	video->dir = acpi_device_dir(device) = device_dir;
1503
	return 0;
1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515

 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 已提交
1516 1517
}

L
Len Brown 已提交
1518
static int acpi_video_bus_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1519
{
1520 1521 1522 1523 1524 1525 1526 1527
	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 已提交
1528
		remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
L
Linus Torvalds 已提交
1529 1530 1531
		acpi_device_dir(device) = NULL;
	}

1532
	return 0;
L
Linus Torvalds 已提交
1533 1534 1535 1536 1537 1538 1539
}

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

/* device interface */
1540 1541 1542
static struct acpi_video_device_attrib*
acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id)
{
1543 1544 1545 1546 1547 1548 1549 1550
	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;
	}
1551 1552 1553

	return NULL;
}
L
Linus Torvalds 已提交
1554 1555

static int
L
Len Brown 已提交
1556 1557
acpi_video_bus_get_one_device(struct acpi_device *device,
			      struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1558
{
1559
	unsigned long long device_id;
1560
	int status;
L
Len Brown 已提交
1561
	struct acpi_video_device *data;
1562
	struct acpi_video_device_attrib* attribute;
L
Linus Torvalds 已提交
1563 1564

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

L
Len Brown 已提交
1567 1568
	status =
	    acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
L
Linus Torvalds 已提交
1569 1570
	if (ACPI_SUCCESS(status)) {

1571
		data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
L
Linus Torvalds 已提交
1572
		if (!data)
1573
			return -ENOMEM;
L
Linus Torvalds 已提交
1574 1575 1576

		strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
		strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
1577
		device->driver_data = data;
L
Linus Torvalds 已提交
1578 1579 1580 1581 1582

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

1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605
		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;
		} else
L
Linus Torvalds 已提交
1606
			data->flags.unknown = 1;
L
Len Brown 已提交
1607

L
Linus Torvalds 已提交
1608 1609 1610
		acpi_video_device_bind(video, data);
		acpi_video_device_find_cap(data);

1611
		status = acpi_install_notify_handler(device->handle,
L
Len Brown 已提交
1612 1613 1614
						     ACPI_DEVICE_NOTIFY,
						     acpi_video_device_notify,
						     data);
L
Linus Torvalds 已提交
1615
		if (ACPI_FAILURE(status)) {
1616 1617
			printk(KERN_ERR PREFIX
					  "Error installing notify handler\n");
1618 1619 1620 1621 1622
			if(data->brightness)
				kfree(data->brightness->levels);
			kfree(data->brightness);
			kfree(data);
			return -ENODEV;
L
Linus Torvalds 已提交
1623 1624
		}

1625
		mutex_lock(&video->device_list_lock);
L
Linus Torvalds 已提交
1626
		list_add_tail(&data->entry, &video->video_device_list);
1627
		mutex_unlock(&video->device_list_lock);
L
Linus Torvalds 已提交
1628 1629 1630

		acpi_video_device_add_fs(device);

1631
		return 0;
L
Linus Torvalds 已提交
1632 1633
	}

1634
	return -ENOENT;
L
Linus Torvalds 已提交
1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646
}

/*
 *  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 已提交
1647
 */
L
Linus Torvalds 已提交
1648

L
Len Brown 已提交
1649
static void acpi_video_device_rebind(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1650
{
1651 1652
	struct acpi_video_device *dev;

1653
	mutex_lock(&video->device_list_lock);
1654 1655

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

1658
	mutex_unlock(&video->device_list_lock);
L
Linus Torvalds 已提交
1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671
}

/*
 *  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 已提交
1672
 */
L
Linus Torvalds 已提交
1673 1674

static void
L
Len Brown 已提交
1675 1676
acpi_video_device_bind(struct acpi_video_bus *video,
		       struct acpi_video_device *device)
L
Linus Torvalds 已提交
1677
{
1678
	struct acpi_video_enumerated_device *ids;
L
Len Brown 已提交
1679
	int i;
L
Linus Torvalds 已提交
1680

1681 1682 1683 1684
	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 已提交
1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698
			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 已提交
1699
 */
L
Linus Torvalds 已提交
1700 1701 1702

static int acpi_video_device_enumerate(struct acpi_video_bus *video)
{
L
Len Brown 已提交
1703 1704 1705
	int status;
	int count;
	int i;
1706
	struct acpi_video_enumerated_device *active_list;
L
Len Brown 已提交
1707 1708 1709
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *dod = NULL;
	union acpi_object *obj;
L
Linus Torvalds 已提交
1710

1711
	status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
L
Linus Torvalds 已提交
1712
	if (!ACPI_SUCCESS(status)) {
1713
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD"));
1714
		return status;
L
Linus Torvalds 已提交
1715 1716
	}

1717
	dod = buffer.pointer;
L
Linus Torvalds 已提交
1718
	if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
1719
		ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data"));
L
Linus Torvalds 已提交
1720 1721 1722 1723 1724
		status = -EFAULT;
		goto out;
	}

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

1727 1728 1729 1730
	active_list = kcalloc(1 + dod->package.count,
			      sizeof(struct acpi_video_enumerated_device),
			      GFP_KERNEL);
	if (!active_list) {
L
Linus Torvalds 已提交
1731 1732 1733 1734 1735 1736
		status = -ENOMEM;
		goto out;
	}

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

		if (obj->type != ACPI_TYPE_INTEGER) {
1740 1741 1742
			printk(KERN_ERR PREFIX
				"Invalid _DOD data in element %d\n", i);
			continue;
L
Linus Torvalds 已提交
1743
		}
1744 1745 1746

		active_list[count].value.int_val = obj->integer.value;
		active_list[count].bind_info = NULL;
L
Len Brown 已提交
1747 1748
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i,
				  (int)obj->integer.value));
L
Linus Torvalds 已提交
1749 1750 1751
		count++;
	}

1752
	kfree(video->attached_array);
L
Len Brown 已提交
1753

1754
	video->attached_array = active_list;
L
Linus Torvalds 已提交
1755
	video->attached_count = count;
1756 1757

 out:
1758
	kfree(buffer.pointer);
1759
	return status;
L
Linus Torvalds 已提交
1760 1761
}

L
Len Brown 已提交
1762 1763 1764
static int
acpi_video_get_next_level(struct acpi_video_device *device,
			  u32 level_current, u32 event)
L
Linus Torvalds 已提交
1765
{
1766
	int min, max, min_above, max_below, i, l, delta = 255;
1767 1768
	max = max_below = 0;
	min = min_above = 255;
1769
	/* Find closest level to level_current */
1770
	for (i = 2; i < device->brightness->count; i++) {
1771 1772 1773 1774 1775 1776 1777 1778 1779
		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;
1780
	for (i = 2; i < device->brightness->count; i++) {
1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804
		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 已提交
1805 1806
}

1807
static int
L
Len Brown 已提交
1808
acpi_video_switch_brightness(struct acpi_video_device *device, int event)
L
Linus Torvalds 已提交
1809
{
1810
	unsigned long long level_current, level_next;
1811 1812
	int result = -EINVAL;

1813
	if (!device->brightness)
1814 1815 1816 1817 1818 1819 1820
		goto out;

	result = acpi_video_device_lcd_get_level_current(device,
							 &level_current);
	if (result)
		goto out;

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

1823
	result = acpi_video_device_lcd_set_level(device, level_next);
1824 1825 1826 1827 1828 1829

out:
	if (result)
		printk(KERN_ERR PREFIX "Failed to switch the brightness\n");

	return result;
L
Linus Torvalds 已提交
1830 1831 1832
}

static int
L
Len Brown 已提交
1833 1834
acpi_video_bus_get_devices(struct acpi_video_bus *video,
			   struct acpi_device *device)
L
Linus Torvalds 已提交
1835
{
L
Len Brown 已提交
1836
	int status = 0;
1837
	struct acpi_device *dev;
L
Linus Torvalds 已提交
1838 1839 1840

	acpi_video_device_enumerate(video);

1841
	list_for_each_entry(dev, &device->children, node) {
L
Linus Torvalds 已提交
1842 1843 1844

		status = acpi_video_bus_get_one_device(dev, video);
		if (ACPI_FAILURE(status)) {
1845 1846
			printk(KERN_WARNING PREFIX
					"Cant attach device");
L
Linus Torvalds 已提交
1847 1848 1849
			continue;
		}
	}
1850
	return status;
L
Linus Torvalds 已提交
1851 1852
}

L
Len Brown 已提交
1853
static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
L
Linus Torvalds 已提交
1854
{
1855
	acpi_status status;
L
Linus Torvalds 已提交
1856 1857 1858 1859
	struct acpi_video_bus *video;


	if (!device || !device->video)
1860
		return -ENOENT;
L
Linus Torvalds 已提交
1861 1862 1863 1864 1865

	video = device->video;

	acpi_video_device_remove_fs(device->dev);

1866
	status = acpi_remove_notify_handler(device->dev->handle,
L
Len Brown 已提交
1867 1868
					    ACPI_DEVICE_NOTIFY,
					    acpi_video_device_notify);
1869
	backlight_device_unregister(device->backlight);
1870 1871 1872 1873 1874 1875 1876 1877
	if (device->cdev) {
		sysfs_remove_link(&device->dev->dev.kobj,
				  "thermal_cooling");
		sysfs_remove_link(&device->cdev->device.kobj,
				  "device");
		thermal_cooling_device_unregister(device->cdev);
		device->cdev = NULL;
	}
1878
	video_output_unregister(device->output_dev);
1879

1880
	return 0;
L
Linus Torvalds 已提交
1881 1882
}

L
Len Brown 已提交
1883
static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1884
{
L
Len Brown 已提交
1885
	int status;
1886
	struct acpi_video_device *dev, *next;
L
Linus Torvalds 已提交
1887

1888
	mutex_lock(&video->device_list_lock);
L
Linus Torvalds 已提交
1889

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

1892
		status = acpi_video_bus_put_one_device(dev);
L
Len Brown 已提交
1893 1894 1895
		if (ACPI_FAILURE(status))
			printk(KERN_WARNING PREFIX
			       "hhuuhhuu bug in acpi video driver.\n");
L
Linus Torvalds 已提交
1896

1897 1898 1899 1900 1901 1902
		if (dev->brightness) {
			kfree(dev->brightness->levels);
			kfree(dev->brightness);
		}
		list_del(&dev->entry);
		kfree(dev);
L
Linus Torvalds 已提交
1903 1904
	}

1905
	mutex_unlock(&video->device_list_lock);
1906

1907
	return 0;
L
Linus Torvalds 已提交
1908 1909 1910 1911
}

/* acpi_video interface */

L
Len Brown 已提交
1912
static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1913
{
1914
	return acpi_video_bus_DOS(video, 0, 0);
L
Linus Torvalds 已提交
1915 1916
}

L
Len Brown 已提交
1917
static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1918 1919 1920 1921
{
	return acpi_video_bus_DOS(video, 0, 1);
}

L
Len Brown 已提交
1922
static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
L
Linus Torvalds 已提交
1923
{
1924
	struct acpi_video_bus *video = data;
L
Len Brown 已提交
1925
	struct acpi_device *device = NULL;
1926 1927 1928
	struct input_dev *input;
	int keycode;

L
Linus Torvalds 已提交
1929
	if (!video)
1930
		return;
L
Linus Torvalds 已提交
1931

1932
	device = video->device;
1933
	input = video->input;
L
Linus Torvalds 已提交
1934 1935

	switch (event) {
1936
	case ACPI_VIDEO_NOTIFY_SWITCH:	/* User requested a switch,
L
Linus Torvalds 已提交
1937
					 * most likely via hotkey. */
1938
		acpi_bus_generate_proc_event(device, event, 0);
1939
		keycode = KEY_SWITCHVIDEOMODE;
L
Linus Torvalds 已提交
1940 1941
		break;

1942
	case ACPI_VIDEO_NOTIFY_PROBE:	/* User plugged in or removed a video
L
Linus Torvalds 已提交
1943 1944 1945
					 * connector. */
		acpi_video_device_enumerate(video);
		acpi_video_device_rebind(video);
1946
		acpi_bus_generate_proc_event(device, event, 0);
1947
		keycode = KEY_SWITCHVIDEOMODE;
L
Linus Torvalds 已提交
1948 1949
		break;

L
Len Brown 已提交
1950
	case ACPI_VIDEO_NOTIFY_CYCLE:	/* Cycle Display output hotkey pressed. */
L
Len Brown 已提交
1951
		acpi_bus_generate_proc_event(device, event, 0);
1952 1953
		keycode = KEY_SWITCHVIDEOMODE;
		break;
L
Len Brown 已提交
1954
	case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:	/* Next Display output hotkey pressed. */
L
Len Brown 已提交
1955
		acpi_bus_generate_proc_event(device, event, 0);
1956 1957
		keycode = KEY_VIDEO_NEXT;
		break;
L
Len Brown 已提交
1958
	case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:	/* previous Display output hotkey pressed. */
1959
		acpi_bus_generate_proc_event(device, event, 0);
1960
		keycode = KEY_VIDEO_PREV;
L
Linus Torvalds 已提交
1961 1962 1963
		break;

	default:
1964
		keycode = KEY_UNKNOWN;
L
Linus Torvalds 已提交
1965
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
1966
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
1967 1968 1969
		break;
	}

1970
	acpi_notifier_call_chain(device, event, 0);
1971 1972 1973 1974 1975
	input_report_key(input, keycode, 1);
	input_sync(input);
	input_report_key(input, keycode, 0);
	input_sync(input);

1976
	return;
L
Linus Torvalds 已提交
1977 1978
}

L
Len Brown 已提交
1979
static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
L
Linus Torvalds 已提交
1980
{
1981
	struct acpi_video_device *video_device = data;
L
Len Brown 已提交
1982
	struct acpi_device *device = NULL;
1983 1984 1985
	struct acpi_video_bus *bus;
	struct input_dev *input;
	int keycode;
L
Linus Torvalds 已提交
1986 1987

	if (!video_device)
1988
		return;
L
Linus Torvalds 已提交
1989

1990
	device = video_device->dev;
1991 1992
	bus = video_device->video;
	input = bus->input;
L
Linus Torvalds 已提交
1993 1994

	switch (event) {
L
Len Brown 已提交
1995
	case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:	/* Cycle brightness */
1996 1997
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
1998
		acpi_bus_generate_proc_event(device, event, 0);
1999 2000
		keycode = KEY_BRIGHTNESS_CYCLE;
		break;
L
Len Brown 已提交
2001
	case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:	/* Increase brightness */
2002 2003
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
L
Len Brown 已提交
2004
		acpi_bus_generate_proc_event(device, event, 0);
2005 2006
		keycode = KEY_BRIGHTNESSUP;
		break;
L
Len Brown 已提交
2007
	case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:	/* Decrease brightness */
2008 2009
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
L
Len Brown 已提交
2010
		acpi_bus_generate_proc_event(device, event, 0);
2011 2012
		keycode = KEY_BRIGHTNESSDOWN;
		break;
L
Len Brown 已提交
2013
	case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:	/* zero brightnesss */
2014 2015
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
L
Len Brown 已提交
2016
		acpi_bus_generate_proc_event(device, event, 0);
2017 2018
		keycode = KEY_BRIGHTNESS_ZERO;
		break;
L
Len Brown 已提交
2019
	case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:	/* display device off */
2020 2021
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
2022
		acpi_bus_generate_proc_event(device, event, 0);
2023
		keycode = KEY_DISPLAY_OFF;
L
Linus Torvalds 已提交
2024 2025
		break;
	default:
2026
		keycode = KEY_UNKNOWN;
L
Linus Torvalds 已提交
2027
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
2028
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
2029 2030
		break;
	}
2031

2032
	acpi_notifier_call_chain(device, event, 0);
2033 2034 2035 2036 2037
	input_report_key(input, keycode, 1);
	input_sync(input);
	input_report_key(input, keycode, 0);
	input_sync(input);

2038
	return;
L
Linus Torvalds 已提交
2039 2040
}

2041
static int instance;
2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060
static int acpi_video_resume(struct acpi_device *device)
{
	struct acpi_video_bus *video;
	struct acpi_video_device *video_device;
	int i;

	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;
		if (video_device && video_device->backlight)
			acpi_video_set_brightness(video_device->backlight);
	}
	return AE_OK;
}

L
Len Brown 已提交
2061
static int acpi_video_bus_add(struct acpi_device *device)
L
Linus Torvalds 已提交
2062
{
2063 2064
	acpi_status status;
	struct acpi_video_bus *video;
2065
	struct input_dev *input;
2066
	int error;
L
Linus Torvalds 已提交
2067

2068
	video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
L
Linus Torvalds 已提交
2069
	if (!video)
2070
		return -ENOMEM;
L
Linus Torvalds 已提交
2071

2072 2073 2074 2075 2076 2077
	/* 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 ++;
	}
2078 2079 2080 2081 2082 2083
	/* 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++;
	}
2084

2085
	video->device = device;
L
Linus Torvalds 已提交
2086 2087
	strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
	strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
2088
	device->driver_data = video;
L
Linus Torvalds 已提交
2089 2090

	acpi_video_bus_find_cap(video);
2091 2092 2093
	error = acpi_video_bus_check(video);
	if (error)
		goto err_free_video;
L
Linus Torvalds 已提交
2094

2095 2096 2097
	error = acpi_video_bus_add_fs(device);
	if (error)
		goto err_free_video;
L
Linus Torvalds 已提交
2098

2099
	mutex_init(&video->device_list_lock);
L
Linus Torvalds 已提交
2100 2101 2102 2103 2104
	INIT_LIST_HEAD(&video->video_device_list);

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

2105
	status = acpi_install_notify_handler(device->handle,
L
Len Brown 已提交
2106 2107
					     ACPI_DEVICE_NOTIFY,
					     acpi_video_bus_notify, video);
L
Linus Torvalds 已提交
2108
	if (ACPI_FAILURE(status)) {
2109 2110
		printk(KERN_ERR PREFIX
				  "Error installing notify handler\n");
2111 2112
		error = -ENODEV;
		goto err_stop_video;
L
Linus Torvalds 已提交
2113 2114
	}

2115
	video->input = input = input_allocate_device();
2116 2117 2118 2119
	if (!input) {
		error = -ENOMEM;
		goto err_uninstall_notify;
	}
2120 2121 2122 2123 2124 2125 2126 2127

	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;
2128
	input->dev.parent = &device->dev;
2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139
	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);
	set_bit(KEY_UNKNOWN, input->keybit);

2140 2141 2142
	error = input_register_device(input);
	if (error)
		goto err_free_input_dev;
2143

L
Linus Torvalds 已提交
2144
	printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s  rom: %s  post: %s)\n",
L
Len Brown 已提交
2145 2146 2147 2148
	       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 已提交
2149

2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163
	return 0;

 err_free_input_dev:
	input_free_device(input);
 err_uninstall_notify:
	acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
				   acpi_video_bus_notify);
 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);
2164
	device->driver_data = NULL;
L
Linus Torvalds 已提交
2165

2166
	return error;
L
Linus Torvalds 已提交
2167 2168
}

L
Len Brown 已提交
2169
static int acpi_video_bus_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
2170
{
L
Len Brown 已提交
2171 2172
	acpi_status status = 0;
	struct acpi_video_bus *video = NULL;
L
Linus Torvalds 已提交
2173 2174 2175


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

2178
	video = acpi_driver_data(device);
L
Linus Torvalds 已提交
2179 2180 2181

	acpi_video_bus_stop_devices(video);

2182
	status = acpi_remove_notify_handler(video->device->handle,
L
Len Brown 已提交
2183 2184
					    ACPI_DEVICE_NOTIFY,
					    acpi_video_bus_notify);
L
Linus Torvalds 已提交
2185 2186 2187 2188

	acpi_video_bus_put_devices(video);
	acpi_video_bus_remove_fs(device);

2189
	input_unregister_device(video->input);
2190
	kfree(video->attached_array);
L
Linus Torvalds 已提交
2191 2192
	kfree(video);

2193
	return 0;
L
Linus Torvalds 已提交
2194 2195
}

L
Len Brown 已提交
2196
static int __init acpi_video_init(void)
L
Linus Torvalds 已提交
2197
{
L
Len Brown 已提交
2198
	int result = 0;
L
Linus Torvalds 已提交
2199 2200 2201

	acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
	if (!acpi_video_dir)
2202
		return -ENODEV;
L
Linus Torvalds 已提交
2203 2204 2205 2206 2207
	acpi_video_dir->owner = THIS_MODULE;

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

2211
	return 0;
L
Linus Torvalds 已提交
2212 2213
}

L
Len Brown 已提交
2214
static void __exit acpi_video_exit(void)
L
Linus Torvalds 已提交
2215 2216 2217 2218 2219 2220
{

	acpi_bus_unregister_driver(&acpi_video_bus);

	remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);

2221
	return;
L
Linus Torvalds 已提交
2222 2223 2224 2225
}

module_init(acpi_video_init);
module_exit(acpi_video_exit);