Add the infrastructure to support the idea of multiple ringbuffers.
Assign each ringbuffer an id and use that as an index for the various
ring specific operations.

The biggest delta is to support legacy fences. Each fence gets its own
sequence number but the legacy functions expect to use a unique integer.
To handle this we return a unique identifer for each submission but
map it to a specific ring/sequence under the covers. Newer users use
a dma_fence pointer anyway so they don't care about the actual sequence
ID or ring.

The actual mechanics for multiple ringbuffers are very target specific
so this code just allows for the possibility but still only defines
one ringbuffer for each target family.

Signed-off-by: Jordan Crouse <jcro...@codeaurora.org>
---
 drivers/gpu/drm/msm/adreno/a3xx_gpu.c   |   9 +-
 drivers/gpu/drm/msm/adreno/a4xx_gpu.c   |   9 +-
 drivers/gpu/drm/msm/adreno/a5xx_gpu.c   |  45 +++++-----
 drivers/gpu/drm/msm/adreno/a5xx_gpu.h   |   2 +-
 drivers/gpu/drm/msm/adreno/a5xx_power.c |   6 +-
 drivers/gpu/drm/msm/adreno/adreno_gpu.c | 155 +++++++++++++++++++++-----------
 drivers/gpu/drm/msm/adreno/adreno_gpu.h |  36 +++++---
 drivers/gpu/drm/msm/msm_drv.h           |   2 +
 drivers/gpu/drm/msm/msm_fence.c         |  85 +++++++++++++-----
 drivers/gpu/drm/msm/msm_fence.h         |  13 +--
 drivers/gpu/drm/msm/msm_gem.h           |   1 +
 drivers/gpu/drm/msm/msm_gem_submit.c    |  10 ++-
 drivers/gpu/drm/msm/msm_gpu.c           | 122 ++++++++++++++++---------
 drivers/gpu/drm/msm/msm_gpu.h           |  38 ++++++--
 drivers/gpu/drm/msm/msm_ringbuffer.c    |  13 ++-
 drivers/gpu/drm/msm/msm_ringbuffer.h    |   8 +-
 include/uapi/drm/msm_drm.h              |   5 ++
 17 files changed, 373 insertions(+), 186 deletions(-)

diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c 
b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
index fc4fd2d..2f72848 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
@@ -44,7 +44,7 @@
 
 static bool a3xx_me_init(struct msm_gpu *gpu)
 {
-       struct msm_ringbuffer *ring = gpu->rb;
+       struct msm_ringbuffer *ring = gpu->rb[0];
 
        OUT_PKT3(ring, CP_ME_INIT, 17);
        OUT_RING(ring, 0x000003f7);
@@ -65,7 +65,7 @@ static bool a3xx_me_init(struct msm_gpu *gpu)
        OUT_RING(ring, 0x00000000);
        OUT_RING(ring, 0x00000000);
 
-       gpu->funcs->flush(gpu);
+       gpu->funcs->flush(gpu, ring);
        return a3xx_idle(gpu);
 }
 
@@ -339,7 +339,7 @@ static void a3xx_destroy(struct msm_gpu *gpu)
 static bool a3xx_idle(struct msm_gpu *gpu)
 {
        /* wait for ringbuffer to drain: */
-       if (!adreno_idle(gpu))
+       if (!adreno_idle(gpu, gpu->rb[0]))
                return false;
 
        /* then wait for GPU to finish: */
@@ -449,6 +449,7 @@ static void a3xx_dump(struct msm_gpu *gpu)
                .last_fence = adreno_last_fence,
                .submit = adreno_submit,
                .flush = adreno_flush,
+               .active_ring = adreno_active_ring,
                .irq = a3xx_irq,
                .destroy = a3xx_destroy,
 #ifdef CONFIG_DEBUG_FS
@@ -496,7 +497,7 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
        adreno_gpu->registers = a3xx_registers;
        adreno_gpu->reg_offsets = a3xx_register_offsets;
 
-       ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs);
+       ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1);
        if (ret)
                goto fail;
 
diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c 
b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c
index 6bc948b..bdd2a24 100644
--- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c
@@ -116,7 +116,7 @@ static void a4xx_enable_hwcg(struct msm_gpu *gpu)
 
 static bool a4xx_me_init(struct msm_gpu *gpu)
 {
-       struct msm_ringbuffer *ring = gpu->rb;
+       struct msm_ringbuffer *ring = gpu->rb[0];
 
        OUT_PKT3(ring, CP_ME_INIT, 17);
        OUT_RING(ring, 0x000003f7);
@@ -137,7 +137,7 @@ static bool a4xx_me_init(struct msm_gpu *gpu)
        OUT_RING(ring, 0x00000000);
        OUT_RING(ring, 0x00000000);
 
-       gpu->funcs->flush(gpu);
+       gpu->funcs->flush(gpu, ring);
        return a4xx_idle(gpu);
 }
 
