/*
- * video.c - ACPI Video Driver ($Revision:$)
+ * video.c - ACPI Video Driver
*
* Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
* Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org>
static bool use_bios_initial_backlight = 1;
module_param(use_bios_initial_backlight, bool, 0644);
-static int register_count = 0;
+/*
+ * For Windows 8 systems: if set ture and the GPU driver has
+ * registered a backlight interface, skip registering ACPI video's.
+ */
+static bool use_native_backlight = false;
+module_param(use_native_backlight, bool, 0644);
+
+static int register_count;
+static struct mutex video_list_lock;
+static struct list_head video_bus_head;
static int acpi_video_bus_add(struct acpi_device *device);
static int acpi_video_bus_remove(struct acpi_device *device);
static void acpi_video_bus_notify(struct acpi_device *device, u32 event);
};
struct acpi_video_bus_cap {
- 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 _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;
};
struct acpi_video_device_attrib {
u32 display_index:4; /* A zero-based instance of the Display */
- u32 display_port_attachment:4; /*This field differentiates the display type */
- u32 display_type:4; /*Describe the specific type in use */
- u32 vendor_specific:4; /*Chipset Vendor Specific */
- u32 bios_can_detect:1; /*BIOS can detect the device */
- u32 depend_on_vga:1; /*Non-VGA output device whose power is related to
+ u32 display_port_attachment:4; /* This field differentiates the display type */
+ u32 display_type:4; /* Describe the specific type in use */
+ u32 vendor_specific:4; /* Chipset Vendor Specific */
+ 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 */
+ u32 pipe_id:3; /* For VGA multiple-head devices. */
+ u32 reserved:10; /* Must be 0 */
+ u32 device_id_scheme:1; /* Device ID Scheme */
};
struct acpi_video_enumerated_device {
struct acpi_video_bus_flags flags;
struct list_head video_device_list;
struct mutex device_list_lock; /* protects video_device_list */
+ struct list_head entry;
struct input_dev *input;
char phys[32]; /* for input device */
struct notifier_block pm_nb;
};
struct acpi_video_device_cap {
- 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 _ADR:1; /* Return the unique ID */
+ u8 _BCL:1; /* Query list of brightness control levels supported */
+ u8 _BCM:1; /* Set the brightness level */
u8 _BQC:1; /* Get current brightness level */
u8 _BCQ:1; /* Some buggy BIOS uses _BCQ instead of _BQC */
- u8 _DDC:1; /*Return the EDID for this device */
+ u8 _DDC:1; /* Return the EDID for this device */
};
struct acpi_video_brightness_flags {
u8 _BCL_no_ac_battery_levels:1; /* no AC/Battery levels in _BCL */
- u8 _BCL_reversed:1; /* _BCL package is in a reversed order*/
- u8 _BCL_use_index:1; /* levels in _BCL are index values */
- u8 _BCM_use_index:1; /* input of _BCM is an index value */
+ u8 _BCL_reversed:1; /* _BCL package is in a reversed order */
u8 _BQC_use_index:1; /* _BQC returns an index value */
};
static int acpi_video_switch_brightness(struct acpi_video_device *device,
int event);
-/*backlight device sysfs support*/
+static bool acpi_video_verify_backlight_support(void)
+{
+ if (acpi_osi_is_win8() && use_native_backlight &&
+ backlight_device_registered(BACKLIGHT_RAW))
+ return false;
+ return acpi_video_backlight_support();
+}
+
+/* backlight device sysfs support */
static int acpi_video_get_brightness(struct backlight_device *bd)
{
unsigned long long cur_level;
int i;
- struct acpi_video_device *vd =
- (struct acpi_video_device *)bl_get_data(bd);
+ struct acpi_video_device *vd = bl_get_data(bd);
if (acpi_video_device_lcd_get_level_current(vd, &cur_level, false))
return -EINVAL;
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;
+ /*
+ * The first two entries are special - see page 575
+ * of the ACPI spec 3.0
+ */
+ return i - 2;
}
return 0;
}
static int acpi_video_set_brightness(struct backlight_device *bd)
{
int request_level = bd->props.brightness + 2;
- struct acpi_video_device *vd =
- (struct acpi_video_device *)bl_get_data(bd);
+ struct acpi_video_device *vd = bl_get_data(bd);
return acpi_video_device_lcd_set_level(vd,
vd->brightness->levels[request_level]);
struct acpi_video_device *video = acpi_driver_data(device);
int level;
- if ( state >= video->brightness->count - 2)
+ if (state >= video->brightness->count - 2)
return -EINVAL;
state = video->brightness->count - state;
- level = video->brightness->levels[state -1];
+ level = video->brightness->levels[state - 1];
return acpi_video_device_lcd_set_level(video, level);
}
.set_cur_state = video_set_cur_state,
};
-/* --------------------------------------------------------------------------
- Video Management
- -------------------------------------------------------------------------- */
+/*
+ * --------------------------------------------------------------------------
+ * Video Management
+ * --------------------------------------------------------------------------
+ */
static int
acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
return 0;
- err:
+err:
kfree(buffer.pointer);
return status;
acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
{
int status;
- union acpi_object arg0 = { ACPI_TYPE_INTEGER };
- struct acpi_object_list args = { 1, &arg0 };
int state;
- arg0.integer.value = level;
-
- status = acpi_evaluate_object(device->dev->handle, "_BCM",
- &args, NULL);
+ status = acpi_execute_simple_method(device->dev->handle,
+ "_BCM", level);
if (ACPI_FAILURE(status)) {
ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));
return -EIO;
if (device->brightness->levels[i] == *level) {
device->brightness->curr = *level;
return 0;
- }
+ }
/*
* BQC returned an invalid level.
* Stop using it.
buf));
device->cap._BQC = device->cap._BCQ = 0;
} else {
- /* Fixme:
+ /*
+ * 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.
/*
* Arg:
- * video : video bus device pointer
- * bios_flag :
+ * 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
* 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
+ * 1. The system BIOS should NOT automatically control the brightness
* level of the LCD when the power changes from AC to DC.
- * Return Value:
+ * Return Value:
* -EINVAL wrong arg.
*/
acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
{
acpi_status status;
- union acpi_object arg0 = { ACPI_TYPE_INTEGER };
- struct acpi_object_list args = { 1, &arg0 };
if (!video->cap._DOS)
return 0;
if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1)
return -EINVAL;
- arg0.integer.value = (lcd_flag << 2) | bios_flag;
- video->dos_setting = arg0.integer.value;
- status = acpi_evaluate_object(video->device->handle, "_DOS",
- &args, NULL);
+ video->dos_setting = (lcd_flag << 2) | bios_flag;
+ status = acpi_execute_simple_method(video->device->handle, "_DOS",
+ (lcd_flag << 2) | bios_flag);
if (ACPI_FAILURE(status))
return -EIO;
/*
- * Arg:
- * device : video output device (LCD, CRT, ..)
+ * Arg:
+ * device : video output device (LCD, CRT, ..)
*
* Return Value:
* Maximum brightness level
br->count = count;
device->brightness = br;
- /* 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 = level = max_level;
* device : video output device (LCD, CRT, ..)
*
* Return Value:
- * None
+ * None
*
* Find out all required AML methods defined under the output
* device.
static void acpi_video_device_find_cap(struct acpi_video_device *device)
{
- acpi_handle h_dummy1;
-
- if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
+ if (acpi_has_method(device->dev->handle, "_ADR"))
device->cap._ADR = 1;
- }
- if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) {
+ if (acpi_has_method(device->dev->handle, "_BCL"))
device->cap._BCL = 1;
- }
- if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
+ if (acpi_has_method(device->dev->handle, "_BCM"))
device->cap._BCM = 1;
- }
- if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
+ if (acpi_has_method(device->dev->handle, "_BQC")) {
device->cap._BQC = 1;
- else if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCQ",
- &h_dummy1))) {
+ } else if (acpi_has_method(device->dev->handle, "_BCQ")) {
printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n");
device->cap._BCQ = 1;
}
- if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
+ if (acpi_has_method(device->dev->handle, "_DDC"))
device->cap._DDC = 1;
- }
-
- if (acpi_video_backlight_support()) {
- struct backlight_properties props;
- struct pci_dev *pdev;
- acpi_handle acpi_parent;
- struct device *parent = NULL;
- int result;
- static int count = 0;
- char *name;
-
- result = acpi_video_init_brightness(device);
- if (result)
- return;
- name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
- if (!name)
- return;
- count++;
-
- acpi_get_parent(device->dev->handle, &acpi_parent);
-
- pdev = acpi_get_pci_dev(acpi_parent);
- if (pdev) {
- parent = &pdev->dev;
- pci_dev_put(pdev);
- }
-
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_FIRMWARE;
- props.max_brightness = device->brightness->count - 3;
- device->backlight = backlight_device_register(name,
- parent,
- device,
- &acpi_backlight_ops,
- &props);
- kfree(name);
- if (IS_ERR(device->backlight))
- return;
-
- /*
- * Save current brightness level in case we have to restore it
- * before acpi_video_device_lcd_set_level() is called next time.
- */
- device->backlight->props.brightness =
- acpi_video_get_brightness(device->backlight);
-
- device->cooling_dev = thermal_cooling_device_register("LCD",
- device->dev, &video_cooling_ops);
- if (IS_ERR(device->cooling_dev)) {
- /*
- * Set cooling_dev to NULL so we don't crash trying to
- * free it.
- * Also, why the hell we are returning early and
- * not attempt to register video output if cooling
- * device registration failed?
- * -- dtor
- */
- device->cooling_dev = NULL;
- return;
- }
-
- dev_info(&device->dev->dev, "registered as cooling_device%d\n",
- device->cooling_dev->id);
- result = sysfs_create_link(&device->dev->dev.kobj,
- &device->cooling_dev->device.kobj,
- "thermal_cooling");
- if (result)
- printk(KERN_ERR PREFIX "Create sysfs link\n");
- result = sysfs_create_link(&device->cooling_dev->device.kobj,
- &device->dev->dev.kobj, "device");
- if (result)
- printk(KERN_ERR PREFIX "Create sysfs link\n");
-
- }
}
/*
- * Arg:
- * device : video output device (VGA)
+ * Arg:
+ * device : video output device (VGA)
*
* Return Value:
- * None
+ * None
*
* Find out all required AML methods defined under the video bus device.
*/
static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
{
- acpi_handle h_dummy1;
-
- if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
+ if (acpi_has_method(video->device->handle, "_DOS"))
video->cap._DOS = 1;
- }
- if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) {
+ if (acpi_has_method(video->device->handle, "_DOD"))
video->cap._DOD = 1;
- }
- if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) {
+ if (acpi_has_method(video->device->handle, "_ROM"))
video->cap._ROM = 1;
- }
- if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) {
+ if (acpi_has_method(video->device->handle, "_GPD"))
video->cap._GPD = 1;
- }
- if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) {
+ if (acpi_has_method(video->device->handle, "_SPD"))
video->cap._SPD = 1;
- }
- if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) {
+ if (acpi_has_method(video->device->handle, "_VPO"))
video->cap._VPO = 1;
- }
}
/*
return -ENODEV;
pci_dev_put(dev);
- /* Since there is no HID, CID and so on for VGA driver, we have
+ /*
+ * Since there is no HID, CID and so on for VGA driver, we have
* to check well known required nodes.
*/
return status;
}
-/* --------------------------------------------------------------------------
- Driver Interface
- -------------------------------------------------------------------------- */
+/*
+ * --------------------------------------------------------------------------
+ * Driver Interface
+ * --------------------------------------------------------------------------
+ */
/* device interface */
-static struct acpi_video_device_attrib*
+static struct acpi_video_device_attrib *
acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id)
{
struct acpi_video_enumerated_device *ids;
unsigned long long device_id;
int status, device_type;
struct acpi_video_device *data;
- struct acpi_video_device_attrib* attribute;
+ struct acpi_video_device_attrib *attribute;
status =
acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
attribute = acpi_video_get_device_attr(video, device_id);
- if((attribute != NULL) && attribute->device_id_scheme) {
+ if (attribute && attribute->device_id_scheme) {
switch (attribute->display_type) {
case ACPI_VIDEO_DISPLAY_CRT:
data->flags.crt = 1;
data->flags.unknown = 1;
break;
}
- if(attribute->bios_can_detect)
+ if (attribute->bios_can_detect)
data->flags.bios = 1;
} else {
/* Check for legacy IDs */
device_type = acpi_video_get_device_type(video, device_id);
/* Ignore bits 16 and 18-20 */
switch (device_type & 0xffe2ffff) {
- case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR:
- data->flags.crt = 1;
- break;
- case ACPI_VIDEO_DISPLAY_LEGACY_PANEL:
- data->flags.lcd = 1;
- break;
- case ACPI_VIDEO_DISPLAY_LEGACY_TV:
- data->flags.tvout = 1;
- break;
- default:
- data->flags.unknown = 1;
+ case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR:
+ data->flags.crt = 1;
+ break;
+ case ACPI_VIDEO_DISPLAY_LEGACY_PANEL:
+ data->flags.lcd = 1;
+ break;
+ case ACPI_VIDEO_DISPLAY_LEGACY_TV:
+ data->flags.tvout = 1;
+ break;
+ default:
+ data->flags.unknown = 1;
}
}
acpi_video_device_bind(video, data);
acpi_video_device_find_cap(data);
- status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
- acpi_video_device_notify, data);
- if (ACPI_FAILURE(status))
- dev_err(&device->dev, "Error installing notify handler\n");
- else
- data->flags.notify = 1;
-
mutex_lock(&video->device_list_lock);
list_add_tail(&data->entry, &video->video_device_list);
mutex_unlock(&video->device_list_lock);
/*
* Arg:
- * video : video bus device
+ * video : video bus device
*
* Return:
- * none
- *
- * Enumerate the video device list of the video bus,
+ * none
+ *
+ * Enumerate the video device list of the video bus,
* bind the ids with the corresponding video devices
* under the video bus.
*/
/*
* Arg:
- * video : video bus device
- * device : video output device under the video
- * bus
+ * video : video bus device
+ * device : video output device under the video
+ * bus
*
* Return:
- * none
- *
+ * none
+ *
* Bind the ids with the corresponding video devices
* under the video bus.
*/
/*
* Arg:
- * video : video bus device
+ * video : video bus device
*
* Return:
- * < 0 : error
- *
+ * < 0 : error
+ *
* Call _DOD to enumerate all devices attached to display adapter
*
*/
video->attached_array = active_list;
video->attached_count = count;
- out:
+out:
kfree(buffer.pointer);
return status;
}
unsigned long long level_current, level_next;
int result = -EINVAL;
- /* no warning message if acpi_backlight=vendor is used */
- if (!acpi_video_backlight_support())
+ /* no warning message if acpi_backlight=vendor or a quirk is used */
+ if (!acpi_video_verify_backlight_support())
return 0;
if (!device->brightness)
return status;
}
-static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
-{
- acpi_status status;
-
- if (!device || !device->video)
- return -ENOENT;
-
- if (device->flags.notify) {
- status = acpi_remove_notify_handler(device->dev->handle,
- ACPI_DEVICE_NOTIFY, acpi_video_device_notify);
- if (ACPI_FAILURE(status))
- dev_err(&device->dev->dev,
- "Can't remove video notify handler\n");
- }
-
- if (device->backlight) {
- backlight_device_unregister(device->backlight);
- device->backlight = NULL;
- }
- if (device->cooling_dev) {
- sysfs_remove_link(&device->dev->dev.kobj,
- "thermal_cooling");
- sysfs_remove_link(&device->cooling_dev->device.kobj,
- "device");
- thermal_cooling_device_unregister(device->cooling_dev);
- device->cooling_dev = NULL;
- }
-
- return 0;
-}
-
-static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
-{
- int status;
- struct acpi_video_device *dev, *next;
-
- mutex_lock(&video->device_list_lock);
-
- list_for_each_entry_safe(dev, next, &video->video_device_list, entry) {
-
- status = acpi_video_bus_put_one_device(dev);
- if (ACPI_FAILURE(status))
- printk(KERN_WARNING PREFIX
- "hhuuhhuu bug in acpi video driver.\n");
-
- if (dev->brightness) {
- kfree(dev->brightness->levels);
- kfree(dev->brightness);
- }
- list_del(&dev->entry);
- kfree(dev);
- }
-
- mutex_unlock(&video->device_list_lock);
-
- return 0;
-}
-
/* acpi_video interface */
/*
static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
{
return acpi_video_bus_DOS(video, 0,
- acpi_video_backlight_quirks() ? 1 : 0);
+ acpi_osi_is_win8() ? 1 : 0);
}
static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
{
return acpi_video_bus_DOS(video, 0,
- acpi_video_backlight_quirks() ? 0 : 1);
+ acpi_osi_is_win8() ? 0 : 1);
}
static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
struct input_dev *input;
int keycode = 0;
- if (!video)
+ if (!video || !video->input)
return;
input = video->input;
switch (event) {
case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch,
* most likely via hotkey. */
- acpi_bus_generate_proc_event(device, event, 0);
keycode = KEY_SWITCHVIDEOMODE;
break;
* connector. */
acpi_video_device_enumerate(video);
acpi_video_device_rebind(video);
- acpi_bus_generate_proc_event(device, event, 0);
keycode = KEY_SWITCHVIDEOMODE;
break;
case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */
- acpi_bus_generate_proc_event(device, event, 0);
keycode = KEY_SWITCHVIDEOMODE;
break;
case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */
- acpi_bus_generate_proc_event(device, event, 0);
keycode = KEY_VIDEO_NEXT;
break;
case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */
- acpi_bus_generate_proc_event(device, event, 0);
keycode = KEY_VIDEO_PREV;
break;
case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */
if (brightness_switch_enabled)
acpi_video_switch_brightness(video_device, event);
- acpi_bus_generate_proc_event(device, event, 0);
keycode = KEY_BRIGHTNESS_CYCLE;
break;
case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */
if (brightness_switch_enabled)
acpi_video_switch_brightness(video_device, event);
- acpi_bus_generate_proc_event(device, event, 0);
keycode = KEY_BRIGHTNESSUP;
break;
case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */
if (brightness_switch_enabled)
acpi_video_switch_brightness(video_device, event);
- acpi_bus_generate_proc_event(device, event, 0);
keycode = KEY_BRIGHTNESSDOWN;
break;
case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightness */
if (brightness_switch_enabled)
acpi_video_switch_brightness(video_device, event);
- acpi_bus_generate_proc_event(device, event, 0);
keycode = KEY_BRIGHTNESS_ZERO;
break;
case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */
if (brightness_switch_enabled)
acpi_video_switch_brightness(video_device, event);
- acpi_bus_generate_proc_event(device, event, 0);
keycode = KEY_DISPLAY_OFF;
break;
default:
return AE_OK;
}
+static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
+{
+ if (acpi_video_verify_backlight_support()) {
+ struct backlight_properties props;
+ struct pci_dev *pdev;
+ acpi_handle acpi_parent;
+ struct device *parent = NULL;
+ int result;
+ static int count;
+ char *name;
+
+ result = acpi_video_init_brightness(device);
+ if (result)
+ return;
+ name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
+ if (!name)
+ return;
+ count++;
+
+ acpi_get_parent(device->dev->handle, &acpi_parent);
+
+ pdev = acpi_get_pci_dev(acpi_parent);
+ if (pdev) {
+ parent = &pdev->dev;
+ pci_dev_put(pdev);
+ }
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_FIRMWARE;
+ props.max_brightness = device->brightness->count - 3;
+ device->backlight = backlight_device_register(name,
+ parent,
+ device,
+ &acpi_backlight_ops,
+ &props);
+ kfree(name);
+ if (IS_ERR(device->backlight))
+ return;
+
+ /*
+ * Save current brightness level in case we have to restore it
+ * before acpi_video_device_lcd_set_level() is called next time.
+ */
+ device->backlight->props.brightness =
+ acpi_video_get_brightness(device->backlight);
+
+ device->cooling_dev = thermal_cooling_device_register("LCD",
+ device->dev, &video_cooling_ops);
+ if (IS_ERR(device->cooling_dev)) {
+ /*
+ * Set cooling_dev to NULL so we don't crash trying to
+ * free it.
+ * Also, why the hell we are returning early and
+ * not attempt to register video output if cooling
+ * device registration failed?
+ * -- dtor
+ */
+ device->cooling_dev = NULL;
+ return;
+ }
+
+ dev_info(&device->dev->dev, "registered as cooling_device%d\n",
+ device->cooling_dev->id);
+ result = sysfs_create_link(&device->dev->dev.kobj,
+ &device->cooling_dev->device.kobj,
+ "thermal_cooling");
+ if (result)
+ printk(KERN_ERR PREFIX "Create sysfs link\n");
+ result = sysfs_create_link(&device->cooling_dev->device.kobj,
+ &device->dev->dev.kobj, "device");
+ if (result)
+ printk(KERN_ERR PREFIX "Create sysfs link\n");
+ }
+}
+
+static int acpi_video_bus_register_backlight(struct acpi_video_bus *video)
+{
+ struct acpi_video_device *dev;
+
+ mutex_lock(&video->device_list_lock);
+ list_for_each_entry(dev, &video->video_device_list, entry)
+ acpi_video_dev_register_backlight(dev);
+ mutex_unlock(&video->device_list_lock);
+
+ video->pm_nb.notifier_call = acpi_video_resume;
+ video->pm_nb.priority = 0;
+ return register_pm_notifier(&video->pm_nb);
+}
+
+static void acpi_video_dev_unregister_backlight(struct acpi_video_device *device)
+{
+ if (device->backlight) {
+ backlight_device_unregister(device->backlight);
+ device->backlight = NULL;
+ }
+ if (device->brightness) {
+ kfree(device->brightness->levels);
+ kfree(device->brightness);
+ device->brightness = NULL;
+ }
+ if (device->cooling_dev) {
+ sysfs_remove_link(&device->dev->dev.kobj, "thermal_cooling");
+ sysfs_remove_link(&device->cooling_dev->device.kobj, "device");
+ thermal_cooling_device_unregister(device->cooling_dev);
+ device->cooling_dev = NULL;
+ }
+}
+
+static int acpi_video_bus_unregister_backlight(struct acpi_video_bus *video)
+{
+ struct acpi_video_device *dev;
+ int error = unregister_pm_notifier(&video->pm_nb);
+
+ mutex_lock(&video->device_list_lock);
+ list_for_each_entry(dev, &video->video_device_list, entry)
+ acpi_video_dev_unregister_backlight(dev);
+ mutex_unlock(&video->device_list_lock);
+
+ return error;
+}
+
+static void acpi_video_dev_add_notify_handler(struct acpi_video_device *device)
+{
+ acpi_status status;
+ struct acpi_device *adev = device->dev;
+
+ status = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
+ acpi_video_device_notify, device);
+ if (ACPI_FAILURE(status))
+ dev_err(&adev->dev, "Error installing notify handler\n");
+ else
+ device->flags.notify = 1;
+}
+
+static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video)
+{
+ struct input_dev *input;
+ struct acpi_video_device *dev;
+ int error;
+
+ video->input = input = input_allocate_device();
+ if (!input) {
+ error = -ENOMEM;
+ goto out;
+ }
+
+ error = acpi_video_bus_start_devices(video);
+ if (error)
+ goto err_free_input;
+
+ 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;
+ input->dev.parent = &video->device->dev;
+ 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);
+
+ error = input_register_device(input);
+ if (error)
+ goto err_stop_dev;
+
+ mutex_lock(&video->device_list_lock);
+ list_for_each_entry(dev, &video->video_device_list, entry)
+ acpi_video_dev_add_notify_handler(dev);
+ mutex_unlock(&video->device_list_lock);
+
+ return 0;
+
+err_stop_dev:
+ acpi_video_bus_stop_devices(video);
+err_free_input:
+ input_free_device(input);
+ video->input = NULL;
+out:
+ return error;
+}
+
+static void acpi_video_dev_remove_notify_handler(struct acpi_video_device *dev)
+{
+ if (dev->flags.notify) {
+ acpi_remove_notify_handler(dev->dev->handle, ACPI_DEVICE_NOTIFY,
+ acpi_video_device_notify);
+ dev->flags.notify = 0;
+ }
+}
+
+static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video)
+{
+ struct acpi_video_device *dev;
+
+ mutex_lock(&video->device_list_lock);
+ list_for_each_entry(dev, &video->video_device_list, entry)
+ acpi_video_dev_remove_notify_handler(dev);
+ mutex_unlock(&video->device_list_lock);
+
+ acpi_video_bus_stop_devices(video);
+ input_unregister_device(video->input);
+ video->input = NULL;
+}
+
+static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
+{
+ struct acpi_video_device *dev, *next;
+
+ mutex_lock(&video->device_list_lock);
+ list_for_each_entry_safe(dev, next, &video->video_device_list, entry) {
+ list_del(&dev->entry);
+ kfree(dev);
+ }
+ mutex_unlock(&video->device_list_lock);
+
+ return 0;
+}
+
static int instance;
static int acpi_video_bus_add(struct acpi_device *device)
{
struct acpi_video_bus *video;
- struct input_dev *input;
int error;
acpi_status status;
if (!strcmp(device->pnp.bus_id, "VID")) {
if (instance)
device->pnp.bus_id[3] = '0' + instance;
- instance ++;
+ instance++;
}
/* a hack to fix the duplicate name "VGA" problem on Pa 3553 */
if (!strcmp(device->pnp.bus_id, "VGA")) {
if (error)
goto err_put_video;
- video->input = input = input_allocate_device();
- if (!input) {
- error = -ENOMEM;
- goto err_put_video;
- }
-
- error = acpi_video_bus_start_devices(video);
- if (error)
- goto err_free_input_dev;
-
- 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;
- input->dev.parent = &device->dev;
- 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);
-
printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n",
ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
video->flags.multihead ? "yes" : "no",
video->flags.rom ? "yes" : "no",
video->flags.post ? "yes" : "no");
+ mutex_lock(&video_list_lock);
+ list_add_tail(&video->entry, &video_bus_head);
+ mutex_unlock(&video_list_lock);
- video->pm_nb.notifier_call = acpi_video_resume;
- video->pm_nb.priority = 0;
- error = register_pm_notifier(&video->pm_nb);
- if (error)
- goto err_stop_video;
-
- error = input_register_device(input);
- if (error)
- goto err_unregister_pm_notifier;
+ acpi_video_bus_register_backlight(video);
+ acpi_video_bus_add_notify_handler(video);
return 0;
- err_unregister_pm_notifier:
- unregister_pm_notifier(&video->pm_nb);
- err_stop_video:
- acpi_video_bus_stop_devices(video);
- err_free_input_dev:
- input_free_device(input);
- err_put_video:
+err_put_video:
acpi_video_bus_put_devices(video);
kfree(video->attached_array);
- err_free_video:
+err_free_video:
kfree(video);
device->driver_data = NULL;
video = acpi_driver_data(device);
- unregister_pm_notifier(&video->pm_nb);
-
- acpi_video_bus_stop_devices(video);
+ acpi_video_bus_remove_notify_handler(video);
+ acpi_video_bus_unregister_backlight(video);
acpi_video_bus_put_devices(video);
- input_unregister_device(video->input);
+ mutex_lock(&video_list_lock);
+ list_del(&video->entry);
+ mutex_unlock(&video_list_lock);
+
kfree(video->attached_array);
kfree(video);
return 0;
}
+ mutex_init(&video_list_lock);
+ INIT_LIST_HEAD(&video_bus_head);
+
result = acpi_bus_register_driver(&acpi_video_bus);
if (result < 0)
return -ENODEV;