@@ -29,6 +29,7 @@
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>
#include "drmP.h"
#include "drm.h"
#include "intel_drv.h"
@@ -1015,6 +1016,165 @@ static int i915_wedged_create(struct dentry *root, struct drm_minor *minor)
return drm_add_fake_info_node(minor, ent, &i915_wedged_fops);
}
+static void * i915_aub_data = NULL;
+static struct i915_aub_data * i915_aub_data_ptr = NULL;
+static struct mutex i915_aub_data_lock;
+
+static inline void i915_aub_out(struct seq_file *m, uint32_t data)
+{
+ seq_write(m, &data, 4);
+}
+
+static inline void
+i915_aub_out_data(struct seq_file *m, void *data, size_t size)
+{
+ seq_write(m, data, size);
+}
+
+
+/*
+ * Check should we trace this register. Since some registers,
+ * like GPIOX, writes too often, which will fill up the
+ * i915_aub_data buffer soon. That's really not good!
+ *
+ * You can add your own filter here.
+ */
+static int i915_aub_trace_reg(uint32_t reg)
+{
+#if 0
+ if ((reg - PCH_GPIOA) >= 0 &&
+ (reg - PCH_GPIOA) <= (PCH_GPIOF - PCH_GPIOA))
+ return 0;
+ if (reg == CURABASE || reg == CURAPOS ||
+ reg == CURBBASE || reg == CURBPOS)
+ return 0;
+#endif
+ return 1;
+}
+
+void i915_aub_write_reg(uint32_t reg, uint64_t val, int len)
+{
+ if (!i915_aub_trace_reg(reg))
+ return;
+
+ mutex_lock(&i915_aub_data_lock);
+ if ((void *)i915_aub_data_ptr < i915_aub_data + AUB_DATA_SIZE) {
+ i915_aub_data_ptr->type = AUB_TYPE_REG;
+ i915_aub_data_ptr->size = len;
+ i915_aub_data_ptr->addr = reg;
+ i915_aub_data_ptr->data = val;
+ i915_aub_data_ptr++;
+ }
+ mutex_unlock(&i915_aub_data_lock);
+}
+
+
+/*
+ * Write out the aub header
+ */
+static void i915_aub_out_header(struct seq_file *m)
+{
+ char app_name[AUB_APP_NAME_LEN] = "kernel-i915";
+
+ i915_aub_out(m, AUB_CMD_HEADER | (13 - 2));
+ i915_aub_out(m, AUB_VERSION);
+ i915_aub_out_data(m, app_name, AUB_APP_NAME_LEN);
+ i915_aub_out(m, 0); /* timestamp */
+ i915_aub_out(m, 0); /* timestamp */
+ i915_aub_out(m, 0); /* comment len */
+}
+
+static void i915_aub_out_reg(struct seq_file *m, struct i915_aub_data *reg)
+{
+ i915_aub_out(m, AUB_CMD_TRACE_HEADER_BLOCK | (5 - 2));
+ i915_aub_out(m, AUB_TRACE_OP_MMIO_WRITE);
+ i915_aub_out(m, 0);
+ i915_aub_out(m, reg->addr); /* reg offset */
+ i915_aub_out(m, reg->size); /* size */
+ i915_aub_out_data(m, ®->data, reg->size); /* data */
+}
+
+/*
+ * Set up the GTT. The max we can handle is 256M
+ *
+ * FIXME: is 256M OK?
+ */
+static void i915_aub_setup_gtt(struct seq_file *m)
+{
+ int i;
+ uint32_t entry = 0x200003;
+
+ for (i = 0x0; i < 0x10000; i += 4, entry += 0x1000) {
+ i915_aub_out(m, AUB_CMD_TRACE_HEADER_BLOCK | (5 - 2));
+ i915_aub_out(m, AUB_TRACE_MEMTYPE_NONLOCAL | 0 | AUB_TRACE_OP_DATA_WRITE);
+ i915_aub_out(m, 0);
+ i915_aub_out(m, i);
+ i915_aub_out(m, 4);
+ i915_aub_out(m, entry);
+ }
+}
+
+/*
+ * It's time to tell fulsim to handle these data
+ */
+static void i915_aub_draw(struct seq_file *m)
+{
+ /* AUB_CMD_DRAW */
+ i915_aub_out(m, AUB_CMD_DRAW | 5);
+
+ /* FIXME: currently write all ZERO */
+ i915_aub_out(m, 0x0); /* Lenght */
+ i915_aub_out(m, 0x0); /* Flags */
+ i915_aub_out(m, 0x0); /* XLeft, Ytop */
+ i915_aub_out(m, 0x0); /* Width, Height */
+ i915_aub_out(m, 0x0); /* BitsPerPixels, Pitch */
+ i915_aub_out(m, 0x0); /* SpanGrid */
+}
+
+static int i915_aub(struct seq_file *m, void *unused)
+{
+ struct i915_aub_data *p = (struct i915_aub_data *)i915_aub_data;
+ struct i915_aub_data *end = i915_aub_data_ptr;
+
+ if (i915_aub_debug == 0) {
+ seq_printf(m, "aub debug is disabled! You can add 'i915.aub_debug=1' "
+ "at the end of boot command line to enable it.\n");
+ return 0;
+ }
+
+ i915_aub_out_header(m);
+
+ if ((void *)i915_aub_data_ptr >= i915_aub_data + AUB_DATA_SIZE)
+ printk("WARNING: i915_aub_data_ptr exceed the size of aub data"
+ " buffer. You may want to enlarge the size\n");
+
+ /* FIXME: As we are now in kernel, should we do thing? */
+ i915_aub_setup_gtt(m);
+
+ while (p < end) {
+ if (p->type == AUB_TYPE_REG)
+ i915_aub_out_reg(m, p);
+ else
+ DRM_ERROR("Invaled aub type(%d)\n", p->type);
+
+ p++;
+ }
+
+ i915_aub_draw(m);
+
+ return 0;
+}
+
+static int i915_aub_reset(struct seq_file *m, void *unused)
+{
+ if (i915_aub_debug) {
+ mutex_lock(&i915_aub_data_lock);
+ i915_aub_data_ptr = (struct i915_aub_data *)i915_aub_data;
+ mutex_unlock(&i915_aub_data_lock);
+ }
+ return 0;
+}
+
static struct drm_info_list i915_debugfs_list[] = {
{"i915_capabilities", i915_capabilities, 0, 0},
{"i915_gem_objects", i915_gem_object_info, 0},
@@ -1044,6 +1204,8 @@ static struct drm_info_list i915_debugfs_list[] = {
{"i915_sr_status", i915_sr_status, 0},
{"i915_opregion", i915_opregion, 0},
{"i915_gem_framebuffer", i915_gem_framebuffer_info, 0},
+ {"i915.aub", i915_aub, 0},
+ {"i915_aub_reset", i915_aub_reset, 0},
};
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
@@ -1055,6 +1217,14 @@ int i915_debugfs_init(struct drm_minor *minor)
if (ret)
return ret;
+ if (i915_aub_debug) {
+ i915_aub_data = vmalloc(AUB_DATA_SIZE);
+ if (!i915_aub_data)
+ return -ENOMEM;
+ i915_aub_data_ptr = (struct i915_aub_data *)i915_aub_data;
+ mutex_init(&i915_aub_data_lock);
+ }
+
return drm_debugfs_create_files(i915_debugfs_list,
I915_DEBUGFS_ENTRIES,
minor->debugfs_root, minor);
@@ -1066,6 +1236,9 @@ void i915_debugfs_cleanup(struct drm_minor *minor)
I915_DEBUGFS_ENTRIES, minor);
drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops,
1, minor);
+
+ if (i915_aub_debug)
+ vfree(i915_aub_data);
}
#endif /* CONFIG_DEBUG_FS */
@@ -40,6 +40,9 @@
static int i915_modeset = -1;
module_param_named(modeset, i915_modeset, int, 0400);
+int i915_aub_debug = 0;
+module_param_named(aub_debug, i915_aub_debug, int, 0400);
+
unsigned int i915_fbpercrtc = 0;
module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400);
@@ -880,6 +880,50 @@ enum intel_chip_family {
CHIP_I965 = 0x08,
};
+#define AUB_CMD (7 << 29)
+#define AUB_APP_NAME_LEN 32
+
+/*
+ * 32 Page is really not enough. Since some registers, like CURAPOS and
+ * CURABASE will fill up the aub data buffer soon!
+ *
+ * So, if you want to record everything(impossible, in fact)!, change
+ * the size here.
+ */
+#define AUB_DATA_SIZE (32 << PAGE_SHIFT)
+
+#define AUB_HEADER_MAJOR_SHIFT 24
+#define AUB_HEADER_MINOR_SHIFT 16
+#define AUB_VERSION ((4 << AUB_HEADER_MAJOR_SHIFT) | \
+ (0 << AUB_HEADER_MINOR_SHIFT))
+
+#define AUB_CMD_HEADER (AUB_CMD | (1 << 23) | (0x05 << 16))
+#define AUB_CMD_TRACE_HEADER_BLOCK (AUB_CMD | (1 << 23) | (0x41 << 16))
+#define AUB_CMD_DRAW (AUB_CMD | (1 << 23) | (0xaf << 16))
+
+
+#define AUB_TRACE_MEMTYPE_GTT (0 << 16)
+#define AUB_TRACE_MEMTYPE_LOCAL (1 << 16)
+#define AUB_TRACE_MEMTYPE_NONLOCAL (2 << 16)
+
+#define AUB_TRACE_OP_DATA_WRITE 0x00000001
+#define AUB_TRACE_OP_MMIO_WRITE 0x00000003
+
+struct i915_aub_data {
+ uint32_t type:8;
+ uint32_t size:24;
+ uint32_t addr;
+ uint64_t data;
+};
+
+/* Currently support one type of data */
+enum i915_aub_type {
+ AUB_TYPE_REG = 1,
+};
+
+
+extern int i915_aub_debug;
+
extern struct drm_ioctl_desc i915_ioctls[];
extern int i915_max_ioctl;
extern unsigned int i915_fbpercrtc;
@@ -1105,6 +1149,7 @@ void i915_gem_dump_object(struct drm_gem_object *obj, int len,
/* i915_debugfs.c */
int i915_debugfs_init(struct drm_minor *minor);
void i915_debugfs_cleanup(struct drm_minor *minor);
+void i915_aub_write_reg(uint32_t reg, uint64_t val, int len);
/* i915_suspend.c */
extern int i915_save_state(struct drm_device *dev);
@@ -1195,18 +1240,43 @@ static inline u32 i915_read(struct drm_i915_private *dev_priv, u32 reg)
static inline void i915_write(struct drm_i915_private *dev_priv, u32 reg,
u32 val)
{
+ if (i915_aub_debug)
+ i915_aub_write_reg(reg, val, 4);
writel(val, dev_priv->regs + reg);
if (dev_priv->debug_flags & I915_DEBUG_WRITE)
printk(KERN_ERR "wrote 0x%08x to 0x%08x\n", val, reg);
}
+static inline void i915_writeb(struct drm_i915_private *dev_priv, u32 reg,
+ u8 val)
+{
+ if (i915_aub_debug)
+ i915_aub_write_reg(reg, val, 1);
+ writeb(val, dev_priv->regs + reg);
+}
+
+static inline void i915_writew(struct drm_i915_private *dev_priv, u32 reg,
+ u16 val)
+{
+ if (i915_aub_debug)
+ i915_aub_write_reg(reg, val, 2);
+ writew(val, dev_priv->regs + reg);
+}
+
+static inline void i915_writeq(struct drm_i915_private *dev_priv, u32 reg,
+ u64 val)
+{
+ if (i915_aub_debug)
+ i915_aub_write_reg(reg, val, 8);
+ writeq(val, dev_priv->regs + reg);
+}
#define I915_READ(reg) i915_read(dev_priv, (reg))
#define I915_WRITE(reg, val) i915_write(dev_priv, (reg), (val))
#define I915_READ16(reg) readw(dev_priv->regs + (reg))
-#define I915_WRITE16(reg, val) writel(val, dev_priv->regs + (reg))
+#define I915_WRITE16(reg, val) i915_writew(dev_priv, (reg), (val))
#define I915_READ8(reg) readb(dev_priv->regs + (reg))
-#define I915_WRITE8(reg, val) writeb(val, dev_priv->regs + (reg))
-#define I915_WRITE64(reg, val) writeq(val, dev_priv->regs + (reg))
+#define I915_WRITE8(reg, val) i915_writeb(dev_priv, (reg), (val))
+#define I915_WRITE64(reg, val) i915_writeq(dev_priv, (reg), (val))
#define I915_READ64(reg) readq(dev_priv->regs + (reg))
#define POSTING_READ(reg) (void)I915_READ(reg)
#define POSTING_READ16(reg) (void)I915_READ16(reg)