1b8e80941Smrg/*
2b8e80941Smrg * Copyright (C) 2018 Rob Clark <robclark@freedesktop.org>
3b8e80941Smrg *
4b8e80941Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5b8e80941Smrg * copy of this software and associated documentation files (the "Software"),
6b8e80941Smrg * to deal in the Software without restriction, including without limitation
7b8e80941Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8b8e80941Smrg * and/or sell copies of the Software, and to permit persons to whom the
9b8e80941Smrg * Software is furnished to do so, subject to the following conditions:
10b8e80941Smrg *
11b8e80941Smrg * The above copyright notice and this permission notice (including the next
12b8e80941Smrg * paragraph) shall be included in all copies or substantial portions of the
13b8e80941Smrg * Software.
14b8e80941Smrg *
15b8e80941Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16b8e80941Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17b8e80941Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18b8e80941Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19b8e80941Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20b8e80941Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21b8e80941Smrg * SOFTWARE.
22b8e80941Smrg *
23b8e80941Smrg * Authors:
24b8e80941Smrg *    Rob Clark <robclark@freedesktop.org>
25b8e80941Smrg */
26b8e80941Smrg
27b8e80941Smrg#include <assert.h>
28b8e80941Smrg#include <inttypes.h>
29b8e80941Smrg
30b8e80941Smrg#include "util/hash_table.h"
31b8e80941Smrg#include "util/slab.h"
32b8e80941Smrg
33b8e80941Smrg#include "drm/freedreno_ringbuffer.h"
34b8e80941Smrg#include "msm_priv.h"
35b8e80941Smrg
36b8e80941Smrg/* A "softpin" implementation of submit/ringbuffer, which lowers CPU overhead
37b8e80941Smrg * by avoiding the additional tracking necessary to build cmds/relocs tables
38b8e80941Smrg * (but still builds a bos table)
39b8e80941Smrg */
40b8e80941Smrg
41b8e80941Smrg
42b8e80941Smrg#define INIT_SIZE 0x1000
43b8e80941Smrg
44b8e80941Smrgstatic pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
45b8e80941Smrg
46b8e80941Smrg
47b8e80941Smrgstruct msm_submit_sp {
48b8e80941Smrg	struct fd_submit base;
49b8e80941Smrg
50b8e80941Smrg	DECLARE_ARRAY(struct drm_msm_gem_submit_bo, submit_bos);
51b8e80941Smrg	DECLARE_ARRAY(struct fd_bo *, bos);
52b8e80941Smrg
53b8e80941Smrg	unsigned seqno;
54b8e80941Smrg
55b8e80941Smrg	/* maps fd_bo to idx in bos table: */
56b8e80941Smrg	struct hash_table *bo_table;
57b8e80941Smrg
58b8e80941Smrg	struct slab_mempool ring_pool;
59b8e80941Smrg
60b8e80941Smrg	struct fd_ringbuffer *primary;
61b8e80941Smrg
62b8e80941Smrg	/* Allow for sub-allocation of stateobj ring buffers (ie. sharing
63b8e80941Smrg	 * the same underlying bo)..
64b8e80941Smrg	 *
65b8e80941Smrg	 * We also rely on previous stateobj having been fully constructed
66b8e80941Smrg	 * so we can reclaim extra space at it's end.
67b8e80941Smrg	 */
68b8e80941Smrg	struct fd_ringbuffer *suballoc_ring;
69b8e80941Smrg};
70b8e80941SmrgFD_DEFINE_CAST(fd_submit, msm_submit_sp);
71b8e80941Smrg
72b8e80941Smrg/* for FD_RINGBUFFER_GROWABLE rb's, tracks the 'finalized' cmdstream buffers
73b8e80941Smrg * and sizes.  Ie. a finalized buffer can have no more commands appended to
74b8e80941Smrg * it.
75b8e80941Smrg */
76b8e80941Smrgstruct msm_cmd_sp {
77b8e80941Smrg	struct fd_bo *ring_bo;
78b8e80941Smrg	unsigned size;
79b8e80941Smrg};
80b8e80941Smrg
81b8e80941Smrg/* for _FD_RINGBUFFER_OBJECT rb's we need to track the bo's and flags to
82b8e80941Smrg * later copy into the submit when the stateobj rb is later referenced by
83b8e80941Smrg * a regular rb:
84b8e80941Smrg */
85b8e80941Smrgstruct msm_reloc_bo_sp {
86b8e80941Smrg	struct fd_bo *bo;
87b8e80941Smrg	unsigned flags;
88b8e80941Smrg};
89b8e80941Smrg
90b8e80941Smrgstruct msm_ringbuffer_sp {
91b8e80941Smrg	struct fd_ringbuffer base;
92b8e80941Smrg
93b8e80941Smrg	/* for FD_RINGBUFFER_STREAMING rb's which are sub-allocated */
94b8e80941Smrg	unsigned offset;
95b8e80941Smrg
96b8e80941Smrg// TODO check disasm.. hopefully compilers CSE can realize that
97b8e80941Smrg// reloc_bos and cmds are at the same offsets and optimize some
98b8e80941Smrg// divergent cases into single case
99b8e80941Smrg	union {
100b8e80941Smrg		/* for _FD_RINGBUFFER_OBJECT case: */
101b8e80941Smrg		struct {
102b8e80941Smrg			struct fd_pipe *pipe;
103b8e80941Smrg			DECLARE_ARRAY(struct msm_reloc_bo_sp, reloc_bos);
104b8e80941Smrg		};
105b8e80941Smrg		/* for other cases: */
106b8e80941Smrg		struct {
107b8e80941Smrg			struct fd_submit *submit;
108b8e80941Smrg			DECLARE_ARRAY(struct msm_cmd_sp, cmds);
109b8e80941Smrg		};
110b8e80941Smrg	} u;
111b8e80941Smrg
112b8e80941Smrg	struct fd_bo *ring_bo;
113b8e80941Smrg};
114b8e80941SmrgFD_DEFINE_CAST(fd_ringbuffer, msm_ringbuffer_sp);
115b8e80941Smrg
116b8e80941Smrgstatic void finalize_current_cmd(struct fd_ringbuffer *ring);
117b8e80941Smrgstatic struct fd_ringbuffer * msm_ringbuffer_sp_init(
118b8e80941Smrg		struct msm_ringbuffer_sp *msm_ring,
119b8e80941Smrg		uint32_t size, enum fd_ringbuffer_flags flags);
120b8e80941Smrg
121b8e80941Smrg/* add (if needed) bo to submit and return index: */
122b8e80941Smrgstatic uint32_t
123b8e80941Smrgappend_bo(struct msm_submit_sp *submit, struct fd_bo *bo, uint32_t flags)
124b8e80941Smrg{
125b8e80941Smrg	struct msm_bo *msm_bo = to_msm_bo(bo);
126b8e80941Smrg	uint32_t idx;
127b8e80941Smrg	pthread_mutex_lock(&idx_lock);
128b8e80941Smrg	if (likely(msm_bo->current_submit_seqno == submit->seqno)) {
129b8e80941Smrg		idx = msm_bo->idx;
130b8e80941Smrg	} else {
131b8e80941Smrg		uint32_t hash = _mesa_hash_pointer(bo);
132b8e80941Smrg		struct hash_entry *entry;
133b8e80941Smrg
134b8e80941Smrg		entry = _mesa_hash_table_search_pre_hashed(submit->bo_table, hash, bo);
135b8e80941Smrg		if (entry) {
136b8e80941Smrg			/* found */
137b8e80941Smrg			idx = (uint32_t)(uintptr_t)entry->data;
138b8e80941Smrg		} else {
139b8e80941Smrg			idx = APPEND(submit, submit_bos);
140b8e80941Smrg			idx = APPEND(submit, bos);
141b8e80941Smrg
142b8e80941Smrg			submit->submit_bos[idx].flags = 0;
143b8e80941Smrg			submit->submit_bos[idx].handle = bo->handle;
144b8e80941Smrg			submit->submit_bos[idx].presumed = 0;
145b8e80941Smrg
146b8e80941Smrg			submit->bos[idx] = fd_bo_ref(bo);
147b8e80941Smrg
148b8e80941Smrg			_mesa_hash_table_insert_pre_hashed(submit->bo_table, hash, bo,
149b8e80941Smrg					(void *)(uintptr_t)idx);
150b8e80941Smrg		}
151b8e80941Smrg		msm_bo->current_submit_seqno = submit->seqno;
152b8e80941Smrg		msm_bo->idx = idx;
153b8e80941Smrg	}
154b8e80941Smrg	pthread_mutex_unlock(&idx_lock);
155b8e80941Smrg	if (flags & FD_RELOC_READ)
156b8e80941Smrg		submit->submit_bos[idx].flags |= MSM_SUBMIT_BO_READ;
157b8e80941Smrg	if (flags & FD_RELOC_WRITE)
158b8e80941Smrg		submit->submit_bos[idx].flags |= MSM_SUBMIT_BO_WRITE;
159b8e80941Smrg	if (flags & FD_RELOC_DUMP)
160b8e80941Smrg		submit->submit_bos[idx].flags |= MSM_SUBMIT_BO_DUMP;
161b8e80941Smrg	return idx;
162b8e80941Smrg}
163b8e80941Smrg
164b8e80941Smrgstatic void
165b8e80941Smrgmsm_submit_suballoc_ring_bo(struct fd_submit *submit,
166b8e80941Smrg		struct msm_ringbuffer_sp *msm_ring, uint32_t size)
167b8e80941Smrg{
168b8e80941Smrg	struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);
169b8e80941Smrg	unsigned suballoc_offset = 0;
170b8e80941Smrg	struct fd_bo *suballoc_bo = NULL;
171b8e80941Smrg
172b8e80941Smrg	if (msm_submit->suballoc_ring) {
173b8e80941Smrg		struct msm_ringbuffer_sp *suballoc_ring =
174b8e80941Smrg				to_msm_ringbuffer_sp(msm_submit->suballoc_ring);
175b8e80941Smrg
176b8e80941Smrg		suballoc_bo = suballoc_ring->ring_bo;
177b8e80941Smrg		suballoc_offset = fd_ringbuffer_size(msm_submit->suballoc_ring) +
178b8e80941Smrg				suballoc_ring->offset;
179b8e80941Smrg
180b8e80941Smrg		suballoc_offset = align(suballoc_offset, 0x10);
181b8e80941Smrg
182b8e80941Smrg		if ((size + suballoc_offset) > suballoc_bo->size) {
183b8e80941Smrg			suballoc_bo = NULL;
184b8e80941Smrg		}
185b8e80941Smrg	}
186b8e80941Smrg
187b8e80941Smrg	if (!suballoc_bo) {
188b8e80941Smrg		// TODO possibly larger size for streaming bo?
189b8e80941Smrg		msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev,
190b8e80941Smrg				0x8000, DRM_FREEDRENO_GEM_GPUREADONLY);
191b8e80941Smrg		msm_ring->offset = 0;
192b8e80941Smrg	} else {
193b8e80941Smrg		msm_ring->ring_bo = fd_bo_ref(suballoc_bo);
194b8e80941Smrg		msm_ring->offset = suballoc_offset;
195b8e80941Smrg	}
196b8e80941Smrg
197b8e80941Smrg	struct fd_ringbuffer *old_suballoc_ring = msm_submit->suballoc_ring;
198b8e80941Smrg
199b8e80941Smrg	msm_submit->suballoc_ring = fd_ringbuffer_ref(&msm_ring->base);
200b8e80941Smrg
201b8e80941Smrg	if (old_suballoc_ring)
202b8e80941Smrg		fd_ringbuffer_del(old_suballoc_ring);
203b8e80941Smrg}
204b8e80941Smrg
205b8e80941Smrgstatic struct fd_ringbuffer *
206b8e80941Smrgmsm_submit_sp_new_ringbuffer(struct fd_submit *submit, uint32_t size,
207b8e80941Smrg		enum fd_ringbuffer_flags flags)
208b8e80941Smrg{
209b8e80941Smrg	struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);
210b8e80941Smrg	struct msm_ringbuffer_sp *msm_ring;
211b8e80941Smrg
212b8e80941Smrg	msm_ring = slab_alloc_st(&msm_submit->ring_pool);
213b8e80941Smrg
214b8e80941Smrg	msm_ring->u.submit = submit;
215b8e80941Smrg
216b8e80941Smrg	/* NOTE: needs to be before _suballoc_ring_bo() since it could
217b8e80941Smrg	 * increment the refcnt of the current ring
218b8e80941Smrg	 */
219b8e80941Smrg	msm_ring->base.refcnt = 1;
220b8e80941Smrg
221b8e80941Smrg	if (flags & FD_RINGBUFFER_STREAMING) {
222b8e80941Smrg		msm_submit_suballoc_ring_bo(submit, msm_ring, size);
223b8e80941Smrg	} else {
224b8e80941Smrg		if (flags & FD_RINGBUFFER_GROWABLE)
225b8e80941Smrg			size = INIT_SIZE;
226b8e80941Smrg
227b8e80941Smrg		msm_ring->offset = 0;
228b8e80941Smrg		msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, size,
229b8e80941Smrg				DRM_FREEDRENO_GEM_GPUREADONLY);
230b8e80941Smrg	}
231b8e80941Smrg
232b8e80941Smrg	if (!msm_ringbuffer_sp_init(msm_ring, size, flags))
233b8e80941Smrg		return NULL;
234b8e80941Smrg
235b8e80941Smrg	if (flags & FD_RINGBUFFER_PRIMARY) {
236b8e80941Smrg		debug_assert(!msm_submit->primary);
237b8e80941Smrg		msm_submit->primary = fd_ringbuffer_ref(&msm_ring->base);
238b8e80941Smrg	}
239b8e80941Smrg
240b8e80941Smrg	return &msm_ring->base;
241b8e80941Smrg}
242b8e80941Smrg
243b8e80941Smrgstatic int
244b8e80941Smrgmsm_submit_sp_flush(struct fd_submit *submit, int in_fence_fd,
245b8e80941Smrg		int *out_fence_fd, uint32_t *out_fence)
246b8e80941Smrg{
247b8e80941Smrg	struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);
248b8e80941Smrg	struct msm_pipe *msm_pipe = to_msm_pipe(submit->pipe);
249b8e80941Smrg	struct drm_msm_gem_submit req = {
250b8e80941Smrg			.flags = msm_pipe->pipe,
251b8e80941Smrg			.queueid = msm_pipe->queue_id,
252b8e80941Smrg	};
253b8e80941Smrg	int ret;
254b8e80941Smrg
255b8e80941Smrg	debug_assert(msm_submit->primary);
256b8e80941Smrg	finalize_current_cmd(msm_submit->primary);
257b8e80941Smrg
258b8e80941Smrg	struct msm_ringbuffer_sp *primary = to_msm_ringbuffer_sp(msm_submit->primary);
259b8e80941Smrg	struct drm_msm_gem_submit_cmd cmds[primary->u.nr_cmds];
260b8e80941Smrg
261b8e80941Smrg	for (unsigned i = 0; i < primary->u.nr_cmds; i++) {
262b8e80941Smrg		cmds[i].type = MSM_SUBMIT_CMD_BUF;
263b8e80941Smrg		cmds[i].submit_idx = append_bo(msm_submit,
264b8e80941Smrg				primary->u.cmds[i].ring_bo, FD_RELOC_READ | FD_RELOC_DUMP);
265b8e80941Smrg		cmds[i].submit_offset = primary->offset;
266b8e80941Smrg		cmds[i].size = primary->u.cmds[i].size;
267b8e80941Smrg		cmds[i].pad = 0;
268b8e80941Smrg		cmds[i].nr_relocs = 0;
269b8e80941Smrg	}
270b8e80941Smrg
271b8e80941Smrg	if (in_fence_fd != -1) {
272b8e80941Smrg		req.flags |= MSM_SUBMIT_FENCE_FD_IN | MSM_SUBMIT_NO_IMPLICIT;
273b8e80941Smrg		req.fence_fd = in_fence_fd;
274b8e80941Smrg	}
275b8e80941Smrg
276b8e80941Smrg	if (out_fence_fd) {
277b8e80941Smrg		req.flags |= MSM_SUBMIT_FENCE_FD_OUT;
278b8e80941Smrg	}
279b8e80941Smrg
280b8e80941Smrg	/* needs to be after get_cmd() as that could create bos/cmds table: */
281b8e80941Smrg	req.bos = VOID2U64(msm_submit->submit_bos),
282b8e80941Smrg	req.nr_bos = msm_submit->nr_submit_bos;
283b8e80941Smrg	req.cmds = VOID2U64(cmds),
284b8e80941Smrg	req.nr_cmds = primary->u.nr_cmds;
285b8e80941Smrg
286b8e80941Smrg	DEBUG_MSG("nr_cmds=%u, nr_bos=%u", req.nr_cmds, req.nr_bos);
287b8e80941Smrg
288b8e80941Smrg	ret = drmCommandWriteRead(submit->pipe->dev->fd, DRM_MSM_GEM_SUBMIT,
289b8e80941Smrg			&req, sizeof(req));
290b8e80941Smrg	if (ret) {
291b8e80941Smrg		ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
292b8e80941Smrg		msm_dump_submit(&req);
293b8e80941Smrg	} else if (!ret) {
294b8e80941Smrg		if (out_fence)
295b8e80941Smrg			*out_fence = req.fence;
296b8e80941Smrg
297b8e80941Smrg		if (out_fence_fd)
298b8e80941Smrg			*out_fence_fd = req.fence_fd;
299b8e80941Smrg	}
300b8e80941Smrg
301b8e80941Smrg	return ret;
302b8e80941Smrg}
303b8e80941Smrg
304b8e80941Smrgstatic void
305b8e80941Smrgmsm_submit_sp_destroy(struct fd_submit *submit)
306b8e80941Smrg{
307b8e80941Smrg	struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);
308b8e80941Smrg
309b8e80941Smrg	if (msm_submit->primary)
310b8e80941Smrg		fd_ringbuffer_del(msm_submit->primary);
311b8e80941Smrg	if (msm_submit->suballoc_ring)
312b8e80941Smrg		fd_ringbuffer_del(msm_submit->suballoc_ring);
313b8e80941Smrg
314b8e80941Smrg	_mesa_hash_table_destroy(msm_submit->bo_table, NULL);
315b8e80941Smrg
316b8e80941Smrg	// TODO it would be nice to have a way to debug_assert() if all
317b8e80941Smrg	// rb's haven't been free'd back to the slab, because that is
318b8e80941Smrg	// an indication that we are leaking bo's
319b8e80941Smrg	slab_destroy(&msm_submit->ring_pool);
320b8e80941Smrg
321b8e80941Smrg	for (unsigned i = 0; i < msm_submit->nr_bos; i++)
322b8e80941Smrg		fd_bo_del(msm_submit->bos[i]);
323b8e80941Smrg
324b8e80941Smrg	free(msm_submit->submit_bos);
325b8e80941Smrg	free(msm_submit->bos);
326b8e80941Smrg	free(msm_submit);
327b8e80941Smrg}
328b8e80941Smrg
329b8e80941Smrgstatic const struct fd_submit_funcs submit_funcs = {
330b8e80941Smrg		.new_ringbuffer = msm_submit_sp_new_ringbuffer,
331b8e80941Smrg		.flush = msm_submit_sp_flush,
332b8e80941Smrg		.destroy = msm_submit_sp_destroy,
333b8e80941Smrg};
334b8e80941Smrg
335b8e80941Smrgstruct fd_submit *
336b8e80941Smrgmsm_submit_sp_new(struct fd_pipe *pipe)
337b8e80941Smrg{
338b8e80941Smrg	struct msm_submit_sp *msm_submit = calloc(1, sizeof(*msm_submit));
339b8e80941Smrg	struct fd_submit *submit;
340b8e80941Smrg	static unsigned submit_cnt = 0;
341b8e80941Smrg
342b8e80941Smrg	msm_submit->seqno = ++submit_cnt;
343b8e80941Smrg	msm_submit->bo_table = _mesa_hash_table_create(NULL,
344b8e80941Smrg			_mesa_hash_pointer, _mesa_key_pointer_equal);
345b8e80941Smrg	// TODO tune size:
346b8e80941Smrg	slab_create(&msm_submit->ring_pool, sizeof(struct msm_ringbuffer_sp), 16);
347b8e80941Smrg
348b8e80941Smrg	submit = &msm_submit->base;
349b8e80941Smrg	submit->pipe = pipe;
350b8e80941Smrg	submit->funcs = &submit_funcs;
351b8e80941Smrg
352b8e80941Smrg	return submit;
353b8e80941Smrg}
354b8e80941Smrg
355b8e80941Smrg
356b8e80941Smrgstatic void
357b8e80941Smrgfinalize_current_cmd(struct fd_ringbuffer *ring)
358b8e80941Smrg{
359b8e80941Smrg	debug_assert(!(ring->flags & _FD_RINGBUFFER_OBJECT));
360b8e80941Smrg
361b8e80941Smrg	struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);
362b8e80941Smrg	unsigned idx = APPEND(&msm_ring->u, cmds);
363b8e80941Smrg
364b8e80941Smrg	msm_ring->u.cmds[idx].ring_bo = fd_bo_ref(msm_ring->ring_bo);
365b8e80941Smrg	msm_ring->u.cmds[idx].size = offset_bytes(ring->cur, ring->start);
366b8e80941Smrg}
367b8e80941Smrg
368b8e80941Smrgstatic void
369b8e80941Smrgmsm_ringbuffer_sp_grow(struct fd_ringbuffer *ring, uint32_t size)
370b8e80941Smrg{
371b8e80941Smrg	struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);
372b8e80941Smrg	struct fd_pipe *pipe = msm_ring->u.submit->pipe;
373b8e80941Smrg
374b8e80941Smrg	debug_assert(ring->flags & FD_RINGBUFFER_GROWABLE);
375b8e80941Smrg
376b8e80941Smrg	finalize_current_cmd(ring);
377b8e80941Smrg
378b8e80941Smrg	fd_bo_del(msm_ring->ring_bo);
379b8e80941Smrg	msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size,
380b8e80941Smrg			DRM_FREEDRENO_GEM_GPUREADONLY);
381b8e80941Smrg
382b8e80941Smrg	ring->start = fd_bo_map(msm_ring->ring_bo);
383b8e80941Smrg	ring->end = &(ring->start[size/4]);
384b8e80941Smrg	ring->cur = ring->start;
385b8e80941Smrg	ring->size = size;
386b8e80941Smrg}
387b8e80941Smrg
388b8e80941Smrgstatic void
389b8e80941Smrgmsm_ringbuffer_sp_emit_reloc(struct fd_ringbuffer *ring,
390b8e80941Smrg		const struct fd_reloc *reloc)
391b8e80941Smrg{
392b8e80941Smrg	struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);
393b8e80941Smrg	struct fd_pipe *pipe;
394b8e80941Smrg
395b8e80941Smrg	if (ring->flags & _FD_RINGBUFFER_OBJECT) {
396b8e80941Smrg		unsigned idx = APPEND(&msm_ring->u, reloc_bos);
397b8e80941Smrg
398b8e80941Smrg		msm_ring->u.reloc_bos[idx].bo = fd_bo_ref(reloc->bo);
399b8e80941Smrg		msm_ring->u.reloc_bos[idx].flags = reloc->flags;
400b8e80941Smrg
401b8e80941Smrg		pipe = msm_ring->u.pipe;
402b8e80941Smrg	} else {
403b8e80941Smrg		struct msm_submit_sp *msm_submit =
404b8e80941Smrg				to_msm_submit_sp(msm_ring->u.submit);
405b8e80941Smrg
406b8e80941Smrg		append_bo(msm_submit, reloc->bo, reloc->flags);
407b8e80941Smrg
408b8e80941Smrg		pipe = msm_ring->u.submit->pipe;
409b8e80941Smrg	}
410b8e80941Smrg
411b8e80941Smrg	uint64_t iova = fd_bo_get_iova(reloc->bo) + reloc->offset;
412b8e80941Smrg	uint32_t dword = iova;
413b8e80941Smrg	int shift = reloc->shift;
414b8e80941Smrg
415b8e80941Smrg	if (shift < 0)
416b8e80941Smrg		dword >>= -shift;
417b8e80941Smrg	else
418b8e80941Smrg		dword <<= shift;
419b8e80941Smrg
420b8e80941Smrg	(*ring->cur++) = dword | reloc->or;
421b8e80941Smrg
422b8e80941Smrg	if (pipe->gpu_id >= 500) {
423b8e80941Smrg		dword = iova >> 32;
424b8e80941Smrg		shift -= 32;
425b8e80941Smrg
426b8e80941Smrg		if (shift < 0)
427b8e80941Smrg			dword >>= -shift;
428b8e80941Smrg		else
429b8e80941Smrg			dword <<= shift;
430b8e80941Smrg
431b8e80941Smrg		(*ring->cur++) = dword | reloc->orhi;
432b8e80941Smrg	}
433b8e80941Smrg}
434b8e80941Smrg
435b8e80941Smrgstatic uint32_t
436b8e80941Smrgmsm_ringbuffer_sp_emit_reloc_ring(struct fd_ringbuffer *ring,
437b8e80941Smrg		struct fd_ringbuffer *target, uint32_t cmd_idx)
438b8e80941Smrg{
439b8e80941Smrg	struct msm_ringbuffer_sp *msm_target = to_msm_ringbuffer_sp(target);
440b8e80941Smrg	struct fd_bo *bo;
441b8e80941Smrg	uint32_t size;
442b8e80941Smrg
443b8e80941Smrg	if ((target->flags & FD_RINGBUFFER_GROWABLE) &&
444b8e80941Smrg			(cmd_idx < msm_target->u.nr_cmds)) {
445b8e80941Smrg		bo   = msm_target->u.cmds[cmd_idx].ring_bo;
446b8e80941Smrg		size = msm_target->u.cmds[cmd_idx].size;
447b8e80941Smrg	} else {
448b8e80941Smrg		bo   = msm_target->ring_bo;
449b8e80941Smrg		size = offset_bytes(target->cur, target->start);
450b8e80941Smrg	}
451b8e80941Smrg
452b8e80941Smrg	msm_ringbuffer_sp_emit_reloc(ring, &(struct fd_reloc){
453b8e80941Smrg		.bo     = bo,
454b8e80941Smrg		.flags  = FD_RELOC_READ | FD_RELOC_DUMP,
455b8e80941Smrg		.offset = msm_target->offset,
456b8e80941Smrg	});
457b8e80941Smrg
458b8e80941Smrg	if (!(target->flags & _FD_RINGBUFFER_OBJECT))
459b8e80941Smrg		return size;
460b8e80941Smrg
461b8e80941Smrg	struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);
462b8e80941Smrg
463b8e80941Smrg	if (ring->flags & _FD_RINGBUFFER_OBJECT) {
464b8e80941Smrg		for (unsigned i = 0; i < msm_target->u.nr_reloc_bos; i++) {
465b8e80941Smrg			unsigned idx = APPEND(&msm_ring->u, reloc_bos);
466b8e80941Smrg
467b8e80941Smrg			msm_ring->u.reloc_bos[idx].bo =
468b8e80941Smrg				fd_bo_ref(msm_target->u.reloc_bos[i].bo);
469b8e80941Smrg			msm_ring->u.reloc_bos[idx].flags =
470b8e80941Smrg				msm_target->u.reloc_bos[i].flags;
471b8e80941Smrg		}
472b8e80941Smrg	} else {
473b8e80941Smrg		// TODO it would be nice to know whether we have already
474b8e80941Smrg		// seen this target before.  But hopefully we hit the
475b8e80941Smrg		// append_bo() fast path enough for this to not matter:
476b8e80941Smrg		struct msm_submit_sp *msm_submit = to_msm_submit_sp(msm_ring->u.submit);
477b8e80941Smrg
478b8e80941Smrg		for (unsigned i = 0; i < msm_target->u.nr_reloc_bos; i++) {
479b8e80941Smrg			append_bo(msm_submit, msm_target->u.reloc_bos[i].bo,
480b8e80941Smrg					msm_target->u.reloc_bos[i].flags);
481b8e80941Smrg		}
482b8e80941Smrg	}
483b8e80941Smrg
484b8e80941Smrg	return size;
485b8e80941Smrg}
486b8e80941Smrg
487b8e80941Smrgstatic uint32_t
488b8e80941Smrgmsm_ringbuffer_sp_cmd_count(struct fd_ringbuffer *ring)
489b8e80941Smrg{
490b8e80941Smrg	if (ring->flags & FD_RINGBUFFER_GROWABLE)
491b8e80941Smrg		return to_msm_ringbuffer_sp(ring)->u.nr_cmds + 1;
492b8e80941Smrg	return 1;
493b8e80941Smrg}
494b8e80941Smrg
495b8e80941Smrgstatic void
496b8e80941Smrgmsm_ringbuffer_sp_destroy(struct fd_ringbuffer *ring)
497b8e80941Smrg{
498b8e80941Smrg	struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);
499b8e80941Smrg
500b8e80941Smrg	fd_bo_del(msm_ring->ring_bo);
501b8e80941Smrg
502b8e80941Smrg	if (ring->flags & _FD_RINGBUFFER_OBJECT) {
503b8e80941Smrg		for (unsigned i = 0; i < msm_ring->u.nr_reloc_bos; i++) {
504b8e80941Smrg			fd_bo_del(msm_ring->u.reloc_bos[i].bo);
505b8e80941Smrg		}
506b8e80941Smrg
507b8e80941Smrg		free(msm_ring);
508b8e80941Smrg	} else {
509b8e80941Smrg		struct fd_submit *submit = msm_ring->u.submit;
510b8e80941Smrg
511b8e80941Smrg		for (unsigned i = 0; i < msm_ring->u.nr_cmds; i++) {
512b8e80941Smrg			fd_bo_del(msm_ring->u.cmds[i].ring_bo);
513b8e80941Smrg		}
514b8e80941Smrg
515b8e80941Smrg		slab_free_st(&to_msm_submit_sp(submit)->ring_pool, msm_ring);
516b8e80941Smrg	}
517b8e80941Smrg}
518b8e80941Smrg
519b8e80941Smrgstatic const struct fd_ringbuffer_funcs ring_funcs = {
520b8e80941Smrg		.grow = msm_ringbuffer_sp_grow,
521b8e80941Smrg		.emit_reloc = msm_ringbuffer_sp_emit_reloc,
522b8e80941Smrg		.emit_reloc_ring = msm_ringbuffer_sp_emit_reloc_ring,
523b8e80941Smrg		.cmd_count = msm_ringbuffer_sp_cmd_count,
524b8e80941Smrg		.destroy = msm_ringbuffer_sp_destroy,
525b8e80941Smrg};
526b8e80941Smrg
527b8e80941Smrgstatic inline struct fd_ringbuffer *
528b8e80941Smrgmsm_ringbuffer_sp_init(struct msm_ringbuffer_sp *msm_ring, uint32_t size,
529b8e80941Smrg		enum fd_ringbuffer_flags flags)
530b8e80941Smrg{
531b8e80941Smrg	struct fd_ringbuffer *ring = &msm_ring->base;
532b8e80941Smrg
533b8e80941Smrg	debug_assert(msm_ring->ring_bo);
534b8e80941Smrg
535b8e80941Smrg	uint8_t *base = fd_bo_map(msm_ring->ring_bo);
536b8e80941Smrg	ring->start = (void *)(base + msm_ring->offset);
537b8e80941Smrg	ring->end = &(ring->start[size/4]);
538b8e80941Smrg	ring->cur = ring->start;
539b8e80941Smrg
540b8e80941Smrg	ring->size = size;
541b8e80941Smrg	ring->flags = flags;
542b8e80941Smrg
543b8e80941Smrg	ring->funcs = &ring_funcs;
544b8e80941Smrg
545b8e80941Smrg	// TODO initializing these could probably be conditional on flags
546b8e80941Smrg	// since unneed for FD_RINGBUFFER_STAGING case..
547b8e80941Smrg	msm_ring->u.cmds = NULL;
548b8e80941Smrg	msm_ring->u.nr_cmds = msm_ring->u.max_cmds = 0;
549b8e80941Smrg
550b8e80941Smrg	msm_ring->u.reloc_bos = NULL;
551b8e80941Smrg	msm_ring->u.nr_reloc_bos = msm_ring->u.max_reloc_bos = 0;
552b8e80941Smrg
553b8e80941Smrg	return ring;
554b8e80941Smrg}
555b8e80941Smrg
556b8e80941Smrgstruct fd_ringbuffer *
557b8e80941Smrgmsm_ringbuffer_sp_new_object(struct fd_pipe *pipe, uint32_t size)
558b8e80941Smrg{
559b8e80941Smrg	struct msm_ringbuffer_sp *msm_ring = malloc(sizeof(*msm_ring));
560b8e80941Smrg
561b8e80941Smrg	msm_ring->u.pipe = pipe;
562b8e80941Smrg	msm_ring->offset = 0;
563b8e80941Smrg	msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size,
564b8e80941Smrg			DRM_FREEDRENO_GEM_GPUREADONLY);
565b8e80941Smrg	msm_ring->base.refcnt = 1;
566b8e80941Smrg
567b8e80941Smrg	return msm_ringbuffer_sp_init(msm_ring, size, _FD_RINGBUFFER_OBJECT);
568b8e80941Smrg}
569