1d722e3fbSopenharmony_ci/*
2d722e3fbSopenharmony_ci * Copyright (C) 2014-2015 Etnaviv Project
3d722e3fbSopenharmony_ci *
4d722e3fbSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5d722e3fbSopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6d722e3fbSopenharmony_ci * to deal in the Software without restriction, including without limitation
7d722e3fbSopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8d722e3fbSopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9d722e3fbSopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10d722e3fbSopenharmony_ci *
11d722e3fbSopenharmony_ci * The above copyright notice and this permission notice (including the next
12d722e3fbSopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13d722e3fbSopenharmony_ci * Software.
14d722e3fbSopenharmony_ci *
15d722e3fbSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16d722e3fbSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17d722e3fbSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18d722e3fbSopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19d722e3fbSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20d722e3fbSopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21d722e3fbSopenharmony_ci * SOFTWARE.
22d722e3fbSopenharmony_ci *
23d722e3fbSopenharmony_ci * Authors:
24d722e3fbSopenharmony_ci *    Christian Gmeiner <christian.gmeiner@gmail.com>
25d722e3fbSopenharmony_ci */
26d722e3fbSopenharmony_ci
27d722e3fbSopenharmony_ci#include <assert.h>
28d722e3fbSopenharmony_ci
29d722e3fbSopenharmony_ci#include "etnaviv_drmif.h"
30d722e3fbSopenharmony_ci#include "etnaviv_priv.h"
31d722e3fbSopenharmony_ci
32d722e3fbSopenharmony_cistatic pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
33d722e3fbSopenharmony_ci
34d722e3fbSopenharmony_cistatic void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz)
35d722e3fbSopenharmony_ci{
36d722e3fbSopenharmony_ci	if ((nr + 1) > *max) {
37d722e3fbSopenharmony_ci		if ((*max * 2) < (nr + 1))
38d722e3fbSopenharmony_ci			*max = nr + 5;
39d722e3fbSopenharmony_ci		else
40d722e3fbSopenharmony_ci			*max = *max * 2;
41d722e3fbSopenharmony_ci		ptr = realloc(ptr, *max * sz);
42d722e3fbSopenharmony_ci	}
43d722e3fbSopenharmony_ci
44d722e3fbSopenharmony_ci	return ptr;
45d722e3fbSopenharmony_ci}
46d722e3fbSopenharmony_ci
47d722e3fbSopenharmony_ci#define APPEND(x, name) ({ \
48d722e3fbSopenharmony_ci	(x)->name = grow((x)->name, (x)->nr_ ## name, &(x)->max_ ## name, sizeof((x)->name[0])); \
49d722e3fbSopenharmony_ci	(x)->nr_ ## name ++; \
50d722e3fbSopenharmony_ci})
51d722e3fbSopenharmony_ci
52d722e3fbSopenharmony_cistatic inline struct etna_cmd_stream_priv *
53d722e3fbSopenharmony_cietna_cmd_stream_priv(struct etna_cmd_stream *stream)
54d722e3fbSopenharmony_ci{
55d722e3fbSopenharmony_ci    return (struct etna_cmd_stream_priv *)stream;
56d722e3fbSopenharmony_ci}
57d722e3fbSopenharmony_ci
58d722e3fbSopenharmony_cidrm_public struct etna_cmd_stream *etna_cmd_stream_new(struct etna_pipe *pipe,
59d722e3fbSopenharmony_ci        uint32_t size,
60d722e3fbSopenharmony_ci		void (*reset_notify)(struct etna_cmd_stream *stream, void *priv),
61d722e3fbSopenharmony_ci		void *priv)
62d722e3fbSopenharmony_ci{
63d722e3fbSopenharmony_ci	struct etna_cmd_stream_priv *stream = NULL;
64d722e3fbSopenharmony_ci
65d722e3fbSopenharmony_ci	if (size == 0) {
66d722e3fbSopenharmony_ci		ERROR_MSG("invalid size of 0");
67d722e3fbSopenharmony_ci		goto fail;
68d722e3fbSopenharmony_ci	}
69d722e3fbSopenharmony_ci
70d722e3fbSopenharmony_ci	stream = calloc(1, sizeof(*stream));
71d722e3fbSopenharmony_ci	if (!stream) {
72d722e3fbSopenharmony_ci		ERROR_MSG("allocation failed");
73d722e3fbSopenharmony_ci		goto fail;
74d722e3fbSopenharmony_ci	}
75d722e3fbSopenharmony_ci
76d722e3fbSopenharmony_ci	/* allocate even number of 32-bit words */
77d722e3fbSopenharmony_ci	size = ALIGN(size, 2);
78d722e3fbSopenharmony_ci
79d722e3fbSopenharmony_ci	stream->base.buffer = malloc(size * sizeof(uint32_t));
80d722e3fbSopenharmony_ci	if (!stream->base.buffer) {
81d722e3fbSopenharmony_ci		ERROR_MSG("allocation failed");
82d722e3fbSopenharmony_ci		goto fail;
83d722e3fbSopenharmony_ci	}
84d722e3fbSopenharmony_ci
85d722e3fbSopenharmony_ci	stream->base.size = size;
86d722e3fbSopenharmony_ci	stream->pipe = pipe;
87d722e3fbSopenharmony_ci	stream->reset_notify = reset_notify;
88d722e3fbSopenharmony_ci	stream->reset_notify_priv = priv;
89d722e3fbSopenharmony_ci
90d722e3fbSopenharmony_ci	return &stream->base;
91d722e3fbSopenharmony_ci
92d722e3fbSopenharmony_cifail:
93d722e3fbSopenharmony_ci	if (stream)
94d722e3fbSopenharmony_ci		etna_cmd_stream_del(&stream->base);
95d722e3fbSopenharmony_ci
96d722e3fbSopenharmony_ci	return NULL;
97d722e3fbSopenharmony_ci}
98d722e3fbSopenharmony_ci
99d722e3fbSopenharmony_cidrm_public void etna_cmd_stream_del(struct etna_cmd_stream *stream)
100d722e3fbSopenharmony_ci{
101d722e3fbSopenharmony_ci	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
102d722e3fbSopenharmony_ci
103d722e3fbSopenharmony_ci	free(stream->buffer);
104d722e3fbSopenharmony_ci	free(priv->submit.relocs);
105d722e3fbSopenharmony_ci	free(priv->submit.pmrs);
106d722e3fbSopenharmony_ci	free(priv);
107d722e3fbSopenharmony_ci}
108d722e3fbSopenharmony_ci
109d722e3fbSopenharmony_cistatic void reset_buffer(struct etna_cmd_stream *stream)
110d722e3fbSopenharmony_ci{
111d722e3fbSopenharmony_ci	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
112d722e3fbSopenharmony_ci
113d722e3fbSopenharmony_ci	stream->offset = 0;
114d722e3fbSopenharmony_ci	priv->submit.nr_bos = 0;
115d722e3fbSopenharmony_ci	priv->submit.nr_relocs = 0;
116d722e3fbSopenharmony_ci	priv->submit.nr_pmrs = 0;
117d722e3fbSopenharmony_ci	priv->nr_bos = 0;
118d722e3fbSopenharmony_ci
119d722e3fbSopenharmony_ci	if (priv->reset_notify)
120d722e3fbSopenharmony_ci		priv->reset_notify(stream, priv->reset_notify_priv);
121d722e3fbSopenharmony_ci}
122d722e3fbSopenharmony_ci
123d722e3fbSopenharmony_cidrm_public uint32_t etna_cmd_stream_timestamp(struct etna_cmd_stream *stream)
124d722e3fbSopenharmony_ci{
125d722e3fbSopenharmony_ci	return etna_cmd_stream_priv(stream)->last_timestamp;
126d722e3fbSopenharmony_ci}
127d722e3fbSopenharmony_ci
128d722e3fbSopenharmony_cistatic uint32_t append_bo(struct etna_cmd_stream *stream, struct etna_bo *bo)
129d722e3fbSopenharmony_ci{
130d722e3fbSopenharmony_ci	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
131d722e3fbSopenharmony_ci	uint32_t idx;
132d722e3fbSopenharmony_ci
133d722e3fbSopenharmony_ci	idx = APPEND(&priv->submit, bos);
134d722e3fbSopenharmony_ci	idx = APPEND(priv, bos);
135d722e3fbSopenharmony_ci
136d722e3fbSopenharmony_ci	priv->submit.bos[idx].flags = 0;
137d722e3fbSopenharmony_ci	priv->submit.bos[idx].handle = bo->handle;
138d722e3fbSopenharmony_ci
139d722e3fbSopenharmony_ci	priv->bos[idx] = etna_bo_ref(bo);
140d722e3fbSopenharmony_ci
141d722e3fbSopenharmony_ci	return idx;
142d722e3fbSopenharmony_ci}
143d722e3fbSopenharmony_ci
144d722e3fbSopenharmony_ci/* add (if needed) bo, return idx: */
145d722e3fbSopenharmony_cistatic uint32_t bo2idx(struct etna_cmd_stream *stream, struct etna_bo *bo,
146d722e3fbSopenharmony_ci		uint32_t flags)
147d722e3fbSopenharmony_ci{
148d722e3fbSopenharmony_ci	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
149d722e3fbSopenharmony_ci	uint32_t idx;
150d722e3fbSopenharmony_ci
151d722e3fbSopenharmony_ci	pthread_mutex_lock(&idx_lock);
152d722e3fbSopenharmony_ci
153d722e3fbSopenharmony_ci	if (bo->current_stream == stream) {
154d722e3fbSopenharmony_ci		idx = bo->idx;
155d722e3fbSopenharmony_ci	} else {
156d722e3fbSopenharmony_ci		/* slow-path: */
157d722e3fbSopenharmony_ci		for (idx = 0; idx < priv->nr_bos; idx++)
158d722e3fbSopenharmony_ci			if (priv->bos[idx] == bo)
159d722e3fbSopenharmony_ci				break;
160d722e3fbSopenharmony_ci		if (idx == priv->nr_bos) {
161d722e3fbSopenharmony_ci			/* not found */
162d722e3fbSopenharmony_ci			idx = append_bo(stream, bo);
163d722e3fbSopenharmony_ci		}
164d722e3fbSopenharmony_ci		bo->current_stream = stream;
165d722e3fbSopenharmony_ci		bo->idx = idx;
166d722e3fbSopenharmony_ci	}
167d722e3fbSopenharmony_ci	pthread_mutex_unlock(&idx_lock);
168d722e3fbSopenharmony_ci
169d722e3fbSopenharmony_ci	if (flags & ETNA_RELOC_READ)
170d722e3fbSopenharmony_ci		priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_READ;
171d722e3fbSopenharmony_ci	if (flags & ETNA_RELOC_WRITE)
172d722e3fbSopenharmony_ci		priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_WRITE;
173d722e3fbSopenharmony_ci
174d722e3fbSopenharmony_ci	return idx;
175d722e3fbSopenharmony_ci}
176d722e3fbSopenharmony_ci
177d722e3fbSopenharmony_cistatic void flush(struct etna_cmd_stream *stream, int in_fence_fd,
178d722e3fbSopenharmony_ci		  int *out_fence_fd)
179d722e3fbSopenharmony_ci{
180d722e3fbSopenharmony_ci	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
181d722e3fbSopenharmony_ci	int ret, id = priv->pipe->id;
182d722e3fbSopenharmony_ci	struct etna_gpu *gpu = priv->pipe->gpu;
183d722e3fbSopenharmony_ci
184d722e3fbSopenharmony_ci	struct drm_etnaviv_gem_submit req = {
185d722e3fbSopenharmony_ci		.pipe = gpu->core,
186d722e3fbSopenharmony_ci		.exec_state = id,
187d722e3fbSopenharmony_ci		.bos = VOID2U64(priv->submit.bos),
188d722e3fbSopenharmony_ci		.nr_bos = priv->submit.nr_bos,
189d722e3fbSopenharmony_ci		.relocs = VOID2U64(priv->submit.relocs),
190d722e3fbSopenharmony_ci		.nr_relocs = priv->submit.nr_relocs,
191d722e3fbSopenharmony_ci		.pmrs = VOID2U64(priv->submit.pmrs),
192d722e3fbSopenharmony_ci		.nr_pmrs = priv->submit.nr_pmrs,
193d722e3fbSopenharmony_ci		.stream = VOID2U64(stream->buffer),
194d722e3fbSopenharmony_ci		.stream_size = stream->offset * 4, /* in bytes */
195d722e3fbSopenharmony_ci	};
196d722e3fbSopenharmony_ci
197d722e3fbSopenharmony_ci	if (in_fence_fd != -1) {
198d722e3fbSopenharmony_ci		req.flags |= ETNA_SUBMIT_FENCE_FD_IN | ETNA_SUBMIT_NO_IMPLICIT;
199d722e3fbSopenharmony_ci		req.fence_fd = in_fence_fd;
200d722e3fbSopenharmony_ci	}
201d722e3fbSopenharmony_ci
202d722e3fbSopenharmony_ci	if (out_fence_fd)
203d722e3fbSopenharmony_ci		req.flags |= ETNA_SUBMIT_FENCE_FD_OUT;
204d722e3fbSopenharmony_ci
205d722e3fbSopenharmony_ci	ret = drmCommandWriteRead(gpu->dev->fd, DRM_ETNAVIV_GEM_SUBMIT,
206d722e3fbSopenharmony_ci			&req, sizeof(req));
207d722e3fbSopenharmony_ci
208d722e3fbSopenharmony_ci	if (ret)
209d722e3fbSopenharmony_ci		ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
210d722e3fbSopenharmony_ci	else
211d722e3fbSopenharmony_ci		priv->last_timestamp = req.fence;
212d722e3fbSopenharmony_ci
213d722e3fbSopenharmony_ci	for (uint32_t i = 0; i < priv->nr_bos; i++) {
214d722e3fbSopenharmony_ci		struct etna_bo *bo = priv->bos[i];
215d722e3fbSopenharmony_ci
216d722e3fbSopenharmony_ci		bo->current_stream = NULL;
217d722e3fbSopenharmony_ci		etna_bo_del(bo);
218d722e3fbSopenharmony_ci	}
219d722e3fbSopenharmony_ci
220d722e3fbSopenharmony_ci	if (out_fence_fd)
221d722e3fbSopenharmony_ci		*out_fence_fd = req.fence_fd;
222d722e3fbSopenharmony_ci}
223d722e3fbSopenharmony_ci
224d722e3fbSopenharmony_cidrm_public void etna_cmd_stream_flush(struct etna_cmd_stream *stream)
225d722e3fbSopenharmony_ci{
226d722e3fbSopenharmony_ci	flush(stream, -1, NULL);
227d722e3fbSopenharmony_ci	reset_buffer(stream);
228d722e3fbSopenharmony_ci}
229d722e3fbSopenharmony_ci
230d722e3fbSopenharmony_cidrm_public void etna_cmd_stream_flush2(struct etna_cmd_stream *stream,
231d722e3fbSopenharmony_ci									   int in_fence_fd,
232d722e3fbSopenharmony_ci									   int *out_fence_fd)
233d722e3fbSopenharmony_ci{
234d722e3fbSopenharmony_ci	flush(stream, in_fence_fd, out_fence_fd);
235d722e3fbSopenharmony_ci	reset_buffer(stream);
236d722e3fbSopenharmony_ci}
237d722e3fbSopenharmony_ci
238d722e3fbSopenharmony_cidrm_public void etna_cmd_stream_finish(struct etna_cmd_stream *stream)
239d722e3fbSopenharmony_ci{
240d722e3fbSopenharmony_ci	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
241d722e3fbSopenharmony_ci
242d722e3fbSopenharmony_ci	flush(stream, -1, NULL);
243d722e3fbSopenharmony_ci	etna_pipe_wait(priv->pipe, priv->last_timestamp, 5000);
244d722e3fbSopenharmony_ci	reset_buffer(stream);
245d722e3fbSopenharmony_ci}
246d722e3fbSopenharmony_ci
247d722e3fbSopenharmony_cidrm_public void etna_cmd_stream_reloc(struct etna_cmd_stream *stream,
248d722e3fbSopenharmony_ci									  const struct etna_reloc *r)
249d722e3fbSopenharmony_ci{
250d722e3fbSopenharmony_ci	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
251d722e3fbSopenharmony_ci	struct drm_etnaviv_gem_submit_reloc *reloc;
252d722e3fbSopenharmony_ci	uint32_t idx = APPEND(&priv->submit, relocs);
253d722e3fbSopenharmony_ci	uint32_t addr = 0;
254d722e3fbSopenharmony_ci
255d722e3fbSopenharmony_ci	reloc = &priv->submit.relocs[idx];
256d722e3fbSopenharmony_ci
257d722e3fbSopenharmony_ci	reloc->reloc_idx = bo2idx(stream, r->bo, r->flags);
258d722e3fbSopenharmony_ci	reloc->reloc_offset = r->offset;
259d722e3fbSopenharmony_ci	reloc->submit_offset = stream->offset * 4; /* in bytes */
260d722e3fbSopenharmony_ci	reloc->flags = 0;
261d722e3fbSopenharmony_ci
262d722e3fbSopenharmony_ci	etna_cmd_stream_emit(stream, addr);
263d722e3fbSopenharmony_ci}
264d722e3fbSopenharmony_ci
265d722e3fbSopenharmony_cidrm_public void etna_cmd_stream_perf(struct etna_cmd_stream *stream, const struct etna_perf *p)
266d722e3fbSopenharmony_ci{
267d722e3fbSopenharmony_ci	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
268d722e3fbSopenharmony_ci	struct drm_etnaviv_gem_submit_pmr *pmr;
269d722e3fbSopenharmony_ci	uint32_t idx = APPEND(&priv->submit, pmrs);
270d722e3fbSopenharmony_ci
271d722e3fbSopenharmony_ci	pmr = &priv->submit.pmrs[idx];
272d722e3fbSopenharmony_ci
273d722e3fbSopenharmony_ci	pmr->flags = p->flags;
274d722e3fbSopenharmony_ci	pmr->sequence = p->sequence;
275d722e3fbSopenharmony_ci	pmr->read_offset = p->offset;
276d722e3fbSopenharmony_ci	pmr->read_idx = bo2idx(stream, p->bo, ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE);
277d722e3fbSopenharmony_ci	pmr->domain = p->signal->domain->id;
278d722e3fbSopenharmony_ci	pmr->signal = p->signal->signal;
279d722e3fbSopenharmony_ci}
280