video.c 57.2 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 */
165
	u8 _BCQ:1;		/* Some buggy BIOS uses _BCQ instead of _BQC */
L
Len Brown 已提交
166 167 168 169
	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 已提交
170 171
};

172 173
struct acpi_video_brightness_flags {
	u8 _BCL_no_ac_battery_levels:1;	/* no AC/Battery levels in _BCL */
174
	u8 _BCL_reversed:1;		/* _BCL package is in a reversed order*/
175 176 177
	u8 _BCL_use_index:1;		/* levels in _BCL are index values */
	u8 _BCM_use_index:1;		/* input of _BCM is an index value */
	u8 _BQC_use_index:1;		/* _BQC returns an index value */
178 179
};

L
Linus Torvalds 已提交
180
struct acpi_video_device_brightness {
L
Len Brown 已提交
181 182 183
	int curr;
	int count;
	int *levels;
184
	struct acpi_video_brightness_flags flags;
L
Linus Torvalds 已提交
185 186 187
};

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

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

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

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

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

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

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

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

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

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

L
Len Brown 已提交
288
static char device_decode[][30] = {
L
Linus Torvalds 已提交
289 290 291 292 293 294
	"motherboard VGA device",
	"PCI VGA device",
	"AGP VGA device",
	"UNKNOWN",
};

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

314 315 316
/*backlight device sysfs support*/
static int acpi_video_get_brightness(struct backlight_device *bd)
{
317
	unsigned long long cur_level;
318
	int i;
319
	struct acpi_video_device *vd =
320
		(struct acpi_video_device *)bl_get_data(bd);
321 322 323

	if (acpi_video_device_lcd_get_level_current(vd, &cur_level))
		return -EINVAL;
324 325 326 327 328 329 330
	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;
331 332 333 334
}

static int acpi_video_set_brightness(struct backlight_device *bd)
{
335
	int request_level = bd->props.brightness + 2;
336
	struct acpi_video_device *vd =
337
		(struct acpi_video_device *)bl_get_data(bd);
338 339 340

	return acpi_video_device_lcd_set_level(vd,
				vd->brightness->levels[request_level]);
341 342
}

343 344 345 346 347
static struct backlight_ops acpi_backlight_ops = {
	.get_brightness = acpi_video_get_brightness,
	.update_status  = acpi_video_set_brightness,
};

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


/* 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);
385
	unsigned long long level;
386 387
	int state;

388 389
	if (acpi_video_device_lcd_get_level_current(video, &level))
		return -EINVAL;
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 415 416 417 418
	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 已提交
419 420 421 422 423 424 425
/* --------------------------------------------------------------------------
                               Video Management
   -------------------------------------------------------------------------- */

/* device */

static int
426
acpi_video_device_query(struct acpi_video_device *device, unsigned long long *state)
L
Linus Torvalds 已提交
427
{
L
Len Brown 已提交
428
	int status;
429 430

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

432
	return status;
L
Linus Torvalds 已提交
433 434 435
}

static int
L
Len Brown 已提交
436
acpi_video_device_get_state(struct acpi_video_device *device,
437
			    unsigned long long *state)
L
Linus Torvalds 已提交
438
{
L
Len Brown 已提交
439
	int status;
L
Linus Torvalds 已提交
440

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

443
	return status;
L
Linus Torvalds 已提交
444 445 446
}

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


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

458
	return status;
L
Linus Torvalds 已提交
459 460 461
}

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


	*levels = NULL;

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

	*levels = obj;

484
	return 0;
L
Linus Torvalds 已提交
485

L
Len Brown 已提交
486
      err:
487
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
488

489
	return status;
L
Linus Torvalds 已提交
490 491 492
}

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

	arg0.integer.value = level;

502 503 504 505 506 507 508
	status = acpi_evaluate_object(device->dev->handle, "_BCM",
				      &args, NULL);
	if (ACPI_FAILURE(status)) {
		ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));
		return -EIO;
	}

509
	device->brightness->curr = level;
510
	for (state = 2; state < device->brightness->count; state++)
511
		if (level == device->brightness->levels[state]) {
512 513
			if (device->backlight)
				device->backlight->props.brightness = state - 2;
514 515
			return 0;
		}
516

517 518
	ACPI_ERROR((AE_INFO, "Current brightness invalid"));
	return -EINVAL;
L
Linus Torvalds 已提交
519 520 521
}

static int
L
Len Brown 已提交
522
acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
523
					unsigned long long *level)
L
Linus Torvalds 已提交
524
{
525 526
	acpi_status status = AE_OK;

527 528 529 530
	if (device->cap._BQC || device->cap._BCQ) {
		char *buf = device->cap._BQC ? "_BQC" : "_BCQ";

		status = acpi_evaluate_integer(device->dev->handle, buf,
531 532
						NULL, level);
		if (ACPI_SUCCESS(status)) {
533 534 535 536 537 538 539
			if (device->brightness->flags._BQC_use_index) {
				if (device->brightness->flags._BCL_reversed)
					*level = device->brightness->count
								 - 3 - (*level);
				*level = device->brightness->levels[*level + 2];

			}
540 541 542 543 544 545 546 547 548 549
			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
			 */
550 551
			ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf));
			device->cap._BQC = device->cap._BCQ = 0;
552 553 554
		}
	}

555
	*level = device->brightness->curr;
556
	return 0;
L
Linus Torvalds 已提交
557 558 559
}

static int
L
Len Brown 已提交
560 561
acpi_video_device_EDID(struct acpi_video_device *device,
		       union acpi_object **edid, ssize_t length)
L
Linus Torvalds 已提交
562
{
L
Len Brown 已提交
563 564 565 566 567
	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 已提交
568 569 570 571 572


	*edid = NULL;

	if (!device)
573
		return -ENODEV;
L
Linus Torvalds 已提交
574 575 576 577 578
	if (length == 128)
		arg0.integer.value = 1;
	else if (length == 256)
		arg0.integer.value = 2;
	else
579
		return -EINVAL;
L
Linus Torvalds 已提交
580

581
	status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer);
L
Linus Torvalds 已提交
582
	if (ACPI_FAILURE(status))
583
		return -ENODEV;
L
Linus Torvalds 已提交
584

585
	obj = buffer.pointer;
L
Linus Torvalds 已提交
586 587 588 589

	if (obj && obj->type == ACPI_TYPE_BUFFER)
		*edid = obj;
	else {
590
		printk(KERN_ERR PREFIX "Invalid _DDC data\n");
L
Linus Torvalds 已提交
591 592 593 594
		status = -EFAULT;
		kfree(obj);
	}

595
	return status;
L
Linus Torvalds 已提交
596 597 598 599 600
}

/* bus */

