video.c 45.8 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  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>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

#include <asm/uaccess.h>

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

#define ACPI_VIDEO_COMPONENT		0x08000000
#define ACPI_VIDEO_CLASS		"video"
#define ACPI_VIDEO_DRIVER_NAME		"ACPI Video Driver"
#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

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

#define ACPI_VIDEO_HEAD_INVALID		(~0u - 1)
#define ACPI_VIDEO_HEAD_END		(~0u)

#define _COMPONENT		ACPI_VIDEO_COMPONENT
L
Len Brown 已提交
61
ACPI_MODULE_NAME("acpi_video")
L
Linus Torvalds 已提交
62

L
Len Brown 已提交
63
    MODULE_AUTHOR("Bruno Ducrot");
L
Linus Torvalds 已提交
64 65 66
MODULE_DESCRIPTION(ACPI_VIDEO_DRIVER_NAME);
MODULE_LICENSE("GPL");

L
Len Brown 已提交
67 68 69 70
static int acpi_video_bus_add(struct acpi_device *device);
static int acpi_video_bus_remove(struct acpi_device *device, int type);
static int acpi_video_bus_match(struct acpi_device *device,
				struct acpi_driver *driver);
L
Linus Torvalds 已提交
71 72 73 74 75 76 77 78

static struct acpi_driver acpi_video_bus = {
	.name = ACPI_VIDEO_DRIVER_NAME,
	.class = ACPI_VIDEO_CLASS,
	.ops = {
		.add = acpi_video_bus_add,
		.remove = acpi_video_bus_remove,
		.match = acpi_video_bus_match,
L
Len Brown 已提交
79
		},
L
Linus Torvalds 已提交
80 81 82
};

struct acpi_video_bus_flags {
L
Len Brown 已提交
83 84 85 86
	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 已提交
87 88 89
};

struct acpi_video_bus_cap {
L
Len Brown 已提交
90 91 92 93 94 95 96
	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 已提交
97 98
};

L
Len Brown 已提交
99 100 101 102 103 104 105 106 107 108 109
struct acpi_video_device_attrib {
	u32 display_index:4;	/* A zero-based instance of the Display */
	u32 display_port_attachment:4;	/*This field differenates displays type */
	u32 display_type:4;	/*Describe the specific type in use */
	u32 vendor_specific:4;	/*Chipset Vendor Specifi */
	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 已提交
110 111 112 113 114
};

struct acpi_video_enumerated_device {
	union {
		u32 int_val;
L
Len Brown 已提交
115
		struct acpi_video_device_attrib attrib;
L
Linus Torvalds 已提交
116 117 118 119 120
	} value;
	struct acpi_video_device *bind_info;
};

struct acpi_video_bus {
121
	struct acpi_device *device;
L
Len Brown 已提交
122
	u8 dos_setting;
L
Linus Torvalds 已提交
123
	struct acpi_video_enumerated_device *attached_array;
L
Len Brown 已提交
124 125
	u8 attached_count;
	struct acpi_video_bus_cap cap;
L
Linus Torvalds 已提交
126
	struct acpi_video_bus_flags flags;
L
Len Brown 已提交
127 128 129
	struct semaphore sem;
	struct list_head video_device_list;
	struct proc_dir_entry *dir;
L
Linus Torvalds 已提交
130 131 132
};

struct acpi_video_device_flags {
L
Len Brown 已提交
133 134 135 136 137 138
	u8 crt:1;
	u8 lcd:1;
	u8 tvout:1;
	u8 bios:1;
	u8 unknown:1;
	u8 reserved:3;
L
Linus Torvalds 已提交
139 140 141
};

struct acpi_video_device_cap {
L
Len Brown 已提交
142 143 144 145 146 147 148 149
	u8 _ADR:1;		/*Return the unique ID */
	u8 _BCL:1;		/*Query list of brightness control levels supported */
	u8 _BCM:1;		/*Set the brightness level */
	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 */
	u8 _reserved:1;
L
Linus Torvalds 已提交
150 151 152
};

struct acpi_video_device_brightness {
L
Len Brown 已提交
153 154 155
	int curr;
	int count;
	int *levels;
L
Linus Torvalds 已提交
156 157 158
};

struct acpi_video_device {
L
Len Brown 已提交
159 160 161 162 163 164
	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 已提交
165 166 167 168 169 170
	struct acpi_video_device_brightness *brightness;
};

/* bus */
static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file);
static struct file_operations acpi_video_bus_info_fops = {
L
Len Brown 已提交
171 172 173 174
	.open = acpi_video_bus_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
175 176 177 178
};

static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file);
static struct file_operations acpi_video_bus_ROM_fops = {
L
Len Brown 已提交
179 180 181 182
	.open = acpi_video_bus_ROM_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
183 184
};

L
Len Brown 已提交
185 186
static int acpi_video_bus_POST_info_open_fs(struct inode *inode,
					    struct file *file);
L
Linus Torvalds 已提交
187
static struct file_operations acpi_video_bus_POST_info_fops = {
L
Len Brown 已提交
188 189 190 191
	.open = acpi_video_bus_POST_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
192 193 194 195
};

static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file);
static struct file_operations acpi_video_bus_POST_fops = {
L
Len Brown 已提交
196 197 198 199
	.open = acpi_video_bus_POST_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
200 201 202 203
};

static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file);
static struct file_operations acpi_video_bus_DOS_fops = {
L
Len Brown 已提交
204 205 206 207
	.open = acpi_video_bus_DOS_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
208 209 210
};

/* device */
L
Len Brown 已提交
211 212
static int acpi_video_device_info_open_fs(struct inode *inode,
					  struct file *file);
L
Linus Torvalds 已提交
213
static struct file_operations acpi_video_device_info_fops = {
L
Len Brown 已提交
214 215 216 217
	.open = acpi_video_device_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
218 219
};

L
Len Brown 已提交
220 221
static int acpi_video_device_state_open_fs(struct inode *inode,
					   struct file *file);
L
Linus Torvalds 已提交
222
static struct file_operations acpi_video_device_state_fops = {
L
Len Brown 已提交
223 224 225 226
	.open = acpi_video_device_state_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
227 228
};

L
Len Brown 已提交
229 230
static int acpi_video_device_brightness_open_fs(struct inode *inode,
						struct file *file);
L
Linus Torvalds 已提交
231
static struct file_operations acpi_video_device_brightness_fops = {
L
Len Brown 已提交
232 233 234 235
	.open = acpi_video_device_brightness_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
236 237
};

L
Len Brown 已提交
238 239
static int acpi_video_device_EDID_open_fs(struct inode *inode,
					  struct file *file);
L
Linus Torvalds 已提交
240
static struct file_operations acpi_video_device_EDID_fops = {
L
Len Brown 已提交
241 242 243 244
	.open = acpi_video_device_EDID_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
245 246
};

L
Len Brown 已提交
247
static char device_decode[][30] = {
L
Linus Torvalds 已提交
248 249 250 251 252 253
	"motherboard VGA device",
	"PCI VGA device",
	"AGP VGA device",
	"UNKNOWN",
};

L
Len Brown 已提交
254 255 256 257
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 已提交
258
static int acpi_video_device_enumerate(struct acpi_video_bus *video);
L
Len Brown 已提交
259 260 261 262 263
static int acpi_video_switch_output(struct acpi_video_bus *video, int event);
static int acpi_video_get_next_level(struct acpi_video_device *device,
				     u32 level_current, u32 event);
static void acpi_video_switch_brightness(struct acpi_video_device *device,
					 int event);
L
Linus Torvalds 已提交
264 265 266 267 268 269 270 271

/* --------------------------------------------------------------------------
                               Video Management
   -------------------------------------------------------------------------- */

/* device */

static int
L
Len Brown 已提交
272
acpi_video_device_query(struct acpi_video_device *device, unsigned long *state)
L
Linus Torvalds 已提交
273
{
L
Len Brown 已提交
274
	int status;
275 276

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

278
	return status;
L
Linus Torvalds 已提交
279 280 281
}

