1e88f27b3Smrg/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ 2e88f27b3Smrg 3e88f27b3Smrg/* 4e88f27b3Smrg * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org> 5e88f27b3Smrg * 6e88f27b3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 7e88f27b3Smrg * copy of this software and associated documentation files (the "Software"), 8e88f27b3Smrg * to deal in the Software without restriction, including without limitation 9e88f27b3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10e88f27b3Smrg * and/or sell copies of the Software, and to permit persons to whom the 11e88f27b3Smrg * Software is furnished to do so, subject to the following conditions: 12e88f27b3Smrg * 13e88f27b3Smrg * The above copyright notice and this permission notice (including the next 14e88f27b3Smrg * paragraph) shall be included in all copies or substantial portions of the 15e88f27b3Smrg * Software. 16e88f27b3Smrg * 17e88f27b3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18e88f27b3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19e88f27b3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20e88f27b3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21e88f27b3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22e88f27b3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23e88f27b3Smrg * SOFTWARE. 24e88f27b3Smrg * 25e88f27b3Smrg * Authors: 26e88f27b3Smrg * Rob Clark <robclark@freedesktop.org> 27e88f27b3Smrg */ 28e88f27b3Smrg 29e88f27b3Smrg#include <assert.h> 303f012e29Smrg#include <inttypes.h> 31e88f27b3Smrg 327cdc0497Smrg#include "xf86atomic.h" 33e88f27b3Smrg#include "freedreno_ringbuffer.h" 34e88f27b3Smrg#include "msm_priv.h" 35e88f27b3Smrg 363f012e29Smrg/* represents a single cmd buffer in the submit ioctl. Each cmd buffer has 373f012e29Smrg * a backing bo, and a reloc table. 383f012e29Smrg */ 393f012e29Smrgstruct msm_cmd { 403f012e29Smrg struct list_head list; 413f012e29Smrg 423f012e29Smrg struct fd_ringbuffer *ring; 43e88f27b3Smrg struct fd_bo *ring_bo; 44e88f27b3Smrg 453f012e29Smrg /* reloc's table: */ 467cdc0497Smrg DECLARE_ARRAY(struct drm_msm_gem_submit_reloc, relocs); 473f012e29Smrg 483f012e29Smrg uint32_t size; 497cdc0497Smrg 507cdc0497Smrg /* has cmd already been added to parent rb's submit.cmds table? */ 517cdc0497Smrg int is_appended_to_submit; 523f012e29Smrg}; 533f012e29Smrg 543f012e29Smrgstruct msm_ringbuffer { 553f012e29Smrg struct fd_ringbuffer base; 56e88f27b3Smrg 573f012e29Smrg /* submit ioctl related tables: 583f012e29Smrg * Note that bos and cmds are tracked by the parent ringbuffer, since 593f012e29Smrg * that is global to the submit ioctl call. The reloc's table is tracked 603f012e29Smrg * per cmd-buffer. 613f012e29Smrg */ 623f012e29Smrg struct { 633f012e29Smrg /* bo's table: */ 647cdc0497Smrg DECLARE_ARRAY(struct drm_msm_gem_submit_bo, bos); 653f012e29Smrg 663f012e29Smrg /* cmd's table: */ 677cdc0497Smrg DECLARE_ARRAY(struct drm_msm_gem_submit_cmd, cmds); 683f012e29Smrg } submit; 693f012e29Smrg 703f012e29Smrg /* should have matching entries in submit.bos: */ 713f012e29Smrg /* Note, only in parent ringbuffer */ 727cdc0497Smrg DECLARE_ARRAY(struct fd_bo *, bos); 73e88f27b3Smrg 743f012e29Smrg /* should have matching entries in submit.cmds: */ 757cdc0497Smrg DECLARE_ARRAY(struct msm_cmd *, cmds); 76e88f27b3Smrg 775324fb0dSmrg /* List of physical cmdstream buffers (msm_cmd) associated with this 783f012e29Smrg * logical fd_ringbuffer. 793f012e29Smrg * 803f012e29Smrg * Note that this is different from msm_ringbuffer::cmds (which 813f012e29Smrg * shadows msm_ringbuffer::submit::cmds for tracking submit ioctl 823f012e29Smrg * related stuff, and *only* is tracked in the parent ringbuffer. 833f012e29Smrg * And only has "completed" cmd buffers (ie. we already know the 843f012e29Smrg * size) added via get_cmd(). 853f012e29Smrg */ 863f012e29Smrg struct list_head cmd_list; 873f012e29Smrg 883f012e29Smrg int is_growable; 893f012e29Smrg unsigned cmd_count; 903f012e29Smrg 917cdc0497Smrg unsigned offset; /* for sub-allocated stateobj rb's */ 927cdc0497Smrg 933f012e29Smrg unsigned seqno; 943f012e29Smrg 953f012e29Smrg /* maps fd_bo to idx: */ 963f012e29Smrg void *bo_table; 977cdc0497Smrg 987cdc0497Smrg /* maps msm_cmd to drm_msm_gem_submit_cmd in parent rb. Each rb has a 997cdc0497Smrg * list of msm_cmd's which correspond to each chunk of cmdstream in 1007cdc0497Smrg * a 'growable' rb. For each of those we need to create one 1017cdc0497Smrg * drm_msm_gem_submit_cmd in the parent rb which collects the state 1027cdc0497Smrg * for the submit ioctl. Because we can have multiple IB's to the same 1037cdc0497Smrg * target rb (for example, or same stateobj emit multiple times), and 1047cdc0497Smrg * because in theory we can have multiple different rb's that have a 1057cdc0497Smrg * reference to a given target, we need a hashtable to track this per 1067cdc0497Smrg * rb. 1077cdc0497Smrg */ 1087cdc0497Smrg void *cmd_table; 109e88f27b3Smrg}; 110e88f27b3Smrg 1113f012e29Smrgstatic inline struct msm_ringbuffer * to_msm_ringbuffer(struct fd_ringbuffer *x) 1123f012e29Smrg{ 1133f012e29Smrg return (struct msm_ringbuffer *)x; 1143f012e29Smrg} 1153f012e29Smrg 1163f012e29Smrg#define INIT_SIZE 0x1000 1173f012e29Smrg 1183f012e29Smrgstatic pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER; 1193f012e29Smrg 1207cdc0497Smrgstatic struct msm_cmd *current_cmd(struct fd_ringbuffer *ring) 1213f012e29Smrg{ 1227cdc0497Smrg struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); 1237cdc0497Smrg assert(!LIST_IS_EMPTY(&msm_ring->cmd_list)); 1247cdc0497Smrg return LIST_LAST_ENTRY(&msm_ring->cmd_list, struct msm_cmd, list); 1253f012e29Smrg} 1263f012e29Smrg 1273f012e29Smrgstatic void ring_cmd_del(struct msm_cmd *cmd) 1283f012e29Smrg{ 1297cdc0497Smrg fd_bo_del(cmd->ring_bo); 1303f012e29Smrg list_del(&cmd->list); 1313f012e29Smrg to_msm_ringbuffer(cmd->ring)->cmd_count--; 1323f012e29Smrg free(cmd->relocs); 1333f012e29Smrg free(cmd); 1343f012e29Smrg} 1353f012e29Smrg 1367cdc0497Smrgstatic struct msm_cmd * ring_cmd_new(struct fd_ringbuffer *ring, uint32_t size, 1377cdc0497Smrg enum fd_ringbuffer_flags flags) 1383f012e29Smrg{ 1393f012e29Smrg struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); 1403f012e29Smrg struct msm_cmd *cmd = calloc(1, sizeof(*cmd)); 1413f012e29Smrg 1423f012e29Smrg if (!cmd) 1433f012e29Smrg return NULL; 1443f012e29Smrg 1453f012e29Smrg cmd->ring = ring; 1467cdc0497Smrg 1477cdc0497Smrg /* TODO separate suballoc buffer for small non-streaming state, using 1487cdc0497Smrg * smaller page-sized backing bo's. 1497cdc0497Smrg */ 1507cdc0497Smrg if (flags & FD_RINGBUFFER_STREAMING) { 1517cdc0497Smrg struct msm_pipe *msm_pipe = to_msm_pipe(ring->pipe); 1527cdc0497Smrg unsigned suballoc_offset = 0; 1537cdc0497Smrg struct fd_bo *suballoc_bo = NULL; 1547cdc0497Smrg 1557cdc0497Smrg if (msm_pipe->suballoc_ring) { 1567cdc0497Smrg struct msm_ringbuffer *suballoc_ring = to_msm_ringbuffer(msm_pipe->suballoc_ring); 1577cdc0497Smrg 1587cdc0497Smrg assert(msm_pipe->suballoc_ring->flags & FD_RINGBUFFER_OBJECT); 1597cdc0497Smrg assert(suballoc_ring->cmd_count == 1); 1607cdc0497Smrg 1617cdc0497Smrg suballoc_bo = current_cmd(msm_pipe->suballoc_ring)->ring_bo; 1627cdc0497Smrg 1637cdc0497Smrg suballoc_offset = fd_ringbuffer_size(msm_pipe->suballoc_ring) + 1647cdc0497Smrg suballoc_ring->offset; 1657cdc0497Smrg 1667cdc0497Smrg suballoc_offset = ALIGN(suballoc_offset, 0x10); 1677cdc0497Smrg 1687cdc0497Smrg if ((size + suballoc_offset) > suballoc_bo->size) { 1697cdc0497Smrg suballoc_bo = NULL; 1707cdc0497Smrg } 1717cdc0497Smrg } 1727cdc0497Smrg 1737cdc0497Smrg if (!suballoc_bo) { 1747cdc0497Smrg cmd->ring_bo = fd_bo_new_ring(ring->pipe->dev, 0x8000, 0); 1757cdc0497Smrg msm_ring->offset = 0; 1767cdc0497Smrg } else { 1777cdc0497Smrg cmd->ring_bo = fd_bo_ref(suballoc_bo); 1787cdc0497Smrg msm_ring->offset = suballoc_offset; 1797cdc0497Smrg } 1807cdc0497Smrg 1817cdc0497Smrg if (msm_pipe->suballoc_ring) 1827cdc0497Smrg fd_ringbuffer_del(msm_pipe->suballoc_ring); 1837cdc0497Smrg 1847cdc0497Smrg msm_pipe->suballoc_ring = fd_ringbuffer_ref(ring); 1857cdc0497Smrg } else { 1867cdc0497Smrg cmd->ring_bo = fd_bo_new_ring(ring->pipe->dev, size, 0); 1877cdc0497Smrg } 1883f012e29Smrg if (!cmd->ring_bo) 1893f012e29Smrg goto fail; 1903f012e29Smrg 1913f012e29Smrg list_addtail(&cmd->list, &msm_ring->cmd_list); 1923f012e29Smrg msm_ring->cmd_count++; 1933f012e29Smrg 1943f012e29Smrg return cmd; 1953f012e29Smrg 1963f012e29Smrgfail: 1973f012e29Smrg ring_cmd_del(cmd); 1983f012e29Smrg return NULL; 1993f012e29Smrg} 2003f012e29Smrg 2013f012e29Smrgstatic uint32_t append_bo(struct fd_ringbuffer *ring, struct fd_bo *bo) 2023f012e29Smrg{ 2033f012e29Smrg struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); 2043f012e29Smrg uint32_t idx; 2053f012e29Smrg 2063f012e29Smrg idx = APPEND(&msm_ring->submit, bos); 2073f012e29Smrg idx = APPEND(msm_ring, bos); 2083f012e29Smrg 2093f012e29Smrg msm_ring->submit.bos[idx].flags = 0; 2103f012e29Smrg msm_ring->submit.bos[idx].handle = bo->handle; 2113f012e29Smrg msm_ring->submit.bos[idx].presumed = to_msm_bo(bo)->presumed; 2123f012e29Smrg 2133f012e29Smrg msm_ring->bos[idx] = fd_bo_ref(bo); 2143f012e29Smrg 2153f012e29Smrg return idx; 216e88f27b3Smrg} 217e88f27b3Smrg 218e88f27b3Smrg/* add (if needed) bo, return idx: */ 219e88f27b3Smrgstatic uint32_t bo2idx(struct fd_ringbuffer *ring, struct fd_bo *bo, uint32_t flags) 220e88f27b3Smrg{ 221e88f27b3Smrg struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); 222e88f27b3Smrg struct msm_bo *msm_bo = to_msm_bo(bo); 223e88f27b3Smrg uint32_t idx; 2243f012e29Smrg pthread_mutex_lock(&idx_lock); 2253f012e29Smrg if (msm_bo->current_ring_seqno == msm_ring->seqno) { 2263f012e29Smrg idx = msm_bo->idx; 227e88f27b3Smrg } else { 2283f012e29Smrg void *val; 2293f012e29Smrg 2303f012e29Smrg if (!msm_ring->bo_table) 2313f012e29Smrg msm_ring->bo_table = drmHashCreate(); 2323f012e29Smrg 2333f012e29Smrg if (!drmHashLookup(msm_ring->bo_table, bo->handle, &val)) { 2343f012e29Smrg /* found */ 2353f012e29Smrg idx = (uint32_t)(uintptr_t)val; 2363f012e29Smrg } else { 2373f012e29Smrg idx = append_bo(ring, bo); 2383f012e29Smrg val = (void *)(uintptr_t)idx; 2393f012e29Smrg drmHashInsert(msm_ring->bo_table, bo->handle, val); 2403f012e29Smrg } 2413f012e29Smrg msm_bo->current_ring_seqno = msm_ring->seqno; 2423f012e29Smrg msm_bo->idx = idx; 243e88f27b3Smrg } 2443f012e29Smrg pthread_mutex_unlock(&idx_lock); 245e88f27b3Smrg if (flags & FD_RELOC_READ) 2463f012e29Smrg msm_ring->submit.bos[idx].flags |= MSM_SUBMIT_BO_READ; 247e88f27b3Smrg if (flags & FD_RELOC_WRITE) 2483f012e29Smrg msm_ring->submit.bos[idx].flags |= MSM_SUBMIT_BO_WRITE; 249e88f27b3Smrg return idx; 250e88f27b3Smrg} 251e88f27b3Smrg 2523f012e29Smrg/* Ensure that submit has corresponding entry in cmds table for the 2533f012e29Smrg * target cmdstream buffer: 2547cdc0497Smrg * 2557cdc0497Smrg * Returns TRUE if new cmd added (else FALSE if it was already in 2567cdc0497Smrg * the cmds table) 2573f012e29Smrg */ 2587cdc0497Smrgstatic int get_cmd(struct fd_ringbuffer *ring, struct msm_cmd *target_cmd, 259e88f27b3Smrg uint32_t submit_offset, uint32_t size, uint32_t type) 260e88f27b3Smrg{ 261e88f27b3Smrg struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); 2623f012e29Smrg struct drm_msm_gem_submit_cmd *cmd; 263e88f27b3Smrg uint32_t i; 2647cdc0497Smrg void *val; 265e88f27b3Smrg 2667cdc0497Smrg if (!msm_ring->cmd_table) 2677cdc0497Smrg msm_ring->cmd_table = drmHashCreate(); 2687cdc0497Smrg 2697cdc0497Smrg /* figure out if we already have a cmd buf.. short-circuit hash 2707cdc0497Smrg * lookup if: 2717cdc0497Smrg * - target cmd has never been added to submit.cmds 2727cdc0497Smrg * - target cmd is not a streaming stateobj (which unlike longer 2737cdc0497Smrg * lived CSO stateobj, is not expected to be reused with multiple 2747cdc0497Smrg * submits) 2757cdc0497Smrg */ 2767cdc0497Smrg if (target_cmd->is_appended_to_submit && 2777cdc0497Smrg !(target_cmd->ring->flags & FD_RINGBUFFER_STREAMING) && 2787cdc0497Smrg !drmHashLookup(msm_ring->cmd_table, (unsigned long)target_cmd, &val)) { 2797cdc0497Smrg i = VOID2U64(val); 2803f012e29Smrg cmd = &msm_ring->submit.cmds[i]; 2817cdc0497Smrg 2827cdc0497Smrg assert(cmd->submit_offset == submit_offset); 2837cdc0497Smrg assert(cmd->size == size); 2847cdc0497Smrg assert(cmd->type == type); 2857cdc0497Smrg assert(msm_ring->submit.bos[cmd->submit_idx].handle == 2867cdc0497Smrg target_cmd->ring_bo->handle); 2877cdc0497Smrg 2887cdc0497Smrg return FALSE; 289e88f27b3Smrg } 290e88f27b3Smrg 291e88f27b3Smrg /* create cmd buf if not: */ 2923f012e29Smrg i = APPEND(&msm_ring->submit, cmds); 2933f012e29Smrg APPEND(msm_ring, cmds); 2943f012e29Smrg msm_ring->cmds[i] = target_cmd; 2953f012e29Smrg cmd = &msm_ring->submit.cmds[i]; 2963f012e29Smrg cmd->type = type; 2973f012e29Smrg cmd->submit_idx = bo2idx(ring, target_cmd->ring_bo, FD_RELOC_READ); 2983f012e29Smrg cmd->submit_offset = submit_offset; 2993f012e29Smrg cmd->size = size; 3003f012e29Smrg cmd->pad = 0; 3013f012e29Smrg 3027cdc0497Smrg target_cmd->is_appended_to_submit = TRUE; 3037cdc0497Smrg 3047cdc0497Smrg if (!(target_cmd->ring->flags & FD_RINGBUFFER_STREAMING)) { 3057cdc0497Smrg drmHashInsert(msm_ring->cmd_table, (unsigned long)target_cmd, 3067cdc0497Smrg U642VOID(i)); 3077cdc0497Smrg } 3087cdc0497Smrg 3093f012e29Smrg target_cmd->size = size; 310e88f27b3Smrg 3117cdc0497Smrg return TRUE; 312e88f27b3Smrg} 313e88f27b3Smrg 3147cdc0497Smrgstatic void * msm_ringbuffer_hostptr(struct fd_ringbuffer *ring) 315e88f27b3Smrg{ 3167cdc0497Smrg struct msm_cmd *cmd = current_cmd(ring); 3177cdc0497Smrg uint8_t *base = fd_bo_map(cmd->ring_bo); 3187cdc0497Smrg return base + to_msm_ringbuffer(ring)->offset; 319e88f27b3Smrg} 320e88f27b3Smrg 3213f012e29Smrgstatic void delete_cmds(struct msm_ringbuffer *msm_ring) 3223f012e29Smrg{ 3233f012e29Smrg struct msm_cmd *cmd, *tmp; 3243f012e29Smrg 3253f012e29Smrg LIST_FOR_EACH_ENTRY_SAFE(cmd, tmp, &msm_ring->cmd_list, list) { 3263f012e29Smrg ring_cmd_del(cmd); 3273f012e29Smrg } 3283f012e29Smrg} 3293f012e29Smrg 330857b0bc6Smrgstatic void flush_reset(struct fd_ringbuffer *ring) 331857b0bc6Smrg{ 332857b0bc6Smrg struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); 333857b0bc6Smrg unsigned i; 334857b0bc6Smrg 3353f012e29Smrg for (i = 0; i < msm_ring->nr_bos; i++) { 3363f012e29Smrg struct msm_bo *msm_bo = to_msm_bo(msm_ring->bos[i]); 3377cdc0497Smrg if (!msm_bo) 3387cdc0497Smrg continue; 3393f012e29Smrg msm_bo->current_ring_seqno = 0; 3403f012e29Smrg fd_bo_del(&msm_bo->base); 3413f012e29Smrg } 3423f012e29Smrg 3437cdc0497Smrg for (i = 0; i < msm_ring->nr_cmds; i++) { 3447cdc0497Smrg struct msm_cmd *msm_cmd = msm_ring->cmds[i]; 3457cdc0497Smrg 3467cdc0497Smrg if (msm_cmd->ring == ring) 3477cdc0497Smrg continue; 3487cdc0497Smrg 3497cdc0497Smrg if (msm_cmd->ring->flags & FD_RINGBUFFER_OBJECT) 3507cdc0497Smrg fd_ringbuffer_del(msm_cmd->ring); 351857b0bc6Smrg } 352857b0bc6Smrg 3533f012e29Smrg msm_ring->submit.nr_cmds = 0; 3543f012e29Smrg msm_ring->submit.nr_bos = 0; 355857b0bc6Smrg msm_ring->nr_cmds = 0; 356857b0bc6Smrg msm_ring->nr_bos = 0; 3573f012e29Smrg 3583f012e29Smrg if (msm_ring->bo_table) { 3593f012e29Smrg drmHashDestroy(msm_ring->bo_table); 3603f012e29Smrg msm_ring->bo_table = NULL; 3613f012e29Smrg } 3623f012e29Smrg 3637cdc0497Smrg if (msm_ring->cmd_table) { 3647cdc0497Smrg drmHashDestroy(msm_ring->cmd_table); 3657cdc0497Smrg msm_ring->cmd_table = NULL; 3667cdc0497Smrg } 3677cdc0497Smrg 3683f012e29Smrg if (msm_ring->is_growable) { 3693f012e29Smrg delete_cmds(msm_ring); 3703f012e29Smrg } else { 3713f012e29Smrg /* in old mode, just reset the # of relocs: */ 3723f012e29Smrg current_cmd(ring)->nr_relocs = 0; 3733f012e29Smrg } 3743f012e29Smrg} 3753f012e29Smrg 3763f012e29Smrgstatic void finalize_current_cmd(struct fd_ringbuffer *ring, uint32_t *last_start) 3773f012e29Smrg{ 3783f012e29Smrg uint32_t submit_offset, size, type; 3793f012e29Smrg struct fd_ringbuffer *parent; 3803f012e29Smrg 3813f012e29Smrg if (ring->parent) { 3823f012e29Smrg parent = ring->parent; 3833f012e29Smrg type = MSM_SUBMIT_CMD_IB_TARGET_BUF; 3843f012e29Smrg } else { 3853f012e29Smrg parent = ring; 3863f012e29Smrg type = MSM_SUBMIT_CMD_BUF; 3873f012e29Smrg } 3883f012e29Smrg 3893f012e29Smrg submit_offset = offset_bytes(last_start, ring->start); 3903f012e29Smrg size = offset_bytes(ring->cur, last_start); 3913f012e29Smrg 3923f012e29Smrg get_cmd(parent, current_cmd(ring), submit_offset, size, type); 3933f012e29Smrg} 3943f012e29Smrg 3953f012e29Smrgstatic void dump_submit(struct msm_ringbuffer *msm_ring) 3963f012e29Smrg{ 3973f012e29Smrg uint32_t i, j; 3983f012e29Smrg 3993f012e29Smrg for (i = 0; i < msm_ring->submit.nr_bos; i++) { 4003f012e29Smrg struct drm_msm_gem_submit_bo *bo = &msm_ring->submit.bos[i]; 4013f012e29Smrg ERROR_MSG(" bos[%d]: handle=%u, flags=%x", i, bo->handle, bo->flags); 4023f012e29Smrg } 4033f012e29Smrg for (i = 0; i < msm_ring->submit.nr_cmds; i++) { 4043f012e29Smrg struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i]; 4053f012e29Smrg struct drm_msm_gem_submit_reloc *relocs = U642VOID(cmd->relocs); 4063f012e29Smrg ERROR_MSG(" cmd[%d]: type=%u, submit_idx=%u, submit_offset=%u, size=%u", 4073f012e29Smrg i, cmd->type, cmd->submit_idx, cmd->submit_offset, cmd->size); 4083f012e29Smrg for (j = 0; j < cmd->nr_relocs; j++) { 4093f012e29Smrg struct drm_msm_gem_submit_reloc *r = &relocs[j]; 4103f012e29Smrg ERROR_MSG(" reloc[%d]: submit_offset=%u, or=%08x, shift=%d, reloc_idx=%u" 4113f012e29Smrg ", reloc_offset=%"PRIu64, j, r->submit_offset, r->or, r->shift, 4123f012e29Smrg r->reloc_idx, r->reloc_offset); 4133f012e29Smrg } 4143f012e29Smrg } 415857b0bc6Smrg} 416857b0bc6Smrg 4177cdc0497Smrgstatic struct drm_msm_gem_submit_reloc * 4187cdc0497Smrghandle_stateobj_relocs(struct fd_ringbuffer *parent, struct fd_ringbuffer *stateobj, 4197cdc0497Smrg struct drm_msm_gem_submit_reloc *orig_relocs, unsigned nr_relocs) 4207cdc0497Smrg{ 4217cdc0497Smrg struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(stateobj); 4227cdc0497Smrg struct drm_msm_gem_submit_reloc *relocs = malloc(nr_relocs * sizeof(*relocs)); 4237cdc0497Smrg unsigned i; 4247cdc0497Smrg 4257cdc0497Smrg for (i = 0; i < nr_relocs; i++) { 4267cdc0497Smrg unsigned idx = orig_relocs[i].reloc_idx; 4277cdc0497Smrg struct fd_bo *bo = msm_ring->bos[idx]; 4287cdc0497Smrg unsigned flags = 0; 4297cdc0497Smrg 4307cdc0497Smrg if (msm_ring->submit.bos[idx].flags & MSM_SUBMIT_BO_READ) 4317cdc0497Smrg flags |= FD_RELOC_READ; 4327cdc0497Smrg if (msm_ring->submit.bos[idx].flags & MSM_SUBMIT_BO_WRITE) 4337cdc0497Smrg flags |= FD_RELOC_WRITE; 4347cdc0497Smrg 4357cdc0497Smrg relocs[i] = orig_relocs[i]; 4367cdc0497Smrg relocs[i].reloc_idx = bo2idx(parent, bo, flags); 4377cdc0497Smrg } 4387cdc0497Smrg 4397cdc0497Smrg /* stateobj rb's could have reloc's to other stateobj rb's which didn't 4407cdc0497Smrg * get propagated to the parent rb at _emit_reloc_ring() time (because 4417cdc0497Smrg * the parent wasn't known then), so fix that up now: 4427cdc0497Smrg */ 4437cdc0497Smrg for (i = 0; i < msm_ring->nr_cmds; i++) { 4447cdc0497Smrg struct msm_cmd *msm_cmd = msm_ring->cmds[i]; 4457cdc0497Smrg struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i]; 4467cdc0497Smrg 4477cdc0497Smrg if (msm_ring->cmds[i]->ring == stateobj) 4487cdc0497Smrg continue; 4497cdc0497Smrg 4507cdc0497Smrg assert(msm_cmd->ring->flags & FD_RINGBUFFER_OBJECT); 4517cdc0497Smrg 4527cdc0497Smrg if (get_cmd(parent, msm_cmd, cmd->submit_offset, cmd->size, cmd->type)) { 4537cdc0497Smrg fd_ringbuffer_ref(msm_cmd->ring); 4547cdc0497Smrg } 4557cdc0497Smrg } 4567cdc0497Smrg 4577cdc0497Smrg return relocs; 4587cdc0497Smrg} 4597cdc0497Smrg 460037b3c26Smrgstatic int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start, 461037b3c26Smrg int in_fence_fd, int *out_fence_fd) 462e88f27b3Smrg{ 463e88f27b3Smrg struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); 4647cdc0497Smrg struct msm_pipe *msm_pipe = to_msm_pipe(ring->pipe); 465e88f27b3Smrg struct drm_msm_gem_submit req = { 4667cdc0497Smrg .flags = msm_pipe->pipe, 4677cdc0497Smrg .queueid = msm_pipe->queue_id, 468e88f27b3Smrg }; 4693f012e29Smrg uint32_t i; 4703f012e29Smrg int ret; 471e88f27b3Smrg 4727cdc0497Smrg assert(!ring->parent); 4737cdc0497Smrg 474037b3c26Smrg if (in_fence_fd != -1) { 475037b3c26Smrg req.flags |= MSM_SUBMIT_FENCE_FD_IN | MSM_SUBMIT_NO_IMPLICIT; 476037b3c26Smrg req.fence_fd = in_fence_fd; 477037b3c26Smrg } 478037b3c26Smrg 479037b3c26Smrg if (out_fence_fd) { 480037b3c26Smrg req.flags |= MSM_SUBMIT_FENCE_FD_OUT; 481037b3c26Smrg } 482037b3c26Smrg 4833f012e29Smrg finalize_current_cmd(ring, last_start); 484e88f27b3Smrg 4857cdc0497Smrg /* for each of the cmd's fix up their reloc's: */ 4867cdc0497Smrg for (i = 0; i < msm_ring->submit.nr_cmds; i++) { 4877cdc0497Smrg struct msm_cmd *msm_cmd = msm_ring->cmds[i]; 4887cdc0497Smrg struct drm_msm_gem_submit_reloc *relocs = msm_cmd->relocs; 4897cdc0497Smrg struct drm_msm_gem_submit_cmd *cmd; 4907cdc0497Smrg unsigned nr_relocs = msm_cmd->nr_relocs; 4917cdc0497Smrg 4927cdc0497Smrg /* for reusable stateobjs, the reloc table has reloc_idx that 4937cdc0497Smrg * points into it's own private bos table, rather than the global 4947cdc0497Smrg * bos table used for the submit, so we need to add the stateobj's 4957cdc0497Smrg * bos to the global table and construct new relocs table with 4967cdc0497Smrg * corresponding reloc_idx 4977cdc0497Smrg */ 4987cdc0497Smrg if (msm_cmd->ring->flags & FD_RINGBUFFER_OBJECT) { 4997cdc0497Smrg relocs = handle_stateobj_relocs(ring, msm_cmd->ring, 5007cdc0497Smrg relocs, nr_relocs); 5017cdc0497Smrg } 5027cdc0497Smrg 5037cdc0497Smrg cmd = &msm_ring->submit.cmds[i]; 5047cdc0497Smrg cmd->relocs = VOID2U64(relocs); 5057cdc0497Smrg cmd->nr_relocs = nr_relocs; 5067cdc0497Smrg } 5077cdc0497Smrg 508e88f27b3Smrg /* needs to be after get_cmd() as that could create bos/cmds table: */ 5093f012e29Smrg req.bos = VOID2U64(msm_ring->submit.bos), 5103f012e29Smrg req.nr_bos = msm_ring->submit.nr_bos; 5113f012e29Smrg req.cmds = VOID2U64(msm_ring->submit.cmds), 5123f012e29Smrg req.nr_cmds = msm_ring->submit.nr_cmds; 513e88f27b3Smrg 5143f012e29Smrg DEBUG_MSG("nr_cmds=%u, nr_bos=%u", req.nr_cmds, req.nr_bos); 515e88f27b3Smrg 516e88f27b3Smrg ret = drmCommandWriteRead(ring->pipe->dev->fd, DRM_MSM_GEM_SUBMIT, 517e88f27b3Smrg &req, sizeof(req)); 518857b0bc6Smrg if (ret) { 519e88f27b3Smrg ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno)); 5203f012e29Smrg dump_submit(msm_ring); 5213f012e29Smrg } else if (!ret) { 522857b0bc6Smrg /* update timestamp on all rings associated with submit: */ 5233f012e29Smrg for (i = 0; i < msm_ring->submit.nr_cmds; i++) { 5243f012e29Smrg struct msm_cmd *msm_cmd = msm_ring->cmds[i]; 5253f012e29Smrg msm_cmd->ring->last_timestamp = req.fence; 526857b0bc6Smrg } 527037b3c26Smrg 528037b3c26Smrg if (out_fence_fd) { 529037b3c26Smrg *out_fence_fd = req.fence_fd; 530037b3c26Smrg } 531857b0bc6Smrg } 532e88f27b3Smrg 5337cdc0497Smrg /* free dynamically constructed stateobj relocs tables: */ 5347cdc0497Smrg for (i = 0; i < msm_ring->submit.nr_cmds; i++) { 5357cdc0497Smrg struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i]; 5367cdc0497Smrg struct msm_cmd *msm_cmd = msm_ring->cmds[i]; 5377cdc0497Smrg if (msm_cmd->ring->flags & FD_RINGBUFFER_OBJECT) { 5387cdc0497Smrg free(U642VOID(cmd->relocs)); 5397cdc0497Smrg } 5407cdc0497Smrg } 5417cdc0497Smrg 542857b0bc6Smrg flush_reset(ring); 543e88f27b3Smrg 544e88f27b3Smrg return ret; 545e88f27b3Smrg} 546e88f27b3Smrg 5473f012e29Smrgstatic void msm_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t size) 5483f012e29Smrg{ 5493f012e29Smrg assert(to_msm_ringbuffer(ring)->is_growable); 5503f012e29Smrg finalize_current_cmd(ring, ring->last_start); 5517cdc0497Smrg ring_cmd_new(ring, size, 0); 5523f012e29Smrg} 5533f012e29Smrg 554857b0bc6Smrgstatic void msm_ringbuffer_reset(struct fd_ringbuffer *ring) 555857b0bc6Smrg{ 556857b0bc6Smrg flush_reset(ring); 557857b0bc6Smrg} 558857b0bc6Smrg 559e88f27b3Smrgstatic void msm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring, 560e88f27b3Smrg const struct fd_reloc *r) 561e88f27b3Smrg{ 562e88f27b3Smrg struct fd_ringbuffer *parent = ring->parent ? ring->parent : ring; 563e88f27b3Smrg struct msm_bo *msm_bo = to_msm_bo(r->bo); 564e88f27b3Smrg struct drm_msm_gem_submit_reloc *reloc; 5653f012e29Smrg struct msm_cmd *cmd = current_cmd(ring); 5663f012e29Smrg uint32_t idx = APPEND(cmd, relocs); 567e88f27b3Smrg uint32_t addr; 568e88f27b3Smrg 5693f012e29Smrg reloc = &cmd->relocs[idx]; 570e88f27b3Smrg 571e88f27b3Smrg reloc->reloc_idx = bo2idx(parent, r->bo, r->flags); 572e88f27b3Smrg reloc->reloc_offset = r->offset; 573e88f27b3Smrg reloc->or = r->or; 574e88f27b3Smrg reloc->shift = r->shift; 5757cdc0497Smrg reloc->submit_offset = offset_bytes(ring->cur, ring->start) + 5767cdc0497Smrg to_msm_ringbuffer(ring)->offset; 577e88f27b3Smrg 578e88f27b3Smrg addr = msm_bo->presumed; 579037b3c26Smrg if (reloc->shift < 0) 580037b3c26Smrg addr >>= -reloc->shift; 581e88f27b3Smrg else 582037b3c26Smrg addr <<= reloc->shift; 583e88f27b3Smrg (*ring->cur++) = addr | r->or; 584037b3c26Smrg 585037b3c26Smrg if (ring->pipe->gpu_id >= 500) { 586037b3c26Smrg struct drm_msm_gem_submit_reloc *reloc_hi; 587037b3c26Smrg 588d8807b2fSmrg /* NOTE: grab reloc_idx *before* APPEND() since that could 589d8807b2fSmrg * realloc() meaning that 'reloc' ptr is no longer valid: 590d8807b2fSmrg */ 591d8807b2fSmrg uint32_t reloc_idx = reloc->reloc_idx; 592d8807b2fSmrg 593037b3c26Smrg idx = APPEND(cmd, relocs); 594037b3c26Smrg 595037b3c26Smrg reloc_hi = &cmd->relocs[idx]; 596037b3c26Smrg 597d8807b2fSmrg reloc_hi->reloc_idx = reloc_idx; 598037b3c26Smrg reloc_hi->reloc_offset = r->offset; 599037b3c26Smrg reloc_hi->or = r->orhi; 600037b3c26Smrg reloc_hi->shift = r->shift - 32; 6017cdc0497Smrg reloc_hi->submit_offset = offset_bytes(ring->cur, ring->start) + 6027cdc0497Smrg to_msm_ringbuffer(ring)->offset; 603037b3c26Smrg 604037b3c26Smrg addr = msm_bo->presumed >> 32; 605037b3c26Smrg if (reloc_hi->shift < 0) 606037b3c26Smrg addr >>= -reloc_hi->shift; 607037b3c26Smrg else 608037b3c26Smrg addr <<= reloc_hi->shift; 609037b3c26Smrg (*ring->cur++) = addr | r->orhi; 610037b3c26Smrg } 611e88f27b3Smrg} 612e88f27b3Smrg 6133f012e29Smrgstatic uint32_t msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring, 6147cdc0497Smrg struct fd_ringbuffer *target, uint32_t cmd_idx) 615e88f27b3Smrg{ 6163f012e29Smrg struct msm_cmd *cmd = NULL; 6177cdc0497Smrg struct msm_ringbuffer *msm_target = to_msm_ringbuffer(target); 6183f012e29Smrg uint32_t idx = 0; 6197cdc0497Smrg int added_cmd = FALSE; 6207cdc0497Smrg uint32_t size; 6217cdc0497Smrg uint32_t submit_offset = msm_target->offset; 6223f012e29Smrg 6237cdc0497Smrg LIST_FOR_EACH_ENTRY(cmd, &msm_target->cmd_list, list) { 6243f012e29Smrg if (idx == cmd_idx) 6253f012e29Smrg break; 6263f012e29Smrg idx++; 6273f012e29Smrg } 628e88f27b3Smrg 6293f012e29Smrg assert(cmd && (idx == cmd_idx)); 630e88f27b3Smrg 6317cdc0497Smrg if (idx < (msm_target->cmd_count - 1)) { 6323f012e29Smrg /* All but the last cmd buffer is fully "baked" (ie. already has 6333f012e29Smrg * done get_cmd() to add it to the cmds table). But in this case, 6343f012e29Smrg * the size we get is invalid (since it is calculated from the 6353f012e29Smrg * last cmd buffer): 6363f012e29Smrg */ 6373f012e29Smrg size = cmd->size; 6383f012e29Smrg } else { 6397cdc0497Smrg struct fd_ringbuffer *parent = ring->parent ? ring->parent : ring; 6407cdc0497Smrg size = offset_bytes(target->cur, target->start); 6417cdc0497Smrg added_cmd = get_cmd(parent, cmd, submit_offset, size, 6427cdc0497Smrg MSM_SUBMIT_CMD_IB_TARGET_BUF); 6433f012e29Smrg } 644e88f27b3Smrg 645e88f27b3Smrg msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){ 6463f012e29Smrg .bo = cmd->ring_bo, 647e88f27b3Smrg .flags = FD_RELOC_READ, 648e88f27b3Smrg .offset = submit_offset, 649e88f27b3Smrg }); 6503f012e29Smrg 6517cdc0497Smrg /* Unlike traditional ringbuffers which are deleted as a set (after 6527cdc0497Smrg * being flushed), mesa can't really guarantee that a stateobj isn't 6537cdc0497Smrg * destroyed after emitted but before flush, so we must hold a ref: 6547cdc0497Smrg */ 6557cdc0497Smrg if (added_cmd && (target->flags & FD_RINGBUFFER_OBJECT)) { 6567cdc0497Smrg fd_ringbuffer_ref(target); 6577cdc0497Smrg } 6587cdc0497Smrg 6593f012e29Smrg return size; 6603f012e29Smrg} 6613f012e29Smrg 6623f012e29Smrgstatic uint32_t msm_ringbuffer_cmd_count(struct fd_ringbuffer *ring) 6633f012e29Smrg{ 6643f012e29Smrg return to_msm_ringbuffer(ring)->cmd_count; 665e88f27b3Smrg} 666e88f27b3Smrg 667e88f27b3Smrgstatic void msm_ringbuffer_destroy(struct fd_ringbuffer *ring) 668e88f27b3Smrg{ 669e88f27b3Smrg struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); 6703f012e29Smrg 6713f012e29Smrg flush_reset(ring); 6723f012e29Smrg delete_cmds(msm_ring); 6733f012e29Smrg 6743f012e29Smrg free(msm_ring->submit.cmds); 6753f012e29Smrg free(msm_ring->submit.bos); 6763f012e29Smrg free(msm_ring->bos); 6773f012e29Smrg free(msm_ring->cmds); 678e88f27b3Smrg free(msm_ring); 679e88f27b3Smrg} 680e88f27b3Smrg 6813f012e29Smrgstatic const struct fd_ringbuffer_funcs funcs = { 682e88f27b3Smrg .hostptr = msm_ringbuffer_hostptr, 683e88f27b3Smrg .flush = msm_ringbuffer_flush, 6843f012e29Smrg .grow = msm_ringbuffer_grow, 685857b0bc6Smrg .reset = msm_ringbuffer_reset, 686e88f27b3Smrg .emit_reloc = msm_ringbuffer_emit_reloc, 687e88f27b3Smrg .emit_reloc_ring = msm_ringbuffer_emit_reloc_ring, 6883f012e29Smrg .cmd_count = msm_ringbuffer_cmd_count, 689e88f27b3Smrg .destroy = msm_ringbuffer_destroy, 690e88f27b3Smrg}; 691e88f27b3Smrg 692e6188e58Smrgdrm_private struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe, 6937cdc0497Smrg uint32_t size, enum fd_ringbuffer_flags flags) 694e88f27b3Smrg{ 695e88f27b3Smrg struct msm_ringbuffer *msm_ring; 696d8807b2fSmrg struct fd_ringbuffer *ring; 697e88f27b3Smrg 698e88f27b3Smrg msm_ring = calloc(1, sizeof(*msm_ring)); 699e88f27b3Smrg if (!msm_ring) { 700e88f27b3Smrg ERROR_MSG("allocation failed"); 701d8807b2fSmrg return NULL; 702e88f27b3Smrg } 703e88f27b3Smrg 7043f012e29Smrg if (size == 0) { 7053f012e29Smrg assert(pipe->dev->version >= FD_VERSION_UNLIMITED_CMDS); 7063f012e29Smrg size = INIT_SIZE; 7073f012e29Smrg msm_ring->is_growable = TRUE; 7083f012e29Smrg } 7093f012e29Smrg 7103f012e29Smrg list_inithead(&msm_ring->cmd_list); 7113f012e29Smrg msm_ring->seqno = ++to_msm_device(pipe->dev)->ring_cnt; 7123f012e29Smrg 713e88f27b3Smrg ring = &msm_ring->base; 7147cdc0497Smrg atomic_set(&ring->refcnt, 1); 7157cdc0497Smrg 716e88f27b3Smrg ring->funcs = &funcs; 7173f012e29Smrg ring->size = size; 7183f012e29Smrg ring->pipe = pipe; /* needed in ring_cmd_new() */ 719e88f27b3Smrg 7207cdc0497Smrg ring_cmd_new(ring, size, flags); 721e88f27b3Smrg 722e88f27b3Smrg return ring; 723e88f27b3Smrg} 724