1/*
2 * Copyright (C) 2012-2018 Rob Clark <robclark@freedesktop.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 *    Rob Clark <robclark@freedesktop.org>
25 */
26
27#include "msm_priv.h"
28
29static int bo_allocate(struct msm_bo *msm_bo)
30{
31	struct fd_bo *bo = &msm_bo->base;
32	if (!msm_bo->offset) {
33		struct drm_msm_gem_info req = {
34				.handle = bo->handle,
35				.info = MSM_INFO_GET_OFFSET,
36		};
37		int ret;
38
39		/* if the buffer is already backed by pages then this
40		 * doesn't actually do anything (other than giving us
41		 * the offset)
42		 */
43		ret = drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_INFO,
44				&req, sizeof(req));
45		if (ret) {
46			ERROR_MSG("alloc failed: %s", strerror(errno));
47			return ret;
48		}
49
50		msm_bo->offset = req.value;
51	}
52
53	return 0;
54}
55
56static int msm_bo_offset(struct fd_bo *bo, uint64_t *offset)
57{
58	struct msm_bo *msm_bo = to_msm_bo(bo);
59	int ret = bo_allocate(msm_bo);
60	if (ret)
61		return ret;
62	*offset = msm_bo->offset;
63	return 0;
64}
65
66static int msm_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
67{
68	struct drm_msm_gem_cpu_prep req = {
69			.handle = bo->handle,
70			.op = op,
71	};
72
73	get_abs_timeout(&req.timeout, 5000000000);
74
75	return drmCommandWrite(bo->dev->fd, DRM_MSM_GEM_CPU_PREP, &req, sizeof(req));
76}
77
78static void msm_bo_cpu_fini(struct fd_bo *bo)
79{
80	struct drm_msm_gem_cpu_fini req = {
81			.handle = bo->handle,
82	};
83
84	drmCommandWrite(bo->dev->fd, DRM_MSM_GEM_CPU_FINI, &req, sizeof(req));
85}
86
87static int msm_bo_madvise(struct fd_bo *bo, int willneed)
88{
89	struct drm_msm_gem_madvise req = {
90			.handle = bo->handle,
91			.madv = willneed ? MSM_MADV_WILLNEED : MSM_MADV_DONTNEED,
92	};
93	int ret;
94
95	/* older kernels do not support this: */
96	if (bo->dev->version < FD_VERSION_MADVISE)
97		return willneed;
98
99	ret = drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_MADVISE, &req, sizeof(req));
100	if (ret)
101		return ret;
102
103	return req.retained;
104}
105
106static uint64_t msm_bo_iova(struct fd_bo *bo)
107{
108	struct drm_msm_gem_info req = {
109			.handle = bo->handle,
110			.info = MSM_INFO_GET_IOVA,
111	};
112	int ret;
113
114	ret = drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_INFO, &req, sizeof(req));
115	debug_assert(ret == 0);
116
117	return req.value;
118}
119
120static void msm_bo_set_name(struct fd_bo *bo, const char *fmt, va_list ap)
121{
122	struct drm_msm_gem_info req = {
123			.handle = bo->handle,
124			.info = MSM_INFO_SET_NAME,
125	};
126	char buf[32];
127	int sz;
128
129	if (bo->dev->version < FD_VERSION_SOFTPIN)
130		return;
131
132	sz = vsnprintf(buf, sizeof(buf), fmt, ap);
133
134	req.value = VOID2U64(buf);
135	req.len = MIN2(sz, sizeof(buf));
136
137	drmCommandWrite(bo->dev->fd, DRM_MSM_GEM_INFO, &req, sizeof(req));
138}
139
140static void msm_bo_destroy(struct fd_bo *bo)
141{
142	struct msm_bo *msm_bo = to_msm_bo(bo);
143	free(msm_bo);
144}
145
146static const struct fd_bo_funcs funcs = {
147		.offset = msm_bo_offset,
148		.cpu_prep = msm_bo_cpu_prep,
149		.cpu_fini = msm_bo_cpu_fini,
150		.madvise = msm_bo_madvise,
151		.iova = msm_bo_iova,
152		.set_name = msm_bo_set_name,
153		.destroy = msm_bo_destroy,
154};
155
156/* allocate a buffer handle: */
157int msm_bo_new_handle(struct fd_device *dev,
158		uint32_t size, uint32_t flags, uint32_t *handle)
159{
160	struct drm_msm_gem_new req = {
161			.size = size,
162			.flags = MSM_BO_WC,  // TODO figure out proper flags..
163	};
164	int ret;
165
166	if (flags & DRM_FREEDRENO_GEM_SCANOUT)
167		req.flags |= MSM_BO_SCANOUT;
168
169	if (flags & DRM_FREEDRENO_GEM_GPUREADONLY)
170		req.flags |= MSM_BO_GPU_READONLY;
171
172	ret = drmCommandWriteRead(dev->fd, DRM_MSM_GEM_NEW,
173			&req, sizeof(req));
174	if (ret)
175		return ret;
176
177	*handle = req.handle;
178
179	return 0;
180}
181
182/* allocate a new buffer object */
183struct fd_bo * msm_bo_from_handle(struct fd_device *dev,
184		uint32_t size, uint32_t handle)
185{
186	struct msm_bo *msm_bo;
187	struct fd_bo *bo;
188
189	msm_bo = calloc(1, sizeof(*msm_bo));
190	if (!msm_bo)
191		return NULL;
192
193	bo = &msm_bo->base;
194	bo->funcs = &funcs;
195
196	return bo;
197}
198