static int
L
Len Brown 已提交
282 283
acpi_video_device_get_state(struct acpi_video_device *device,
			    unsigned long *state)
L
Linus Torvalds 已提交
284
{
L
Len Brown 已提交
285
	int status;
L
Linus Torvalds 已提交
286

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

289
	return status;
L
Linus Torvalds 已提交
290 291 292
}

static int
L
Len Brown 已提交
293
acpi_video_device_set_state(struct acpi_video_device *device, int state)
L
Linus Torvalds 已提交
294
{
L
Len Brown 已提交
295 296 297
	int status;
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
298
	unsigned long ret;
L
Linus Torvalds 已提交
299 300 301


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

304
	return status;
L
Linus Torvalds 已提交
305 306 307
}

static int
L
Len Brown 已提交
308 309
acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
				   union acpi_object **levels)
L
Linus Torvalds 已提交
310
{
L
Len Brown 已提交
311 312 313
	int status;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *obj;
L
Linus Torvalds 已提交
314 315 316 317


	*levels = NULL;

318
	status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer);
L
Linus Torvalds 已提交
319
	if (!ACPI_SUCCESS(status))
320
		return status;
L
Len Brown 已提交
321
	obj = (union acpi_object *)buffer.pointer;
322
	if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
323
		printk(KERN_ERR PREFIX "Invalid _BCL data\n");
L
Linus Torvalds 已提交
324 325 326 327 328 329
		status = -EFAULT;
		goto err;
	}

	*levels = obj;

330
	return 0;
L
Linus Torvalds 已提交
331

L
Len Brown 已提交
332
      err:
333
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
334

335
	return status;
L
Linus Torvalds 已提交
336 337 338
}

static int
L
Len Brown 已提交
339
acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
L
Linus Torvalds 已提交
340
{
L
Len Brown 已提交
341 342 343
	int status;
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
L
Linus Torvalds 已提交
344 345 346


	arg0.integer.value = level;
347
	status = acpi_evaluate_object(device->dev->handle, "_BCM", &args, NULL);
L
Linus Torvalds 已提交
348 349

	printk(KERN_DEBUG "set_level status: %x\n", status);
350
	return status;
L
Linus Torvalds 已提交
351 352 353
}

static int
L
Len Brown 已提交
354 355
acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
					unsigned long *level)
L
Linus Torvalds 已提交
356
{
L
Len Brown 已提交
357
	int status;
L
Linus Torvalds 已提交
358

359
	status = acpi_evaluate_integer(device->dev->handle, "_BQC", NULL, level);
L
Linus Torvalds 已提交
360

361
	return status;
L
Linus Torvalds 已提交
362 363 364
}

static int
L
Len Brown 已提交
365 366
acpi_video_device_EDID(struct acpi_video_device *device,
		       union acpi_object **edid, ssize_t length)
L
Linus Torvalds 已提交
367
{
L
Len Brown 已提交
368 369 370 371 372
	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 已提交
373 374 375 376 377


	*edid = NULL;

	if (!device)
378
		return -ENODEV;
L
Linus Torvalds 已提交
379 380 381 382 383
	if (length == 128)
		arg0.integer.value = 1;
	else if (length == 256)
		arg0.integer.value = 2;
	else
384
		return -EINVAL;
L
Linus Torvalds 已提交
385

386
	status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer);
L
Linus Torvalds 已提交
387
	if (ACPI_FAILURE(status))
388
		return -ENODEV;
L
Linus Torvalds 已提交
389

L
Len Brown 已提交
390
	obj = (union acpi_object *)buffer.pointer;
L
Linus Torvalds 已提交
391 392 393 394

	if (obj && obj->type == ACPI_TYPE_BUFFER)
		*edid = obj;
	else {
395
		printk(KERN_ERR PREFIX "Invalid _DDC data\n");
L
Linus Torvalds 已提交
396 397 398 399
		status = -EFAULT;
		kfree(obj);
	}

400
	return status;
L
Linus Torvalds 已提交
401 402 403 404 405
}

/* bus */

static int
L
Len Brown 已提交
406
acpi_video_bus_set_POST(struct acpi_video_bus *video, unsigned long option)
L
Linus Torvalds 已提交
407
{
L
Len Brown 已提交
408 409 410 411
	int status;
	unsigned long tmp;
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
L
Linus Torvalds 已提交
412 413 414 415


	arg0.integer.value = option;

416
	status = acpi_evaluate_integer(video->device->handle, "_SPD", &args, &tmp);
L
Linus Torvalds 已提交
417
	if (ACPI_SUCCESS(status))
L
Len Brown 已提交
418
		status = tmp ? (-EINVAL) : (AE_OK);
L
Linus Torvalds 已提交
419

420
	return status;
L
Linus Torvalds 已提交
421 422 423
}

static int
L
Len Brown 已提交
424
acpi_video_bus_get_POST(struct acpi_video_bus *video, unsigned long *id)
L
Linus Torvalds 已提交
425 426 427
{
	int status;

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

430
	return status;
L
Linus Torvalds 已提交
431 432 433
}

static int
L
Len Brown 已提交
434 435
acpi_video_bus_POST_options(struct acpi_video_bus *video,
			    unsigned long *options)
L
Linus Torvalds 已提交
436
{
L
Len Brown 已提交
437
	int status;
L
Linus Torvalds 已提交
438

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

442
	return status;
L
Linus Torvalds 已提交
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
}

/*
 *  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
 *			active display output. No swich event.
 *		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
 *			of the LCD, when the power changes from AC to DC
 *		1. 	The system BIOS should NOT automatically control the brightness 
 *			level of the LCD, when the power changes from AC to DC.
 * Return Value:
 * 		-1	wrong arg.
 */

static int
L
Len Brown 已提交
467
acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
L
Linus Torvalds 已提交
468
{
L
Len Brown 已提交
469 470 471
	acpi_integer status = 0;
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
L
Linus Torvalds 已提交
472 473


L
Len Brown 已提交
474
	if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) {
L
Linus Torvalds 已提交
475 476 477 478 479
		status = -1;
		goto Failed;
	}
	arg0.integer.value = (lcd_flag << 2) | bios_flag;
	video->dos_setting = arg0.integer.value;
480
	acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL);
L
Linus Torvalds 已提交
481

L
Len Brown 已提交
482
      Failed:
483
	return status;
L
Linus Torvalds 已提交
484 485 486 487 488 489 490 491 492 493 494 495 496
}

/*
 *  Arg:	
 *  	device	: video output device (LCD, CRT, ..)
 *
 *  Return Value:
 *  	None
 *
 *  Find out all required AML method defined under the output
 *  device.
 */

L
Len Brown 已提交
497
static void acpi_video_device_find_cap(struct acpi_video_device *device)
L
Linus Torvalds 已提交
498
{
L
Len Brown 已提交
499
	acpi_integer status;
L
Linus Torvalds 已提交
500 501 502 503 504 505
	acpi_handle h_dummy1;
	int i;
	union acpi_object *obj = NULL;
	struct acpi_video_device_brightness *br = NULL;


L
Len Brown 已提交
506
	memset(&device->cap, 0, 4);
L
Linus Torvalds 已提交
507

508
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
L
Linus Torvalds 已提交
509 510
		device->cap._ADR = 1;
	}
511
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) {
L
Len Brown 已提交
512
		device->cap._BCL = 1;
L
Linus Torvalds 已提交
513
	}
514
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
L
Len Brown 已提交
515
		device->cap._BCM = 1;
L
Linus Torvalds 已提交
516
	}
517
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
L
Len Brown 已提交
518
		device->cap._DDC = 1;
L
Linus Torvalds 已提交
519
	}
520
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DCS", &h_dummy1))) {
L
Linus Torvalds 已提交
521 522
		device->cap._DCS = 1;
	}
523
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DGS", &h_dummy1))) {
L
Linus Torvalds 已提交
524 525
		device->cap._DGS = 1;
	}