@@ -337,7 +337,7 @@ static void a4xx_destroy(struct msm_gpu *gpu)
 static bool a4xx_idle(struct msm_gpu *gpu)
 {
        /* wait for ringbuffer to drain: */
-       if (!adreno_idle(gpu))
+       if (!adreno_idle(gpu, gpu->rb[0]))
                return false;
 
        /* then wait for GPU to finish: */
@@ -539,6 +539,7 @@ static int a4xx_get_timestamp(struct msm_gpu *gpu, uint64_t 
*value)
                .last_fence = adreno_last_fence,
                .submit = adreno_submit,
                .flush = adreno_flush,
+               .active_ring = adreno_active_ring,
                .irq = a4xx_irq,
                .destroy = a4xx_destroy,
 #ifdef CONFIG_DEBUG_FS
@@ -580,7 +581,7 @@ struct msm_gpu *a4xx_gpu_init(struct drm_device *dev)
        adreno_gpu->registers = a4xx_registers;
        adreno_gpu->reg_offsets = a4xx_register_offsets;
 
-       ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs);
+       ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1);
        if (ret)
                goto fail;
 
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c 
b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index 546280c..5f02ff3 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -189,7 +189,7 @@ static void a5xx_submit(struct msm_gpu *gpu, struct 
msm_gem_submit *submit,
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
        struct msm_drm_private *priv = gpu->dev->dev_private;
-       struct msm_ringbuffer *ring = gpu->rb;
+       struct msm_ringbuffer *ring = submit->ring;
        unsigned int i, ibs = 0;
 
        for (i = 0; i < submit->nr_cmds; i++) {
@@ -214,11 +214,11 @@ static void a5xx_submit(struct msm_gpu *gpu, struct 
msm_gem_submit *submit,
 
        OUT_PKT7(ring, CP_EVENT_WRITE, 4);
        OUT_RING(ring, CACHE_FLUSH_TS | (1 << 31));
-       OUT_RING(ring, lower_32_bits(rbmemptr(adreno_gpu, fence)));
-       OUT_RING(ring, upper_32_bits(rbmemptr(adreno_gpu, fence)));
+       OUT_RING(ring, lower_32_bits(rbmemptr(adreno_gpu, ring->id, fence)));
+       OUT_RING(ring, upper_32_bits(rbmemptr(adreno_gpu, ring->id, fence)));
        OUT_RING(ring, submit->fence->seqno);
 
-       gpu->funcs->flush(gpu);
+       gpu->funcs->flush(gpu, ring);
 }
 
 struct a5xx_hwcg {
@@ -358,7 +358,7 @@ static void a5xx_enable_hwcg(struct msm_gpu *gpu)
 static int a5xx_me_init(struct msm_gpu *gpu)
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
-       struct msm_ringbuffer *ring = gpu->rb;
+       struct msm_ringbuffer *ring = gpu->rb[0];
 
        OUT_PKT7(ring, CP_ME_INIT, 8);
 
@@ -389,9 +389,8 @@ static int a5xx_me_init(struct msm_gpu *gpu)
        OUT_RING(ring, 0x00000000);
        OUT_RING(ring, 0x00000000);
 
-       gpu->funcs->flush(gpu);
-
-       return a5xx_idle(gpu) ? 0 : -EINVAL;
+       gpu->funcs->flush(gpu, ring);
+       return a5xx_idle(gpu, ring) ? 0 : -EINVAL;
 }
 
 static struct drm_gem_object *a5xx_ucode_load_bo(struct msm_gpu *gpu,
@@ -695,11 +694,11 @@ static int a5xx_hw_init(struct msm_gpu *gpu)
         * ticking correctly
         */
        if (adreno_is_a530(adreno_gpu)) {
-               OUT_PKT7(gpu->rb, CP_EVENT_WRITE, 1);
-               OUT_RING(gpu->rb, 0x0F);
+               OUT_PKT7(gpu->rb[0], CP_EVENT_WRITE, 1);
+               OUT_RING(gpu->rb[0], 0x0F);
 
-               gpu->funcs->flush(gpu);
-               if (!a5xx_idle(gpu))
+               gpu->funcs->flush(gpu, gpu->rb[0]);
+               if (!a5xx_idle(gpu, gpu->rb[0]))
                        return -EINVAL;
        }
 
@@ -712,11 +711,11 @@ static int a5xx_hw_init(struct msm_gpu *gpu)
         */
        ret = a5xx_zap_shader_init(gpu);
        if (!ret) {
-               OUT_PKT7(gpu->rb, CP_SET_SECURE_MODE, 1);
-               OUT_RING(gpu->rb, 0x00000000);
+               OUT_PKT7(gpu->rb[0], CP_SET_SECURE_MODE, 1);
+               OUT_RING(gpu->rb[0], 0x00000000);
 
-               gpu->funcs->flush(gpu);
-               if (!a5xx_idle(gpu))
+               gpu->funcs->flush(gpu, gpu->rb[0]);
+               if (!a5xx_idle(gpu, gpu->rb[0]))
                        return -EINVAL;
        } else {
                /* Print a warning so if we die, we know why */
@@ -790,15 +789,18 @@ static inline bool _a5xx_check_idle(struct msm_gpu *gpu)
                A5XX_RBBM_INT_0_MASK_MISC_HANG_DETECT);
 }
 
-bool a5xx_idle(struct msm_gpu *gpu)
+bool a5xx_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
 {
        /* wait for CP to drain ringbuffer: */
-       if (!adreno_idle(gpu))
+       if (!adreno_idle(gpu, ring))
                return false;
 
        if (spin_until(_a5xx_check_idle(gpu))) {
-               DRM_ERROR("%s: %ps: timeout waiting for GPU to idle: status 
%8.8X irq %8.8X\n",
-                       gpu->name, __builtin_return_address(0),
+               DRM_ERROR(
+                       "%s: timeout waiting for GPU RB %d to idle: status 
%8.8X rptr/wptr: %4.4X/%4.4X irq %8.8X\n",
+                       gpu->name, ring->id,
+                       gpu_read(gpu, REG_A5XX_CP_RB_RPTR),
+                       gpu_read(gpu, REG_A5XX_CP_RB_WPTR),
                        gpu_read(gpu, REG_A5XX_RBBM_STATUS),
                        gpu_read(gpu, REG_A5XX_RBBM_INT_0_STATUS));
 
@@ -1091,6 +1093,7 @@ static void a5xx_show(struct msm_gpu *gpu, struct 
seq_file *m)
                .last_fence = adreno_last_fence,
                .submit = a5xx_submit,
                .flush = adreno_flush,
+               .active_ring = adreno_active_ring,
                .irq = a5xx_irq,
                .destroy = a5xx_destroy,
                .show = a5xx_show,
@@ -1125,7 +1128,7 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev)
 
        a5xx_gpu->lm_leakage = 0x4E001A;
 
-       ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs);
+       ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1);
        if (ret) {
                a5xx_destroy(&(a5xx_gpu->base.base));
                return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h 
b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
index 6b20f28..405b563 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
@@ -56,6 +56,6 @@ static inline int spin_usecs(struct msm_gpu *gpu, uint32_t 
usecs,
        return -ETIMEDOUT;
 }
 
-bool a5xx_idle(struct msm_gpu *gpu);
+bool a5xx_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
 
 #endif /* __A5XX_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_power.c 
b/drivers/gpu/drm/msm/adreno/a5xx_power.c
index 2fdee44..a7d91ac 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_power.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_power.c
@@ -173,7 +173,7 @@ static int a5xx_gpmu_init(struct msm_gpu *gpu)
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
        struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
-       struct msm_ringbuffer *ring = gpu->rb;
+       struct msm_ringbuffer *ring = gpu->rb[0];
 
        if (!a5xx_gpu->gpmu_dwords)
                return 0;
@@ -192,9 +192,9 @@ static int a5xx_gpmu_init(struct msm_gpu *gpu)
        OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1);
        OUT_RING(ring, 1);
 
-       gpu->funcs->flush(gpu);
+       gpu->funcs->flush(gpu, ring);
 
-       if (!a5xx_idle(gpu)) {
+       if (!a5xx_idle(gpu, ring)) {
                DRM_ERROR("%s: Unable to load GPMU firmware. GPMU will not be 
active\n",
                        gpu->name);
                return -EINVAL;
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c 
b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index 4c3e9b3..f5c2bad 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -21,7 +21,6 @@
 #include "msm_gem.h"
 #include "msm_mmu.h"
 
-#define RB_SIZE    SZ_32K
 #define RB_BLKSIZE 32
 
 int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)
@@ -57,32 +56,36 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, 
uint64_t *value)
 int adreno_hw_init(struct msm_gpu *gpu)
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
-       int ret;
+       int i;
 
        DBG("%s", gpu->name);
 
-       ret = msm_gem_get_iova(gpu->rb->bo, gpu->aspace, &gpu->rb_iova);
-       if (ret) {
-               gpu->rb_iova = 0;
-               dev_err(gpu->dev->dev, "could not map ringbuffer: %d\n", ret);
-               return ret;
+       for (i = 0; i < gpu->nr_rings; i++) {
+               int ret = msm_gem_get_iova(gpu->rb[i]->bo, gpu->aspace,
+                       &gpu->rb[i]->iova);
+               if (ret) {
+                       gpu->rb[i]->iova = 0;
+                       dev_err(gpu->dev->dev,
+                               "could not map ringbuffer %d: %d\n", i, ret);
+                       return ret;
+               }
        }
 
        /* Setup REG_CP_RB_CNTL: */
        adreno_gpu_write(adreno_gpu, REG_ADRENO_CP_RB_CNTL,
-                       /* size is log2(quad-words): */
-                       AXXX_CP_RB_CNTL_BUFSZ(ilog2(gpu->rb->size / 8)) |
-                       AXXX_CP_RB_CNTL_BLKSZ(ilog2(RB_BLKSIZE / 8)) |
-                       (adreno_is_a430(adreno_gpu) ? AXXX_CP_RB_CNTL_NO_UPDATE 
: 0));
+               /* size is log2(quad-words): */
+               AXXX_CP_RB_CNTL_BUFSZ(ilog2(MSM_GPU_RINGBUFFER_SZ / 8)) |
+               AXXX_CP_RB_CNTL_BLKSZ(ilog2(RB_BLKSIZE / 8)) |
+               (adreno_is_a430(adreno_gpu) ? AXXX_CP_RB_CNTL_NO_UPDATE : 0));
 
-       /* Setup ringbuffer address: */
+       /* Setup ringbuffer address - use ringbuffer[0] for GPU init */
        adreno_gpu_write64(adreno_gpu, REG_ADRENO_CP_RB_BASE,
-               REG_ADRENO_CP_RB_BASE_HI, gpu->rb_iova);
+               REG_ADRENO_CP_RB_BASE_HI, gpu->rb[0]->iova);
 
        if (!adreno_is_a430(adreno_gpu)) {
                adreno_gpu_write64(adreno_gpu, REG_ADRENO_CP_RB_RPTR_ADDR,
                        REG_ADRENO_CP_RB_RPTR_ADDR_HI,
-                       rbmemptr(adreno_gpu, rptr));
+                       rbmemptr(adreno_gpu, 0, rptr));
        }
 
        return 0;