static int
L
Len Brown 已提交
601
acpi_video_bus_set_POST(struct acpi_video_bus *video, unsigned long option)
L
Linus Torvalds 已提交
602
{
L
Len Brown 已提交
603
	int status;
604
	unsigned long long tmp;
L
Len Brown 已提交
605 606
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
L
Linus Torvalds 已提交
607 608 609 610


	arg0.integer.value = option;

611
	status = acpi_evaluate_integer(video->device->handle, "_SPD", &args, &tmp);
L
Linus Torvalds 已提交
612
	if (ACPI_SUCCESS(status))
L
Len Brown 已提交
613
		status = tmp ? (-EINVAL) : (AE_OK);
L
Linus Torvalds 已提交
614

615
	return status;
L
Linus Torvalds 已提交
616 617 618
}

static int
619
acpi_video_bus_get_POST(struct acpi_video_bus *video, unsigned long long *id)
L
Linus Torvalds 已提交
620 621 622
{
	int status;

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

625
	return status;
L
Linus Torvalds 已提交
626 627 628
}

static int
L
Len Brown 已提交
629
acpi_video_bus_POST_options(struct acpi_video_bus *video,
630
			    unsigned long long *options)
L
Linus Torvalds 已提交
631
{
L
Len Brown 已提交
632
	int status;
L
Linus Torvalds 已提交
633

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

637
	return status;
L
Linus Torvalds 已提交
638 639 640 641 642 643 644 645 646
}

/*
 *  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
647
 *			active display output. No switch event.
L
Linus Torvalds 已提交
648 649 650 651 652 653
 *		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
654
 *			of the LCD when the power changes from AC to DC
L
Linus Torvalds 已提交
655
 *		1. 	The system BIOS should NOT automatically control the brightness 
656
 *			level of the LCD when the power changes from AC to DC.
L
Linus Torvalds 已提交
657 658 659 660 661
 * Return Value:
 * 		-1	wrong arg.
 */

static int
L
Len Brown 已提交
662
acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
L
Linus Torvalds 已提交
663
{
L
Len Brown 已提交
664 665 666
	acpi_integer status = 0;
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list args = { 1, &arg0 };
L
Linus Torvalds 已提交
667 668


L
Len Brown 已提交
669
	if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) {
L
Linus Torvalds 已提交
670 671 672 673 674
		status = -1;
		goto Failed;
	}
	arg0.integer.value = (lcd_flag << 2) | bios_flag;
	video->dos_setting = arg0.integer.value;
675
	acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL);
L
Linus Torvalds 已提交
676

L
Len Brown 已提交
677
      Failed:
678
	return status;
L
Linus Torvalds 已提交
679 680
}

681 682 683 684 685 686 687 688 689 690
/*
 * 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 已提交
691 692 693 694 695
/*
 *  Arg:	
 *  	device	: video output device (LCD, CRT, ..)
 *
 *  Return Value:
696 697 698 699 700 701 702 703 704
 *	Maximum brightness level
 *
 *  Allocate and initialize device->brightness.
 */

static int
acpi_video_init_brightness(struct acpi_video_device *device)
{
	union acpi_object *obj = NULL;
705
	int i, max_level = 0, count = 0, level_ac_battery = 0;
706
	unsigned long long level, level_old;
707 708
	union acpi_object *o;
	struct acpi_video_device_brightness *br = NULL;
709
	int result = -EINVAL;
710 711 712 713 714 715 716 717 718 719 720 721 722

	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");
723
		result = -ENOMEM;
724 725 726
		goto out;
	}

727
	br->levels = kmalloc((obj->package.count + 2) * sizeof *(br->levels),
728
				GFP_KERNEL);
729 730
	if (!br->levels) {
		result = -ENOMEM;
731
		goto out_free;
732
	}
733 734 735 736 737 738 739 740 741 742 743 744 745 746

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

747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
	/*
	 * 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"));

767 768 769 770 771 772 773 774
	/* 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"));
775

776 777
	br->count = count;
	device->brightness = br;
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822

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

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

	/* _BQC uses INDEX while _BCL uses VALUE in some laptops */
	br->curr = max_level;
	result = acpi_video_device_lcd_get_level_current(device, &level_old);
	if (result)
		goto out_free_levels;

	result = acpi_video_device_lcd_set_level(device, br->curr);
	if (result)
		goto out_free_levels;

	result = acpi_video_device_lcd_get_level_current(device, &level);
	if (result)
		goto out_free_levels;

	if ((level != level_old) && !br->flags._BCM_use_index) {
		/* Note:
		 * This piece of code does not work correctly if the current
		 * brightness levels is 0.
		 * But I guess boxes that boot with such a dark screen are rare
		 * and no more code is needed to cover this specifial case.
		 */

		if (level_ac_battery != 2) {
			/*
			 * For now, we don't support the _BCL like this:
			 * 16, 15, 0, 1, 2, 3, ..., 14, 15, 16
			 * because we may mess up the index returned by _BQC.
			 * Plus: we have not got a box like this.
			 */
			ACPI_ERROR((AE_INFO, "_BCL not supported\n"));
		}
		br->flags._BQC_use_index = 1;
	}

823 824
	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
			  "found %d brightness levels\n", count - 2));
825
	kfree(obj);
826
	return result;
827 828 829 830 831 832 833 834

out_free_levels:
	kfree(br->levels);
out_free:
	kfree(br);
out:
	device->brightness = NULL;
	kfree(obj);
835
	return result;
836 837 838 839 840 841 842
}

/*
 *  Arg:
 *	device	: video output device (LCD, CRT, ..)
 *
 *  Return Value:
L
Linus Torvalds 已提交
843 844
 *  	None
 *
845
 *  Find out all required AML methods defined under the output
L
Linus Torvalds 已提交
846 847 848
 *  device.
 */

L
Len Brown 已提交
849
static void acpi_video_device_find_cap(struct acpi_video_device *device)
L
Linus Torvalds 已提交
850 851 852 853
{
	acpi_handle h_dummy1;


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

856
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
L
Linus Torvalds 已提交
857 858
		device->cap._ADR = 1;
	}
859
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) {
L
Len Brown 已提交
860
		device->cap._BCL = 1;
L
Linus Torvalds 已提交
861
	}
862
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
L
Len Brown 已提交
863
		device->cap._BCM = 1;
L
Linus Torvalds 已提交
864
	}
865 866
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
		device->cap._BQC = 1;
867 868 869 870 871 872
	else if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCQ",
				&h_dummy1))) {
		printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n");
		device->cap._BCQ = 1;
	}

873
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
L
Len Brown 已提交
874
		device->cap._DDC = 1;
L
Linus Torvalds 已提交
875
	}
876
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DCS", &h_dummy1))) {
L
Linus Torvalds 已提交
877 878
		device->cap._DCS = 1;
	}
879
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DGS", &h_dummy1))) {
L
Linus Torvalds 已提交
880 881
		device->cap._DGS = 1;
	}
882
	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DSS", &h_dummy1))) {
L
Linus Torvalds 已提交
883 884 885
		device->cap._DSS = 1;
	}