526
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DSS", &h_dummy1))) {
L
Linus Torvalds 已提交
527 528 529 530 531 532 533 534
		device->cap._DSS = 1;
	}

	status = acpi_video_device_lcd_query_levels(device, &obj);

	if (obj && obj->type == ACPI_TYPE_PACKAGE && obj->package.count >= 2) {
		int count = 0;
		union acpi_object *o;
L
Len Brown 已提交
535

536
		br = kmalloc(sizeof(*br), GFP_KERNEL);
L
Linus Torvalds 已提交
537 538 539
		if (!br) {
			printk(KERN_ERR "can't allocate memory\n");
		} else {
540 541
			memset(br, 0, sizeof(*br));
			br->levels = kmalloc(obj->package.count *
L
Len Brown 已提交
542
					     sizeof *(br->levels), GFP_KERNEL);
L
Linus Torvalds 已提交
543 544 545 546
			if (!br->levels)
				goto out;

			for (i = 0; i < obj->package.count; i++) {
L
Len Brown 已提交
547 548
				o = (union acpi_object *)&obj->package.
				    elements[i];
L
Linus Torvalds 已提交
549
				if (o->type != ACPI_TYPE_INTEGER) {
550
					printk(KERN_ERR PREFIX "Invalid data\n");
L
Linus Torvalds 已提交
551 552 553 554 555
					continue;
				}
				br->levels[count] = (u32) o->integer.value;
				count++;
			}
L
Len Brown 已提交
556
		      out:
L
Linus Torvalds 已提交
557
			if (count < 2) {
558
				kfree(br->levels);
L
Linus Torvalds 已提交
559 560 561 562
				kfree(br);
			} else {
				br->count = count;
				device->brightness = br;
L
Len Brown 已提交
563 564 565
				ACPI_DEBUG_PRINT((ACPI_DB_INFO,
						  "found %d brightness levels\n",
						  count));
L
Linus Torvalds 已提交
566 567 568 569
			}
		}
	}

570
	kfree(obj);
L
Linus Torvalds 已提交
571

572
	return;
L
Linus Torvalds 已提交
573 574 575 576 577 578 579 580 581 582 583 584
}

/*
 *  Arg:	
 *  	device	: video output device (VGA)
 *
 *  Return Value:
 *  	None
 *
 *  Find out all required AML method defined under the video bus device.
 */

L
Len Brown 已提交
585
static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
586
{
L
Len Brown 已提交
587
	acpi_handle h_dummy1;
L
Linus Torvalds 已提交
588

L
Len Brown 已提交
589
	memset(&video->cap, 0, 4);
590
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
L
Linus Torvalds 已提交
591 592
		video->cap._DOS = 1;
	}
593
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) {
L
Linus Torvalds 已提交
594 595
		video->cap._DOD = 1;
	}
596
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) {
L
Linus Torvalds 已提交
597 598
		video->cap._ROM = 1;
	}
599
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) {
L
Linus Torvalds 已提交
600 601
		video->cap._GPD = 1;
	}
602
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) {
L
Linus Torvalds 已提交
603 604
		video->cap._SPD = 1;
	}
605
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) {
L
Linus Torvalds 已提交
606 607 608 609 610 611 612 613 614
		video->cap._VPO = 1;
	}
}

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

L
Len Brown 已提交
615
static int acpi_video_bus_check(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
616
{
L
Len Brown 已提交
617
	acpi_status status = -ENOENT;
L
Linus Torvalds 已提交
618 619 620


	if (!video)
621
		return -EINVAL;
L
Linus Torvalds 已提交
622 623 624 625 626 627

	/* Since there is no HID, CID and so on for VGA driver, we have
	 * to check well known required nodes.
	 */

	/* Does this device able to support video switching ? */
L
Len Brown 已提交
628
	if (video->cap._DOS) {
L
Linus Torvalds 已提交
629 630 631 632 633
		video->flags.multihead = 1;
		status = 0;
	}

	/* Does this device able to retrieve a retrieve a video ROM ? */
L
Len Brown 已提交
634
	if (video->cap._ROM) {
L
Linus Torvalds 已提交
635 636 637 638 639
		video->flags.rom = 1;
		status = 0;
	}

	/* Does this device able to configure which video device to POST ? */
L
Len Brown 已提交
640
	if (video->cap._GPD && video->cap._SPD && video->cap._VPO) {
L
Linus Torvalds 已提交
641 642 643 644
		video->flags.post = 1;
		status = 0;
	}

645
	return status;
L
Linus Torvalds 已提交
646 647 648 649 650 651
}

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

L
Len Brown 已提交
652
static struct proc_dir_entry *acpi_video_dir;
L
Linus Torvalds 已提交
653 654 655

/* video devices */

L
Len Brown 已提交
656
static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
657
{
L
Len Brown 已提交
658 659
	struct acpi_video_device *dev =
	    (struct acpi_video_device *)seq->private;
L
Linus Torvalds 已提交
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675


	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");
	else
		seq_printf(seq, "UNKNOWN\n");

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

L
Len Brown 已提交
678
      end:
679
	return 0;
L
Linus Torvalds 已提交
680 681 682
}

static int
L
Len Brown 已提交
683
acpi_video_device_info_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
684 685 686 687 688
{
	return single_open(file, acpi_video_device_info_seq_show,
			   PDE(inode)->data);
}

L
Len Brown 已提交
689
static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
690
{
L
Len Brown 已提交
691 692 693 694
	int status;
	struct acpi_video_device *dev =
	    (struct acpi_video_device *)seq->private;
	unsigned long state;
L
Linus Torvalds 已提交
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713


	if (!dev)
		goto end;

	status = acpi_video_device_get_state(dev, &state);
	seq_printf(seq, "state:     ");
	if (ACPI_SUCCESS(status))
		seq_printf(seq, "0x%02lx\n", state);
	else
		seq_printf(seq, "<not supported>\n");

	status = acpi_video_device_query(dev, &state);
	seq_printf(seq, "query:     ");
	if (ACPI_SUCCESS(status))
		seq_printf(seq, "0x%02lx\n", state);
	else
		seq_printf(seq, "<not supported>\n");

L
Len Brown 已提交
714
      end:
715
	return 0;
L
Linus Torvalds 已提交
716 717 718
}

static int
L
Len Brown 已提交
719
acpi_video_device_state_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
720 721 722 723 724 725
{
	return single_open(file, acpi_video_device_state_seq_show,
			   PDE(inode)->data);
}

static ssize_t
L
Len Brown 已提交
726 727 728
acpi_video_device_write_state(struct file *file,
			      const char __user * buffer,
			      size_t count, loff_t * data)
L
Linus Torvalds 已提交
729
{
L
Len Brown 已提交
730 731 732 733 734
	int status;
	struct seq_file *m = (struct seq_file *)file->private_data;
	struct acpi_video_device *dev = (struct acpi_video_device *)m->private;
	char str[12] = { 0 };
	u32 state = 0;
L
Linus Torvalds 已提交
735 736 737


	if (!dev || count + 1 > sizeof str)
738
		return -EINVAL;
L
Linus Torvalds 已提交
739 740

	if (copy_from_user(str, buffer, count))
741
		return -EFAULT;
L
Linus Torvalds 已提交
742 743 744

	str[count] = 0;
	state = simple_strtoul(str, NULL, 0);
L
Len Brown 已提交
745
	state &= ((1ul << 31) | (1ul << 30) | (1ul << 0));
L
Linus Torvalds 已提交
746 747 748 749

	status = acpi_video_device_set_state(dev, state);

	if (status)
750
		return -EFAULT;
L
Linus Torvalds 已提交
751

752
	return count;
L
Linus Torvalds 已提交
753 754 755
}

static int
L
Len Brown 已提交
756
acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
757
{
L
Len Brown 已提交
758 759 760
	struct acpi_video_device *dev =
	    (struct acpi_video_device *)seq->private;
	int i;
L
Linus Torvalds 已提交
761 762 763 764


	if (!dev || !dev->brightness) {
		seq_printf(seq, "<not supported>\n");
765
		return 0;
L
Linus Torvalds 已提交
766 767 768 769 770 771 772
	}

	seq_printf(seq, "levels: ");
	for (i = 0; i < dev->brightness->count; i++)
		seq_printf(seq, " %d", dev->brightness->levels[i]);
	seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr);

773
	return 0;
L
Linus Torvalds 已提交
774 775 776
}

