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@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  */
vsp_cmdbuf_create(object_context_p obj_context,psb_driver_data_p driver_data,vsp_cmdbuf_p cmdbuf)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  */
vsp_cmdbuf_destroy(vsp_cmdbuf_p cmdbuf)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  */
vsp_cmdbuf_reset(vsp_cmdbuf_p cmdbuf)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  */
vsp_cmdbuf_unmap(vsp_cmdbuf_p cmdbuf)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  */
vsp_cmdbuf_buffer_ref(vsp_cmdbuf_p cmdbuf,psb_buffer_p buf)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  */
vsp_cmdbuf_add_relocation(vsp_cmdbuf_p cmdbuf,uint32_t * addr_in_dst_buffer,psb_buffer_p ref_buffer,uint32_t buf_offset,uint32_t mask,uint32_t background,uint32_t align_shift,uint32_t dst_buffer,uint32_t * start_of_dst_buffer)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  */
vsp_context_get_next_cmdbuf(object_context_p obj_context)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
vspDRMCmdBuf(int fd,int ioctl_offset,psb_buffer_p * buffer_list,int buffer_count,unsigned cmdBufHandle,unsigned cmdBufOffset,unsigned cmdBufSize,unsigned relocBufHandle,unsigned relocBufOffset,unsigned numRelocs,int __maybe_unused damage,unsigned engine,unsigned fence_flags,struct psb_ttm_fence_rep * fence_rep)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  */
vsp_context_submit_cmdbuf(object_context_p __maybe_unused obj_context)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  */
vsp_context_flush_cmdbuf(object_context_p obj_context)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