C++程序  |  1483行  |  53.49 KB

/*
 * 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:
 *    Zeng Li <zeng.li@intel.com>
 *    Shengquan Yuan  <shengquan.yuan@intel.com>
 *    Binglin Chen <binglin.chen@intel.com>
 *
 */



#include "psb_drv_video.h"

#include "lnc_hostcode.h"
#include "hwdefs/topaz_defs.h"
#include "psb_def.h"
#include "psb_cmdbuf.h"
#include <stdio.h>
#include "psb_output.h"
#include <wsbm/wsbm_manager.h>
#include "lnc_hostheader.h"
#include "psb_drv_debug.h"

#define ALIGN_TO(value, align) ((value + align - 1) & ~(align - 1))
#define PAGE_ALIGN(value) ALIGN_TO(value, 4096)

static VAStatus lnc__alloc_context_buffer(context_ENC_p ctx)
{
    int width, height;
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    /* width and height should be source surface's w and h or ?? */
    width = ctx->obj_context->picture_width;
    height = ctx->obj_context->picture_height;

    ctx->pic_params_size  = 256;

    ctx->header_buffer_size = 4 * HEADER_SIZE + MAX_SLICES_PER_PICTURE * HEADER_SIZE;

    ctx->seq_header_ofs = 0;
    ctx->pic_header_ofs = HEADER_SIZE;
    ctx->eoseq_header_ofs = 2 * HEADER_SIZE;
    ctx->eostream_header_ofs = 3 * HEADER_SIZE;
    ctx->slice_header_ofs = 4 * HEADER_SIZE;

    ctx->sliceparam_buffer_size = ((sizeof(SLICE_PARAMS) + 15) & 0xfff0) * MAX_SLICES_PER_PICTURE;

    /* All frame share same MTX_CURRENT_IN_PARAMS and above/bellow param
     * create MTX_CURRENT_IN_PARAMS buffer seperately
     * every MB has one MTX_CURRENT_IN_PARAMS structure, and the (N+1) frame can
     * reuse (N) frame's structure
     */
    ctx->in_params_size = (10 + width * height / (16 * 16)) * sizeof(MTX_CURRENT_IN_PARAMS);
    ctx->bellow_params_size = BELOW_PARAMS_SIZE * width * height / (16 * 16) * 16;
    ctx->above_params_size = (width * height / 16) * 128 + 15;

    ctx->topaz_buffer_size = ctx->in_params_size + /* MTX_CURRENT_IN_PARAMS size */
                             ctx->bellow_params_size + /* above_params */
                             ctx->above_params_size; /* above_params */

    vaStatus = psb_buffer_create(ctx->obj_context->driver_data, ctx->in_params_size, psb_bt_cpu_vpu, &ctx->topaz_in_params_I);
    vaStatus |= psb_buffer_create(ctx->obj_context->driver_data, ctx->in_params_size, psb_bt_cpu_vpu, &ctx->topaz_in_params_P);
    vaStatus |= psb_buffer_create(ctx->obj_context->driver_data, ctx->above_params_size + ctx->bellow_params_size, psb_bt_cpu_vpu, &ctx->topaz_above_bellow_params);

    ctx->in_params_ofs = 0;
    ctx->bellow_params_ofs = 0;
    ctx->above_params_ofs = ctx->bellow_params_ofs + ctx->bellow_params_size;

    return vaStatus;
}

unsigned int lnc__get_ipe_control(enum drm_lnc_topaz_codec  eEncodingFormat)
{
    unsigned int RegVal = 0;

    RegVal = F_ENCODE(2, MVEA_CR_IPE_GRID_FINE_SEARCH) |
             F_ENCODE(0, MVEA_CR_IPE_GRID_SEARCH_SIZE) |
             F_ENCODE(1, MVEA_CR_IPE_Y_FINE_SEARCH);

    switch (eEncodingFormat) {
    case IMG_CODEC_H263_NO_RC:
    case IMG_CODEC_H263_VBR:
    case IMG_CODEC_H263_CBR:
        RegVal |= F_ENCODE(0, MVEA_CR_IPE_BLOCKSIZE) | F_ENCODE(0, MVEA_CR_IPE_ENCODING_FORMAT);
        break;
    case IMG_CODEC_MPEG4_NO_RC:
    case IMG_CODEC_MPEG4_VBR:
    case IMG_CODEC_MPEG4_CBR:
        RegVal |= F_ENCODE(1, MVEA_CR_IPE_BLOCKSIZE) | F_ENCODE(1, MVEA_CR_IPE_ENCODING_FORMAT);
    default:
        break;
    case IMG_CODEC_H264_NO_RC:
    case IMG_CODEC_H264_VBR:
    case IMG_CODEC_H264_CBR:
    case IMG_CODEC_H264_VCM:
        RegVal |= F_ENCODE(2, MVEA_CR_IPE_BLOCKSIZE) | F_ENCODE(2, MVEA_CR_IPE_ENCODING_FORMAT);
        break;
    }
    RegVal |= F_ENCODE(6, MVEA_CR_IPE_Y_CANDIDATE_NUM);
    return RegVal;
}


void lnc_DestroyContext(object_context_p obj_context)
{
    context_ENC_p ctx;
    ctx = (context_ENC_p)obj_context->format_data;
    if (NULL != ctx->slice_param_cache)
        free(ctx->slice_param_cache);
    if (NULL == ctx->save_seq_header_p)
        free(ctx->save_seq_header_p);
    free(obj_context->format_data);
    obj_context->format_data = NULL;
}

VAStatus lnc_CreateContext(
    object_context_p obj_context,
    object_config_p obj_config)
{
    int width, height;
    context_ENC_p ctx;
    VAStatus vaStatus;

    width = obj_context->picture_width;
    height = obj_context->picture_height;
    ctx = (context_ENC_p) calloc(1, sizeof(struct context_ENC_s));
    if (NULL == ctx) {
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DEBUG_FAILURE;
        return vaStatus;
    }

    obj_context->format_data = (void*) ctx;
    ctx->obj_context = obj_context;

    ctx->RawWidth = (unsigned short) width;
    ctx->RawHeight = (unsigned short) height;

    ctx->Width = (unsigned short)(~0xf & (width + 0xf));
    ctx->Height = (unsigned short)(~0xf & (height + 0xf));

    ctx->HeightMinus16MinusLRBTopOffset = ctx->Height - (MVEA_LRB_TOP_OFFSET + 16);
    ctx->HeightMinus32MinusLRBTopOffset = ctx->Height - (MVEA_LRB_TOP_OFFSET + 32);
    ctx->HeightMinusLRB_TopAndBottom_OffsetsPlus16 = ctx->Height - (MVEA_LRB_TOP_OFFSET + MVEA_LRB_TOP_OFFSET + 16);
    ctx->HeightMinusLRBSearchHeight = ctx->Height - MVEA_LRB_SEARCH_HEIGHT;

    ctx->FCode = 0;

    ctx->sRCParams.VCMBitrateMargin = 0;
    ctx->sRCParams.BufferSize = 0;
    ctx->sRCParams.InitialQp = 0;
    ctx->sRCParams.MinQP = 0;

    vaStatus = lnc__alloc_context_buffer(ctx);

    return vaStatus;
}


