gpu_read(gpu, REG_A3XX_RBBM_STATUS));
        adreno_dump(gpu);
 }
+
+static struct msm_gpu_state *a3xx_gpu_state_get(struct msm_gpu *gpu)
+{
+       struct msm_gpu_state *state = adreno_gpu_state_get(gpu);
+
+       if (IS_ERR(state))
+               return state;
+
+       state->rbbm_status = gpu_read(gpu, REG_A3XX_RBBM_STATUS);
+
+       return state;
+}
+
 /* Register offset defines for A3XX */
 static const unsigned int a3xx_register_offsets[REG_ADRENO_REGISTER_MAX] = {
        REG_ADRENO_DEFINE(REG_ADRENO_CP_RB_BASE, REG_AXXX_CP_RB_BASE),
 #ifdef CONFIG_DEBUG_FS
                .show = a3xx_show,
 #endif
+               .gpu_state_get = a3xx_gpu_state_get,
+               .gpu_state_put = adreno_gpu_state_put,
        },
 };
 
 
 }
 #endif
 
+static struct msm_gpu_state *a4xx_gpu_state_get(struct msm_gpu *gpu)
+{
+       struct msm_gpu_state *state = adreno_gpu_state_get(gpu);
+
+       if (IS_ERR(state))
+               return state;
+
+       state->rbbm_status = gpu_read(gpu, REG_A4XX_RBBM_STATUS);
+
+       return state;
+}
+
 /* Register offset defines for A4XX, in order of enum adreno_regs */
 static const unsigned int a4xx_register_offsets[REG_ADRENO_REGISTER_MAX] = {
        REG_ADRENO_DEFINE(REG_ADRENO_CP_RB_BASE, REG_A4XX_CP_RB_BASE),
 #ifdef CONFIG_DEBUG_FS
                .show = a4xx_show,
 #endif
+               .gpu_state_get = a4xx_gpu_state_get,
+               .gpu_state_put = adreno_gpu_state_put,
        },
        .get_timestamp = a4xx_get_timestamp,
 };
 
        return 0;
 }
 
+static struct msm_gpu_state *a5xx_gpu_state_get(struct msm_gpu *gpu)
+{
+       struct msm_gpu_state *state;
+
+       /*
+        * Temporarily disable hardware clock gating before going into
+        * adreno_show to avoid issues while reading the registers
+        */
+       a5xx_set_hwcg(gpu, false);
+
+       state = adreno_gpu_state_get(gpu);
+
+       if (!IS_ERR(state))
+               state->rbbm_status = gpu_read(gpu, REG_A5XX_RBBM_STATUS);
+
+       a5xx_set_hwcg(gpu, true);
+
+       return state;
+}
+
 #ifdef CONFIG_DEBUG_FS
 static void a5xx_show(struct msm_gpu *gpu, struct seq_file *m)
 {
                .debugfs_init = a5xx_debugfs_init,
 #endif
                .gpu_busy = a5xx_gpu_busy,
+               .gpu_state_get = a5xx_gpu_state_get,
+               .gpu_state_put = adreno_gpu_state_put,
        },
        .get_timestamp = a5xx_get_timestamp,
 };
 
        return false;
 }
 
+struct msm_gpu_state *adreno_gpu_state_get(struct msm_gpu *gpu)
+{
+       struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+       struct msm_gpu_state *state;
+       int i, count = 0;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return ERR_PTR(-ENOMEM);
+
+       do_gettimeofday(&state->time);
+
+       for (i = 0; i < gpu->nr_rings; i++) {
+               state->ring[i].fence = gpu->rb[i]->memptrs->fence;
+               state->ring[i].iova = gpu->rb[i]->iova;
+               state->ring[i].seqno = gpu->rb[i]->seqno;
+               state->ring[i].rptr = get_rptr(adreno_gpu, gpu->rb[i]);
+               state->ring[i].wptr = get_wptr(gpu->rb[i]);
+       }
+
+       /* Count the number of registers */
+       for (i = 0; adreno_gpu->registers[i] != ~0; i += 2)
+               count += adreno_gpu->registers[i + 1] -
+                       adreno_gpu->registers[i] + 1;
+
+       state->registers = kcalloc(count * 2, sizeof(u32), GFP_KERNEL);
+       if (state->registers) {
+               int pos = 0;
+
+               for (i = 0; adreno_gpu->registers[i] != ~0; i += 2) {
+                       u32 start = adreno_gpu->registers[i];
+                       u32 end   = adreno_gpu->registers[i + 1];
+                       u32 addr;
+
+                       for (addr = start; addr <= end; addr++) {
+                               state->registers[pos++] = addr;
+                               state->registers[pos++] = gpu_read(gpu, addr);
+                       }
+               }
+
+               state->nr_registers = count;
+       }
+
+       return state;
+}
+
+void adreno_gpu_state_put(struct msm_gpu_state *state)
+{
+       if (IS_ERR_OR_NULL(state))
+               return;
+
+       kfree(state->registers);
+       kfree(state);
+}
+
 #ifdef CONFIG_DEBUG_FS
 void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
 {
 
 void adreno_gpu_cleanup(struct adreno_gpu *gpu);
 
 
+struct msm_gpu_state *adreno_gpu_state_get(struct msm_gpu *gpu);
+void adreno_gpu_state_put(struct msm_gpu_state *state);
+
 /* ringbuffer helpers (the parts that are adreno specific) */
 
 static inline void
 
 
 struct msm_gem_submit;
 struct msm_gpu_perfcntr;
+struct msm_gpu_state;
 
 struct msm_gpu_config {
        const char *ioname;
        int (*debugfs_init)(struct msm_gpu *gpu, struct drm_minor *minor);
 #endif
        int (*gpu_busy)(struct msm_gpu *gpu, uint64_t *value);
+       struct msm_gpu_state *(*gpu_state_get)(struct msm_gpu *gpu);
+       void (*gpu_state_put)(struct msm_gpu_state *state);
 };
 
 struct msm_gpu {
        struct kref ref;
 };
 
+struct msm_gpu_state {
+       struct timeval time;
+
+       struct {
+               u64 iova;
+               u32 fence;
+               u32 seqno;
+               u32 rptr;
+               u32 wptr;
+       } ring[MSM_GPU_MAX_RINGS];
+
+       int nr_registers;
+       u32 *registers;
+
+       u32 rbbm_status;
+};
+
 static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
 {
        msm_writel(data, gpu->mmio + (reg << 2));