/*
 * Copyright (c) 2011 Intel Corporation. All Rights Reserved.
 * Copyright (c) Imagination Technologies Limited, UK
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Waldo Bastian <waldo.bastian@intel.com>
 *    Zeng Li <zeng.li@intel.com>
 *    Edward Lin <edward.lin@intel.com>
 *
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <wsbm/wsbm_manager.h>
#include "psb_buffer.h"
#include "tng_cmdbuf.h"
#include "psb_def.h"
#include "psb_drv_debug.h"
#include "tng_hostcode.h"
#include "psb_ws_driver.h"

#ifdef ANDROID
#include <linux/psb_drm.h>
#else
#include "psb_drm.h"
#endif

#include "tng_trace.h"

/*
 * Buffer layout:
 *         cmd_base <= cmd_idx < CMD_END() == reloc_base
 *         reloc_base <= reloc_idx < RELOC_END() == (reloc_size)
 */

#define RELOC_END(cmdbuf)     (cmdbuf->cmd_base + cmdbuf->size)
#define CMD_END(cmdbuf)       (cmdbuf->reloc_base)
#define RELOC_SIZE            (0x3000)
#define CMD_SIZE              (0x3000)
#define RELOC_MARGIN          (0x0800)
#define CMD_MARGIN            (0x0400)
#define MAX_CMD_COUNT         12
#define MTX_SEG_SIZE          (0x0800)

/*!
 *****************************************************************************
 *
 * @Name           Command word format
 *
 * @details    Mask and shift values for command word
 *
 ****************************************************************************/

/*
 * clear buffer
 */
void tng_cmdbuf_mem_unmap(tng_cmdbuf_p cmdbuf)
{
    psb_buffer_unmap(&cmdbuf->frame_mem);
    psb_buffer_unmap(&cmdbuf->jpeg_pic_params);
    psb_buffer_unmap(&cmdbuf->jpeg_header_mem);
    psb_buffer_unmap(&cmdbuf->jpeg_header_interface_mem);
    return ;
}

/*
 * clear buffer
 */
static void tng_cmdbuf_clear(tng_cmdbuf_p cmdbuf, int flag)
{
    switch (flag) {
        default:
        case 4:
            psb_buffer_destroy(&cmdbuf->jpeg_header_mem);
        case 3:
            psb_buffer_destroy(&cmdbuf->jpeg_pic_params);
        case 2:
        case 1:
            psb_buffer_destroy(&cmdbuf->frame_mem);
            break;
    }

    if (cmdbuf->size) {
        psb_buffer_destroy(&cmdbuf->buf);
        cmdbuf->size = 0;
    }
    if (cmdbuf->buffer_refs_allocated) {
        free(cmdbuf->buffer_refs);
        cmdbuf->buffer_refs = NULL;
        cmdbuf->buffer_refs_allocated = 0;
    }
}


/*
 * Create command buffer
 */