VAStatus lnc_BeginPicture(context_ENC_p ctx)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    lnc_cmdbuf_p cmdbuf;
    int ret;

    ctx->src_surface = ctx->obj_context->current_render_target;

    /* clear frameskip flag to 0 */
    CLEAR_SURFACE_INFO_skipped_flag(ctx->src_surface->psb_surface);

    /* Initialise the command buffer */
    ret = lnc_context_get_next_cmdbuf(ctx->obj_context);
    if (ret) {
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "get next cmdbuf fail\n");
        vaStatus = VA_STATUS_ERROR_UNKNOWN;
        return vaStatus;
    }
    cmdbuf = ctx->obj_context->lnc_cmdbuf;

    /* map start_pic param */
    vaStatus = psb_buffer_map(&cmdbuf->pic_params, &cmdbuf->pic_params_p);
    if (vaStatus) {
        return vaStatus;
    }
    vaStatus = psb_buffer_map(&cmdbuf->header_mem, &cmdbuf->header_mem_p);
    if (vaStatus) {
        psb_buffer_unmap(&cmdbuf->pic_params);
        return vaStatus;
    }

    vaStatus = psb_buffer_map(&cmdbuf->slice_params, &cmdbuf->slice_params_p);
    if (vaStatus) {
        psb_buffer_unmap(&cmdbuf->pic_params);
        psb_buffer_unmap(&cmdbuf->header_mem);
        return vaStatus;
    }

    /* only map topaz param when necessary */
    cmdbuf->topaz_in_params_I_p = NULL;
    cmdbuf->topaz_in_params_P_p = NULL;
    cmdbuf->topaz_above_bellow_params_p = NULL;

    if (ctx->obj_context->frame_count == 0) { /* first picture */
        psb_driver_data_p driver_data = ctx->obj_context->driver_data;

        lnc_cmdbuf_insert_command(cmdbuf, MTX_CMDID_SW_NEW_CODEC, 3, driver_data->drm_context);
        lnc_cmdbuf_insert_command_param(cmdbuf, ctx->eCodec);
        lnc_cmdbuf_insert_command_param(cmdbuf, (ctx->Width << 16) | ctx->Height);
    }

    /* insert START_PIC command */
    lnc_cmdbuf_insert_command(cmdbuf, MTX_CMDID_START_PIC, 3, ctx->obj_context->frame_count);
    /* write the address of structure PIC_PARAMS following command MTX_CMDID_START_PIC
     * the content of PIC_PARAMS is filled when RenderPicture(...,VAEncPictureParameterBufferXXX)
     */
    RELOC_CMDBUF(cmdbuf->cmd_idx, 0, &cmdbuf->pic_params);
    cmdbuf->cmd_idx++;
    ctx->initial_qp_in_cmdbuf = cmdbuf->cmd_idx; /* remember the place */
    cmdbuf->cmd_idx++;

    ctx->obj_context->slice_count = 0;

    /* no RC paramter provided in vaBeginPicture
     * so delay RC param setup into vaRenderPicture(SequenceHeader...)
     */
    return vaStatus;
}


VAStatus lnc_RenderPictureParameter(context_ENC_p ctx)
{
    PIC_PARAMS *psPicParams;    /* PIC_PARAMS has been put in lnc_hostcode.h */
    object_surface_p src_surface;
    unsigned int srf_buf_offset;
    object_surface_p rec_surface;
    object_surface_p ref_surface;
    lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;
    VAStatus vaStatus = VA_STATUS_ERROR_UNKNOWN;

    psPicParams = (PIC_PARAMS *)cmdbuf->pic_params_p;

    /* second frame will reuse some rate control parameters (IN_PARAMS_MP4)
     * so only memset picture parames except IN_PARAMS
     * BUT now IN_RC_PARAMS was reload from the cache, so it now can
     * memset entirE PIC_PARAMS
     */
    memset(psPicParams, 0, (int)((unsigned char *)&psPicParams->sInParams - (unsigned char *)psPicParams));

    src_surface = ctx->src_surface;
    if (NULL == src_surface) {
        vaStatus = VA_STATUS_ERROR_INVALID_SURFACE;
        DEBUG_FAILURE;
        return vaStatus;
    }

    rec_surface = ctx->dest_surface;
    if (NULL == rec_surface) {
        vaStatus = VA_STATUS_ERROR_INVALID_SURFACE;
        DEBUG_FAILURE;
        return vaStatus;
    }

    /*The fisrt frame always is I frame and the content of reference frame wouldn't be used.
     * But the heights of ref and dest frame should be the same.
     * That allows Topaz to keep its motion vectors up to date, which helps maintain performance */
    if (ctx->obj_context->frame_count == 0)
        ctx->ref_surface = ctx->dest_surface;

    ref_surface = ctx->ref_surface;
    if (NULL == ref_surface) {
        vaStatus = VA_STATUS_ERROR_INVALID_SURFACE;
        DEBUG_FAILURE;
        return vaStatus;
    }

    /* clear frameskip flag */
    CLEAR_SURFACE_INFO_skipped_flag(rec_surface->psb_surface);
    CLEAR_SURFACE_INFO_skipped_flag(ref_surface->psb_surface);

    /* Write video data byte offset into Coded buffer
     * If it is here, it will be a SYNC point, which have performance impact
     * Move to psb__CreateBuffer
     vaStatus = psb_buffer_map(ctx->coded_buf->psb_buffer, &pBuffer);
     if(vaStatus) {
     DEBUG_FAILURE;
     return vaStatus;
     }
     *(IMG_UINT32 *)(pBuffer+8) = 16;
     psb_buffer_unmap(ctx->coded_buf->psb_buffer);
    */

    psPicParams->SrcYStride = src_surface->psb_surface->stride;
    switch (ctx->eFormat) {
    case IMG_CODEC_IYUV:        /* IYUV */
    case IMG_CODEC_PL8:
        psPicParams->SrcUVStride = src_surface->psb_surface->stride / 2;
        psPicParams->SrcUVRowStride = src_surface->psb_surface->stride * 16 / 2;
        break;
    case IMG_CODEC_IMC2:    /* IMC2 */
    case IMG_CODEC_PL12:
        psPicParams->SrcUVStride = src_surface->psb_surface->stride;
        psPicParams->SrcUVRowStride = src_surface->psb_surface->stride * 16;
        break;
    default:
        break;
    }
    psPicParams->SrcYRowStride    = src_surface->psb_surface->stride * 16;
    /* psPicParams->SrcUVRowStride = src_surface->psb_surface->stride * 16 / 2; */

    /* Dest(rec) stride
     * The are now literally Dst stride (not equivalent to 'offset to next row')
     */
#ifdef VA_EMULATOR
    /* only for simulator, va-emulator needs the actually stride for
     * reconstructed frame transfer (va-emulator->driver)
     */
    psPicParams->DstYStride = rec_surface->psb_surface->stride;
    psPicParams->DstUVStride = rec_surface->psb_surface->stride;
    psPicParams->DstYRowStride = rec_surface->psb_surface->stride * 16;
    psPicParams->DstUVRowStride = rec_surface->psb_surface->stride * 16 / 2;
#else
    psPicParams->DstYStride = rec_surface->height * 16;
    psPicParams->DstUVStride = rec_surface->height * 16 / 2;
    psPicParams->DstYRowStride = psPicParams->DstYStride;
    psPicParams->DstUVRowStride = psPicParams->DstUVStride;
#endif

    psPicParams->InParamsRowStride = (ctx->obj_context->picture_width / 16) * 256;
    psPicParams->BelowParamRowStride = (ctx->obj_context->picture_width / 16) * 32;

    psPicParams->Width  = ctx->Width;
    psPicParams->Height = ctx->Height;

    /* not sure why we are setting this up here... */
    psPicParams->Flags = 0;
    switch (ctx->eCodec) {
    case IMG_CODEC_H264_NO_RC:
    case IMG_CODEC_H264_VBR:
    case IMG_CODEC_H264_CBR:
    case IMG_CODEC_H264_VCM:
        psPicParams->Flags |= ISH264_FLAGS;
        break;
    case IMG_CODEC_H263_VBR:
    case IMG_CODEC_H263_CBR:
    case IMG_CODEC_H263_NO_RC:
        psPicParams->Flags |= ISH263_FLAGS;
        break;
    case IMG_CODEC_MPEG4_NO_RC:
    case IMG_CODEC_MPEG4_VBR:
    case IMG_CODEC_MPEG4_CBR:
        psPicParams->Flags |= ISMPEG4_FLAGS;
        break;
    default:
        return VA_STATUS_ERROR_UNKNOWN;
    }

    switch (ctx->eCodec) {
    case IMG_CODEC_H264_VBR:
    case IMG_CODEC_MPEG4_VBR:
    case IMG_CODEC_H263_VBR:
        psPicParams->Flags |= ISVBR_FLAGS;
        break;
    case IMG_CODEC_H264_VCM:
        psPicParams->Flags |= ISVCM_FLAGS;
        /* drop through to CBR case */
    case IMG_CODEC_H263_CBR:
    case IMG_CODEC_H264_CBR:
    case IMG_CODEC_MPEG4_CBR:
        psPicParams->Flags |= ISCBR_FLAGS;
        break;
    case IMG_CODEC_MPEG4_NO_RC:
    case IMG_CODEC_H263_NO_RC:
    case IMG_CODEC_H264_NO_RC:
        break;
    default:
        return VA_STATUS_ERROR_UNKNOWN;
    }


    if (ctx->sRCParams.RCEnable) {
        /* for the first frame, will setup RC params in EndPicture */
        if (ctx->obj_context->frame_count > 0) { /* reuse in_params parameter */
            /* reload IN_RC_PARAMS from cache */
            memcpy(&psPicParams->sInParams, &ctx->in_params_cache, sizeof(IN_RC_PARAMS));

            /* delay these into END_PICTURE timeframe */
            /*
            psPicParams->sInParams.BitsTransmitted = ctx->sRCParams.BitsTransmitted;
            */
        }
    } else
        psPicParams->sInParams.SeInitQP = ctx->sRCParams.InitialQp;

    /* some relocations have to been done here */
    srf_buf_offset = src_surface->psb_surface->buf.buffer_ofs;
    if (src_surface->psb_surface->buf.type == psb_bt_camera)
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "src surface GPU offset 0x%08x, luma offset 0x%08x\n",
                                 wsbmBOOffsetHint(src_surface->psb_surface->buf.drm_buf), srf_buf_offset);

    RELOC_PIC_PARAMS(&psPicParams->SrcYBase, srf_buf_offset, &src_surface->psb_surface->buf);
    switch (ctx->eFormat) {
    case IMG_CODEC_IYUV:
    case IMG_CODEC_PL8:
    case IMG_CODEC_PL12:
        RELOC_PIC_PARAMS(&psPicParams->SrcUBase,
                         srf_buf_offset + src_surface->psb_surface->chroma_offset,
                         &src_surface->psb_surface->buf);

        RELOC_PIC_PARAMS(&psPicParams->SrcVBase,
                         srf_buf_offset + src_surface->psb_surface->chroma_offset,
                         &src_surface->psb_surface->buf);

        break;
    case IMG_CODEC_IMC2:
    case IMG_CODEC_NV12:
        break;
    }

    /*
     * Do not forget this!
     * MTXWriteMem(MTXData.ui32CCBCtrlAddr + MTX_CCBCTRL_QP, sRCParams.ui32InitialQp);
     */
    /* following START_PIC, insert initial QP */
    *ctx->initial_qp_in_cmdbuf = ctx->sRCParams.InitialQp;


    RELOC_PIC_PARAMS(&psPicParams->DstYBase, 0, &rec_surface->psb_surface->buf);

    RELOC_PIC_PARAMS(&psPicParams->DstUVBase,
                     rec_surface->psb_surface->stride * rec_surface->height,
                     &rec_surface->psb_surface->buf);

    RELOC_PIC_PARAMS(&psPicParams->CodedBase, 0, ctx->coded_buf->psb_buffer);

    /* MTX_CURRENT_IN_PARAMS buffer is seperate buffer now */
    /*The type of frame will decide psPicParams->InParamsBase should
     * use cmdbuf->topaz_in_params_P or cmdbuf->topaz_in_params_I*/
    /*RELOC_PIC_PARAMS(&psPicParams->InParamsBase, ctx->in_params_ofs, cmdbuf->topaz_in_params_P);*/
    RELOC_PIC_PARAMS(&psPicParams->BelowParamsBase, ctx->bellow_params_ofs, cmdbuf->topaz_above_bellow_params);
    RELOC_PIC_PARAMS(&psPicParams->AboveParamsBase, ctx->above_params_ofs, cmdbuf->topaz_above_bellow_params);

    return VA_STATUS_SUCCESS;
}

