[PATCH 01/16] drm/i915: Expose latest 200 CRC value for pipe through debugfs

Damien Lespiau
2013-10-15T17:55:52Z

From: Shuang He <shuang.he@intel.com>

There are several points in the display pipeline where CRCs can be
computed on the bits flowing there. For instance, it's usually possible
to compute the CRCs of the primary plane, the sprite plane or the CRCs
of the bits after the panel fitter (collectively called pipe CRCs).

v2: Quite a bit of rework here and there (Damien)

Signed-off-by: Shuang He <shuang.he@intel.com>
Signed-off-by: Damien Lespiau <damien.lespiau@intel.com>
---
drivers/gpu/drm/i915/i915_debugfs.c | 33 +++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/i915_drv.h | 16 ++++++++++++++++
drivers/gpu/drm/i915/i915_irq.c | 35 +++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/i915_reg.h | 36 +++++++++++++++++++++++++++++++++++-
4 files changed, 119 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 72d0458..e1d45aa 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -1732,6 +1732,36 @@ static int i915_pc8_status(struct seq_file *m, void *unused)
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)
{
@@ -2247,6 +2277,9 @@ static struct drm_info_list i915_debugfs_list[] = {
{"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)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 6106d3d..6855d91 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1219,6 +1219,18 @@ struct i915_package_c8 {
} 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];
+ enum intel_pipe_crc_source source;
+ atomic_t slot;
+};
+
typedef struct drm_i915_private {
struct drm_device *dev;
struct kmem_cache *slab;
@@ -1423,6 +1435,10 @@ typedef struct drm_i915_private {
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)
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 26753b6..d2074f1 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1188,6 +1188,32 @@ static void dp_aux_irq_handler(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. */
@@ -1366,6 +1392,15 @@ static void ivb_err_int_handler(struct drm_device *dev)
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);
}

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 13153c3..4d01eaf 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -722,8 +722,11 @@
#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))

@@ -1835,6 +1838,38 @@
* 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)
@@ -1857,7 +1892,6 @@
#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)
--
1.8.3.1