static int
L
Len Brown 已提交
777
acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
778 779 780 781 782 783
{
	return single_open(file, acpi_video_device_brightness_seq_show,
			   PDE(inode)->data);
}

static ssize_t
L
Len Brown 已提交
784 785 786
acpi_video_device_write_brightness(struct file *file,
				   const char __user * buffer,
				   size_t count, loff_t * data)
L
Linus Torvalds 已提交
787
{
L
Len Brown 已提交
788 789 790 791 792
	struct seq_file *m = (struct seq_file *)file->private_data;
	struct acpi_video_device *dev = (struct acpi_video_device *)m->private;
	char str[4] = { 0 };
	unsigned int level = 0;
	int i;
L
Linus Torvalds 已提交
793 794


795
	if (!dev || !dev->brightness || count + 1 > sizeof str)
796
		return -EINVAL;
L
Linus Torvalds 已提交
797 798

	if (copy_from_user(str, buffer, count))
799
		return -EFAULT;
L
Linus Torvalds 已提交
800 801 802

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

L
Linus Torvalds 已提交
804
	if (level > 100)
805
		return -EFAULT;
L
Linus Torvalds 已提交
806 807 808 809

	/* validate though the list of available levels */
	for (i = 0; i < dev->brightness->count; i++)
		if (level == dev->brightness->levels[i]) {
L
Len Brown 已提交
810 811
			if (ACPI_SUCCESS
			    (acpi_video_device_lcd_set_level(dev, level)))
L
Linus Torvalds 已提交
812 813 814 815
				dev->brightness->curr = level;
			break;
		}

816
	return count;
L
Linus Torvalds 已提交
817 818
}

L
Len Brown 已提交
819
static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
820
{
L
Len Brown 已提交
821 822 823 824 825
	struct acpi_video_device *dev =
	    (struct acpi_video_device *)seq->private;
	int status;
	int i;
	union acpi_object *edid = NULL;
L
Linus Torvalds 已提交
826 827 828 829 830


	if (!dev)
		goto out;

L
Len Brown 已提交
831
	status = acpi_video_device_EDID(dev, &edid, 128);
L
Linus Torvalds 已提交
832
	if (ACPI_FAILURE(status)) {
L
Len Brown 已提交
833
		status = acpi_video_device_EDID(dev, &edid, 256);
L
Linus Torvalds 已提交
834 835 836 837 838 839 840 841 842 843 844
	}

	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 已提交
845
      out:
L
Linus Torvalds 已提交
846 847 848 849 850
	if (!edid)
		seq_printf(seq, "<not supported>\n");
	else
		kfree(edid);

851
	return 0;
L
Linus Torvalds 已提交
852 853 854
}

static int
L
Len Brown 已提交
855
acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
856 857 858 859 860
{
	return single_open(file, acpi_video_device_EDID_seq_show,
			   PDE(inode)->data);
}

L
Len Brown 已提交
861
static int acpi_video_device_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
862
{
L
Len Brown 已提交
863
	struct proc_dir_entry *entry = NULL;
L
Linus Torvalds 已提交
864 865 866 867
	struct acpi_video_device *vid_dev;


	if (!device)
868
		return -ENODEV;
L
Linus Torvalds 已提交
869

L
Len Brown 已提交
870
	vid_dev = (struct acpi_video_device *)acpi_driver_data(device);
L
Linus Torvalds 已提交
871
	if (!vid_dev)
872
		return -ENODEV;
L
Linus Torvalds 已提交
873 874 875

	if (!acpi_device_dir(device)) {
		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
L
Len Brown 已提交
876
						     vid_dev->video->dir);
L
Linus Torvalds 已提交
877
		if (!acpi_device_dir(device))
878
			return -ENODEV;
L
Linus Torvalds 已提交
879 880 881 882 883 884
		acpi_device_dir(device)->owner = THIS_MODULE;
	}

	/* 'info' [R] */
	entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device));
	if (!entry)
885
		return -ENODEV;
L
Linus Torvalds 已提交
886 887 888 889 890 891 892
	else {
		entry->proc_fops = &acpi_video_device_info_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}

	/* 'state' [R/W] */
L
Len Brown 已提交
893 894 895
	entry =
	    create_proc_entry("state", S_IFREG | S_IRUGO | S_IWUSR,
			      acpi_device_dir(device));
L
Linus Torvalds 已提交
896
	if (!entry)
897
		return -ENODEV;
L
Linus Torvalds 已提交
898
	else {
899
		acpi_video_device_state_fops.write = acpi_video_device_write_state;
L
Linus Torvalds 已提交
900 901 902 903 904 905
		entry->proc_fops = &acpi_video_device_state_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}

	/* 'brightness' [R/W] */
L
Len Brown 已提交
906 907 908
	entry =
	    create_proc_entry("brightness", S_IFREG | S_IRUGO | S_IWUSR,
			      acpi_device_dir(device));
L
Linus Torvalds 已提交
909
	if (!entry)
910
		return -ENODEV;
L
Linus Torvalds 已提交
911
	else {
912
		acpi_video_device_brightness_fops.write = acpi_video_device_write_brightness;
L
Linus Torvalds 已提交
913 914 915 916 917 918 919 920
		entry->proc_fops = &acpi_video_device_brightness_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}

	/* 'EDID' [R] */
	entry = create_proc_entry("EDID", S_IRUGO, acpi_device_dir(device));
	if (!entry)
921
		return -ENODEV;
L
Linus Torvalds 已提交
922 923 924 925 926 927
	else {
		entry->proc_fops = &acpi_video_device_EDID_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}

928
	return 0;
L
Linus Torvalds 已提交
929 930
}

L
Len Brown 已提交
931
static int acpi_video_device_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
932 933 934
{
	struct acpi_video_device *vid_dev;

L
Len Brown 已提交
935
	vid_dev = (struct acpi_video_device *)acpi_driver_data(device);
L
Linus Torvalds 已提交
936
	if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
937
		return -ENODEV;
L
Linus Torvalds 已提交
938 939 940 941 942 943

	if (acpi_device_dir(device)) {
		remove_proc_entry("info", acpi_device_dir(device));
		remove_proc_entry("state", acpi_device_dir(device));
		remove_proc_entry("brightness", acpi_device_dir(device));
		remove_proc_entry("EDID", acpi_device_dir(device));
L
Len Brown 已提交
944
		remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
L
Linus Torvalds 已提交
945 946 947
		acpi_device_dir(device) = NULL;
	}

948
	return 0;
L
Linus Torvalds 已提交
949 950 951
}

/* video bus */
L
Len Brown 已提交
952
static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
953
{
L
Len Brown 已提交
954
	struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
L
Linus Torvalds 已提交
955 956 957 958 959 960


	if (!video)
		goto end;

	seq_printf(seq, "Switching heads:              %s\n",
L
Len Brown 已提交
961
		   video->flags.multihead ? "yes" : "no");
L
Linus Torvalds 已提交
962
	seq_printf(seq, "Video ROM:                    %s\n",
L
Len Brown 已提交
963
		   video->flags.rom ? "yes" : "no");
L
Linus Torvalds 已提交
964
	seq_printf(seq, "Device to be POSTed on boot:  %s\n",
L
Len Brown 已提交
965
		   video->flags.post ? "yes" : "no");
L
Linus Torvalds 已提交
966

L
Len Brown 已提交
967
      end:
968
	return 0;
L
Linus Torvalds 已提交
969 970
}

L
Len Brown 已提交
971
static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
972
{
L
Len Brown 已提交
973 974
	return single_open(file, acpi_video_bus_info_seq_show,
			   PDE(inode)->data);
L
Linus Torvalds 已提交
975 976
}