@@ -94,35 +97,58 @@ static uint32_t get_wptr(struct msm_ringbuffer *ring)
 }
 
 /* Use this helper to read rptr, since a430 doesn't update rptr in memory */
-static uint32_t get_rptr(struct adreno_gpu *adreno_gpu)
+static uint32_t get_rptr(struct adreno_gpu *adreno_gpu,
+               struct msm_ringbuffer *ring)
 {
-       if (adreno_is_a430(adreno_gpu))
-               return adreno_gpu->memptrs->rptr = adreno_gpu_read(
+       if (adreno_is_a430(adreno_gpu)) {
+               /*
+                * If index is anything but 0 this will probably break horribly,
+                * but I think that we have enough infrastructure in place to
+                * ensure that it won't be. If not then this is why your
+                * a430 stopped working.
+                */
+               return adreno_gpu->memptrs->rptr[ring->id] = adreno_gpu_read(
                        adreno_gpu, REG_ADRENO_CP_RB_RPTR);
-       else
-               return adreno_gpu->memptrs->rptr;
+       } else
+               return adreno_gpu->memptrs->rptr[ring->id];
 }
 
-uint32_t adreno_last_fence(struct msm_gpu *gpu)
+struct msm_ringbuffer *adreno_active_ring(struct msm_gpu *gpu)
+{
+       return gpu->rb[0];
+}
+
+uint32_t adreno_last_fence(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
-       return adreno_gpu->memptrs->fence;
+
+       if (!ring)
+               return 0;
+
+       return adreno_gpu->memptrs->fence[ring->id];
 }
 
 void adreno_recover(struct msm_gpu *gpu)
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
        struct drm_device *dev = gpu->dev;
-       int ret;
+       struct msm_ringbuffer *ring;
+       int ret, i;
 
        gpu->funcs->pm_suspend(gpu);
 
-       /* reset ringbuffer: */
-       gpu->rb->cur = gpu->rb->start;
+       /* reset ringbuffer(s): */
+
+       FOR_EACH_RING(gpu, ring, i) {
+               if (!ring)
+                       continue;
+
+               ring->cur = ring->start;
 
-       /* reset completed fence seqno: */
-       adreno_gpu->memptrs->fence = gpu->fctx->completed_fence;
-       adreno_gpu->memptrs->rptr  = 0;
+               /* reset completed fence seqno, discard anything pending: */
+               adreno_gpu->memptrs->fence[ring->id] = ring->completed_fence;
+               adreno_gpu->memptrs->rptr[ring->id]  = 0;
+       }
 
        gpu->funcs->pm_resume(gpu);
 
@@ -140,7 +166,7 @@ void adreno_submit(struct msm_gpu *gpu, struct 
msm_gem_submit *submit,
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
        struct msm_drm_private *priv = gpu->dev->dev_private;
-       struct msm_ringbuffer *ring = gpu->rb;
+       struct msm_ringbuffer *ring = submit->ring;
        unsigned i;
 
        for (i = 0; i < submit->nr_cmds; i++) {
@@ -179,7 +205,7 @@ void adreno_submit(struct msm_gpu *gpu, struct 
msm_gem_submit *submit,
 
        OUT_PKT3(ring, CP_EVENT_WRITE, 3);
        OUT_RING(ring, CACHE_FLUSH_TS);
-       OUT_RING(ring, rbmemptr(adreno_gpu, fence));
+       OUT_RING(ring, rbmemptr(adreno_gpu, ring->id, fence));
        OUT_RING(ring, submit->fence->seqno);
 
        /* we could maybe be clever and only CP_COND_EXEC the interrupt: */
@@ -206,10 +232,10 @@ void adreno_submit(struct msm_gpu *gpu, struct 
msm_gem_submit *submit,
        }
 #endif
 
-       gpu->funcs->flush(gpu);
+       gpu->funcs->flush(gpu, ring);
 }
 
-void adreno_flush(struct msm_gpu *gpu)
+void adreno_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
        uint32_t wptr;
@@ -219,7 +245,7 @@ void adreno_flush(struct msm_gpu *gpu)
         * to account for the possibility that the last command fit exactly into
         * the ringbuffer and rb->next hasn't wrapped to zero yet
         */
-       wptr = get_wptr(gpu->rb) & ((gpu->rb->size / 4) - 1);
+       wptr = get_wptr(ring) % (MSM_GPU_RINGBUFFER_SZ >> 2);
 
        /* ensure writes to ringbuffer have hit system memory: */
        mb();
@@ -227,17 +253,18 @@ void adreno_flush(struct msm_gpu *gpu)
        adreno_gpu_write(adreno_gpu, REG_ADRENO_CP_RB_WPTR, wptr);
 }
 
-bool adreno_idle(struct msm_gpu *gpu)
+bool adreno_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
-       uint32_t wptr = get_wptr(gpu->rb);
+       uint32_t wptr = get_wptr(ring);
 
        /* wait for CP to drain ringbuffer: */
-       if (!spin_until(get_rptr(adreno_gpu) == wptr))
+       if (!spin_until(get_rptr(adreno_gpu, ring) == wptr))
                return true;
 
        /* TODO maybe we need to reset GPU here to recover from hang? */
-       DRM_ERROR("%s: timeout waiting to drain ringbuffer!\n", gpu->name);
+       DRM_ERROR("%s: timeout waiting to drain ringbuffer %d!\n", gpu->name,
+               ring->id);
        return false;
 }
 
@@ -245,6 +272,7 @@ bool adreno_idle(struct msm_gpu *gpu)
 void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+       struct msm_ringbuffer *ring;
        int i;
 
        seq_printf(m, "revision: %d (%d.%d.%d.%d)\n",
@@ -252,10 +280,18 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
                        adreno_gpu->rev.major, adreno_gpu->rev.minor,
                        adreno_gpu->rev.patchid);
 
-       seq_printf(m, "fence:    %d/%d\n", adreno_gpu->memptrs->fence,
-                       gpu->fctx->last_fence);
-       seq_printf(m, "rptr:     %d\n", get_rptr(adreno_gpu));
-       seq_printf(m, "rb wptr:  %d\n", get_wptr(gpu->rb));
+       FOR_EACH_RING(gpu, ring, i) {
+               if (!ring)
+                       continue;
+
+               seq_printf(m, "rb %d: fence:    %d/%d\n", i,
+                       adreno_last_fence(gpu, ring),
+                       ring->completed_fence);
+
+               seq_printf(m, "      rptr:     %d\n",
+                       get_rptr(adreno_gpu, ring));
+               seq_printf(m, "rb wptr:  %d\n", get_wptr(ring));
+       }
 
        gpu->funcs->pm_resume(gpu);
 
@@ -285,16 +321,25 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
 void adreno_dump_info(struct msm_gpu *gpu)
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+       struct msm_ringbuffer *ring;
+       int i;
 
        printk("revision: %d (%d.%d.%d.%d)\n",
                        adreno_gpu->info->revn, adreno_gpu->rev.core,
                        adreno_gpu->rev.major, adreno_gpu->rev.minor,
                        adreno_gpu->rev.patchid);
 
-       printk("fence:    %d/%d\n", adreno_gpu->memptrs->fence,
-                       gpu->fctx->last_fence);
-       printk("rptr:     %d\n", get_rptr(adreno_gpu));
-       printk("rb wptr:  %d\n", get_wptr(gpu->rb));
+       FOR_EACH_RING(gpu, ring, i) {
+               if (!ring)
+                       continue;
+
+               printk("rb %d: fence:    %d/%d\n", i,
+                       adreno_last_fence(gpu, ring),
+                       ring->completed_fence);
+
+               printk("rptr:     %d\n", get_rptr(adreno_gpu, ring));
+               printk("rb wptr:  %d\n", get_wptr(ring));
+       }
 }
 
 /* would be nice to not have to duplicate the _show() stuff with printk(): */
