Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Christian Gmeiner <christian.gmeiner@gmail.com>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>

authored by

Christian Gmeiner and committed by
Lucas Stach
357713ce 249300c7

+69
+36
drivers/gpu/drm/etnaviv/etnaviv_buffer.c
··· 250 250 } 251 251 } 252 252 253 + /* Append a 'sync point' to the ring buffer. */ 254 + void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event) 255 + { 256 + struct etnaviv_cmdbuf *buffer = gpu->buffer; 257 + unsigned int waitlink_offset = buffer->user_size - 16; 258 + u32 dwords, target; 259 + 260 + /* 261 + * We need at most 3 dwords in the return target: 262 + * 1 event + 1 end + 1 wait + 1 link. 263 + */ 264 + dwords = 4; 265 + target = etnaviv_buffer_reserve(gpu, buffer, dwords); 266 + 267 + /* Signal sync point event */ 268 + CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) | 269 + VIVS_GL_EVENT_FROM_PE); 270 + 271 + /* Stop the FE to 'pause' the GPU */ 272 + CMD_END(buffer); 273 + 274 + /* Append waitlink */ 275 + CMD_WAIT(buffer); 276 + CMD_LINK(buffer, 2, etnaviv_cmdbuf_get_va(buffer) + 277 + buffer->user_size - 4); 278 + 279 + /* 280 + * Kick off the 'sync point' command by replacing the previous 281 + * WAIT with a link to the address in the ring buffer. 282 + */ 283 + etnaviv_buffer_replace_wait(buffer, waitlink_offset, 284 + VIV_FE_LINK_HEADER_OP_LINK | 285 + VIV_FE_LINK_HEADER_PREFETCH(dwords), 286 + target); 287 + } 288 + 253 289 /* Append a command buffer to the ring buffer. */ 254 290 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, 255 291 struct etnaviv_cmdbuf *cmdbuf)
+1
drivers/gpu/drm/etnaviv/etnaviv_drv.h
··· 100 100 u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu); 101 101 u16 etnaviv_buffer_config_mmuv2(struct etnaviv_gpu *gpu, u32 mtlb_addr, u32 safe_addr); 102 102 void etnaviv_buffer_end(struct etnaviv_gpu *gpu); 103 + void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event); 103 104 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, 104 105 struct etnaviv_cmdbuf *cmdbuf); 105 106 void etnaviv_validate_init(void);
+26
drivers/gpu/drm/etnaviv/etnaviv_gpu.c
··· 25 25 #include "etnaviv_gpu.h" 26 26 #include "etnaviv_gem.h" 27 27 #include "etnaviv_mmu.h" 28 + #include "etnaviv_perfmon.h" 28 29 #include "common.xml.h" 29 30 #include "state.xml.h" 30 31 #include "state_hi.xml.h" ··· 1365 1364 } 1366 1365 1367 1366 gpu->event[event].fence = fence; 1367 + gpu->event[event].sync_point = NULL; 1368 1368 submit->fence = dma_fence_get(fence); 1369 1369 gpu->active_fence = submit->fence->seqno; 1370 1370 ··· 1409 1407 etnaviv_gpu_pm_put(gpu); 1410 1408 1411 1409 return ret; 1410 + } 1411 + 1412 + static void etnaviv_process_sync_point(struct etnaviv_gpu *gpu, 1413 + struct etnaviv_event *event) 1414 + { 1415 + u32 addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS); 1416 + 1417 + event->sync_point(gpu, event); 1418 + etnaviv_gpu_start_fe(gpu, addr + 2, 2); 1419 + } 1420 + 1421 + static void sync_point_worker(struct work_struct *work) 1422 + { 1423 + struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, 1424 + sync_point_work); 1425 + 1426 + etnaviv_process_sync_point(gpu, &gpu->event[gpu->sync_point_event]); 1427 + event_free(gpu, gpu->sync_point_event); 1412 1428 } 1413 1429 1414 1430 /* ··· 1474 1454 intr &= ~(1 << event); 1475 1455 1476 1456 dev_dbg(gpu->dev, "event %u\n", event); 1457 + 1458 + if (gpu->event[event].sync_point) { 1459 + gpu->sync_point_event = event; 1460 + etnaviv_queue_work(gpu->drm, &gpu->sync_point_work); 1461 + } 1477 1462 1478 1463 fence = gpu->event[event].fence; 1479 1464 gpu->event[event].fence = NULL; ··· 1685 1660 1686 1661 INIT_LIST_HEAD(&gpu->active_cmd_list); 1687 1662 INIT_WORK(&gpu->retire_work, retire_worker); 1663 + INIT_WORK(&gpu->sync_point_work, sync_point_worker); 1688 1664 INIT_WORK(&gpu->recover_work, recover_worker); 1689 1665 init_waitqueue_head(&gpu->fence_event); 1690 1666
+6
drivers/gpu/drm/etnaviv/etnaviv_gpu.h
··· 89 89 90 90 struct etnaviv_event { 91 91 struct dma_fence *fence; 92 + 93 + void (*sync_point)(struct etnaviv_gpu *gpu, struct etnaviv_event *event); 92 94 }; 93 95 94 96 struct etnaviv_cmdbuf_suballoc; ··· 136 134 137 135 /* worker for handling active-list retiring: */ 138 136 struct work_struct retire_work; 137 + 138 + /* worker for handling 'sync' points: */ 139 + struct work_struct sync_point_work; 140 + int sync_point_event; 139 141 140 142 void __iomem *mmio; 141 143 int irq;