1 /* 2 * Copyright (c) 2011 Intel Corporation. All Rights Reserved. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sub license, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the 13 * next paragraph) shall be included in all copies or substantial portions 14 * of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 19 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR 20 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: 25 * Binglin Chen <binglin.chen (at) intel.com> 26 * 27 */ 28 29 #include "vsp_VPP.h" 30 #include "vsp_cmdbuf.h" 31 #include "psb_drv_debug.h" 32 33 #define CMD_SIZE (0x3000) 34 #define LLDMA_SIZE (0x2000) 35 #define RELOC_SIZE (0x3000) 36 37 static int 38 vspDRMCmdBuf(int fd, int ioctl_offset, psb_buffer_p *buffer_list, int buffer_count, unsigned cmdBufHandle, 39 unsigned cmdBufOffset, unsigned cmdBufSize, 40 unsigned relocBufHandle, unsigned relocBufOffset, 41 unsigned numRelocs, int damage, 42 unsigned engine, unsigned fence_flags, struct psb_ttm_fence_rep *fence_rep); 43 44 /* 45 * Create command buffer 46 */ 47 VAStatus vsp_cmdbuf_create( 48 object_context_p obj_context, 49 psb_driver_data_p driver_data, 50 vsp_cmdbuf_p cmdbuf) 51 { 52 VAStatus vaStatus = VA_STATUS_SUCCESS; 53 unsigned int size = CMD_SIZE + RELOC_SIZE; 54 context_VPP_p ctx = (context_VPP_p) obj_context->format_data; 55 56 cmdbuf->size = 0; 57 cmdbuf->cmd_base = NULL; 58 cmdbuf->cmd_idx = NULL; 59 cmdbuf->reloc_base = NULL; 60 cmdbuf->reloc_idx = NULL; 61 cmdbuf->buffer_refs_count = 0; 62 cmdbuf->buffer_refs_allocated = 10; 63 cmdbuf->buffer_refs = (psb_buffer_p *) calloc(1, sizeof(psb_buffer_p) * cmdbuf->buffer_refs_allocated); 64 if (NULL == cmdbuf->buffer_refs) { 65 cmdbuf->buffer_refs_allocated = 0; 66 vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED; 67 goto err1; 68 } 69 70 vaStatus = psb_buffer_create(driver_data, size, psb_bt_cpu_only, &cmdbuf->buf); 71 cmdbuf->size = size; 72 73 if (VA_STATUS_SUCCESS != vaStatus) 74 goto err2; 75 76 vaStatus = psb_buffer_create(driver_data, ctx->param_sz, psb_bt_cpu_vpu, &cmdbuf->param_mem); 77 if (VA_STATUS_SUCCESS != vaStatus) 78 goto err3; 79 80 return vaStatus; 81 err3: 82 psb_buffer_destroy(&cmdbuf->buf); 83 err2: 84 free(cmdbuf->buffer_refs); 85 cmdbuf->buffer_refs = NULL; 86 cmdbuf->buffer_refs_allocated = 0; 87 err1: 88 return vaStatus; 89 } 90 91 /* 92 * Destroy buffer 93 */ 94 void vsp_cmdbuf_destroy(vsp_cmdbuf_p cmdbuf) 95 { 96 if (cmdbuf->size) { 97 psb_buffer_destroy(&cmdbuf->buf); 98 cmdbuf->size = 0; 99 } 100 if (cmdbuf->buffer_refs_allocated) { 101 free(cmdbuf->buffer_refs); 102 cmdbuf->buffer_refs = NULL; 103 cmdbuf->buffer_refs_allocated = 0; 104 } 105 psb_buffer_destroy(&cmdbuf->param_mem); 106 } 107 108 /* 109 * Reset buffer & map 110 * 111 * Returns 0 on success 112 */ 113 int vsp_cmdbuf_reset(vsp_cmdbuf_p cmdbuf) 114 { 115 int ret; 116 117 cmdbuf->cmd_base = NULL; 118 cmdbuf->cmd_idx = NULL; 119 cmdbuf->reloc_base = NULL; 120 cmdbuf->reloc_idx = NULL; 121 122 cmdbuf->buffer_refs_count = 0; 123 cmdbuf->cmd_count = 0; 124 125 ret = psb_buffer_map(&cmdbuf->buf, &cmdbuf->cmd_base); 126 if (ret) { 127 return ret; 128 } 129 130 cmdbuf->cmd_start = cmdbuf->cmd_base; 131 cmdbuf->cmd_idx = (uint32_t *) cmdbuf->cmd_base; 132 133 cmdbuf->reloc_base = cmdbuf->cmd_base + CMD_SIZE; 134 cmdbuf->reloc_idx = (struct drm_psb_reloc *) cmdbuf->reloc_base; 135 136 /* Add ourselves to the buffer list */ 137 vsp_cmdbuf_buffer_ref(cmdbuf, &cmdbuf->buf); /* cmd buf == 0 */ 138 139 return ret; 140 } 141 142 /* 143 * Unmap buffer 144 * 145 * Returns 0 on success 146 */ 147 int vsp_cmdbuf_unmap(vsp_cmdbuf_p cmdbuf) 148 { 149 cmdbuf->cmd_base = NULL; 150 cmdbuf->cmd_start = NULL; 151 cmdbuf->cmd_idx = NULL; 152 cmdbuf->reloc_base = NULL; 153 cmdbuf->reloc_idx = NULL; 154 cmdbuf->cmd_count = 0; 155 psb_buffer_unmap(&cmdbuf->buf); 156 return 0; 157 } 158 159 /* 160 * Reference an addtional buffer "buf" in the command stream 161 * Returns a reference index that can be used to refer to "buf" in 162 * relocation records, -1 on error 163 */ 164 int vsp_cmdbuf_buffer_ref(vsp_cmdbuf_p cmdbuf, psb_buffer_p buf) 165 { 166 int item_loc = 0; 167 168 /*Reserve the same TTM BO twice will cause kernel lock up*/ 169 while ((item_loc < cmdbuf->buffer_refs_count) 170 && (wsbmKBufHandle(wsbmKBuf(cmdbuf->buffer_refs[item_loc]->drm_buf)) 171 != wsbmKBufHandle(wsbmKBuf(buf->drm_buf)))) { 172 item_loc++; 173 } 174 if (item_loc == cmdbuf->buffer_refs_count) { 175 /* Add new entry */ 176 if (item_loc >= cmdbuf->buffer_refs_allocated) { 177 /* Allocate more entries */ 178 int new_size = cmdbuf->buffer_refs_allocated + 10; 179 psb_buffer_p *new_array; 180 new_array = (psb_buffer_p *) calloc(1, sizeof(psb_buffer_p) * new_size); 181 if (NULL == new_array) { 182 return -1; /* Allocation failure */ 183 } 184 memcpy(new_array, cmdbuf->buffer_refs, sizeof(psb_buffer_p) * cmdbuf->buffer_refs_allocated); 185 free(cmdbuf->buffer_refs); 186 cmdbuf->buffer_refs_allocated = new_size; 187 cmdbuf->buffer_refs = new_array; 188 } 189 cmdbuf->buffer_refs[item_loc] = buf; 190 cmdbuf->buffer_refs_count++; 191 buf->status = psb_bs_queued; 192 } 193 return item_loc; 194 } 195 196 /* Creates a relocation record for a DWORD in the mapped "cmdbuf" at address 197 * "addr_in_cmdbuf" 198 * The relocation is based on the device virtual address of "ref_buffer" 199 * "buf_offset" is be added to the device virtual address, and the sum is then 200 * right shifted with "align_shift". 201 * "mask" determines which bits of the target DWORD will be updated with the so 202 * constructed address. The remaining bits will be filled with bits from "background". 203 */ 204 void vsp_cmdbuf_add_relocation(vsp_cmdbuf_p cmdbuf, 205 uint32_t *addr_in_dst_buffer,/*addr of dst_buffer for the DWORD*/ 206 psb_buffer_p ref_buffer, 207 uint32_t buf_offset, 208 uint32_t mask, 209 uint32_t background, 210 uint32_t align_shift, 211 uint32_t dst_buffer, 212 uint32_t *start_of_dst_buffer) /*Index of the list refered by cmdbuf->buffer_refs */ 213 { 214 struct drm_psb_reloc *reloc = cmdbuf->reloc_idx; 215 uint64_t presumed_offset = wsbmBOOffsetHint(ref_buffer->drm_buf); 216 217 reloc->where = addr_in_dst_buffer - start_of_dst_buffer; /* Offset in DWORDs */ 218 219 reloc->buffer = vsp_cmdbuf_buffer_ref(cmdbuf, ref_buffer); 220 ASSERT(reloc->buffer != -1); 221 222 reloc->reloc_op = PSB_RELOC_OP_OFFSET; 223 #ifndef VA_EMULATOR 224 if (presumed_offset) { 225 uint32_t new_val = presumed_offset + buf_offset; 226 227 new_val = ((new_val >> align_shift) << (align_shift << PSB_RELOC_ALSHIFT_SHIFT)); 228 new_val = (background & ~mask) | (new_val & mask); 229 *addr_in_dst_buffer = new_val; 230 } else { 231 *addr_in_dst_buffer = PSB_RELOC_MAGIC; 232 } 233 #else 234 /* indicate subscript of relocation buffer */ 235 *addr_in_dst_buffer = reloc - (struct drm_psb_reloc *)cmdbuf->reloc_base; 236 #endif 237 reloc->mask = mask; 238 reloc->shift = align_shift << PSB_RELOC_ALSHIFT_SHIFT; 239 reloc->pre_add = buf_offset; 240 reloc->background = background; 241 reloc->dst_buffer = dst_buffer; 242 cmdbuf->reloc_idx++; 243 244 ASSERT(((unsigned char *)(cmdbuf->reloc_idx)) < RELOC_END(cmdbuf)); 245 } 246 247 /* 248 * Advances "obj_context" to the next cmdbuf 249 * 250 * Returns 0 on success 251 */ 252 int vsp_context_get_next_cmdbuf(object_context_p obj_context) 253 { 254 vsp_cmdbuf_p cmdbuf; 255 int ret; 256 257 if (obj_context->vsp_cmdbuf) { 258 return 0; 259 } 260 261 obj_context->cmdbuf_current++; 262 if (obj_context->cmdbuf_current >= VSP_MAX_CMDBUFS) { 263 obj_context->cmdbuf_current = 0; 264 } 265 266 cmdbuf = obj_context->vsp_cmdbuf_list[obj_context->cmdbuf_current]; 267 268 ret = vsp_cmdbuf_reset(cmdbuf); 269 if (!ret) { 270 /* Success */ 271 obj_context->vsp_cmdbuf = cmdbuf; 272 } 273 274 cmdbuf->param_mem_loc = vsp_cmdbuf_buffer_ref(cmdbuf, &cmdbuf->param_mem); 275 276 return ret; 277 } 278 279 /* 280 * This is the user-space do-it-all interface to the drm cmdbuf ioctl. 281 * It allows different buffers as command- and reloc buffer. A list of 282 * cliprects to apply and whether to copy the clipRect content to all 283 * scanout buffers (damage = 1). 284 */ 285 /* 286 * Don't add debug statements in this function, it gets called with the 287 * DRM lock held and output to an X terminal can cause X to deadlock 288 */ 289 static int 290 vspDRMCmdBuf(int fd, int ioctl_offset, psb_buffer_p *buffer_list, int buffer_count, unsigned cmdBufHandle, 291 unsigned cmdBufOffset, unsigned cmdBufSize, 292 unsigned relocBufHandle, unsigned relocBufOffset, 293 unsigned numRelocs, int __maybe_unused damage, 294 unsigned engine, unsigned fence_flags, struct psb_ttm_fence_rep *fence_rep) 295 { 296 drm_psb_cmdbuf_arg_t ca; 297 struct psb_validate_arg *arg_list; 298 int i; 299 int ret = 0; 300 uint64_t mask = PSB_GPU_ACCESS_MASK; 301 arg_list = (struct psb_validate_arg *) calloc(1, sizeof(struct psb_validate_arg) * buffer_count); 302 if (arg_list == NULL) { 303 drv_debug_msg(VIDEO_DEBUG_ERROR, "Allocate memory failed\n"); 304 return -ENOMEM; 305 } 306 307 for (i = 0; i < buffer_count; i++) { 308 struct psb_validate_arg *arg = &(arg_list[i]); 309 struct psb_validate_req *req = &arg->d.req; 310 311 req->next = (unsigned long) & (arg_list[i+1]); 312 313 req->buffer_handle = wsbmKBufHandle(wsbmKBuf(buffer_list[i]->drm_buf)); 314 //req->group = 0; 315 req->set_flags = (PSB_GPU_ACCESS_READ | PSB_GPU_ACCESS_WRITE) & mask; 316 req->clear_flags = (~(PSB_GPU_ACCESS_READ | PSB_GPU_ACCESS_WRITE)) & mask; 317 #if 1 318 req->presumed_gpu_offset = (uint64_t)wsbmBOOffsetHint(buffer_list[i]->drm_buf); 319 req->presumed_flags = PSB_USE_PRESUMED; 320 req->unfence_flag = buffer_list[i]->unfence_flag; 321 #else 322 req->presumed_flags = 0; 323 #endif 324 req->pad64 = (uint32_t)buffer_list[i]->pl_flags; 325 } 326 arg_list[buffer_count-1].d.req.next = 0; 327 328 ca.buffer_list = (uint64_t)((unsigned long)arg_list); 329 ca.cmdbuf_handle = cmdBufHandle; 330 ca.cmdbuf_offset = cmdBufOffset; 331 ca.cmdbuf_size = cmdBufSize; 332 ca.reloc_handle = relocBufHandle; 333 ca.reloc_offset = relocBufOffset; 334 ca.num_relocs = numRelocs; 335 ca.engine = engine; 336 ca.fence_flags = fence_flags; 337 ca.fence_arg = (uint64_t)((unsigned long)fence_rep); 338 339 do { 340 ret = drmCommandWrite(fd, ioctl_offset, &ca, sizeof(ca)); 341 } while (ret == EAGAIN); 342 343 if (ret) 344 goto out; 345 346 for (i = 0; i < buffer_count; i++) { 347 struct psb_validate_arg *arg = &(arg_list[i]); 348 struct psb_validate_rep *rep = &arg->d.rep; 349 350 if (!arg->handled) { 351 ret = -EFAULT; 352 goto out; 353 } 354 if (arg->ret != 0) { 355 ret = arg->ret; 356 goto out; 357 } 358 wsbmUpdateKBuf(wsbmKBuf(buffer_list[i]->drm_buf), 359 rep->gpu_offset, rep->placement, rep->fence_type_mask); 360 } 361 out: 362 free(arg_list); 363 for (i = 0; i < buffer_count; i++) { 364 /* 365 * Buffer no longer queued in userspace 366 */ 367 switch (buffer_list[i]->status) { 368 case psb_bs_queued: 369 buffer_list[i]->status = psb_bs_ready; 370 break; 371 372 case psb_bs_abandoned: 373 psb_buffer_destroy(buffer_list[i]); 374 free(buffer_list[i]); 375 break; 376 377 default: 378 /* Not supposed to happen */ 379 ASSERT(0); 380 } 381 } 382 383 return ret; 384 } 385 386 /* 387 * Submits the current cmdbuf 388 * 389 * Returns 0 on success 390 */ 391 int vsp_context_submit_cmdbuf(object_context_p __maybe_unused obj_context) 392 { 393 return 0; 394 } 395 396 397 /* 398 * Flushes all cmdbufs 399 */ 400 int vsp_context_flush_cmdbuf(object_context_p obj_context) 401 { 402 vsp_cmdbuf_p cmdbuf = obj_context->vsp_cmdbuf; 403 psb_driver_data_p driver_data = obj_context->driver_data; 404 unsigned int fence_flags; 405 struct psb_ttm_fence_rep fence_rep; 406 unsigned int reloc_offset; 407 unsigned int num_relocs; 408 int ret; 409 unsigned int cmdbuffer_size = (unsigned char *)cmdbuf->cmd_idx - cmdbuf->cmd_start; /* In bytes */ 410 411 ASSERT(cmdbuffer_size < CMD_SIZE); 412 ASSERT((void *) cmdbuf->cmd_idx < CMD_END(cmdbuf)); 413 /* LOCK */ 414 ret = LOCK_HARDWARE(driver_data); 415 if (ret) { 416 UNLOCK_HARDWARE(driver_data); 417 DEBUG_FAILURE_RET; 418 return ret; 419 } 420 421 /* Now calculate the total number of relocations */ 422 reloc_offset = cmdbuf->reloc_base - cmdbuf->cmd_base; 423 num_relocs = ((unsigned char *)cmdbuf->reloc_idx - cmdbuf->reloc_base) / sizeof(struct drm_psb_reloc); 424 425 vsp_cmdbuf_unmap(cmdbuf); 426 427 ASSERT(NULL == cmdbuf->reloc_base); 428 429 #ifdef DEBUG_TRACE 430 fence_flags = 0; 431 #else 432 fence_flags = DRM_PSB_FENCE_NO_USER; 433 #endif 434 435 #ifndef VSP_ENGINE_VPP 436 #define VSP_ENGINE_VPP 6 437 #endif 438 wsbmWriteLockKernelBO(); 439 ret = vspDRMCmdBuf(driver_data->drm_fd, driver_data->execIoctlOffset, 440 cmdbuf->buffer_refs, cmdbuf->buffer_refs_count, wsbmKBufHandle(wsbmKBuf(cmdbuf->buf.drm_buf)), 441 0, cmdbuffer_size,/*unsigned cmdBufSize*/ 442 wsbmKBufHandle(wsbmKBuf(cmdbuf->buf.drm_buf)), reloc_offset, num_relocs, 443 0, VSP_ENGINE_VPP, fence_flags, &fence_rep); 444 wsbmWriteUnlockKernelBO(); 445 UNLOCK_HARDWARE(driver_data); 446 447 if (ret) { 448 obj_context->vsp_cmdbuf = NULL; 449 450 DEBUG_FAILURE_RET; 451 return ret; 452 } 453 454 obj_context->vsp_cmdbuf = NULL; 455 456 return 0; 457 } 458