886
	if (acpi_video_backlight_support()) {
887
		int result;
888 889
		static int count = 0;
		char *name;
890 891 892 893

		result = acpi_video_init_brightness(device);
		if (result)
			return;
894 895 896 897 898 899
		name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
		if (!name)
			return;

		sprintf(name, "acpi_video%d", count++);
		device->backlight = backlight_device_register(name,
900
			NULL, device, &acpi_backlight_ops);
901
		device->backlight->props.max_brightness = device->brightness->count-3;
902
		kfree(name);
903 904 905

		device->cdev = thermal_cooling_device_register("LCD",
					device->dev, &video_cooling_ops);
906 907 908
		if (IS_ERR(device->cdev))
			return;

909 910
		dev_info(&device->dev->dev, "registered as cooling_device%d\n",
			 device->cdev->id);
911 912 913 914 915 916 917 918 919 920
		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");

921
	}
922 923 924 925 926 927 928 929 930 931 932 933 934 935

	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);
		}
936
	}
L
Linus Torvalds 已提交
937 938 939 940 941 942 943 944 945
}

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

L
Len Brown 已提交
949
static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
950
{
L
Len Brown 已提交
951
	acpi_handle h_dummy1;
L
Linus Torvalds 已提交
952

953
	memset(&video->cap, 0, sizeof(video->cap));
954
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
L
Linus Torvalds 已提交
955 956
		video->cap._DOS = 1;
	}
957
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) {
L
Linus Torvalds 已提交
958 959
		video->cap._DOD = 1;
	}
960
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) {
L
Linus Torvalds 已提交
961 962
		video->cap._ROM = 1;
	}
963
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) {
L
Linus Torvalds 已提交
964 965
		video->cap._GPD = 1;
	}
966
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) {
L
Linus Torvalds 已提交
967 968
		video->cap._SPD = 1;
	}
969
	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) {
L
Linus Torvalds 已提交
970 971 972 973 974 975 976 977 978
		video->cap._VPO = 1;
	}
}

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