static VAStatus lnc__PatchBitsConsumedInRCParam(context_ENC_p ctx)
{
    lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;
    /* PIC_PARAMS  *psPicParams = cmdbuf->pic_params_p; */
    VAStatus vaStatus;

    (void)cmdbuf;
    /* it will wait until last encode session is done */
    /* now it just wait the last session is done and the frame skip
     * is  */
    drv_debug_msg(VIDEO_DEBUG_GENERAL, "will patch bits consumed for rc\n");
    if (ctx->pprevious_coded_buf) {
        vaStatus = psb_buffer_sync(ctx->pprevious_coded_buf->psb_buffer);
        if (vaStatus)
            return vaStatus;
    }

    return VA_STATUS_SUCCESS;
}

static VAStatus lnc_RedoRenderPictureSkippedFrame(context_ENC_p ctx)
{
    lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    int i = 0;

    /* reset cmdbuf to skip existing picture/slice DO_HEAD commands */
    cmdbuf->cmd_idx = cmdbuf->cmd_idx_saved_frameskip;

    switch (ctx->eCodec) {
    case IMG_CODEC_H263_CBR: /* H263 don't need picture header/slice header, only reset command buf */
    case IMG_CODEC_H263_VBR:
        break;
    case IMG_CODEC_H264_VBR:
    case IMG_CODEC_H264_CBR: /* slice header needs redo */
    case IMG_CODEC_H264_VCM: {
        /* only need one slice header here */
        VAEncSliceParameterBuffer *pBuffer = &ctx->slice_param_cache[i];
        unsigned int MBSkipRun, FirstMBAddress;
        int deblock_on;

        if ((pBuffer->slice_flags.bits.disable_deblocking_filter_idc == 0)
            || (pBuffer->slice_flags.bits.disable_deblocking_filter_idc == 2))
            deblock_on = IMG_TRUE;
        else
            deblock_on = IMG_FALSE;

        if (1 /* ctx->sRCParams.RCEnable && ctx->sRCParams.FrameSkip */) /* we know it is true */
            MBSkipRun = (ctx->Width * ctx->Height) / 256;
        else
            MBSkipRun = 0;

        FirstMBAddress = (pBuffer->start_row_number * ctx->Width) / 16;
        /* Insert Do Header command, relocation is needed */

        lnc__H264_prepare_slice_header((IMG_UINT32 *)(cmdbuf->header_mem_p + ctx->slice_header_ofs + i * HEADER_SIZE),
                                       0, /*pBuffer->slice_flags.bits.is_intra*/
                                       pBuffer->slice_flags.bits.disable_deblocking_filter_idc,
                                       ctx->obj_context->frame_count,
                                       FirstMBAddress,
                                       MBSkipRun,
                                       (ctx->obj_context->frame_count == 0),
                                       pBuffer->slice_flags.bits.uses_long_term_ref,
                                       pBuffer->slice_flags.bits.is_long_term_ref,
                                       ctx->idr_pic_id);


        lnc_cmdbuf_insert_command(cmdbuf, MTX_CMDID_DO_HEADER, 2, (i << 2) | 2);
        RELOC_CMDBUF(cmdbuf->cmd_idx++,
                     ctx->slice_header_ofs + i * HEADER_SIZE,
                     &cmdbuf->header_mem);
    }

    break;
    case IMG_CODEC_MPEG4_VBR:
    case IMG_CODEC_MPEG4_CBR: /* only picture header need redo */
        lnc__MPEG4_prepare_vop_header((IMG_UINT32 *)(cmdbuf->header_mem_p + ctx->pic_header_ofs),
                                      IMG_FALSE /* bIsVOPCoded is false now */,
                                      ctx->MPEG4_vop_time_increment_frameskip, /* In testbench, this should be FrameNum */
                                      4,/* default value is 4,search range */
                                      ctx->MPEG4_picture_type_frameskip,
                                      ctx->MPEG4_vop_time_increment_resolution/* defaule value */);

        lnc_cmdbuf_insert_command(cmdbuf, MTX_CMDID_DO_HEADER, 2, 1);
        RELOC_CMDBUF(cmdbuf->cmd_idx++, ctx->pic_header_ofs, &cmdbuf->header_mem);
        vaStatus = lnc_RenderPictureParameter(ctx);
        break;
    default:
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Non-RC mode should be here for FrameSkip handling\n");
        ASSERT(0);
    }

    return vaStatus;
}

