drm/etnaviv: add 'sync point' support
authorChristian Gmeiner <christian.gmeiner@gmail.com>
Sun, 24 Sep 2017 13:15:28 +0000 (15:15 +0200)
committerLucas Stach <l.stach@pengutronix.de>
Tue, 10 Oct 2017 09:45:45 +0000 (11:45 +0200)
In order to support performance counters in a sane way we need to provide
a method to sync the GPU with the CPU. The GPU can process multpile command
buffers/events per irq. With the help of a 'sync point' we can trigger an event
and stop the GPU/FE immediately. When the CPU is done with is processing it
simply needs to restart the FE and the GPU will process the command stream.

Changes from v1 -> v2:
- process sync point with a work item to keep irq as fast as possible

Changes from v4 -> v5:
- renamed pmrs_* to sync_point_*
- call event_free(..) in sync_point_worker(..)

Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
drivers/gpu/drm/etnaviv/etnaviv_buffer.c
drivers/gpu/drm/etnaviv/etnaviv_drv.h
drivers/gpu/drm/etnaviv/etnaviv_gpu.c
drivers/gpu/drm/etnaviv/etnaviv_gpu.h

index ed9588f36bc9b4a6214eca1e2954f520a0ce8ef4..9e7098e3207f5d3b7914ec22dc9dc44ebd020d7a 100644 (file)
@@ -250,6 +250,42 @@ void etnaviv_buffer_end(struct etnaviv_gpu *gpu)
        }
 }
 
+/* Append a 'sync point' to the ring buffer. */
+void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event)
+{
+       struct etnaviv_cmdbuf *buffer = gpu->buffer;
+       unsigned int waitlink_offset = buffer->user_size - 16;
+       u32 dwords, target;
+
+       /*
+        * We need at most 3 dwords in the return target:
+        * 1 event + 1 end + 1 wait + 1 link.
+        */
+       dwords = 4;
+       target = etnaviv_buffer_reserve(gpu, buffer, dwords);
+
+       /* Signal sync point event */
+       CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
+                      VIVS_GL_EVENT_FROM_PE);
+
+       /* Stop the FE to 'pause' the GPU */
+       CMD_END(buffer);
+
+       /* Append waitlink */
+       CMD_WAIT(buffer);
+       CMD_LINK(buffer, 2, etnaviv_cmdbuf_get_va(buffer) +
+                           buffer->user_size - 4);
+
+       /*
+        * Kick off the 'sync point' command by replacing the previous
+        * WAIT with a link to the address in the ring buffer.
+        */
+       etnaviv_buffer_replace_wait(buffer, waitlink_offset,
+                                   VIV_FE_LINK_HEADER_OP_LINK |
+                                   VIV_FE_LINK_HEADER_PREFETCH(dwords),
+                                   target);
+}
+
 /* Append a command buffer to the ring buffer. */
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
        struct etnaviv_cmdbuf *cmdbuf)
index d157d9379e68cdc588ff149ac17357a6f60d04af..203613ae24dce2856ee7224d652eb0f50941ae26 100644 (file)
@@ -100,6 +100,7 @@ int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
 u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
 u16 etnaviv_buffer_config_mmuv2(struct etnaviv_gpu *gpu, u32 mtlb_addr, u32 safe_addr);
 void etnaviv_buffer_end(struct etnaviv_gpu *gpu);
+void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event);
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
        struct etnaviv_cmdbuf *cmdbuf);
 void etnaviv_validate_init(void);
index 7b61071af0def74e77ab7ab0bda45a252116718a..cd70e7c043056f1692e0ff1f4948724da027e804 100644 (file)
@@ -25,6 +25,7 @@
 #include "etnaviv_gpu.h"
 #include "etnaviv_gem.h"
 #include "etnaviv_mmu.h"
+#include "etnaviv_perfmon.h"
 #include "common.xml.h"
 #include "state.xml.h"
 #include "state_hi.xml.h"
@@ -1364,6 +1365,7 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
        }
 
        gpu->event[event].fence = fence;
+       gpu->event[event].sync_point = NULL;
        submit->fence = dma_fence_get(fence);
        gpu->active_fence = submit->fence->seqno;
 
@@ -1409,6 +1411,24 @@ out_pm_put:
        return ret;
 }
 
+static void etnaviv_process_sync_point(struct etnaviv_gpu *gpu,
+       struct etnaviv_event *event)
+{
+       u32 addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS);
+
+       event->sync_point(gpu, event);
+       etnaviv_gpu_start_fe(gpu, addr + 2, 2);
+}
+
+static void sync_point_worker(struct work_struct *work)
+{
+       struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
+                                              sync_point_work);
+
+       etnaviv_process_sync_point(gpu, &gpu->event[gpu->sync_point_event]);
+       event_free(gpu, gpu->sync_point_event);
+}
+
 /*
  * Init/Cleanup:
  */
@@ -1455,6 +1475,11 @@ static irqreturn_t irq_handler(int irq, void *data)
 
                        dev_dbg(gpu->dev, "event %u\n", event);
 
+                       if (gpu->event[event].sync_point) {
+                               gpu->sync_point_event = event;
+                               etnaviv_queue_work(gpu->drm, &gpu->sync_point_work);
+                       }
+
                        fence = gpu->event[event].fence;
                        gpu->event[event].fence = NULL;
                        dma_fence_signal(fence);
@@ -1660,6 +1685,7 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 
        INIT_LIST_HEAD(&gpu->active_cmd_list);
        INIT_WORK(&gpu->retire_work, retire_worker);
+       INIT_WORK(&gpu->sync_point_work, sync_point_worker);
        INIT_WORK(&gpu->recover_work, recover_worker);
        init_waitqueue_head(&gpu->fence_event);
 
index 70e6590aacdfb8b74378037a8a52e1cce4f4a73c..3be5cb53e89f62ebdc9756739a199483e20db5df 100644 (file)
@@ -89,6 +89,8 @@ struct etnaviv_chip_identity {
 
 struct etnaviv_event {
        struct dma_fence *fence;
+
+       void (*sync_point)(struct etnaviv_gpu *gpu, struct etnaviv_event *event);
 };
 
 struct etnaviv_cmdbuf_suballoc;
@@ -135,6 +137,10 @@ struct etnaviv_gpu {
        /* worker for handling active-list retiring: */
        struct work_struct retire_work;
 
+       /* worker for handling 'sync' points: */
+       struct work_struct sync_point_work;
+       int sync_point_event;
+
        void __iomem *mmio;
        int irq;