VAStatus tng_cmdbuf_create(
    object_context_p obj_context,
    psb_driver_data_p driver_data,
    tng_cmdbuf_p cmdbuf)
{
    context_ENC_p ctx = (context_ENC_p) obj_context->format_data;

    VAStatus vaStatus = VA_STATUS_SUCCESS;
    unsigned int size = CMD_SIZE + RELOC_SIZE;

    cmdbuf->size = 0;
    cmdbuf->cmd_base = NULL;
    cmdbuf->cmd_idx = NULL;
    cmdbuf->reloc_base = NULL;
    cmdbuf->reloc_idx = NULL;
    cmdbuf->buffer_refs_count = 0;
    cmdbuf->buffer_refs_allocated = 10;
    cmdbuf->buffer_refs = (psb_buffer_p *) calloc(1, sizeof(psb_buffer_p) * cmdbuf->buffer_refs_allocated);
    if (NULL == cmdbuf->buffer_refs) {
        cmdbuf->buffer_refs_allocated = 0;
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

    if (VA_STATUS_SUCCESS == vaStatus) {
        vaStatus = psb_buffer_create(driver_data, size, psb_bt_cpu_only, &cmdbuf->buf);
        cmdbuf->size = size;
    }

    if (VA_STATUS_SUCCESS != vaStatus) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "psb buffer create 0 \n", __FUNCTION__);
	tng_cmdbuf_clear(cmdbuf, 1);
        free(cmdbuf->buffer_refs);
        cmdbuf->buffer_refs = NULL;
        cmdbuf->buffer_refs_allocated = 0;
        return vaStatus;
    }

    cmdbuf->mem_size = tng_align_KB(TNG_HEADER_SIZE);
    drv_debug_msg(VIDEO_DEBUG_GENERAL, "mem size %d\n", __FUNCTION__, cmdbuf->mem_size);
    /* create buffer information buffer */
    //DEBUG-FIXME
    //tng__alloc_init_buffer(driver_data, COMM_CMD_FRAME_BUF_NUM * cmdbuf->mem_size, psb_bt_cpu_vpu, &cmdbuf->frame_mem);

    vaStatus = psb_buffer_create(driver_data, COMM_CMD_FRAME_BUF_NUM * cmdbuf->mem_size, psb_bt_cpu_vpu, &cmdbuf->frame_mem);
    if (VA_STATUS_SUCCESS != vaStatus) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "psb buffer create xx \n", __FUNCTION__);
        free(cmdbuf->buffer_refs);
        cmdbuf->buffer_refs = NULL;
        cmdbuf->buffer_refs_allocated = 0;
        return vaStatus;
    }
    /* all cmdbuf share one MTX_CURRENT_IN_PARAMS since every MB has a MTX_CURRENT_IN_PARAMS structure
     * and filling this structure for all MB is very time-consuming
     */

    /* create JPEG quantization buffer */
    vaStatus = psb_buffer_create(driver_data, ctx->jpeg_pic_params_size, psb_bt_cpu_vpu, &cmdbuf->jpeg_pic_params);
    if (VA_STATUS_SUCCESS != vaStatus) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "psb buffer create 1 \n", __FUNCTION__);
        tng_cmdbuf_clear(cmdbuf, 2);
        return vaStatus;
    }

    /* create JPEG MTX setup buffer */
    vaStatus = psb_buffer_create(driver_data, ctx->jpeg_header_mem_size, psb_bt_cpu_vpu, &cmdbuf->jpeg_header_mem);
    if (VA_STATUS_SUCCESS != vaStatus) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "psb buffer create 2 \n", __FUNCTION__);
        tng_cmdbuf_clear(cmdbuf, 3);
        return vaStatus;
    }

    /* create JPEG MTX setup interface buffer */
    vaStatus = psb_buffer_create(driver_data, ctx->jpeg_header_interface_mem_size, psb_bt_cpu_vpu, &cmdbuf->jpeg_header_interface_mem);
    if (VA_STATUS_SUCCESS != vaStatus) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "psb buffer create 3 \n", __FUNCTION__);
        tng_cmdbuf_clear(cmdbuf, 4);
        return vaStatus;
    }

    return vaStatus;
}

/*
 * Destroy buffer
 */
void tng_cmdbuf_destroy(tng_cmdbuf_p cmdbuf)
{
    psb_buffer_destroy(&cmdbuf->frame_mem);
    psb_buffer_destroy(&cmdbuf->jpeg_header_mem);
    psb_buffer_destroy(&cmdbuf->jpeg_pic_params);
    psb_buffer_destroy(&cmdbuf->jpeg_header_interface_mem);

    if (cmdbuf->size) {
        psb_buffer_destroy(&cmdbuf->buf);
        cmdbuf->size = 0;
    }
    if (cmdbuf->buffer_refs_allocated) {
        free(cmdbuf->buffer_refs);
        cmdbuf->buffer_refs = NULL;
        cmdbuf->buffer_refs_allocated = 0;
    }
    return ;
}

/*
 * Reset buffer & map
 *
 * Returns 0 on success
 */