static VAStatus lnc_SetupRCParam(context_ENC_p ctx)
{
    lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;
    PIC_PARAMS  *psPicParams = (PIC_PARAMS  *)cmdbuf->pic_params_p;
    int origin_qp;/* in DDK setup_rc will change qp strangly,
                   * just for keep same with DDK
                   */

    origin_qp = ctx->sRCParams.InitialQp;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "will setup rc data\n");

    psPicParams->Flags |= ISRC_FLAGS;
    lnc__setup_rcdata(ctx, psPicParams, &ctx->sRCParams);

    /* restore it, just keep same with DDK */
    ctx->sRCParams.InitialQp = origin_qp;

    /* save IN_RC_PARAMS into the cache */
    memcpy(&ctx->in_params_cache, (unsigned char *)&psPicParams->sInParams, sizeof(IN_RC_PARAMS));

    return VA_STATUS_SUCCESS;
}

static VAStatus lnc_UpdateRCParam(context_ENC_p ctx)
{
    int origin_qp;
    lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;
    PIC_PARAMS  *psPicParams = (PIC_PARAMS  *)cmdbuf->pic_params_p;

    origin_qp = ctx->sRCParams.InitialQp;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "will update rc data\n");
    lnc__update_rcdata(ctx, psPicParams, &ctx->sRCParams);

    /* set minQP if hosts set minQP */
    if (ctx->sRCParams.MinQP)
        psPicParams->sInParams.MinQPVal = ctx->sRCParams.MinQP;

    /* if seinitqp is set, restore the value hosts want */
    if (origin_qp) {
        psPicParams->sInParams.SeInitQP = origin_qp;
        psPicParams->sInParams.MyInitQP = origin_qp;
        ctx->sRCParams.InitialQp = origin_qp;
    }

    /* save IN_RC_PARAMS into the cache */
    memcpy(&ctx->in_params_cache, (unsigned char *)&psPicParams->sInParams, sizeof(IN_RC_PARAMS));

    return VA_STATUS_SUCCESS;
}

static VAStatus lnc_PatchRCMode(context_ENC_p ctx)
{
    int frame_skip = 0;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "will patch rc data\n");
    /* it will ensure previous encode finished */
    lnc__PatchBitsConsumedInRCParam(ctx);

    /* get frameskip flag */
    lnc_surface_get_frameskip(ctx->obj_context->driver_data, ctx->src_surface->psb_surface, &frame_skip);
    /* current frame is skipped
     * redo RenderPicture with FrameSkip set
     */
    if (frame_skip == 1)
        lnc_RedoRenderPictureSkippedFrame(ctx);

    return VA_STATUS_SUCCESS;
}

VAStatus lnc_EndPicture(context_ENC_p ctx)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;

    if (ctx->sRCParams.RCEnable == IMG_TRUE) {
        if (ctx->obj_context->frame_count == 0)
            lnc_SetupRCParam(ctx);
        else if (ctx->update_rc_control)
            lnc_UpdateRCParam(ctx);
        else
            lnc_PatchRCMode(ctx);
    }
    ctx->update_rc_control = 0;

    /* save current settings */
    ctx->previous_src_surface = ctx->src_surface;
    ctx->previous_ref_surface = ctx->ref_surface;
    ctx->previous_dest_surface = ctx->dest_surface; /* reconstructed surface */
    ctx->pprevious_coded_buf = ctx->previous_coded_buf;
    ctx->previous_coded_buf = ctx->coded_buf;

    lnc_cmdbuf_insert_command(cmdbuf, MTX_CMDID_END_PIC, 3, 0);
    lnc_cmdbuf_insert_command_param(cmdbuf, 0);/* two meaningless parameters */
    lnc_cmdbuf_insert_command_param(cmdbuf, 0);
    psb_buffer_unmap(&cmdbuf->pic_params);
    psb_buffer_unmap(&cmdbuf->header_mem);
    psb_buffer_unmap(&cmdbuf->slice_params);

    /* unmap MTX_CURRENT_IN_PARAMS buffer only when it is mapped */
    if (cmdbuf->topaz_in_params_I_p != NULL) {
        psb_buffer_unmap(cmdbuf->topaz_in_params_I);
        cmdbuf->topaz_in_params_I_p = NULL;
    }

    if (cmdbuf->topaz_in_params_P_p != NULL) {
        psb_buffer_unmap(cmdbuf->topaz_in_params_P);
        cmdbuf->topaz_in_params_P_p = NULL;
    }

    if (cmdbuf->topaz_above_bellow_params_p != NULL) {
        psb_buffer_unmap(cmdbuf->topaz_above_bellow_params);
        cmdbuf->topaz_above_bellow_params_p = NULL;
    }

    if (lnc_context_flush_cmdbuf(ctx->obj_context)) {
        vaStatus = VA_STATUS_ERROR_UNKNOWN;
    }

    return vaStatus;
}

static void lnc__setup_busize(context_ENC_p ctx)
{
    unsigned int old_busize = ctx->sRCParams.BUSize;

    /* it is called at EndPicture, we should now the Slice number */
    ctx->Slices = ctx->obj_context->slice_count;

    /* if no BU size is given then pick one ourselves */
    if (ctx->sRCParams.BUSize != 0)  { /* application provided BUSize */
        IMG_UINT32 MBs, MBsperSlice, MBsLastSlice;
        IMG_UINT32 BUs;
        IMG_INT32  SliceHeight;

        MBs     = ctx->Height * ctx->Width / (16 * 16);

        SliceHeight     = ctx->Height / ctx->Slices;
        /* SliceHeight += 15; */
        SliceHeight &= ~15;

        MBsperSlice     = (SliceHeight * ctx->Width) / (16 * 16);
        MBsLastSlice    = MBs - (MBsperSlice * (ctx->Slices - 1));

        /* they have given us a basic unit so validate it */
        if (ctx->sRCParams.BUSize < 6) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "ERROR: Basic unit size too small, must be greater than 6\n");
            ctx->sRCParams.BUSize = 0; /* need repatch */;
        }
        if (ctx->sRCParams.BUSize > MBsperSlice) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "ERROR: Basic unit size too large, must be less than the number of macroblocks in a slice\n");
            ctx->sRCParams.BUSize = 0; /* need repatch */;
        }
        if (ctx->sRCParams.BUSize > MBsLastSlice) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "ERROR: Basic unit size too large, must be less than number of macroblocks in the last slice\n");
            ctx->sRCParams.BUSize = 0; /* need repatch */;
        }
        BUs = MBsperSlice / ctx->sRCParams.BUSize;
        if ((BUs * ctx->sRCParams.BUSize) != MBsperSlice)   {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "ERROR: Basic unit size not an integer divisor of MB's in a slice");
            ctx->sRCParams.BUSize = 0; /* need repatch */;
        }
        if (BUs > 200) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "ERROR: Basic unit size too small. There must be less than 200 basic units per slice");
            ctx->sRCParams.BUSize = 0; /* need repatch */;
        }
        BUs = MBsLastSlice / ctx->sRCParams.BUSize;
        if (BUs > 200) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "ERROR: Basic unit size too small. There must be less than 200 basic units per slice");
            ctx->sRCParams.BUSize = 0; /* need repatch */;
        }
    }

    if (ctx->sRCParams.BUSize == 0)  {
        IMG_UINT32 MBs, MBsperSlice, MBsLastSlice;
        IMG_UINT32 BUs, BUsperSlice, BUsLastSlice;
        IMG_INT32  SliceHeight;

        MBs     = ctx->Height * ctx->Width / (16 * 16);

        SliceHeight     = ctx->Height / ctx->Slices;
        /* SliceHeight += 15; */
        SliceHeight &= ~15;

        MBsperSlice     = (SliceHeight * ctx->Width) / (16 * 16);
        MBsLastSlice = MBs - (MBsperSlice * (ctx->Slices - 1));

        /* we have to verify that MBs is divisiable by BU AND that BU is > pipeline length */
        if (ctx->sRCParams.BUSize < 6)  {
            ctx->sRCParams.BUSize = 6;
        }

        BUs = MBs / ctx->sRCParams.BUSize;
        while (BUs*ctx->sRCParams.BUSize != MBs) {
            ctx->sRCParams.BUSize++;
            BUs = MBs / ctx->sRCParams.BUSize;
        }

        /* Check number of BUs in the pipe is less than maximum number allowed 200  */
        BUsperSlice = MBsperSlice / ctx->sRCParams.BUSize;
        BUsLastSlice = MBsLastSlice / ctx->sRCParams.BUSize;
        while ((BUsperSlice  *(ctx->Slices - 1) + BUsLastSlice) > 200)  {
            ctx->sRCParams.BUSize++;
            BUsperSlice = MBsperSlice / ctx->sRCParams.BUSize;
            BUsLastSlice = MBsLastSlice / ctx->sRCParams.BUSize;
        }

        /* Check whether there are integer number of BUs in the slices  */
        BUsperSlice = MBsperSlice / ctx->sRCParams.BUSize;
        BUsLastSlice = MBsLastSlice / ctx->sRCParams.BUSize;
        while ((BUsperSlice*ctx->sRCParams.BUSize != MBsperSlice) ||
               (BUsLastSlice*ctx->sRCParams.BUSize != MBsLastSlice))   {
            ctx->sRCParams.BUSize++;
            BUsperSlice = MBsperSlice / ctx->sRCParams.BUSize;
            BUsLastSlice = MBsLastSlice / ctx->sRCParams.BUSize;
        }

        if (ctx->sRCParams.BUSize != old_busize)
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Patched Basic unit to %d (original=%d)\n", ctx->sRCParams.BUSize, old_busize);
    }
}