L
Len Brown 已提交
977
static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
978
{
L
Len Brown 已提交
979
	struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
L
Linus Torvalds 已提交
980 981 982 983 984 985 986 987


	if (!video)
		goto end;

	printk(KERN_INFO PREFIX "Please implement %s\n", __FUNCTION__);
	seq_printf(seq, "<TODO>\n");

L
Len Brown 已提交
988
      end:
989
	return 0;
L
Linus Torvalds 已提交
990 991
}

L
Len Brown 已提交
992
static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
993 994 995 996
{
	return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data);
}

L
Len Brown 已提交
997
static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
998
{
L
Len Brown 已提交
999 1000 1001
	struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
	unsigned long options;
	int status;
L
Linus Torvalds 已提交
1002 1003 1004 1005 1006 1007 1008 1009


	if (!video)
		goto end;

	status = acpi_video_bus_POST_options(video, &options);
	if (ACPI_SUCCESS(status)) {
		if (!(options & 1)) {
L
Len Brown 已提交
1010 1011 1012 1013
			printk(KERN_WARNING PREFIX
			       "The motherboard VGA device is not listed as a possible POST device.\n");
			printk(KERN_WARNING PREFIX
			       "This indicate a BIOS bug.  Please contact the manufacturer.\n");
L
Linus Torvalds 已提交
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
		}
		printk("%lx\n", options);
		seq_printf(seq, "can POST: <intgrated video>");
		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 已提交
1024
      end:
1025
	return 0;
L
Linus Torvalds 已提交
1026 1027 1028
}

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

L
Len Brown 已提交
1035
static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1036
{
L
Len Brown 已提交
1037 1038 1039
	struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
	int status;
	unsigned long id;
L
Linus Torvalds 已提交
1040 1041 1042 1043 1044


	if (!video)
		goto end;

L
Len Brown 已提交
1045
	status = acpi_video_bus_get_POST(video, &id);
L
Linus Torvalds 已提交
1046 1047 1048 1049
	if (!ACPI_SUCCESS(status)) {
		seq_printf(seq, "<not supported>\n");
		goto end;
	}
L
Len Brown 已提交
1050
	seq_printf(seq, "device posted is <%s>\n", device_decode[id & 3]);
L
Linus Torvalds 已提交
1051

L
Len Brown 已提交
1052
      end:
1053
	return 0;
L
Linus Torvalds 已提交
1054 1055
}

L
Len Brown 已提交
1056
static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1057
{
L
Len Brown 已提交
1058
	struct acpi_video_bus *video = (struct acpi_video_bus *)seq->private;
L
Linus Torvalds 已提交
1059 1060


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

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

L
Len Brown 已提交
1066
static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1067
{
L
Len Brown 已提交
1068 1069
	return single_open(file, acpi_video_bus_POST_seq_show,
			   PDE(inode)->data);
L
Linus Torvalds 已提交
1070 1071
}

L
Len Brown 已提交
1072
static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1073 1074 1075 1076 1077
{
	return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data);
}

static ssize_t
L
Len Brown 已提交
1078 1079 1080
acpi_video_bus_write_POST(struct file *file,
			  const char __user * buffer,
			  size_t count, loff_t * data)
L
Linus Torvalds 已提交
1081
{
L
Len Brown 已提交
1082 1083 1084 1085 1086
	int status;
	struct seq_file *m = (struct seq_file *)file->private_data;
	struct acpi_video_bus *video = (struct acpi_video_bus *)m->private;
	char str[12] = { 0 };
	unsigned long opt, options;
L
Linus Torvalds 已提交
1087 1088 1089


	if (!video || count + 1 > sizeof str)
1090
		return -EINVAL;
L
Linus Torvalds 已提交
1091 1092 1093

	status = acpi_video_bus_POST_options(video, &options);
	if (!ACPI_SUCCESS(status))
1094
		return -EINVAL;
L
Linus Torvalds 已提交
1095 1096

	if (copy_from_user(str, buffer, count))
1097
		return -EFAULT;
L
Linus Torvalds 已提交
1098 1099 1100 1101

	str[count] = 0;
	opt = strtoul(str, NULL, 0);
	if (opt > 3)
1102
		return -EFAULT;
L
Linus Torvalds 已提交
1103 1104 1105 1106 1107

	/* just in case an OEM 'forget' the motherboard... */
	options |= 1;

	if (options & (1ul << opt)) {
L
Len Brown 已提交
1108
		status = acpi_video_bus_set_POST(video, opt);
L
Linus Torvalds 已提交
1109
		if (!ACPI_SUCCESS(status))
1110
			return -EFAULT;
L
Linus Torvalds 已提交
1111 1112 1113

	}

1114
	return count;
L
Linus Torvalds 已提交
1115 1116 1117
}

static ssize_t
L
Len Brown 已提交
1118 1119 1120
acpi_video_bus_write_DOS(struct file *file,
			 const char __user * buffer,
			 size_t count, loff_t * data)
L
Linus Torvalds 已提交
1121
{
L
Len Brown 已提交
1122 1123 1124 1125 1126
	int status;
	struct seq_file *m = (struct seq_file *)file->private_data;
	struct acpi_video_bus *video = (struct acpi_video_bus *)m->private;
	char str[12] = { 0 };
	unsigned long opt;
L
Linus Torvalds 已提交
1127 1128 1129


	if (!video || count + 1 > sizeof str)
1130
		return -EINVAL;
L
Linus Torvalds 已提交
1131 1132

	if (copy_from_user(str, buffer, count))
1133
		return -EFAULT;
L
Linus Torvalds 已提交
1134 1135 1136 1137

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

L
Len Brown 已提交
1140
	status = acpi_video_bus_DOS(video, opt & 0x3, (opt & 0x4) >> 2);
L
Linus Torvalds 已提交
1141 1142

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

1145
	return count;
L
Linus Torvalds 已提交
1146 1147
}

L
Len Brown 已提交
1148
static int acpi_video_bus_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1149
{
L
Len Brown 已提交
1150 1151
	struct proc_dir_entry *entry = NULL;
	struct acpi_video_bus *video;
L
Linus Torvalds 已提交
1152 1153


L
Len Brown 已提交
1154
	video = (struct acpi_video_bus *)acpi_driver_data(device);
L
Linus Torvalds 已提交
1155 1156 1157

	if (!acpi_device_dir(device)) {
		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
L
Len Brown 已提交
1158
						     acpi_video_dir);
L
Linus Torvalds 已提交
1159
		if (!acpi_device_dir(device))
1160
			return -ENODEV;
L
Linus Torvalds 已提交
1161 1162 1163 1164 1165 1166 1167
		video->dir = acpi_device_dir(device);
		acpi_device_dir(device)->owner = THIS_MODULE;
	}

	/* 'info' [R] */
	entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device));
	if (!entry)
1168
		return -ENODEV;
L
Linus Torvalds 已提交
1169 1170 1171 1172 1173 1174 1175 1176 1177
	else {
		entry->proc_fops = &acpi_video_bus_info_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}

	/* 'ROM' [R] */
	entry = create_proc_entry("ROM", S_IRUGO, acpi_device_dir(device));
	if (!entry)
1178
		return -ENODEV;
L
Linus Torvalds 已提交
1179 1180 1181 1182 1183 1184 1185
	else {
		entry->proc_fops = &acpi_video_bus_ROM_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}

	/* 'POST_info' [R] */
L
Len Brown 已提交
1186 1187
	entry =
	    create_proc_entry("POST_info", S_IRUGO, acpi_device_dir(device));
L
Linus Torvalds 已提交
1188
	if (!entry)
1189
		return -ENODEV;
L
Linus Torvalds 已提交
1190 1191 1192 1193 1194 1195 1196
	else {
		entry->proc_fops = &acpi_video_bus_POST_info_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}

	/* 'POST' [R/W] */
L
Len Brown 已提交
1197 1198 1199
	entry =
	    create_proc_entry("POST", S_IFREG | S_IRUGO | S_IRUSR,
			      acpi_device_dir(device));
L
Linus Torvalds 已提交
1200
	if (!entry)
1201
		return -ENODEV;
L
Linus Torvalds 已提交
1202
	else {
1203
		acpi_video_bus_POST_fops.write = acpi_video_bus_write_POST;
L
Linus Torvalds 已提交
1204 1205 1206 1207 1208 1209
		entry->proc_fops = &acpi_video_bus_POST_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}

	/* 'DOS' [R/W] */
L
Len Brown 已提交
1210 1211 1212
	entry =
	    create_proc_entry("DOS", S_IFREG | S_IRUGO | S_IRUSR,
			      acpi_device_dir(device));
L
Linus Torvalds 已提交
1213
	if (!entry)
1214
		return -ENODEV;
L
Linus Torvalds 已提交
1215
	else {
1216
		acpi_video_bus_DOS_fops.write = acpi_video_bus_write_DOS;
L
Linus Torvalds 已提交
1217 1218 1219 1220 1221
		entry->proc_fops = &acpi_video_bus_DOS_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}

1222
	return 0;
L
Linus Torvalds 已提交
1223 1224
}

L
Len Brown 已提交
1225
static int acpi_video_bus_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1226
{
L
Len Brown 已提交
1227
	struct acpi_video_bus *video;
L
Linus Torvalds 已提交
1228 1229


L
Len Brown 已提交
1230
	video = (struct acpi_video_bus *)acpi_driver_data(device);
L
Linus Torvalds 已提交
1231 1232 1233 1234 1235 1236 1237

	if (acpi_device_dir(device)) {
		remove_proc_entry("info", acpi_device_dir(device));
		remove_proc_entry("ROM", acpi_device_dir(device));
		remove_proc_entry("POST_info", acpi_device_dir(device));
		remove_proc_entry("POST", acpi_device_dir(device));
		remove_proc_entry("DOS", acpi_device_dir(device));
L
Len Brown 已提交
1238
		remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
L
Linus Torvalds 已提交
1239 1240 1241
		acpi_device_dir(device) = NULL;
	}

1242
	return 0;
L
Linus Torvalds 已提交
1243 1244 1245 1246 1247 1248 1249 1250 1251
}

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

/* device interface */

static int
L
Len Brown 已提交
1252 1253
acpi_video_bus_get_one_device(struct acpi_device *device,
			      struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1254
{
L
Len Brown 已提交
1255
	unsigned long device_id;
1256
	int status;
L
Len Brown 已提交
1257
	struct acpi_video_device *data;
L
Linus Torvalds 已提交
1258 1259 1260


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

L
Len Brown 已提交
1263 1264
	status =
	    acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
L
Linus Torvalds 已提交
1265 1266 1267 1268
	if (ACPI_SUCCESS(status)) {

		data = kmalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
		if (!data)
1269
			return -ENOMEM;
L
Linus Torvalds 已提交
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294

		memset(data, 0, sizeof(struct acpi_video_device));

		strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
		strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
		acpi_driver_data(device) = data;

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

		switch (device_id & 0xffff) {
		case 0x0100:
			data->flags.crt = 1;
			break;
		case 0x0400:
			data->flags.lcd = 1;
			break;
		case 0x0200:
			data->flags.tvout = 1;
			break;
		default:
			data->flags.unknown = 1;
			break;
		}
L
Len Brown 已提交
1295

L
Linus Torvalds 已提交
1296 1297 1298
		acpi_video_device_bind(video, data);
		acpi_video_device_find_cap(data);

1299
		status = acpi_install_notify_handler(device->handle,
L
Len Brown 已提交
1300 1301 1302
						     ACPI_DEVICE_NOTIFY,
						     acpi_video_device_notify,
						     data);
L
Linus Torvalds 已提交
1303 1304
		if (ACPI_FAILURE(status)) {
			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
L
Len Brown 已提交
1305
					  "Error installing notify handler\n"));
1306 1307 1308 1309 1310
			if(data->brightness)
				kfree(data->brightness->levels);
			kfree(data->brightness);
			kfree(data);
			return -ENODEV;
L
Linus Torvalds 已提交
1311 1312 1313 1314 1315 1316 1317 1318
		}

		down(&video->sem);
		list_add_tail(&data->entry, &video->video_device_list);
		up(&video->sem);

		acpi_video_device_add_fs(device);

1319
		return 0;
L
Linus Torvalds 已提交
1320 1321
	}

1322
	return -ENOENT;
L
Linus Torvalds 已提交
1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
}

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

L
Len Brown 已提交
1337
static void acpi_video_device_rebind(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1338
{
L
Len Brown 已提交
1339
	struct list_head *node, *next;
L
Linus Torvalds 已提交
1340
	list_for_each_safe(node, next, &video->video_device_list) {
L
Len Brown 已提交
1341 1342 1343
		struct acpi_video_device *dev =
		    container_of(node, struct acpi_video_device, entry);
		acpi_video_device_bind(video, dev);
L
Linus Torvalds 已提交
1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357
	}
}

/*
 *  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 已提交
1358
 */
L
Linus Torvalds 已提交
1359 1360

static void
L
Len Brown 已提交
1361 1362
acpi_video_device_bind(struct acpi_video_bus *video,
		       struct acpi_video_device *device)
L
Linus Torvalds 已提交
1363
{
L
Len Brown 已提交
1364
	int i;
L
Linus Torvalds 已提交
1365 1366 1367

#define IDS_VAL(i) video->attached_array[i].value.int_val
#define IDS_BIND(i) video->attached_array[i].bind_info
L
Len Brown 已提交
1368 1369 1370 1371

	for (i = 0; IDS_VAL(i) != ACPI_VIDEO_HEAD_INVALID &&
	     i < video->attached_count; i++) {
		if (device->device_id == (IDS_VAL(i) & 0xffff)) {
L
Linus Torvalds 已提交
1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
			IDS_BIND(i) = device;
			ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i));
		}
	}
#undef IDS_VAL
#undef IDS_BIND
}

/*
 *  Arg:
 *  	video	: video bus device 
 *
 *  Return:
 *  	< 0	: error
 *  
 *  Call _DOD to enumerate all devices attached to display adapter
 *
L
Len Brown 已提交
1389
 */
L
Linus Torvalds 已提交
1390 1391 1392

static int acpi_video_device_enumerate(struct acpi_video_bus *video)
{
L
Len Brown 已提交
1393 1394 1395
	int status;
	int count;
	int i;
L
Linus Torvalds 已提交
1396
	struct acpi_video_enumerated_device *active_device_list;
L
Len Brown 已提交
1397 1398 1399
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *dod = NULL;
	union acpi_object *obj;
L
Linus Torvalds 已提交
1400

1401
	status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
L
Linus Torvalds 已提交
1402
	if (!ACPI_SUCCESS(status)) {
1403
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD"));
1404
		return status;
L
Linus Torvalds 已提交
1405 1406
	}

L
Len Brown 已提交
1407
	dod = (union acpi_object *)buffer.pointer;
L
Linus Torvalds 已提交
1408
	if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
1409
		ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data"));
L
Linus Torvalds 已提交
1410 1411 1412 1413 1414
		status = -EFAULT;
		goto out;
	}

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

L
Len Brown 已提交
1417 1418 1419 1420 1421
	active_device_list = kmalloc((1 +
				      dod->package.count) *
				     sizeof(struct
					    acpi_video_enumerated_device),
				     GFP_KERNEL);
L
Linus Torvalds 已提交
1422 1423 1424 1425 1426 1427 1428 1429

	if (!active_device_list) {
		status = -ENOMEM;
		goto out;
	}

	count = 0;
	for (i = 0; i < dod->package.count; i++) {
L
Len Brown 已提交
1430
		obj = (union acpi_object *)&dod->package.elements[i];
L
Linus Torvalds 已提交
1431 1432

		if (obj->type != ACPI_TYPE_INTEGER) {
1433
			printk(KERN_ERR PREFIX "Invalid _DOD data\n");
L
Len Brown 已提交
1434 1435
			active_device_list[i].value.int_val =
			    ACPI_VIDEO_HEAD_INVALID;
L
Linus Torvalds 已提交
1436 1437 1438
		}
		active_device_list[i].value.int_val = obj->integer.value;
		active_device_list[i].bind_info = NULL;
L
Len Brown 已提交
1439 1440
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i,
				  (int)obj->integer.value));