int tng_cmdbuf_reset(tng_cmdbuf_p cmdbuf)
{
    int ret;
    cmdbuf->cmd_base = NULL;
    cmdbuf->cmd_idx = NULL;
    cmdbuf->reloc_base = NULL;
    cmdbuf->reloc_idx = NULL;

    cmdbuf->buffer_refs_count = 0;
    cmdbuf->frame_mem_index = 0;
    cmdbuf->cmd_count = 0;
    cmdbuf->mem_size = tng_align_KB(TNG_HEADER_SIZE);

    ret = psb_buffer_map(&cmdbuf->buf, &cmdbuf->cmd_base);
    if (ret) {
        return ret;
    }

    cmdbuf->cmd_start = cmdbuf->cmd_base;
    cmdbuf->cmd_idx = (IMG_UINT32 *) cmdbuf->cmd_base;

    cmdbuf->reloc_base = cmdbuf->cmd_base + CMD_SIZE;
    cmdbuf->reloc_idx = (struct drm_psb_reloc *) cmdbuf->reloc_base;

    /* Add ourselves to the buffer list */
    tng_cmdbuf_buffer_ref(cmdbuf, &cmdbuf->buf); /* cmd buf == 0 */
    return ret;
}

/*
 * Unmap buffer
 *
 * Returns 0 on success
 */
int tng_cmdbuf_unmap(tng_cmdbuf_p cmdbuf)
{
    cmdbuf->cmd_base = NULL;
    cmdbuf->cmd_start = NULL;
    cmdbuf->cmd_idx = NULL;
    cmdbuf->reloc_base = NULL;
    cmdbuf->reloc_idx = NULL;
    cmdbuf->cmd_count = 0;
    psb_buffer_unmap(&cmdbuf->buf);
    return 0;
}


/*
 * Reference an addtional buffer "buf" in the command stream
 * Returns a reference index that can be used to refer to "buf" in
 * relocation records, -1 on error
 */
int tng_cmdbuf_buffer_ref(tng_cmdbuf_p cmdbuf, psb_buffer_p buf)
{
    int item_loc = 0;

    /*Reserve the same TTM BO twice will cause kernel lock up*/
    while ((item_loc < cmdbuf->buffer_refs_count)
           && (wsbmKBufHandle(wsbmKBuf(cmdbuf->buffer_refs[item_loc]->drm_buf))
               != wsbmKBufHandle(wsbmKBuf(buf->drm_buf))))
        //while( (item_loc < cmdbuf->buffer_refs_count) && (cmdbuf->buffer_refs[item_loc] != buf) )
    {
        item_loc++;
    }
    if (item_loc == cmdbuf->buffer_refs_count) {
        /* Add new entry */
        if (item_loc >= cmdbuf->buffer_refs_allocated) {
            /* Allocate more entries */
            int new_size = cmdbuf->buffer_refs_allocated + 10;
            psb_buffer_p *new_array;
            new_array = (psb_buffer_p *) calloc(1, sizeof(psb_buffer_p) * new_size);
            if (NULL == new_array) {
                return -1; /* Allocation failure */
            }
            memcpy(new_array, cmdbuf->buffer_refs, sizeof(psb_buffer_p) * cmdbuf->buffer_refs_allocated);
            free(cmdbuf->buffer_refs);
            cmdbuf->buffer_refs_allocated = new_size;
            cmdbuf->buffer_refs = new_array;
        }
        cmdbuf->buffer_refs[item_loc] = buf;
        cmdbuf->buffer_refs_count++;
        buf->status = psb_bs_queued;
    }
    return item_loc;
}

/* Creates a relocation record for a DWORD in the mapped "cmdbuf" at address
 * "addr_in_cmdbuf"
 * The relocation is based on the device virtual address of "ref_buffer"
 * "buf_offset" is be added to the device virtual address, and the sum is then
 * right shifted with "align_shift".
 * "mask" determines which bits of the target DWORD will be updated with the so
 * constructed address. The remaining bits will be filled with bits from "background".
 */
