return 0;
 }
 
+static int i915_pipe_crc(struct seq_file *m, void *data)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe = (enum pipe)node->info_ent->data;
+       const struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
+       int i;
+       int start;
+
+       if (!IS_IVYBRIDGE(dev)) {
+               seq_puts(m, "unsupported\n");
+               return 0;
+       }
+
+       start = atomic_read(&pipe_crc->slot) + 1;
+       seq_puts(m, " timestamp     CRC1     CRC2     CRC3     CRC4     CRC5\n");
+       for (i = 0; i < INTEL_PIPE_CRC_ENTRIES_NR; i++) {
+               const struct intel_pipe_crc_entry *entry =
+                       &pipe_crc->entries[(start + i) %
+                                          INTEL_PIPE_CRC_ENTRIES_NR];
+
+               seq_printf(m, "%12u %8x %8x %8x %8x %8x\n", entry->timestamp,
+                          entry->crc[0], entry->crc[1], entry->crc[2],
+                          entry->crc[3], entry->crc[4]);
+       }
+
+       return 0;
+}
+
 static int
 i915_wedged_get(void *data, u64 *val)
 {
        {"i915_edp_psr_status", i915_edp_psr_status, 0},
        {"i915_energy_uJ", i915_energy_uJ, 0},
        {"i915_pc8_status", i915_pc8_status, 0},
+       {"i915_pipe_A_crc", i915_pipe_crc, 0, (void *)PIPE_A},
+       {"i915_pipe_B_crc", i915_pipe_crc, 0, (void *)PIPE_B},
+       {"i915_pipe_C_crc", i915_pipe_crc, 0, (void *)PIPE_C},
 };
 #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
 
 
        } regsave;
 };
 
+struct intel_pipe_crc_entry {
+       uint32_t timestamp;
+       uint32_t crc[5];
+};
+
+#define INTEL_PIPE_CRC_ENTRIES_NR      200
+struct intel_pipe_crc {
+       struct intel_pipe_crc_entry entries[INTEL_PIPE_CRC_ENTRIES_NR];
+       atomic_t slot;
+};
+
 typedef struct drm_i915_private {
        struct drm_device *dev;
        struct kmem_cache *slab;
        struct i915_dri1_state dri1;
        /* Old ums support infrastructure, same warning applies. */
        struct i915_ums_state ums;
+
+#ifdef CONFIG_DEBUG_FS
+       struct intel_pipe_crc pipe_crc[I915_MAX_PIPES];
+#endif
 } drm_i915_private_t;
 
 static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
 
        wake_up_all(&dev_priv->gmbus_wait_queue);
 }
 
+#if defined(CONFIG_DEBUG_FS)
+static void ivb_pipe_crc_update(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
+       struct intel_pipe_crc_entry *entry;
+       ktime_t now;
+       int ts, slot;
+
+       now = ktime_get();
+       ts = ktime_to_us(now);
+
+       slot = (atomic_read(&pipe_crc->slot) + 1) % INTEL_PIPE_CRC_ENTRIES_NR;
+       entry = &pipe_crc->entries[slot];
+       entry->timestamp = ts;
+       entry->crc[0] = I915_READ(PIPE_CRC_RES_1_IVB(pipe));
+       entry->crc[1] = I915_READ(PIPE_CRC_RES_2_IVB(pipe));
+       entry->crc[2] = I915_READ(PIPE_CRC_RES_3_IVB(pipe));
+       entry->crc[3] = I915_READ(PIPE_CRC_RES_4_IVB(pipe));
+       entry->crc[4] = I915_READ(PIPE_CRC_RES_5_IVB(pipe));
+       atomic_set(&dev_priv->pipe_crc[pipe].slot, slot);
+}
+#else
+static void ivb_pipe_crc_update(struct drm_device *dev, int pipe) {}
+#endif
+
 /* The RPS events need forcewake, so we add them to a work queue and mask their
  * IMR bits until the work is done. Other interrupts can be processed without
  * the work queue. */
                if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_C, false))
                        DRM_DEBUG_DRIVER("Pipe C FIFO underrun\n");
 
+       if (err_int & ERR_INT_PIPE_CRC_DONE_A)
+               ivb_pipe_crc_update(dev, PIPE_A);
+
+       if (err_int & ERR_INT_PIPE_CRC_DONE_B)
+               ivb_pipe_crc_update(dev, PIPE_B);
+
+       if (err_int & ERR_INT_PIPE_CRC_DONE_C)
+               ivb_pipe_crc_update(dev, PIPE_C);
+
        I915_WRITE(GEN7_ERR_INT, err_int);
 }
 
 
 #define GEN7_ERR_INT   0x44040
 #define   ERR_INT_POISON               (1<<31)
 #define   ERR_INT_MMIO_UNCLAIMED       (1<<13)
