diff options
Diffstat (limited to 'drivers/gpu/drm/i915')
-rw-r--r-- | drivers/gpu/drm/i915/i915_debugfs.c | 253 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_dma.c | 326 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.c | 27 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 69 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem.c | 430 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem_tiling.c | 169 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 313 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_reg.h | 170 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_suspend.c | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_bios.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_crt.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 216 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_dp.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_fb.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_hdmi.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_i2c.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_lvds.c | 41 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_overlay.c | 29 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_sdvo.c | 23 |
20 files changed, 1545 insertions, 564 deletions
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a894ade03093..1376dfe44c95 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -162,7 +162,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; - if (!IS_IRONLAKE(dev)) { + if (!HAS_PCH_SPLIT(dev)) { seq_printf(m, "Interrupt enable: %08x\n", I915_READ(IER)); seq_printf(m, "Interrupt identity: %08x\n", @@ -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 2307f98349f7..8bfc0bbf13e6 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -35,6 +35,9 @@ #include "i915_drv.h" #include "i915_trace.h" #include <linux/vgaarb.h> +#include <linux/acpi.h> +#include <linux/pnp.h> +#include <linux/vga_switcheroo.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 +936,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 @@ -978,59 +1095,123 @@ static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size, * Some of the preallocated space is taken by the GTT * and popup. GTT is 1K per MB of aperture size, and popup is 4K. */ - if (IS_G4X(dev) || IS_PINEVIEW(dev) || IS_IRONLAKE(dev)) + if (IS_G4X(dev) || IS_PINEVIEW(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev)) overhead = 4096; else overhead = (*aperture_size / 1024) + 4096; - switch (tmp & INTEL_GMCH_GMS_MASK) { - case INTEL_855_GMCH_GMS_DISABLED: - DRM_ERROR("video memory is disabled\n"); - return -1; - case INTEL_855_GMCH_GMS_STOLEN_1M: - stolen = 1 * 1024 * 1024; - break; - case INTEL_855_GMCH_GMS_STOLEN_4M: - stolen = 4 * 1024 * 1024; - break; - case INTEL_855_GMCH_GMS_STOLEN_8M: - stolen = 8 * 1024 * 1024; - break; - case INTEL_855_GMCH_GMS_STOLEN_16M: - stolen = 16 * 1024 * 1024; - break; - case INTEL_855_GMCH_GMS_STOLEN_32M: - stolen = 32 * 1024 * 1024; - break; - case INTEL_915G_GMCH_GMS_STOLEN_48M: - stolen = 48 * 1024 * 1024; - break; - case INTEL_915G_GMCH_GMS_STOLEN_64M: - stolen = 64 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_128M: - stolen = 128 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_256M: - stolen = 256 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_96M: - stolen = 96 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_160M: - stolen = 160 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_224M: - stolen = 224 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_352M: - stolen = 352 * 1024 * 1024; - break; - default: - DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n", - tmp & INTEL_GMCH_GMS_MASK); - return -1; + if (IS_GEN6(dev)) { + /* SNB has memory control reg at 0x50.w */ + pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &tmp); + + switch (tmp & SNB_GMCH_GMS_STOLEN_MASK) { + case INTEL_855_GMCH_GMS_DISABLED: + DRM_ERROR("video memory is disabled\n"); + return -1; + case SNB_GMCH_GMS_STOLEN_32M: + stolen = 32 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_64M: + stolen = 64 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_96M: + stolen = 96 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_128M: + stolen = 128 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_160M: + stolen = 160 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_192M: + stolen = 192 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_224M: + stolen = 224 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_256M: + stolen = 256 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_288M: + stolen = 288 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_320M: + stolen = 320 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_352M: + stolen = 352 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_384M: + stolen = 384 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_416M: + stolen = 416 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_448M: + stolen = 448 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_480M: + stolen = 480 * 1024 * 1024; + break; + case SNB_GMCH_GMS_STOLEN_512M: + stolen = 512 * 1024 * 1024; + break; + default: + DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n", + tmp & SNB_GMCH_GMS_STOLEN_MASK); + return -1; + } + } else { + switch (tmp & INTEL_GMCH_GMS_MASK) { + case INTEL_855_GMCH_GMS_DISABLED: + DRM_ERROR("video memory is disabled\n"); + return -1; + case INTEL_855_GMCH_GMS_STOLEN_1M: + stolen = 1 * 1024 * 1024; + break; + case INTEL_855_GMCH_GMS_STOLEN_4M: + stolen = 4 * 1024 * 1024; + break; + case INTEL_855_GMCH_GMS_STOLEN_8M: + stolen = 8 * 1024 * 1024; + break; + case INTEL_855_GMCH_GMS_STOLEN_16M: + stolen = 16 * 1024 * 1024; + break; + case INTEL_855_GMCH_GMS_STOLEN_32M: + stolen = 32 * 1024 * 1024; + break; + case INTEL_915G_GMCH_GMS_STOLEN_48M: + stolen = 48 * 1024 * 1024; + break; + case INTEL_915G_GMCH_GMS_STOLEN_64M: + stolen = 64 * 1024 * 1024; + break; + case INTEL_GMCH_GMS_STOLEN_128M: + stolen = 128 * 1024 * 1024; + break; + case INTEL_GMCH_GMS_STOLEN_256M: + stolen = 256 * 1024 * 1024; + break; + case INTEL_GMCH_GMS_STOLEN_96M: + stolen = 96 * 1024 * 1024; + break; + case INTEL_GMCH_GMS_STOLEN_160M: + stolen = 160 * 1024 * 1024; + break; + case INTEL_GMCH_GMS_STOLEN_224M: + stolen = 224 * 1024 * 1024; + break; + case INTEL_GMCH_GMS_STOLEN_352M: + stolen = 352 * 1024 * 1024; + break; + default: + DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n", + tmp & INTEL_GMCH_GMS_MASK); + return -1; + } } + *preallocated_size = stolen - overhead; *start = overhead; @@ -1064,7 +1245,7 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev, int gtt_offset, gtt_size; if (IS_I965G(dev)) { - if (IS_G4X(dev) || IS_IRONLAKE(dev)) { + if (IS_G4X(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev)) { gtt_offset = 2*1024*1024; gtt_size = 2*1024*1024; } else { @@ -1133,6 +1314,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 +1322,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; } @@ -1199,6 +1382,32 @@ static unsigned int i915_vga_set_decode(void *cookie, bool state) return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; } +static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; + if (state == VGA_SWITCHEROO_ON) { + printk(KERN_INFO "i915: switched off\n"); + /* i915 resume handler doesn't set to D0 */ + pci_set_power_state(dev->pdev, PCI_D0); + i915_resume(dev); + } else { + printk(KERN_ERR "i915: switched off\n"); + i915_suspend(dev, pmm); + } +} + +static bool i915_switcheroo_can_switch(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + bool can_switch; + + spin_lock(&dev->count_lock); + can_switch = (dev->open_count == 0); + spin_unlock(&dev->count_lock); + return can_switch; +} + static int i915_load_modeset_init(struct drm_device *dev, unsigned long prealloc_start, unsigned long prealloc_size, @@ -1260,6 +1469,12 @@ static int i915_load_modeset_init(struct drm_device *dev, if (ret) goto destroy_ringbuffer; + ret = vga_switcheroo_register_client(dev->pdev, + i915_switcheroo_set_state, + i915_switcheroo_can_switch); + if (ret) + goto destroy_ringbuffer; + intel_modeset_init(dev); ret = drm_irq_install(dev); @@ -1281,7 +1496,9 @@ static int i915_load_modeset_init(struct drm_device *dev, return 0; destroy_ringbuffer: + mutex_lock(&dev->struct_mutex); i915_gem_cleanup_ringbuffer(dev); + mutex_unlock(&dev->struct_mutex); out: return ret; } @@ -1445,11 +1662,14 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) 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_IRONLAKE(dev)) { + if (IS_G4X(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev)) { dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */ 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 +1743,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); @@ -1544,6 +1766,7 @@ int i915_driver_unload(struct drm_device *dev) dev_priv->child_dev_num = 0; } drm_irq_uninstall(dev); + vga_switcheroo_unregister_client(dev->pdev); vga_client_register(dev->pdev, NULL, NULL, NULL); } @@ -1569,6 +1792,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); @@ -1611,6 +1836,7 @@ void i915_driver_lastclose(struct drm_device * dev) if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) { drm_fb_helper_restore(); + vga_switcheroo_process_delayed_switch(); return; } diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index cf4cb3e9a0c2..1b2e95455c05 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -49,6 +49,7 @@ unsigned int i915_lvds_downclock = 0; module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400); static struct drm_driver driver; +extern int intel_agp_enabled; #define INTEL_VGA_DEVICE(id, info) { \ .class = PCI_CLASS_DISPLAY_VGA << 8, \ @@ -136,6 +137,16 @@ const static struct intel_device_info intel_ironlake_m_info = { .has_hotplug = 1, }; +const static struct intel_device_info intel_sandybridge_d_info = { + .is_i965g = 1, .is_i9xx = 1, .need_gfx_hws = 1, + .has_hotplug = 1, +}; + +const static struct intel_device_info intel_sandybridge_m_info = { + .is_i965g = 1, .is_mobile = 1, .is_i9xx = 1, .need_gfx_hws = 1, + .has_hotplug = 1, +}; + const static struct pci_device_id pciidlist[] = { INTEL_VGA_DEVICE(0x3577, &intel_i830_info), INTEL_VGA_DEVICE(0x2562, &intel_845g_info), @@ -167,6 +178,8 @@ const static struct pci_device_id pciidlist[] = { INTEL_VGA_DEVICE(0xa011, &intel_pineview_info), INTEL_VGA_DEVICE(0x0042, &intel_ironlake_d_info), INTEL_VGA_DEVICE(0x0046, &intel_ironlake_m_info), + INTEL_VGA_DEVICE(0x0102, &intel_sandybridge_d_info), + INTEL_VGA_DEVICE(0x0106, &intel_sandybridge_m_info), {0, 0, 0} }; @@ -201,7 +214,7 @@ static int i915_drm_freeze(struct drm_device *dev) return 0; } -static int i915_suspend(struct drm_device *dev, pm_message_t state) +int i915_suspend(struct drm_device *dev, pm_message_t state) { int error; @@ -255,7 +268,7 @@ static int i915_drm_thaw(struct drm_device *dev) return error; } -static int i915_resume(struct drm_device *dev) +int i915_resume(struct drm_device *dev) { if (pci_enable_device(dev->pdev)) return -EIO; @@ -546,6 +559,11 @@ static struct drm_driver driver = { static int __init i915_init(void) { + if (!intel_agp_enabled) { + DRM_ERROR("drm/i915 can't work without intel_agp module!\n"); + return -ENODEV; + } + driver.num_ioctls = i915_max_ioctl; i915_gem_shrinker_init(); @@ -571,6 +589,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 b99b6a841d95..979439cfb827 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 */ @@ -736,6 +773,8 @@ extern unsigned int i915_fbpercrtc; extern unsigned int i915_powersave; extern unsigned int i915_lvds_downclock; +extern int i915_suspend(struct drm_device *dev, pm_message_t state); +extern int i915_resume(struct drm_device *dev); extern void i915_save_display(struct drm_device *dev); extern void i915_restore_display(struct drm_device *dev); extern int i915_master_create(struct drm_device *dev, struct drm_master *master); @@ -761,6 +800,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 +937,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, @@ -1026,7 +1067,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define IS_845G(dev) ((dev)->pci_device == 0x2562) #define IS_I85X(dev) ((dev)->pci_device == 0x3582) #define IS_I865G(dev) ((dev)->pci_device == 0x2572) -#define IS_I8XX(dev) (INTEL_INFO(dev)->is_i8xx) +#define IS_GEN2(dev) (INTEL_INFO(dev)->is_i8xx) #define IS_I915G(dev) (INTEL_INFO(dev)->is_i915g) #define IS_I915GM(dev) ((dev)->pci_device == 0x2592) #define IS_I945G(dev) ((dev)->pci_device == 0x2772) @@ -1045,8 +1086,29 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define IS_I9XX(dev) (INTEL_INFO(dev)->is_i9xx) #define IS_MOBILE(dev) (INTEL_INFO(dev)->is_mobile) +#define IS_GEN3(dev) (IS_I915G(dev) || \ + IS_I915GM(dev) || \ + IS_I945G(dev) || \ + IS_I945GM(dev) || \ + IS_G33(dev) || \ + IS_PINEVIEW(dev)) +#define IS_GEN4(dev) ((dev)->pci_device == 0x2972 || \ + (dev)->pci_device == 0x2982 || \ + (dev)->pci_device == 0x2992 || \ + (dev)->pci_device == 0x29A2 || \ + (dev)->pci_device == 0x2A02 || \ + (dev)->pci_device == 0x2A12 || \ + (dev)->pci_device == 0x2E02 || \ + (dev)->pci_device == 0x2E12 || \ + (dev)->pci_device == 0x2E22 || \ + (dev)->pci_device == 0x2E32 || \ + (dev)->pci_device == 0x2A42 || \ + (dev)->pci_device == 0x2E42) + #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws) +#define IS_GEN6(dev) ((dev)->pci_device == 0x0102) + /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte * rows, which changed the alignment requirements and fence programming. */ @@ -1067,6 +1129,9 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define I915_HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc) #define I915_HAS_RC6(dev) (INTEL_INFO(dev)->has_rc6) +#define HAS_PCH_SPLIT(dev) (IS_IRONLAKE(dev) || \ + IS_GEN6(dev)) + #define PRIMARY_RINGBUFFER_SIZE (128*1024) #endif diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ec8a0d7ffa39..fba37e9f775d 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -128,9 +128,7 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data, return -ENOMEM; ret = drm_gem_handle_create(file_priv, obj, &handle); - mutex_lock(&dev->struct_mutex); - drm_gem_object_handle_unreference(obj); - mutex_unlock(&dev->struct_mutex); + drm_gem_object_handle_unreference_unlocked(obj); if (ret) return ret; @@ -488,7 +486,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, */ if (args->offset > obj->size || args->size > obj->size || args->offset + args->size > obj->size) { - drm_gem_object_unreference(obj); + drm_gem_object_unreference_unlocked(obj); return -EINVAL; } @@ -501,7 +499,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, file_priv); } - drm_gem_object_unreference(obj); + drm_gem_object_unreference_unlocked(obj); return ret; } @@ -961,7 +959,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, */ if (args->offset > obj->size || args->size > obj->size || args->offset + args->size > obj->size) { - drm_gem_object_unreference(obj); + drm_gem_object_unreference_unlocked(obj); return -EINVAL; } @@ -995,7 +993,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, DRM_INFO("pwrite failed %d\n", ret); #endif - drm_gem_object_unreference(obj); + drm_gem_object_unreference_unlocked(obj); return ret; } @@ -1138,9 +1136,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, PROT_READ | PROT_WRITE, MAP_SHARED, args->offset); up_write(¤t->mm->mmap_sem); - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(obj); - mutex_unlock(&dev->struct_mutex); + drm_gem_object_unreference_unlocked(obj); if (IS_ERR((void *)addr)) return addr; @@ -1562,6 +1558,38 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj) i915_verify_inactive(dev, __FILE__, __LINE__); } +static void +i915_gem_process_flushing_list(struct drm_device *dev, + uint32_t flush_domains, uint32_t seqno) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv, *next; + + list_for_each_entry_safe(obj_priv, next, + &dev_priv->mm.gpu_write_list, + gpu_write_list) { + struct drm_gem_object *obj = obj_priv->obj; + + if ((obj->write_domain & flush_domains) == + obj->write_domain) { + uint32_t old_write_domain = obj->write_domain; + + obj->write_domain = 0; + list_del_init(&obj_priv->gpu_write_list); + i915_gem_object_move_to_active(obj, seqno); + + /* update the fence lru list */ + if (obj_priv->fence_reg != I915_FENCE_REG_NONE) + list_move_tail(&obj_priv->fence_list, + &dev_priv->mm.fence_list); + + trace_i915_gem_object_change_domain(obj, + obj->read_domains, + old_write_domain); + } + } +} + /** * Creates a new sequence number, emitting a write of it to the status page * plus an interrupt, which will trigger i915_user_interrupt_handler. @@ -1620,29 +1648,8 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv, /* Associate any objects on the flushing list matching the write * domain we're flushing with our flush. */ - if (flush_domains != 0) { - struct drm_i915_gem_object *obj_priv, *next; - - list_for_each_entry_safe(obj_priv, next, - &dev_priv->mm.gpu_write_list, - gpu_write_list) { - struct drm_gem_object *obj = obj_priv->obj; - - if ((obj->write_domain & flush_domains) == - obj->write_domain) { - uint32_t old_write_domain = obj->write_domain; - - obj->write_domain = 0; - list_del_init(&obj_priv->gpu_write_list); - i915_gem_object_move_to_active(obj, seqno); - - trace_i915_gem_object_change_domain(obj, - obj->read_domains, - old_write_domain); - } - } - - } + if (flush_domains != 0) + i915_gem_process_flushing_list(dev, flush_domains, seqno); if (!dev_priv->mm.suspended) { mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); @@ -1822,7 +1829,7 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno, int interruptible) return -EIO; if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) { - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) ier = I915_READ(DEIER) | I915_READ(GTIER); else ier = I915_READ(IER); @@ -1991,6 +1998,7 @@ int i915_gem_object_unbind(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = obj->driver_private; int ret = 0; @@ -2046,8 +2054,10 @@ i915_gem_object_unbind(struct drm_gem_object *obj) } /* Remove ourselves from the LRU list if present. */ + spin_lock(&dev_priv->mm.active_list_lock); if (!list_empty(&obj_priv->list)) list_del_init(&obj_priv->list); + spin_unlock(&dev_priv->mm.active_list_lock); if (i915_gem_object_is_purgeable(obj_priv)) i915_gem_object_truncate(obj); @@ -2085,11 +2095,34 @@ i915_gem_find_inactive_object(struct drm_device *dev, int min_size) } static int +i915_gpu_idle(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + bool lists_empty; + uint32_t seqno; + + 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 (lists_empty) + return 0; + + /* 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) + return -ENOMEM; + + return i915_wait_request(dev, seqno); +} + +static int i915_gem_evict_everything(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; int ret; - uint32_t seqno; bool lists_empty; spin_lock(&dev_priv->mm.active_list_lock); @@ -2102,12 +2135,7 @@ i915_gem_evict_everything(struct drm_device *dev) return -ENOSPC; /* Flush everything (on to the inactive lists) and evict */ - 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) - return -ENOMEM; - - ret = i915_wait_request(dev, seqno); + ret = i915_gpu_idle(dev); if (ret) return ret; @@ -2265,6 +2293,28 @@ i915_gem_object_get_pages(struct drm_gem_object *obj, return 0; } +static void sandybridge_write_fence_reg(struct drm_i915_fence_reg *reg) +{ + struct drm_gem_object *obj = reg->obj; + struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + int regnum = obj_priv->fence_reg; + uint64_t val; + + val = (uint64_t)((obj_priv->gtt_offset + obj->size - 4096) & + 0xfffff000) << 32; + val |= obj_priv->gtt_offset & 0xfffff000; + val |= (uint64_t)((obj_priv->stride / 128) - 1) << + SANDYBRIDGE_FENCE_PITCH_SHIFT; + + if (obj_priv->tiling_mode == I915_TILING_Y) + val |= 1 << I965_FENCE_TILING_Y_SHIFT; + val |= I965_FENCE_REG_VALID; + + I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + (regnum * 8), val); +} + static void i965_write_fence_reg(struct drm_i915_fence_reg *reg) { struct drm_gem_object *obj = reg->obj; @@ -2361,6 +2411,58 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg) I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val); } +static int i915_find_fence_reg(struct drm_device *dev) +{ + struct drm_i915_fence_reg *reg = NULL; + struct drm_i915_gem_object *obj_priv = NULL; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_gem_object *obj = NULL; + int i, avail, ret; + + /* First try to find a free reg */ + avail = 0; + for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) { + reg = &dev_priv->fence_regs[i]; + if (!reg->obj) + return i; + + obj_priv = reg->obj->driver_private; + if (!obj_priv->pin_count) + avail++; + } + + if (avail == 0) + return -ENOSPC; + + /* None available, try to steal one or wait for a user to finish */ + i = I915_FENCE_REG_NONE; + list_for_each_entry(obj_priv, &dev_priv->mm.fence_list, + fence_list) { + obj = obj_priv->obj; + + if (obj_priv->pin_count) + continue; + + /* found one! */ + i = obj_priv->fence_reg; + break; + } + + BUG_ON(i == I915_FENCE_REG_NONE); + + /* We only have a reference on obj from the active list. put_fence_reg + * might drop that one, causing a use-after-free in it. So hold a + * private reference to obj like the other callers of put_fence_reg + * (set_tiling ioctl) do. */ + drm_gem_object_reference(obj); + ret = i915_gem_object_put_fence_reg(obj); + drm_gem_object_unreference(obj); + if (ret != 0) + return ret; + + return i; +} + /** * i915_gem_object_get_fence_reg - set up a fence reg for an object * @obj: object to map through a fence reg @@ -2381,8 +2483,7 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj) struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = obj->driver_private; struct drm_i915_fence_reg *reg = NULL; - struct drm_i915_gem_object *old_obj_priv = NULL; - int i, ret, avail; + int ret; /* Just update our place in the LRU if our fence is getting used. */ if (obj_priv->fence_reg != I915_FENCE_REG_NONE) { @@ -2410,86 +2511,27 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj) break; } - /* First try to find a free reg */ - avail = 0; - for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) { - reg = &dev_priv->fence_regs[i]; - if (!reg->obj) - break; - - old_obj_priv = reg->obj->driver_private; - if (!old_obj_priv->pin_count) - avail++; - } - - /* None available, try to steal one or wait for a user to finish */ - if (i == dev_priv->num_fence_regs) { - struct drm_gem_object *old_obj = NULL; - - if (avail == 0) - return -ENOSPC; - - list_for_each_entry(old_obj_priv, &dev_priv->mm.fence_list, - fence_list) { - old_obj = old_obj_priv->obj; - - if (old_obj_priv->pin_count) - continue; - - /* Take a reference, as otherwise the wait_rendering - * below may cause the object to get freed out from - * under us. - */ - drm_gem_object_reference(old_obj); - - /* i915 uses fences for GPU access to tiled buffers */ - if (IS_I965G(dev) || !old_obj_priv->active) - break; - - /* This brings the object to the head of the LRU if it - * had been written to. The only way this should - * result in us waiting longer than the expected - * optimal amount of time is if there was a - * fence-using buffer later that was read-only. - */ - i915_gem_object_flush_gpu_write_domain(old_obj); - ret = i915_gem_object_wait_rendering(old_obj); - if (ret != 0) { - drm_gem_object_unreference(old_obj); - return ret; - } - - break; - } - - /* - * Zap this virtual mapping so we can set up a fence again - * for this object next time we need it. - */ - i915_gem_release_mmap(old_obj); - - i = old_obj_priv->fence_reg; - reg = &dev_priv->fence_regs[i]; - - old_obj_priv->fence_reg = I915_FENCE_REG_NONE; - list_del_init(&old_obj_priv->fence_list); - - drm_gem_object_unreference(old_obj); - } + ret = i915_find_fence_reg(dev); + if (ret < 0) + return ret; - obj_priv->fence_reg = i; + obj_priv->fence_reg = ret; + reg = &dev_priv->fence_regs[obj_priv->fence_reg]; list_add_tail(&obj_priv->fence_list, &dev_priv->mm.fence_list); reg->obj = obj; - if (IS_I965G(dev)) + if (IS_GEN6(dev)) + sandybridge_write_fence_reg(reg); + else if (IS_I965G(dev)) i965_write_fence_reg(reg); else if (IS_I9XX(dev)) i915_write_fence_reg(reg); else i830_write_fence_reg(reg); - trace_i915_gem_object_get_fence(obj, i, obj_priv->tiling_mode); + trace_i915_gem_object_get_fence(obj, obj_priv->fence_reg, + obj_priv->tiling_mode); return 0; } @@ -2508,9 +2550,12 @@ i915_gem_clear_fence_reg(struct drm_gem_object *obj) drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = obj->driver_private; - if (IS_I965G(dev)) + if (IS_GEN6(dev)) { + I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + + (obj_priv->fence_reg * 8), 0); + } else if (IS_I965G(dev)) { I915_WRITE64(FENCE_REG_965_0 + (obj_priv->fence_reg * 8), 0); - else { + } else { uint32_t fence_reg; if (obj_priv->fence_reg < 8) @@ -2544,6 +2589,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. @@ -2552,12 +2603,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; @@ -2697,7 +2748,6 @@ static void i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; - uint32_t seqno; uint32_t old_write_domain; if ((obj->write_domain & I915_GEM_GPU_DOMAINS) == 0) @@ -2706,9 +2756,8 @@ i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj) /* Queue the GPU write cache flushing we need. */ old_write_domain = obj->write_domain; i915_gem_flush(dev, 0, obj->write_domain); - seqno = i915_add_request(dev, NULL, obj->write_domain); + (void) i915_add_request(dev, NULL, obj->write_domain); BUG_ON(obj->write_domain); - i915_gem_object_move_to_active(obj, seqno); trace_i915_gem_object_change_domain(obj, obj->read_domains, @@ -3247,7 +3296,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. */ @@ -3317,6 +3367,16 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, } /* Validate that the target is in a valid r/w GPU domain */ + if (reloc->write_domain & (reloc->write_domain - 1)) { + DRM_ERROR("reloc with multiple write domains: " + "obj %p target %d offset %d " + "read %08x write %08x", + obj, reloc->target_handle, + (int) reloc->offset, + reloc->read_domains, + reloc->write_domain); + return -EINVAL; + } if (reloc->write_domain & I915_GEM_DOMAIN_CPU || reloc->read_domains & I915_GEM_DOMAIN_CPU) { DRM_ERROR("reloc with read/write CPU domains: " @@ -4445,8 +4505,7 @@ int i915_gem_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - uint32_t seqno, cur_seqno, last_seqno; - int stuck, ret; + int ret; mutex_lock(&dev->struct_mutex); @@ -4455,115 +4514,36 @@ i915_gem_idle(struct drm_device *dev) 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 - */ - 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) { + ret = i915_gpu_idle(dev); + if (ret) { mutex_unlock(&dev->struct_mutex); - return -ENOMEM; + return ret; } - 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; - } + /* 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; } - 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. + /* 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! */ - 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); - - while (!list_empty(&dev_priv->mm.flushing_list)) { - struct drm_gem_object *obj; - uint32_t old_write_domain; - - 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); - - trace_i915_gem_object_change_domain(obj, - obj->read_domains, - old_write_domain); - } - - - /* 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)); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ret; - } + 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; } @@ -4607,8 +4587,13 @@ i915_gem_init_hws(struct drm_device *dev) } dev_priv->hws_obj = obj; memset(dev_priv->hw_status_page, 0, PAGE_SIZE); - I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr); - I915_READ(HWS_PGA); /* posting read */ + if (IS_GEN6(dev)) { + I915_WRITE(HWS_PGA_GEN6, dev_priv->status_gfx_addr); + I915_READ(HWS_PGA_GEN6); /* posting read */ + } else { + I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr); + I915_READ(HWS_PGA); /* posting read */ + } DRM_DEBUG_DRIVER("hws offset: 0x%08x\n", dev_priv->status_gfx_addr); return 0; @@ -4850,7 +4835,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 df278b2685bf..b5c55d88ff76 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,9 +91,8 @@ 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)) { + if (IS_IRONLAKE(dev) || IS_GEN6(dev)) { /* On Ironlake whatever DRAM config, GPU always do * same swizzling setup. */ @@ -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; @@ -438,9 +283,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, obj_priv = obj->driver_private; if (!i915_tiling_ok(dev, args->stride, obj->size, args->tiling_mode)) { - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(obj); - mutex_unlock(&dev->struct_mutex); + drm_gem_object_unreference_unlocked(obj); return -EINVAL; } @@ -493,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 a17d6bdfe63e..5388354da0d1 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -166,7 +166,7 @@ void intel_enable_asle (struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) ironlake_enable_display_irq(dev_priv, DE_GSE); else i915_enable_pipestat(dev_priv, 1, @@ -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; + + count++; + } - dev_priv->first_error = error; + /* 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; + } + + do_gettimeofday(&error->time); -out: + 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); } /** @@ -576,7 +842,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) atomic_inc(&dev_priv->irq_received); - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) return ironlake_irq_handler(dev); iir = I915_READ(IIR); @@ -737,7 +1003,7 @@ void i915_user_irq_get(struct drm_device *dev) spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) { - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) ironlake_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT); else i915_enable_irq(dev_priv, I915_USER_INTERRUPT); @@ -753,7 +1019,7 @@ void i915_user_irq_put(struct drm_device *dev) spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0); if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) { - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) ironlake_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT); else i915_disable_irq(dev_priv, I915_USER_INTERRUPT); @@ -861,7 +1127,7 @@ int i915_enable_vblank(struct drm_device *dev, int pipe) return -EINVAL; spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) ironlake_enable_display_irq(dev_priv, (pipe == 0) ? DE_PIPEA_VBLANK: DE_PIPEB_VBLANK); else if (IS_I965G(dev)) @@ -883,7 +1149,7 @@ void i915_disable_vblank(struct drm_device *dev, int pipe) unsigned long irqflags; spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) ironlake_disable_display_irq(dev_priv, (pipe == 0) ? DE_PIPEA_VBLANK: DE_PIPEB_VBLANK); else @@ -897,7 +1163,7 @@ void i915_enable_interrupt (struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (!IS_IRONLAKE(dev)) + if (!HAS_PCH_SPLIT(dev)) opregion_enable_asle(dev); dev_priv->irq_enabled = 1; } @@ -973,7 +1239,11 @@ void i915_hangcheck_elapsed(unsigned long data) struct drm_device *dev = (struct drm_device *)data; drm_i915_private_t *dev_priv = dev->dev_private; uint32_t acthd; - + + /* No reset support on this chip yet. */ + if (IS_GEN6(dev)) + return; + if (!IS_I965G(dev)) acthd = I915_READ(ACTHD); else @@ -1064,6 +1334,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; } @@ -1076,7 +1353,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev) INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); INIT_WORK(&dev_priv->error_work, i915_error_work_func); - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { ironlake_irq_preinstall(dev); return; } @@ -1108,7 +1385,7 @@ int i915_driver_irq_postinstall(struct drm_device *dev) dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) return ironlake_irq_postinstall(dev); /* Unmask the interrupts that we always want on. */ @@ -1196,7 +1473,7 @@ void i915_driver_irq_uninstall(struct drm_device * dev) dev_priv->vblank_pipe = 0; - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { ironlake_irq_uninstall(dev); return; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index ab1bd2d3d3b6..3d59862c7ccd 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -53,6 +53,25 @@ #define INTEL_GMCH_GMS_STOLEN_224M (0xc << 4) #define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4) +#define SNB_GMCH_CTRL 0x50 +#define SNB_GMCH_GMS_STOLEN_MASK 0xF8 +#define SNB_GMCH_GMS_STOLEN_32M (1 << 3) +#define SNB_GMCH_GMS_STOLEN_64M (2 << 3) +#define SNB_GMCH_GMS_STOLEN_96M (3 << 3) +#define SNB_GMCH_GMS_STOLEN_128M (4 << 3) +#define SNB_GMCH_GMS_STOLEN_160M (5 << 3) +#define SNB_GMCH_GMS_STOLEN_192M (6 << 3) +#define SNB_GMCH_GMS_STOLEN_224M (7 << 3) +#define SNB_GMCH_GMS_STOLEN_256M (8 << 3) +#define SNB_GMCH_GMS_STOLEN_288M (9 << 3) +#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3) +#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3) +#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3) +#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3) +#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3) +#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3) +#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3) + /* PCI config space */ #define HPLLCC 0xc0 /* 855 only */ @@ -61,6 +80,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) @@ -234,6 +254,9 @@ #define I965_FENCE_REG_VALID (1<<0) #define I965_FENCE_MAX_PITCH_VAL 0x0400 +#define FENCE_REG_SANDYBRIDGE_0 0x100000 +#define SANDYBRIDGE_FENCE_PITCH_SHIFT 32 + /* * Instruction and interrupt control regs */ @@ -265,6 +288,7 @@ #define INSTDONE1 0x0207c /* 965+ only */ #define ACTHD_I965 0x02074 #define HWS_PGA 0x02080 +#define HWS_PGA_GEN6 0x04080 #define HWS_ADDRESS_MASK 0xfffff000 #define HWS_START_ADDRESS_SHIFT 4 #define PWRCTXA 0x2088 /* 965GM+ only */ @@ -282,7 +306,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 +330,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 +351,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 +812,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 a3b90c9561dc..ac0d1a73ac22 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_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 15fbc1b5a83e..70c9d4ba7042 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -247,6 +247,7 @@ static void parse_general_features(struct drm_i915_private *dev_priv, struct bdb_header *bdb) { + struct drm_device *dev = dev_priv->dev; struct bdb_general_features *general; /* Set sensible defaults in case we can't find the general block */ @@ -263,7 +264,7 @@ parse_general_features(struct drm_i915_private *dev_priv, if (IS_I85X(dev_priv->dev)) dev_priv->lvds_ssc_freq = general->ssc_freq ? 66 : 48; - else if (IS_IRONLAKE(dev_priv->dev)) + else if (IS_IRONLAKE(dev_priv->dev) || IS_GEN6(dev)) dev_priv->lvds_ssc_freq = general->ssc_freq ? 100 : 120; else diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 79dd4026586f..fccf07470c8f 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -39,7 +39,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode) struct drm_i915_private *dev_priv = dev->dev_private; u32 temp, reg; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) reg = PCH_ADPA; else reg = ADPA; @@ -113,7 +113,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder, else dpll_md_reg = DPLL_B_MD; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) adpa_reg = PCH_ADPA; else adpa_reg = ADPA; @@ -122,7 +122,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder, * Disable separate mode multiplier used when cloning SDVO to CRT * XXX this needs to be adjusted when we really are cloning */ - if (IS_I965G(dev) && !IS_IRONLAKE(dev)) { + if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) { dpll_md = I915_READ(dpll_md_reg); I915_WRITE(dpll_md_reg, dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); @@ -136,11 +136,11 @@ static void intel_crt_mode_set(struct drm_encoder *encoder, if (intel_crtc->pipe == 0) { adpa |= ADPA_PIPE_A_SELECT; - if (!IS_IRONLAKE(dev)) + if (!HAS_PCH_SPLIT(dev)) I915_WRITE(BCLRPAT_A, 0); } else { adpa |= ADPA_PIPE_B_SELECT; - if (!IS_IRONLAKE(dev)) + if (!HAS_PCH_SPLIT(dev)) I915_WRITE(BCLRPAT_B, 0); } @@ -202,7 +202,7 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector) u32 hotplug_en; int i, tries = 0; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) return intel_ironlake_crt_detect_hotplug(connector); /* @@ -524,7 +524,7 @@ void intel_crt_init(struct drm_device *dev) &intel_output->enc); /* Set up the DDC bus. */ - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) i2c_reg = PCH_GPIOA; else { i2c_reg = GPIOA; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b27202d23ebc..9cd6de5f9906 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -232,7 +232,7 @@ struct intel_limit { #define G4X_P2_DISPLAY_PORT_FAST 10 #define G4X_P2_DISPLAY_PORT_LIMIT 0 -/* Ironlake */ +/* Ironlake / Sandybridge */ /* as we calculate clock using (register_value + 2) for N/M1/M2, so here the range value for them is (actual_value-2). */ @@ -690,7 +690,7 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; const intel_limit_t *limit; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) limit = intel_ironlake_limit(crtc); else if (IS_G4X(dev)) { limit = intel_g4x_limit(crtc); @@ -886,7 +886,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { int lvds_reg; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) lvds_reg = PCH_LVDS; else lvds_reg = LVDS; @@ -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; } @@ -1366,7 +1371,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, dspcntr &= ~DISPPLANE_TILED; } - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) /* must disable */ dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; @@ -1427,7 +1432,7 @@ static void i915_disable_vga (struct drm_device *dev) u8 sr1; u32 vga_reg; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) vga_reg = CPU_VGACNTRL; else vga_reg = VGACNTRL; @@ -2111,7 +2116,7 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = crtc->dev; - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { /* FDI link clock is fixed at 2.7G */ if (mode->clock * 3 > 27000 * 4) return MODE_CLOCK_HIGH; @@ -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", @@ -2967,7 +2983,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, refclk / 1000); } else if (IS_I9XX(dev)) { refclk = 96000; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) refclk = 120000; /* 120Mhz refclk */ } else { refclk = 48000; @@ -3025,7 +3041,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, } /* FDI link */ - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { int lane, link_bw, bpp; /* eDP doesn't require FDI link, so just set DP M/N according to current link config */ @@ -3102,7 +3118,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, * PCH B stepping, previous chipset stepping should be * ignoring this setting. */ - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { temp = I915_READ(PCH_DREF_CONTROL); /* Always enable nonspread source */ temp &= ~DREF_NONSPREAD_SOURCE_MASK; @@ -3149,7 +3165,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, reduced_clock.m2; } - if (!IS_IRONLAKE(dev)) + if (!HAS_PCH_SPLIT(dev)) dpll = DPLL_VGA_MODE_DIS; if (IS_I9XX(dev)) { @@ -3162,7 +3178,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; - else if (IS_IRONLAKE(dev)) + else if (HAS_PCH_SPLIT(dev)) dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; } if (is_dp) @@ -3174,7 +3190,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, else { dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; /* also FPA1 */ - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; if (IS_G4X(dev) && has_reduced_clock) dpll |= (1 << (reduced_clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; @@ -3193,7 +3209,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; break; } - if (IS_I965G(dev) && !IS_IRONLAKE(dev)) + if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); } else { if (is_lvds) { @@ -3227,7 +3243,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* Ironlake's plane is forced to pipe, bit 24 is to enable color space conversion */ - if (!IS_IRONLAKE(dev)) { + if (!HAS_PCH_SPLIT(dev)) { if (pipe == 0) dspcntr &= ~DISPPLANE_SEL_PIPE_MASK; else @@ -3254,14 +3270,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* Disable the panel fitter if it was on our pipe */ - if (!IS_IRONLAKE(dev) && intel_panel_fitter_pipe(dev) == pipe) + if (!HAS_PCH_SPLIT(dev) && intel_panel_fitter_pipe(dev) == pipe) I915_WRITE(PFIT_CONTROL, 0); DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); drm_mode_debug_printmodeline(mode); /* assign to Ironlake registers */ - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { fp_reg = pch_fp_reg; dpll_reg = pch_dpll_reg; } @@ -3282,7 +3298,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, if (is_lvds) { u32 lvds; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) lvds_reg = PCH_LVDS; lvds = I915_READ(lvds_reg); @@ -3304,12 +3320,12 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* set the dithering flag */ if (IS_I965G(dev)) { if (dev_priv->lvds_dither) { - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) pipeconf |= PIPE_ENABLE_DITHER; else lvds |= LVDS_ENABLE_DITHER; } else { - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) pipeconf &= ~PIPE_ENABLE_DITHER; else lvds &= ~LVDS_ENABLE_DITHER; @@ -3328,7 +3344,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* Wait for the clocks to stabilize. */ udelay(150); - if (IS_I965G(dev) && !IS_IRONLAKE(dev)) { + if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) { if (is_sdvo) { sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | @@ -3375,14 +3391,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* pipesrc and dspsize control the size that is scaled from, which should * always be the user's requested size. */ - if (!IS_IRONLAKE(dev)) { + if (!HAS_PCH_SPLIT(dev)) { I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); I915_WRITE(dsppos_reg, 0); } I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m); I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n); I915_WRITE(link_m1_reg, m_n.link_m); @@ -3438,7 +3454,7 @@ void intel_crtc_load_lut(struct drm_crtc *crtc) return; /* use legacy palette for Ironlake */ - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) palreg = (intel_crtc->pipe == 0) ? LGC_PALETTE_A : LGC_PALETTE_B; @@ -3553,11 +3569,10 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, intel_crtc->cursor_bo = bo; return 0; -fail: - mutex_lock(&dev->struct_mutex); fail_locked: - drm_gem_object_unreference(bo); mutex_unlock(&dev->struct_mutex); +fail: + drm_gem_object_unreference_unlocked(bo); return ret; } @@ -3922,7 +3937,7 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule) int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; int dpll = I915_READ(dpll_reg); - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) return; if (!dev_priv->lvds_downclock_avail) @@ -3961,7 +3976,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc) int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; int dpll = I915_READ(dpll_reg); - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) return; if (!dev_priv->lvds_downclock_avail) @@ -4011,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) @@ -4044,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)); @@ -4058,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; @@ -4382,7 +4418,7 @@ static void intel_setup_outputs(struct drm_device *dev) if (IS_MOBILE(dev) && !IS_I830(dev)) intel_lvds_init(dev); - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { int found; if (IS_MOBILE(dev) && (I915_READ(DP_A) & DP_DETECTED)) @@ -4451,7 +4487,7 @@ static void intel_setup_outputs(struct drm_device *dev) DRM_DEBUG_KMS("probing DP_D\n"); intel_dp_init(dev, DP_D); } - } else if (IS_I8XX(dev)) + } else if (IS_GEN2(dev)) intel_dvo_init(dev); if (SUPPORTS_TV(dev)) @@ -4476,9 +4512,7 @@ static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) intelfb_remove(dev, fb); drm_framebuffer_cleanup(fb); - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(intel_fb->obj); - mutex_unlock(&dev->struct_mutex); + drm_gem_object_unreference_unlocked(intel_fb->obj); kfree(intel_fb); } @@ -4541,9 +4575,7 @@ intel_user_framebuffer_create(struct drm_device *dev, ret = intel_framebuffer_create(dev, mode_cmd, &fb, obj); if (ret) { - mutex_lock(&dev->struct_mutex); - drm_gem_object_unreference(obj); - mutex_unlock(&dev->struct_mutex); + drm_gem_object_unreference_unlocked(obj); return NULL; } @@ -4591,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; @@ -4599,7 +4716,7 @@ void intel_init_clock_gating(struct drm_device *dev) * Disable clock gating reported to work incorrectly according to the * specs, but enable as much else as we can. */ - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { return; } else if (IS_G4X(dev)) { uint32_t dspclk_gate; @@ -4672,7 +4789,7 @@ static void intel_init_display(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; /* We always want a DPMS function */ - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) dev_priv->display.dpms = ironlake_crtc_dpms; else dev_priv->display.dpms = i9xx_crtc_dpms; @@ -4715,7 +4832,7 @@ static void intel_init_display(struct drm_device *dev) i830_get_display_clock_speed; /* For FIFO watermark updates */ - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) dev_priv->display.update_wm = NULL; else if (IS_G4X(dev)) dev_priv->display.update_wm = g4x_update_wm; @@ -4774,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); } @@ -4787,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); @@ -4834,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_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 439506cefc14..3ef3a0d0edd0 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -231,7 +231,7 @@ intel_dp_aux_ch(struct intel_output *intel_output, */ if (IS_eDP(intel_output)) aux_clock_divider = 225; /* eDP input clock at 450Mhz */ - else if (IS_IRONLAKE(dev)) + else if (HAS_PCH_SPLIT(dev)) aux_clock_divider = 62; /* IRL input clock fixed at 125Mhz */ else aux_clock_divider = intel_hrawclk(dev) / 2; @@ -584,7 +584,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, intel_dp_compute_m_n(3, lane_count, mode->clock, adjusted_mode->clock, &m_n); - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { if (intel_crtc->pipe == 0) { I915_WRITE(TRANSA_DATA_M1, ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) | @@ -1176,7 +1176,7 @@ intel_dp_detect(struct drm_connector *connector) dp_priv->has_audio = false; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) return ironlake_dp_detect(connector); temp = I915_READ(PORT_HOTPLUG_EN); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a51573da1ff6..3a467ca57857 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_fb.c b/drivers/gpu/drm/i915/intel_fb.c index aaabbcbe5905..8cd791dc5b29 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -35,6 +35,7 @@ #include <linux/delay.h> #include <linux/fb.h> #include <linux/init.h> +#include <linux/vga_switcheroo.h> #include "drmP.h" #include "drm.h" @@ -235,6 +236,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, obj_priv->gtt_offset, fbo); mutex_unlock(&dev->struct_mutex); + vga_switcheroo_client_fb_set(dev->pdev, info); return 0; out_unpin: diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 0e268deed761..a30f8bfc1985 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -82,7 +82,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) /* HW workaround, need to toggle enable bit off and on for 12bpc, but * we do this anyway which shows more stable in testing. */ - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { I915_WRITE(hdmi_priv->sdvox_reg, temp & ~SDVO_ENABLE); POSTING_READ(hdmi_priv->sdvox_reg); } @@ -99,7 +99,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) /* HW workaround, need to write this twice for issue that may result * in first write getting masked. */ - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { I915_WRITE(hdmi_priv->sdvox_reg, temp); POSTING_READ(hdmi_priv->sdvox_reg); } diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 8673c735b8ab..fcc753ca5d94 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -128,7 +128,7 @@ intel_i2c_reset_gmbus(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { I915_WRITE(PCH_GMBUS0, 0); } else { I915_WRITE(GMBUS0, 0); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index c2e8a45780d5..14e516fdc2dd 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -56,7 +56,7 @@ static void intel_lvds_set_backlight(struct drm_device *dev, int level) struct drm_i915_private *dev_priv = dev->dev_private; u32 blc_pwm_ctl, reg; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) reg = BLC_PWM_CPU_CTL; else reg = BLC_PWM_CTL; @@ -74,7 +74,7 @@ static u32 intel_lvds_get_max_backlight(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; u32 reg; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) reg = BLC_PWM_PCH_CTL2; else reg = BLC_PWM_CTL; @@ -89,17 +89,22 @@ static u32 intel_lvds_get_max_backlight(struct drm_device *dev) static void intel_lvds_set_power(struct drm_device *dev, bool on) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 pp_status, ctl_reg, status_reg; + u32 pp_status, ctl_reg, status_reg, lvds_reg; - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; status_reg = PCH_PP_STATUS; + lvds_reg = PCH_LVDS; } else { ctl_reg = PP_CONTROL; status_reg = PP_STATUS; + lvds_reg = LVDS; } if (on) { + I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN); + POSTING_READ(lvds_reg); + I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); do { @@ -115,6 +120,9 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) do { pp_status = I915_READ(status_reg); } while (pp_status & PP_ON); + + I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); + POSTING_READ(lvds_reg); } } @@ -137,7 +145,7 @@ static void intel_lvds_save(struct drm_connector *connector) u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg; u32 pwm_ctl_reg; - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { pp_on_reg = PCH_PP_ON_DELAYS; pp_off_reg = PCH_PP_OFF_DELAYS; pp_ctl_reg = PCH_PP_CONTROL; @@ -174,7 +182,7 @@ static void intel_lvds_restore(struct drm_connector *connector) u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg; u32 pwm_ctl_reg; - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { pp_on_reg = PCH_PP_ON_DELAYS; pp_off_reg = PCH_PP_OFF_DELAYS; pp_ctl_reg = PCH_PP_CONTROL; @@ -297,7 +305,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, } /* full screen scale for now */ - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) goto out; /* 965+ wants fuzzy fitting */ @@ -327,7 +335,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, * to register description and PRM. * Change the value here to see the borders for debugging */ - if (!IS_IRONLAKE(dev)) { + if (!HAS_PCH_SPLIT(dev)) { I915_WRITE(BCLRPAT_A, 0); I915_WRITE(BCLRPAT_B, 0); } @@ -548,7 +556,7 @@ static void intel_lvds_prepare(struct drm_encoder *encoder) struct drm_i915_private *dev_priv = dev->dev_private; u32 reg; - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) reg = BLC_PWM_CPU_CTL; else reg = BLC_PWM_CTL; @@ -587,7 +595,7 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder, * settings. */ - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) return; /* @@ -655,8 +663,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_GEN2(dev)) + return connector_status_connected; + if (!dmi_check_system(bad_lid_status) && !acpi_lid_open()) status = connector_status_disconnected; @@ -1020,7 +1035,7 @@ void intel_lvds_init(struct drm_device *dev) return; } - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) return; if (dev_priv->edp_support) { @@ -1123,7 +1138,7 @@ void intel_lvds_init(struct drm_device *dev) */ /* Ironlake: FIXME if still fail, not try pipe mode now */ - if (IS_IRONLAKE(dev)) + if (HAS_PCH_SPLIT(dev)) goto failed; lvds = I915_READ(LVDS); @@ -1144,7 +1159,7 @@ void intel_lvds_init(struct drm_device *dev) goto failed; out: - if (IS_IRONLAKE(dev)) { + if (HAS_PCH_SPLIT(dev)) { u32 pwm; /* make sure PWM is enabled */ pwm = I915_READ(BLC_PWM_CPU_CTL2); diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 2639591c72e9..d355d1d527e7 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -172,7 +172,7 @@ struct overlay_registers { #define OFC_UPDATE 0x1 #define OVERLAY_NONPHYSICAL(dev) (IS_G33(dev) || IS_I965G(dev)) -#define OVERLAY_EXISTS(dev) (!IS_G4X(dev) && !IS_IRONLAKE(dev)) +#define OVERLAY_EXISTS(dev) (!IS_G4X(dev) && !IS_IRONLAKE(dev) && !IS_GEN6(dev)) static struct overlay_registers *intel_overlay_map_regs_atomic(struct intel_overlay *overlay) @@ -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); @@ -1179,7 +1164,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, out_unlock: mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->mode_config.mutex); - drm_gem_object_unreference(new_bo); + drm_gem_object_unreference_unlocked(new_bo); kfree(params); return ret; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 82678d30ab06..48daee5c9c63 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -35,6 +35,7 @@ #include "i915_drm.h" #include "i915_drv.h" #include "intel_sdvo_regs.h" +#include <linux/dmi.h> static char *tv_format_names[] = { "NTSC_M" , "NTSC_J" , "NTSC_443", @@ -2283,6 +2284,25 @@ intel_sdvo_get_slave_addr(struct drm_device *dev, int output_device) return 0x72; } +static int intel_sdvo_bad_tv_callback(const struct dmi_system_id *id) +{ + DRM_DEBUG_KMS("Ignoring bad SDVO TV connector for %s\n", id->ident); + return 1; +} + +static struct dmi_system_id intel_sdvo_bad_tv[] = { + { + .callback = intel_sdvo_bad_tv_callback, + .ident = "IntelG45/ICH10R/DME1737", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "IBM CORPORATION"), + DMI_MATCH(DMI_PRODUCT_NAME, "4800784"), + }, + }, + + { } /* terminating entry */ +}; + static bool intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags) { @@ -2323,7 +2343,8 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags) (1 << INTEL_SDVO_NON_TV_CLONE_BIT) | (1 << INTEL_ANALOG_CLONE_BIT); } - } else if (flags & SDVO_OUTPUT_SVID0) { + } else if ((flags & SDVO_OUTPUT_SVID0) && + !dmi_check_system(intel_sdvo_bad_tv)) { sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0; encoder->encoder_type = DRM_MODE_ENCODER_TVDAC; |