void tng_cmdbuf_add_relocation(tng_cmdbuf_p cmdbuf,
                               IMG_UINT32 *addr_in_dst_buffer,/*addr of dst_buffer for the DWORD*/
                               psb_buffer_p ref_buffer,
                               IMG_UINT32 buf_offset,
                               IMG_UINT32 mask,
                               IMG_UINT32 background,
                               IMG_UINT32 align_shift,
                               IMG_UINT32 dst_buffer,
                               IMG_UINT32 *start_of_dst_buffer) /*Index of the list refered by cmdbuf->buffer_refs */
{
    struct drm_psb_reloc *reloc = cmdbuf->reloc_idx;
    uint64_t presumed_offset = wsbmBOOffsetHint(ref_buffer->drm_buf);

    reloc->where = addr_in_dst_buffer - start_of_dst_buffer; /* Offset in DWORDs */

    reloc->buffer = tng_cmdbuf_buffer_ref(cmdbuf, ref_buffer);
    ASSERT(reloc->buffer != -1);

    reloc->reloc_op = PSB_RELOC_OP_OFFSET;
#ifndef VA_EMULATOR
    if (presumed_offset) {
        IMG_UINT32 new_val =  presumed_offset + buf_offset;

        new_val = ((new_val >> align_shift) << (align_shift << PSB_RELOC_ALSHIFT_SHIFT));
        new_val = (background & ~mask) | (new_val & mask);
        *addr_in_dst_buffer = new_val;
    } else {
        *addr_in_dst_buffer = PSB_RELOC_MAGIC;
    }
#else
    /* indicate subscript of relocation buffer */
    *addr_in_dst_buffer = reloc - (struct drm_psb_reloc *)cmdbuf->reloc_base;
#endif
    reloc->mask = mask;
    reloc->shift = align_shift << PSB_RELOC_ALSHIFT_SHIFT;
    reloc->pre_add = buf_offset;
    reloc->background = background;
    reloc->dst_buffer = dst_buffer;
    cmdbuf->reloc_idx++;

    ASSERT(((void *)(cmdbuf->reloc_idx)) < RELOC_END(cmdbuf));
}

