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