/***********************************************************************************
 * Function Name      : SetupRCData
 * Inputs             :
 * Outputs            :
 * Returns            :
 * Description        : Sets up RC Data
 ************************************************************************************/
void lnc__setup_rcdata(
    context_ENC_p psContext,
    PIC_PARAMS *psPicParams,
    IMG_RC_PARAMS *psRCParams)
{
    IMG_UINT32   max_bitrate = psContext->Width * psContext->Height * 1.5 * 8 * 60;
    IMG_UINT8 InitialSeInitQP = 0;

    /* frameskip is always cleared, specially handled at vaEndPicture */
    psRCParams->FrameSkip = 0;

    if (!psRCParams->BitsPerSecond)
        psRCParams->BitsPerSecond = 64000;
    if (psRCParams->BitsPerSecond > max_bitrate)
        psRCParams->BitsPerSecond = max_bitrate;

    if (!psRCParams->FrameRate)
        psRCParams->FrameRate = 30;

    if (psRCParams->BufferSize == 0) {
        if (psRCParams->BitsPerSecond < 256000)
            psRCParams->BufferSize = (9 * psRCParams->BitsPerSecond) >> 1;
        else
            psRCParams->BufferSize = (5 * psRCParams->BitsPerSecond) >> 1;
    }
    psRCParams->InitialLevel = (3 * psRCParams->BufferSize) >> 4;
    psRCParams->InitialDelay = (13 * psRCParams->BufferSize) >> 4;

    lnc__setup_busize(psContext); /* calculate BasicUnitSize */

    psPicParams->sInParams.SeInitQP = psRCParams->InitialQp;

    psPicParams->sInParams.MBPerRow = (psContext->Width >> 4);
    psPicParams->sInParams.MBPerBU = psRCParams->BUSize;
    psPicParams->sInParams.MBPerFrm     = (psContext->Width >> 4) * (psContext->Height >> 4);
    psPicParams->sInParams.BUPerFrm     = (psPicParams->sInParams.MBPerFrm) / psRCParams->BUSize;

    InitialSeInitQP = psPicParams->sInParams.SeInitQP;

    lnc__update_rcdata(psContext, psPicParams, psRCParams);
    /* set minQP if hosts set minQP */
    if (psRCParams->MinQP)
        psPicParams->sInParams.MinQPVal = psRCParams->MinQP;

    /* if seinitqp is set, restore the value hosts want */
    if (InitialSeInitQP) {
        psPicParams->sInParams.SeInitQP = InitialSeInitQP;
        psPicParams->sInParams.MyInitQP = InitialSeInitQP;
        psRCParams->InitialQp = InitialSeInitQP;
    }
}