L
Len Brown 已提交
979
static int acpi_video_bus_check(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
980
{
L
Len Brown 已提交
981
	acpi_status status = -ENOENT;
982
	struct device *dev;
L
Linus Torvalds 已提交
983 984

	if (!video)
985
		return -EINVAL;
L
Linus Torvalds 已提交
986

987 988 989 990 991
	dev = acpi_get_physical_pci_device(video->device->handle);
	if (!dev)
		return -ENODEV;
	put_device(dev);

L
Linus Torvalds 已提交
992 993 994 995
	/* Since there is no HID, CID and so on for VGA driver, we have
	 * to check well known required nodes.
	 */

996
	/* Does this device support video switching? */
L
Len Brown 已提交
997
	if (video->cap._DOS) {
L
Linus Torvalds 已提交
998 999 1000 1001
		video->flags.multihead = 1;
		status = 0;
	}

1002
	/* Does this device support retrieving a video ROM? */
L
Len Brown 已提交
1003
	if (video->cap._ROM) {
L
Linus Torvalds 已提交
1004 1005 1006 1007
		video->flags.rom = 1;
		status = 0;
	}

1008
	/* Does this device support configuring which video device to POST? */
L
Len Brown 已提交
1009
	if (video->cap._GPD && video->cap._SPD && video->cap._VPO) {
L
Linus Torvalds 已提交
1010 1011 1012 1013
		video->flags.post = 1;
		status = 0;
	}

1014
	return status;
L
Linus Torvalds 已提交
1015 1016 1017 1018 1019 1020
}

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

L
Len Brown 已提交
1021
static struct proc_dir_entry *acpi_video_dir;
L
Linus Torvalds 已提交
1022 1023 1024

/* video devices */

L
Len Brown 已提交
1025
static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1026
{
1027
	struct acpi_video_device *dev = seq->private;
L
Linus Torvalds 已提交
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040


	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");
1041 1042
	else if (dev->flags.dvi)
		seq_printf(seq, "DVI\n");
L
Linus Torvalds 已提交
1043 1044 1045
	else
		seq_printf(seq, "UNKNOWN\n");

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

L
Len Brown 已提交
1048
      end:
1049
	return 0;
L
Linus Torvalds 已提交
1050 1051 1052
}

static int
L
Len Brown 已提交
1053
acpi_video_device_info_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1054 1055 1056 1057 1058
{
	return single_open(file, acpi_video_device_info_seq_show,
			   PDE(inode)->data);
}

L
Len Brown 已提交
1059
static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1060
{
L
Len Brown 已提交
1061
	int status;
1062
	struct acpi_video_device *dev = seq->private;
1063
	unsigned long long state;
L
Linus Torvalds 已提交
1064 1065 1066 1067 1068 1069 1070 1071


	if (!dev)
		goto end;

	status = acpi_video_device_get_state(dev, &state);
	seq_printf(seq, "state:     ");
	if (ACPI_SUCCESS(status))
1072
		seq_printf(seq, "0x%02llx\n", state);
L
Linus Torvalds 已提交
1073 1074 1075 1076 1077 1078
	else
		seq_printf(seq, "<not supported>\n");

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

L
Len Brown 已提交
1083
      end:
1084
	return 0;
L
Linus Torvalds 已提交
1085 1086 1087
}

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

static ssize_t
L
Len Brown 已提交
1095 1096 1097
acpi_video_device_write_state(struct file *file,
			      const char __user * buffer,
			      size_t count, loff_t * data)
L
Linus Torvalds 已提交
1098
{
L
Len Brown 已提交
1099
	int status;
1100 1101
	struct seq_file *m = file->private_data;
	struct acpi_video_device *dev = m->private;
L
Len Brown 已提交
1102 1103
	char str[12] = { 0 };
	u32 state = 0;
L
Linus Torvalds 已提交
1104 1105 1106


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

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

	str[count] = 0;
	state = simple_strtoul(str, NULL, 0);
L
Len Brown 已提交
1114
	state &= ((1ul << 31) | (1ul << 30) | (1ul << 0));
L
Linus Torvalds 已提交
1115 1116 1117 1118

	status = acpi_video_device_set_state(dev, state);

	if (status)
1119
		return -EFAULT;
L
Linus Torvalds 已提交
1120

1121
	return count;
L
Linus Torvalds 已提交
1122 1123 1124
}

static int
L
Len Brown 已提交
1125
acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1126
{
1127
	struct acpi_video_device *dev = seq->private;
L
Len Brown 已提交
1128
	int i;
L
Linus Torvalds 已提交
1129 1130 1131 1132


	if (!dev || !dev->brightness) {
		seq_printf(seq, "<not supported>\n");
1133
		return 0;
L
Linus Torvalds 已提交
1134 1135 1136
	}

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

1141
	return 0;
L
Linus Torvalds 已提交
1142 1143 1144
}

static int
L
Len Brown 已提交
1145
acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1146 1147 1148 1149 1150 1151
{
	return single_open(file, acpi_video_device_brightness_seq_show,
			   PDE(inode)->data);
}

static ssize_t
L
Len Brown 已提交
1152 1153 1154
acpi_video_device_write_brightness(struct file *file,
				   const char __user * buffer,
				   size_t count, loff_t * data)
L
Linus Torvalds 已提交
1155
{
1156 1157
	struct seq_file *m = file->private_data;
	struct acpi_video_device *dev = m->private;
1158
	char str[5] = { 0 };
L
Len Brown 已提交
1159 1160
	unsigned int level = 0;
	int i;
L
Linus Torvalds 已提交
1161 1162


1163
	if (!dev || !dev->brightness || count + 1 > sizeof str)
1164
		return -EINVAL;
L
Linus Torvalds 已提交
1165 1166

	if (copy_from_user(str, buffer, count))
1167
		return -EFAULT;
L
Linus Torvalds 已提交
1168 1169 1170

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

L
Linus Torvalds 已提交
1172
	if (level > 100)
1173
		return -EFAULT;
L
Linus Torvalds 已提交
1174

1175
	/* validate through the list of available levels */
1176
	for (i = 2; i < dev->brightness->count; i++)
L
Linus Torvalds 已提交
1177
		if (level == dev->brightness->levels[i]) {
1178 1179
			if (!acpi_video_device_lcd_set_level(dev, level))
				return count;
L
Linus Torvalds 已提交
1180 1181 1182
			break;
		}

1183
	return -EINVAL;
L
Linus Torvalds 已提交
1184 1185
}

L
Len Brown 已提交
1186
static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1187
{
1188
	struct acpi_video_device *dev = seq->private;
L
Len Brown 已提交
1189 1190 1191
	int status;
	int i;
	union acpi_object *edid = NULL;
L
Linus Torvalds 已提交
1192 1193 1194 1195 1196


	if (!dev)
		goto out;

L
Len Brown 已提交
1197
	status = acpi_video_device_EDID(dev, &edid, 128);
L
Linus Torvalds 已提交
1198
	if (ACPI_FAILURE(status)) {
L
Len Brown 已提交
1199
		status = acpi_video_device_EDID(dev, &edid, 256);
L
Linus Torvalds 已提交
1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
	}

	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 已提交
1211
      out:
L
Linus Torvalds 已提交
1212 1213 1214 1215 1216
	if (!edid)
		seq_printf(seq, "<not supported>\n");
	else
		kfree(edid);

1217
	return 0;
L
Linus Torvalds 已提交
1218 1219 1220
}

static int
L
Len Brown 已提交
1221
acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1222 1223 1224 1225 1226
{
	return single_open(file, acpi_video_device_EDID_seq_show,
			   PDE(inode)->data);
}

L
Len Brown 已提交
1227
static int acpi_video_device_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1228
{
1229
	struct proc_dir_entry *entry, *device_dir;
L
Linus Torvalds 已提交
1230 1231
	struct acpi_video_device *vid_dev;

1232
	vid_dev = acpi_driver_data(device);
L
Linus Torvalds 已提交
1233
	if (!vid_dev)
1234
		return -ENODEV;
L
Linus Torvalds 已提交
1235

1236 1237 1238 1239 1240 1241
	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 已提交
1242 1243

	/* 'info' [R] */
1244
	entry = proc_create_data("info", S_IRUGO, device_dir,
1245
			&acpi_video_device_info_fops, acpi_driver_data(device));
L
Linus Torvalds 已提交
1246
	if (!entry)
1247
		goto err_remove_dir;
L
Linus Torvalds 已提交
1248 1249

	/* 'state' [R/W] */
1250 1251
	acpi_video_device_state_fops.write = acpi_video_device_write_state;
	entry = proc_create_data("state", S_IFREG | S_IRUGO | S_IWUSR,
1252
				 device_dir,
1253 1254
				 &acpi_video_device_state_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1255
	if (!entry)
1256
		goto err_remove_info;
L
Linus Torvalds 已提交
1257 1258

	/* 'brightness' [R/W] */
1259 1260 1261
	acpi_video_device_brightness_fops.write =
		acpi_video_device_write_brightness;
	entry = proc_create_data("brightness", S_IFREG | S_IRUGO | S_IWUSR,
1262
				 device_dir,
1263 1264
				 &acpi_video_device_brightness_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1265
	if (!entry)
1266
		goto err_remove_state;
L
Linus Torvalds 已提交
1267 1268

	/* 'EDID' [R] */
1269
	entry = proc_create_data("EDID", S_IRUGO, device_dir,
1270 1271
				 &acpi_video_device_EDID_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1272
	if (!entry)
1273
		goto err_remove_brightness;
L
Linus Torvalds 已提交
1274

1275 1276
	acpi_device_dir(device) = device_dir;

1277
	return 0;
1278 1279 1280 1281 1282 1283 1284 1285 1286 1287

 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 已提交
1288 1289
}

L
Len Brown 已提交
1290
static int acpi_video_device_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1291 1292
{
	struct acpi_video_device *vid_dev;
1293
	struct proc_dir_entry *device_dir;
L
Linus Torvalds 已提交
1294

1295
	vid_dev = acpi_driver_data(device);
L
Linus Torvalds 已提交
1296
	if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
1297
		return -ENODEV;
L
Linus Torvalds 已提交
1298

1299 1300 1301 1302 1303 1304
	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 已提交
1305
		remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
L
Linus Torvalds 已提交
1306 1307 1308
		acpi_device_dir(device) = NULL;
	}

1309
	return 0;
L
Linus Torvalds 已提交
1310 1311 1312
}

/* video bus */
L
Len Brown 已提交
1313
static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1314
{
1315
	struct acpi_video_bus *video = seq->private;
L
Linus Torvalds 已提交
1316 1317 1318 1319 1320 1321


	if (!video)
		goto end;

	seq_printf(seq, "Switching heads:              %s\n",
L
Len Brown 已提交
1322
		   video->flags.multihead ? "yes" : "no");
L
Linus Torvalds 已提交
1323
	seq_printf(seq, "Video ROM:                    %s\n",
L
Len Brown 已提交
1324
		   video->flags.rom ? "yes" : "no");
L
Linus Torvalds 已提交
1325
	seq_printf(seq, "Device to be POSTed on boot:  %s\n",
L
Len Brown 已提交
1326
		   video->flags.post ? "yes" : "no");
L
Linus Torvalds 已提交
1327

L
Len Brown 已提交
1328
      end:
1329
	return 0;
L
Linus Torvalds 已提交
1330 1331
}

L
Len Brown 已提交
1332
static int acpi_video_bus_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_info_seq_show,
			   PDE(inode)->data);
L
Linus Torvalds 已提交
1336 1337
}

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


	if (!video)
		goto end;

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

L
Len Brown 已提交
1349
      end:
1350
	return 0;
L
Linus Torvalds 已提交
1351 1352
}

L
Len Brown 已提交
1353
static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1354 1355 1356 1357
{
	return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data);
}

