Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 357713ce authored by Christian Gmeiner's avatar Christian Gmeiner Committed by Lucas Stach
Browse files

drm/etnaviv: add 'sync point' support



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: default avatarChristian Gmeiner <christian.gmeiner@gmail.com>
Signed-off-by: default avatarLucas Stach <l.stach@pengutronix.de>
parent 249300c7
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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);
+26 −0
Original line number Diff line number Diff line
@@ -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 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
	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);

+6 −0
Original line number Diff line number Diff line
@@ -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;