void lnc__update_rcdata(context_ENC_p psContext,
                        PIC_PARAMS *psPicParams,
                        IMG_RC_PARAMS *psRCParams)
{
    double              L1, L2, L3, L4, L5, flBpp;
    INT16               i16TempQP;
    IMG_INT32   i32BufferSizeInFrames;

    flBpp                                                                       = 1.0 * psRCParams->BitsPerSecond / (psRCParams->FrameRate * psContext->Width * psContext->Height);

    /* recalculate for small frames */
    if (psContext->Width <= 176)
        flBpp = flBpp / 2.0;

    psPicParams->sInParams.IntraPeriod  = psRCParams->IntraFreq;
    psPicParams->sInParams.BitRate              = psRCParams->BitsPerSecond;
    psPicParams->sInParams.IntraPeriod  = psRCParams->IntraFreq;

    psPicParams->sInParams.BitsPerFrm   = psRCParams->BitsPerSecond / psRCParams->FrameRate;
    psPicParams->sInParams.BitsPerGOP   = psPicParams->sInParams.BitsPerFrm * psRCParams->IntraFreq;
    psPicParams->sInParams.BitsPerBU            = psPicParams->sInParams.BitsPerFrm / (4 * psPicParams->sInParams.BUPerFrm);
    psPicParams->sInParams.BitsPerMB            = psPicParams->sInParams.BitsPerBU / psRCParams->BUSize;

    i32BufferSizeInFrames = psRCParams->BufferSize / psPicParams->sInParams.BitsPerFrm;

    // select thresholds and initial Qps etc that are codec dependent
    switch (psContext->eCodec) {
    case IMG_CODEC_H264_CBR:
    case IMG_CODEC_H264_VCM:
    case IMG_CODEC_H264_VBR:
        L1 = 0.1;
        L2 = 0.15;
        L3 = 0.2;
        psPicParams->sInParams.MaxQPVal = 51;

        // Set THSkip Values
        if (flBpp <= 0.07)
            psPicParams->THSkip = TH_SKIP_24;
        else if (flBpp <= 0.14)
            psPicParams->THSkip = TH_SKIP_12;
        else
            psPicParams->THSkip = TH_SKIP_0;

        if (flBpp <= 0.3)
            psPicParams->Flags |= ISRC_I16BIAS;

        // Setup MAX and MIN Quant Values
        if (flBpp >= 0.50)
            i16TempQP = 4;
        else if (flBpp > 0.133)
            i16TempQP = (unsigned int)(24 - (40 * flBpp));
        else
            i16TempQP = (unsigned int)(32 - (100 * flBpp));

        psPicParams->sInParams.MinQPVal = (max(min(psPicParams->sInParams.MaxQPVal, i16TempQP), 0));
        // Calculate Initial QP if it has not been specified

        L1 = 0.050568;
        L2 = 0.202272;
        L3 = 0.40454321;
        L4 = 0.80908642;
        L5 = 1.011358025;
        if (flBpp < L1)
            i16TempQP = (IMG_INT16)(47 - 78.10 * flBpp);

        else if (flBpp >= L1 && flBpp < L2)
            i16TempQP = (IMG_INT16)(46 - 72.51 * flBpp);

        else if (flBpp >= L2 && flBpp < L3)
            i16TempQP = (IMG_INT16)(36 - 24.72 * flBpp);

        else if (flBpp >= L3 && flBpp < L4)
            i16TempQP = (IMG_INT16)(34 - 19.78 * flBpp);

        else if (flBpp >= L4 && flBpp < L5)
            i16TempQP = (IMG_INT16)(27 - 9.89 * flBpp);

        else if (flBpp >= L5)
            i16TempQP = (IMG_INT16)(20 - 4.95 * flBpp);

        psPicParams->sInParams.SeInitQP = (IMG_UINT8)(max(min(psPicParams->sInParams.MaxQPVal, i16TempQP), 0));
        break;

    case IMG_CODEC_MPEG4_CBR:
    case IMG_CODEC_MPEG4_VBR:
        psPicParams->sInParams.MaxQPVal  = 31;

        if (psContext->Width == 176) {
            L1 = 0.1;
            L2 = 0.3;
            L3 = 0.6;
        } else if (psContext->Width == 352) {
            L1 = 0.2;
            L2 = 0.6;
            L3 = 1.2;
        } else {
            L1 = 0.25;
            L2 = 1.4;
            L3 = 2.4;
        }

        // Calculate Initial QP if it has not been specified
        if (flBpp <= L1)
            psPicParams->sInParams.SeInitQP = 31;
        else {
            if (flBpp <= L2)
                psPicParams->sInParams.SeInitQP = 25;
            else
                psPicParams->sInParams.SeInitQP = (flBpp <= L3) ? 20 : 10;
        }

        if (flBpp >= 0.25) {
            psPicParams->sInParams.MinQPVal = 1;
        } else {
            psPicParams->sInParams.MinQPVal = 2;
        }
        break;

    case IMG_CODEC_H263_CBR:
    case IMG_CODEC_H263_VBR:
        psPicParams->sInParams.MaxQPVal  = 31;

        if (psContext->Width == 176) {
            L1 = 0.1;
            L2 = 0.3;
            L3 = 0.6;
        } else if (psContext->Width == 352) {
            L1 = 0.2;
            L2 = 0.6;
            L3 = 1.2;
        } else {
            L1 = 0.25;
            L2 = 1.4;
            L3 = 2.4;
        }

        // Calculate Initial QP if it has not been specified
        if (flBpp <= L1)
            psPicParams->sInParams.SeInitQP = 31;
        else {
            if (flBpp <= L2)
                psPicParams->sInParams.SeInitQP = 25;
            else
                psPicParams->sInParams.SeInitQP = (flBpp <= L3) ? 20 : 10;
        }

        psPicParams->sInParams.MinQPVal = 3;

        break;

    default:
        /* the NO RC cases will fall here */
        break;
    }

    // Set up Input Parameters that are mode dependent
    switch (psContext->eCodec) {
    case IMG_CODEC_H264_NO_RC:
    case IMG_CODEC_H263_NO_RC:
    case IMG_CODEC_MPEG4_NO_RC:
        return;

    case IMG_CODEC_H264_VCM:
        psPicParams->Flags                              |= ISVCM_FLAGS | ISCBR_FLAGS;
        /* drop through to CBR case */
        /* for SD and above we can target 95% (122/128) of maximum bitrate */
        if (psRCParams->VCMBitrateMargin) {
            psPicParams->sInParams.VCMBitrateMargin = psRCParams->VCMBitrateMargin;
        } else {
            if (psContext->Height >= 480)
                psPicParams->sInParams.VCMBitrateMargin = 122;
            else
                psPicParams->sInParams.VCMBitrateMargin = 115; /* for less and SD we target 90% (115/128) of maximum bitrate */
            if (i32BufferSizeInFrames < 15)
                psPicParams->sInParams.VCMBitrateMargin -= 5;/* when we have a very small window size we reduce the target further to avoid too much skipping */
        }
        psPicParams->sInParams.ForceSkipMargin = 500;/* start skipping MBs when within 500 bits of slice or frame limit */

        // Set a scale factor to avoid overflows in maths
        if (psRCParams->BitsPerSecond < 1000000) {      // 1 Mbits/s
            psPicParams->sInParams.ScaleFactor = 0;
        } else if (psRCParams->BitsPerSecond < 2000000) { // 2 Mbits/s
            psPicParams->sInParams.ScaleFactor = 1;
        } else if (psRCParams->BitsPerSecond < 4000000) { // 4 Mbits/s
            psPicParams->sInParams.ScaleFactor = 2;
        } else if (psRCParams->BitsPerSecond < 8000000) { // 8 Mbits/s
            psPicParams->sInParams.ScaleFactor = 3;
        } else {
            psPicParams->sInParams.ScaleFactor = 4;
        }

        psPicParams->sInParams.BufferSize = i32BufferSizeInFrames;
        break;

    case IMG_CODEC_H264_CBR:
        psPicParams->Flags                              |= ISCBR_FLAGS;
        // ------------------- H264 CBR RC ------------------- //
        // Initialize the parameters of fluid flow traffic model.
        psPicParams->sInParams.BufferSize = psRCParams->BufferSize;

        // HRD consideration - These values are used by H.264 reference code.
        if (psRCParams->BitsPerSecond < 1000000) {      // 1 Mbits/s
            psPicParams->sInParams.ScaleFactor = 0;
        } else if (psRCParams->BitsPerSecond < 2000000) { // 2 Mbits/s
            psPicParams->sInParams.ScaleFactor = 1;
        } else if (psRCParams->BitsPerSecond < 4000000) { // 4 Mbits/s
            psPicParams->sInParams.ScaleFactor = 2;
        } else if (psRCParams->BitsPerSecond < 8000000) { // 8 Mbits/s
            psPicParams->sInParams.ScaleFactor = 3;
        } else {
            psPicParams->sInParams.ScaleFactor = 4;
        }
        break;

    case IMG_CODEC_MPEG4_CBR:
    case IMG_CODEC_H263_CBR:
        psPicParams->Flags                                      |= ISCBR_FLAGS;

        flBpp  = 256 * (psRCParams->BitsPerSecond / psContext->Width);
        flBpp /= (psContext->Height * psRCParams->FrameRate);

        if ((psPicParams->sInParams.MBPerFrm > 1024 && flBpp < 16) || (psPicParams->sInParams.MBPerFrm <= 1024 && flBpp < 24))
            psPicParams->sInParams.HalfFrameRate = 1;
        else
            psPicParams->sInParams.HalfFrameRate = 0;

        if (psPicParams->sInParams.HalfFrameRate >= 1) {
            psPicParams->sInParams.SeInitQP = 31;
            psPicParams->sInParams.AvQPVal = 31;
            psPicParams->sInParams.MyInitQP = 31;
        }

        if (psRCParams->BitsPerSecond <= 384000)
            psPicParams->sInParams.BufferSize = ((psRCParams->BitsPerSecond * 5) >> 1);
        else
            psPicParams->sInParams.BufferSize = psRCParams->BitsPerSecond * 4;
        break;

    case IMG_CODEC_MPEG4_VBR:
    case IMG_CODEC_H263_VBR:
    case IMG_CODEC_H264_VBR:
        psPicParams->Flags                              |= ISVBR_FLAGS;

        psPicParams->sInParams.MBPerBU  = psPicParams->sInParams.MBPerFrm;
        psPicParams->sInParams.BUPerFrm = 1;

        // Initialize the parameters of fluid flow traffic model.
        psPicParams->sInParams.BufferSize       = ((5 * psRCParams->BitsPerSecond) >> 1);

        // These scale factor are used only for rate control to avoid overflow
        // in fixed-point calculation these scale factors are decided by bit rate
        if (psRCParams->BitsPerSecond < 640000) {
            psPicParams->sInParams.ScaleFactor  = 2;                                            // related to complexity
        } else if (psRCParams->BitsPerSecond < 2000000) {
            psPicParams->sInParams.ScaleFactor  = 4;
        } else {
            psPicParams->sInParams.ScaleFactor  = 6;
        }
        break;
    default:
        break;
    }

    psPicParams->sInParams.MyInitQP             = psPicParams->sInParams.SeInitQP;
    psPicParams->sInParams.InitialDelay = psRCParams->InitialDelay;
    psPicParams->sInParams.InitialLevel = psRCParams->InitialLevel;
    psRCParams->InitialQp                               = psPicParams->sInParams.SeInitQP;
}