/* Prepare one command package */
void tng_cmdbuf_insert_command(
    object_context_p obj_context,
    IMG_UINT32 stream_id,
    IMG_UINT32 cmd_id,
    IMG_UINT32 cmd_data,
    psb_buffer_p data_addr,
    IMG_UINT32 offset)
{
    IMG_UINT32 cmd_word;
    context_ENC_p ctx = (context_ENC_p) obj_context->format_data;
    context_ENC_cmdbuf *ps_cmd = &(ctx->ctx_cmdbuf[stream_id]);
    context_ENC_mem *ps_mem = &(ctx->ctx_mem[stream_id]);
    tng_cmdbuf_p cmdbuf = obj_context->tng_cmdbuf;
    psb_driver_data_p driver_data = ctx->obj_context->driver_data;
    int interrupt_flags;

    //CMD composed by user space does not generate Interrupt
    interrupt_flags = 0;

    assert(stream_id <= TOPAZHP_MAX_NUM_STREAMS);
   
    /* Write command to FIFO */
    {
        cmd_word = F_ENCODE(stream_id, MTX_MSG_CORE) |
            F_ENCODE(cmd_id, MTX_MSG_CMD_ID);

        if (cmd_id & MTX_CMDID_PRIORITY) {
            /* increment the command counter */
            ps_cmd->ui32HighCmdCount++;

            /* Prepare high priority command */
            cmd_word |= F_ENCODE(1, MTX_MSG_PRIORITY) |
                F_ENCODE(((ps_cmd->ui32LowCmdCount - 1) & 0xff) |(ps_cmd->ui32HighCmdCount << 8), MTX_MSG_COUNT);
        } else {
            /* Prepare low priority command */
            cmd_word |=
            F_ENCODE(ps_cmd->ui32LowCmdCount & 0xff, MTX_MSG_COUNT);
            ++(ps_cmd->ui32LowCmdCount);
        }
        drv_debug_msg(VIDEO_DEBUG_GENERAL,
            "%s: cmd_id = 0x%08x\n",
            __FUNCTION__, cmd_word);
        *cmdbuf->cmd_idx++ = cmd_word;
    }

    /* write command word into cmdbuf */
    *cmdbuf->cmd_idx++ = cmd_data;
/* Command data address */
    if (data_addr) {
        if (cmd_id == MTX_CMDID_RC_UPDATE) {
            *cmdbuf->cmd_idx++ = (IMG_UINT32)data_addr;
            drv_debug_msg(VIDEO_DEBUG_GENERAL,
                        "%s: data_addr = 0x%08x\n",
                        __FUNCTION__, *(cmdbuf->cmd_idx));
        } else {
            if ((cmd_id >= MTX_CMDID_SETQUANT) && (cmd_id <= MTX_CMDID_SETUP)) {
                if (cmd_id == MTX_CMDID_ISSUEBUFF)
                    TNG_RELOC_CMDBUF_START(cmdbuf->cmd_idx, offset, data_addr);
                else
                    tng_cmdbuf_set_phys(cmdbuf->cmd_idx, 0, data_addr, offset, 0);
            } else {
#ifdef _TNG_RELOC_
                TNG_RELOC_CMDBUF_START(cmdbuf->cmd_idx, offset, data_addr);
#else
                tng_cmdbuf_set_phys(cmdbuf->cmd_idx, 0, data_addr, offset, 0);
#endif
            }
            drv_debug_msg(VIDEO_DEBUG_GENERAL,
                "%s: data_addr = 0x%08x\n",
                __FUNCTION__, *(cmdbuf->cmd_idx));
            cmdbuf->cmd_idx++;
        }
    } else {
        *cmdbuf->cmd_idx++ = 0;
    }

    if (cmd_id == MTX_CMDID_SW_FILL_INPUT_CTRL) {
        *cmdbuf->cmd_idx++ = ctx->ui16IntraRefresh;
        *cmdbuf->cmd_idx++ = ctx->sRCParams.ui32InitialQp;
        *cmdbuf->cmd_idx++ = ctx->sRCParams.iMinQP;
        *cmdbuf->cmd_idx++ = ctx->ctx_mem_size.mb_ctrl_in_params;
        *cmdbuf->cmd_idx++ = ctx->ui32pseudo_rand_seed;
    }

    if (cmd_id == MTX_CMDID_SW_UPDATE_AIR_SEND) {
        *cmdbuf->cmd_idx++ = ctx->sAirInfo.i16AIRSkipCnt;
        *cmdbuf->cmd_idx++ = ctx->sAirInfo.i32NumAIRSPerFrame;
        *cmdbuf->cmd_idx++ = ctx->ctx_mem_size.mb_ctrl_in_params;
        *cmdbuf->cmd_idx++ = ctx->ui32FrameCount[0];
    }

    if (cmd_id == MTX_CMDID_SW_UPDATE_AIR_CALC) {
        *cmdbuf->cmd_idx++ = ctx->ctx_mem_size.first_pass_out_best_multipass_param;
        *cmdbuf->cmd_idx++ = ctx->sAirInfo.i32SAD_Threshold;
        *cmdbuf->cmd_idx++ = ctx->ui8EnableSelStatsFlags;
    }

    /* Command data address */
    if (cmd_id == MTX_CMDID_SETVIDEO) {
        *(cmdbuf->cmd_idx)++ = wsbmKBufHandle(wsbmKBuf(ctx->bufs_writeback.drm_buf));
        drv_debug_msg(VIDEO_DEBUG_GENERAL,
            "%s: cmd_param = 0x%08x\n",
            __FUNCTION__, *(cmdbuf->cmd_idx - 1));

    if (data_addr)
        *(cmdbuf->cmd_idx)++ = wsbmKBufHandle(wsbmKBuf(data_addr->drm_buf));

        *(cmdbuf->cmd_idx)++ = wsbmKBufHandle(wsbmKBuf(ps_mem->bufs_mb_ctrl_in_params.drm_buf));
        *(cmdbuf->cmd_idx)++ =  wsbmKBufHandle(wsbmKBuf(ps_mem->bufs_first_pass_out_params.drm_buf));
        *(cmdbuf->cmd_idx)++ =  wsbmKBufHandle(wsbmKBuf(ps_mem->bufs_first_pass_out_best_multipass_param.drm_buf));
    }

    if (cmd_id == MTX_CMDID_SETUP_INTERFACE) {
        *(cmdbuf->cmd_idx)++ = wsbmKBufHandle(wsbmKBuf(ctx->bufs_writeback.drm_buf));
        drv_debug_msg(VIDEO_DEBUG_GENERAL,
            "%s: cmd_param = 0x%08x\n",
            __FUNCTION__, *(cmdbuf->cmd_idx - 1));
    }

    if (cmd_id == MTX_CMDID_SHUTDOWN) {
        *(cmdbuf->cmd_idx)++ = ctx->eCodec;

        drv_debug_msg(VIDEO_DEBUG_GENERAL,
            "%s: cmd_param = 0x%08x\n",
            __FUNCTION__, *(cmdbuf->cmd_idx - 1));
    }

    return ;
}