L
Len Brown 已提交
1358
static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1359
{
1360
	struct acpi_video_bus *video = seq->private;
1361
	unsigned long long options;
L
Len Brown 已提交
1362
	int status;
L
Linus Torvalds 已提交
1363 1364 1365 1366 1367 1368 1369 1370


	if (!video)
		goto end;

	status = acpi_video_bus_POST_options(video, &options);
	if (ACPI_SUCCESS(status)) {
		if (!(options & 1)) {
L
Len Brown 已提交
1371 1372 1373
			printk(KERN_WARNING PREFIX
			       "The motherboard VGA device is not listed as a possible POST device.\n");
			printk(KERN_WARNING PREFIX
1374
			       "This indicates a BIOS bug. Please contact the manufacturer.\n");
L
Linus Torvalds 已提交
1375
		}
1376
		printk(KERN_WARNING "%llx\n", options);
1377
		seq_printf(seq, "can POST: <integrated video>");
L
Linus Torvalds 已提交
1378 1379 1380 1381 1382 1383 1384
		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 已提交
1385
      end:
1386
	return 0;
L
Linus Torvalds 已提交
1387 1388 1389
}

static int
L
Len Brown 已提交
1390
acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1391
{
L
Len Brown 已提交
1392 1393
	return single_open(file, acpi_video_bus_POST_info_seq_show,
			   PDE(inode)->data);
L
Linus Torvalds 已提交
1394 1395
}

L
Len Brown 已提交
1396
static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
L
Linus Torvalds 已提交
1397
{
1398
	struct acpi_video_bus *video = seq->private;
L
Len Brown 已提交
1399
	int status;
1400
	unsigned long long id;
L
Linus Torvalds 已提交
1401 1402 1403 1404 1405


	if (!video)
		goto end;

L
Len Brown 已提交
1406
	status = acpi_video_bus_get_POST(video, &id);
L
Linus Torvalds 已提交
1407 1408 1409 1410
	if (!ACPI_SUCCESS(status)) {
		seq_printf(seq, "<not supported>\n");
		goto end;
	}
1411
	seq_printf(seq, "device POSTed is <%s>\n", device_decode[id & 3]);
L
Linus Torvalds 已提交
1412

L
Len Brown 已提交
1413
      end:
1414
	return 0;
L
Linus Torvalds 已提交
1415 1416
}

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


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

1424
	return 0;
L
Linus Torvalds 已提交
1425 1426
}

L
Len Brown 已提交
1427
static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1428
{
L
Len Brown 已提交
1429 1430
	return single_open(file, acpi_video_bus_POST_seq_show,
			   PDE(inode)->data);
L
Linus Torvalds 已提交
1431 1432
}

L
Len Brown 已提交
1433
static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
1434 1435 1436 1437 1438
{
	return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data);
}

static ssize_t
L
Len Brown 已提交
1439 1440 1441
acpi_video_bus_write_POST(struct file *file,
			  const char __user * buffer,
			  size_t count, loff_t * data)
L
Linus Torvalds 已提交
1442
{
L
Len Brown 已提交
1443
	int status;
1444 1445
	struct seq_file *m = file->private_data;
	struct acpi_video_bus *video = m->private;
L
Len Brown 已提交
1446
	char str[12] = { 0 };
1447
	unsigned long long opt, options;
L
Linus Torvalds 已提交
1448 1449 1450


	if (!video || count + 1 > sizeof str)
1451
		return -EINVAL;
L
Linus Torvalds 已提交
1452 1453 1454

	status = acpi_video_bus_POST_options(video, &options);
	if (!ACPI_SUCCESS(status))
1455
		return -EINVAL;
L
Linus Torvalds 已提交
1456 1457

	if (copy_from_user(str, buffer, count))
1458
		return -EFAULT;
L
Linus Torvalds 已提交
1459 1460 1461 1462

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

1465
	/* just in case an OEM 'forgot' the motherboard... */
L
Linus Torvalds 已提交
1466 1467 1468
	options |= 1;

	if (options & (1ul << opt)) {
L
Len Brown 已提交
1469
		status = acpi_video_bus_set_POST(video, opt);
L
Linus Torvalds 已提交
1470
		if (!ACPI_SUCCESS(status))
1471
			return -EFAULT;
L
Linus Torvalds 已提交
1472 1473 1474

	}

1475
	return count;
L
Linus Torvalds 已提交
1476 1477 1478
}

static ssize_t
L
Len Brown 已提交
1479 1480 1481
acpi_video_bus_write_DOS(struct file *file,
			 const char __user * buffer,
			 size_t count, loff_t * data)
