Commit 44834a67 authored by Chris Wilson's avatar Chris Wilson

drm/i915: Use the VBT from OpRegion when available (v3)

It is recommended that we use the Video BIOS tables that were copied
into the OpRegion during POST when initialising the driver. This saves
us from having to furtle around inside the ROM ourselves and possibly
allows the vBIOS to adjust the tables prior to initialisation.

On some systems, such as the Samsung N210, there is no accessible VBIOS
and the only means of finding the VBT is through the OpRegion.

v2: Rearrange the code so that ASLE is enabled along with ACPI
v3: Enable OpRegion parsing even without ACPI
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: Matthew Garrett <mjg@redhat.com>
parent 3b617967
...@@ -26,13 +26,13 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \ ...@@ -26,13 +26,13 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
intel_dvo.o \ intel_dvo.o \
intel_ringbuffer.o \ intel_ringbuffer.o \
intel_overlay.o \ intel_overlay.o \
intel_opregion.o \
dvo_ch7xxx.o \ dvo_ch7xxx.o \
dvo_ch7017.o \ dvo_ch7017.o \
dvo_ivch.o \ dvo_ivch.o \
dvo_tfp410.o \ dvo_tfp410.o \
dvo_sil164.o dvo_sil164.o
i915-$(CONFIG_ACPI) += intel_opregion.o
i915-$(CONFIG_COMPAT) += i915_ioc32.o i915-$(CONFIG_COMPAT) += i915_ioc32.o
obj-$(CONFIG_DRM_I915) += i915.o obj-$(CONFIG_DRM_I915) += i915.o
......
...@@ -718,6 +718,26 @@ static int i915_gfxec(struct seq_file *m, void *unused) ...@@ -718,6 +718,26 @@ static int i915_gfxec(struct seq_file *m, void *unused)
return 0; return 0;
} }
static int i915_opregion(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
int ret;
ret = mutex_lock_interruptible(&dev->struct_mutex);
if (ret)
return ret;
if (opregion->header)
seq_write(m, opregion->header, OPREGION_SIZE);
mutex_unlock(&dev->struct_mutex);
return 0;
}
static int static int
i915_wedged_open(struct inode *inode, i915_wedged_open(struct inode *inode,
struct file *filp) struct file *filp)
...@@ -845,6 +865,7 @@ static struct drm_info_list i915_debugfs_list[] = { ...@@ -845,6 +865,7 @@ static struct drm_info_list i915_debugfs_list[] = {
{"i915_gfxec", i915_gfxec, 0}, {"i915_gfxec", i915_gfxec, 0},
{"i915_fbc_status", i915_fbc_status, 0}, {"i915_fbc_status", i915_fbc_status, 0},
{"i915_sr_status", i915_sr_status, 0}, {"i915_sr_status", i915_sr_status, 0},
{"i915_opregion", i915_opregion, 0},
}; };
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <linux/pnp.h> #include <linux/pnp.h>
#include <linux/vga_switcheroo.h> #include <linux/vga_switcheroo.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <acpi/video.h>
extern int intel_max_stolen; /* from AGP driver */ extern int intel_max_stolen; /* from AGP driver */
...@@ -2166,6 +2167,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ...@@ -2166,6 +2167,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
/* Try to make sure MCHBAR is enabled before poking at it */ /* Try to make sure MCHBAR is enabled before poking at it */
intel_setup_mchbar(dev); intel_setup_mchbar(dev);
intel_opregion_setup(dev);
i915_gem_load(dev); i915_gem_load(dev);
...@@ -2221,7 +2223,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ...@@ -2221,7 +2223,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
} }
/* Must be done after probing outputs */ /* Must be done after probing outputs */
intel_opregion_init(dev, 0); intel_opregion_init(dev);
acpi_video_register();
setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed,
(unsigned long) dev); (unsigned long) dev);
...@@ -2271,6 +2274,8 @@ int i915_driver_unload(struct drm_device *dev) ...@@ -2271,6 +2274,8 @@ int i915_driver_unload(struct drm_device *dev)
dev_priv->mm.gtt_mtrr = -1; dev_priv->mm.gtt_mtrr = -1;
} }
acpi_video_unregister();
if (drm_core_check_feature(dev, DRIVER_MODESET)) { if (drm_core_check_feature(dev, DRIVER_MODESET)) {
intel_modeset_cleanup(dev); intel_modeset_cleanup(dev);
...@@ -2299,7 +2304,7 @@ int i915_driver_unload(struct drm_device *dev) ...@@ -2299,7 +2304,7 @@ int i915_driver_unload(struct drm_device *dev)
if (dev_priv->regs != NULL) if (dev_priv->regs != NULL)
iounmap(dev_priv->regs); iounmap(dev_priv->regs);
intel_opregion_free(dev, 0); intel_opregion_fini(dev);
if (drm_core_check_feature(dev, DRIVER_MODESET)) { if (drm_core_check_feature(dev, DRIVER_MODESET)) {
/* Flush any outstanding unpin_work. */ /* Flush any outstanding unpin_work. */
......
...@@ -236,7 +236,7 @@ static int i915_drm_freeze(struct drm_device *dev) ...@@ -236,7 +236,7 @@ static int i915_drm_freeze(struct drm_device *dev)
i915_save_state(dev); i915_save_state(dev);
intel_opregion_free(dev, 1); intel_opregion_fini(dev);
/* Modeset on resume, not lid events */ /* Modeset on resume, not lid events */
dev_priv->modeset_on_lid = 0; dev_priv->modeset_on_lid = 0;
...@@ -276,8 +276,7 @@ static int i915_drm_thaw(struct drm_device *dev) ...@@ -276,8 +276,7 @@ static int i915_drm_thaw(struct drm_device *dev)
int error = 0; int error = 0;
i915_restore_state(dev); i915_restore_state(dev);
intel_opregion_setup(dev);
intel_opregion_init(dev, 1);
/* KMS EnterVT equivalent */ /* KMS EnterVT equivalent */
if (drm_core_check_feature(dev, DRIVER_MODESET)) { if (drm_core_check_feature(dev, DRIVER_MODESET)) {
...@@ -293,6 +292,8 @@ static int i915_drm_thaw(struct drm_device *dev) ...@@ -293,6 +292,8 @@ static int i915_drm_thaw(struct drm_device *dev)
drm_helper_resume_force_mode(dev); drm_helper_resume_force_mode(dev);
} }
intel_opregion_init(dev);
dev_priv->modeset_on_lid = 0; dev_priv->modeset_on_lid = 0;
return error; return error;
......
...@@ -110,8 +110,9 @@ struct intel_opregion { ...@@ -110,8 +110,9 @@ struct intel_opregion {
struct opregion_acpi *acpi; struct opregion_acpi *acpi;
struct opregion_swsci *swsci; struct opregion_swsci *swsci;
struct opregion_asle *asle; struct opregion_asle *asle;
int enabled; void *vbt;
}; };
#define OPREGION_SIZE (8*1024)
struct intel_overlay; struct intel_overlay;
struct intel_overlay_error_state; struct intel_overlay_error_state;
...@@ -1053,16 +1054,17 @@ extern int i915_restore_state(struct drm_device *dev); ...@@ -1053,16 +1054,17 @@ extern int i915_restore_state(struct drm_device *dev);
extern int i915_save_state(struct drm_device *dev); extern int i915_save_state(struct drm_device *dev);
extern int i915_restore_state(struct drm_device *dev); extern int i915_restore_state(struct drm_device *dev);
#ifdef CONFIG_ACPI
/* intel_opregion.c */ /* intel_opregion.c */
extern int intel_opregion_init(struct drm_device *dev, int resume); extern int intel_opregion_setup(struct drm_device *dev);
extern void intel_opregion_free(struct drm_device *dev, int suspend); #ifdef CONFIG_ACPI
extern void intel_opregion_init(struct drm_device *dev);
extern void intel_opregion_fini(struct drm_device *dev);
extern void intel_opregion_asle_intr(struct drm_device *dev); extern void intel_opregion_asle_intr(struct drm_device *dev);
extern void intel_opregion_gse_intr(struct drm_device *dev); extern void intel_opregion_gse_intr(struct drm_device *dev);
extern void intel_opregion_enable_asle(struct drm_device *dev); extern void intel_opregion_enable_asle(struct drm_device *dev);
#else #else
static inline int intel_opregion_init(struct drm_device *dev, int resume) { return 0; } static inline void intel_opregion_init(struct drm_device *dev) { return; }
static inline void intel_opregion_free(struct drm_device *dev, int suspend) { return; } static inline void intel_opregion_fini(struct drm_device *dev) { return; }
static inline void intel_opregion_asle_intr(struct drm_device *dev) { return; } static inline void intel_opregion_asle_intr(struct drm_device *dev) { return; }
static inline void intel_opregion_gse_intr(struct drm_device *dev) { return; } static inline void intel_opregion_gse_intr(struct drm_device *dev) { return; }
static inline void intel_opregion_enable_asle(struct drm_device *dev) { return; } static inline void intel_opregion_enable_asle(struct drm_device *dev) { return; }
......
...@@ -317,7 +317,7 @@ parse_general_definitions(struct drm_i915_private *dev_priv, ...@@ -317,7 +317,7 @@ parse_general_definitions(struct drm_i915_private *dev_priv,
static void static void
parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
struct bdb_header *bdb) struct bdb_header *bdb)
{ {
struct sdvo_device_mapping *p_mapping; struct sdvo_device_mapping *p_mapping;
struct bdb_general_definitions *p_defs; struct bdb_general_definitions *p_defs;
...@@ -327,7 +327,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, ...@@ -327,7 +327,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
if (!p_defs) { if (!p_defs) {
DRM_DEBUG_KMS("No general definition block is found\n"); DRM_DEBUG_KMS("No general definition block is found, unable to construct sdvo mapping.\n");
return; return;
} }
/* judge whether the size of child device meets the requirements. /* judge whether the size of child device meets the requirements.
...@@ -460,7 +460,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv, ...@@ -460,7 +460,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
if (!p_defs) { if (!p_defs) {
DRM_DEBUG_KMS("No general definition block is found\n"); DRM_DEBUG_KMS("No general definition block is found, no devices defined.\n");
return; return;
} }
/* judge whether the size of child device meets the requirements. /* judge whether the size of child device meets the requirements.
...@@ -513,6 +513,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv, ...@@ -513,6 +513,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
} }
return; return;
} }
/** /**
* intel_init_bios - initialize VBIOS settings & find VBT * intel_init_bios - initialize VBIOS settings & find VBT
* @dev: DRM device * @dev: DRM device
...@@ -520,11 +521,6 @@ parse_device_mapping(struct drm_i915_private *dev_priv, ...@@ -520,11 +521,6 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
* Loads the Video BIOS and checks that the VBT exists. Sets scratch registers * Loads the Video BIOS and checks that the VBT exists. Sets scratch registers
* to appropriate values. * to appropriate values.
* *
* VBT existence is a sanity check that is relied on by other i830_bios.c code.
* Note that it would be better to use a BIOS call to get the VBT, as BIOSes may
* feed an updated VBT back through that, compared to what we'll fetch using
* this method of groping around in the BIOS data.
*
* Returns 0 on success, nonzero on failure. * Returns 0 on success, nonzero on failure.
*/ */
bool bool
...@@ -532,31 +528,45 @@ intel_init_bios(struct drm_device *dev) ...@@ -532,31 +528,45 @@ intel_init_bios(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct pci_dev *pdev = dev->pdev; struct pci_dev *pdev = dev->pdev;
struct vbt_header *vbt = NULL; struct bdb_header *bdb = NULL;
struct bdb_header *bdb; u8 __iomem *bios = NULL;
u8 __iomem *bios;
size_t size; /* XXX Should this validation be moved to intel_opregion.c? */
int i; if (dev_priv->opregion.vbt) {
struct vbt_header *vbt = dev_priv->opregion.vbt;
bios = pci_map_rom(pdev, &size); if (memcmp(vbt->signature, "$VBT", 4) == 0) {
if (!bios) DRM_DEBUG_DRIVER("Using VBT from OpRegion: %20s\n",
return -1; vbt->signature);
bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset);
/* Scour memory looking for the VBT signature */ } else
for (i = 0; i + 4 < size; i++) { dev_priv->opregion.vbt = NULL;
if (!memcmp(bios + i, "$VBT", 4)) {
vbt = (struct vbt_header *)(bios + i);
break;
}
} }
if (!vbt) { if (bdb == NULL) {
DRM_ERROR("VBT signature missing\n"); struct vbt_header *vbt = NULL;
pci_unmap_rom(pdev, bios); size_t size;
return -1; int i;
}
bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset); bios = pci_map_rom(pdev, &size);
if (!bios)
return -1;
/* Scour memory looking for the VBT signature */
for (i = 0; i + 4 < size; i++) {
if (!memcmp(bios + i, "$VBT", 4)) {
vbt = (struct vbt_header *)(bios + i);
break;
}
}
if (!vbt) {
DRM_ERROR("VBT signature missing\n");
pci_unmap_rom(pdev, bios);
return -1;
}
bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
}
/* Grab useful general definitions */ /* Grab useful general definitions */
parse_general_features(dev_priv, bdb); parse_general_features(dev_priv, bdb);
...@@ -568,7 +578,8 @@ intel_init_bios(struct drm_device *dev) ...@@ -568,7 +578,8 @@ intel_init_bios(struct drm_device *dev)
parse_driver_features(dev_priv, bdb); parse_driver_features(dev_priv, bdb);
parse_edp(dev_priv, bdb); parse_edp(dev_priv, bdb);
pci_unmap_rom(pdev, bios); if (bios)
pci_unmap_rom(pdev, bios);
return 0; return 0;
} }
...@@ -36,12 +36,11 @@ ...@@ -36,12 +36,11 @@
#define PCI_LBPC 0xf4 #define PCI_LBPC 0xf4
#define PCI_ASLS 0xfc #define PCI_ASLS 0xfc
#define OPREGION_SZ (8*1024)
#define OPREGION_HEADER_OFFSET 0 #define OPREGION_HEADER_OFFSET 0
#define OPREGION_ACPI_OFFSET 0x100 #define OPREGION_ACPI_OFFSET 0x100
#define OPREGION_SWSCI_OFFSET 0x200 #define OPREGION_SWSCI_OFFSET 0x200
#define OPREGION_ASLE_OFFSET 0x300 #define OPREGION_ASLE_OFFSET 0x300
#define OPREGION_VBT_OFFSET 0x1000 #define OPREGION_VBT_OFFSET 0x400
#define OPREGION_SIGNATURE "IntelGraphicsMem" #define OPREGION_SIGNATURE "IntelGraphicsMem"
#define MBOX_ACPI (1<<0) #define MBOX_ACPI (1<<0)
...@@ -143,6 +142,7 @@ struct opregion_asle { ...@@ -143,6 +142,7 @@ struct opregion_asle {
#define ACPI_DIGITAL_OUTPUT (3<<8) #define ACPI_DIGITAL_OUTPUT (3<<8)
#define ACPI_LVDS_OUTPUT (4<<8) #define ACPI_LVDS_OUTPUT (4<<8)
#ifdef CONFIG_ACPI
static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
...@@ -465,7 +465,58 @@ static void intel_didl_outputs(struct drm_device *dev) ...@@ -465,7 +465,58 @@ static void intel_didl_outputs(struct drm_device *dev)
goto end; goto end;
} }
int intel_opregion_init(struct drm_device *dev, int resume) void intel_opregion_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
if (!opregion->header)
return;
if (opregion->acpi) {
if (drm_core_check_feature(dev, DRIVER_MODESET))
intel_didl_outputs(dev);
/* Notify BIOS we are ready to handle ACPI video ext notifs.
* Right now, all the events are handled by the ACPI video module.
* We don't actually need to do anything with them. */
opregion->acpi->csts = 0;
opregion->acpi->drdy = 1;
system_opregion = opregion;
register_acpi_notifier(&intel_opregion_notifier);
}
if (opregion->asle)
intel_opregion_enable_asle(dev);
}
void intel_opregion_fini(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
if (!opregion->header)
return;
if (opregion->acpi) {
opregion->acpi->drdy = 0;
system_opregion = NULL;
unregister_acpi_notifier(&intel_opregion_notifier);
}
/* just clear all opregion memory pointers now */
iounmap(opregion->header);
opregion->header = NULL;
opregion->acpi = NULL;
opregion->swsci = NULL;
opregion->asle = NULL;
opregion->vbt = NULL;
}
#endif
int intel_opregion_setup(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion; struct intel_opregion *opregion = &dev_priv->opregion;
...@@ -480,29 +531,23 @@ int intel_opregion_init(struct drm_device *dev, int resume) ...@@ -480,29 +531,23 @@ int intel_opregion_init(struct drm_device *dev, int resume)
return -ENOTSUPP; return -ENOTSUPP;
} }
base = ioremap(asls, OPREGION_SZ); base = ioremap(asls, OPREGION_SIZE);
if (!base) if (!base)
return -ENOMEM; return -ENOMEM;
opregion->header = base; if (memcmp(base, OPREGION_SIGNATURE, 16)) {
if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) {
DRM_DEBUG_DRIVER("opregion signature mismatch\n"); DRM_DEBUG_DRIVER("opregion signature mismatch\n");
err = -EINVAL; err = -EINVAL;
goto err_out; goto err_out;
} }
opregion->header = base;
opregion->vbt = base + OPREGION_VBT_OFFSET;
mboxes = opregion->header->mboxes; mboxes = opregion->header->mboxes;
if (mboxes & MBOX_ACPI) { if (mboxes & MBOX_ACPI) {
DRM_DEBUG_DRIVER("Public ACPI methods supported\n"); DRM_DEBUG_DRIVER("Public ACPI methods supported\n");
opregion->acpi = base + OPREGION_ACPI_OFFSET; opregion->acpi = base + OPREGION_ACPI_OFFSET;
if (drm_core_check_feature(dev, DRIVER_MODESET))
intel_didl_outputs(dev);
} else {
DRM_DEBUG_DRIVER("Public ACPI methods not supported\n");
err = -ENOTSUPP;
goto err_out;
} }
opregion->enabled = 1;
if (mboxes & MBOX_SWSCI) { if (mboxes & MBOX_SWSCI) {
DRM_DEBUG_DRIVER("SWSCI supported\n"); DRM_DEBUG_DRIVER("SWSCI supported\n");
...@@ -511,53 +556,11 @@ int intel_opregion_init(struct drm_device *dev, int resume) ...@@ -511,53 +556,11 @@ int intel_opregion_init(struct drm_device *dev, int resume)
if (mboxes & MBOX_ASLE) { if (mboxes & MBOX_ASLE) {
DRM_DEBUG_DRIVER("ASLE supported\n"); DRM_DEBUG_DRIVER("ASLE supported\n");
opregion->asle = base + OPREGION_ASLE_OFFSET; opregion->asle = base + OPREGION_ASLE_OFFSET;
intel_opregion_enable_asle(dev);
} }
if (!resume)
acpi_video_register();
/* Notify BIOS we are ready to handle ACPI video ext notifs.
* Right now, all the events are handled by the ACPI video module.
* We don't actually need to do anything with them. */
opregion->acpi->csts = 0;
opregion->acpi->drdy = 1;
system_opregion = opregion;
register_acpi_notifier(&intel_opregion_notifier);
return 0; return 0;
err_out: err_out:
iounmap(opregion->header); iounmap(opregion->header);
opregion->header = NULL;
acpi_video_register();
return err; return err;
} }
void intel_opregion_free(struct drm_device *dev, int suspend)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
if (!opregion->enabled)
return;
if (!suspend)
acpi_video_unregister();
opregion->acpi->drdy = 0;
system_opregion = NULL;
unregister_acpi_notifier(&intel_opregion_notifier);
/* just clear all opregion memory pointers now */
iounmap(opregion->header);
opregion->header = NULL;
opregion->acpi = NULL;
opregion->swsci = NULL;
opregion->asle = NULL;
opregion->enabled = 0;
}
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