/*
 * Advances "obj_context" to the next cmdbuf
 *
 * Returns 0 on success
 */
int tng_context_get_next_cmdbuf(object_context_p obj_context)
{
    tng_cmdbuf_p cmdbuf;
    int ret;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: start obj_context->tng_cmdbuf = %x\n", __FUNCTION__, obj_context->tng_cmdbuf);

    if (obj_context->tng_cmdbuf) {
        return 0;
    }

    obj_context->cmdbuf_current++;
    if (obj_context->cmdbuf_current >= TNG_MAX_CMDBUFS_ENCODE) {
        obj_context->cmdbuf_current = 0;
    }

    cmdbuf = obj_context->tng_cmdbuf_list[obj_context->cmdbuf_current];
    ret = tng_cmdbuf_reset(cmdbuf);
    if (!ret) {
        /* Success */
        obj_context->tng_cmdbuf = cmdbuf;
    }

//    tng_cmdbuf_buffer_ref(cmdbuf, &cmdbuf->pic_params);
//    tng_cmdbuf_buffer_ref(cmdbuf, &cmdbuf->slice_mem);
    return ret;
}

/*
 * This is the user-space do-it-all interface to the drm cmdbuf ioctl.
 * It allows different buffers as command- and reloc buffer. A list of
 * cliprects to apply and whether to copy the clipRect content to all
 * scanout buffers (damage = 1).
 */
/*
 * Don't add debug statements in this function, it gets called with the
 * DRM lock held and output to an X terminal can cause X to deadlock
 */
static int
ptgDRMCmdBuf(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)
{
    drm_psb_cmdbuf_arg_t ca;
    struct psb_validate_arg *arg_list;
    int i, ret;
    unsigned int retry = 0;
    uint64_t mask = PSB_GPU_ACCESS_MASK;

    arg_list = (struct psb_validate_arg *) calloc(1, sizeof(struct psb_validate_arg) * buffer_count);
    if (arg_list == NULL) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Allocate memory failed\n");
        return -ENOMEM;
    }

    for (i = 0; i < buffer_count; i++) {
        struct psb_validate_arg *arg = &(arg_list[i]);
        struct psb_validate_req *req = &arg->d.req;

        //memset(arg, 0, sizeof(*arg));
        req->next = (unsigned long) & (arg_list[i+1]);

        req->buffer_handle = wsbmKBufHandle(wsbmKBuf(buffer_list[i]->drm_buf));
        //req->group = 0;
        req->set_flags = (PSB_GPU_ACCESS_READ | PSB_GPU_ACCESS_WRITE) & mask;
        req->clear_flags = (~(PSB_GPU_ACCESS_READ | PSB_GPU_ACCESS_WRITE)) & mask;
#if 1
        req->presumed_gpu_offset = (uint64_t)wsbmBOOffsetHint(buffer_list[i]->drm_buf);
        req->presumed_flags = PSB_USE_PRESUMED;
#else
        req->presumed_flags = 0;
#endif
        req->pad64 = (IMG_UINT32)buffer_list[i]->pl_flags;
#ifndef BAYTRAIL
        req->unfence_flag = buffer_list[i]->unfence_flag;
#endif
    }
    arg_list[buffer_count-1].d.req.next = 0;

    memset(&ca, 0, sizeof(ca));

    ca.buffer_list = (uint64_t)((unsigned long)arg_list);
    ca.cmdbuf_handle = cmdBufHandle;
    ca.cmdbuf_offset = cmdBufOffset;
    ca.cmdbuf_size = cmdBufSize;
    ca.reloc_handle = relocBufHandle;
    ca.reloc_offset = relocBufOffset;
    ca.num_relocs = numRelocs;
    ca.engine = engine;
    ca.fence_flags = fence_flags;
    ca.fence_arg = (uint64_t)((unsigned long)fence_rep);
    //ca.damage = damage;


    do {
        ret = drmCommandWrite(fd, ioctl_offset, &ca, sizeof(ca));
        if (ret == -EAGAIN || ret == -EBUSY) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "drmCommandWrite returns with %s, retry\n",
                          ret==-EAGAIN?"EAGAIN":"EBUSY");
            retry++;
        }
    } while (ret == -EAGAIN || ret == -EBUSY);

    if (retry > 0)
        drv_debug_msg(VIDEO_DEBUG_ERROR,"drmCommandWrite tries %d time, finally %s with ret=%d\n",
                      retry, ret==0?"okay":"failed!", ret);

    if (ret)
        goto out;

    for (i = 0; i < buffer_count; i++) {
        struct psb_validate_arg *arg = &(arg_list[i]);
        struct psb_validate_rep *rep = &arg->d.rep;

        if (!arg->handled) {
            ret = -EFAULT;
            goto out;
        }
        if (arg->ret != 0) {
            ret = arg->ret;
            goto out;
        }
        wsbmUpdateKBuf(wsbmKBuf(buffer_list[i]->drm_buf),
                       rep->gpu_offset, rep->placement, rep->fence_type_mask);
    }