L
Linus Torvalds 已提交
1441 1442 1443 1444
		count++;
	}
	active_device_list[count].value.int_val = ACPI_VIDEO_HEAD_END;

1445
	kfree(video->attached_array);
L
Len Brown 已提交
1446

L
Linus Torvalds 已提交
1447 1448
	video->attached_array = active_device_list;
	video->attached_count = count;
L
Len Brown 已提交
1449
      out:
1450
	kfree(buffer.pointer);
1451
	return status;
L
Linus Torvalds 已提交
1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464
}

/*
 *  Arg:
 *  	video	: video bus device 
 *  	event	: Nontify Event
 *
 *  Return:
 *  	< 0	: error
 *  
 *	1. Find out the current active output device.
 *	2. Identify the next output device to switch
 *	3. call _DSS to do actual switch.
L
Len Brown 已提交
1465
 */
L
Linus Torvalds 已提交
1466

L
Len Brown 已提交
1467
static int acpi_video_switch_output(struct acpi_video_bus *video, int event)
L
Linus Torvalds 已提交
1468
{
L
Len Brown 已提交
1469 1470 1471 1472
	struct list_head *node, *next;
	struct acpi_video_device *dev = NULL;
	struct acpi_video_device *dev_next = NULL;
	struct acpi_video_device *dev_prev = NULL;
L
Linus Torvalds 已提交
1473 1474 1475 1476 1477
	unsigned long state;
	int status = 0;


	list_for_each_safe(node, next, &video->video_device_list) {
1478
		dev = container_of(node, struct acpi_video_device, entry);
L
Linus Torvalds 已提交
1479
		status = acpi_video_device_get_state(dev, &state);
L
Len Brown 已提交
1480 1481 1482 1483 1484 1485 1486
		if (state & 0x2) {
			dev_next =
			    container_of(node->next, struct acpi_video_device,
					 entry);
			dev_prev =
			    container_of(node->prev, struct acpi_video_device,
					 entry);
L
Linus Torvalds 已提交
1487 1488 1489 1490 1491
			goto out;
		}
	}
	dev_next = container_of(node->next, struct acpi_video_device, entry);
	dev_prev = container_of(node->prev, struct acpi_video_device, entry);
L
Len Brown 已提交
1492
      out:
L
Linus Torvalds 已提交
1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
	switch (event) {
	case ACPI_VIDEO_NOTIFY_CYCLE:
	case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:
		acpi_video_device_set_state(dev, 0);
		acpi_video_device_set_state(dev_next, 0x80000001);
		break;
	case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:
		acpi_video_device_set_state(dev, 0);
		acpi_video_device_set_state(dev_prev, 0x80000001);
	default:
		break;
	}

1506
	return status;
L
Linus Torvalds 已提交
1507 1508
}

L
Len Brown 已提交
1509 1510 1511
static int
acpi_video_get_next_level(struct acpi_video_device *device,
			  u32 level_current, u32 event)
L
Linus Torvalds 已提交
1512
{
1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540
	int min, max, min_above, max_below, i, l;
	max = max_below = 0;
	min = min_above = 255;
	for (i = 0; i < device->brightness->count; i++) {
		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 已提交
1541 1542 1543
}

static void
L
Len Brown 已提交
1544
acpi_video_switch_brightness(struct acpi_video_device *device, int event)
L
Linus Torvalds 已提交
1545 1546 1547 1548 1549 1550 1551 1552
{
	unsigned long level_current, level_next;
	acpi_video_device_lcd_get_level_current(device, &level_current);
	level_next = acpi_video_get_next_level(device, level_current, event);
	acpi_video_device_lcd_set_level(device, level_next);
}

static int
L
Len Brown 已提交
1553 1554
acpi_video_bus_get_devices(struct acpi_video_bus *video,
			   struct acpi_device *device)
L
Linus Torvalds 已提交
1555
{
L
Len Brown 已提交
1556 1557
	int status = 0;
	struct list_head *node, *next;
L
Linus Torvalds 已提交
1558 1559 1560 1561 1562


	acpi_video_device_enumerate(video);

	list_for_each_safe(node, next, &device->children) {
L
Len Brown 已提交
1563 1564
		struct acpi_device *dev =
		    list_entry(node, struct acpi_device, node);
L
Linus Torvalds 已提交
1565 1566 1567 1568 1569 1570

		if (!dev)
			continue;

		status = acpi_video_bus_get_one_device(dev, video);
		if (ACPI_FAILURE(status)) {
1571
			ACPI_EXCEPTION((AE_INFO, status, "Cant attach device"));
L
Linus Torvalds 已提交
1572 1573 1574 1575
			continue;
		}

	}
1576
	return status;
L
Linus Torvalds 已提交
1577 1578
}

L
Len Brown 已提交
1579
static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
L
Linus Torvalds 已提交
1580
{
1581
	acpi_status status;
L
Linus Torvalds 已提交
1582 1583 1584 1585
	struct acpi_video_bus *video;


	if (!device || !device->video)
1586
		return -ENOENT;
L
Linus Torvalds 已提交
1587 1588 1589 1590 1591 1592 1593 1594

	video = device->video;

	down(&video->sem);
	list_del(&device->entry);
	up(&video->sem);
	acpi_video_device_remove_fs(device->dev);

1595
	status = acpi_remove_notify_handler(device->dev->handle,
L
Len Brown 已提交
1596 1597
					    ACPI_DEVICE_NOTIFY,
					    acpi_video_device_notify);
1598

1599
	return 0;
L
Linus Torvalds 已提交
1600 1601
}

L
Len Brown 已提交
1602
static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1603
{
L
Len Brown 已提交
1604 1605
	int status;
	struct list_head *node, *next;
L
Linus Torvalds 已提交
1606 1607 1608


	list_for_each_safe(node, next, &video->video_device_list) {
L
Len Brown 已提交
1609 1610
		struct acpi_video_device *data =
		    list_entry(node, struct acpi_video_device, entry);
L
Linus Torvalds 已提交
1611 1612 1613 1614
		if (!data)
			continue;

		status = acpi_video_bus_put_one_device(data);
L
Len Brown 已提交
1615 1616 1617
		if (ACPI_FAILURE(status))
			printk(KERN_WARNING PREFIX
			       "hhuuhhuu bug in acpi video driver.\n");
L
Linus Torvalds 已提交
1618

1619
		if (data->brightness)
1620
			kfree(data->brightness->levels);
1621
		kfree(data->brightness);
L
Linus Torvalds 已提交
1622 1623 1624
		kfree(data);
	}

1625
	return 0;
L
Linus Torvalds 已提交
1626 1627 1628 1629
}

/* acpi_video interface */

L
Len Brown 已提交
1630
static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1631 1632 1633 1634
{
	return acpi_video_bus_DOS(video, 1, 0);
}

L
Len Brown 已提交
1635
static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1636 1637 1638 1639
{
	return acpi_video_bus_DOS(video, 0, 1);
}

L
Len Brown 已提交
1640
static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
L
Linus Torvalds 已提交
1641
{
L
Len Brown 已提交
1642 1643
	struct acpi_video_bus *video = (struct acpi_video_bus *)data;
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
1644 1645 1646 1647

	printk("video bus notify\n");

	if (!video)
1648
		return;
L
Linus Torvalds 已提交
1649

1650
	device = video->device;
L
Linus Torvalds 已提交
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665

	switch (event) {
	case ACPI_VIDEO_NOTIFY_SWITCH:	/* User request that a switch occur,
					 * most likely via hotkey. */
		acpi_bus_generate_event(device, event, 0);
		break;

	case ACPI_VIDEO_NOTIFY_PROBE:	/* User plug or remove a video
					 * connector. */
		acpi_video_device_enumerate(video);
		acpi_video_device_rebind(video);
		acpi_video_switch_output(video, event);
		acpi_bus_generate_event(device, event, 0);
		break;

L
Len Brown 已提交
1666 1667 1668
	case ACPI_VIDEO_NOTIFY_CYCLE:	/* Cycle Display output hotkey pressed. */
	case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:	/* Next Display output hotkey pressed. */
	case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:	/* previous Display output hotkey pressed. */
L
Linus Torvalds 已提交
1669 1670 1671 1672 1673 1674
		acpi_video_switch_output(video, event);
		acpi_bus_generate_event(device, event, 0);
		break;

	default:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
1675
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
1676 1677 1678
		break;
	}

1679
	return;
L
Linus Torvalds 已提交
1680 1681
}

L
Len Brown 已提交
1682
static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
L
Linus Torvalds 已提交
1683
{
L
Len Brown 已提交
1684 1685 1686
	struct acpi_video_device *video_device =
	    (struct acpi_video_device *)data;
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
1687 1688 1689 1690


	printk("video device notify\n");
	if (!video_device)
1691
		return;
L
Linus Torvalds 已提交
1692

1693
	device = video_device->dev;
L
Linus Torvalds 已提交
1694 1695

	switch (event) {
L
Len Brown 已提交
1696 1697
	case ACPI_VIDEO_NOTIFY_SWITCH:	/* change in status (cycle output device) */
	case ACPI_VIDEO_NOTIFY_PROBE:	/* change in status (output device status) */
L
Linus Torvalds 已提交
1698 1699
		acpi_bus_generate_event(device, event, 0);
		break;
L
Len Brown 已提交
1700 1701 1702 1703 1704 1705
	case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:	/* Cycle brightness */
	case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:	/* Increase brightness */
	case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:	/* Decrease brightness */
	case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:	/* zero brightnesss */
	case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:	/* display device off */
		acpi_video_switch_brightness(video_device, event);
L
Linus Torvalds 已提交
1706 1707 1708 1709
		acpi_bus_generate_event(device, event, 0);
		break;
	default:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
1710
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
1711 1712
		break;
	}
1713
	return;
L
Linus Torvalds 已提交
1714 1715
}

L
Len Brown 已提交
1716
static int acpi_video_bus_add(struct acpi_device *device)
L
Linus Torvalds 已提交
1717
{
L
Len Brown 已提交
1718 1719 1720
	int result = 0;
	acpi_status status = 0;
	struct acpi_video_bus *video = NULL;
L
Linus Torvalds 已提交
1721

L
Len Brown 已提交
1722

L
Linus Torvalds 已提交
1723
	if (!device)
1724
		return -EINVAL;
L
Linus Torvalds 已提交
1725 1726 1727

	video = kmalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
	if (!video)
1728
		return -ENOMEM;
L
Linus Torvalds 已提交
1729 1730
	memset(video, 0, sizeof(struct acpi_video_bus));

1731
	video->device = device;
L
Linus Torvalds 已提交
1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750
	strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
	strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
	acpi_driver_data(device) = video;

	acpi_video_bus_find_cap(video);
	result = acpi_video_bus_check(video);
	if (result)
		goto end;

	result = acpi_video_bus_add_fs(device);
	if (result)
		goto end;

	init_MUTEX(&video->sem);
	INIT_LIST_HEAD(&video->video_device_list);

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

1751
	status = acpi_install_notify_handler(device->handle,
L
Len Brown 已提交
1752 1753
					     ACPI_DEVICE_NOTIFY,
					     acpi_video_bus_notify, video);
L
Linus Torvalds 已提交
1754 1755
	if (ACPI_FAILURE(status)) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
L
Len Brown 已提交
1756
				  "Error installing notify handler\n"));