@@ -317,19 +362,20 @@ void adreno_dump(struct msm_gpu *gpu)
        }
 }
 
-static uint32_t ring_freewords(struct msm_gpu *gpu)
+static uint32_t ring_freewords(struct msm_ringbuffer *ring)
 {
-       struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
-       uint32_t size = gpu->rb->size / 4;
-       uint32_t wptr = get_wptr(gpu->rb);
-       uint32_t rptr = get_rptr(adreno_gpu);
+       struct adreno_gpu *adreno_gpu = to_adreno_gpu(ring->gpu);
+       uint32_t size = MSM_GPU_RINGBUFFER_SZ >> 2;
+       uint32_t wptr = get_wptr(ring);
+       uint32_t rptr = get_rptr(adreno_gpu, ring);
        return (rptr + (size - 1) - wptr) % size;
 }
 
-void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords)
+void adreno_wait_ring(struct msm_ringbuffer *ring, uint32_t ndwords)
 {
-       if (spin_until(ring_freewords(gpu) >= ndwords))
-               DRM_ERROR("%s: timeout waiting for ringbuffer space\n", 
gpu->name);
+       if (spin_until(ring_freewords(ring) >= ndwords))
+               DRM_ERROR("%s: timeout waiting for space in ringubffer %d\n",
+                       ring->gpu->name, ring->id);
 }
 
 static const char *iommu_ports[] = {
@@ -338,7 +384,8 @@ void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords)
 };
 
 int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
-               struct adreno_gpu *adreno_gpu, const struct adreno_gpu_funcs 
*funcs)
+               struct adreno_gpu *adreno_gpu,
+               const struct adreno_gpu_funcs *funcs, int nr_rings)
 {
        struct adreno_platform_config *config = pdev->dev.platform_data;
        struct msm_gpu_config adreno_gpu_config  = { 0 };
@@ -368,7 +415,7 @@ int adreno_gpu_init(struct drm_device *drm, struct 
platform_device *pdev,
        adreno_gpu_config.va_start = SZ_16M;
        adreno_gpu_config.va_end = 0xffffffff;
 
-       adreno_gpu_config.ringsz = RB_SIZE;
+       adreno_gpu_config.nr_rings = nr_rings;
 
        ret = msm_gpu_init(drm, pdev, &adreno_gpu->base, &funcs->base,
                        adreno_gpu->info->name, &adreno_gpu_config);
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h 
b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index fdf4ef3..f5118ad 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -80,12 +80,18 @@ struct adreno_info {
 
 const struct adreno_info *adreno_info(struct adreno_rev rev);
 
-#define rbmemptr(adreno_gpu, member)  \
+#define _sizeof(member) \
+       sizeof(((struct adreno_rbmemptrs *) 0)->member[0])
+
+#define _base(adreno_gpu, member)  \
        ((adreno_gpu)->memptrs_iova + offsetof(struct adreno_rbmemptrs, member))
 
+#define rbmemptr(adreno_gpu, index, member) \
+       (_base((adreno_gpu), member) + ((index) * _sizeof(member)))
+
 struct adreno_rbmemptrs {
-       volatile uint32_t rptr;
-       volatile uint32_t fence;
+       volatile uint32_t rptr[MSM_GPU_MAX_RINGS];
+       volatile uint32_t fence[MSM_GPU_MAX_RINGS];
 };
 
 struct adreno_gpu {
@@ -198,21 +204,25 @@ static inline int adreno_is_a530(struct adreno_gpu *gpu)
 
 int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value);
 int adreno_hw_init(struct msm_gpu *gpu);
-uint32_t adreno_last_fence(struct msm_gpu *gpu);
+uint32_t adreno_last_fence(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
+uint32_t adreno_submitted_fence(struct msm_gpu *gpu,
+               struct msm_ringbuffer *ring);
 void adreno_recover(struct msm_gpu *gpu);
 void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
                struct msm_file_private *ctx);
-void adreno_flush(struct msm_gpu *gpu);
-bool adreno_idle(struct msm_gpu *gpu);
+void adreno_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
+bool adreno_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
 #ifdef CONFIG_DEBUG_FS
 void adreno_show(struct msm_gpu *gpu, struct seq_file *m);
 #endif
 void adreno_dump_info(struct msm_gpu *gpu);
 void adreno_dump(struct msm_gpu *gpu);
-void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords);
+void adreno_wait_ring(struct msm_ringbuffer *ring, uint32_t ndwords);
+struct msm_ringbuffer *adreno_active_ring(struct msm_gpu *gpu);
 
 int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
-               struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs);
+               struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
+               int nr_rings);
 void adreno_gpu_cleanup(struct adreno_gpu *gpu);
 
 
