Skip to content

Commit

Permalink
drm/i915: Use the VBT from OpRegion when available (v3)
Browse files Browse the repository at this point in the history
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: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Matthew Garrett <mjg@redhat.com>
  • Loading branch information
Chris Wilson committed Sep 8, 2010
1 parent 3b61796 commit 44834a6
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 98 deletions.
2 changes: 1 addition & 1 deletion drivers/gpu/drm/i915/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
intel_dvo.o \
intel_ringbuffer.o \
intel_overlay.o \
intel_opregion.o \
dvo_ch7xxx.o \
dvo_ch7017.o \
dvo_ivch.o \
dvo_tfp410.o \
dvo_sil164.o

i915-$(CONFIG_ACPI) += intel_opregion.o
i915-$(CONFIG_COMPAT) += i915_ioc32.o

obj-$(CONFIG_DRM_I915) += i915.o
Expand Down
21 changes: 21 additions & 0 deletions drivers/gpu/drm/i915/i915_debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,26 @@ static int i915_gfxec(struct seq_file *m, void *unused)
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
i915_wedged_open(struct inode *inode,
struct file *filp)
Expand Down Expand Up @@ -845,6 +865,7 @@ static struct drm_info_list i915_debugfs_list[] = {
{"i915_gfxec", i915_gfxec, 0},
{"i915_fbc_status", i915_fbc_status, 0},
{"i915_sr_status", i915_sr_status, 0},
{"i915_opregion", i915_opregion, 0},
};
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)

Expand Down
9 changes: 7 additions & 2 deletions drivers/gpu/drm/i915/i915_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <linux/pnp.h>
#include <linux/vga_switcheroo.h>
#include <linux/slab.h>
#include <acpi/video.h>

extern int intel_max_stolen; /* from AGP driver */

Expand Down Expand Up @@ -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 */
intel_setup_mchbar(dev);
intel_opregion_setup(dev);

i915_gem_load(dev);

Expand Down Expand Up @@ -2221,7 +2223,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
}

/* 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,
(unsigned long) dev);
Expand Down Expand Up @@ -2271,6 +2274,8 @@ int i915_driver_unload(struct drm_device *dev)
dev_priv->mm.gtt_mtrr = -1;
}

acpi_video_unregister();

if (drm_core_check_feature(dev, DRIVER_MODESET)) {
intel_modeset_cleanup(dev);

Expand Down Expand Up @@ -2299,7 +2304,7 @@ int i915_driver_unload(struct drm_device *dev)
if (dev_priv->regs != NULL)
iounmap(dev_priv->regs);

intel_opregion_free(dev, 0);
intel_opregion_fini(dev);

if (drm_core_check_feature(dev, DRIVER_MODESET)) {
/* Flush any outstanding unpin_work. */
Expand Down
7 changes: 4 additions & 3 deletions drivers/gpu/drm/i915/i915_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ static int i915_drm_freeze(struct drm_device *dev)

i915_save_state(dev);

intel_opregion_free(dev, 1);
intel_opregion_fini(dev);

/* Modeset on resume, not lid events */
dev_priv->modeset_on_lid = 0;
Expand Down Expand Up @@ -276,8 +276,7 @@ static int i915_drm_thaw(struct drm_device *dev)
int error = 0;

i915_restore_state(dev);

intel_opregion_init(dev, 1);
intel_opregion_setup(dev);

/* KMS EnterVT equivalent */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
Expand All @@ -293,6 +292,8 @@ static int i915_drm_thaw(struct drm_device *dev)
drm_helper_resume_force_mode(dev);
}

intel_opregion_init(dev);

dev_priv->modeset_on_lid = 0;

return error;
Expand Down
14 changes: 8 additions & 6 deletions drivers/gpu/drm/i915/i915_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ struct intel_opregion {
struct opregion_acpi *acpi;
struct opregion_swsci *swsci;
struct opregion_asle *asle;
int enabled;
void *vbt;
};
#define OPREGION_SIZE (8*1024)

struct intel_overlay;
struct intel_overlay_error_state;
Expand Down Expand Up @@ -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_restore_state(struct drm_device *dev);

#ifdef CONFIG_ACPI
/* intel_opregion.c */
extern int intel_opregion_init(struct drm_device *dev, int resume);
extern void intel_opregion_free(struct drm_device *dev, int suspend);
extern int intel_opregion_setup(struct drm_device *dev);
#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_gse_intr(struct drm_device *dev);
extern void intel_opregion_enable_asle(struct drm_device *dev);
#else
static inline int intel_opregion_init(struct drm_device *dev, int resume) { return 0; }
static inline void intel_opregion_free(struct drm_device *dev, int suspend) { return; }
static inline void intel_opregion_init(struct drm_device *dev) { 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_gse_intr(struct drm_device *dev) { return; }
static inline void intel_opregion_enable_asle(struct drm_device *dev) { return; }
Expand Down
73 changes: 42 additions & 31 deletions drivers/gpu/drm/i915/intel_bios.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ parse_general_definitions(struct drm_i915_private *dev_priv,

static void
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 bdb_general_definitions *p_defs;
Expand All @@ -327,7 +327,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,

p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
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;
}
/* judge whether the size of child device meets the requirements.
Expand Down Expand Up @@ -460,7 +460,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv,

p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
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;
}
/* judge whether the size of child device meets the requirements.
Expand Down Expand Up @@ -513,50 +513,60 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
}
return;
}

/**
* intel_init_bios - initialize VBIOS settings & find VBT
* @dev: DRM device
*
* Loads the Video BIOS and checks that the VBT exists. Sets scratch registers
* 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.
*/
bool
intel_init_bios(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct pci_dev *pdev = dev->pdev;
struct vbt_header *vbt = NULL;
struct bdb_header *bdb;
u8 __iomem *bios;
size_t size;
int i;

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;
}
struct bdb_header *bdb = NULL;
u8 __iomem *bios = NULL;

/* XXX Should this validation be moved to intel_opregion.c? */
if (dev_priv->opregion.vbt) {
struct vbt_header *vbt = dev_priv->opregion.vbt;
if (memcmp(vbt->signature, "$VBT", 4) == 0) {
DRM_DEBUG_DRIVER("Using VBT from OpRegion: %20s\n",
vbt->signature);
bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset);
} else
dev_priv->opregion.vbt = NULL;
}

if (!vbt) {
DRM_ERROR("VBT signature missing\n");
pci_unmap_rom(pdev, bios);
return -1;
}
if (bdb == NULL) {
struct vbt_header *vbt = NULL;
size_t size;
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 */
parse_general_features(dev_priv, bdb);
Expand All @@ -568,7 +578,8 @@ intel_init_bios(struct drm_device *dev)
parse_driver_features(dev_priv, bdb);
parse_edp(dev_priv, bdb);

pci_unmap_rom(pdev, bios);
if (bios)
pci_unmap_rom(pdev, bios);

return 0;
}
Loading

0 comments on commit 44834a6

Please sign in to comment.