1757 1758 1759 1760
		acpi_video_bus_stop_devices(video);
		acpi_video_bus_put_devices(video);
		kfree(video->attached_array);
		acpi_video_bus_remove_fs(device);
L
Linus Torvalds 已提交
1761 1762 1763 1764 1765
		result = -ENODEV;
		goto end;
	}

	printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s  rom: %s  post: %s)\n",
L
Len Brown 已提交
1766 1767 1768 1769
	       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 已提交
1770

L
Len Brown 已提交
1771
      end:
1772
	if (result)
L
Linus Torvalds 已提交
1773 1774
		kfree(video);

1775
	return result;
L
Linus Torvalds 已提交
1776 1777
}

L
Len Brown 已提交
1778
static int acpi_video_bus_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
1779
{
L
Len Brown 已提交
1780 1781
	acpi_status status = 0;
	struct acpi_video_bus *video = NULL;
L
Linus Torvalds 已提交
1782 1783 1784


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

L
Len Brown 已提交
1787
	video = (struct acpi_video_bus *)acpi_driver_data(device);
L
Linus Torvalds 已提交
1788 1789 1790

	acpi_video_bus_stop_devices(video);

1791
	status = acpi_remove_notify_handler(video->device->handle,
L
Len Brown 已提交
1792 1793
					    ACPI_DEVICE_NOTIFY,
					    acpi_video_bus_notify);
L
Linus Torvalds 已提交
1794 1795 1796 1797

	acpi_video_bus_put_devices(video);
	acpi_video_bus_remove_fs(device);

1798
	kfree(video->attached_array);
L
Linus Torvalds 已提交
1799 1800
	kfree(video);

1801
	return 0;
L
Linus Torvalds 已提交
1802 1803 1804
}

static int
L
Len Brown 已提交
1805
acpi_video_bus_match(struct acpi_device *device, struct acpi_driver *driver)
L
Linus Torvalds 已提交
1806
{
L
Len Brown 已提交
1807 1808 1809
	acpi_handle h_dummy1;
	acpi_handle h_dummy2;
	acpi_handle h_dummy3;
L
Linus Torvalds 已提交
1810 1811 1812


	if (!device || !driver)
1813
		return -EINVAL;
L
Linus Torvalds 已提交
1814 1815 1816 1817 1818 1819 1820 1821

	/* Since there is no HID, CID for ACPI Video drivers, we have
	 * to check well known required nodes for each feature we support.
	 */

	/* Does this device able to support video switching ? */
	if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy1)) &&
	    ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy2)))
1822
		return 0;
L
Linus Torvalds 已提交
1823 1824 1825

	/* Does this device able to retrieve a video ROM ? */
	if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy1)))
1826
		return 0;
L
Linus Torvalds 已提交
1827 1828 1829 1830 1831

	/* Does this device able to configure which video head to be POSTed ? */
	if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy1)) &&
	    ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy2)) &&
	    ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy3)))
1832
		return 0;
L
Linus Torvalds 已提交
1833

1834
	return -ENODEV;
L
Linus Torvalds 已提交
1835 1836
}

L
Len Brown 已提交
1837
static int __init acpi_video_init(void)
L
Linus Torvalds 已提交
1838
{
L
Len Brown 已提交
1839
	int result = 0;
L
Linus Torvalds 已提交
1840 1841 1842


	/*
L
Len Brown 已提交
1843 1844 1845
	   acpi_dbg_level = 0xFFFFFFFF;
	   acpi_dbg_layer = 0x08000000;
	 */
L
Linus Torvalds 已提交
1846 1847 1848

	acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
	if (!acpi_video_dir)
1849
		return -ENODEV;
L
Linus Torvalds 已提交
1850 1851 1852 1853 1854
	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);
1855
		return -ENODEV;
L
Linus Torvalds 已提交
1856 1857
	}

1858
	return 0;
L
Linus Torvalds 已提交
1859 1860
}

L
Len Brown 已提交
1861
static void __exit acpi_video_exit(void)
L
Linus Torvalds 已提交
1862 1863 1864 1865 1866 1867
{

	acpi_bus_unregister_driver(&acpi_video_bus);

	remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);

1868
	return;
L
Linus Torvalds 已提交
1869 1870 1871 1872
}

module_init(acpi_video_init);
module_exit(acpi_video_exit);