L
Linus Torvalds 已提交
1482
{
L
Len Brown 已提交
1483
	int status;
1484 1485
	struct seq_file *m = file->private_data;
	struct acpi_video_bus *video = m->private;
L
Len Brown 已提交
1486 1487
	char str[12] = { 0 };
	unsigned long opt;
L
Linus Torvalds 已提交
1488 1489 1490


	if (!video || count + 1 > sizeof str)
1491
		return -EINVAL;
L
Linus Torvalds 已提交
1492 1493

	if (copy_from_user(str, buffer, count))
1494
		return -EFAULT;
L
Linus Torvalds 已提交
1495 1496 1497 1498

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

L
Len Brown 已提交
1501
	status = acpi_video_bus_DOS(video, opt & 0x3, (opt & 0x4) >> 2);
L
Linus Torvalds 已提交
1502 1503

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

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

L
Len Brown 已提交
1509
static int acpi_video_bus_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1510
{
1511 1512 1513
	struct acpi_video_bus *video = acpi_driver_data(device);
	struct proc_dir_entry *device_dir;
	struct proc_dir_entry *entry;
L
Linus Torvalds 已提交
1514

1515 1516 1517
	device_dir = proc_mkdir(acpi_device_bid(device), acpi_video_dir);
	if (!device_dir)
		return -ENOMEM;
L
Linus Torvalds 已提交
1518

1519
	device_dir->owner = THIS_MODULE;
L
Linus Torvalds 已提交
1520 1521

	/* 'info' [R] */
1522
	entry = proc_create_data("info", S_IRUGO, device_dir,
1523 1524
				 &acpi_video_bus_info_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1525
	if (!entry)
1526
		goto err_remove_dir;
L
Linus Torvalds 已提交
1527 1528

	/* 'ROM' [R] */
1529
	entry = proc_create_data("ROM", S_IRUGO, device_dir,
1530 1531
				 &acpi_video_bus_ROM_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1532
	if (!entry)
1533
		goto err_remove_info;
L
Linus Torvalds 已提交
1534 1535

	/* 'POST_info' [R] */
1536
	entry = proc_create_data("POST_info", S_IRUGO, device_dir,
1537 1538
				 &acpi_video_bus_POST_info_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1539
	if (!entry)
1540
		goto err_remove_rom;
L
Linus Torvalds 已提交
1541 1542

	/* 'POST' [R/W] */
1543
	acpi_video_bus_POST_fops.write = acpi_video_bus_write_POST;
1544
	entry = proc_create_data("POST", S_IFREG | S_IRUGO | S_IWUSR,
1545
				 device_dir,
1546 1547
				 &acpi_video_bus_POST_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1548
	if (!entry)
1549
		goto err_remove_post_info;
L
Linus Torvalds 已提交
1550 1551

	/* 'DOS' [R/W] */
1552
	acpi_video_bus_DOS_fops.write = acpi_video_bus_write_DOS;
1553
	entry = proc_create_data("DOS", S_IFREG | S_IRUGO | S_IWUSR,
1554
				 device_dir,
1555 1556
				 &acpi_video_bus_DOS_fops,
				 acpi_driver_data(device));
L
Linus Torvalds 已提交
1557
	if (!entry)
1558
		goto err_remove_post;
L
Linus Torvalds 已提交
1559

1560
	video->dir = acpi_device_dir(device) = device_dir;
1561
	return 0;
1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573

 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 已提交
1574 1575
}

L
Len Brown 已提交
1576
static int acpi_video_bus_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
1577
{
1578 1579 1580 1581 1582 1583 1584 1585
	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 已提交
1586
		remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
L
Linus Torvalds 已提交
1587 1588 1589
		acpi_device_dir(device) = NULL;
	}

1590
	return 0;
L
Linus Torvalds 已提交
1591 1592 1593 1594 1595 1596 1597
}

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

/* device interface */
1598 1599 1600
static struct acpi_video_device_attrib*
acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id)
{
1601 1602 1603 1604 1605 1606 1607 1608
	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;
	}
1609 1610 1611

	return NULL;
}
L
Linus Torvalds 已提交
1612 1613

static int
L
Len Brown 已提交
1614 1615
acpi_video_bus_get_one_device(struct acpi_device *device,
			      struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1616
{
1617
	unsigned long long device_id;
1618
	int status;
L
Len Brown 已提交
1619
	struct acpi_video_device *data;
1620
	struct acpi_video_device_attrib* attribute;
L
Linus Torvalds 已提交
1621 1622

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

L
Len Brown 已提交
1625 1626
	status =
	    acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
L
Linus Torvalds 已提交
1627 1628
	if (ACPI_SUCCESS(status)) {

1629
		data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
L
Linus Torvalds 已提交
1630
		if (!data)
1631
			return -ENOMEM;
L
Linus Torvalds 已提交
1632 1633 1634

		strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
		strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
1635
		device->driver_data = data;
L
Linus Torvalds 已提交
1636 1637 1638 1639 1640

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

1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663
		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 已提交
1664
			data->flags.unknown = 1;
L
Len Brown 已提交
1665

L
Linus Torvalds 已提交
1666 1667 1668
		acpi_video_device_bind(video, data);
		acpi_video_device_find_cap(data);

1669
		status = acpi_install_notify_handler(device->handle,
L
Len Brown 已提交
1670 1671 1672
						     ACPI_DEVICE_NOTIFY,
						     acpi_video_device_notify,
						     data);
L
Linus Torvalds 已提交
1673
		if (ACPI_FAILURE(status)) {
1674 1675
			printk(KERN_ERR PREFIX
					  "Error installing notify handler\n");
1676 1677 1678 1679 1680
			if(data->brightness)
				kfree(data->brightness->levels);
			kfree(data->brightness);
			kfree(data);
			return -ENODEV;
L
Linus Torvalds 已提交
1681 1682
		}

1683
		mutex_lock(&video->device_list_lock);
L
Linus Torvalds 已提交
1684
		list_add_tail(&data->entry, &video->video_device_list);
1685
		mutex_unlock(&video->device_list_lock);
L
Linus Torvalds 已提交
1686 1687 1688

		acpi_video_device_add_fs(device);

1689
		return 0;
L
Linus Torvalds 已提交
1690 1691
	}

1692
	return -ENOENT;
L
Linus Torvalds 已提交
1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704
}

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

L
Len Brown 已提交
1707
static void acpi_video_device_rebind(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1708
{
1709 1710
	struct acpi_video_device *dev;

1711
	mutex_lock(&video->device_list_lock);
1712 1713

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

1716
	mutex_unlock(&video->device_list_lock);
L
Linus Torvalds 已提交
1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729
}

/*
 *  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 已提交
1730
 */
L
Linus Torvalds 已提交
1731 1732

static void
L
Len Brown 已提交
1733 1734
acpi_video_device_bind(struct acpi_video_bus *video,
		       struct acpi_video_device *device)
L
Linus Torvalds 已提交
1735
{
1736
	struct acpi_video_enumerated_device *ids;
L
Len Brown 已提交
1737
	int i;
L
Linus Torvalds 已提交
1738

1739 1740 1741 1742
	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 已提交
1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756
			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 已提交
1757
 */
L
Linus Torvalds 已提交
1758 1759 1760

static int acpi_video_device_enumerate(struct acpi_video_bus *video)
{
L
Len Brown 已提交
1761 1762 1763
	int status;
	int count;
	int i;
1764
	struct acpi_video_enumerated_device *active_list;
L
Len Brown 已提交
1765 1766 1767
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *dod = NULL;
	union acpi_object *obj;
L
Linus Torvalds 已提交
1768

1769
	status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
L
Linus Torvalds 已提交
1770
	if (!ACPI_SUCCESS(status)) {
1771
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD"));
1772
		return status;
L
Linus Torvalds 已提交
1773 1774
	}

1775
	dod = buffer.pointer;
L
Linus Torvalds 已提交
1776
	if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
1777
		ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data"));
L
Linus Torvalds 已提交
1778 1779 1780 1781 1782
		status = -EFAULT;
		goto out;
	}

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

1785 1786 1787 1788
	active_list = kcalloc(1 + dod->package.count,
			      sizeof(struct acpi_video_enumerated_device),
			      GFP_KERNEL);
	if (!active_list) {
L
Linus Torvalds 已提交
1789 1790 1791 1792 1793 1794
		status = -ENOMEM;
		goto out;
	}

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

		if (obj->type != ACPI_TYPE_INTEGER) {
1798 1799 1800
			printk(KERN_ERR PREFIX
				"Invalid _DOD data in element %d\n", i);
			continue;
L
Linus Torvalds 已提交
1801
		}
1802 1803 1804

		active_list[count].value.int_val = obj->integer.value;
		active_list[count].bind_info = NULL;
L
Len Brown 已提交
1805 1806
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i,
				  (int)obj->integer.value));
L
Linus Torvalds 已提交
1807 1808 1809
		count++;
	}

1810
	kfree(video->attached_array);
L
Len Brown 已提交
1811

1812
	video->attached_array = active_list;
L
Linus Torvalds 已提交
1813
	video->attached_count = count;
1814 1815

 out:
1816
	kfree(buffer.pointer);
1817
	return status;
L
Linus Torvalds 已提交
1818 1819
}

L
Len Brown 已提交
1820 1821 1822
static int
acpi_video_get_next_level(struct acpi_video_device *device,
			  u32 level_current, u32 event)
L
Linus Torvalds 已提交
1823
{
1824
	int min, max, min_above, max_below, i, l, delta = 255;
1825 1826
	max = max_below = 0;
	min = min_above = 255;
1827
	/* Find closest level to level_current */
1828
	for (i = 2; i < device->brightness->count; i++) {
1829 1830 1831 1832 1833 1834 1835 1836 1837
		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;
1838
	for (i = 2; i < device->brightness->count; i++) {
1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862
		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 已提交
1863 1864
}

1865
static int
L
Len Brown 已提交
1866
acpi_video_switch_brightness(struct acpi_video_device *device, int event)
L
Linus Torvalds 已提交
1867
{
1868
	unsigned long long level_current, level_next;
1869 1870
	int result = -EINVAL;

1871
	if (!device->brightness)
1872 1873 1874 1875 1876 1877 1878
		goto out;

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

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

1881
	result = acpi_video_device_lcd_set_level(device, level_next);
1882 1883 1884 1885 1886 1887

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

	return result;
L
Linus Torvalds 已提交
1888 1889 1890
}

static int
L
Len Brown 已提交
1891 1892
acpi_video_bus_get_devices(struct acpi_video_bus *video,
			   struct acpi_device *device)
L
Linus Torvalds 已提交
1893
{
L
Len Brown 已提交
1894
	int status = 0;
1895
	struct acpi_device *dev;
L
Linus Torvalds 已提交
1896 1897 1898

	acpi_video_device_enumerate(video);

1899
	list_for_each_entry(dev, &device->children, node) {
L
Linus Torvalds 已提交
1900 1901 1902

		status = acpi_video_bus_get_one_device(dev, video);
		if (ACPI_FAILURE(status)) {
1903 1904
			printk(KERN_WARNING PREFIX
					"Cant attach device");
L
Linus Torvalds 已提交
1905 1906 1907
			continue;
		}
	}
1908
	return status;
L
Linus Torvalds 已提交
1909 1910
}

L
Len Brown 已提交
1911
static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
L
Linus Torvalds 已提交
1912
{
1913
	acpi_status status;
L
Linus Torvalds 已提交
1914 1915 1916 1917
	struct acpi_video_bus *video;


	if (!device || !device->video)
1918
		return -ENOENT;
L
Linus Torvalds 已提交
1919 1920 1921 1922 1923

	video = device->video;

	acpi_video_device_remove_fs(device->dev);

1924
	status = acpi_remove_notify_handler(device->dev->handle,
L
Len Brown 已提交
1925 1926
					    ACPI_DEVICE_NOTIFY,
					    acpi_video_device_notify);
1927
	backlight_device_unregister(device->backlight);
1928 1929 1930 1931 1932 1933 1934 1935
	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;
	}
1936
	video_output_unregister(device->output_dev);
1937

1938
	return 0;
L
Linus Torvalds 已提交
1939 1940
}

L
Len Brown 已提交
1941
static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1942
{
L
Len Brown 已提交
1943
	int status;
1944
	struct acpi_video_device *dev, *next;
L
Linus Torvalds 已提交
1945

1946
	mutex_lock(&video->device_list_lock);
L
Linus Torvalds 已提交
1947

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

1950
		status = acpi_video_bus_put_one_device(dev);
L
Len Brown 已提交
1951 1952 1953
		if (ACPI_FAILURE(status))
			printk(KERN_WARNING PREFIX
			       "hhuuhhuu bug in acpi video driver.\n");
L
Linus Torvalds 已提交
1954

1955 1956 1957 1958 1959 1960
		if (dev->brightness) {
			kfree(dev->brightness->levels);
			kfree(dev->brightness);
		}
		list_del(&dev->entry);
		kfree(dev);
L
Linus Torvalds 已提交
1961 1962
	}

1963
	mutex_unlock(&video->device_list_lock);
1964

1965
	return 0;
L
Linus Torvalds 已提交
1966 1967 1968 1969
}

/* acpi_video interface */

L
Len Brown 已提交
1970
static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1971
{
1972
	return acpi_video_bus_DOS(video, 0, 0);
L
Linus Torvalds 已提交
1973 1974
}

L
Len Brown 已提交
1975
static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
L
Linus Torvalds 已提交
1976 1977 1978 1979
{
	return acpi_video_bus_DOS(video, 0, 1);
}

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

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

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

	switch (event) {
1994
	case ACPI_VIDEO_NOTIFY_SWITCH:	/* User requested a switch,
L
Linus Torvalds 已提交
1995
					 * most likely via hotkey. */
1996
		acpi_bus_generate_proc_event(device, event, 0);
1997
		keycode = KEY_SWITCHVIDEOMODE;
L
Linus Torvalds 已提交
1998 1999
		break;

2000
	case ACPI_VIDEO_NOTIFY_PROBE:	/* User plugged in or removed a video
L
Linus Torvalds 已提交
2001 2002 2003
					 * connector. */
		acpi_video_device_enumerate(video);
		acpi_video_device_rebind(video);
2004
		acpi_bus_generate_proc_event(device, event, 0);
2005
		keycode = KEY_SWITCHVIDEOMODE;
L
Linus Torvalds 已提交
2006 2007
		break;

L
Len Brown 已提交
2008
	case ACPI_VIDEO_NOTIFY_CYCLE:	/* Cycle Display output hotkey pressed. */
L
Len Brown 已提交
2009
		acpi_bus_generate_proc_event(device, event, 0);
2010 2011
		keycode = KEY_SWITCHVIDEOMODE;
		break;
L
Len Brown 已提交
2012
	case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:	/* Next Display output hotkey pressed. */
L
Len Brown 已提交
2013
		acpi_bus_generate_proc_event(device, event, 0);
2014 2015
		keycode = KEY_VIDEO_NEXT;
		break;
L
Len Brown 已提交
2016
	case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:	/* previous Display output hotkey pressed. */
2017
		acpi_bus_generate_proc_event(device, event, 0);
2018
		keycode = KEY_VIDEO_PREV;
L
Linus Torvalds 已提交
2019 2020 2021
		break;

	default:
2022
		keycode = KEY_UNKNOWN;
L
Linus Torvalds 已提交
2023
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
2024
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
2025 2026 2027
		break;
	}

2028
	acpi_notifier_call_chain(device, event, 0);
2029 2030 2031 2032 2033
	input_report_key(input, keycode, 1);
	input_sync(input);
	input_report_key(input, keycode, 0);
	input_sync(input);

2034
	return;
L
Linus Torvalds 已提交
2035 2036
}

