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 31 32static int kgsl_pipe_get_param(struct fd_pipe *pipe, 33 enum fd_param_id param, uint64_t *value) 34{ 35 struct kgsl_pipe *kgsl_pipe = to_kgsl_pipe(pipe); 36 switch (param) { 37 case FD_DEVICE_ID: 38 *value = kgsl_pipe->devinfo.device_id; 39 return 0; 40 case FD_GPU_ID: 41 *value = kgsl_pipe->devinfo.gpu_id; 42 return 0; 43 case FD_GMEM_SIZE: 44 *value = kgsl_pipe->devinfo.gmem_sizebytes; 45 return 0; 46 case FD_CHIP_ID: 47 *value = kgsl_pipe->devinfo.chip_id; 48 return 0; 49 case FD_MAX_FREQ: 50 case FD_TIMESTAMP: 51 case FD_NR_RINGS: 52 /* unsupported on kgsl */ 53 return -1; 54 default: 55 ERROR_MSG("invalid param id: %d", param); 56 return -1; 57 } 58} 59 60static int kgsl_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp, 61 uint64_t timeout) 62{ 63 struct kgsl_pipe *kgsl_pipe = to_kgsl_pipe(pipe); 64 struct kgsl_device_waittimestamp req = { 65 .timestamp = timestamp, 66 .timeout = 5000, 67 }; 68 int ret; 69 70 do { 71 ret = ioctl(kgsl_pipe->fd, IOCTL_KGSL_DEVICE_WAITTIMESTAMP, &req); 72 } while ((ret == -1) && ((errno == EINTR) || (errno == EAGAIN))); 73 if (ret) 74 ERROR_MSG("waittimestamp failed! %d (%s)", ret, strerror(errno)); 75 else 76 kgsl_pipe_process_pending(kgsl_pipe, timestamp); 77 return ret; 78} 79 80drm_private int kgsl_pipe_timestamp(struct kgsl_pipe *kgsl_pipe, 81 uint32_t *timestamp) 82{ 83 struct kgsl_cmdstream_readtimestamp req = { 84 .type = KGSL_TIMESTAMP_RETIRED 85 }; 86 int ret = ioctl(kgsl_pipe->fd, IOCTL_KGSL_CMDSTREAM_READTIMESTAMP, &req); 87 if (ret) { 88 ERROR_MSG("readtimestamp failed! %d (%s)", 89 ret, strerror(errno)); 90 return ret; 91 } 92 *timestamp = req.timestamp; 93 return 0; 94} 95 96static void kgsl_pipe_destroy(struct fd_pipe *pipe) 97{ 98 struct kgsl_pipe *kgsl_pipe = to_kgsl_pipe(pipe); 99 struct kgsl_drawctxt_destroy req = { 100 .drawctxt_id = kgsl_pipe->drawctxt_id, 101 }; 102 103 if (kgsl_pipe->drawctxt_id) 104 ioctl(kgsl_pipe->fd, IOCTL_KGSL_DRAWCTXT_DESTROY, &req); 105 106 if (kgsl_pipe->fd >= 0) 107 close(kgsl_pipe->fd); 108 109 free(kgsl_pipe); 110} 111 112static const struct fd_pipe_funcs funcs = { 113 .ringbuffer_new = kgsl_ringbuffer_new, 114 .get_param = kgsl_pipe_get_param, 115 .wait = kgsl_pipe_wait, 116 .destroy = kgsl_pipe_destroy, 117}; 118 119drm_private int is_kgsl_pipe(struct fd_pipe *pipe) 120{ 121 return pipe->funcs == &funcs; 122} 123 124/* add buffer to submit list when it is referenced in cmdstream: */ 125drm_private void kgsl_pipe_add_submit(struct kgsl_pipe *kgsl_pipe, 126 struct kgsl_bo *kgsl_bo) 127{ 128 struct fd_pipe *pipe = &kgsl_pipe->base; 129 struct fd_bo *bo = &kgsl_bo->base; 130 struct list_head *list = &kgsl_bo->list[pipe->id]; 131 if (LIST_IS_EMPTY(list)) { 132 fd_bo_ref(bo); 133 } else { 134 list_del(list); 135 } 136 list_addtail(list, &kgsl_pipe->submit_list); 137} 138 139/* prepare buffers on submit list before flush: */ 140drm_private void kgsl_pipe_pre_submit(struct kgsl_pipe *kgsl_pipe) 141{ 142 struct fd_pipe *pipe = &kgsl_pipe->base; 143 struct kgsl_bo *kgsl_bo = NULL; 144 145 if (!kgsl_pipe->p3d) 146 kgsl_pipe->p3d = fd_pipe_new(pipe->dev, FD_PIPE_3D); 147 148 LIST_FOR_EACH_ENTRY(kgsl_bo, &kgsl_pipe->submit_list, list[pipe->id]) { 149 uint32_t timestamp = kgsl_bo_get_timestamp(kgsl_bo); 150 if (timestamp) 151 fd_pipe_wait(kgsl_pipe->p3d, timestamp); 152 } 153} 154 155/* process buffers on submit list after flush: */ 156drm_private void kgsl_pipe_post_submit(struct kgsl_pipe *kgsl_pipe, 157 uint32_t timestamp) 158{ 159 struct fd_pipe *pipe = &kgsl_pipe->base; 160 struct kgsl_bo *kgsl_bo = NULL, *tmp; 161 162 LIST_FOR_EACH_ENTRY_SAFE(kgsl_bo, tmp, &kgsl_pipe->submit_list, list[pipe->id]) { 163 struct list_head *list = &kgsl_bo->list[pipe->id]; 164 list_del(list); 165 kgsl_bo->timestamp[pipe->id] = timestamp; 166 list_addtail(list, &kgsl_pipe->pending_list); 167 168 kgsl_bo_set_timestamp(kgsl_bo, timestamp); 169 } 170 171 if (!kgsl_pipe_timestamp(kgsl_pipe, ×tamp)) 172 kgsl_pipe_process_pending(kgsl_pipe, timestamp); 173} 174 175drm_private void kgsl_pipe_process_pending(struct kgsl_pipe *kgsl_pipe, 176 uint32_t timestamp) 177{ 178 struct fd_pipe *pipe = &kgsl_pipe->base; 179 struct kgsl_bo *kgsl_bo = NULL, *tmp; 180 181 LIST_FOR_EACH_ENTRY_SAFE(kgsl_bo, tmp, &kgsl_pipe->pending_list, list[pipe->id]) { 182 struct list_head *list = &kgsl_bo->list[pipe->id]; 183 if (kgsl_bo->timestamp[pipe->id] > timestamp) 184 return; 185 list_delinit(list); 186 kgsl_bo->timestamp[pipe->id] = 0; 187 fd_bo_del(&kgsl_bo->base); 188 } 189} 190 191static int getprop(int fd, enum kgsl_property_type type, 192 void *value, int sizebytes) 193{ 194 struct kgsl_device_getproperty req = { 195 .type = type, 196 .value = value, 197 .sizebytes = sizebytes, 198 }; 199 return ioctl(fd, IOCTL_KGSL_DEVICE_GETPROPERTY, &req); 200} 201 202#define GETPROP(fd, prop, x) do { \ 203 if (getprop((fd), KGSL_PROP_##prop, &(x), sizeof(x))) { \ 204 ERROR_MSG("failed to get property: " #prop); \ 205 goto fail; \ 206 } } while (0) 207 208 209drm_private struct fd_pipe * kgsl_pipe_new(struct fd_device *dev, 210 enum fd_pipe_id id, uint32_t prio) 211{ 212 static const char *paths[] = { 213 [FD_PIPE_3D] = "/dev/kgsl-3d0", 214 [FD_PIPE_2D] = "/dev/kgsl-2d0", 215 }; 216 struct kgsl_drawctxt_create req = { 217 .flags = 0x2000, /* ??? */ 218 }; 219 struct kgsl_pipe *kgsl_pipe = NULL; 220 struct fd_pipe *pipe = NULL; 221 int ret, fd; 222 223 fd = open(paths[id], O_RDWR); 224 if (fd < 0) { 225 ERROR_MSG("could not open %s device: %d (%s)", 226 paths[id], fd, strerror(errno)); 227 goto fail; 228 } 229 230 ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE, &req); 231 if (ret) { 232 ERROR_MSG("failed to allocate context: %d (%s)", 233 ret, strerror(errno)); 234 goto fail; 235 } 236 237 kgsl_pipe = calloc(1, sizeof(*kgsl_pipe)); 238 if (!kgsl_pipe) { 239 ERROR_MSG("allocation failed"); 240 goto fail; 241 } 242 243 pipe = &kgsl_pipe->base; 244 pipe->funcs = &funcs; 245 246 kgsl_pipe->fd = fd; 247 kgsl_pipe->drawctxt_id = req.drawctxt_id; 248 249 list_inithead(&kgsl_pipe->submit_list); 250 list_inithead(&kgsl_pipe->pending_list); 251 252 GETPROP(fd, VERSION, kgsl_pipe->version); 253 GETPROP(fd, DEVICE_INFO, kgsl_pipe->devinfo); 254 255 if (kgsl_pipe->devinfo.gpu_id >= 500) { 256 ERROR_MSG("64b unsupported with kgsl"); 257 goto fail; 258 } 259 260 INFO_MSG("Pipe Info:"); 261 INFO_MSG(" Device: %s", paths[id]); 262 INFO_MSG(" Chip-id: %d.%d.%d.%d", 263 (kgsl_pipe->devinfo.chip_id >> 24) & 0xff, 264 (kgsl_pipe->devinfo.chip_id >> 16) & 0xff, 265 (kgsl_pipe->devinfo.chip_id >> 8) & 0xff, 266 (kgsl_pipe->devinfo.chip_id >> 0) & 0xff); 267 INFO_MSG(" Device-id: %d", kgsl_pipe->devinfo.device_id); 268 INFO_MSG(" GPU-id: %d", kgsl_pipe->devinfo.gpu_id); 269 INFO_MSG(" MMU enabled: %d", kgsl_pipe->devinfo.mmu_enabled); 270 INFO_MSG(" GMEM Base addr: 0x%08x", kgsl_pipe->devinfo.gmem_gpubaseaddr); 271 INFO_MSG(" GMEM size: 0x%08x", kgsl_pipe->devinfo.gmem_sizebytes); 272 INFO_MSG(" Driver version: %d.%d", 273 kgsl_pipe->version.drv_major, kgsl_pipe->version.drv_minor); 274 INFO_MSG(" Device version: %d.%d", 275 kgsl_pipe->version.dev_major, kgsl_pipe->version.dev_minor); 276 277 return pipe; 278fail: 279 if (pipe) 280 fd_pipe_del(pipe); 281 return NULL; 282} 283