diff options
Diffstat (limited to 'drivers/gpu/drm/etnaviv/etnaviv_buffer.c')
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_buffer.c | 219 |
1 files changed, 139 insertions, 80 deletions
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c index 332c55e..d8d5564 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c @@ -21,6 +21,7 @@ #include "common.xml.h" #include "state.xml.h" +#include "state_3d.xml.h" #include "cmdstream.xml.h" /* @@ -85,10 +86,17 @@ static inline void CMD_STALL(struct etnaviv_cmdbuf *buffer, OUT(buffer, VIV_FE_STALL_TOKEN_FROM(from) | VIV_FE_STALL_TOKEN_TO(to)); } -static void etnaviv_cmd_select_pipe(struct etnaviv_cmdbuf *buffer, u8 pipe) +static inline void CMD_SEM(struct etnaviv_cmdbuf *buffer, u32 from, u32 to) { - u32 flush; - u32 stall; + CMD_LOAD_STATE(buffer, VIVS_GL_SEMAPHORE_TOKEN, + VIVS_GL_SEMAPHORE_TOKEN_FROM(from) | + VIVS_GL_SEMAPHORE_TOKEN_TO(to)); +} + +static void etnaviv_cmd_select_pipe(struct etnaviv_gpu *gpu, + struct etnaviv_cmdbuf *buffer, u8 pipe) +{ + u32 flush = 0; /* * This assumes that if we're switching to 2D, we're switching @@ -96,17 +104,13 @@ static void etnaviv_cmd_select_pipe(struct etnaviv_cmdbuf *buffer, u8 pipe) * the 2D core, we need to flush the 3D depth and color caches, * otherwise we need to flush the 2D pixel engine cache. */ - if (pipe == ETNA_PIPE_2D) - flush = VIVS_GL_FLUSH_CACHE_DEPTH | VIVS_GL_FLUSH_CACHE_COLOR; - else + if (gpu->exec_state == ETNA_PIPE_2D) flush = VIVS_GL_FLUSH_CACHE_PE2D; - - stall = VIVS_GL_SEMAPHORE_TOKEN_FROM(SYNC_RECIPIENT_FE) | - VIVS_GL_SEMAPHORE_TOKEN_TO(SYNC_RECIPIENT_PE); + else if (gpu->exec_state == ETNA_PIPE_3D) + flush = VIVS_GL_FLUSH_CACHE_DEPTH | VIVS_GL_FLUSH_CACHE_COLOR; CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE, flush); - CMD_LOAD_STATE(buffer, VIVS_GL_SEMAPHORE_TOKEN, stall); - + CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT, @@ -131,6 +135,36 @@ static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, ptr, len * 4, 0); } +/* + * Safely replace the WAIT of a waitlink with a new command and argument. + * The GPU may be executing this WAIT while we're modifying it, so we have + * to write it in a specific order to avoid the GPU branching to somewhere + * else. 'wl_offset' is the offset to the first byte of the WAIT command. + */ +static void etnaviv_buffer_replace_wait(struct etnaviv_cmdbuf *buffer, + unsigned int wl_offset, u32 cmd, u32 arg) +{ + u32 *lw = buffer->vaddr + wl_offset; + + lw[1] = arg; + mb(); + lw[0] = cmd; + mb(); +} + +/* + * Ensure that there is space in the command buffer to contiguously write + * 'cmd_dwords' 64-bit words into the buffer, wrapping if necessary. + */ +static u32 etnaviv_buffer_reserve(struct etnaviv_gpu *gpu, + struct etnaviv_cmdbuf *buffer, unsigned int cmd_dwords) +{ + if (buffer->user_size + cmd_dwords * sizeof(u64) > buffer->size) + buffer->user_size = 0; + + return gpu_va(gpu, buffer) + buffer->user_size; +} + u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu) { struct etnaviv_cmdbuf *buffer = gpu->buffer; @@ -147,81 +181,79 @@ u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu) void etnaviv_buffer_end(struct etnaviv_gpu *gpu) { struct etnaviv_cmdbuf *buffer = gpu->buffer; + unsigned int waitlink_offset = buffer->user_size - 16; + u32 link_target, flush = 0; - /* Replace the last WAIT with an END */ - buffer->user_size -= 16; - - CMD_END(buffer); - mb(); + if (gpu->exec_state == ETNA_PIPE_2D) + flush = VIVS_GL_FLUSH_CACHE_PE2D; + else if (gpu->exec_state == ETNA_PIPE_3D) + flush = VIVS_GL_FLUSH_CACHE_DEPTH | + VIVS_GL_FLUSH_CACHE_COLOR | + VIVS_GL_FLUSH_CACHE_TEXTURE | + VIVS_GL_FLUSH_CACHE_TEXTUREVS | + VIVS_GL_FLUSH_CACHE_SHADER_L2; + + if (flush) { + unsigned int dwords = 7; + + link_target = etnaviv_buffer_reserve(gpu, buffer, dwords); + + CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE, flush); + if (gpu->exec_state == ETNA_PIPE_3D) + CMD_LOAD_STATE(buffer, VIVS_TS_FLUSH_CACHE, + VIVS_TS_FLUSH_CACHE_FLUSH); + CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + CMD_END(buffer); + + etnaviv_buffer_replace_wait(buffer, waitlink_offset, + VIV_FE_LINK_HEADER_OP_LINK | + VIV_FE_LINK_HEADER_PREFETCH(dwords), + link_target); + } else { + /* Replace the last link-wait with an "END" command */ + etnaviv_buffer_replace_wait(buffer, waitlink_offset, + VIV_FE_END_HEADER_OP_END, 0); + } } +/* Append a command buffer to the ring buffer. */ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct etnaviv_cmdbuf *cmdbuf) { struct etnaviv_cmdbuf *buffer = gpu->buffer; - u32 *lw = buffer->vaddr + buffer->user_size - 16; - u32 back, link_target, link_size, reserve_size, extra_size = 0; + unsigned int waitlink_offset = buffer->user_size - 16; + u32 return_target, return_dwords; + u32 link_target, link_dwords; if (drm_debug & DRM_UT_DRIVER) etnaviv_buffer_dump(gpu, buffer, 0, 0x50); + link_target = gpu_va(gpu, cmdbuf); + link_dwords = cmdbuf->size / 8; + /* - * If we need to flush the MMU prior to submitting this buffer, we - * will need to append a mmu flush load state, followed by a new + * If we need maintanence prior to submitting this buffer, we will + * need to append a mmu flush load state, followed by a new * link to this buffer - a total of four additional words. */ if (gpu->mmu->need_flush || gpu->switch_context) { + u32 target, extra_dwords; + /* link command */ - extra_size += 2; + extra_dwords = 1; + /* flush command */ if (gpu->mmu->need_flush) - extra_size += 2; + extra_dwords += 1; + /* pipe switch commands */ if (gpu->switch_context) - extra_size += 8; - } + extra_dwords += 4; - reserve_size = (6 + extra_size) * 4; - - /* - * if we are going to completely overflow the buffer, we need to wrap. - */ - if (buffer->user_size + reserve_size > buffer->size) - buffer->user_size = 0; - - /* save offset back into main buffer */ - back = buffer->user_size + reserve_size - 6 * 4; - link_target = gpu_va(gpu, buffer) + buffer->user_size; - link_size = 6; - - /* Skip over any extra instructions */ - link_target += extra_size * sizeof(u32); - - if (drm_debug & DRM_UT_DRIVER) - pr_info("stream link to 0x%08x @ 0x%08x %p\n", - link_target, gpu_va(gpu, cmdbuf), cmdbuf->vaddr); - - /* jump back from cmd to main buffer */ - CMD_LINK(cmdbuf, link_size, link_target); - - link_target = gpu_va(gpu, cmdbuf); - link_size = cmdbuf->size / 8; - - - - if (drm_debug & DRM_UT_DRIVER) { - print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, - cmdbuf->vaddr, cmdbuf->size, 0); - - pr_info("link op: %p\n", lw); - pr_info("link addr: %p\n", lw + 1); - pr_info("addr: 0x%08x\n", link_target); - pr_info("back: 0x%08x\n", gpu_va(gpu, buffer) + back); - pr_info("event: %d\n", event); - } - - if (gpu->mmu->need_flush || gpu->switch_context) { - u32 new_target = gpu_va(gpu, buffer) + buffer->user_size; + target = etnaviv_buffer_reserve(gpu, buffer, extra_dwords); if (gpu->mmu->need_flush) { /* Add the MMU flush */ @@ -236,32 +268,59 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, } if (gpu->switch_context) { - etnaviv_cmd_select_pipe(buffer, cmdbuf->exec_state); + etnaviv_cmd_select_pipe(gpu, buffer, cmdbuf->exec_state); + gpu->exec_state = cmdbuf->exec_state; gpu->switch_context = false; } - /* And the link to the first buffer */ - CMD_LINK(buffer, link_size, link_target); + /* And the link to the submitted buffer */ + CMD_LINK(buffer, link_dwords, link_target); /* Update the link target to point to above instructions */ - link_target = new_target; - link_size = extra_size; + link_target = target; + link_dwords = extra_dwords; } - /* trigger event */ + /* + * Append a LINK to the submitted command buffer to return to + * the ring buffer. return_target is the ring target address. + * We need three dwords: event, wait, link. + */ + return_dwords = 3; + return_target = etnaviv_buffer_reserve(gpu, buffer, return_dwords); + CMD_LINK(cmdbuf, return_dwords, return_target); + + /* + * Append event, wait and link pointing back to the wait + * command to the ring buffer. + */ CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) | VIVS_GL_EVENT_FROM_PE); - - /* append WAIT/LINK to main buffer */ CMD_WAIT(buffer); - CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + (buffer->user_size - 4)); + CMD_LINK(buffer, 2, return_target + 8); - /* Change WAIT into a LINK command; write the address first. */ - *(lw + 1) = link_target; - mb(); - *(lw) = VIV_FE_LINK_HEADER_OP_LINK | - VIV_FE_LINK_HEADER_PREFETCH(link_size); - mb(); + if (drm_debug & DRM_UT_DRIVER) + pr_info("stream link to 0x%08x @ 0x%08x %p\n", + return_target, gpu_va(gpu, cmdbuf), cmdbuf->vaddr); + + if (drm_debug & DRM_UT_DRIVER) { + print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, + cmdbuf->vaddr, cmdbuf->size, 0); + + pr_info("link op: %p\n", buffer->vaddr + waitlink_offset); + pr_info("addr: 0x%08x\n", link_target); + pr_info("back: 0x%08x\n", return_target); + pr_info("event: %d\n", event); + } + + /* + * Kick off the submitted 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(link_dwords), + link_target); if (drm_debug & DRM_UT_DRIVER) etnaviv_buffer_dump(gpu, buffer, 0, 0x50); |