L
Len Brown 已提交
2037
static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
L
Linus Torvalds 已提交
2038
{
2039
	struct acpi_video_device *video_device = data;
L
Len Brown 已提交
2040
	struct acpi_device *device = NULL;
2041 2042 2043
	struct acpi_video_bus *bus;
	struct input_dev *input;
	int keycode;
L
Linus Torvalds 已提交
2044 2045

	if (!video_device)
2046
		return;
L
Linus Torvalds 已提交
2047

2048
	device = video_device->dev;
2049 2050
	bus = video_device->video;
	input = bus->input;
L
Linus Torvalds 已提交
2051 2052

	switch (event) {
L
Len Brown 已提交
2053
	case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:	/* Cycle brightness */
2054 2055
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
2056
		acpi_bus_generate_proc_event(device, event, 0);
2057 2058
		keycode = KEY_BRIGHTNESS_CYCLE;
		break;
L
Len Brown 已提交
2059
	case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:	/* Increase brightness */
2060 2061
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
L
Len Brown 已提交
2062
		acpi_bus_generate_proc_event(device, event, 0);
2063 2064
		keycode = KEY_BRIGHTNESSUP;
		break;
L
Len Brown 已提交
2065
	case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:	/* Decrease brightness */
2066 2067
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
L
Len Brown 已提交
2068
		acpi_bus_generate_proc_event(device, event, 0);
2069 2070
		keycode = KEY_BRIGHTNESSDOWN;
		break;
L
Len Brown 已提交
2071
	case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:	/* zero brightnesss */
2072 2073
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
L
Len Brown 已提交
2074
		acpi_bus_generate_proc_event(device, event, 0);
2075 2076
		keycode = KEY_BRIGHTNESS_ZERO;
		break;
L
Len Brown 已提交
2077
	case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:	/* display device off */
2078 2079
		if (brightness_switch_enabled)
			acpi_video_switch_brightness(video_device, event);
2080
		acpi_bus_generate_proc_event(device, event, 0);
2081
		keycode = KEY_DISPLAY_OFF;
L
Linus Torvalds 已提交
2082 2083
		break;
	default:
2084
		keycode = KEY_UNKNOWN;
L
Linus Torvalds 已提交
2085
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
2086
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
2087 2088
		break;
	}
2089

2090
	acpi_notifier_call_chain(device, event, 0);
2091 2092 2093 2094 2095
	input_report_key(input, keycode, 1);
	input_sync(input);
	input_report_key(input, keycode, 0);
	input_sync(input);

2096
	return;
L
Linus Torvalds 已提交
2097 2098
}