static void lnc__setup_qpvalue_h264(
    MTX_CURRENT_IN_PARAMS * psCurrent,
    IMG_BYTE bySliceQP)
{
    /* H.264 QP scaling tables */
    IMG_BYTE HOST_PVR_QP_SCALE_CR[52] = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
        12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
        28, 29, 29, 30, 31, 32, 32, 33, 34, 34, 35, 35, 36, 36, 37, 37,
        37, 38, 38, 38, 39, 39, 39, 39
    };

    psCurrent->bySliceQP = bySliceQP;
    psCurrent->bySliceQPC = HOST_PVR_QP_SCALE_CR[psCurrent->bySliceQP];
}


static void lnc__setup_qpvalues_mpeg4(
    MTX_CURRENT_IN_PARAMS * psCurrent,
    IMG_BYTE bySliceQP)
{
    psCurrent->bySliceQP =      bySliceQP;
}


static void lnc__setup_slice_row_params(
    context_ENC_p ctx,
    IMG_BOOL IsIntra,
    IMG_UINT16 CurrentRowY,
    IMG_INT16 SliceStartRowY,
    IMG_INT16 SliceHeight,
    IMG_BOOL VectorsValid,
    int bySliceQP)
{
    /* Note: CurrentRowY and SliceStartRowY are now in pixels (not MacroBlocks)
     * - saves needless multiplications and divisions
     */
    IMG_INT16   Pos, YPos, srcY;
    MTX_CURRENT_IN_PARAMS *psCurrent;
    lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;
    IMG_UINT16  tmp;

    if (IsIntra && cmdbuf->topaz_in_params_I_p == NULL) {
        VAStatus vaStatus = psb_buffer_map(cmdbuf->topaz_in_params_I, &cmdbuf->topaz_in_params_I_p);
        if (vaStatus != VA_STATUS_SUCCESS) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "map topaz MTX_CURRENT_IN_PARAMS failed\n");
            return;
        }
    }

    if ((!IsIntra) && cmdbuf->topaz_in_params_P_p == NULL) {
        VAStatus vaStatus = psb_buffer_map(cmdbuf->topaz_in_params_P, &cmdbuf->topaz_in_params_P_p);
        if (vaStatus != VA_STATUS_SUCCESS) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "map topaz MTX_CURRENT_IN_PARAMS failed\n");
            return;
        }
    }
    if (IsIntra)
        psCurrent = (MTX_CURRENT_IN_PARAMS*)(cmdbuf->topaz_in_params_I_p + ctx->in_params_ofs);
    else
        psCurrent = (MTX_CURRENT_IN_PARAMS*)(cmdbuf->topaz_in_params_P_p + ctx->in_params_ofs);

    psCurrent += (CurrentRowY  * (ctx->Width) / 256);

    if ((YPos = srcY = CurrentRowY - MVEA_LRB_TOP_OFFSET) < 0)
        srcY = 0;
    else if (YPos > ctx->HeightMinusLRB_TopAndBottom_OffsetsPlus16)
        srcY = ctx->HeightMinusLRBSearchHeight;

    tmp = (CurrentRowY != SliceStartRowY);

    for (Pos = 0; Pos < (int)ctx->Width; Pos += 16, psCurrent++) {
        memset(psCurrent, 0, sizeof(MTX_CURRENT_IN_PARAMS));
        psCurrent->MVValid = 0;
        psCurrent->ParamsValid = 0;

        /* Setup the parameters and motion vectors */
        if (tmp) {
            psCurrent->MVValid = 66;
            psCurrent->ParamsValid |= PARAMS_ABOVE_VALID;

            if (Pos + 16 < (int)ctx->Width) {
                psCurrent->ParamsValid |= PARAMS_ABOVER_VALID;
                psCurrent->MVValid |= 4; /* (1<<2) */
            }

            if (Pos > 0 && (Pos < (int)ctx->Width)) {
                psCurrent->ParamsValid |= PARAMS_ABOVEL_VALID;
                psCurrent->MVValid |= 1; /* (1<<0) */
            }
        }
        if ((Pos == 0) && (CurrentRowY == SliceStartRowY)) {
            psCurrent->ParamsValid |= MB_START_OF_SLICE;/* OPTI? */
        }
        /* Have to fill in the right hand row of 4x4 vectors into the the left block */
        if (Pos) {
            psCurrent->MVValid |= 72; /* (1<<3)+(1<<6) */
            psCurrent->ParamsValid |= 8; /* (1<<3) */
        }
        if (Pos == (int)(ctx->Width - 16)) {
            /* indicate the last MB in a row */
            psCurrent->ParamsValid |= MB_END_OF_ROW;
            /* are we the last mb in the slice? */
            if (YPos == (SliceStartRowY + SliceHeight - (MVEA_LRB_TOP_OFFSET + 16))) {
                psCurrent->ParamsValid |= MB_END_OF_SLICE;
                if (YPos == ctx->HeightMinus16MinusLRBTopOffset) {
                    psCurrent->ParamsValid |= MB_END_OF_PICTURE;
                }
            }
        }
        /* And now the below block
         * should do some kind of check to see if we are the first inter block,
         * as otherwise the vectors will be invalid!
         */
        if (VectorsValid) {
            if (YPos < ctx->HeightMinus16MinusLRBTopOffset) {
                psCurrent->MVValid |= 16; /* (1<<4) */

                if (YPos < ctx->HeightMinus32MinusLRBTopOffset) {
                    psCurrent->MVValid |= 32; /* (1<<5) */
                }
            }
        }

        /* Set up IPEMin and Max for coordinate X in the search reference region */
        /* And set up flags in SPEMax when needed */
        if (Pos <= 48) {
            psCurrent->IPEMin[0] = 48 - Pos;
            psCurrent->RealEdge |= SPE_EDGE_LEFT;
        } else {
            psCurrent->IPEMin[0] = 3;
        }

        if ((Pos + 48 + 16) > (int)ctx->Width) {
            psCurrent->IPEMax[0] = (47 + ctx->Width) - Pos; /* (112 - 1) - ((Pos + 48+16) - ctx->Width); */
            psCurrent->RealEdge |= SPE_EDGE_RIGHT;
        } else {
            psCurrent->IPEMax[0] = 108; /* (112 - 1) - 3; */
        }

        /* Set up IPEMin and Max for Y coordinate in the search reference region */
        /* And set up flags in SPEMax when needed */
        if (YPos <= 0) {
            psCurrent->IPEMin[1] = 0;
            psCurrent->RealEdge |= SPE_EDGE_TOP;
        } else {
            psCurrent->IPEMin[1] = 3;
        }

        /* Max Y */
        if (YPos > ctx->HeightMinusLRB_TopAndBottom_OffsetsPlus16) {
            psCurrent->IPEMax[1] = MVEA_LRB_SEARCH_HEIGHT - 1;
            psCurrent->RealEdge |= SPE_EDGE_BOTTOM;
        } else {
            psCurrent->IPEMax[1] = MVEA_LRB_SEARCH_HEIGHT - 4;
        }

        psCurrent->CurBlockAddr = ((IMG_UINT8)(((YPos + MVEA_LRB_TOP_OFFSET) - srcY) / 16) << 4) | 0x3;

        /* Setup the control register values These will get setup and transferred to a different location within
         * the macroblock parameter structure.  They are then read out of the esb by the mtx and used to control
         * the hardware units
         */
        psCurrent->IPEControl = ctx->IPEControl;

        switch (ctx->eCodec) {
        case IMG_CODEC_H263_NO_RC:
        case IMG_CODEC_H263_VBR:
        case IMG_CODEC_H263_CBR:
            lnc__setup_qpvalues_mpeg4(psCurrent, bySliceQP);
            psCurrent->JMCompControl = F_ENCODE(2, MVEA_CR_JMCOMP_MODE);
            psCurrent->VLCControl = F_ENCODE(3, TOPAZ_VLC_CR_CODEC) |
                                    F_ENCODE(IsIntra ? 0 : 1, TOPAZ_VLC_CR_SLICE_CODING_TYPE);
            break;
        case IMG_CODEC_MPEG4_NO_RC:
        case IMG_CODEC_MPEG4_VBR:
        case IMG_CODEC_MPEG4_CBR:
            lnc__setup_qpvalues_mpeg4(psCurrent, bySliceQP);
            psCurrent->JMCompControl = F_ENCODE(1, MVEA_CR_JMCOMP_MODE) |
                                       F_ENCODE(1, MVEA_CR_JMCOMP_AC_ENABLE);
            psCurrent->VLCControl = F_ENCODE(2, TOPAZ_VLC_CR_CODEC) |
                                    F_ENCODE(IsIntra ? 0 : 1, TOPAZ_VLC_CR_SLICE_CODING_TYPE);
            break;
        default:
        case IMG_CODEC_H264_NO_RC:
        case IMG_CODEC_H264_VBR:
        case IMG_CODEC_H264_CBR:
        case IMG_CODEC_H264_VCM:
            lnc__setup_qpvalue_h264(psCurrent, bySliceQP);
            psCurrent->JMCompControl = F_ENCODE(0, MVEA_CR_JMCOMP_MODE);
            psCurrent->VLCControl = F_ENCODE(1, TOPAZ_VLC_CR_CODEC) |
                                    F_ENCODE(IsIntra ? 0 : 1, TOPAZ_VLC_CR_SLICE_CODING_TYPE);
            break;
        }
    }

    psCurrent->RealEdge = 0;

}