out:
    free(arg_list);
    for (i = 0; i < buffer_count; i++) {
        /*
         * Buffer no longer queued in userspace
         */
        switch (buffer_list[i]->status) {
        case psb_bs_queued:
            buffer_list[i]->status = psb_bs_ready;
            break;

        case psb_bs_abandoned:
            psb_buffer_destroy(buffer_list[i]);
            free(buffer_list[i]);
            break;

        default:
            /* Not supposed to happen */
            ASSERT(0);
        }
    }

    return ret;
}

#if 0
static struct _WsbmFenceObject *
lnc_fence_wait(psb_driver_data_p driver_data,
               struct psb_ttm_fence_rep *fence_rep, int *status)

{
    struct _WsbmFenceObject *fence = NULL;
    int ret = -1;

    /* copy fence information */
    if (fence_rep->error != 0) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "drm failed to create a fence"
                           " and has idled the HW\n");
        DEBUG_FAILURE_RET;
        return NULL;
    }

    fence = wsbmFenceCreate(driver_data->fence_mgr, fence_rep->fence_class,
                            fence_rep->fence_type,
                            (void *)fence_rep->handle,
                            0);
    if (fence)
        *status = wsbmFenceFinish(fence, fence_rep->fence_type, 0);

    return fence;
}
#endif

/*
 * Submits the current cmdbuf
 *
 * Returns 0 on success
 */
int tng_context_submit_cmdbuf(object_context_p __maybe_unused obj_context)
{
    return 0;
}



/*
 * FrameSkip is only meaningful for RC enabled mode
 * Topaz raises this flag after surface N encoding is finished (vaSyncSurface gets back)
 * then for the next encode surface N+1 (ctx->src_surface) frameskip flag is cleared in vaBeginPicuture
 * and is always set in vaEndPicture:lnc_PatchRCMode
 * vaQuerySurfaceStatus is supposed only to be called after vaEndPicture/vaSyncSurface,
 * The caller should ensure the surface pertains to an encode context
 */
int tng_surface_get_frameskip(psb_driver_data_p __maybe_unused driver_data,
                              psb_surface_p surface,
                              int *frame_skip)
{
    /* bit31 indicate if frameskip is already settled, it is used to record the frame skip flag for old surfaces
     * bit31 is cleared when the surface is used as encode render target or reference/reconstrucure target
     */
    if (GET_SURFACE_INFO_skipped_flag(surface) & SURFACE_INFO_SKIP_FLAG_SETTLED) {
        *frame_skip = GET_SURFACE_INFO_skipped_flag(surface) & 1;
    } else
        *frame_skip = 0;

    return 0;
}

VAStatus tng_set_frame_skip_flag(object_context_p obj_context)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    context_ENC_p ctx = (context_ENC_p) obj_context->format_data;
    context_ENC_frame_buf *ps_buf = &(ctx->ctx_frame_buf);

    if (ctx && ps_buf->previous_src_surface) {
        SET_SURFACE_INFO_skipped_flag(ps_buf->previous_src_surface->psb_surface, 1);
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Detected a skipped frame for surface 0x%08x.\n",
            ps_buf->previous_src_surface->psb_surface);
    }

    return vaStatus;
}


