1 /* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ 2 3 /* 4 * Copyright (C) 2013 Rob Clark <robclark (at) 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 (at) freedesktop.org> 27 */ 28 29 #ifdef HAVE_CONFIG_H 30 # include <config.h> 31 #endif 32 33 #include "kgsl_priv.h" 34 35 #include <linux/fb.h> 36 37 static int set_memtype(struct fd_device *dev, uint32_t handle, uint32_t flags) 38 { 39 struct drm_kgsl_gem_memtype req = { 40 .handle = handle, 41 .type = flags & DRM_FREEDRENO_GEM_TYPE_MEM_MASK, 42 }; 43 44 return drmCommandWrite(dev->fd, DRM_KGSL_GEM_SETMEMTYPE, 45 &req, sizeof(req)); 46 } 47 48 static int bo_alloc(struct kgsl_bo *kgsl_bo) 49 { 50 struct fd_bo *bo = &kgsl_bo->base; 51 if (!kgsl_bo->offset) { 52 struct drm_kgsl_gem_alloc req = { 53 .handle = bo->handle, 54 }; 55 int ret; 56 57 /* if the buffer is already backed by pages then this 58 * doesn't actually do anything (other than giving us 59 * the offset) 60 */ 61 ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_ALLOC, 62 &req, sizeof(req)); 63 if (ret) { 64 ERROR_MSG("alloc failed: %s", strerror(errno)); 65 return ret; 66 } 67 68 kgsl_bo->offset = req.offset; 69 } 70 71 return 0; 72 } 73 74 static int kgsl_bo_offset(struct fd_bo *bo, uint64_t *offset) 75 { 76 struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo); 77 int ret = bo_alloc(kgsl_bo); 78 if (ret) 79 return ret; 80 *offset = kgsl_bo->offset; 81 return 0; 82 } 83 84 static int kgsl_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op) 85 { 86 uint32_t timestamp = kgsl_bo_get_timestamp(to_kgsl_bo(bo)); 87 88 if (op & DRM_FREEDRENO_PREP_NOSYNC) { 89 uint32_t current; 90 int ret; 91 92 /* special case for is_idle().. we can't really handle that 93 * properly in kgsl (perhaps we need a way to just disable 94 * the bo-cache for kgsl?) 95 */ 96 if (!pipe) 97 return -EBUSY; 98 99 ret = kgsl_pipe_timestamp(to_kgsl_pipe(pipe), ¤t); 100 if (ret) 101 return ret; 102 103 if (timestamp > current) 104 return -EBUSY; 105 106 return 0; 107 } 108 109 if (timestamp) 110 fd_pipe_wait(pipe, timestamp); 111 112 return 0; 113 } 114 115 static void kgsl_bo_cpu_fini(struct fd_bo *bo) 116 { 117 } 118 119 static void kgsl_bo_destroy(struct fd_bo *bo) 120 { 121 struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo); 122 free(kgsl_bo); 123 124 } 125 126 static const struct fd_bo_funcs funcs = { 127 .offset = kgsl_bo_offset, 128 .cpu_prep = kgsl_bo_cpu_prep, 129 .cpu_fini = kgsl_bo_cpu_fini, 130 .destroy = kgsl_bo_destroy, 131 }; 132 133 /* allocate a buffer handle: */ 134 drm_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 */ 158 drm_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 178 struct fd_bo * 179 fd_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; 215 fail: 216 if (bo) 217 fd_bo_del(bo); 218 return NULL; 219 } 220 221 drm_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 270 drm_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 289 drm_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