1b8e80941Smrg/*
2b8e80941Smrg * Copyright (C) 2012-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/set.h"
32b8e80941Smrg#include "util/slab.h"
33b8e80941Smrg
34b8e80941Smrg#include "drm/freedreno_ringbuffer.h"
35b8e80941Smrg#include "msm_priv.h"
36b8e80941Smrg
37b8e80941Smrg/* The legacy implementation of submit/ringbuffer, which still does the
38b8e80941Smrg * traditional reloc and cmd tracking
39b8e80941Smrg */
40b8e80941Smrg
41b8e80941Smrg
42b8e80941Smrg#define INIT_SIZE 0x1000
43b8e80941Smrg
44b8e80941Smrgstatic pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
45b8e80941Smrg
46b8e80941Smrg
47b8e80941Smrgstruct msm_submit {
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	/* hash-set of associated rings: */
61b8e80941Smrg	struct set *ring_set;
62b8e80941Smrg
63b8e80941Smrg	struct fd_ringbuffer *primary;
64b8e80941Smrg
65b8e80941Smrg	/* Allow for sub-allocation of stateobj ring buffers (ie. sharing
66b8e80941Smrg	 * the same underlying bo)..
67b8e80941Smrg	 *
68b8e80941Smrg	 * We also rely on previous stateobj having been fully constructed
69b8e80941Smrg	 * so we can reclaim extra space at it's end.
70b8e80941Smrg	 */
71b8e80941Smrg	struct fd_ringbuffer *suballoc_ring;
72b8e80941Smrg};
73b8e80941SmrgFD_DEFINE_CAST(fd_submit, msm_submit);
74b8e80941Smrg
75b8e80941Smrg/* for FD_RINGBUFFER_GROWABLE rb's, tracks the 'finalized' cmdstream buffers
76b8e80941Smrg * and sizes.  Ie. a finalized buffer can have no more commands appended to
77b8e80941Smrg * it.
78b8e80941Smrg */
79b8e80941Smrgstruct msm_cmd {
80b8e80941Smrg	struct fd_bo *ring_bo;
81b8e80941Smrg	unsigned size;
82b8e80941Smrg	DECLARE_ARRAY(struct drm_msm_gem_submit_reloc, relocs);
83b8e80941Smrg};
84b8e80941Smrg
85b8e80941Smrgstatic struct msm_cmd *
86b8e80941Smrgcmd_new(struct fd_bo *ring_bo)
87b8e80941Smrg{
88b8e80941Smrg	struct msm_cmd *cmd = malloc(sizeof(*cmd));
89b8e80941Smrg	cmd->ring_bo = fd_bo_ref(ring_bo);
90b8e80941Smrg	cmd->size = 0;
91b8e80941Smrg	cmd->nr_relocs = cmd->max_relocs = 0;
92b8e80941Smrg	cmd->relocs = NULL;
93b8e80941Smrg	return cmd;
94b8e80941Smrg}
95b8e80941Smrg
96b8e80941Smrgstatic void
97b8e80941Smrgcmd_free(struct msm_cmd *cmd)
98b8e80941Smrg{
99b8e80941Smrg	fd_bo_del(cmd->ring_bo);
100b8e80941Smrg	free(cmd->relocs);
101b8e80941Smrg	free(cmd);
102b8e80941Smrg}
103b8e80941Smrg
104b8e80941Smrg/* for _FD_RINGBUFFER_OBJECT rb's we need to track the bo's and flags to
105b8e80941Smrg * later copy into the submit when the stateobj rb is later referenced by
106b8e80941Smrg * a regular rb:
107b8e80941Smrg */
108b8e80941Smrgstruct msm_reloc_bo {
109b8e80941Smrg	struct fd_bo *bo;
110b8e80941Smrg	unsigned flags;
111b8e80941Smrg};
112b8e80941Smrg
113b8e80941Smrgstruct msm_ringbuffer {
114b8e80941Smrg	struct fd_ringbuffer base;
115b8e80941Smrg
116b8e80941Smrg	/* for FD_RINGBUFFER_STREAMING rb's which are sub-allocated */
117b8e80941Smrg	unsigned offset;
118b8e80941Smrg
119b8e80941Smrg	union {
120b8e80941Smrg		/* for _FD_RINGBUFFER_OBJECT case: */
121b8e80941Smrg		struct {
122b8e80941Smrg			struct fd_pipe *pipe;
123b8e80941Smrg			DECLARE_ARRAY(struct msm_reloc_bo, reloc_bos);
124b8e80941Smrg			struct set *ring_set;
125b8e80941Smrg		};
126b8e80941Smrg		/* for other cases: */
127b8e80941Smrg		struct {
128b8e80941Smrg			struct fd_submit *submit;
129b8e80941Smrg			DECLARE_ARRAY(struct msm_cmd *, cmds);
130b8e80941Smrg		};
131b8e80941Smrg	} u;
132b8e80941Smrg
133b8e80941Smrg	struct msm_cmd *cmd;          /* current cmd */
134b8e80941Smrg	struct fd_bo *ring_bo;
135b8e80941Smrg};
136b8e80941SmrgFD_DEFINE_CAST(fd_ringbuffer, msm_ringbuffer);
137b8e80941Smrg
138b8e80941Smrgstatic void finalize_current_cmd(struct fd_ringbuffer *ring);
139b8e80941Smrgstatic struct fd_ringbuffer * msm_ringbuffer_init(
140b8e80941Smrg		struct msm_ringbuffer *msm_ring,
141b8e80941Smrg		uint32_t size, enum fd_ringbuffer_flags flags);
142b8e80941Smrg
143b8e80941Smrg/* add (if needed) bo to submit and return index: */
144b8e80941Smrgstatic uint32_t
145b8e80941Smrgappend_bo(struct msm_submit *submit, struct fd_bo *bo, uint32_t flags)
146b8e80941Smrg{
147b8e80941Smrg	struct msm_bo *msm_bo = to_msm_bo(bo);
148b8e80941Smrg	uint32_t idx;
149b8e80941Smrg	pthread_mutex_lock(&idx_lock);
150b8e80941Smrg	if (likely(msm_bo->current_submit_seqno == submit->seqno)) {
151b8e80941Smrg		idx = msm_bo->idx;
152b8e80941Smrg	} else {
153b8e80941Smrg		uint32_t hash = _mesa_hash_pointer(bo);
154b8e80941Smrg		struct hash_entry *entry;
155b8e80941Smrg
156b8e80941Smrg		entry = _mesa_hash_table_search_pre_hashed(submit->bo_table, hash, bo);
157b8e80941Smrg		if (entry) {
158b8e80941Smrg			/* found */
159b8e80941Smrg			idx = (uint32_t)(uintptr_t)entry->data;
160b8e80941Smrg		} else {
161b8e80941Smrg			idx = APPEND(submit, submit_bos);
162b8e80941Smrg			idx = APPEND(submit, bos);
163b8e80941Smrg
164b8e80941Smrg			submit->submit_bos[idx].flags = 0;
165b8e80941Smrg			submit->submit_bos[idx].handle = bo->handle;
166b8e80941Smrg			submit->submit_bos[idx].presumed = 0;
167b8e80941Smrg
168b8e80941Smrg			submit->bos[idx] = fd_bo_ref(bo);
169b8e80941Smrg
170b8e80941Smrg			_mesa_hash_table_insert_pre_hashed(submit->bo_table, hash, bo,
171b8e80941Smrg					(void *)(uintptr_t)idx);
172b8e80941Smrg		}
173b8e80941Smrg		msm_bo->current_submit_seqno = submit->seqno;
174b8e80941Smrg		msm_bo->idx = idx;
175b8e80941Smrg	}
176b8e80941Smrg	pthread_mutex_unlock(&idx_lock);
177b8e80941Smrg	if (flags & FD_RELOC_READ)
178b8e80941Smrg		submit->submit_bos[idx].flags |= MSM_SUBMIT_BO_READ;
179b8e80941Smrg	if (flags & FD_RELOC_WRITE)
180b8e80941Smrg		submit->submit_bos[idx].flags |= MSM_SUBMIT_BO_WRITE;
181b8e80941Smrg	return idx;
182b8e80941Smrg}
183b8e80941Smrg
184b8e80941Smrgstatic void
185b8e80941Smrgappend_ring(struct set *set, struct fd_ringbuffer *ring)
186b8e80941Smrg{
187b8e80941Smrg	uint32_t hash = _mesa_hash_pointer(ring);
188b8e80941Smrg
189b8e80941Smrg	if (!_mesa_set_search_pre_hashed(set, hash, ring)) {
190b8e80941Smrg		fd_ringbuffer_ref(ring);
191b8e80941Smrg		_mesa_set_add_pre_hashed(set, hash, ring);
192b8e80941Smrg	}
193b8e80941Smrg}
194b8e80941Smrg
195b8e80941Smrgstatic void
196b8e80941Smrgmsm_submit_suballoc_ring_bo(struct fd_submit *submit,
197b8e80941Smrg		struct msm_ringbuffer *msm_ring, uint32_t size)
198b8e80941Smrg{
199b8e80941Smrg	struct msm_submit *msm_submit = to_msm_submit(submit);
200b8e80941Smrg	unsigned suballoc_offset = 0;
201b8e80941Smrg	struct fd_bo *suballoc_bo = NULL;
202b8e80941Smrg
203b8e80941Smrg	if (msm_submit->suballoc_ring) {
204b8e80941Smrg		struct msm_ringbuffer *suballoc_ring =
205b8e80941Smrg				to_msm_ringbuffer(msm_submit->suballoc_ring);
206b8e80941Smrg
207b8e80941Smrg		suballoc_bo = suballoc_ring->ring_bo;
208b8e80941Smrg		suballoc_offset = fd_ringbuffer_size(msm_submit->suballoc_ring) +
209b8e80941Smrg				suballoc_ring->offset;
210b8e80941Smrg
211b8e80941Smrg		suballoc_offset = align(suballoc_offset, 0x10);
212b8e80941Smrg
213b8e80941Smrg		if ((size + suballoc_offset) > suballoc_bo->size) {
214b8e80941Smrg			suballoc_bo = NULL;
215b8e80941Smrg		}
216b8e80941Smrg	}
217b8e80941Smrg
218b8e80941Smrg	if (!suballoc_bo) {
219b8e80941Smrg		// TODO possibly larger size for streaming bo?
220b8e80941Smrg		msm_ring->ring_bo = fd_bo_new_ring(
221b8e80941Smrg				submit->pipe->dev, 0x8000, 0);
222b8e80941Smrg		msm_ring->offset = 0;
223b8e80941Smrg	} else {
224b8e80941Smrg		msm_ring->ring_bo = fd_bo_ref(suballoc_bo);
225b8e80941Smrg		msm_ring->offset = suballoc_offset;
226b8e80941Smrg	}
227b8e80941Smrg
228b8e80941Smrg	struct fd_ringbuffer *old_suballoc_ring = msm_submit->suballoc_ring;
229b8e80941Smrg
230b8e80941Smrg	msm_submit->suballoc_ring = fd_ringbuffer_ref(&msm_ring->base);
231b8e80941Smrg
232b8e80941Smrg	if (old_suballoc_ring)
233b8e80941Smrg		fd_ringbuffer_del(old_suballoc_ring);
234b8e80941Smrg}
235b8e80941Smrg
236b8e80941Smrgstatic struct fd_ringbuffer *
237b8e80941Smrgmsm_submit_new_ringbuffer(struct fd_submit *submit, uint32_t size,
238b8e80941Smrg		enum fd_ringbuffer_flags flags)
239b8e80941Smrg{
240b8e80941Smrg	struct msm_submit *msm_submit = to_msm_submit(submit);
241b8e80941Smrg	struct msm_ringbuffer *msm_ring;
242b8e80941Smrg
243b8e80941Smrg	msm_ring = slab_alloc_st(&msm_submit->ring_pool);
244b8e80941Smrg
245b8e80941Smrg	msm_ring->u.submit = submit;
246b8e80941Smrg
247b8e80941Smrg	/* NOTE: needs to be before _suballoc_ring_bo() since it could
248b8e80941Smrg	 * increment the refcnt of the current ring
249b8e80941Smrg	 */
250b8e80941Smrg	msm_ring->base.refcnt = 1;
251b8e80941Smrg
252b8e80941Smrg	if (flags & FD_RINGBUFFER_STREAMING) {
253b8e80941Smrg		msm_submit_suballoc_ring_bo(submit, msm_ring, size);
254b8e80941Smrg	} else {
255b8e80941Smrg		if (flags & FD_RINGBUFFER_GROWABLE)
256b8e80941Smrg			size = INIT_SIZE;
257b8e80941Smrg
258b8e80941Smrg		msm_ring->offset = 0;
259b8e80941Smrg		msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, size, 0);
260b8e80941Smrg	}
261b8e80941Smrg
262b8e80941Smrg	if (!msm_ringbuffer_init(msm_ring, size, flags))
263b8e80941Smrg		return NULL;
264b8e80941Smrg
265b8e80941Smrg	if (flags & FD_RINGBUFFER_PRIMARY) {
266b8e80941Smrg		debug_assert(!msm_submit->primary);
267b8e80941Smrg		msm_submit->primary = fd_ringbuffer_ref(&msm_ring->base);
268b8e80941Smrg	}
269b8e80941Smrg
270b8e80941Smrg	return &msm_ring->base;
271b8e80941Smrg}
272b8e80941Smrg
273b8e80941Smrgstatic struct drm_msm_gem_submit_reloc *
274b8e80941Smrghandle_stateobj_relocs(struct msm_submit *submit, struct msm_ringbuffer *ring)
275b8e80941Smrg{
276b8e80941Smrg	struct msm_cmd *cmd = ring->cmd;
277b8e80941Smrg	struct drm_msm_gem_submit_reloc *relocs;
278b8e80941Smrg
279b8e80941Smrg	relocs = malloc(cmd->nr_relocs * sizeof(*relocs));
280b8e80941Smrg
281b8e80941Smrg	for (unsigned i = 0; i < cmd->nr_relocs; i++) {
282b8e80941Smrg		unsigned idx = cmd->relocs[i].reloc_idx;
283b8e80941Smrg		struct fd_bo *bo = ring->u.reloc_bos[idx].bo;
284b8e80941Smrg		unsigned flags = 0;
285b8e80941Smrg
286b8e80941Smrg		if (ring->u.reloc_bos[idx].flags & MSM_SUBMIT_BO_READ)
287b8e80941Smrg			flags |= FD_RELOC_READ;
288b8e80941Smrg		if (ring->u.reloc_bos[idx].flags & MSM_SUBMIT_BO_WRITE)
289b8e80941Smrg			flags |= FD_RELOC_WRITE;
290b8e80941Smrg
291b8e80941Smrg		relocs[i] = cmd->relocs[i];
292b8e80941Smrg		relocs[i].reloc_idx = append_bo(submit, bo, flags);
293b8e80941Smrg	}
294b8e80941Smrg
295b8e80941Smrg	return relocs;
296b8e80941Smrg}
297b8e80941Smrg
298b8e80941Smrgstatic int
299b8e80941Smrgmsm_submit_flush(struct fd_submit *submit, int in_fence_fd,
300b8e80941Smrg		int *out_fence_fd, uint32_t *out_fence)
301b8e80941Smrg{
302b8e80941Smrg	struct msm_submit *msm_submit = to_msm_submit(submit);
303b8e80941Smrg	struct msm_pipe *msm_pipe = to_msm_pipe(submit->pipe);
304b8e80941Smrg	struct drm_msm_gem_submit req = {
305b8e80941Smrg			.flags = msm_pipe->pipe,
306b8e80941Smrg			.queueid = msm_pipe->queue_id,
307b8e80941Smrg	};
308b8e80941Smrg	int ret;
309b8e80941Smrg
310b8e80941Smrg	debug_assert(msm_submit->primary);
311b8e80941Smrg
312b8e80941Smrg	finalize_current_cmd(msm_submit->primary);
313b8e80941Smrg	append_ring(msm_submit->ring_set, msm_submit->primary);
314b8e80941Smrg
315b8e80941Smrg	unsigned nr_cmds = 0;
316b8e80941Smrg	unsigned nr_objs = 0;
317b8e80941Smrg
318b8e80941Smrg	set_foreach(msm_submit->ring_set, entry) {
319b8e80941Smrg		struct fd_ringbuffer *ring = (void *)entry->key;
320b8e80941Smrg		if (ring->flags & _FD_RINGBUFFER_OBJECT) {
321b8e80941Smrg			nr_cmds += 1;
322b8e80941Smrg			nr_objs += 1;
323b8e80941Smrg		} else {
324b8e80941Smrg			if (ring != msm_submit->primary)
325b8e80941Smrg				finalize_current_cmd(ring);
326b8e80941Smrg			nr_cmds += to_msm_ringbuffer(ring)->u.nr_cmds;
327b8e80941Smrg		}
328b8e80941Smrg	}
329b8e80941Smrg
330b8e80941Smrg	void *obj_relocs[nr_objs];
331b8e80941Smrg	struct drm_msm_gem_submit_cmd cmds[nr_cmds];
332b8e80941Smrg	unsigned i = 0, o = 0;
333b8e80941Smrg
334b8e80941Smrg	set_foreach(msm_submit->ring_set, entry) {
335b8e80941Smrg		struct fd_ringbuffer *ring = (void *)entry->key;
336b8e80941Smrg		struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
337b8e80941Smrg
338b8e80941Smrg		debug_assert(i < nr_cmds);
339b8e80941Smrg
340b8e80941Smrg		// TODO handle relocs:
341b8e80941Smrg		if (ring->flags & _FD_RINGBUFFER_OBJECT) {
342b8e80941Smrg
343b8e80941Smrg			debug_assert(o < nr_objs);
344b8e80941Smrg
345b8e80941Smrg			void *relocs = handle_stateobj_relocs(msm_submit, msm_ring);
346b8e80941Smrg			obj_relocs[o++] = relocs;
347b8e80941Smrg
348b8e80941Smrg			cmds[i].type = MSM_SUBMIT_CMD_IB_TARGET_BUF;
349b8e80941Smrg			cmds[i].submit_idx =
350b8e80941Smrg				append_bo(msm_submit, msm_ring->ring_bo, FD_RELOC_READ);
351b8e80941Smrg			cmds[i].submit_offset = msm_ring->offset;
352b8e80941Smrg			cmds[i].size = offset_bytes(ring->cur, ring->start);
353b8e80941Smrg			cmds[i].pad = 0;
354b8e80941Smrg			cmds[i].nr_relocs = msm_ring->cmd->nr_relocs;
355b8e80941Smrg			cmds[i].relocs = VOID2U64(relocs);
356b8e80941Smrg
357b8e80941Smrg			i++;
358b8e80941Smrg		} else {
359b8e80941Smrg			for (unsigned j = 0; j < msm_ring->u.nr_cmds; j++) {
360b8e80941Smrg				if (ring->flags & FD_RINGBUFFER_PRIMARY) {
361b8e80941Smrg					cmds[i].type = MSM_SUBMIT_CMD_BUF;
362b8e80941Smrg				} else {
363b8e80941Smrg					cmds[i].type = MSM_SUBMIT_CMD_IB_TARGET_BUF;
364b8e80941Smrg				}
365b8e80941Smrg				cmds[i].submit_idx = append_bo(msm_submit,
366b8e80941Smrg						msm_ring->u.cmds[j]->ring_bo, FD_RELOC_READ);
367b8e80941Smrg				cmds[i].submit_offset = msm_ring->offset;
368b8e80941Smrg				cmds[i].size = msm_ring->u.cmds[j]->size;
369b8e80941Smrg				cmds[i].pad = 0;
370b8e80941Smrg				cmds[i].nr_relocs = msm_ring->u.cmds[j]->nr_relocs;
371b8e80941Smrg				cmds[i].relocs = VOID2U64(msm_ring->u.cmds[j]->relocs);
372b8e80941Smrg
373b8e80941Smrg				i++;
374b8e80941Smrg			}
375b8e80941Smrg		}
376b8e80941Smrg	}
377b8e80941Smrg
378b8e80941Smrg	if (in_fence_fd != -1) {
379b8e80941Smrg		req.flags |= MSM_SUBMIT_FENCE_FD_IN | MSM_SUBMIT_NO_IMPLICIT;
380b8e80941Smrg		req.fence_fd = in_fence_fd;
381b8e80941Smrg	}
382b8e80941Smrg
383b8e80941Smrg	if (out_fence_fd) {
384b8e80941Smrg		req.flags |= MSM_SUBMIT_FENCE_FD_OUT;
385b8e80941Smrg	}
386b8e80941Smrg
387b8e80941Smrg	/* needs to be after get_cmd() as that could create bos/cmds table: */
388b8e80941Smrg	req.bos = VOID2U64(msm_submit->submit_bos),
389b8e80941Smrg	req.nr_bos = msm_submit->nr_submit_bos;
390b8e80941Smrg	req.cmds = VOID2U64(cmds),
391b8e80941Smrg	req.nr_cmds = nr_cmds;
392b8e80941Smrg
393b8e80941Smrg	DEBUG_MSG("nr_cmds=%u, nr_bos=%u", req.nr_cmds, req.nr_bos);
394b8e80941Smrg
395b8e80941Smrg	ret = drmCommandWriteRead(submit->pipe->dev->fd, DRM_MSM_GEM_SUBMIT,
396b8e80941Smrg			&req, sizeof(req));
397b8e80941Smrg	if (ret) {
398b8e80941Smrg		ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
399b8e80941Smrg		msm_dump_submit(&req);
400b8e80941Smrg	} else if (!ret) {
401b8e80941Smrg		if (out_fence)
402b8e80941Smrg			*out_fence = req.fence;
403b8e80941Smrg
404b8e80941Smrg		if (out_fence_fd)
405b8e80941Smrg			*out_fence_fd = req.fence_fd;
406b8e80941Smrg	}
407b8e80941Smrg
408b8e80941Smrg	for (unsigned o = 0; o < nr_objs; o++)
409b8e80941Smrg		free(obj_relocs[o]);
410b8e80941Smrg
411b8e80941Smrg	return ret;
412b8e80941Smrg}
413b8e80941Smrg
414b8e80941Smrgstatic void
415b8e80941Smrgunref_rings(struct set_entry *entry)
416b8e80941Smrg{
417b8e80941Smrg	struct fd_ringbuffer *ring = (void *)entry->key;
418b8e80941Smrg	fd_ringbuffer_del(ring);
419b8e80941Smrg}
420b8e80941Smrg
421b8e80941Smrgstatic void
422b8e80941Smrgmsm_submit_destroy(struct fd_submit *submit)
423b8e80941Smrg{
424b8e80941Smrg	struct msm_submit *msm_submit = to_msm_submit(submit);
425b8e80941Smrg
426b8e80941Smrg	if (msm_submit->primary)
427b8e80941Smrg		fd_ringbuffer_del(msm_submit->primary);
428b8e80941Smrg	if (msm_submit->suballoc_ring)
429b8e80941Smrg		fd_ringbuffer_del(msm_submit->suballoc_ring);
430b8e80941Smrg
431b8e80941Smrg	_mesa_hash_table_destroy(msm_submit->bo_table, NULL);
432b8e80941Smrg	_mesa_set_destroy(msm_submit->ring_set, unref_rings);
433b8e80941Smrg
434b8e80941Smrg	// TODO it would be nice to have a way to debug_assert() if all
435b8e80941Smrg	// rb's haven't been free'd back to the slab, because that is
436b8e80941Smrg	// an indication that we are leaking bo's
437b8e80941Smrg	slab_destroy(&msm_submit->ring_pool);
438b8e80941Smrg
439b8e80941Smrg	for (unsigned i = 0; i < msm_submit->nr_bos; i++)
440b8e80941Smrg		fd_bo_del(msm_submit->bos[i]);
441b8e80941Smrg
442b8e80941Smrg	free(msm_submit->submit_bos);
443b8e80941Smrg	free(msm_submit->bos);
444b8e80941Smrg	free(msm_submit);
445b8e80941Smrg}
446b8e80941Smrg
447b8e80941Smrgstatic const struct fd_submit_funcs submit_funcs = {
448b8e80941Smrg		.new_ringbuffer = msm_submit_new_ringbuffer,
449b8e80941Smrg		.flush = msm_submit_flush,
450b8e80941Smrg		.destroy = msm_submit_destroy,
451b8e80941Smrg};
452b8e80941Smrg
453b8e80941Smrgstruct fd_submit *
454b8e80941Smrgmsm_submit_new(struct fd_pipe *pipe)
455b8e80941Smrg{
456b8e80941Smrg	struct msm_submit *msm_submit = calloc(1, sizeof(*msm_submit));
457b8e80941Smrg	struct fd_submit *submit;
458b8e80941Smrg	static unsigned submit_cnt = 0;
459b8e80941Smrg
460b8e80941Smrg	msm_submit->seqno = ++submit_cnt;
461b8e80941Smrg	msm_submit->bo_table = _mesa_hash_table_create(NULL,
462b8e80941Smrg			_mesa_hash_pointer, _mesa_key_pointer_equal);
463b8e80941Smrg	msm_submit->ring_set = _mesa_set_create(NULL,
464b8e80941Smrg			_mesa_hash_pointer, _mesa_key_pointer_equal);
465b8e80941Smrg	// TODO tune size:
466b8e80941Smrg	slab_create(&msm_submit->ring_pool, sizeof(struct msm_ringbuffer), 16);
467b8e80941Smrg
468b8e80941Smrg	submit = &msm_submit->base;
469b8e80941Smrg	submit->pipe = pipe;
470b8e80941Smrg	submit->funcs = &submit_funcs;
471b8e80941Smrg
472b8e80941Smrg	return submit;
473b8e80941Smrg}
474b8e80941Smrg
475b8e80941Smrg
476b8e80941Smrgstatic void
477b8e80941Smrgfinalize_current_cmd(struct fd_ringbuffer *ring)
478b8e80941Smrg{
479b8e80941Smrg	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
480b8e80941Smrg
481b8e80941Smrg	debug_assert(!(ring->flags & _FD_RINGBUFFER_OBJECT));
482b8e80941Smrg
483b8e80941Smrg	if (!msm_ring->cmd)
484b8e80941Smrg		return;
485b8e80941Smrg
486b8e80941Smrg	debug_assert(msm_ring->cmd->ring_bo == msm_ring->ring_bo);
487b8e80941Smrg
488b8e80941Smrg	unsigned idx = APPEND(&msm_ring->u, cmds);
489b8e80941Smrg
490b8e80941Smrg	msm_ring->u.cmds[idx] = msm_ring->cmd;
491b8e80941Smrg	msm_ring->cmd = NULL;
492b8e80941Smrg
493b8e80941Smrg	msm_ring->u.cmds[idx]->size = offset_bytes(ring->cur, ring->start);
494b8e80941Smrg}
495b8e80941Smrg
496b8e80941Smrgstatic void
497b8e80941Smrgmsm_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t size)
498b8e80941Smrg{
499b8e80941Smrg	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
500b8e80941Smrg	struct fd_pipe *pipe = msm_ring->u.submit->pipe;
501b8e80941Smrg
502b8e80941Smrg	debug_assert(ring->flags & FD_RINGBUFFER_GROWABLE);
503b8e80941Smrg
504b8e80941Smrg	finalize_current_cmd(ring);
505b8e80941Smrg
506b8e80941Smrg	fd_bo_del(msm_ring->ring_bo);
507b8e80941Smrg	msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size, 0);
508b8e80941Smrg	msm_ring->cmd = cmd_new(msm_ring->ring_bo);
509b8e80941Smrg
510b8e80941Smrg	ring->start = fd_bo_map(msm_ring->ring_bo);
511b8e80941Smrg	ring->end = &(ring->start[size/4]);
512b8e80941Smrg	ring->cur = ring->start;
513b8e80941Smrg	ring->size = size;
514b8e80941Smrg}
515b8e80941Smrg
516b8e80941Smrgstatic void
517b8e80941Smrgmsm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
518b8e80941Smrg		const struct fd_reloc *reloc)
519b8e80941Smrg{
520b8e80941Smrg	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
521b8e80941Smrg	struct fd_pipe *pipe;
522b8e80941Smrg	unsigned reloc_idx;
523b8e80941Smrg
524b8e80941Smrg	if (ring->flags & _FD_RINGBUFFER_OBJECT) {
525b8e80941Smrg		unsigned idx = APPEND(&msm_ring->u, reloc_bos);
526b8e80941Smrg
527b8e80941Smrg		msm_ring->u.reloc_bos[idx].bo = fd_bo_ref(reloc->bo);
528b8e80941Smrg		msm_ring->u.reloc_bos[idx].flags = reloc->flags;
529b8e80941Smrg
530b8e80941Smrg		/* this gets fixed up at submit->flush() time, since this state-
531b8e80941Smrg		 * object rb can be used with many different submits
532b8e80941Smrg		 */
533b8e80941Smrg		reloc_idx = idx;
534b8e80941Smrg
535b8e80941Smrg		pipe = msm_ring->u.pipe;
536b8e80941Smrg	} else {
537b8e80941Smrg		struct msm_submit *msm_submit =
538b8e80941Smrg				to_msm_submit(msm_ring->u.submit);
539b8e80941Smrg
540b8e80941Smrg		reloc_idx = append_bo(msm_submit, reloc->bo, reloc->flags);
541b8e80941Smrg
542b8e80941Smrg		pipe = msm_ring->u.submit->pipe;
543b8e80941Smrg	}
544b8e80941Smrg
545b8e80941Smrg	struct drm_msm_gem_submit_reloc *r;
546b8e80941Smrg	unsigned idx = APPEND(msm_ring->cmd, relocs);
547b8e80941Smrg
548b8e80941Smrg	r = &msm_ring->cmd->relocs[idx];
549b8e80941Smrg
550b8e80941Smrg	r->reloc_idx = reloc_idx;
551b8e80941Smrg	r->reloc_offset = reloc->offset;
552b8e80941Smrg	r->or = reloc->or;
553b8e80941Smrg	r->shift = reloc->shift;
554b8e80941Smrg	r->submit_offset = offset_bytes(ring->cur, ring->start) +
555b8e80941Smrg			msm_ring->offset;
556b8e80941Smrg
557b8e80941Smrg	ring->cur++;
558b8e80941Smrg
559b8e80941Smrg	if (pipe->gpu_id >= 500) {
560b8e80941Smrg		idx = APPEND(msm_ring->cmd, relocs);
561b8e80941Smrg		r = &msm_ring->cmd->relocs[idx];
562b8e80941Smrg
563b8e80941Smrg		r->reloc_idx = reloc_idx;
564b8e80941Smrg		r->reloc_offset = reloc->offset;
565b8e80941Smrg		r->or = reloc->orhi;
566b8e80941Smrg		r->shift = reloc->shift - 32;
567b8e80941Smrg		r->submit_offset = offset_bytes(ring->cur, ring->start) +
568b8e80941Smrg				msm_ring->offset;
569b8e80941Smrg
570b8e80941Smrg		ring->cur++;
571b8e80941Smrg	}
572b8e80941Smrg}
573b8e80941Smrg
574b8e80941Smrgstatic void
575b8e80941Smrgappend_stateobj_rings(struct msm_submit *submit, struct fd_ringbuffer *target)
576b8e80941Smrg{
577b8e80941Smrg	struct msm_ringbuffer *msm_target = to_msm_ringbuffer(target);
578b8e80941Smrg
579b8e80941Smrg	debug_assert(target->flags & _FD_RINGBUFFER_OBJECT);
580b8e80941Smrg
581b8e80941Smrg	set_foreach(msm_target->u.ring_set, entry) {
582b8e80941Smrg		struct fd_ringbuffer *ring = (void *)entry->key;
583b8e80941Smrg
584b8e80941Smrg		append_ring(submit->ring_set, ring);
585b8e80941Smrg
586b8e80941Smrg		if (ring->flags & _FD_RINGBUFFER_OBJECT) {
587b8e80941Smrg			append_stateobj_rings(submit, ring);
588b8e80941Smrg		}
589b8e80941Smrg	}
590b8e80941Smrg}
591b8e80941Smrg
592b8e80941Smrgstatic uint32_t
593b8e80941Smrgmsm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
594b8e80941Smrg		struct fd_ringbuffer *target, uint32_t cmd_idx)
595b8e80941Smrg{
596b8e80941Smrg	struct msm_ringbuffer *msm_target = to_msm_ringbuffer(target);
597b8e80941Smrg	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
598b8e80941Smrg	struct fd_bo *bo;
599b8e80941Smrg	uint32_t size;
600b8e80941Smrg
601b8e80941Smrg	if ((target->flags & FD_RINGBUFFER_GROWABLE) &&
602b8e80941Smrg			(cmd_idx < msm_target->u.nr_cmds)) {
603b8e80941Smrg		bo   = msm_target->u.cmds[cmd_idx]->ring_bo;
604b8e80941Smrg		size = msm_target->u.cmds[cmd_idx]->size;
605b8e80941Smrg	} else {
606b8e80941Smrg		bo   = msm_target->ring_bo;
607b8e80941Smrg		size = offset_bytes(target->cur, target->start);
608b8e80941Smrg	}
609b8e80941Smrg
610b8e80941Smrg	msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){
611b8e80941Smrg		.bo     = bo,
612b8e80941Smrg		.flags  = FD_RELOC_READ,
613b8e80941Smrg		.offset = msm_target->offset,
614b8e80941Smrg	});
615b8e80941Smrg
616b8e80941Smrg	if ((target->flags & _FD_RINGBUFFER_OBJECT) &&
617b8e80941Smrg			!(ring->flags & _FD_RINGBUFFER_OBJECT)) {
618b8e80941Smrg		struct msm_submit *msm_submit = to_msm_submit(msm_ring->u.submit);
619b8e80941Smrg
620b8e80941Smrg		append_stateobj_rings(msm_submit, target);
621b8e80941Smrg	}
622b8e80941Smrg
623b8e80941Smrg	if (ring->flags & _FD_RINGBUFFER_OBJECT) {
624b8e80941Smrg		append_ring(msm_ring->u.ring_set, target);
625b8e80941Smrg	} else {
626b8e80941Smrg		struct msm_submit *msm_submit = to_msm_submit(msm_ring->u.submit);
627b8e80941Smrg		append_ring(msm_submit->ring_set, target);
628b8e80941Smrg	}
629b8e80941Smrg
630b8e80941Smrg	return size;
631b8e80941Smrg}
632b8e80941Smrg
633b8e80941Smrgstatic uint32_t
634b8e80941Smrgmsm_ringbuffer_cmd_count(struct fd_ringbuffer *ring)
635b8e80941Smrg{
636b8e80941Smrg	if (ring->flags & FD_RINGBUFFER_GROWABLE)
637b8e80941Smrg		return to_msm_ringbuffer(ring)->u.nr_cmds + 1;
638b8e80941Smrg	return 1;
639b8e80941Smrg}
640b8e80941Smrg
641b8e80941Smrgstatic void
642b8e80941Smrgmsm_ringbuffer_destroy(struct fd_ringbuffer *ring)
643b8e80941Smrg{
644b8e80941Smrg	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
645b8e80941Smrg
646b8e80941Smrg	fd_bo_del(msm_ring->ring_bo);
647b8e80941Smrg	if (msm_ring->cmd)
648b8e80941Smrg		cmd_free(msm_ring->cmd);
649b8e80941Smrg
650b8e80941Smrg	if (ring->flags & _FD_RINGBUFFER_OBJECT) {
651b8e80941Smrg		for (unsigned i = 0; i < msm_ring->u.nr_reloc_bos; i++) {
652b8e80941Smrg			fd_bo_del(msm_ring->u.reloc_bos[i].bo);
653b8e80941Smrg		}
654b8e80941Smrg
655b8e80941Smrg		_mesa_set_destroy(msm_ring->u.ring_set, unref_rings);
656b8e80941Smrg
657b8e80941Smrg		free(msm_ring->u.reloc_bos);
658b8e80941Smrg		free(msm_ring);
659b8e80941Smrg	} else {
660b8e80941Smrg		struct fd_submit *submit = msm_ring->u.submit;
661b8e80941Smrg
662b8e80941Smrg		for (unsigned i = 0; i < msm_ring->u.nr_cmds; i++) {
663b8e80941Smrg			cmd_free(msm_ring->u.cmds[i]);
664b8e80941Smrg		}
665b8e80941Smrg
666b8e80941Smrg		free(msm_ring->u.cmds);
667b8e80941Smrg		slab_free_st(&to_msm_submit(submit)->ring_pool, msm_ring);
668b8e80941Smrg	}
669b8e80941Smrg}
670b8e80941Smrg
671b8e80941Smrgstatic const struct fd_ringbuffer_funcs ring_funcs = {
672b8e80941Smrg		.grow = msm_ringbuffer_grow,
673b8e80941Smrg		.emit_reloc = msm_ringbuffer_emit_reloc,
674b8e80941Smrg		.emit_reloc_ring = msm_ringbuffer_emit_reloc_ring,
675b8e80941Smrg		.cmd_count = msm_ringbuffer_cmd_count,
676b8e80941Smrg		.destroy = msm_ringbuffer_destroy,
677b8e80941Smrg};
678b8e80941Smrg
679b8e80941Smrgstatic inline struct fd_ringbuffer *
680b8e80941Smrgmsm_ringbuffer_init(struct msm_ringbuffer *msm_ring, uint32_t size,
681b8e80941Smrg		enum fd_ringbuffer_flags flags)
682b8e80941Smrg{
683b8e80941Smrg	struct fd_ringbuffer *ring = &msm_ring->base;
684b8e80941Smrg
685b8e80941Smrg	debug_assert(msm_ring->ring_bo);
686b8e80941Smrg
687b8e80941Smrg	uint8_t *base = fd_bo_map(msm_ring->ring_bo);
688b8e80941Smrg	ring->start = (void *)(base + msm_ring->offset);
689b8e80941Smrg	ring->end = &(ring->start[size/4]);
690b8e80941Smrg	ring->cur = ring->start;
691b8e80941Smrg
692b8e80941Smrg	ring->size = size;
693b8e80941Smrg	ring->flags = flags;
694b8e80941Smrg
695b8e80941Smrg	ring->funcs = &ring_funcs;
696b8e80941Smrg
697b8e80941Smrg	msm_ring->u.cmds = NULL;
698b8e80941Smrg	msm_ring->u.nr_cmds = msm_ring->u.max_cmds = 0;
699b8e80941Smrg
700b8e80941Smrg	msm_ring->cmd = cmd_new(msm_ring->ring_bo);
701b8e80941Smrg
702b8e80941Smrg	return ring;
703b8e80941Smrg}
704b8e80941Smrg
705b8e80941Smrgstruct fd_ringbuffer *
706b8e80941Smrgmsm_ringbuffer_new_object(struct fd_pipe *pipe, uint32_t size)
707b8e80941Smrg{
708b8e80941Smrg	struct msm_ringbuffer *msm_ring = malloc(sizeof(*msm_ring));
709b8e80941Smrg
710b8e80941Smrg	msm_ring->u.pipe = pipe;
711b8e80941Smrg	msm_ring->offset = 0;
712b8e80941Smrg	msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size, 0);
713b8e80941Smrg	msm_ring->base.refcnt = 1;
714b8e80941Smrg
715b8e80941Smrg	msm_ring->u.reloc_bos = NULL;
716b8e80941Smrg	msm_ring->u.nr_reloc_bos = msm_ring->u.max_reloc_bos = 0;
717b8e80941Smrg
718b8e80941Smrg	msm_ring->u.ring_set = _mesa_set_create(NULL,
719b8e80941Smrg			_mesa_hash_pointer, _mesa_key_pointer_equal);
720b8e80941Smrg
721b8e80941Smrg	return msm_ringbuffer_init(msm_ring, size, _FD_RINGBUFFER_OBJECT);
722b8e80941Smrg}
723