1d722e3fbSopenharmony_ci/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
2d722e3fbSopenharmony_ci
3d722e3fbSopenharmony_ci/*
4d722e3fbSopenharmony_ci * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
5d722e3fbSopenharmony_ci *
6d722e3fbSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
7d722e3fbSopenharmony_ci * copy of this software and associated documentation files (the "Software"),
8d722e3fbSopenharmony_ci * to deal in the Software without restriction, including without limitation
9d722e3fbSopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10d722e3fbSopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
11d722e3fbSopenharmony_ci * Software is furnished to do so, subject to the following conditions:
12d722e3fbSopenharmony_ci *
13d722e3fbSopenharmony_ci * The above copyright notice and this permission notice (including the next
14d722e3fbSopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
15d722e3fbSopenharmony_ci * Software.
16d722e3fbSopenharmony_ci *
17d722e3fbSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18d722e3fbSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19d722e3fbSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20d722e3fbSopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21d722e3fbSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22d722e3fbSopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23d722e3fbSopenharmony_ci * SOFTWARE.
24d722e3fbSopenharmony_ci *
25d722e3fbSopenharmony_ci * Authors:
26d722e3fbSopenharmony_ci *    Rob Clark <robclark@freedesktop.org>
27d722e3fbSopenharmony_ci */
28d722e3fbSopenharmony_ci
29d722e3fbSopenharmony_ci#include "kgsl_priv.h"
30d722e3fbSopenharmony_ci
31d722e3fbSopenharmony_ci#include <linux/fb.h>
32d722e3fbSopenharmony_ci
33d722e3fbSopenharmony_cistatic int set_memtype(struct fd_device *dev, uint32_t handle, uint32_t flags)
34d722e3fbSopenharmony_ci{
35d722e3fbSopenharmony_ci	struct drm_kgsl_gem_memtype req = {
36d722e3fbSopenharmony_ci			.handle = handle,
37d722e3fbSopenharmony_ci			.type = flags & DRM_FREEDRENO_GEM_TYPE_MEM_MASK,
38d722e3fbSopenharmony_ci	};
39d722e3fbSopenharmony_ci
40d722e3fbSopenharmony_ci	return drmCommandWrite(dev->fd, DRM_KGSL_GEM_SETMEMTYPE,
41d722e3fbSopenharmony_ci			&req, sizeof(req));
42d722e3fbSopenharmony_ci}
43d722e3fbSopenharmony_ci
44d722e3fbSopenharmony_cistatic int bo_alloc(struct kgsl_bo *kgsl_bo)
45d722e3fbSopenharmony_ci{
46d722e3fbSopenharmony_ci	struct fd_bo *bo = &kgsl_bo->base;
47d722e3fbSopenharmony_ci	if (!kgsl_bo->offset) {
48d722e3fbSopenharmony_ci		struct drm_kgsl_gem_alloc req = {
49d722e3fbSopenharmony_ci				.handle = bo->handle,
50d722e3fbSopenharmony_ci		};
51d722e3fbSopenharmony_ci		int ret;
52d722e3fbSopenharmony_ci
53d722e3fbSopenharmony_ci		/* if the buffer is already backed by pages then this
54d722e3fbSopenharmony_ci		 * doesn't actually do anything (other than giving us
55d722e3fbSopenharmony_ci		 * the offset)
56d722e3fbSopenharmony_ci		 */
57d722e3fbSopenharmony_ci		ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_ALLOC,
58d722e3fbSopenharmony_ci				&req, sizeof(req));
59d722e3fbSopenharmony_ci		if (ret) {
60d722e3fbSopenharmony_ci			ERROR_MSG("alloc failed: %s", strerror(errno));
61d722e3fbSopenharmony_ci			return ret;
62d722e3fbSopenharmony_ci		}
63d722e3fbSopenharmony_ci
64d722e3fbSopenharmony_ci		kgsl_bo->offset = req.offset;
65d722e3fbSopenharmony_ci	}
66d722e3fbSopenharmony_ci
67d722e3fbSopenharmony_ci	return 0;
68d722e3fbSopenharmony_ci}
69d722e3fbSopenharmony_ci
70d722e3fbSopenharmony_cistatic int kgsl_bo_offset(struct fd_bo *bo, uint64_t *offset)
71d722e3fbSopenharmony_ci{
72d722e3fbSopenharmony_ci	struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
73d722e3fbSopenharmony_ci	int ret = bo_alloc(kgsl_bo);
74d722e3fbSopenharmony_ci	if (ret)
75d722e3fbSopenharmony_ci		return ret;
76d722e3fbSopenharmony_ci	*offset = kgsl_bo->offset;
77d722e3fbSopenharmony_ci	return 0;
78d722e3fbSopenharmony_ci}
79d722e3fbSopenharmony_ci
80d722e3fbSopenharmony_cistatic int kgsl_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
81d722e3fbSopenharmony_ci{
82d722e3fbSopenharmony_ci	uint32_t timestamp = kgsl_bo_get_timestamp(to_kgsl_bo(bo));
83d722e3fbSopenharmony_ci
84d722e3fbSopenharmony_ci	if (op & DRM_FREEDRENO_PREP_NOSYNC) {
85d722e3fbSopenharmony_ci		uint32_t current;
86d722e3fbSopenharmony_ci		int ret;
87d722e3fbSopenharmony_ci
88d722e3fbSopenharmony_ci		/* special case for is_idle().. we can't really handle that
89d722e3fbSopenharmony_ci		 * properly in kgsl (perhaps we need a way to just disable
90d722e3fbSopenharmony_ci		 * the bo-cache for kgsl?)
91d722e3fbSopenharmony_ci		 */
92d722e3fbSopenharmony_ci		if (!pipe)
93d722e3fbSopenharmony_ci			return -EBUSY;
94d722e3fbSopenharmony_ci
95d722e3fbSopenharmony_ci		ret = kgsl_pipe_timestamp(to_kgsl_pipe(pipe), &current);
96d722e3fbSopenharmony_ci		if (ret)
97d722e3fbSopenharmony_ci			return ret;
98d722e3fbSopenharmony_ci
99d722e3fbSopenharmony_ci		if (timestamp > current)
100d722e3fbSopenharmony_ci			return -EBUSY;
101d722e3fbSopenharmony_ci
102d722e3fbSopenharmony_ci		return 0;
103d722e3fbSopenharmony_ci	}
104d722e3fbSopenharmony_ci
105d722e3fbSopenharmony_ci	if (timestamp)
106d722e3fbSopenharmony_ci		fd_pipe_wait(pipe, timestamp);
107d722e3fbSopenharmony_ci
108d722e3fbSopenharmony_ci	return 0;
109d722e3fbSopenharmony_ci}
110d722e3fbSopenharmony_ci
111d722e3fbSopenharmony_cistatic void kgsl_bo_cpu_fini(struct fd_bo *bo)
112d722e3fbSopenharmony_ci{
113d722e3fbSopenharmony_ci}
114d722e3fbSopenharmony_ci
115d722e3fbSopenharmony_cistatic int kgsl_bo_madvise(struct fd_bo *bo, int willneed)
116d722e3fbSopenharmony_ci{
117d722e3fbSopenharmony_ci	return willneed; /* not supported by kgsl */
118d722e3fbSopenharmony_ci}
119d722e3fbSopenharmony_ci
120d722e3fbSopenharmony_cistatic void kgsl_bo_destroy(struct fd_bo *bo)
121d722e3fbSopenharmony_ci{
122d722e3fbSopenharmony_ci	struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
123d722e3fbSopenharmony_ci	free(kgsl_bo);
124d722e3fbSopenharmony_ci
125d722e3fbSopenharmony_ci}
126d722e3fbSopenharmony_ci
127d722e3fbSopenharmony_cistatic const struct fd_bo_funcs funcs = {
128d722e3fbSopenharmony_ci		.offset = kgsl_bo_offset,
129d722e3fbSopenharmony_ci		.cpu_prep = kgsl_bo_cpu_prep,
130d722e3fbSopenharmony_ci		.cpu_fini = kgsl_bo_cpu_fini,
131d722e3fbSopenharmony_ci		.madvise = kgsl_bo_madvise,
132d722e3fbSopenharmony_ci		.destroy = kgsl_bo_destroy,
133d722e3fbSopenharmony_ci};
134d722e3fbSopenharmony_ci
135d722e3fbSopenharmony_ci/* allocate a buffer handle: */
136d722e3fbSopenharmony_cidrm_private int kgsl_bo_new_handle(struct fd_device *dev,
137d722e3fbSopenharmony_ci		uint32_t size, uint32_t flags, uint32_t *handle)
138d722e3fbSopenharmony_ci{
139d722e3fbSopenharmony_ci	struct drm_kgsl_gem_create req = {
140d722e3fbSopenharmony_ci			.size = size,
141d722e3fbSopenharmony_ci	};
142d722e3fbSopenharmony_ci	int ret;
143d722e3fbSopenharmony_ci
144d722e3fbSopenharmony_ci	ret = drmCommandWriteRead(dev->fd, DRM_KGSL_GEM_CREATE,
145d722e3fbSopenharmony_ci			&req, sizeof(req));
146d722e3fbSopenharmony_ci	if (ret)
147d722e3fbSopenharmony_ci		return ret;
148d722e3fbSopenharmony_ci
149d722e3fbSopenharmony_ci	// TODO make flags match msm driver, since kgsl is legacy..
150d722e3fbSopenharmony_ci	// translate flags in kgsl..
151d722e3fbSopenharmony_ci
152d722e3fbSopenharmony_ci	set_memtype(dev, req.handle, flags);
153d722e3fbSopenharmony_ci
154d722e3fbSopenharmony_ci	*handle = req.handle;
155d722e3fbSopenharmony_ci
156d722e3fbSopenharmony_ci	return 0;
157d722e3fbSopenharmony_ci}
158d722e3fbSopenharmony_ci
159d722e3fbSopenharmony_ci/* allocate a new buffer object */
160d722e3fbSopenharmony_cidrm_private struct fd_bo * kgsl_bo_from_handle(struct fd_device *dev,
161d722e3fbSopenharmony_ci		uint32_t size, uint32_t handle)
162d722e3fbSopenharmony_ci{
163d722e3fbSopenharmony_ci	struct kgsl_bo *kgsl_bo;
164d722e3fbSopenharmony_ci	struct fd_bo *bo;
165d722e3fbSopenharmony_ci	unsigned i;
166d722e3fbSopenharmony_ci
167d722e3fbSopenharmony_ci	kgsl_bo = calloc(1, sizeof(*kgsl_bo));
168d722e3fbSopenharmony_ci	if (!kgsl_bo)
169d722e3fbSopenharmony_ci		return NULL;
170d722e3fbSopenharmony_ci
171d722e3fbSopenharmony_ci	bo = &kgsl_bo->base;
172d722e3fbSopenharmony_ci	bo->funcs = &funcs;
173d722e3fbSopenharmony_ci
174d722e3fbSopenharmony_ci	for (i = 0; i < ARRAY_SIZE(kgsl_bo->list); i++)
175d722e3fbSopenharmony_ci		list_inithead(&kgsl_bo->list[i]);
176d722e3fbSopenharmony_ci
177d722e3fbSopenharmony_ci	return bo;
178d722e3fbSopenharmony_ci}
179d722e3fbSopenharmony_ci
180d722e3fbSopenharmony_cidrm_public struct fd_bo *
181d722e3fbSopenharmony_cifd_bo_from_fbdev(struct fd_pipe *pipe, int fbfd, uint32_t size)
182d722e3fbSopenharmony_ci{
183d722e3fbSopenharmony_ci	struct fd_bo *bo;
184d722e3fbSopenharmony_ci
185d722e3fbSopenharmony_ci	if (!is_kgsl_pipe(pipe))
186d722e3fbSopenharmony_ci		return NULL;
187d722e3fbSopenharmony_ci
188d722e3fbSopenharmony_ci	bo = fd_bo_new(pipe->dev, 1, 0);
189d722e3fbSopenharmony_ci
190d722e3fbSopenharmony_ci	/* this is fugly, but works around a bug in the kernel..
191d722e3fbSopenharmony_ci	 * priv->memdesc.size never gets set, so getbufinfo ioctl
192d722e3fbSopenharmony_ci	 * thinks the buffer hasn't be allocate and fails
193d722e3fbSopenharmony_ci	 */
194d722e3fbSopenharmony_ci	if (bo) {
195d722e3fbSopenharmony_ci		void *fbmem = drm_mmap(NULL, size, PROT_READ | PROT_WRITE,
196d722e3fbSopenharmony_ci				MAP_SHARED, fbfd, 0);
197d722e3fbSopenharmony_ci		struct kgsl_map_user_mem req = {
198d722e3fbSopenharmony_ci				.memtype = KGSL_USER_MEM_TYPE_ADDR,
199d722e3fbSopenharmony_ci				.len     = size,
200d722e3fbSopenharmony_ci				.offset  = 0,
201d722e3fbSopenharmony_ci				.hostptr = (unsigned long)fbmem,
202d722e3fbSopenharmony_ci		};
203d722e3fbSopenharmony_ci		struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
204d722e3fbSopenharmony_ci		int ret;
205d722e3fbSopenharmony_ci
206d722e3fbSopenharmony_ci		ret = ioctl(to_kgsl_pipe(pipe)->fd, IOCTL_KGSL_MAP_USER_MEM, &req);
207d722e3fbSopenharmony_ci		if (ret) {
208d722e3fbSopenharmony_ci			ERROR_MSG("mapping user mem failed: %s",
209d722e3fbSopenharmony_ci					strerror(errno));
210d722e3fbSopenharmony_ci			goto fail;
211d722e3fbSopenharmony_ci		}
212d722e3fbSopenharmony_ci		kgsl_bo->gpuaddr = req.gpuaddr;
213d722e3fbSopenharmony_ci		bo->map = fbmem;
214d722e3fbSopenharmony_ci	}
215d722e3fbSopenharmony_ci
216d722e3fbSopenharmony_ci	return bo;
217d722e3fbSopenharmony_cifail:
218d722e3fbSopenharmony_ci	if (bo)
219d722e3fbSopenharmony_ci		fd_bo_del(bo);
220d722e3fbSopenharmony_ci	return NULL;
221d722e3fbSopenharmony_ci}
222d722e3fbSopenharmony_ci
223d722e3fbSopenharmony_cidrm_private uint32_t kgsl_bo_gpuaddr(struct kgsl_bo *kgsl_bo, uint32_t offset)
224d722e3fbSopenharmony_ci{
225d722e3fbSopenharmony_ci	struct fd_bo *bo = &kgsl_bo->base;
226d722e3fbSopenharmony_ci	if (!kgsl_bo->gpuaddr) {
227d722e3fbSopenharmony_ci		struct drm_kgsl_gem_bufinfo req = {
228d722e3fbSopenharmony_ci				.handle = bo->handle,
229d722e3fbSopenharmony_ci		};
230d722e3fbSopenharmony_ci		int ret;
231d722e3fbSopenharmony_ci
232d722e3fbSopenharmony_ci		ret = bo_alloc(kgsl_bo);
233d722e3fbSopenharmony_ci		if (ret) {
234d722e3fbSopenharmony_ci			return ret;
235d722e3fbSopenharmony_ci		}
236d722e3fbSopenharmony_ci
237d722e3fbSopenharmony_ci		ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_GET_BUFINFO,
238d722e3fbSopenharmony_ci				&req, sizeof(req));
239d722e3fbSopenharmony_ci		if (ret) {
240d722e3fbSopenharmony_ci			ERROR_MSG("get bufinfo failed: %s", strerror(errno));
241d722e3fbSopenharmony_ci			return 0;
242d722e3fbSopenharmony_ci		}
243d722e3fbSopenharmony_ci
244d722e3fbSopenharmony_ci		kgsl_bo->gpuaddr = req.gpuaddr[0];
245d722e3fbSopenharmony_ci	}
246d722e3fbSopenharmony_ci	return kgsl_bo->gpuaddr + offset;
247d722e3fbSopenharmony_ci}
248d722e3fbSopenharmony_ci
249d722e3fbSopenharmony_ci/*
250d722e3fbSopenharmony_ci * Super-cheezy way to synchronization between mesa and ddx..  the
251d722e3fbSopenharmony_ci * SET_ACTIVE ioctl gives us a way to stash a 32b # w/ a GEM bo, and
252d722e3fbSopenharmony_ci * GET_BUFINFO gives us a way to retrieve it.  We use this to stash
253d722e3fbSopenharmony_ci * the timestamp of the last ISSUEIBCMDS on the buffer.
254d722e3fbSopenharmony_ci *
255d722e3fbSopenharmony_ci * To avoid an obscene amount of syscalls, we:
256d722e3fbSopenharmony_ci *  1) Only set the timestamp for buffers w/ an flink name, ie.
257d722e3fbSopenharmony_ci *     only buffers shared across processes.  This is enough to
258d722e3fbSopenharmony_ci *     catch the DRI2 buffers.
259d722e3fbSopenharmony_ci *  2) Only set the timestamp for buffers submitted to the 3d ring
260d722e3fbSopenharmony_ci *     and only check the timestamps on buffers submitted to the
261d722e3fbSopenharmony_ci *     2d ring.  This should be enough to handle synchronizing of
262d722e3fbSopenharmony_ci *     presentation blit.  We could do synchronization in the other
263d722e3fbSopenharmony_ci *     direction too, but that would be problematic if we are using
264d722e3fbSopenharmony_ci *     the 3d ring from DDX, since client side wouldn't know this.
265d722e3fbSopenharmony_ci *
266d722e3fbSopenharmony_ci * The waiting on timestamp happens before flush, and setting of
267d722e3fbSopenharmony_ci * timestamp happens after flush.  It is transparent to the user
268d722e3fbSopenharmony_ci * of libdrm_freedreno as all the tracking of buffers happens via
269d722e3fbSopenharmony_ci * _emit_reloc()..
270d722e3fbSopenharmony_ci */
271d722e3fbSopenharmony_ci
272d722e3fbSopenharmony_cidrm_private void kgsl_bo_set_timestamp(struct kgsl_bo *kgsl_bo,
273d722e3fbSopenharmony_ci		uint32_t timestamp)
274d722e3fbSopenharmony_ci{
275d722e3fbSopenharmony_ci	struct fd_bo *bo = &kgsl_bo->base;
276d722e3fbSopenharmony_ci	if (bo->name) {
277d722e3fbSopenharmony_ci		struct drm_kgsl_gem_active req = {
278d722e3fbSopenharmony_ci				.handle = bo->handle,
279d722e3fbSopenharmony_ci				.active = timestamp,
280d722e3fbSopenharmony_ci		};
281d722e3fbSopenharmony_ci		int ret;
282d722e3fbSopenharmony_ci
283d722e3fbSopenharmony_ci		ret = drmCommandWrite(bo->dev->fd, DRM_KGSL_GEM_SET_ACTIVE,
284d722e3fbSopenharmony_ci				&req, sizeof(req));
285d722e3fbSopenharmony_ci		if (ret) {
286d722e3fbSopenharmony_ci			ERROR_MSG("set active failed: %s", strerror(errno));
287d722e3fbSopenharmony_ci		}
288d722e3fbSopenharmony_ci	}
289d722e3fbSopenharmony_ci}
290d722e3fbSopenharmony_ci
291d722e3fbSopenharmony_cidrm_private uint32_t kgsl_bo_get_timestamp(struct kgsl_bo *kgsl_bo)
292d722e3fbSopenharmony_ci{
293d722e3fbSopenharmony_ci	struct fd_bo *bo = &kgsl_bo->base;
294d722e3fbSopenharmony_ci	uint32_t timestamp = 0;
295d722e3fbSopenharmony_ci	if (bo->name) {
296d722e3fbSopenharmony_ci		struct drm_kgsl_gem_bufinfo req = {
297d722e3fbSopenharmony_ci				.handle = bo->handle,
298d722e3fbSopenharmony_ci		};
299d722e3fbSopenharmony_ci		int ret;
300d722e3fbSopenharmony_ci
301d722e3fbSopenharmony_ci		ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_GET_BUFINFO,
302d722e3fbSopenharmony_ci				&req, sizeof(req));
303d722e3fbSopenharmony_ci		if (ret) {
304d722e3fbSopenharmony_ci			ERROR_MSG("get bufinfo failed: %s", strerror(errno));
305d722e3fbSopenharmony_ci			return 0;
306d722e3fbSopenharmony_ci		}
307d722e3fbSopenharmony_ci
308d722e3fbSopenharmony_ci		timestamp = req.active;
309d722e3fbSopenharmony_ci	}
310d722e3fbSopenharmony_ci	return timestamp;
311d722e3fbSopenharmony_ci}
312