/*
 * Flushes all cmdbufs
 */
int tng_context_flush_cmdbuf(object_context_p obj_context)
{
    tng_cmdbuf_p cmdbuf = obj_context->tng_cmdbuf;
    psb_driver_data_p driver_data = obj_context->driver_data;
    unsigned int fence_flags;
    struct psb_ttm_fence_rep fence_rep;
    unsigned int reloc_offset;
    unsigned int num_relocs;
    int ret;
    unsigned int cmdbuffer_size = (unsigned int) (((unsigned char *)(cmdbuf->cmd_idx)) - cmdbuf->cmd_start); /* In bytes */

    ASSERT(cmdbuffer_size < CMD_SIZE);
    ASSERT((void *) cmdbuf->cmd_idx < CMD_END(cmdbuf));
    /* LOCK */
    ret = LOCK_HARDWARE(driver_data);
    if (ret) {
        UNLOCK_HARDWARE(driver_data);
        DEBUG_FAILURE_RET;
        return ret;
    }

    /* Now calculate the total number of relocations */
    reloc_offset = cmdbuf->reloc_base - cmdbuf->cmd_base;
    num_relocs = (((unsigned char *) (cmdbuf->reloc_idx)) - cmdbuf->reloc_base) / sizeof(struct drm_psb_reloc);

    tng_cmdbuf_unmap(cmdbuf);

    ASSERT(NULL == cmdbuf->reloc_base);

#ifdef DEBUG_TRACE
    fence_flags = 0;
#else
    fence_flags = DRM_PSB_FENCE_NO_USER;
#endif


    wsbmWriteLockKernelBO();
#if 1 //_PO_DEBUG_
    ret = ptgDRMCmdBuf(driver_data->drm_fd, driver_data->execIoctlOffset, /* FIXME Still use ioctl cmd? */
                       cmdbuf->buffer_refs, cmdbuf->buffer_refs_count, wsbmKBufHandle(wsbmKBuf(cmdbuf->buf.drm_buf)),
                       0, cmdbuffer_size,/*unsigned cmdBufSize*/
                       wsbmKBufHandle(wsbmKBuf(cmdbuf->buf.drm_buf)), reloc_offset, num_relocs,
                       0, LNC_ENGINE_ENCODE, fence_flags, &fence_rep); /* FIXME use LNC_ENGINE_ENCODE */
#endif
    wsbmWriteUnlockKernelBO();

    UNLOCK_HARDWARE(driver_data);

    if (ret) {
        obj_context->tng_cmdbuf = NULL;

        DEBUG_FAILURE_RET;
        return ret;
    }

#if 0 /*DEBUG_TRACE*/
    int status = -1;
    struct _WsbmFenceObject *fence = NULL;

    fence = lnc_fence_wait(driver_data, &fence_rep, &status);
    drv_debug_msg(VIDEO_DEBUG_GENERAL, "psb_fence_wait returns: %d (fence=0x%08x)\n", status, fence);

    if (fence)
        wsbmFenceUnreference(fence);
#endif

    obj_context->tng_cmdbuf = NULL;

    return 0;
}


void tng_cmdbuf_set_phys(IMG_UINT32 *dest_buf, int dest_num,
    psb_buffer_p ref_buf, unsigned int ref_ofs, unsigned int ref_len)
{
    int i = 0;
    IMG_UINT32 addr_phys = (IMG_UINT32)wsbmBOOffsetHint(ref_buf->drm_buf) + ref_ofs;

//    drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: drm_buf 0x%08x, addr_phys 0x%08x, virt addr 0x%08x\n", __FUNCTION__, ref_buf->drm_buf, addr_phys, ref_buf->virtual_addr );

    do {
        dest_buf[i] =  addr_phys;
        ++i;
        addr_phys += ref_len;
    } while(i < dest_num);
    return ;
}


int tng_get_pipe_number(object_context_p obj_context)
{

    context_ENC_p ctx = (context_ENC_p)(obj_context->format_data);
    return ctx->ui8PipesToUse;

}