2099
static int instance;
2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118
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 已提交
2119
static int acpi_video_bus_add(struct acpi_device *device)
L
Linus Torvalds 已提交
2120
{
2121 2122
	acpi_status status;
	struct acpi_video_bus *video;
2123
	struct input_dev *input;
2124
	int error;
L
Linus Torvalds 已提交
2125

2126
	video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
L
Linus Torvalds 已提交
2127
	if (!video)
2128
		return -ENOMEM;
L
Linus Torvalds 已提交
2129

2130 2131 2132 2133 2134 2135
	/* 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 ++;
	}
2136 2137 2138 2139 2140 2141
	/* 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++;
	}
2142

2143
	video->device = device;
L
Linus Torvalds 已提交
2144 2145
	strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
	strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
2146
	device->driver_data = video;
L
Linus Torvalds 已提交
2147 2148

	acpi_video_bus_find_cap(video);
2149 2150 2151
	error = acpi_video_bus_check(video);
	if (error)
		goto err_free_video;
L
Linus Torvalds 已提交
2152

2153 2154 2155
	error = acpi_video_bus_add_fs(device);
	if (error)
		goto err_free_video;
L
Linus Torvalds 已提交
2156

2157
	mutex_init(&video->device_list_lock);
L
Linus Torvalds 已提交
2158 2159 2160 2161 2162
	INIT_LIST_HEAD(&video->video_device_list);

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

2163
	status = acpi_install_notify_handler(device->handle,
L
Len Brown 已提交
2164 2165
					     ACPI_DEVICE_NOTIFY,
					     acpi_video_bus_notify, video);
L
Linus Torvalds 已提交
2166
	if (ACPI_FAILURE(status)) {
2167 2168
		printk(KERN_ERR PREFIX
				  "Error installing notify handler\n");
2169 2170
		error = -ENODEV;
		goto err_stop_video;
L
Linus Torvalds 已提交
2171 2172
	}

2173
	video->input = input = input_allocate_device();
2174 2175 2176 2177
	if (!input) {
		error = -ENOMEM;
		goto err_uninstall_notify;
	}
2178 2179 2180 2181 2182 2183 2184 2185

	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;
2186
	input->dev.parent = &device->dev;
2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197
	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);

2198 2199 2200
	error = input_register_device(input);
	if (error)
		goto err_free_input_dev;
2201

L
Linus Torvalds 已提交
2202
	printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s  rom: %s  post: %s)\n",
L
Len Brown 已提交
2203 2204 2205 2206
	       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 已提交
2207

2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221
	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);
2222
	device->driver_data = NULL;
L
Linus Torvalds 已提交
2223

2224
	return error;
L
Linus Torvalds 已提交
2225 2226
}

L
Len Brown 已提交
2227
static int acpi_video_bus_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
2228
{
L
Len Brown 已提交
2229 2230
	acpi_status status = 0;
	struct acpi_video_bus *video = NULL;
L
Linus Torvalds 已提交
2231 2232 2233


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

2236
	video = acpi_driver_data(device);
L
Linus Torvalds 已提交
2237 2238 2239

	acpi_video_bus_stop_devices(video);

2240
	status = acpi_remove_notify_handler(video->device->handle,
L
Len Brown 已提交
2241 2242
					    ACPI_DEVICE_NOTIFY,
					    acpi_video_bus_notify);
L
Linus Torvalds 已提交
2243 2244 2245 2246

	acpi_video_bus_put_devices(video);
	acpi_video_bus_remove_fs(device);

2247
	input_unregister_device(video->input);
2248
	kfree(video->attached_array);
L
Linus Torvalds 已提交
2249 2250
	kfree(video);

2251
	return 0;
L
Linus Torvalds 已提交
2252 2253
}

L
Len Brown 已提交
2254
static int __init acpi_video_init(void)
L
Linus Torvalds 已提交
2255
{
L
Len Brown 已提交
2256
	int result = 0;
L
Linus Torvalds 已提交
2257 2258 2259

	acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
	if (!acpi_video_dir)
2260
		return -ENODEV;
L
Linus Torvalds 已提交
2261 2262 2263 2264 2265
	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);
2266
		return -ENODEV;
L
Linus Torvalds 已提交
2267 2268
	}

2269
	return 0;
L
Linus Torvalds 已提交
2270 2271
}

L
Len Brown 已提交
2272
static void __exit acpi_video_exit(void)
L
Linus Torvalds 已提交
2273 2274 2275 2276 2277 2278
{

	acpi_bus_unregister_driver(&acpi_video_bus);

	remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);

2279
	return;
L
Linus Torvalds 已提交
2280 2281 2282 2283
}

module_init(acpi_video_init);
module_exit(acpi_video_exit);