summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915')
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c87
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c9
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h31
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c40
-rw-r--r--drivers/gpu/drm/i915/i915_gem_debugfs.c46
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c6
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c234
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h103
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c223
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c44
-rw-r--r--drivers/gpu/drm/i915/intel_bios.h45
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c42
-rw-r--r--drivers/gpu/drm/i915/intel_display.c1051
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c227
-rw-r--r--drivers/gpu/drm/i915/intel_dp_i2c.c11
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h3
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c18
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c64
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c86
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c368
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo_regs.h1
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c22
22 files changed, 2219 insertions, 542 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index f112c769d533..50d1f782768c 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -846,7 +846,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
return 0;
}
- printk(KERN_DEBUG "set status page addr 0x%08x\n", (u32)hws->addr);
+ DRM_DEBUG("set status page addr 0x%08x\n", (u32)hws->addr);
dev_priv->status_gfx_addr = hws->addr & (0x1ffff<<12);
@@ -885,8 +885,8 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
* some RAM for the framebuffer at early boot. This code figures out
* how much was set aside so we can use it for our own purposes.
*/
-static int i915_probe_agp(struct drm_device *dev, unsigned long *aperture_size,
- unsigned long *preallocated_size)
+static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size,
+ uint32_t *preallocated_size)
{
struct pci_dev *bridge_dev;
u16 tmp = 0;
@@ -984,10 +984,11 @@ static int i915_probe_agp(struct drm_device *dev, unsigned long *aperture_size,
return 0;
}
-static int i915_load_modeset_init(struct drm_device *dev)
+static int i915_load_modeset_init(struct drm_device *dev,
+ unsigned long prealloc_size,
+ unsigned long agp_size)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- unsigned long agp_size, prealloc_size;
int fb_bar = IS_I9XX(dev) ? 2 : 0;
int ret = 0;
@@ -1002,10 +1003,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
if (IS_I965G(dev) || IS_G33(dev))
dev_priv->cursor_needs_physical = false;
- ret = i915_probe_agp(dev, &agp_size, &prealloc_size);
- if (ret)
- goto out;
-
/* Basic memrange allocator for stolen space (aka vram) */
drm_mm_init(&dev_priv->vram, 0, prealloc_size);
@@ -1082,6 +1079,44 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master)
master->driver_priv = NULL;
}
+static void i915_get_mem_freq(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 tmp;
+
+ if (!IS_IGD(dev))
+ return;
+
+ tmp = I915_READ(CLKCFG);
+
+ switch (tmp & CLKCFG_FSB_MASK) {
+ case CLKCFG_FSB_533:
+ dev_priv->fsb_freq = 533; /* 133*4 */
+ break;
+ case CLKCFG_FSB_800:
+ dev_priv->fsb_freq = 800; /* 200*4 */
+ break;
+ case CLKCFG_FSB_667:
+ dev_priv->fsb_freq = 667; /* 167*4 */
+ break;
+ case CLKCFG_FSB_400:
+ dev_priv->fsb_freq = 400; /* 100*4 */
+ break;
+ }
+
+ switch (tmp & CLKCFG_MEM_MASK) {
+ case CLKCFG_MEM_533:
+ dev_priv->mem_freq = 533;
+ break;
+ case CLKCFG_MEM_667:
+ dev_priv->mem_freq = 667;
+ break;
+ case CLKCFG_MEM_800:
+ dev_priv->mem_freq = 800;
+ break;
+ }
+}
+
/**
* i915_driver_load - setup chip and create an initial config
* @dev: DRM device
@@ -1098,6 +1133,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
struct drm_i915_private *dev_priv = dev->dev_private;
resource_size_t base, size;
int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1;
+ uint32_t agp_size, prealloc_size;
/* i915 has 4 more counters */
dev->counters += 4;
@@ -1146,9 +1182,29 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
"performance may suffer.\n");
}
+ ret = i915_probe_agp(dev, &agp_size, &prealloc_size);
+ if (ret)
+ goto out_iomapfree;
+
+ dev_priv->wq = create_workqueue("i915");
+ if (dev_priv->wq == NULL) {
+ DRM_ERROR("Failed to create our workqueue.\n");
+ ret = -ENOMEM;
+ goto out_iomapfree;
+ }
+
/* enable GEM by default */
dev_priv->has_gem = 1;
+ if (prealloc_size > agp_size * 3 / 4) {
+ DRM_ERROR("Detected broken video BIOS with %d/%dkB of video "
+ "memory stolen.\n",
+ prealloc_size / 1024, agp_size / 1024);
+ DRM_ERROR("Disabling GEM. (try reducing stolen memory or "
+ "updating the BIOS to fix).\n");
+ dev_priv->has_gem = 0;
+ }
+
dev->driver->get_vblank_counter = i915_get_vblank_counter;
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
if (IS_G4X(dev) || IS_IGDNG(dev)) {
@@ -1162,9 +1218,11 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (!I915_NEED_GFX_HWS(dev)) {
ret = i915_init_phys_hws(dev);
if (ret != 0)
- goto out_iomapfree;
+ goto out_workqueue_free;
}
+ i915_get_mem_freq(dev);
+
/* On the 945G/GM, the chipset reports the MSI capability on the
* integrated graphics even though the support isn't actually there
* according to the published specs. It doesn't appear to function
@@ -1180,6 +1238,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
pci_enable_msi(dev->pdev);
spin_lock_init(&dev_priv->user_irq_lock);
+ spin_lock_init(&dev_priv->error_lock);
dev_priv->user_irq_refcount = 0;
ret = drm_vblank_init(dev, I915_NUM_PIPE);
@@ -1190,10 +1249,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
}
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
- ret = i915_load_modeset_init(dev);
+ ret = i915_load_modeset_init(dev, prealloc_size, agp_size);
if (ret < 0) {
DRM_ERROR("failed to init modeset\n");
- goto out_rmmap;
+ goto out_workqueue_free;
}
}
@@ -1204,6 +1263,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
return 0;
+out_workqueue_free:
+ destroy_workqueue(dev_priv->wq);
out_iomapfree:
io_mapping_free(dev_priv->mm.gtt_mapping);
out_rmmap:
@@ -1217,6 +1278,8 @@ int i915_driver_unload(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ destroy_workqueue(dev_priv->wq);
+
io_mapping_free(dev_priv->mm.gtt_mapping);
if (dev_priv->mm.gtt_mtrr >= 0) {
mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base,
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index e3cb4025e323..fc4b68aa2d05 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -35,6 +35,7 @@
#include "drm_pciids.h"
#include <linux/console.h>
+#include "drm_crtc_helper.h"
static unsigned int i915_modeset = -1;
module_param_named(modeset, i915_modeset, int, 0400);
@@ -57,8 +58,8 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
struct drm_i915_private *dev_priv = dev->dev_private;
if (!dev || !dev_priv) {
- printk(KERN_ERR "dev: %p, dev_priv: %p\n", dev, dev_priv);
- printk(KERN_ERR "DRM not initialized, aborting suspend.\n");
+ DRM_ERROR("dev: %p, dev_priv: %p\n", dev, dev_priv);
+ DRM_ERROR("DRM not initialized, aborting suspend.\n");
return -ENODEV;
}
@@ -115,6 +116,10 @@ static int i915_resume(struct drm_device *dev)
drm_irq_install(dev);
}
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ /* Resume the modeset for every activated CRTC */
+ drm_helper_resume_force_mode(dev);
+ }
return ret;
}
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index bb4c2d387b6c..7537f57d8a87 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -133,6 +133,22 @@ struct sdvo_device_mapping {
u8 initialized;
};
+struct drm_i915_error_state {
+ u32 eir;
+ u32 pgtbl_er;
+ u32 pipeastat;
+ u32 pipebstat;
+ u32 ipeir;
+ u32 ipehr;
+ u32 instdone;
+ u32 acthd;
+ u32 instpm;
+ u32 instps;
+ u32 instdone1;
+ u32 seqno;
+ struct timeval time;
+};
+
typedef struct drm_i915_private {
struct drm_device *dev;
@@ -203,12 +219,20 @@ typedef struct drm_i915_private {
unsigned int lvds_vbt:1;
unsigned int int_crt_support:1;
unsigned int lvds_use_ssc:1;
+ unsigned int edp_support:1;
int lvds_ssc_freq;
struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
int num_fence_regs; /* 8 on pre-965, 16 otherwise */
+ unsigned int fsb_freq, mem_freq;
+
+ spinlock_t error_lock;
+ struct drm_i915_error_state *first_error;
+ struct work_struct error_work;
+ struct workqueue_struct *wq;
+
/* Register state */
u8 saveLBB;
u32 saveDSPACNTR;
@@ -468,9 +492,6 @@ struct drm_i915_gem_object {
*/
int fence_reg;
- /** Boolean whether this object has a valid gtt offset. */
- int gtt_bound;
-
/** How many users have pinned this object in GTT space */
int pin_count;
@@ -655,6 +676,7 @@ void i915_gem_free_object(struct drm_gem_object *obj);
int i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment);
void i915_gem_object_unpin(struct drm_gem_object *obj);
int i915_gem_object_unbind(struct drm_gem_object *obj);
+void i915_gem_release_mmap(struct drm_gem_object *obj);
void i915_gem_lastclose(struct drm_device *dev);
uint32_t i915_get_gem_seqno(struct drm_device *dev);
int i915_gem_object_get_fence_reg(struct drm_gem_object *obj);
@@ -869,7 +891,10 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
IS_I915GM(dev)))
#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IGDNG(dev))
#define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IGDNG(dev))
+#define SUPPORTS_EDP(dev) (IS_IGDNG_M(dev))
#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
+/* dsparb controlled by hw only */
+#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev))
#define PRIMARY_RINGBUFFER_SIZE (128*1024)
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 876b65cb7629..140bee142fc2 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1252,6 +1252,31 @@ out_free_list:
return ret;
}
+/**
+ * i915_gem_release_mmap - remove physical page mappings
+ * @obj: obj in question
+ *
+ * Preserve the reservation of the mmaping with the DRM core code, but
+ * relinquish ownership of the pages back to the system.
+ *
+ * It is vital that we remove the page mapping if we have mapped a tiled
+ * object through the GTT and then lose the fence register due to
+ * resource pressure. Similarly if the object has been moved out of the
+ * aperture, than pages mapped into userspace must be revoked. Removing the
+ * mapping will then trigger a page fault on the next user access, allowing
+ * fixup by i915_gem_fault().
+ */
+void
+i915_gem_release_mmap(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+
+ if (dev->dev_mapping)
+ unmap_mapping_range(dev->dev_mapping,
+ obj_priv->mmap_offset, obj->size, 1);
+}
+
static void
i915_gem_free_mmap_offset(struct drm_gem_object *obj)
{
@@ -1545,7 +1570,7 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
}
if (was_empty && !dev_priv->mm.suspended)
- schedule_delayed_work(&dev_priv->mm.retire_work, HZ);
+ queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
return seqno;
}
@@ -1694,7 +1719,7 @@ i915_gem_retire_work_handler(struct work_struct *work)
i915_gem_retire_requests(dev);
if (!dev_priv->mm.suspended &&
!list_empty(&dev_priv->mm.request_list))
- schedule_delayed_work(&dev_priv->mm.retire_work, HZ);
+ queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
mutex_unlock(&dev->struct_mutex);
}
@@ -1861,7 +1886,6 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
- loff_t offset;
int ret = 0;
#if WATCH_BUF
@@ -1898,9 +1922,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
BUG_ON(obj_priv->active);
/* blow away mappings if mapped through GTT */
- offset = ((loff_t) obj->map_list.hash.key) << PAGE_SHIFT;
- if (dev->dev_mapping)
- unmap_mapping_range(dev->dev_mapping, offset, obj->size, 1);
+ i915_gem_release_mmap(obj);
if (obj_priv->fence_reg != I915_FENCE_REG_NONE)
i915_gem_clear_fence_reg(obj);
@@ -2222,7 +2244,6 @@ try_again:
/* None available, try to steal one or wait for a user to finish */
if (i == dev_priv->num_fence_regs) {
uint32_t seqno = dev_priv->mm.next_gem_seqno;
- loff_t offset;
if (avail == 0)
return -ENOSPC;
@@ -2274,10 +2295,7 @@ try_again:
* Zap this virtual mapping so we can set up a fence again
* for this object next time we need it.
*/
- offset = ((loff_t) reg->obj->map_list.hash.key) << PAGE_SHIFT;
- if (dev->dev_mapping)
- unmap_mapping_range(dev->dev_mapping, offset,
- reg->obj->size, 1);
+ i915_gem_release_mmap(reg->obj);
old_obj_priv->fence_reg = I915_FENCE_REG_NONE;
}
diff --git a/drivers/gpu/drm/i915/i915_gem_debugfs.c b/drivers/gpu/drm/i915/i915_gem_debugfs.c
index 28146e405e87..cb3b97405fbf 100644
--- a/drivers/gpu/drm/i915/i915_gem_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_gem_debugfs.c
@@ -75,11 +75,10 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
case ACTIVE_LIST:
seq_printf(m, "Active:\n");
lock = &dev_priv->mm.active_list_lock;
- spin_lock(lock);
head = &dev_priv->mm.active_list;
break;
case INACTIVE_LIST:
- seq_printf(m, "Inctive:\n");
+ seq_printf(m, "Inactive:\n");
head = &dev_priv->mm.inactive_list;
break;
case FLUSHING_LIST:
@@ -91,6 +90,8 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
return 0;
}
+ if (lock)
+ spin_lock(lock);
list_for_each_entry(obj_priv, head, list)
{
struct drm_gem_object *obj = obj_priv->obj;
@@ -104,7 +105,10 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
if (obj->name)
seq_printf(m, " (name: %d)", obj->name);
if (obj_priv->fence_reg != I915_FENCE_REG_NONE)
- seq_printf(m, " (fence: %d)\n", obj_priv->fence_reg);
+ seq_printf(m, " (fence: %d)", obj_priv->fence_reg);
+ if (obj_priv->gtt_space != NULL)
+ seq_printf(m, " (gtt_offset: %08x)", obj_priv->gtt_offset);
+
seq_printf(m, "\n");
}
@@ -323,6 +327,41 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data)
return 0;
}
+static int i915_error_state(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 drm_i915_error_state *error;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev_priv->error_lock, flags);
+ if (!dev_priv->first_error) {
+ seq_printf(m, "no error state collected\n");
+ goto out;
+ }
+
+ error = dev_priv->first_error;
+
+ seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec,
+ error->time.tv_usec);
+ seq_printf(m, "EIR: 0x%08x\n", error->eir);
+ seq_printf(m, " PGTBL_ER: 0x%08x\n", error->pgtbl_er);
+ seq_printf(m, " INSTPM: 0x%08x\n", error->instpm);
+ seq_printf(m, " IPEIR: 0x%08x\n", error->ipeir);
+ seq_printf(m, " IPEHR: 0x%08x\n", error->ipehr);
+ seq_printf(m, " INSTDONE: 0x%08x\n", error->instdone);
+ seq_printf(m, " ACTHD: 0x%08x\n", error->acthd);
+ if (IS_I965G(dev)) {
+ seq_printf(m, " INSTPS: 0x%08x\n", error->instps);
+ seq_printf(m, " INSTDONE1: 0x%08x\n", error->instdone1);
+ }
+
+out:
+ spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+
+ return 0;
+}
static struct drm_info_list i915_gem_debugfs_list[] = {
{"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST},
@@ -336,6 +375,7 @@ static struct drm_info_list i915_gem_debugfs_list[] = {
{"i915_ringbuffer_data", i915_ringbuffer_data, 0},
{"i915_ringbuffer_info", i915_ringbuffer_info, 0},
{"i915_batchbuffers", i915_batchbuffer_info, 0},
+ {"i915_error_state", i915_error_state, 0},
};
#define I915_GEM_DEBUGFS_ENTRIES ARRAY_SIZE(i915_gem_debugfs_list)
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index daeae62e1c28..a2d527b22ec4 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -521,6 +521,12 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
goto err;
}
+ /* If we've changed tiling, GTT-mappings of the object
+ * need to re-fault to ensure that the correct fence register
+ * setup is in place.
+ */
+ i915_gem_release_mmap(obj);
+
obj_priv->tiling_mode = args->tiling_mode;
obj_priv->stride = args->stride;
}
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 228546f6eaa4..7ebc84c2881e 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -26,6 +26,7 @@
*
*/
+#include <linux/sysrq.h>
#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
@@ -41,9 +42,10 @@
* we leave them always unmasked in IMR and then control enabling them through
* PIPESTAT alone.
*/
-#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \
- I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
- I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
+#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \
+ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
+ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \
+ I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
/** Interrupts that we mask and unmask at runtime. */
#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT)
@@ -188,7 +190,7 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
if (!i915_pipe_enabled(dev, pipe)) {
- DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
+ DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
return 0;
}
@@ -217,7 +219,7 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45;
if (!i915_pipe_enabled(dev, pipe)) {
- DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
+ DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
return 0;
}
@@ -288,6 +290,201 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev)
return ret;
}
+/**
+ * i915_error_work_func - do process context error handling work
+ * @work: work struct
+ *
+ * Fire an error uevent so userspace can see that a hang or error
+ * was detected.
+ */
+static void i915_error_work_func(struct work_struct *work)
+{
+ drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
+ error_work);
+ struct drm_device *dev = dev_priv->dev;
+ char *event_string = "ERROR=1";
+ char *envp[] = { event_string, NULL };
+
+ DRM_DEBUG("generating error event\n");
+
+ kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
+}
+
+/**
+ * i915_capture_error_state - capture an error record for later analysis
+ * @dev: drm device
+ *
+ * Should be called when an error is detected (either a hang or an error
+ * interrupt) to capture error state from the time of the error. Fills
+ * out a structure which becomes available in debugfs for user level tools
+ * to pick up.
+ */
+static void i915_capture_error_state(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_error_state *error;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev_priv->error_lock, flags);
+ if (dev_priv->first_error)
+ goto out;
+
+ error = kmalloc(sizeof(*error), GFP_ATOMIC);
+ if (!error) {
+ DRM_DEBUG("out ot memory, not capturing error state\n");
+ goto out;
+ }
+
+ error->eir = I915_READ(EIR);
+ error->pgtbl_er = I915_READ(PGTBL_ER);
+ error->pipeastat = I915_READ(PIPEASTAT);
+ error->pipebstat = I915_READ(PIPEBSTAT);
+ error->instpm = I915_READ(INSTPM);
+ if (!IS_I965G(dev)) {
+ error->ipeir = I915_READ(IPEIR);
+ error->ipehr = I915_READ(IPEHR);
+ error->instdone = I915_READ(INSTDONE);
+ error->acthd = I915_READ(ACTHD);
+ } else {
+ error->ipeir = I915_READ(IPEIR_I965);
+ error->ipehr = I915_READ(IPEHR_I965);
+ error->instdone = I915_READ(INSTDONE_I965);
+ error->instps = I915_READ(INSTPS);
+ error->instdone1 = I915_READ(INSTDONE1);
+ error->acthd = I915_READ(ACTHD_I965);
+ }
+
+ do_gettimeofday(&error->time);
+
+ dev_priv->first_error = error;
+
+out:
+ spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+}
+
+/**
+ * i915_handle_error - handle an error interrupt
+ * @dev: drm device
+ *
+ * Do some basic checking of regsiter state at error interrupt time and
+ * dump it to the syslog. Also call i915_capture_error_state() to make
+ * sure we get a record and make it available in debugfs. Fire a uevent
+ * so userspace knows something bad happened (should trigger collection
+ * of a ring dump etc.).
+ */
+static void i915_handle_error(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 eir = I915_READ(EIR);
+ u32 pipea_stats = I915_READ(PIPEASTAT);
+ u32 pipeb_stats = I915_READ(PIPEBSTAT);
+
+ i915_capture_error_state(dev);
+
+ printk(KERN_ERR "render error detected, EIR: 0x%08x\n",
+ eir);
+
+ if (IS_G4X(dev)) {
+ if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) {
+ u32 ipeir = I915_READ(IPEIR_I965);
+
+ printk(KERN_ERR " IPEIR: 0x%08x\n",
+ I915_READ(IPEIR_I965));
+ printk(KERN_ERR " IPEHR: 0x%08x\n",
+ I915_READ(IPEHR_I965));
+ printk(KERN_ERR " INSTDONE: 0x%08x\n",
+ I915_READ(INSTDONE_I965));
+ printk(KERN_ERR " INSTPS: 0x%08x\n",
+ I915_READ(INSTPS));
+ printk(KERN_ERR " INSTDONE1: 0x%08x\n",
+ I915_READ(INSTDONE1));
+ printk(KERN_ERR " ACTHD: 0x%08x\n",
+ I915_READ(ACTHD_I965));
+ I915_WRITE(IPEIR_I965, ipeir);
+ (void)I915_READ(IPEIR_I965);
+ }
+ if (eir & GM45_ERROR_PAGE_TABLE) {
+ u32 pgtbl_err = I915_READ(PGTBL_ER);
+ printk(KERN_ERR "page table error\n");
+ printk(KERN_ERR " PGTBL_ER: 0x%08x\n",
+ pgtbl_err);
+ I915_WRITE(PGTBL_ER, pgtbl_err);
+ (void)I915_READ(PGTBL_ER);
+ }
+ }
+
+ if (IS_I9XX(dev)) {
+ if (eir & I915_ERROR_PAGE_TABLE) {
+ u32 pgtbl_err = I915_READ(PGTBL_ER);
+ printk(KERN_ERR "page table error\n");
+ printk(KERN_ERR " PGTBL_ER: 0x%08x\n",
+ pgtbl_err);
+ I915_WRITE(PGTBL_ER, pgtbl_err);
+ (void)I915_READ(PGTBL_ER);
+ }
+ }
+
+ if (eir & I915_ERROR_MEMORY_REFRESH) {
+ printk(KERN_ERR "memory refresh error\n");
+ printk(KERN_ERR "PIPEASTAT: 0x%08x\n",
+ pipea_stats);
+ printk(KERN_ERR "PIPEBSTAT: 0x%08x\n",
+ pipeb_stats);
+ /* pipestat has already been acked */
+ }
+ if (eir & I915_ERROR_INSTRUCTION) {
+ printk(KERN_ERR "instruction error\n");
+ printk(KERN_ERR " INSTPM: 0x%08x\n",
+ I915_READ(INSTPM));
+ if (!IS_I965G(dev)) {
+ u32 ipeir = I915_READ(IPEIR);
+
+ printk(KERN_ERR " IPEIR: 0x%08x\n",
+ I915_READ(IPEIR));
+ printk(KERN_ERR " IPEHR: 0x%08x\n",
+ I915_READ(IPEHR));
+ printk(KERN_ERR " INSTDONE: 0x%08x\n",
+ I915_READ(INSTDONE));
+ printk(KERN_ERR " ACTHD: 0x%08x\n",
+ I915_READ(ACTHD));
+ I915_WRITE(IPEIR, ipeir);
+ (void)I915_READ(IPEIR);
+ } else {
+ u32 ipeir = I915_READ(IPEIR_I965);
+
+ printk(KERN_ERR " IPEIR: 0x%08x\n",
+ I915_READ(IPEIR_I965));
+ printk(KERN_ERR " IPEHR: 0x%08x\n",
+ I915_READ(IPEHR_I965));
+ printk(KERN_ERR " INSTDONE: 0x%08x\n",
+ I915_READ(INSTDONE_I965));
+ printk(KERN_ERR " INSTPS: 0x%08x\n",
+ I915_READ(INSTPS));
+ printk(KERN_ERR " INSTDONE1: 0x%08x\n",
+ I915_READ(INSTDONE1));
+ printk(KERN_ERR " ACTHD: 0x%08x\n",
+ I915_READ(ACTHD_I965));
+ I915_WRITE(IPEIR_I965, ipeir);
+ (void)I915_READ(IPEIR_I965);
+ }
+ }
+
+ I915_WRITE(EIR, eir);
+ (void)I915_READ(EIR);
+ eir = I915_READ(EIR);
+ if (eir) {
+ /*
+ * some errors might have become stuck,
+ * mask them.
+ */
+ DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir);
+ I915_WRITE(EMR, I915_READ(EMR) | eir);
+ I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
+ }
+
+ queue_work(dev_priv->wq, &dev_priv->error_work);
+}
+
irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
@@ -329,15 +526,22 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
pipea_stats = I915_READ(PIPEASTAT);
pipeb_stats = I915_READ(PIPEBSTAT);
+ if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
+ i915_handle_error(dev);
+
/*
* Clear the PIPE(A|B)STAT regs before the IIR
*/
if (pipea_stats & 0x8000ffff) {
+ if (pipea_stats & PIPE_FIFO_UNDERRUN_STATUS)
+ DRM_DEBUG("pipe a underrun\n");
I915_WRITE(PIPEASTAT, pipea_stats);
irq_received = 1;
}
if (pipeb_stats & 0x8000ffff) {
+ if (pipeb_stats & PIPE_FIFO_UNDERRUN_STATUS)
+ DRM_DEBUG("pipe b underrun\n");
I915_WRITE(PIPEBSTAT, pipeb_stats);
irq_received = 1;
}
@@ -356,7 +560,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
DRM_DEBUG("hotplug event received, stat 0x%08x\n",
hotplug_status);
if (hotplug_status & dev_priv->hotplug_supported_mask)
- schedule_work(&dev_priv->hotplug_work);
+ queue_work(dev_priv->wq,
+ &dev_priv->hotplug_work);
I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
I915_READ(PORT_HOTPLUG_STAT);
@@ -709,6 +914,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
atomic_set(&dev_priv->irq_received, 0);
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
+ INIT_WORK(&dev_priv->error_work, i915_error_work_func);
if (IS_IGDNG(dev)) {
igdng_irq_preinstall(dev);
@@ -732,6 +938,7 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR;
+ u32 error_mask;
DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
@@ -768,6 +975,21 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
i915_enable_irq(dev_priv, I915_DISPLAY_PORT_INTERRUPT);
}
+ /*
+ * Enable some error detection, note the instruction error mask
+ * bit is reserved, so we leave it masked.
+ */
+ if (IS_G4X(dev)) {
+ error_mask = ~(GM45_ERROR_PAGE_TABLE |
+ GM45_ERROR_MEM_PRIV |
+ GM45_ERROR_CP_PRIV |
+ I915_ERROR_MEMORY_REFRESH);
+ } else {
+ error_mask = ~(I915_ERROR_PAGE_TABLE |
+ I915_ERROR_MEMORY_REFRESH);
+ }
+ I915_WRITE(EMR, error_mask);
+
/* Disable pipe interrupt enables, clear pending pipe status */
I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff);
I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff);
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 88bf7521405f..2955083aa471 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -206,6 +206,7 @@
/*
* Instruction and interrupt control regs
*/
+#define PGTBL_ER 0x02024
#define PRB0_TAIL 0x02030
#define PRB0_HEAD 0x02034
#define PRB0_START 0x02038
@@ -226,11 +227,18 @@
#define PRB1_HEAD 0x02044 /* 915+ only */
#define PRB1_START 0x02048 /* 915+ only */
#define PRB1_CTL 0x0204c /* 915+ only */
+#define IPEIR_I965 0x02064
+#define IPEHR_I965 0x02068
+#define INSTDONE_I965 0x0206c
+#define INSTPS 0x02070 /* 965+ only */
+#define INSTDONE1 0x0207c /* 965+ only */
#define ACTHD_I965 0x02074
#define HWS_PGA 0x02080
#define HWS_ADDRESS_MASK 0xfffff000
#define HWS_START_ADDRESS_SHIFT 4
#define IPEIR 0x02088
+#define IPEHR 0x0208c
+#define INSTDONE 0x02090
#define NOPID 0x02094
#define HWSTAM 0x02098
#define SCPD0 0x0209c /* 915+ only */
@@ -258,10 +266,22 @@
#define EIR 0x020b0
#define EMR 0x020b4
#define ESR 0x020b8
+#define GM45_ERROR_PAGE_TABLE (1<<5)
+#define GM45_ERROR_MEM_PRIV (1<<4)
+#define I915_ERROR_PAGE_TABLE (1<<4)
+#define GM45_ERROR_CP_PRIV (1<<3)
+#define I915_ERROR_MEMORY_REFRESH (1<<1)
+#define I915_ERROR_INSTRUCTION (1<<0)
#define INSTPM 0x020c0
#define ACTHD 0x020c8
#define FW_BLC 0x020d8
+#define FW_BLC2 0x020dc
#define FW_BLC_SELF 0x020e0 /* 915+ only */
+#define FW_BLC_SELF_EN (1<<15)
+#define MM_BURST_LENGTH 0x00700000
+#define MM_FIFO_WATERMARK 0x0001F000
+#define LM_BURST_LENGTH 0x00000700
+#define LM_FIFO_WATERMARK 0x0000001F
#define MI_ARB_STATE 0x020e4 /* 915+ only */
#define CACHE_MODE_0 0x02120 /* 915+ only */
#define CM0_MASK_SHIFT 16
@@ -571,17 +591,21 @@
/* Clocking configuration register */
#define CLKCFG 0x10c00
-#define CLKCFG_FSB_400 (0 << 0) /* hrawclk 100 */
+#define CLKCFG_FSB_400 (5 << 0) /* hrawclk 100 */
#define CLKCFG_FSB_533 (1 << 0) /* hrawclk 133 */
#define CLKCFG_FSB_667 (3 << 0) /* hrawclk 166 */
#define CLKCFG_FSB_800 (2 << 0) /* hrawclk 200 */
#define CLKCFG_FSB_1067 (6 << 0) /* hrawclk 266 */
#define CLKCFG_FSB_1333 (7 << 0) /* hrawclk 333 */
-/* this is a guess, could be 5 as well */
+/* Note, below two are guess */
#define CLKCFG_FSB_1600 (4 << 0) /* hrawclk 400 */
-#define CLKCFG_FSB_1600_ALT (5 << 0) /* hrawclk 400 */
+#define CLKCFG_FSB_1600_ALT (0 << 0) /* hrawclk 400 */
#define CLKCFG_FSB_MASK (7 << 0)
-
+#define CLKCFG_MEM_533 (1 << 4)
+#define CLKCFG_MEM_667 (2 << 4)
+#define CLKCFG_MEM_800 (3 << 4)
+#define CLKCFG_MEM_MASK (7 << 4)
+
/** GM965 GM45 render standby register */
#define MCHBAR_RENDER_STANDBY 0x111B8
@@ -1371,6 +1395,7 @@
#define TV_V_CHROMA_42 0x684a8
/* Display Port */
+#define DP_A 0x64000 /* eDP */
#define DP_B 0x64100
#define DP_C 0x64200
#define DP_D 0x64300
@@ -1413,13 +1438,22 @@
/* Mystic DPCD version 1.1 special mode */
#define DP_ENHANCED_FRAMING (1 << 18)
+/* eDP */
+#define DP_PLL_FREQ_270