void lnc_setup_slice_params(
    context_ENC_p  ctx,
    IMG_UINT16 YSliceStartPos,
    IMG_UINT16 SliceHeight,
    IMG_BOOL IsIntra,
    IMG_BOOL  VectorsValid,
    int bySliceQP)
{
    IMG_UINT16 Rows, CurrentRowY;

    Rows = SliceHeight / 16;
    CurrentRowY = YSliceStartPos;

    while (Rows) {
        lnc__setup_slice_row_params(
            ctx,
            IsIntra,
            CurrentRowY,
            YSliceStartPos,
            SliceHeight,
            VectorsValid, bySliceQP);

        CurrentRowY += 16;
        Rows--;
    }

}



IMG_UINT32 lnc__send_encode_slice_params(
    context_ENC_p ctx,
    IMG_BOOL IsIntra,
    IMG_UINT16 CurrentRow,
    IMG_BOOL DeblockSlice,
    IMG_UINT32 FrameNum,
    IMG_UINT16 SliceHeight,
    IMG_UINT16 CurrentSlice,
    IMG_UINT32 MaxSliceSize)
{
    SLICE_PARAMS *psSliceParams;
    IMG_UINT16 RowOffset;

    psb_buffer_p psCoded;
    object_surface_p ref_surface;
    psb_buffer_p psRef;
    lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;


    ref_surface = ctx->ref_surface;
    psRef = &ctx->ref_surface->psb_surface->buf;
    psCoded = ctx->coded_buf->psb_buffer;

    psSliceParams = (SLICE_PARAMS *)(cmdbuf->slice_params_p +
                                     CurrentSlice * ((sizeof(SLICE_PARAMS) + 15) & 0xfff0));

    psSliceParams->SliceHeight = SliceHeight;
    psSliceParams->SliceStartRowNum = CurrentRow / 16;

    /* We want multiple ones of these so we can submit multiple slices without having to wait for the next */
    psSliceParams->CodedDataPos = 0;
    psSliceParams->TotalCoded = 0;
    psSliceParams->Flags = 0;

#ifdef VA_EMULATOR
    psSliceParams->RefYStride = ref_surface->psb_surface->stride;
    psSliceParams->RefUVStride = ref_surface->psb_surface->stride;
    psSliceParams->RefYRowStride =  ref_surface->psb_surface->stride * 16;
    psSliceParams->RefUVRowStride = ref_surface->psb_surface->stride * 16 / 2;
#else
    psSliceParams->RefYStride = ref_surface->height * 16;
    psSliceParams->RefUVStride = ref_surface->height * 8;
    psSliceParams->RefYRowStride =  psSliceParams->RefYStride;
    psSliceParams->RefUVRowStride = psSliceParams->RefUVStride;
#endif

    psSliceParams->FCode = ctx->FCode;/* Not clear yet, This field is not appare in firmware doc */
    RowOffset = CurrentRow - 32;
    if (RowOffset <= 0)
        RowOffset = 0;
    if (RowOffset > (ctx->Height - 80))
        RowOffset = (ctx->Height - 80);

    psSliceParams->MaxSliceSize = MaxSliceSize;
    psSliceParams->NumAirMBs = ctx->num_air_mbs;
    /* DDKv145: 3 lsb of threshold used as spacing between AIR MBs */
    psSliceParams->AirThreshold = ctx->air_threshold + (FrameNum & 3) + 2;

    if (ctx->autotune_air_flag)
        psSliceParams->Flags |= AUTOTUNE_AIR;

    if (!IsIntra) {
        psSliceParams->Flags |= ISINTER_FLAGS;
    }
    if (DeblockSlice) {
        psSliceParams->Flags |= DEBLOCK_FRAME;
    }
    switch (ctx->eCodec) {
    case IMG_CODEC_H263_NO_RC:
    case IMG_CODEC_H263_VBR:
    case IMG_CODEC_H263_CBR:
        psSliceParams->Flags |= ISH263_FLAGS;
        break;
    case IMG_CODEC_MPEG4_NO_RC:
    case IMG_CODEC_MPEG4_VBR:
    case IMG_CODEC_MPEG4_CBR:
        psSliceParams->Flags |= ISMPEG4_FLAGS;
        break;
    case IMG_CODEC_H264_NO_RC:
    case IMG_CODEC_H264_VBR:
    case IMG_CODEC_H264_CBR:
    case IMG_CODEC_H264_VCM:
        psSliceParams->Flags |= ISH264_FLAGS;
        break;
    default:
        psSliceParams->Flags |= ISH264_FLAGS;
        drv_debug_msg(VIDEO_DEBUG_ERROR, "No format specified defaulting to h.264\n");
        break;
    }
    /* we should also setup the interleaving requirements based on the source format */
    if (ctx->eFormat != IMG_CODEC_PL12)
        psSliceParams->Flags |= INTERLEAVE_TARGET;

    cmdbuf = ctx->obj_context->lnc_cmdbuf;

    RELOC_SLICE_PARAMS(&(psSliceParams->RefYBase), 16 * RowOffset, psRef);
    RELOC_SLICE_PARAMS(&(psSliceParams->RefUVBase),
                       ref_surface->psb_surface->stride * ref_surface->height + (RowOffset * 16 / 2),
                       psRef);
    RELOC_SLICE_PARAMS(&(psSliceParams->CodedData), 0, psCoded);

    lnc_cmdbuf_insert_command(cmdbuf, MTX_CMDID_ENCODE_SLICE, 2, (CurrentSlice << 2) | (IsIntra & 0x3));
    RELOC_CMDBUF(cmdbuf->cmd_idx++,
                 CurrentSlice *((sizeof(SLICE_PARAMS) + 15) & 0xfff0),
                 &cmdbuf->slice_params);

    return 0;
}



/*
 * Function Name      : Reset_EncoderParams
 * Description        : Reset Above & Below Params at the Start of Intra frame
 */
void lnc_reset_encoder_params(context_ENC_p ctx)
{
    unsigned char *Add_Below, *Add_Above;
    lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;

    /* all frames share the same Topaz param, in_param/aboveparam/bellow
     * map it only when necessary
     */
    if (cmdbuf->topaz_above_bellow_params_p == NULL) {
        VAStatus vaStatus = psb_buffer_map(cmdbuf->topaz_above_bellow_params, &cmdbuf->topaz_above_bellow_params_p);
        if (vaStatus != VA_STATUS_SUCCESS) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "map topaz MTX_CURRENT_IN_PARAMS failed\n");
            return;
        }
    }

    Add_Below = cmdbuf->topaz_above_bellow_params_p + ctx->bellow_params_ofs;
    memset(Add_Below, 0, ctx->bellow_params_size);

    Add_Above = cmdbuf->topaz_above_bellow_params_p + ctx->above_params_ofs;
    memset(Add_Above, 0, ctx->above_params_size);
}