+#define   ERR_INT_PIPE_CRC_DONE_C      (1<<8)
 #define   ERR_INT_FIFO_UNDERRUN_C      (1<<6)
+#define   ERR_INT_PIPE_CRC_DONE_B      (1<<5)
 #define   ERR_INT_FIFO_UNDERRUN_B      (1<<3)
+#define   ERR_INT_PIPE_CRC_DONE_A      (1<<2)
 #define   ERR_INT_FIFO_UNDERRUN_A      (1<<0)
 #define   ERR_INT_FIFO_UNDERRUN(pipe)  (1<<(pipe*3))
 
  * Display engine regs
  */
 
+/* Pipe A CRC regs */
+#define _PIPE_CRC_CTL_A                (dev_priv->info->display_mmio_offset + 0x60050)
+#define   PIPE_CRC_ENABLE              (1 << 31)
+#define   PIPE_CRC_SOURCE_PRIMARY_IVB  (0 << 29)
+#define   PIPE_CRC_SOURCE_SPRITE_IVB   (1 << 29)
+#define   PIPE_CRC_SOURCE_PF_IVB       (2 << 29)
+#define _PIPE_CRC_RES_1_A_IVB  (dev_priv->info->display_mmio_offset + 0x60064)
+#define _PIPE_CRC_RES_2_A_IVB  (dev_priv->info->display_mmio_offset + 0x60068)
+#define _PIPE_CRC_RES_3_A_IVB  (dev_priv->info->display_mmio_offset + 0x6006c)
+#define _PIPE_CRC_RES_4_A_IVB  (dev_priv->info->display_mmio_offset + 0x60070)
+#define _PIPE_CRC_RES_5_A_IVB  (dev_priv->info->display_mmio_offset + 0x60074)
+
+/* Pipe B CRC regs */
+#define _PIPE_CRC_CTL_B                (dev_priv->info->display_mmio_offset + 0x61050)
+#define _PIPE_CRC_RES_1_B_IVB  (dev_priv->info->display_mmio_offset + 0x61064)
+#define _PIPE_CRC_RES_2_B_IVB  (dev_priv->info->display_mmio_offset + 0x61068)
+#define _PIPE_CRC_RES_3_B_IVB  (dev_priv->info->display_mmio_offset + 0x6106c)
+#define _PIPE_CRC_RES_4_B_IVB  (dev_priv->info->display_mmio_offset + 0x61070)
+#define _PIPE_CRC_RES_5_B_IVB  (dev_priv->info->display_mmio_offset + 0x61074)
+
+#define PIPE_CRC_CTL(pipe)     _PIPE(pipe, _PIPE_CRC_CTL_A, _PIPE_CRC_CTL_B)
+#define PIPE_CRC_RES_1_IVB(pipe)       \
+       _PIPE(pipe, _PIPE_CRC_RES_1_A_IVB, _PIPE_CRC_RES_1_B_IVB)
+#define PIPE_CRC_RES_2_IVB(pipe)       \
+       _PIPE(pipe, _PIPE_CRC_RES_2_A_IVB, _PIPE_CRC_RES_2_B_IVB)
+#define PIPE_CRC_RES_3_IVB(pipe)       \
+       _PIPE(pipe, _PIPE_CRC_RES_3_A_IVB, _PIPE_CRC_RES_3_B_IVB)
+#define PIPE_CRC_RES_4_IVB(pipe)       \
+       _PIPE(pipe, _PIPE_CRC_RES_4_A_IVB, _PIPE_CRC_RES_4_B_IVB)
+#define PIPE_CRC_RES_5_IVB(pipe)       \
+       _PIPE(pipe, _PIPE_CRC_RES_5_A_IVB, _PIPE_CRC_RES_5_B_IVB)
+
 /* Pipe A timing regs */
 #define _HTOTAL_A      (dev_priv->info->display_mmio_offset + 0x60000)
 #define _HBLANK_A      (dev_priv->info->display_mmio_offset + 0x60004)
 #define _BCLRPAT_B     (dev_priv->info->display_mmio_offset + 0x61020)
 #define _VSYNCSHIFT_B  (dev_priv->info->display_mmio_offset + 0x61028)
 
-
 #define HTOTAL(trans) _TRANSCODER(trans, _HTOTAL_A, _HTOTAL_B)
 #define HBLANK(trans) _TRANSCODER(trans, _HBLANK_A, _HBLANK_B)
 #define HSYNC(trans) _TRANSCODER(trans, _HSYNC_A, _HSYNC_B)