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
37static int
38vspDRMCmdBuf(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 */
47VAStatus 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;
81err3:
82	psb_buffer_destroy(&cmdbuf->buf);
83err2:
84	free(cmdbuf->buffer_refs);
85	cmdbuf->buffer_refs = NULL;
86	cmdbuf->buffer_refs_allocated = 0;
87err1:
88	return vaStatus;
89}
90
91/*
92 * Destroy buffer
93 */
94void 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 */
113int 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 */
147int 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 */
164int 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 */
204void 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 */
252int 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 */
289static int
290vspDRMCmdBuf(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	}
361out:
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 */
391int vsp_context_submit_cmdbuf(object_context_p __maybe_unused obj_context)
392{
393	return 0;
394}
395
396
397/*
398 * Flushes all cmdbufs
399 */
400int 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