@@ -221,7 +231,7 @@ int adreno_gpu_init(struct drm_device *drm, struct 
platform_device *pdev,
 static inline void
 OUT_PKT0(struct msm_ringbuffer *ring, uint16_t regindx, uint16_t cnt)
 {
-       adreno_wait_ring(ring->gpu, cnt+1);
+       adreno_wait_ring(ring, cnt+1);
        OUT_RING(ring, CP_TYPE0_PKT | ((cnt-1) << 16) | (regindx & 0x7FFF));
 }
 
@@ -229,14 +239,14 @@ int adreno_gpu_init(struct drm_device *drm, struct 
platform_device *pdev,
 static inline void
 OUT_PKT2(struct msm_ringbuffer *ring)
 {
-       adreno_wait_ring(ring->gpu, 1);
+       adreno_wait_ring(ring, 1);
        OUT_RING(ring, CP_TYPE2_PKT);
 }
 
 static inline void
 OUT_PKT3(struct msm_ringbuffer *ring, uint8_t opcode, uint16_t cnt)
 {
-       adreno_wait_ring(ring->gpu, cnt+1);
+       adreno_wait_ring(ring, cnt+1);
        OUT_RING(ring, CP_TYPE3_PKT | ((cnt-1) << 16) | ((opcode & 0xFF) << 8));
 }
 
@@ -258,14 +268,14 @@ static inline u32 PM4_PARITY(u32 val)
 static inline void
 OUT_PKT4(struct msm_ringbuffer *ring, uint16_t regindx, uint16_t cnt)
 {
-       adreno_wait_ring(ring->gpu, cnt + 1);
+       adreno_wait_ring(ring, cnt + 1);
        OUT_RING(ring, PKT4(regindx, cnt));
 }
 
 static inline void
 OUT_PKT7(struct msm_ringbuffer *ring, uint8_t opcode, uint16_t cnt)
 {
-       adreno_wait_ring(ring->gpu, cnt + 1);
+       adreno_wait_ring(ring, cnt + 1);
        OUT_RING(ring, CP_TYPE7_PKT | (cnt << 0) | (PM4_PARITY(cnt) << 15) |
                ((opcode & 0x7F) << 16) | (PM4_PARITY(opcode) << 23));
 }
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 0c1a49e..7ff7a83 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -78,6 +78,8 @@ struct msm_vblank_ctrl {
        spinlock_t lock;
 };
 
+#define MSM_GPU_MAX_RINGS 1
+
 struct msm_drm_private {
 
        struct drm_device *dev;
diff --git a/drivers/gpu/drm/msm/msm_fence.c b/drivers/gpu/drm/msm/msm_fence.c
index 3f299c5..c1e7614 100644
--- a/drivers/gpu/drm/msm/msm_fence.c
+++ b/drivers/gpu/drm/msm/msm_fence.c
@@ -20,7 +20,6 @@
 #include "msm_drv.h"
 #include "msm_fence.h"
 
-
 struct msm_fence_context *
 msm_fence_context_alloc(struct drm_device *dev, const char *name)
 {
@@ -32,9 +31,10 @@ struct msm_fence_context *
 
        fctx->dev = dev;
        fctx->name = name;
-       fctx->context = dma_fence_context_alloc(1);
+       fctx->context = dma_fence_context_alloc(MSM_GPU_MAX_RINGS);
        init_waitqueue_head(&fctx->event);
        spin_lock_init(&fctx->spinlock);
+       hash_init(fctx->hash);
 
        return fctx;
 }
@@ -44,64 +44,94 @@ void msm_fence_context_free(struct msm_fence_context *fctx)
        kfree(fctx);
 }
 
-static inline bool fence_completed(struct msm_fence_context *fctx, uint32_t 
fence)
+static inline bool fence_completed(struct msm_ringbuffer *ring, uint32_t fence)
+{
+       return (int32_t)(ring->completed_fence - fence) >= 0;
+}
+
+struct msm_fence {
+       struct msm_fence_context *fctx;
+       struct msm_ringbuffer *ring;
+       struct dma_fence base;
+       struct hlist_node node;
+       u32 fence_id;
+};
+
+static struct msm_fence *fence_from_id(struct msm_fence_context *fctx,
+               uint32_t id)
 {
-       return (int32_t)(fctx->completed_fence - fence) >= 0;
+       struct msm_fence *f;
+
+       hash_for_each_possible_rcu(fctx->hash, f, node, id) {
+               if (f->fence_id == id) {
+                       if (dma_fence_get_rcu(&f->base))
+                               return f;
+               }
+       }
+
+       return NULL;
 }
 
 /* legacy path for WAIT_FENCE ioctl: */
 int msm_wait_fence(struct msm_fence_context *fctx, uint32_t fence,
                ktime_t *timeout, bool interruptible)
 {
+       struct msm_fence *f = fence_from_id(fctx, fence);
        int ret;
 
-       if (fence > fctx->last_fence) {
-               DRM_ERROR("%s: waiting on invalid fence: %u (of %u)\n",
-                               fctx->name, fence, fctx->last_fence);
-               return -EINVAL;
+       /* If no active fence was found, there are two possibilities */
+       if (!f) {
+               /* The requested ID is newer than last issued - return error */
+               if (fence > fctx->fence_id) {
+                       DRM_ERROR("%s: waiting on invalid fence: %u (of %u)\n",
+                               fctx->name, fence, fctx->fence_id);
+                       return -EINVAL;
+               }
+
+               /* If the id has been issued assume fence has been retired */
+               return 0;
        }
 
        if (!timeout) {
                /* no-wait: */
-               ret = fence_completed(fctx, fence) ? 0 : -EBUSY;
+               ret = fence_completed(f->ring, f->base.seqno) ? 0 : -EBUSY;
        } else {
                unsigned long remaining_jiffies = timeout_to_jiffies(timeout);
 
                if (interruptible)
                        ret = wait_event_interruptible_timeout(fctx->event,
-                               fence_completed(fctx, fence),
+                               fence_completed(f->ring, f->base.seqno),
                                remaining_jiffies);
                else
                        ret = wait_event_timeout(fctx->event,
-                               fence_completed(fctx, fence),
+                               fence_completed(f->ring, f->base.seqno),
                                remaining_jiffies);
 
                if (ret == 0) {
                        DBG("timeout waiting for fence: %u (completed: %u)",
-                                       fence, fctx->completed_fence);
+                               f->base.seqno, f->ring->completed_fence);
                        ret = -ETIMEDOUT;
                } else if (ret != -ERESTARTSYS) {
                        ret = 0;
                }
        }
 
+       dma_fence_put(&f->base);
+
        return ret;
 }
 
 /* called from workqueue */
-void msm_update_fence(struct msm_fence_context *fctx, uint32_t fence)
+void msm_update_fence(struct msm_fence_context *fctx,
+               struct msm_ringbuffer *ring, uint32_t fence)
 {
        spin_lock(&fctx->spinlock);
-       fctx->completed_fence = max(fence, fctx->completed_fence);
+       ring->completed_fence = max(fence, ring->completed_fence);
        spin_unlock(&fctx->spinlock);
 
        wake_up_all(&fctx->event);
 }
 
-struct msm_fence {
-       struct msm_fence_context *fctx;
-       struct dma_fence base;
-};
 
 static inline struct msm_fence *to_msm_fence(struct dma_fence *fence)
 {
@@ -127,12 +157,17 @@ static bool msm_fence_enable_signaling(struct dma_fence 
*fence)
 static bool msm_fence_signaled(struct dma_fence *fence)
 {
        struct msm_fence *f = to_msm_fence(fence);
-       return fence_completed(f->fctx, f->base.seqno);
+       return fence_completed(f->ring, f->base.seqno);
 }
 
 static void msm_fence_release(struct dma_fence *fence)
 {
        struct msm_fence *f = to_msm_fence(fence);
+
+       spin_lock(&f->fctx->spinlock);
+       hash_del_rcu(&f->node);
+       spin_unlock(&f->fctx->spinlock);
+
        kfree_rcu(f, base.rcu);
 }
 
@@ -146,7 +181,7 @@ static void msm_fence_release(struct dma_fence *fence)
 };
 
 struct dma_fence *
-msm_fence_alloc(struct msm_fence_context *fctx)
+msm_fence_alloc(struct msm_fence_context *fctx, struct msm_ringbuffer *ring)
 {
        struct msm_fence *f;
 
@@ -155,9 +190,17 @@ struct dma_fence *
                return ERR_PTR(-ENOMEM);
 
        f->fctx = fctx;
+       f->ring = ring;
+
+       /* Make a user fence ID to pass back for the legacy functions */
+       f->fence_id = ++fctx->fence_id;
+
+       spin_lock(&fctx->spinlock);
+       hash_add(fctx->hash, &f->node, f->fence_id);
+       spin_unlock(&fctx->spinlock);
 
        dma_fence_init(&f->base, &msm_fence_ops, &fctx->spinlock,
-                      fctx->context, ++fctx->last_fence);
+                       fctx->context + ring->id, ++ring->last_fence);
 
        return &f->base;
 }
diff --git a/drivers/gpu/drm/msm/msm_fence.h b/drivers/gpu/drm/msm/msm_fence.h
index 56061aa..540dc61 100644
--- a/drivers/gpu/drm/msm/msm_fence.h
+++ b/drivers/gpu/drm/msm/msm_fence.h
@@ -18,17 +18,18 @@
 #ifndef __MSM_FENCE_H__
 #define __MSM_FENCE_H__
 
+#include <linux/hashtable.h>
 #include "msm_drv.h"
+#include "msm_ringbuffer.h"
 
 struct msm_fence_context {
        struct drm_device *dev;
        const char *name;
        unsigned context;
-       /* last_fence == completed_fence --> no pending work */
-       uint32_t last_fence;          /* last assigned fence */
-       uint32_t completed_fence;     /* last completed fence */
+       u32 fence_id;
        wait_queue_head_t event;
        spinlock_t spinlock;
+       DECLARE_HASHTABLE(hash, 4);
 };
 
 struct msm_fence_context * msm_fence_context_alloc(struct drm_device *dev,
@@ -39,8 +40,10 @@ int msm_wait_fence(struct msm_fence_context *fctx, uint32_t 
fence,
                ktime_t *timeout, bool interruptible);
 int msm_queue_fence_cb(struct msm_fence_context *fctx,
                struct msm_fence_cb *cb, uint32_t fence);
-void msm_update_fence(struct msm_fence_context *fctx, uint32_t fence);
+void msm_update_fence(struct msm_fence_context *fctx,
+               struct msm_ringbuffer *ring, uint32_t fence);
 
-struct dma_fence * msm_fence_alloc(struct msm_fence_context *fctx);
+struct dma_fence *msm_fence_alloc(struct msm_fence_context *fctx,
+               struct msm_ringbuffer *ring);
 
 #endif
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index 40cd0b6..633b5c1 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -120,6 +120,7 @@ struct msm_gem_submit {
        struct dma_fence *fence;
        struct pid *pid;    /* submitting process */
        bool valid;         /* true if no cmdstream patching needed */
+       struct msm_ringbuffer *ring;
        unsigned int nr_cmds;
        unsigned int nr_bos;
        struct {
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c 
b/drivers/gpu/drm/msm/msm_gem_submit.c
index 6fd7446..0907c60 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -389,7 +389,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
        struct sync_file *sync_file = NULL;
        int out_fence_fd = -1;
        unsigned i;
-       int ret;
+       int ret, ring;
 
        if (!gpu)
                return -ENXIO;
@@ -521,7 +521,13 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void 
*data,
 
        submit->nr_cmds = i;
 
-       submit->fence = msm_fence_alloc(gpu->fctx);
+       ring = clamp_t(uint32_t,
+               (args->flags & MSM_SUBMIT_RING_MASK) >> MSM_SUBMIT_RING_SHIFT,
+               0, gpu->nr_rings - 1);
+
+       submit->ring = gpu->rb[ring];
+
+       submit->fence = msm_fence_alloc(gpu->fctx, submit->ring);
        if (IS_ERR(submit->fence)) {
                ret = PTR_ERR(submit->fence);
                submit->fence = NULL;
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index bc75425..c7f0ea9 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -274,15 +274,37 @@ static void recover_worker(struct work_struct *work)
        struct msm_gpu *gpu = container_of(work, struct msm_gpu, recover_work);
        struct drm_device *dev = gpu->dev;
        struct msm_gem_submit *submit;
-       uint32_t fence = gpu->funcs->last_fence(gpu);
+       struct msm_ringbuffer *ring;
+       struct msm_ringbuffer *cur_ring = gpu->funcs->active_ring(gpu);
+       int i;
+
+       /* Update all the rings with the latest and greatest fence */
+       FOR_EACH_RING(gpu, ring, i) {
+               uint32_t fence = gpu->funcs->last_fence(gpu, ring);
 
-       msm_update_fence(gpu->fctx, fence + 1);
+               /*
+                * For the current (faulting?) ring/submit advance the fence by
+                * one more to clear the faulting submit
+                */
+               if (ring == cur_ring)
+                       fence = fence + 1;
+
+               msm_update_fence(gpu->fctx, cur_ring, fence);
+       }
 
        mutex_lock(&dev->struct_mutex);
 
        dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
        list_for_each_entry(submit, &gpu->submit_list, node) {
-               if (submit->fence->seqno == (fence + 1)) {
+               uint32_t fence;
+
+               /* Only consider submits for the current ring */
+               if (submit->ring != cur_ring)
+                       continue;
+
+               fence = gpu->funcs->last_fence(gpu, ring) + 1;
+
+               if (submit->fence->seqno == fence) {
                        struct task_struct *task;
 
                        rcu_read_lock();
@@ -303,10 +325,9 @@ static void recover_worker(struct work_struct *work)
                inactive_cancel(gpu);
                gpu->funcs->recover(gpu);
 
-               /* replay the remaining submits after the one that hung: */
-               list_for_each_entry(submit, &gpu->submit_list, node) {
+               /* replay the remaining submits for all rings: */
+               list_for_each_entry(submit, &gpu->submit_list, node)
                        gpu->funcs->submit(gpu, submit, NULL);
-               }
        }
 
        mutex_unlock(&dev->struct_mutex);
@@ -326,25 +347,27 @@ static void hangcheck_handler(unsigned long data)
        struct msm_gpu *gpu = (struct msm_gpu *)data;
        struct drm_device *dev = gpu->dev;
        struct msm_drm_private *priv = dev->dev_private;
-       uint32_t fence = gpu->funcs->last_fence(gpu);
+       struct msm_ringbuffer *ring = gpu->funcs->active_ring(gpu);
+       uint32_t fence = gpu->funcs->last_fence(gpu, ring);
 
-       if (fence != gpu->hangcheck_fence) {
+       if (fence != gpu->hangcheck_fence[ring->id]) {
                /* some progress has been made.. ya! */
-               gpu->hangcheck_fence = fence;
-       } else if (fence < gpu->fctx->last_fence) {
+               gpu->hangcheck_fence[ring->id] = fence;
+       } else if (fence < ring->last_fence) {
                /* no progress and not done.. hung! */
-               gpu->hangcheck_fence = fence;
-               dev_err(dev->dev, "%s: hangcheck detected gpu lockup!\n",
-                               gpu->name);
+               gpu->hangcheck_fence[ring->id] = fence;
+               dev_err(dev->dev, "%s: hangcheck detected gpu lockup rb %d!\n",
+                               gpu->name, ring->id);
                dev_err(dev->dev, "%s:     completed fence: %u\n",
                                gpu->name, fence);
                dev_err(dev->dev, "%s:     submitted fence: %u\n",
-                               gpu->name, gpu->fctx->last_fence);
+                               gpu->name, ring->last_fence);
+
                queue_work(priv->wq, &gpu->recover_work);
        }
 
        /* if still more pending work, reset the hangcheck timer: */
-       if (gpu->fctx->last_fence > gpu->hangcheck_fence)
+       if (ring->last_fence > gpu->hangcheck_fence[ring->id])
                hangcheck_timer_reset(gpu);
 
        /* workaround for missing irq: */
@@ -468,20 +491,13 @@ static void retire_submit(struct msm_gpu *gpu, struct 
msm_gem_submit *submit)
 static void retire_submits(struct msm_gpu *gpu)
 {
        struct drm_device *dev = gpu->dev;
+       struct msm_gem_submit *submit, *tmp;
 
        WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
-       while (!list_empty(&gpu->submit_list)) {
-               struct msm_gem_submit *submit;
-
-               submit = list_first_entry(&gpu->submit_list,
-                               struct msm_gem_submit, node);
-
-               if (dma_fence_is_signaled(submit->fence)) {
+       list_for_each_entry_safe(submit, tmp, &gpu->submit_list, node) {
+               if (dma_fence_is_signaled(submit->fence))
                        retire_submit(gpu, submit);
-               } else {
-                       break;
-               }
        }
 }
 
@@ -489,9 +505,12 @@ static void retire_worker(struct work_struct *work)
 {
        struct msm_gpu *gpu = container_of(work, struct msm_gpu, retire_work);
        struct drm_device *dev = gpu->dev;
-       uint32_t fence = gpu->funcs->last_fence(gpu);
+       struct msm_ringbuffer *ring;
+       int i;
 
-       msm_update_fence(gpu->fctx, fence);
+       FOR_EACH_RING(gpu, ring, i)
+               msm_update_fence(gpu->fctx, ring,
+                       gpu->funcs->last_fence(gpu, ring));
 
        mutex_lock(&dev->struct_mutex);
        retire_submits(gpu);
@@ -573,7 +592,7 @@ int msm_gpu_init(struct drm_device *drm, struct 
platform_device *pdev,
                const char *name, struct msm_gpu_config *config)
 {
        struct iommu_domain *iommu;
-       int i, ret;
+       int i, ret, nr_rings = config->nr_rings;
 
        if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs)))
                gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs);
@@ -675,37 +694,58 @@ int msm_gpu_init(struct drm_device *drm, struct 
platform_device *pdev,
                dev_info(drm->dev, "%s: no IOMMU, fallback to VRAM 
carveout!\n", name);
        }
 
-       /* Create ringbuffer: */
-       mutex_lock(&drm->struct_mutex);
-       gpu->rb = msm_ringbuffer_new(gpu, config->ringsz);
-       mutex_unlock(&drm->struct_mutex);
-       if (IS_ERR(gpu->rb)) {
-               ret = PTR_ERR(gpu->rb);
-               gpu->rb = NULL;
-               dev_err(drm->dev, "could not create ringbuffer: %d\n", ret);
-               goto fail;
+       if (nr_rings > ARRAY_SIZE(gpu->rb)) {
+               WARN(1, "Only creating %lu ringbuffers\n", ARRAY_SIZE(gpu->rb));
+               nr_rings = ARRAY_SIZE(gpu->rb);
+       }
+
+       /* Create ringbuffer(s): */
+       for (i = 0; i < nr_rings; i++) {
+               mutex_lock(&drm->struct_mutex);
+               gpu->rb[i] = msm_ringbuffer_new(gpu, i);
+               mutex_unlock(&drm->struct_mutex);
+
+               if (IS_ERR(gpu->rb[i])) {
+                       ret = PTR_ERR(gpu->rb[i]);
+                       gpu->rb[i] = NULL;
+                       dev_err(drm->dev,
+                               "could not create ringbuffer %d: %d\n", i, ret);
+                       goto fail;
+               }
        }
 
+       gpu->nr_rings = nr_rings;
        bs_init(gpu);
 
        return 0;
 
 fail:
+       for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) {
+               if (gpu->rb[i])
+                       msm_ringbuffer_destroy(gpu->rb[i]);
+       }
+
        return ret;
 }
 
 void msm_gpu_cleanup(struct msm_gpu *gpu)
 {
+       int i;
+
        DBG("%s", gpu->name);
 
        WARN_ON(!list_empty(&gpu->active_list));
 
        bs_fini(gpu);
 
-       if (gpu->rb) {
-               if (gpu->rb_iova)
-                       msm_gem_put_iova(gpu->rb->bo, gpu->aspace);
-               msm_ringbuffer_destroy(gpu->rb);
+       for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) {
+               if (!gpu->rb[i])
+                       continue;
+
+               if (gpu->rb[i]->iova)
+                       msm_gem_put_iova(gpu->rb[i]->bo, gpu->aspace);
+
+               msm_ringbuffer_destroy(gpu->rb[i]);
        }
 
        if (gpu->fctx)
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index cc6530f..38d826a 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -33,7 +33,7 @@ struct msm_gpu_config {
        const char *irqname;
        uint64_t va_start;
        uint64_t va_end;
-       unsigned int ringsz;
+       unsigned int nr_rings;
 };
 
 /* So far, with hardware that I've seen to date, we can have:
@@ -57,9 +57,11 @@ struct msm_gpu_funcs {
        int (*pm_resume)(struct msm_gpu *gpu);
        void (*submit)(struct msm_gpu *gpu, struct msm_gem_submit *submit,
                        struct msm_file_private *ctx);
-       void (*flush)(struct msm_gpu *gpu);
+       void (*flush)(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
        irqreturn_t (*irq)(struct msm_gpu *irq);
-       uint32_t (*last_fence)(struct msm_gpu *gpu);
+       uint32_t (*last_fence)(struct msm_gpu *gpu,
+                       struct msm_ringbuffer *ring);
+       struct msm_ringbuffer *(*active_ring)(struct msm_gpu *gpu);
        void (*recover)(struct msm_gpu *gpu);
        void (*destroy)(struct msm_gpu *gpu);
 #ifdef CONFIG_DEBUG_FS
@@ -85,9 +87,8 @@ struct msm_gpu {
        const struct msm_gpu_perfcntr *perfcntrs;
        uint32_t num_perfcntrs;
 
-       /* ringbuffer: */
-       struct msm_ringbuffer *rb;
-       uint64_t rb_iova;
+       struct msm_ringbuffer *rb[MSM_GPU_MAX_RINGS];
+       int nr_rings;
 
        /* list of GEM active objects: */
        struct list_head active_list;
@@ -126,15 +127,36 @@ struct msm_gpu {
 #define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */
 #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)
        struct timer_list hangcheck_timer;
-       uint32_t hangcheck_fence;
+       uint32_t hangcheck_fence[MSM_GPU_MAX_RINGS];
        struct work_struct recover_work;
 
        struct list_head submit_list;
 };
 
+/* It turns out that all targets use the same ringbuffer size */
+#define MSM_GPU_RINGBUFFER_SZ SZ_32K
+
+static inline struct msm_ringbuffer *__get_ring(struct msm_gpu *gpu, int index)
+{
+       return (index < ARRAY_SIZE(gpu->rb) ? gpu->rb[index] : NULL);
+}
+
+#define FOR_EACH_RING(gpu, ring, index) \
+       for (index = 0, ring = (gpu)->rb[0]; \
+               index < (gpu)->nr_rings && index < ARRAY_SIZE((gpu)->rb); \
+               index++, ring = __get_ring(gpu, index))
+
 static inline bool msm_gpu_active(struct msm_gpu *gpu)
 {
-       return gpu->fctx->last_fence > gpu->funcs->last_fence(gpu);
+       struct msm_ringbuffer *ring;
+       int i;
+
+       FOR_EACH_RING(gpu, ring, i) {
+               if (ring->last_fence > gpu->funcs->last_fence(gpu, ring))
+                       return true;
+       }
+
+       return false;
 }
 
 /* Perf-Counters:
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c 
b/drivers/gpu/drm/msm/msm_ringbuffer.c
index 67b34e0..2ab31c7 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.c
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.c
@@ -18,13 +18,13 @@
 #include "msm_ringbuffer.h"
 #include "msm_gpu.h"
 
-struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size)
+struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id)
 {
        struct msm_ringbuffer *ring;
        int ret;
 
-       if (WARN_ON(!is_power_of_2(size)))
-               return ERR_PTR(-EINVAL);
+       /* We assume everwhere that MSM_GPU_RINGBUFFER_SZ is a power of 2 */
+       BUILD_BUG_ON(!is_power_of_2(MSM_GPU_RINGBUFFER_SZ));
 
        ring = kzalloc(sizeof(*ring), GFP_KERNEL);
        if (!ring) {
@@ -33,7 +33,8 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu 
*gpu, int size)
        }
 
        ring->gpu = gpu;
-       ring->bo = msm_gem_new(gpu->dev, size, MSM_BO_WC);
+       ring->id = id;
+       ring->bo = msm_gem_new(gpu->dev, MSM_GPU_RINGBUFFER_SZ, MSM_BO_WC);
        if (IS_ERR(ring->bo)) {
                ret = PTR_ERR(ring->bo);
                ring->bo = NULL;
@@ -45,11 +46,9 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu 
*gpu, int size)
                ret = PTR_ERR(ring->start);
                goto fail;
        }
-       ring->end   = ring->start + (size / 4);
+       ring->end   = ring->start + (MSM_GPU_RINGBUFFER_SZ >> 2);
        ring->cur   = ring->start;
 
-       ring->size = size;
-
        return ring;
 
 fail:
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h 
b/drivers/gpu/drm/msm/msm_ringbuffer.h
index 6e0e104..4eb05fe 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.h
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.h
@@ -22,12 +22,16 @@
 
 struct msm_ringbuffer {
        struct msm_gpu *gpu;
-       int size;
+       int id;
        struct drm_gem_object *bo;
        uint32_t *start, *end, *cur;
+       uint64_t iova;
+       /* last_fence == completed_fence --> no pending work */
+       uint32_t last_fence;
+       uint32_t completed_fence;
 };
 
-struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size);
+struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id);
 void msm_ringbuffer_destroy(struct msm_ringbuffer *ring);
 
 /* ringbuffer helpers (the parts that are same for a3xx/a2xx/z180..) */
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h
index 045ad20..5134f4a 100644
--- a/include/uapi/drm/msm_drm.h
+++ b/include/uapi/drm/msm_drm.h
@@ -195,10 +195,15 @@ struct drm_msm_gem_submit_bo {
 #define MSM_SUBMIT_NO_IMPLICIT   0x80000000 /* disable implicit sync */
 #define MSM_SUBMIT_FENCE_FD_IN   0x40000000 /* enable input fence_fd */
 #define MSM_SUBMIT_FENCE_FD_OUT  0x20000000 /* enable output fence_fd */
+
+#define MSM_SUBMIT_RING_MASK 0x000F0000
+#define MSM_SUBMIT_RING_SHIFT 16
+
 #define MSM_SUBMIT_FLAGS                ( \
                MSM_SUBMIT_NO_IMPLICIT   | \
                MSM_SUBMIT_FENCE_FD_IN   | \
                MSM_SUBMIT_FENCE_FD_OUT  | \
+               MSM_SUBMIT_RING_MASK     | \
                0)
 
 /* Each cmdstream submit consists of a table of buffers involved, and
-- 
1.9.1

_______________________________________________
Freedreno mailing list
Freedreno@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/freedreno

Reply via email to