diff options
author | Dave Airlie <airlied@redhat.com> | 2010-02-25 13:39:36 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-02-25 13:39:36 +1000 |
commit | 30d6c72c4a760cfc9069ee100786e4d6cf6de59d (patch) | |
tree | e2f1b3bad0a161a1e94705cb14d2b8c2760caf91 | |
parent | de19322d554fd0f449d17610823c38494c06db9e (diff) | |
parent | 9df30794f609d9412f14cfd0eb7b45dd64d0b14e (diff) |
Merge remote branch 'anholt/drm-intel-next' into drm-next-stage
* anholt/drm-intel-next:
drm/i915: Record batch buffer following GPU error
drm/i915: give up on 8xx lid status
drm/i915: reduce some of the duplication of tiling checking
drm/i915: blow away userspace mappings before fence change
drm/i915: move a gtt flush to the correct place
agp/intel: official names for Pineview and Ironlake
drm/i915: overlay: drop superflous gpu flushes
drm/i915: overlay: nuke readback to flush wc caches
drm/i915: provide self-refresh status in debugfs
drm/i915: provide FBC status in debugfs
drm/i915: fix drps disable so unload & re-load works
drm/i915: Fix OGLC performance regression on 945
drm/i915: Deobfuscate the render p-state obfuscation
drm/i915: add dynamic performance control support for Ironlake
drm/i915: enable memory self refresh on 9xx
drm/i915: Don't reserve compatibility fence regs in KMS mode.
drm/i915: Keep MCHBAR always enabled
drm/i915: Replace open-coded eviction in i915_gem_idle()
-rw-r--r-- | drivers/char/agp/intel-agp.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_debugfs.c | 251 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_dma.c | 125 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 41 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem.c | 158 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem_tiling.c | 163 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 287 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_reg.h | 147 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_suspend.c | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 143 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_lvds.c | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_overlay.c | 25 |
14 files changed, 1063 insertions, 313 deletions
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 3999a5f25f3..e5ffefee34a 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -2345,9 +2345,9 @@ static const struct intel_driver_description { NULL, &intel_g33_driver }, { PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, 0, "Q33", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, 0, "Pineview", + { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, 0, "GMA3150", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, 0, "Pineview", + { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, 0, "GMA3150", NULL, &intel_g33_driver }, { PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, 0, "GM45", NULL, &intel_i965_driver }, @@ -2362,13 +2362,13 @@ static const struct intel_driver_description { { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0, "G41", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, 0, - "Ironlake/D", NULL, &intel_i965_driver }, + "HD Graphics", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, - "Ironlake/M", NULL, &intel_i965_driver }, + "HD Graphics", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, - "Ironlake/MA", NULL, &intel_i965_driver }, + "HD Graphics", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, - "Ironlake/MC2", NULL, &intel_i965_driver }, + "HD Graphics", NULL, &intel_i965_driver }, { 0, 0, 0, NULL, NULL, NULL } }; diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a894ade0309..5eed4631244 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -350,6 +350,36 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data) return 0; } +static const char *pin_flag(int pinned) +{ + if (pinned > 0) + return " P"; + else if (pinned < 0) + return " p"; + else + return ""; +} + +static const char *tiling_flag(int tiling) +{ + switch (tiling) { + default: + case I915_TILING_NONE: return ""; + case I915_TILING_X: return " X"; + case I915_TILING_Y: return " Y"; + } +} + +static const char *dirty_flag(int dirty) +{ + return dirty ? " dirty" : ""; +} + +static const char *purgeable_flag(int purgeable) +{ + return purgeable ? " purgeable" : ""; +} + static int i915_error_state(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *) m->private; @@ -357,6 +387,7 @@ static int i915_error_state(struct seq_file *m, void *unused) drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_error_state *error; unsigned long flags; + int i, page, offset, elt; spin_lock_irqsave(&dev_priv->error_lock, flags); if (!dev_priv->first_error) { @@ -368,6 +399,7 @@ static int i915_error_state(struct seq_file *m, void *unused) seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec, error->time.tv_usec); + seq_printf(m, "PCI ID: 0x%04x\n", dev->pci_device); 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); @@ -379,6 +411,59 @@ static int i915_error_state(struct seq_file *m, void *unused) seq_printf(m, " INSTPS: 0x%08x\n", error->instps); seq_printf(m, " INSTDONE1: 0x%08x\n", error->instdone1); } + seq_printf(m, "seqno: 0x%08x\n", error->seqno); + + if (error->active_bo_count) { + seq_printf(m, "Buffers [%d]:\n", error->active_bo_count); + + for (i = 0; i < error->active_bo_count; i++) { + seq_printf(m, " %08x %8zd %08x %08x %08x%s%s%s%s", + error->active_bo[i].gtt_offset, + error->active_bo[i].size, + error->active_bo[i].read_domains, + error->active_bo[i].write_domain, + error->active_bo[i].seqno, + pin_flag(error->active_bo[i].pinned), + tiling_flag(error->active_bo[i].tiling), + dirty_flag(error->active_bo[i].dirty), + purgeable_flag(error->active_bo[i].purgeable)); + + if (error->active_bo[i].name) + seq_printf(m, " (name: %d)", error->active_bo[i].name); + if (error->active_bo[i].fence_reg != I915_FENCE_REG_NONE) + seq_printf(m, " (fence: %d)", error->active_bo[i].fence_reg); + + seq_printf(m, "\n"); + } + } + + for (i = 0; i < ARRAY_SIZE(error->batchbuffer); i++) { + if (error->batchbuffer[i]) { + struct drm_i915_error_object *obj = error->batchbuffer[i]; + + seq_printf(m, "--- gtt_offset = 0x%08x\n", obj->gtt_offset); + offset = 0; + for (page = 0; page < obj->page_count; page++) { + for (elt = 0; elt < PAGE_SIZE/4; elt++) { + seq_printf(m, "%08x : %08x\n", offset, obj->pages[page][elt]); + offset += 4; + } + } + } + } + + if (error->ringbuffer) { + struct drm_i915_error_object *obj = error->ringbuffer; + + seq_printf(m, "--- ringbuffer = 0x%08x\n", obj->gtt_offset); + offset = 0; + for (page = 0; page < obj->page_count; page++) { + for (elt = 0; elt < PAGE_SIZE/4; elt++) { + seq_printf(m, "%08x : %08x\n", offset, obj->pages[page][elt]); + offset += 4; + } + } + } out: spin_unlock_irqrestore(&dev_priv->error_lock, flags); @@ -386,6 +471,165 @@ out: return 0; } +static int i915_rstdby_delays(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; + u16 crstanddelay = I915_READ16(CRSTANDVID); + + seq_printf(m, "w/ctx: %d, w/o ctx: %d\n", (crstanddelay >> 8) & 0x3f, (crstanddelay & 0x3f)); + + return 0; +} + +static int i915_cur_delayinfo(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; + u16 rgvswctl = I915_READ16(MEMSWCTL); + + seq_printf(m, "Last command: 0x%01x\n", (rgvswctl >> 13) & 0x3); + seq_printf(m, "Command status: %d\n", (rgvswctl >> 12) & 1); + seq_printf(m, "P%d DELAY 0x%02x\n", (rgvswctl >> 8) & 0xf, + rgvswctl & 0x3f); + + return 0; +} + +static int i915_delayfreq_table(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; + u32 delayfreq; + int i; + + for (i = 0; i < 16; i++) { + delayfreq = I915_READ(PXVFREQ_BASE + i * 4); + seq_printf(m, "P%02dVIDFREQ: 0x%08x\n", i, delayfreq); + } + + return 0; +} + +static inline int MAP_TO_MV(int map) +{ + return 1250 - (map * 25); +} + +static int i915_inttoext_table(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; + u32 inttoext; + int i; + + for (i = 1; i <= 32; i++) { + inttoext = I915_READ(INTTOEXT_BASE_ILK + i * 4); + seq_printf(m, "INTTOEXT%02d: 0x%08x\n", i, inttoext); + } + + return 0; +} + +static int i915_drpc_info(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; + u32 rgvmodectl = I915_READ(MEMMODECTL); + + seq_printf(m, "HD boost: %s\n", (rgvmodectl & MEMMODE_BOOST_EN) ? + "yes" : "no"); + seq_printf(m, "Boost freq: %d\n", + (rgvmodectl & MEMMODE_BOOST_FREQ_MASK) >> + MEMMODE_BOOST_FREQ_SHIFT); + seq_printf(m, "HW control enabled: %s\n", + rgvmodectl & MEMMODE_HWIDLE_EN ? "yes" : "no"); + seq_printf(m, "SW control enabled: %s\n", + rgvmodectl & MEMMODE_SWMODE_EN ? "yes" : "no"); + seq_printf(m, "Gated voltage change: %s\n", + rgvmodectl & MEMMODE_RCLK_GATE ? "yes" : "no"); + seq_printf(m, "Starting frequency: P%d\n", + (rgvmodectl & MEMMODE_FSTART_MASK) >> MEMMODE_FSTART_SHIFT); + seq_printf(m, "Max frequency: P%d\n", + (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT); + seq_printf(m, "Min frequency: P%d\n", (rgvmodectl & MEMMODE_FMIN_MASK)); + + return 0; +} + +static int i915_fbc_status(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_crtc *crtc; + drm_i915_private_t *dev_priv = dev->dev_private; + bool fbc_enabled = false; + + if (!dev_priv->display.fbc_enabled) { + seq_printf(m, "FBC unsupported on this chipset\n"); + return 0; + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (!crtc->enabled) + continue; + if (dev_priv->display.fbc_enabled(crtc)) + fbc_enabled = true; + } + + if (fbc_enabled) { + seq_printf(m, "FBC enabled\n"); + } else { + seq_printf(m, "FBC disabled: "); + switch (dev_priv->no_fbc_reason) { + case FBC_STOLEN_TOO_SMALL: + seq_printf(m, "not enough stolen memory"); + break; + case FBC_UNSUPPORTED_MODE: + seq_printf(m, "mode not supported"); + break; + case FBC_MODE_TOO_LARGE: + seq_printf(m, "mode too large"); + break; + case FBC_BAD_PLANE: + seq_printf(m, "FBC unsupported on plane"); + break; + case FBC_NOT_TILED: + seq_printf(m, "scanout buffer not tiled"); + break; + default: + seq_printf(m, "unknown reason"); + } + seq_printf(m, "\n"); + } + return 0; +} + +static int i915_sr_status(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; + bool sr_enabled = false; + + if (IS_I965G(dev) || IS_I945G(dev) || IS_I945GM(dev)) + sr_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN; + else if (IS_I915GM(dev)) + sr_enabled = I915_READ(INSTPM) & INSTPM_SELF_EN; + else if (IS_PINEVIEW(dev)) + sr_enabled = I915_READ(DSPFW3) & PINEVIEW_SELF_REFRESH_EN; + + seq_printf(m, "self-refresh: %s\n", sr_enabled ? "enabled" : + "disabled"); + + return 0; +} + static int i915_wedged_open(struct inode *inode, struct file *filp) @@ -503,6 +747,13 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_ringbuffer_info", i915_ringbuffer_info, 0}, {"i915_batchbuffers", i915_batchbuffer_info, 0}, {"i915_error_state", i915_error_state, 0}, + {"i915_rstdby_delays", i915_rstdby_delays, 0}, + {"i915_cur_delayinfo", i915_cur_delayinfo, 0}, + {"i915_delayfreq_table", i915_delayfreq_table, 0}, + {"i915_inttoext_table", i915_inttoext_table, 0}, + {"i915_drpc_info", i915_drpc_info, 0}, + {"i915_fbc_status", i915_fbc_status, 0}, + {"i915_sr_status", i915_sr_status, 0}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 2307f98349f..dbfe07c90cb 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -35,6 +35,8 @@ #include "i915_drv.h" #include "i915_trace.h" #include <linux/vgaarb.h> +#include <linux/acpi.h> +#include <linux/pnp.h> /* Really want an OS-independent resettable timer. Would like to have * this loop run for (eg) 3 sec, but have the timer reset every time @@ -933,6 +935,120 @@ static int i915_get_bridge_dev(struct drm_device *dev) return 0; } +#define MCHBAR_I915 0x44 +#define MCHBAR_I965 0x48 +#define MCHBAR_SIZE (4*4096) + +#define DEVEN_REG 0x54 +#define DEVEN_MCHBAR_EN (1 << 28) + +/* Allocate space for the MCH regs if needed, return nonzero on error */ +static int +intel_alloc_mchbar_resource(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915; + u32 temp_lo, temp_hi = 0; + u64 mchbar_addr; + int ret = 0; + + if (IS_I965G(dev)) + pci_read_config_dword(dev_priv->bridge_dev, reg + 4, &temp_hi); + pci_read_config_dword(dev_priv->bridge_dev, reg, &temp_lo); + mchbar_addr = ((u64)temp_hi << 32) | temp_lo; + + /* If ACPI doesn't have it, assume we need to allocate it ourselves */ +#ifdef CONFIG_PNP + if (mchbar_addr && + pnp_range_reserved(mchbar_addr, mchbar_addr + MCHBAR_SIZE)) { + ret = 0; + goto out; + } +#endif + + /* Get some space for it */ + ret = pci_bus_alloc_resource(dev_priv->bridge_dev->bus, &dev_priv->mch_res, + MCHBAR_SIZE, MCHBAR_SIZE, + PCIBIOS_MIN_MEM, + 0, pcibios_align_resource, + dev_priv->bridge_dev); + if (ret) { + DRM_DEBUG_DRIVER("failed bus alloc: %d\n", ret); + dev_priv->mch_res.start = 0; + goto out; + } + + if (IS_I965G(dev)) + pci_write_config_dword(dev_priv->bridge_dev, reg + 4, + upper_32_bits(dev_priv->mch_res.start)); + + pci_write_config_dword(dev_priv->bridge_dev, reg, + lower_32_bits(dev_priv->mch_res.start)); +out: + return ret; +} + +/* Setup MCHBAR if possible, return true if we should disable it again */ +static void +intel_setup_mchbar(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915; + u32 temp; + bool enabled; + + dev_priv->mchbar_need_disable = false; + + if (IS_I915G(dev) || IS_I915GM(dev)) { + pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp); + enabled = !!(temp & DEVEN_MCHBAR_EN); + } else { + pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp); + enabled = temp & 1; + } + + /* If it's already enabled, don't have to do anything */ + if (enabled) + return; + + if (intel_alloc_mchbar_resource(dev)) + return; + + dev_priv->mchbar_need_disable = true; + + /* Space is allocated or reserved, so enable it. */ + if (IS_I915G(dev) || IS_I915GM(dev)) { + pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG, + temp | DEVEN_MCHBAR_EN); + } else { + pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp); + pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp | 1); + } +} + +static void +intel_teardown_mchbar(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915; + u32 temp; + + if (dev_priv->mchbar_need_disable) { + if (IS_I915G(dev) || IS_I915GM(dev)) { + pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp); + temp &= ~DEVEN_MCHBAR_EN; + pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG, temp); + } else { + pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp); + temp &= ~1; + pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp); + } + } + + if (dev_priv->mch_res.start) + release_resource(&dev_priv->mch_res); +} + /** * i915_probe_agp - get AGP bootup configuration * @pdev: PCI device @@ -1133,6 +1249,7 @@ static void i915_setup_compression(struct drm_device *dev, int size) /* Leave 1M for line length buffer & misc. */ compressed_fb = drm_mm_search_free(&dev_priv->vram, size, 4096, 0); if (!compressed_fb) { + dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; i915_warn_stolen(dev); return; } @@ -1140,6 +1257,7 @@ static void i915_setup_compression(struct drm_device *dev, int size) compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); if (!compressed_fb) { i915_warn_stolen(dev); + dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; return; } @@ -1450,6 +1568,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) dev->driver->get_vblank_counter = gm45_get_vblank_counter; } + /* Try to make sure MCHBAR is enabled before poking at it */ + intel_setup_mchbar(dev); + i915_gem_load(dev); /* Init HWS */ @@ -1523,6 +1644,8 @@ int i915_driver_unload(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + i915_destroy_error_state(dev); + destroy_workqueue(dev_priv->wq); del_timer_sync(&dev_priv->hangcheck_timer); @@ -1569,6 +1692,8 @@ int i915_driver_unload(struct drm_device *dev) intel_cleanup_overlay(dev); } + intel_teardown_mchbar(dev); + pci_dev_put(dev_priv->bridge_dev); kfree(dev->dev_private); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index cf4cb3e9a0c..742bd8f738c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -571,6 +571,11 @@ static int __init i915_init(void) driver.driver_features &= ~DRIVER_MODESET; #endif + if (!(driver.driver_features & DRIVER_MODESET)) { + driver.suspend = i915_suspend; + driver.resume = i915_resume; + } + return drm_init(&driver); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b99b6a841d9..ec06d4865a5 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -150,7 +150,27 @@ struct drm_i915_error_state { u32 instps; u32 instdone1; u32 seqno; + u64 bbaddr; struct timeval time; + struct drm_i915_error_object { + int page_count; + u32 gtt_offset; + u32 *pages[0]; + } *ringbuffer, *batchbuffer[2]; + struct drm_i915_error_buffer { + size_t size; + u32 name; + u32 seqno; + u32 gtt_offset; + u32 read_domains; + u32 write_domain; + u32 fence_reg; + s32 pinned:2; + u32 tiling:2; + u32 dirty:1; + u32 purgeable:1; + } *active_bo; + u32 active_bo_count; }; struct drm_i915_display_funcs { @@ -192,6 +212,14 @@ struct intel_device_info { u8 cursor_needs_physical : 1; }; +enum no_fbc_reason { + FBC_STOLEN_TOO_SMALL, /* not enough space to hold compressed buffers */ + FBC_UNSUPPORTED_MODE, /* interlace or doublescanned mode */ + FBC_MODE_TOO_LARGE, /* mode too large for compression */ + FBC_BAD_PLANE, /* fbc not supported on plane */ + FBC_NOT_TILED, /* buffer not tiled */ +}; + typedef struct drm_i915_private { struct drm_device *dev; @@ -452,6 +480,7 @@ typedef struct drm_i915_private { u32 savePIPEB_DATA_N1; u32 savePIPEB_LINK_M1; u32 savePIPEB_LINK_N1; + u32 saveMCHBAR_RENDER_STANDBY; struct { struct drm_mm gtt_space; @@ -590,6 +619,14 @@ typedef struct drm_i915_private { int child_dev_num; struct child_device_config *child_dev; struct drm_connector *int_lvds_connector; + + bool mchbar_need_disable; + + u8 cur_delay; + u8 min_delay; + u8 max_delay; + + enum no_fbc_reason no_fbc_reason; } drm_i915_private_t; /** driver private structure attached to each drm_gem_object */ @@ -761,6 +798,7 @@ extern int i965_reset(struct drm_device *dev, u8 flags); /* i915_irq.c */ void i915_hangcheck_elapsed(unsigned long data); +void i915_destroy_error_state(struct drm_device *dev); extern int i915_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int i915_irq_wait(struct drm_device *dev, void *data, @@ -897,7 +935,8 @@ void i915_gem_object_do_bit_17_swizzle(struct drm_gem_object *obj); void i915_gem_object_save_bit_17_swizzle(struct drm_gem_object *obj); bool i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode); -bool i915_obj_fenceable(struct drm_device *dev, struct drm_gem_object *obj); +bool i915_gem_object_fence_offset_ok(struct drm_gem_object *obj, + int tiling_mode); /* i915_gem_debug.c */ void i915_gem_dump_object(struct drm_gem_object *obj, int len, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 9d87d5a41bd..b5df30ca0fa 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2540,6 +2540,12 @@ i915_gem_object_put_fence_reg(struct drm_gem_object *obj) if (obj_priv->fence_reg == I915_FENCE_REG_NONE) return 0; + /* 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); + /* On the i915, GPU access to tiled buffers is via a fence, * therefore we must wait for any outstanding access to complete * before clearing the fence. @@ -2548,12 +2554,12 @@ i915_gem_object_put_fence_reg(struct drm_gem_object *obj) int ret; i915_gem_object_flush_gpu_write_domain(obj); - i915_gem_object_flush_gtt_write_domain(obj); ret = i915_gem_object_wait_rendering(obj); if (ret != 0) return ret; } + i915_gem_object_flush_gtt_write_domain(obj); i915_gem_clear_fence_reg (obj); return 0; @@ -3243,7 +3249,8 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, obj_priv->tiling_mode != I915_TILING_NONE; /* Check fence reg constraints and rebind if necessary */ - if (need_fence && !i915_obj_fenceable(dev, obj)) + if (need_fence && !i915_gem_object_fence_offset_ok(obj, + obj_priv->tiling_mode)) i915_gem_object_unbind(obj); /* Choose the GTT offset for our buffer and put it there. */ @@ -4437,129 +4444,73 @@ i915_gem_evict_from_inactive_list(struct drm_device *dev) return 0; } -int -i915_gem_idle(struct drm_device *dev) +static int +i915_gpu_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - uint32_t seqno, cur_seqno, last_seqno; - int stuck, ret; + bool lists_empty; + uint32_t seqno; - mutex_lock(&dev->struct_mutex); + spin_lock(&dev_priv->mm.active_list_lock); + lists_empty = list_empty(&dev_priv->mm.flushing_list) && + list_empty(&dev_priv->mm.active_list); + spin_unlock(&dev_priv->mm.active_list_lock); - if (dev_priv->mm.suspended || dev_priv->ring.ring_obj == NULL) { - mutex_unlock(&dev->struct_mutex); + if (lists_empty) return 0; - } - - /* Hack! Don't let anybody do execbuf while we don't control the chip. - * We need to replace this with a semaphore, or something. - */ - dev_priv->mm.suspended = 1; - del_timer(&dev_priv->hangcheck_timer); - - /* Cancel the retire work handler, wait for it to finish if running - */ - mutex_unlock(&dev->struct_mutex); - cancel_delayed_work_sync(&dev_priv->mm.retire_work); - mutex_lock(&dev->struct_mutex); - - i915_kernel_lost_context(dev); - /* Flush the GPU along with all non-CPU write domains - */ + /* Flush everything onto the inactive list. */ i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); seqno = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS); - - if (seqno == 0) { - mutex_unlock(&dev->struct_mutex); + if (seqno == 0) return -ENOMEM; - } - - dev_priv->mm.waiting_gem_seqno = seqno; - last_seqno = 0; - stuck = 0; - for (;;) { - cur_seqno = i915_get_gem_seqno(dev); - if (i915_seqno_passed(cur_seqno, seqno)) - break; - if (last_seqno == cur_seqno) { - if (stuck++ > 100) { - DRM_ERROR("hardware wedged\n"); - atomic_set(&dev_priv->mm.wedged, 1); - DRM_WAKEUP(&dev_priv->irq_queue); - break; - } - } - msleep(10); - last_seqno = cur_seqno; - } - dev_priv->mm.waiting_gem_seqno = 0; - - i915_gem_retire_requests(dev); - - spin_lock(&dev_priv->mm.active_list_lock); - if (!atomic_read(&dev_priv->mm.wedged)) { - /* Active and flushing should now be empty as we've - * waited for a sequence higher than any pending execbuffer - */ - WARN_ON(!list_empty(&dev_priv->mm.active_list)); - WARN_ON(!list_empty(&dev_priv->mm.flushing_list)); - /* Request should now be empty as we've also waited - * for the last request in the list - */ - WARN_ON(!list_empty(&dev_priv->mm.request_list)); - } - - /* Empty the active and flushing lists to inactive. If there's - * anything left at this point, it means that we're wedged and - * nothing good's going to happen by leaving them there. So strip - * the GPU domains and just stuff them onto inactive. - */ - while (!list_empty(&dev_priv->mm.active_list)) { - struct drm_gem_object *obj; - uint32_t old_write_domain; - - obj = list_first_entry(&dev_priv->mm.active_list, - struct drm_i915_gem_object, - list)->obj; - old_write_domain = obj->write_domain; - obj->write_domain &= ~I915_GEM_GPU_DOMAINS; - i915_gem_object_move_to_inactive(obj); - trace_i915_gem_object_change_domain(obj, - obj->read_domains, - old_write_domain); - } - spin_unlock(&dev_priv->mm.active_list_lock); + return i915_wait_request(dev, seqno); +} - while (!list_empty(&dev_priv->mm.flushing_list)) { - struct drm_gem_object *obj; - uint32_t old_write_domain; +int +i915_gem_idle(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; - obj = list_first_entry(&dev_priv->mm.flushing_list, - struct drm_i915_gem_object, - list)->obj; - old_write_domain = obj->write_domain; - obj->write_domain &= ~I915_GEM_GPU_DOMAINS; - i915_gem_object_move_to_inactive(obj); + mutex_lock(&dev->struct_mutex); - trace_i915_gem_object_change_domain(obj, - obj->read_domains, - old_write_domain); + if (dev_priv->mm.suspended || dev_priv->ring.ring_obj == NULL) { + mutex_unlock(&dev->struct_mutex); + return 0; } - - /* Move all inactive buffers out of the GTT. */ - ret = i915_gem_evict_from_inactive_list(dev); - WARN_ON(!list_empty(&dev_priv->mm.inactive_list)); + ret = i915_gpu_idle(dev); if (ret) { mutex_unlock(&dev->struct_mutex); return ret; } + /* Under UMS, be paranoid and evict. */ + if (!drm_core_check_feature(dev, DRIVER_MODESET)) { + ret = i915_gem_evict_from_inactive_list(dev); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return ret; + } + } + + /* Hack! Don't let anybody do execbuf while we don't control the chip. + * We need to replace this with a semaphore, or something. + * And not confound mm.suspended! + */ + dev_priv->mm.suspended = 1; + del_timer(&dev_priv->hangcheck_timer); + + i915_kernel_lost_context(dev); i915_gem_cleanup_ringbuffer(dev); + mutex_unlock(&dev->struct_mutex); + /* Cancel the retire work handler, which should be idle now. */ + cancel_delayed_work_sync(&dev_priv->mm.retire_work); + return 0; } @@ -4846,7 +4797,8 @@ i915_gem_load(struct drm_device *dev) spin_unlock(&shrink_list_lock); /* Old X drivers will take 0-2 for front, back, depth buffers */ - dev_priv->fence_reg_start = 3; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + dev_priv->fence_reg_start = 3; if (IS_I965G(dev) || IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) dev_priv->num_fence_regs = 16; diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 137e888427f..20653776965 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -25,8 +25,6 @@ * */ -#include <linux/acpi.h> -#include <linux/pnp.h> #include "linux/string.h" #include "linux/bitops.h" #include "drmP.h" @@ -83,120 +81,6 @@ * to match what the GPU expects. */ -#define MCHBAR_I915 0x44 -#define MCHBAR_I965 0x48 -#define MCHBAR_SIZE (4*4096) - -#define DEVEN_REG 0x54 -#define DEVEN_MCHBAR_EN (1 << 28) - -/* Allocate space for the MCH regs if needed, return nonzero on error */ -static int -intel_alloc_mchbar_resource(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - int reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915; - u32 temp_lo, temp_hi = 0; - u64 mchbar_addr; - int ret = 0; - - if (IS_I965G(dev)) - pci_read_config_dword(dev_priv->bridge_dev, reg + 4, &temp_hi); - pci_read_config_dword(dev_priv->bridge_dev, reg, &temp_lo); - mchbar_addr = ((u64)temp_hi << 32) | temp_lo; - - /* If ACPI doesn't have it, assume we need to allocate it ourselves */ -#ifdef CONFIG_PNP - if (mchbar_addr && - pnp_range_reserved(mchbar_addr, mchbar_addr + MCHBAR_SIZE)) { - ret = 0; - goto out; - } -#endif - - /* Get some space for it */ - ret = pci_bus_alloc_resource(dev_priv->bridge_dev->bus, &dev_priv->mch_res, - MCHBAR_SIZE, MCHBAR_SIZE, - PCIBIOS_MIN_MEM, - 0, pcibios_align_resource, - dev_priv->bridge_dev); - if (ret) { - DRM_DEBUG_DRIVER("failed bus alloc: %d\n", ret); - dev_priv->mch_res.start = 0; - goto out; - } - - if (IS_I965G(dev)) - pci_write_config_dword(dev_priv->bridge_dev, reg + 4, - upper_32_bits(dev_priv->mch_res.start)); - - pci_write_config_dword(dev_priv->bridge_dev, reg, - lower_32_bits(dev_priv->mch_res.start)); -out: - return ret; -} - -/* Setup MCHBAR if possible, return true if we should disable it again */ -static bool -intel_setup_mchbar(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915; - u32 temp; - bool need_disable = false, enabled; - - if (IS_I915G(dev) || IS_I915GM(dev)) { - pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp); - enabled = !!(temp & DEVEN_MCHBAR_EN); - } else { - pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp); - enabled = temp & 1; - } - - /* If it's already enabled, don't have to do anything */ - if (enabled) - goto out; - - if (intel_alloc_mchbar_resource(dev)) - goto out; - - need_disable = true; - - /* Space is allocated or reserved, so enable it. */ - if (IS_I915G(dev) || IS_I915GM(dev)) { - pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG, - temp | DEVEN_MCHBAR_EN); - } else { - pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp); - pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp | 1); - } -out: - return need_disable; -} - -static void -intel_teardown_mchbar(struct drm_device *dev, bool disable) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915; - u32 temp; - - if (disable) { - if (IS_I915G(dev) || IS_I915GM(dev)) { - pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp); - temp &= ~DEVEN_MCHBAR_EN; - pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG, temp); - } else { - pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp); - temp &= ~1; - pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp); - } - } - - if (dev_priv->mch_res.start) - release_resource(&dev_priv->mch_res); -} - /** * Detects bit 6 swizzling of address lookup between IGD access and CPU * access through main memory. @@ -207,7 +91,6 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; - bool need_disable; if (IS_IRONLAKE(dev)) { /* On Ironlake whatever DRAM config, GPU always do @@ -224,9 +107,6 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) } else if (IS_MOBILE(dev)) { uint32_t dcc; - /* Try to make sure MCHBAR is enabled before poking at it */ - need_disable = intel_setup_mchbar(dev); - /* On mobile 9xx chipsets, channel interleave by the CPU is * determined by DCC. For single-channel, neither the CPU * nor the GPU do swizzling. For dual channel interleaved, @@ -266,8 +146,6 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; } - - intel_teardown_mchbar(dev, need_disable); } else { /* The 965, G33, and newer, have a very flexible memory * configuration. It will enable dual-channel mode @@ -302,39 +180,6 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) dev_priv->mm.bit_6_swizzle_y = swizzle_y; } - -/** - * Returns whether an object is currently fenceable. If not, it may need - * to be unbound and have its pitch adjusted. - */ -bool -i915_obj_fenceable(struct drm_device *dev, struct drm_gem_object *obj) -{ - struct drm_i915_gem_object *obj_priv = obj->driver_private; - - if (IS_I965G(dev)) { - /* The 965 can have fences at any page boundary. */ - if (obj->size & 4095) - return false; - return true; - } else if (IS_I9XX(dev)) { - if (obj_priv->gtt_offset & ~I915_FENCE_START_MASK) - return false; - } else { - if (obj_priv->gtt_offset & ~I830_FENCE_START_MASK) - return false; - } - - /* Power of two sized... */ - if (obj->size & (obj->size - 1)) - return false; - - /* Objects must be size aligned as well */ - if (obj_priv->gtt_offset & (obj->size - 1)) - return false; - return true; -} - /* Check pitch constriants for all chips & tiling formats */ bool i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) @@ -391,7 +236,7 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) return true; } -static bool +bool i915_gem_object_fence_offset_ok(struct drm_gem_object *obj, int tiling_mode) { struct drm_device *dev = obj->dev; @@ -491,12 +336,6 @@ 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 a17d6bdfe63..ba1d8314c1c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -269,6 +269,57 @@ static void i915_hotplug_work_func(struct work_struct *work) drm_sysfs_hotplug_event(dev); } +static void i915_handle_rps_change(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 busy_up, busy_down, max_avg, min_avg; + u16 rgvswctl; + u8 new_delay = dev_priv->cur_delay; + + I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS) & ~MEMINT_EVAL_CHG); + busy_up = I915_READ(RCPREVBSYTUPAVG); + busy_down = I915_READ(RCPREVBSYTDNAVG); + max_avg = I915_READ(RCBMAXAVG); + min_avg = I915_READ(RCBMINAVG); + + /* Handle RCS change request from hw */ + if (busy_up > max_avg) { + if (dev_priv->cur_delay != dev_priv->max_delay) + new_delay = dev_priv->cur_delay - 1; + if (new_delay < dev_priv->max_delay) + new_delay = dev_priv->max_delay; + } else if (busy_down < min_avg) { + if (dev_priv->cur_delay != dev_priv->min_delay) + new_delay = dev_priv->cur_delay + 1; + if (new_delay > dev_priv->min_delay) + new_delay = dev_priv->min_delay; + } + + DRM_DEBUG("rps change requested: %d -> %d\n", + dev_priv->cur_delay, new_delay); + + rgvswctl = I915_READ(MEMSWCTL); + if (rgvswctl & MEMCTL_CMD_STS) { + DRM_ERROR("gpu busy, RCS change rejected\n"); + return; /* still busy with another command */ + } + + /* Program the new state */ + rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) | + (new_delay << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM; + I915_WRITE(MEMSWCTL, rgvswctl); + POSTING_READ(MEMSWCTL); + + rgvswctl |= MEMCTL_CMD_STS; + I915_WRITE(MEMSWCTL, rgvswctl); + + dev_priv->cur_delay = new_delay; + + DRM_DEBUG("rps changed\n"); + + return; +} + irqreturn_t ironlake_irq_handler(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -331,6 +382,11 @@ irqreturn_t ironlake_irq_handler(struct drm_device *dev) queue_work(dev_priv->wq, &dev_priv->hotplug_work); } + if (de_iir & DE_PCU_EVENT) { + I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS)); + i915_handle_rps_change(dev); + } + /* should clear PCH hotplug event before clear CPU irq */ I915_WRITE(SDEIIR, pch_iir); I915_WRITE(GTIIR, gt_iir); @@ -376,6 +432,121 @@ static void i915_error_work_func(struct work_struct *work) } } +static struct drm_i915_error_object * +i915_error_object_create(struct drm_device *dev, + struct drm_gem_object *src) +{ + struct drm_i915_error_object *dst; + struct drm_i915_gem_object *src_priv; + int page, page_count; + + if (src == NULL) + return NULL; + + src_priv = src->driver_private; + if (src_priv->pages == NULL) + return NULL; + + page_count = src->size / PAGE_SIZE; + + dst = kmalloc(sizeof(*dst) + page_count * sizeof (u32 *), GFP_ATOMIC); + if (dst == NULL) + return NULL; + + for (page = 0; page < page_count; page++) { + void *s, *d = kmalloc(PAGE_SIZE, GFP_ATOMIC); + if (d == NULL) + goto unwind; + s = kmap_atomic(src_priv->pages[page], KM_USER0); + memcpy(d, s, PAGE_SIZE); + kunmap_atomic(s, KM_USER0); + dst->pages[page] = d; + } + dst->page_count = page_count; + dst->gtt_offset = src_priv->gtt_offset; + + return dst; + +unwind: + while (page--) + kfree(dst->pages[page]); + kfree(dst); + return NULL; +} + +static void +i915_error_object_free(struct drm_i915_error_object *obj) +{ + int page; + + if (obj == NULL) + return; + + for (page = 0; page < obj->page_count; page++) + kfree(obj->pages[page]); + + kfree(obj); +} + +static void +i915_error_state_free(struct drm_device *dev, + struct drm_i915_error_state *error) +{ + i915_error_object_free(error->batchbuffer[0]); + i915_error_object_free(error->batchbuffer[1]); + i915_error_object_free(error->ringbuffer); + kfree(error->active_bo); + kfree(error); +} + +static u32 +i915_get_bbaddr(struct drm_device *dev, u32 *ring) +{ + u32 cmd; + + if (IS_I830(dev) || IS_845G(dev)) + cmd = MI_BATCH_BUFFER; + else if (IS_I965G(dev)) + cmd = (MI_BATCH_BUFFER_START | (2 << 6) | + MI_BATCH_NON_SECURE_I965); + else + cmd = (MI_BATCH_BUFFER_START | (2 << 6)); + + return ring[0] == cmd ? ring[1] : 0; +} + +static u32 +i915_ringbuffer_last_batch(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 head, bbaddr; + u32 *ring; + + /* Locate the current position in the ringbuffer and walk back + * to find the most recently dispatched batch buffer. + */ + bbaddr = 0; + head = I915_READ(PRB0_HEAD) & HEAD_ADDR; + ring = (u32 *)(dev_priv->ring.virtual_start + head); + + while (--ring >= (u32 *)dev_priv->ring.virtual_start) { + bbaddr = i915_get_bbaddr(dev, ring); + if (bbaddr) + break; + } + + if (bbaddr == 0) { + ring = (u32 *)(dev_priv->ring.virtual_start + dev_priv->ring.Size); + while (--ring >= (u32 *)dev_priv->ring.virtual_start) { + bbaddr = i915_get_bbaddr(dev, ring); + if (bbaddr) + break; + } + } + + return bbaddr; +} + /** * i915_capture_error_state - capture an error record for later analysis * @dev: drm device @@ -388,19 +559,26 @@ static void i915_error_work_func(struct work_struct *work) static void i915_capture_error_state(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv; struct drm_i915_error_state *error; + struct drm_gem_object *batchbuffer[2]; unsigned long flags; + u32 bbaddr; + int count; spin_lock_irqsave(&dev_priv->error_lock, flags); - if (dev_priv->first_error) - goto out; + error = dev_priv->first_error; + spin_unlock_irqrestore(&dev_priv->error_lock, flags); + if (error) + return; error = kmalloc(sizeof(*error), GFP_ATOMIC); if (!error) { - DRM_DEBUG_DRIVER("out ot memory, not capturing error state\n"); - goto out; + DRM_DEBUG_DRIVER("out of memory, not capturing error state\n"); + return; } + error->seqno = i915_get_gem_seqno(dev); error->eir = I915_READ(EIR); error->pgtbl_er = I915_READ(PGTBL_ER); error->pipeastat = I915_READ(PIPEASTAT); @@ -411,6 +589,7 @@ static void i915_capture_error_state(struct drm_device *dev) error->ipehr = I915_READ(IPEHR); error->instdone = I915_READ(INSTDONE); error->acthd = I915_READ(ACTHD); + error->bbaddr = 0; } else { error->ipeir = I915_READ(IPEIR_I965); error->ipehr = I915_READ(IPEHR_I965); @@ -418,14 +597,101 @@ static void i915_capture_error_state(struct drm_device *dev) error->instps = I915_READ(INSTPS); error->instdone1 = I915_READ(INSTDONE1); error->acthd = I915_READ(ACTHD_I965); + error->bbaddr = I915_READ64(BB_ADDR); } - do_gettimeofday(&error->time); + bbaddr = i915_ringbuffer_last_batch(dev); + + /* Grab the current batchbuffer, most likely to have crashed. */ + batchbuffer[0] = NULL; + batchbuffer[1] = NULL; + count = 0; + list_for_each_entry(obj_priv, &dev_priv->mm.active_list, list) { + struct drm_gem_object *obj = obj_priv->obj; + + if (batchbuffer[0] == NULL && + bbaddr >= obj_priv->gtt_offset && + bbaddr < obj_priv->gtt_offset + obj->size) + batchbuffer[0] = obj; + + if (batchbuffer[1] == NULL && + error->acthd >= obj_priv->gtt_offset && + error->acthd < obj_priv->gtt_offset + obj->size && + batchbuffer[0] != obj) + batchbuffer[1] = obj; - dev_priv->first_error = error; + count++; + } + + /* We need to copy these to an anonymous buffer as the simplest + * method to avoid being overwritten by userpace. + */ + error->batchbuffer[0] = i915_error_object_create(dev, batchbuffer[0]); + error->batchbuffer[1] = i915_error_object_create(dev, batchbuffer[1]); + + /* Record the ringbuffer */ + error->ringbuffer = i915_error_object_create(dev, dev_priv->ring.ring_obj); + + /* Record buffers on the active list. */ + error->active_bo = NULL; + error->active_bo_count = 0; + + if (count) + error->active_bo = kmalloc(sizeof(*error->active_bo)*count, + GFP_ATOMIC); + + if (error->active_bo) { + int i = 0; + list_for_each_entry(obj_priv, &dev_priv->mm.active_list, list) { + struct drm_gem_object *obj = obj_priv->obj; + + error->active_bo[i].size = obj->size; + error->active_bo[i].name = obj->name; + error->active_bo[i].seqno = obj_priv->last_rendering_seqno; + error->active_bo[i].gtt_offset = obj_priv->gtt_offset; + error->active_bo[i].read_domains = obj->read_domains; + error->active_bo[i].write_domain = obj->write_domain; + error->active_bo[i].fence_reg = obj_priv->fence_reg; + error->active_bo[i].pinned = 0; + if (obj_priv->pin_count > 0) + error->active_bo[i].pinned = 1; + if (obj_priv->user_pin_count > 0) + error->active_bo[i].pinned = -1; + error->active_bo[i].tiling = obj_priv->tiling_mode; + error->active_bo[i].dirty = obj_priv->dirty; + error->active_bo[i].purgeable = obj_priv->madv != I915_MADV_WILLNEED; + + if (++i == count) + break; + } + error->active_bo_count = i; + } -out: + do_gettimeofday(&error->time); + + spin_lock_irqsave(&dev_priv->error_lock, flags); + if (dev_priv->first_error == NULL) { + dev_priv->first_error = error; + error = NULL; + } spin_unlock_irqrestore(&dev_priv->error_lock, flags); + + if (error) + i915_error_state_free(dev, error); +} + +void i915_destroy_error_state(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_error_state *error; + + spin_lock(&dev_priv->error_lock); + error = dev_priv->first_error; + dev_priv->first_error = NULL; + spin_unlock(&dev_priv->error_lock); + + if (error) + i915_error_state_free(dev, error); } /** @@ -1064,6 +1330,13 @@ static int ironlake_irq_postinstall(struct drm_device *dev) I915_WRITE(SDEIER, dev_priv->pch_irq_enable_reg); (void) I915_READ(SDEIER); + if (IS_IRONLAKE_M(dev)) { + /* Clear & enable PCU event interrupts */ + I915_WRITE(DEIIR, DE_PCU_EVENT); + I915_WRITE(DEIER, I915_READ(DEIER) | DE_PCU_EVENT); + ironlake_enable_display_irq(dev_priv, DE_PCU_EVENT); + } + return 0; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index ab1bd2d3d3b..eff8d850a75 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -61,6 +61,7 @@ #define GC_CLOCK_100_200 (1 << 0) #define GC_CLOCK_100_133 (2 << 0) #define GC_CLOCK_166_250 (3 << 0) +#define GCFGC2 0xda #define GCFGC 0xf0 /* 915+ only */ #define GC_LOW_FREQUENCY_ENABLE (1 << 7) #define GC_DISPLAY_CLOCK_190_200_MHZ (0 << 4) @@ -282,7 +283,7 @@ #define I915_PIPE_CONTROL_NOTIFY_INTERRUPT (1<<18) #define I915_DISPLAY_PORT_INTERRUPT (1<<17) #define I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT (1<<15) -#define I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT (1<<14) +#define I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT (1<<14) /* p-state */ #define I915_HWB_OOM_INTERRUPT (1<<13) #define I915_SYNC_STATUS_INTERRUPT (1<<12) #define I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT (1<<11) @@ -306,11 +307,14 @@ #define I915_ERROR_MEMORY_REFRESH (1<<1) #define I915_ERROR_INSTRUCTION (1<<0) #define INSTPM 0x020c0 +#define INSTPM_SELF_EN (1<<12) /* 915GM only */ #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 FW_BLC_SELF_EN_MASK (1<<31) +#define FW_BLC_SELF_FIFO_MASK (1<<16) /* 945 only */ +#define FW_BLC_SELF_EN (1<<15) /* 945 only */ #define MM_BURST_LENGTH 0x00700000 #define MM_FIFO_WATERMARK 0x0001F000 #define LM_BURST_LENGTH 0x00000700 @@ -324,6 +328,7 @@ #define CM0_COLOR_EVICT_DISABLE (1<<3) #define CM0_DEPTH_WRITE_DISABLE (1<<1) #define CM0_RC_OP_FLUSH_DISABLE (1<<0) +#define BB_ADDR 0x02140 /* 8 bytes */ #define GFX_FLSH_CNTL 0x02170 /* 915+ only */ @@ -784,10 +789,144 @@ #define CLKCFG_MEM_800 (3 << 4) #define CLKCFG_MEM_MASK (7 << 4) -/** GM965 GM45 render standby register */ -#define MCHBAR_RENDER_STANDBY 0x111B8 +#define CRSTANDVID 0x11100 +#define PXVFREQ_BASE 0x11110 /* P[0-15]VIDFREQ (0x1114c) (Ironlake) */ +#define PXVFREQ_PX_MASK 0x7f000000 +#define PXVFREQ_PX_SHIFT 24 +#define VIDFREQ_BASE 0x11110 +#define VIDFREQ1 0x11110 /* VIDFREQ1-4 (0x1111c) (Cantiga) */ +#define VIDFREQ2 0x11114 +#define VIDFREQ3 0x11118 +#define VIDFREQ4 0x1111c +#define VIDFREQ_P0_MASK 0x1f000000 +#define VIDFREQ_P0_SHIFT 24 +#define VIDFREQ_P0_CSCLK_MASK 0x00f00000 +#define VIDFREQ_P0_CSCLK_SHIFT 20 +#define VIDFREQ_P0_CRCLK_MASK 0x000f0000 +#define VIDFREQ_P0_CRCLK_SHIFT 16 +#define VIDFREQ_P1_MASK 0x00001f00 +#define VIDFREQ_P1_SHIFT 8 +#define VIDFREQ_P1_CSCLK_MASK 0x000000f0 +#define VIDFREQ_P1_CSCLK_SHIFT 4 +#define VIDFREQ_P1_CRCLK_MASK 0x0000000f +#define INTTOEXT_BASE_ILK 0x11300 +#define INTTOEXT_BASE 0x11120 /* INTTOEXT1-8 (0x1113c) */ +#define INTTOEXT_MAP3_SHIFT 24 +#define INTTOEXT_MAP3_MASK (0x1f << INTTOEXT_MAP3_SHIFT) +#define INTTOEXT_MAP2_SHIFT 16 +#define INTTOEXT_MAP2_MASK (0x1f << INTTOEXT_MAP2_SHIFT) +#define INTTOEXT_MAP1_SHIFT 8 +#define INTTOEXT_MAP1_MASK (0x1f << INTTOEXT_MAP1_SHIFT) +#define INTTOEXT_MAP0_SHIFT 0 +#define INTTOEXT_MAP0_MASK (0x1f << INTTOEXT_MAP0_SHIFT) +#define MEMSWCTL 0x11170 /* Ironlake only */ +#define MEMCTL_CMD_MASK 0xe000 +#define MEMCTL_CMD_SHIFT 13 +#define MEMCTL_CMD_RCLK_OFF 0 +#define MEMCTL_CMD_RCLK_ON 1 +#define MEMCTL_CMD_CHFREQ 2 +#define MEMCTL_CMD_CHVID 3 +#define MEMCTL_CMD_VMMOFF 4 +#define MEMCTL_CMD_VMMON 5 +#define MEMCTL_CMD_STS (1<<12) /* write 1 triggers command, clears + when command complete */ +#define MEMCTL_FREQ_MASK 0x0f00 /* jitter, from 0-15 */ +#define MEMCTL_FREQ_SHIFT 8 +#define MEMCTL_SFCAVM (1<<7) +#define MEMCTL_TGT_VID_MASK 0x007f +#define MEMIHYST 0x1117c +#define MEMINTREN 0x11180 /* 16 bits */ +#define MEMINT_RSEXIT_EN (1<<8) +#define MEMINT_CX_SUPR_EN (1<<7) +#define MEMINT_CONT_BUSY_EN (1<<6) +#define MEMINT_AVG_BUSY_EN (1<<5) +#define MEMINT_EVAL_CHG_EN (1<<4) +#define MEMINT_MON_IDLE_EN (1<<3) +#define MEMINT_UP_EVAL_EN (1<<2) +#define MEMINT_DOWN_EVAL_EN (1<<1) +#define MEMINT_SW_CMD_EN (1<<0) +#define MEMINTRSTR 0x11182 /* 16 bits */ +#define MEM_RSEXIT_MASK 0xc000 +#define MEM_RSEXIT_SHIFT 14 +#define MEM_CONT_BUSY_MASK 0x3000 +#define MEM_CONT_BUSY_SHIFT 12 +#define MEM_AVG_BUSY_MASK 0x0c00 +#define MEM_AVG_BUSY_SHIFT 10 +#define MEM_EVAL_CHG_MASK 0x0300 +#define MEM_EVAL_BUSY_SHIFT 8 +#define MEM_MON_IDLE_MASK 0x00c0 +#define MEM_MON_IDLE_SHIFT 6 +#define MEM_UP_EVAL_MASK 0x0030 +#define MEM_UP_EVAL_SHIFT 4 +#define MEM_DOWN_EVAL_MASK 0x000c +#define MEM_DOWN_EVAL_SHIFT 2 +#define MEM_SW_CMD_MASK 0x0003 +#define MEM_INT_STEER_GFX 0 +#define MEM_INT_STEER_CMR 1 +#define MEM_INT_STEER_SMI 2 +#define MEM_INT_STEER_SCI 3 +#define MEMINTRSTS 0x11184 +#define MEMINT_RSEXIT (1<<7) +#define MEMINT_CONT_BUSY (1<<6) +#define MEMINT_AVG_BUSY (1<<5) +#define MEMINT_EVAL_CHG (1<<4) +#define MEMINT_MON_IDLE (1<<3) +#define MEMINT_UP_EVAL (1<<2) +#define MEMINT_DOWN_EVAL (1<<1) +#define MEMINT_SW_CMD (1<<0) +#define MEMMODECTL 0x11190 +#define MEMMODE_BOOST_EN (1<<31) +#define MEMMODE_BOOST_FREQ_MASK 0x0f000000 /* jitter for boost, 0-15 */ +#define MEMMODE_BOOST_FREQ_SHIFT 24 +#define MEMMODE_IDLE_MODE_MASK 0x00030000 +#define MEMMODE_IDLE_MODE_SHIFT 16 +#define MEMMODE_IDLE_MODE_EVAL 0 +#define MEMMODE_IDLE_MODE_CONT 1 +#define MEMMODE_HWIDLE_EN (1<<15) +#define MEMMODE_SWMODE_EN (1<<14) +#define MEMMODE_RCLK_GATE (1<<13) +#define MEMMODE_HW_UPDATE (1<<12) +#define MEMMODE_FSTART_MASK 0x00000f00 /* starting jitter, 0-15 */ +#define MEMMODE_FSTART_SHIFT 8 +#define MEMMODE_FMAX_MASK 0x000000f0 /* max jitter, 0-15 */ +#define MEMMODE_FMAX_SHIFT 4 +#define MEMMODE_FMIN_MASK 0x0000000f /* min jitter, 0-15 */ +#define RCBMAXAVG 0x1119c +#define MEMSWCTL2 0x1119e /* Cantiga only */ +#define SWMEMCMD_RENDER_OFF (0 << 13) +#define SWMEMCMD_RENDER_ON (1 << 13) +#define SWMEMCMD_SWFREQ (2 << 13) +#define SWMEMCMD_TARVID (3 << 13) +#define SWMEMCMD_VRM_OFF (4 << 13) +#define SWMEMCMD_VRM_ON (5 << 13) +#define CMDSTS (1<<12) +#define SFCAVM (1<<11) +#define SWFREQ_MASK 0x0380 /* P0-7 */ +#define SWFREQ_SHIFT 7 +#define TARVID_MASK 0x001f +#define MEMSTAT_CTG 0x111a0 +#define RCBMINAVG 0x111a0 +#define RCUPEI 0x111b0 +#define RCDNEI 0x111b4 +#define MCHBAR_RENDER_STANDBY 0x111b8 #define RCX_SW_EXIT (1<<23) #define RSX_STATUS_MASK 0x00700000 +#define VIDCTL 0x111c0 +#define VIDSTS 0x111c8 +#define VIDSTART 0x111cc /* 8 bits */ +#define MEMSTAT_ILK 0x111f8 +#define MEMSTAT_VID_MASK 0x7f00 +#define MEMSTAT_VID_SHIFT 8 +#define MEMSTAT_PSTATE_MASK 0x00f8 +#define MEMSTAT_PSTATE_SHIFT 3 +#define MEMSTAT_MON_ACTV (1<<2) +#define MEMSTAT_SRC_CTL_MASK 0x0003 +#define MEMSTAT_SRC_CTL_CORE 0 +#define MEMSTAT_SRC_CTL_TRB 1 +#define MEMSTAT_SRC_CTL_THM 2 +#define MEMSTAT_SRC_CTL_STDBY 3 +#define RCPREVBSYTUPAVG 0x113b8 +#define RCPREVBSYTDNAVG 0x113bc #define PEG_BAND_GAP_DATA 0x14d68 /* diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index a3b90c9561d..ac0d1a73ac2 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -682,6 +682,8 @@ void i915_restore_display(struct drm_device *dev) I915_WRITE(PCH_PP_OFF_DELAYS, dev_priv->savePP_OFF_DELAYS); I915_WRITE(PCH_PP_DIVISOR, dev_priv->savePP_DIVISOR); I915_WRITE(PCH_PP_CONTROL, dev_priv->savePP_CONTROL); + I915_WRITE(MCHBAR_RENDER_STANDBY, + dev_priv->saveMCHBAR_RENDER_STANDBY); } else { I915_WRITE(PFIT_PGM_RATIOS, dev_priv->savePFIT_PGM_RATIOS); I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL); @@ -745,11 +747,16 @@ int i915_save_state(struct drm_device *dev) dev_priv->saveGTIMR = I915_READ(GTIMR); dev_priv->saveFDI_RXA_IMR = I915_READ(FDI_RXA_IMR); dev_priv->saveFDI_RXB_IMR = I915_READ(FDI_RXB_IMR); + dev_priv->saveMCHBAR_RENDER_STANDBY = + I915_READ(MCHBAR_RENDER_STANDBY); } else { dev_priv->saveIER = I915_READ(IER); dev_priv->saveIMR = I915_READ(IMR); } + if (IS_IRONLAKE_M(dev)) + ironlake_disable_drps(dev); + /* Cache mode state */ dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0); @@ -820,6 +827,9 @@ int i915_restore_state(struct drm_device *dev) /* Clock gating state */ intel_init_clock_gating(dev); + if (IS_IRONLAKE_M(dev)) + ironlake_enable_drps(dev); + /* Cache mode state */ I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c8fd15f146a..1b5cd833bc7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1188,25 +1188,30 @@ static void intel_update_fbc(struct drm_crtc *crtc, if (intel_fb->obj->size > dev_priv->cfb_size) { DRM_DEBUG_KMS("framebuffer too large, disabling " "compression\n"); + dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; goto out_disable; } if ((mode->flags & DRM_MODE_FLAG_INTERLACE) || (mode->flags & DRM_MODE_FLAG_DBLSCAN)) { DRM_DEBUG_KMS("mode incompatible with compression, " "disabling\n"); + dev_priv->no_fbc_reason = FBC_UNSUPPORTED_MODE; goto out_disable; } if ((mode->hdisplay > 2048) || (mode->vdisplay > 1536)) { DRM_DEBUG_KMS("mode too large for compression, disabling\n"); + dev_priv->no_fbc_reason = FBC_MODE_TOO_LARGE; goto out_disable; } if ((IS_I915GM(dev) || IS_I945GM(dev)) && plane != 0) { DRM_DEBUG_KMS("plane not 0, disabling compression\n"); + dev_priv->no_fbc_reason = FBC_BAD_PLANE; goto out_disable; } if (obj_priv->tiling_mode != I915_TILING_X) { DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n"); + dev_priv->no_fbc_reason = FBC_NOT_TILED; goto out_disable; } @@ -2757,11 +2762,22 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock, srwm = total_size - sr_entries; if (srwm < 0) srwm = 1; - I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f)); + + if (IS_I945G(dev) || IS_I945GM(dev)) + I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_FIFO_MASK | (srwm & 0xff)); + else if (IS_I915GM(dev)) { + /* 915M has a smaller SRWM field */ + I915_WRITE(FW_BLC_SELF, srwm & 0x3f); + I915_WRITE(INSTPM, I915_READ(INSTPM) | INSTPM_SELF_EN); + } } else { /* Turn off self refresh if both pipes are enabled */ - I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF) - & ~FW_BLC_SELF_EN); + if (IS_I945G(dev) || IS_I945GM(dev)) { + I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF) + & ~FW_BLC_SELF_EN); + } else if (IS_I915GM(dev)) { + I915_WRITE(INSTPM, I915_READ(INSTPM) & ~INSTPM_SELF_EN); + } } DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n", @@ -4010,6 +4026,11 @@ static void intel_idle_update(struct work_struct *work) mutex_lock(&dev->struct_mutex); + if (IS_I945G(dev) || IS_I945GM(dev)) { + DRM_DEBUG_DRIVER("enable memory self refresh on 945\n"); + I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN_MASK | FW_BLC_SELF_EN); + } + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { /* Skip inactive CRTCs */ if (!crtc->fb) @@ -4043,9 +4064,17 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj) if (!drm_core_check_feature(dev, DRIVER_MODESET)) return; - if (!dev_priv->busy) + if (!dev_priv->busy) { + if (IS_I945G(dev) || IS_I945GM(dev)) { + u32 fw_blc_self; + + DRM_DEBUG_DRIVER("disable memory self refresh on 945\n"); + fw_blc_self = I915_READ(FW_BLC_SELF); + fw_blc_self &= ~FW_BLC_SELF_EN; + I915_WRITE(FW_BLC_SELF, fw_blc_self | FW_BLC_SELF_EN_MASK); + } dev_priv->busy = true; - else + } else mod_timer(&dev_priv->idle_timer, jiffies + msecs_to_jiffies(GPU_IDLE_TIMEOUT)); @@ -4057,6 +4086,14 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj) intel_fb = to_intel_framebuffer(crtc->fb); if (intel_fb->obj == obj) { if (!intel_crtc->busy) { + if (IS_I945G(dev) || IS_I945GM(dev)) { + u32 fw_blc_self; + + DRM_DEBUG_DRIVER("disable memory self refresh on 945\n"); + fw_blc_self = I915_READ(FW_BLC_SELF); + fw_blc_self &= ~FW_BLC_SELF_EN; + I915_WRITE(FW_BLC_SELF, fw_blc_self | FW_BLC_SELF_EN_MASK); + } /* Non-busy -> busy, upclock */ intel_increase_pllclock(crtc, true); intel_crtc->busy = true; @@ -4586,6 +4623,91 @@ err_unref: return NULL; } +void ironlake_enable_drps(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 rgvmodectl = I915_READ(MEMMODECTL), rgvswctl; + u8 fmax, fmin, fstart, vstart; + int i = 0; + + /* 100ms RC evaluation intervals */ + I915_WRITE(RCUPEI, 100000); + I915_WRITE(RCDNEI, 100000); + + /* Set max/min thresholds to 90ms and 80ms respectively */ + I915_WRITE(RCBMAXAVG, 90000); + I915_WRITE(RCBMINAVG, 80000); + + I915_WRITE(MEMIHYST, 1); + + /* Set up min, max, and cur for interrupt handling */ + fmax = (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT; + fmin = (rgvmodectl & MEMMODE_FMIN_MASK); + fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >> + MEMMODE_FSTART_SHIFT; + vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >> + PXVFREQ_PX_SHIFT; + + dev_priv->max_delay = fstart; /* can't go to fmax w/o IPS */ + dev_priv->min_delay = fmin; + dev_priv->cur_delay = fstart; + + I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN); + + /* + * Interrupts will be enabled in ironlake_irq_postinstall + */ + + I915_WRITE(VIDSTART, vstart); + POSTING_READ(VIDSTART); + + rgvmodectl |= MEMMODE_SWMODE_EN; + I915_WRITE(MEMMODECTL, rgvmodectl); + + while (I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) { + if (i++ > 100) { + DRM_ERROR("stuck trying to change perf mode\n"); + break; + } + msleep(1); + } + msleep(1); + + rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) | + (fstart << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM; + I915_WRITE(MEMSWCTL, rgvswctl); + POSTING_READ(MEMSWCTL); + + rgvswctl |= MEMCTL_CMD_STS; + I915_WRITE(MEMSWCTL, rgvswctl); +} + +void ironlake_disable_drps(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 rgvswctl; + u8 fstart; + + /* Ack interrupts, disable EFC interrupt */ + I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN); + I915_WRITE(MEMINTRSTS, MEMINT_EVAL_CHG); + I915_WRITE(DEIER, I915_READ(DEIER) & ~DE_PCU_EVENT); + I915_WRITE(DEIIR, DE_PCU_EVENT); + I915_WRITE(DEIMR, I915_READ(DEIMR) | DE_PCU_EVENT); + + /* Go back to the starting frequency */ + fstart = (I915_READ(MEMMODECTL) & MEMMODE_FSTART_MASK) >> + MEMMODE_FSTART_SHIFT; + rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) | + (fstart << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM; + I915_WRITE(MEMSWCTL, rgvswctl); + msleep(1); + rgvswctl |= MEMCTL_CMD_STS; + I915_WRITE(MEMSWCTL, rgvswctl); + msleep(1); + +} + void intel_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -4769,11 +4891,6 @@ void intel_modeset_init(struct drm_device *dev) DRM_DEBUG_KMS("%d display pipe%s available.\n", num_pipe, num_pipe > 1 ? "s" : ""); - if (IS_I85X(dev)) - pci_read_config_word(dev->pdev, HPLLCC, &dev_priv->orig_clock); - else if (IS_I9XX(dev) || IS_G4X(dev)) - pci_read_config_word(dev->pdev, GCFGC, &dev_priv->orig_clock); - for (i = 0; i < num_pipe; i++) { intel_crtc_init(dev, i); } @@ -4782,6 +4899,9 @@ void intel_modeset_init(struct drm_device *dev) intel_init_clock_gating(dev); + if (IS_IRONLAKE_M(dev)) + ironlake_enable_drps(dev); + INIT_WORK(&dev_priv->idle_work, intel_idle_update); setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, (unsigned long)dev); @@ -4829,6 +4949,9 @@ void intel_modeset_cleanup(struct drm_device *dev) drm_gem_object_unreference(dev_priv->pwrctx); } + if (IS_IRONLAKE_M(dev)) + ironlake_disable_drps(dev); + mutex_unlock(&dev->struct_mutex); drm_mode_config_cleanup(dev); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a51573da1ff..3a467ca5785 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -209,6 +209,8 @@ extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, int regno); extern void intel_init_clock_gating(struct drm_device *dev); +extern void ironlake_enable_drps(struct drm_device *dev); +extern void ironlake_disable_drps(struct drm_device *dev); extern int intel_framebuffer_create(struct drm_device *dev, struct drm_mode_fb_cmd *mode_cmd, diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index c2e8a45780d..93031a75d11 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -655,8 +655,15 @@ static const struct dmi_system_id bad_lid_status[] = { */ static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector) { + struct drm_device *dev = connector->dev; enum drm_connector_status status = connector_status_connected; + /* ACPI lid methods were generally unreliable in this generation, so + * don't even bother. + */ + if (IS_I8XX(dev)) + return connector_status_connected; + if (!dmi_check_system(bad_lid_status) && !acpi_lid_open()) status = connector_status_disconnected; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 1b50d61c5aa..c3fa406912b 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -199,16 +199,11 @@ static struct overlay_registers *intel_overlay_map_regs_atomic(struct intel_over static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay) { - struct drm_device *dev = overlay->dev; - drm_i915_private_t *dev_priv = dev->dev_private; - if (OVERLAY_NONPHYSICAL(overlay->dev)) io_mapping_unmap_atomic(overlay->virt_addr); overlay->virt_addr = NULL; - I915_READ(OVADD); /* flush wc cashes */ - return; } @@ -225,9 +220,7 @@ static int intel_overlay_on(struct intel_overlay *overlay) overlay->active = 1; overlay->hw_wedged = NEEDS_WAIT_FOR_FLIP; - BEGIN_LP_RING(6); - OUT_RING(MI_FLUSH); - OUT_RING(MI_NOOP); + BEGIN_LP_RING(4); OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_ON); OUT_RING(overlay->flip_addr | OFC_UPDATE); OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); @@ -267,9 +260,7 @@ static void intel_overlay_continue(struct intel_overlay *overlay, if (tmp & (1 << 17)) DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp); - BEGIN_LP_RING(4); - OUT_RING(MI_FLUSH); - OUT_RING(MI_NOOP); + BEGIN_LP_RING(2); OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); OUT_RING(flip_addr); ADVANCE_LP_RING(); @@ -338,9 +329,7 @@ static int intel_overlay_off(struct intel_overlay *overlay) /* wait for overlay to go idle */ overlay->hw_wedged = SWITCH_OFF_STAGE_1; - BEGIN_LP_RING(6); - OUT_RING(MI_FLUSH); - OUT_RING(MI_NOOP); + BEGIN_LP_RING(4); OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); OUT_RING(flip_addr); OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); @@ -358,9 +347,7 @@ static int intel_overlay_off(struct intel_overlay *overlay) /* turn overlay off */ overlay->hw_wedged = SWITCH_OFF_STAGE_2; - BEGIN_LP_RING(6); - OUT_RING(MI_FLUSH); - OUT_RING(MI_NOOP); + BEGIN_LP_RING(4); OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF); OUT_RING(flip_addr); OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); @@ -435,9 +422,7 @@ int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay, overlay->hw_wedged = SWITCH_OFF_STAGE_2; - BEGIN_LP_RING(6); - OUT_RING(MI_FLUSH); - OUT_RING(MI_NOOP); + BEGIN_LP_RING(4); OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF); OUT_RING(flip_addr); OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); |