Commit 12648810 authored by Len Brown's avatar Len Brown
Browse files

Merge branch 'video' into release



Conflicts:
	drivers/acpi/video.c
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parents 7329e935 03ae61dd
......@@ -37,6 +37,8 @@
#include <linux/thermal.h>
#include <linux/video_output.h>
#include <linux/sort.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <asm/uaccess.h>
#include <acpi/acpi_bus.h>
......@@ -162,16 +164,26 @@ struct acpi_video_device_cap {
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 _DCS:1; /*Return status of output device */
u8 _DGS:1; /*Query graphics state */
u8 _DSS:1; /*Device state set */
};
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 _BQC_use_index:1; /* _BQC returns an index value */
};
struct acpi_video_device_brightness {
int curr;
int count;
int *levels;
struct acpi_video_brightness_flags flags;
};
struct acpi_video_device {
......@@ -294,7 +306,7 @@ static int acpi_video_device_lcd_get_level_current(
unsigned long long *level);
static int acpi_video_get_next_level(struct acpi_video_device *device,
u32 level_current, u32 event);
static void acpi_video_switch_brightness(struct acpi_video_device *device,
static int acpi_video_switch_brightness(struct acpi_video_device *device,
int event);
static int acpi_video_device_get_state(struct acpi_video_device *device,
unsigned long long *state);
......@@ -308,7 +320,9 @@ static int acpi_video_get_brightness(struct backlight_device *bd)
int i;
struct acpi_video_device *vd =
(struct acpi_video_device *)bl_get_data(bd);
acpi_video_device_lcd_get_level_current(vd, &cur_level);
if (acpi_video_device_lcd_get_level_current(vd, &cur_level))
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
......@@ -320,12 +334,12 @@ static int acpi_video_get_brightness(struct backlight_device *bd)
static int acpi_video_set_brightness(struct backlight_device *bd)
{
int request_level = bd->props.brightness+2;
int request_level = bd->props.brightness + 2;
struct acpi_video_device *vd =
(struct acpi_video_device *)bl_get_data(bd);
acpi_video_device_lcd_set_level(vd,
vd->brightness->levels[request_level]);
return 0;
return acpi_video_device_lcd_set_level(vd,
vd->brightness->levels[request_level]);
}
static struct backlight_ops acpi_backlight_ops = {
......@@ -376,7 +390,8 @@ static int video_get_cur_state(struct thermal_cooling_device *cdev, unsigned
unsigned long long level;
int offset;
acpi_video_device_lcd_get_level_current(video, &level);
if (acpi_video_device_lcd_get_level_current(video, &level))
return -EINVAL;
for (offset = 2; offset < video->brightness->count; offset++)
if (level == video->brightness->levels[offset]) {
*state = video->brightness->count - offset - 1;
......@@ -483,34 +498,68 @@ acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
static int
acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
{
int status = AE_OK;
int status;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
int state;
arg0.integer.value = level;
if (device->cap._BCM)
status = acpi_evaluate_object(device->dev->handle, "_BCM",
&args, NULL);
status = acpi_evaluate_object(device->dev->handle, "_BCM",
&args, NULL);
if (ACPI_FAILURE(status)) {
ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));
return -EIO;
}
device->brightness->curr = level;
for (state = 2; state < device->brightness->count; state++)
if (level == device->brightness->levels[state])
device->backlight->props.brightness = state - 2;
if (level == device->brightness->levels[state]) {
if (device->backlight)
device->backlight->props.brightness = state - 2;
return 0;
}
return status;
ACPI_ERROR((AE_INFO, "Current brightness invalid"));
return -EINVAL;
}
static int
acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
unsigned long long *level)
{
if (device->cap._BQC)
return acpi_evaluate_integer(device->dev->handle, "_BQC", NULL,
level);
acpi_status status = AE_OK;
if (device->cap._BQC || device->cap._BCQ) {
char *buf = device->cap._BQC ? "_BQC" : "_BCQ";
status = acpi_evaluate_integer(device->dev->handle, buf,
NULL, level);
if (ACPI_SUCCESS(status)) {
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];
}
device->brightness->curr = *level;
return 0;
} else {
/* Fixme:
* should we return an error or ignore this failure?
* dev->brightness->curr is a cached value which stores
* the correct current backlight level in most cases.
* ACPI video backlight still works w/ buggy _BQC.
* http://bugzilla.kernel.org/show_bug.cgi?id=12233
*/
ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf));
device->cap._BQC = device->cap._BCQ = 0;
}
}
*level = device->brightness->curr;
return AE_OK;
return 0;
}
static int
......@@ -659,9 +708,11 @@ static int
acpi_video_init_brightness(struct acpi_video_device *device)
{
union acpi_object *obj = NULL;
int i, max_level = 0, count = 0;
int i, max_level = 0, count = 0, level_ac_battery = 0;
unsigned long long level, level_old;
union acpi_object *o;
struct acpi_video_device_brightness *br = NULL;
int result = -EINVAL;
if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available "
......@@ -675,13 +726,16 @@ acpi_video_init_brightness(struct acpi_video_device *device)
br = kzalloc(sizeof(*br), GFP_KERNEL);
if (!br) {
printk(KERN_ERR "can't allocate memory\n");
result = -ENOMEM;
goto out;
}
br->levels = kmalloc(obj->package.count * sizeof *(br->levels),
br->levels = kmalloc((obj->package.count + 2) * sizeof *(br->levels),
GFP_KERNEL);
if (!br->levels)
if (!br->levels) {
result = -ENOMEM;
goto out_free;
}
for (i = 0; i < obj->package.count; i++) {
o = (union acpi_object *)&obj->package.elements[i];
......@@ -696,18 +750,86 @@ acpi_video_init_brightness(struct acpi_video_device *device)
count++;
}
/* don't sort the first two brightness levels */
sort(&br->levels[2], count - 2, sizeof(br->levels[2]),
acpi_video_cmp_level, NULL);
if (count < 2)
goto out_free_levels;
/*
* 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"));
/* 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"));
br->count = count;
device->brightness = br;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "found %d brightness levels\n", count));
/* 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;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"found %d brightness levels\n", count - 2));
kfree(obj);
return max_level;
return result;
out_free_levels:
kfree(br->levels);
......@@ -716,7 +838,7 @@ out_free:
out:
device->brightness = NULL;
kfree(obj);
return 0;
return result;
}
/*
......@@ -733,7 +855,6 @@ out:
static void acpi_video_device_find_cap(struct acpi_video_device *device)
{
acpi_handle h_dummy1;
u32 max_level = 0;
memset(&device->cap, 0, sizeof(device->cap));
......@@ -749,6 +870,12 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
}
if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
device->cap._BQC = 1;
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;
}
if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
device->cap._DDC = 1;
}
......@@ -762,13 +889,14 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
device->cap._DSS = 1;
}
if (acpi_video_backlight_support())
max_level = acpi_video_init_brightness(device);
if (device->cap._BCL && device->cap._BCM && max_level > 0) {
if (acpi_video_backlight_support()) {
int result;
static int count = 0;
char *name;
result = acpi_video_init_brightness(device);
if (result)
return;
name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
if (!name)
return;
......@@ -777,18 +905,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
device->backlight = backlight_device_register(name,
NULL, device, &acpi_backlight_ops);
device->backlight->props.max_brightness = device->brightness->count-3;
/*
* If there exists the _BQC object, the _BQC object will be
* called to get the current backlight brightness. Otherwise
* the brightness will be set to the maximum.
*/
if (device->cap._BQC)
device->backlight->props.brightness =
acpi_video_get_brightness(device->backlight);
else
device->backlight->props.brightness =
device->backlight->props.max_brightness;
backlight_update_status(device->backlight);
kfree(name);
device->cdev = thermal_cooling_device_register("LCD",
......@@ -1065,13 +1181,12 @@ acpi_video_device_write_brightness(struct file *file,
/* validate through the list of available levels */
for (i = 2; i < dev->brightness->count; i++)
if (level == dev->brightness->levels[i]) {
if (ACPI_SUCCESS
(acpi_video_device_lcd_set_level(dev, level)))
dev->brightness->curr = level;
if (!acpi_video_device_lcd_set_level(dev, level))
return count;
break;
}
return count;
return -EINVAL;
}
static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
......@@ -1753,15 +1868,29 @@ acpi_video_get_next_level(struct acpi_video_device *device,
}
}
static void
static int
acpi_video_switch_brightness(struct acpi_video_device *device, int event)
{
unsigned long long level_current, level_next;
int result = -EINVAL;
if (!device->brightness)
return;
acpi_video_device_lcd_get_level_current(device, &level_current);
goto out;
result = acpi_video_device_lcd_get_level_current(device,
&level_current);
if (result)
goto out;
level_next = acpi_video_get_next_level(device, level_current, event);
acpi_video_device_lcd_set_level(device, level_next);
result = acpi_video_device_lcd_set_level(device, level_next);
out:
if (result)
printk(KERN_ERR PREFIX "Failed to switch the brightness\n");
return result;
}
static int
......@@ -2128,7 +2257,27 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type)
return 0;
}
static int __init acpi_video_init(void)
static int __init intel_opregion_present(void)
{
#if defined(CONFIG_DRM_I915) || defined(CONFIG_DRM_I915_MODULE)
struct pci_dev *dev = NULL;
u32 address;
for_each_pci_dev(dev) {
if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
continue;
if (dev->vendor != PCI_VENDOR_ID_INTEL)
continue;
pci_read_config_dword(dev, 0xfc, &address);
if (!address)
continue;
return 1;
}
#endif
return 0;
}
int acpi_video_register(void)
{
int result = 0;
......@@ -2145,6 +2294,22 @@ static int __init acpi_video_init(void)
return 0;
}
EXPORT_SYMBOL(acpi_video_register);
/*
* This is kind of nasty. Hardware using Intel chipsets may require
* the video opregion code to be run first in order to initialise
* state before any ACPI video calls are made. To handle this we defer
* registration of the video class until the opregion code has run.
*/
static int __init acpi_video_init(void)
{
if (intel_opregion_present())
return 0;
return acpi_video_register();
}
static void __exit acpi_video_exit(void)
{
......
......@@ -55,6 +55,9 @@ acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context,
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight "
"support\n"));
*cap |= ACPI_VIDEO_BACKLIGHT;
if (ACPI_FAILURE(acpi_get_handle(handle, "_BQC", &h_dummy)))
printk(KERN_WARNING FW_BUG PREFIX "ACPI brightness "
"control misses _BQC function\n");
/* We have backlight support, no need to scan further */
return AE_CTRL_TERMINATE;
}
......
......@@ -1144,8 +1144,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (!IS_I945G(dev) && !IS_I945GM(dev))
pci_enable_msi(dev->pdev);
intel_opregion_init(dev);
spin_lock_init(&dev_priv->user_irq_lock);
dev_priv->user_irq_refcount = 0;
......@@ -1164,6 +1162,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
}
}
/* Must be done after probing outputs */
intel_opregion_init(dev, 0);
return 0;
out_iomapfree:
......
......@@ -99,7 +99,7 @@ static int i915_resume(struct drm_device *dev)
i915_restore_state(dev);
intel_opregion_init(dev);
intel_opregion_init(dev, 1);
/* KMS EnterVT equivalent */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
......
......@@ -659,12 +659,12 @@ extern int i915_restore_state(struct drm_device *dev);
#ifdef CONFIG_ACPI
/* i915_opregion.c */
extern int intel_opregion_init(struct drm_device *dev);
extern int intel_opregion_init(struct drm_device *dev, int resume);
extern void intel_opregion_free(struct drm_device *dev);
extern void opregion_asle_intr(struct drm_device *dev);
extern void opregion_enable_asle(struct drm_device *dev);
#else
static inline int intel_opregion_init(struct drm_device *dev) { return 0; }
static inline int intel_opregion_init(struct drm_device *dev, int resume) { return 0; }
static inline void intel_opregion_free(struct drm_device *dev) { return; }
static inline void opregion_asle_intr(struct drm_device *dev) { return; }
static inline void opregion_enable_asle(struct drm_device *dev) { return; }
......
......@@ -26,6 +26,7 @@
*/
#include <linux/acpi.h>
#include <acpi/video.h>
#include "drmP.h"
#include "i915_drm.h"
......@@ -136,6 +137,12 @@ struct opregion_asle {
#define ASLE_CBLV_VALID (1<<31)
#define ACPI_OTHER_OUTPUT (0<<8)
#define ACPI_VGA_OUTPUT (1<<8)
#define ACPI_TV_OUTPUT (2<<8)
#define ACPI_DIGITAL_OUTPUT (3<<8)
#define ACPI_LVDS_OUTPUT (4<<8)
static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
{
struct drm_i915_private *dev_priv = dev->dev_private;
......@@ -282,7 +289,58 @@ static struct notifier_block intel_opregion_notifier = {
.notifier_call = intel_opregion_video_event,
};
int intel_opregion_init(struct drm_device *dev)
/*
* Initialise the DIDL field in opregion. This passes a list of devices to
* the firmware. Values are defined by section B.4.2 of the ACPI specification
* (version 3)
*/
static void intel_didl_outputs(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
struct drm_connector *connector;
int i = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
int output_type = ACPI_OTHER_OUTPUT;
if (i >= 8) {
dev_printk (KERN_ERR, &dev->pdev->dev,
"More than 8 outputs detected\n");
return;
}
switch (connector->connector_type) {
case DRM_MODE_CONNECTOR_VGA:
case DRM_MODE_CONNECTOR_DVIA:
output_type = ACPI_VGA_OUTPUT;
break;
case DRM_MODE_CONNECTOR_Composite:
case DRM_MODE_CONNECTOR_SVIDEO:
case DRM_MODE_CONNECTOR_Component:
case DRM_MODE_CONNECTOR_9PinDIN:
output_type = ACPI_TV_OUTPUT;
break;
case DRM_MODE_CONNECTOR_DVII:
case DRM_MODE_CONNECTOR_DVID:
case DRM_MODE_CONNECTOR_DisplayPort:
case DRM_MODE_CONNECTOR_HDMIA:
case DRM_MODE_CONNECTOR_HDMIB:
output_type = ACPI_DIGITAL_OUTPUT;
break;
case DRM_MODE_CONNECTOR_LVDS:
output_type = ACPI_LVDS_OUTPUT;
break;
}
opregion->acpi->didl[i] |= (1<<31) | output_type | i;
i++;
}
/* If fewer than 8 outputs, the list must be null terminated */
if (i < 8)
opregion->acpi->didl[i] = 0;
}
int intel_opregion_init(struct drm_device *dev, int resume)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
......@@ -312,6 +370,11 @@ int intel_opregion_init(struct drm_device *dev)
if (mboxes & MBOX_ACPI) {
DRM_DEBUG("Public ACPI methods supported\n");
opregion->acpi = base + OPREGION_ACPI_OFFSET;
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
intel_didl_outputs(dev);
if (!resume)
acpi_video_register();
}
} else {
DRM_DEBUG("Public ACPI methods not supported\n");
err = -ENOTSUPP;
......
#ifndef __ACPI_VIDEO_H
#define __ACPI_VIDEO_H
#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
extern int acpi_video_register(void);
#else
static inline int acpi_video_register(void) { return 0; }
#endif
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment