C++程序  |  1478行  |  59.96 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:
 *    Shengquan Yuan  <shengquan.yuan@intel.com>
 *
 */

#include "psb_MPEG2.h"
#include "psb_def.h"
#include "psb_surface.h"
#include "psb_cmdbuf.h"
#include "psb_drv_debug.h"

#include "hwdefs/reg_io2.h"
#include "hwdefs/msvdx_offsets.h"
#include "hwdefs/msvdx_cmds_io2.h"
#include "hwdefs/msvdx_vdmc_reg_io2.h"
#include "hwdefs/msvdx_vec_reg_io2.h"
#include "hwdefs/msvdx_vec_mpeg2_reg_io2.h"
#include "hwdefs/dxva_fw_ctrl.h"

#include <stdlib.h>
#include <stdint.h>
#include <string.h>


/* TODO: for interlace
 * bit 0: first vector forward
 * bit 1: first vector backward
 * bit 2: second vector forward
 * bit 3: second vector backward
 */
#define MBPARAM_MvertFieldSel_3(ptr)    (((ptr)->motion_vertical_field_select & 0x8) >> 3)
#define MBPARAM_MvertFieldSel_2(ptr)    (((ptr)->motion_vertical_field_select & 0x4) >> 2)
#define MBPARAM_MvertFieldSel_1(ptr)    (((ptr)->motion_vertical_field_select & 0x2) >> 1)
#define MBPARAM_MvertFieldSel_0(ptr)    (((ptr)->motion_vertical_field_select & 0x1) >> 0)
#define MBPARAM_MotionType(ptr)         (((ptr)->macroblock_modes.bits.frame_motion_type << 1) | (ptr)->macroblock_modes.bits.field_motion_type)
#define MBPARAM_MotionBackward(ptr)     (((ptr)->macroblock_type & VA_MB_TYPE_MOTION_BACKWARD)?1:0)
#define MBPARAM_MotionForward(ptr)      (((ptr)->macroblock_type & VA_MB_TYPE_MOTION_FORWARD)?1:0)
#define MBPARAM_IntraMacroblock(ptr)    ((ptr)->macroblock_type & VA_MB_TYPE_MOTION_INTRA )
#define MBPARAM_CodedBlockPattern(ptr)  ((ptr)->coded_block_pattern << 6) /* align with VA code */
#define MBPARAM_MBskipsFollowing(ptr)   ((ptr)->num_skipped_macroblocks)

typedef enum { MB_CODE_TYPE_I , MB_CODE_TYPE_P , MB_CODE_TYPE_B , MB_CODE_TYPE_GMC } eMB_CODE_TYPE;

/* Constants */
#define PICTURE_CODING_I                0x01
#define PICTURE_CODING_P                0x02
#define PICTURE_CODING_B                0x03

#define CODEC_MODE_MPEG2                3
#define CODEC_PROFILE_MPEG2_MAIN        1

/* picture structure */
#define TOP_FIELD                       1
#define BOTTOM_FIELD                    2
#define FRAME_PICTURE                   3

#define INTRA_MB_WORST_CASE             6
#define INTER_MB_WORST_CASE             100

static  const uint32_t  pict_libva2msvdx[] = {0,/* Invalid picture type */
        0,/* I picture */
        1,/* P picture */
        2,/* B pricture */
        3
                                             };/* Invalid picture type */


static  const uint32_t  ft_libva2msvdx[] = {0,/* Invalid picture type   */
        0,/* Top field */
        1,/* Bottom field */
        2
                                           };/* Frame picture     */


struct context_MPEG2MC_s {
    object_context_p obj_context; /* back reference */

    VAMacroblockParameterBufferMPEG2 *mb_param ;        /* Pointer to the mbCtrl structure */
    uint32_t mb_in_buffer;  /* Number of MBs in current buffer */
    uint32_t mb_first_addr; /* MB address of first mb in buffer */

    uint32_t picture_coding_type;
    uint32_t picture_structure;

    uint32_t coded_picture_width;
    uint32_t coded_picture_height;

    uint32_t picture_width_mb; /* in macroblocks */
    uint32_t picture_height_mb; /* in macroblocks */
    uint32_t size_mb; /* in macroblocks */

    VAPictureParameterBufferMPEG2 *pic_params;

    object_surface_p forward_ref_surface;
    object_surface_p backward_ref_surface;

    uint32_t ref_indexA;
    uint32_t ref_indexB;

    uint32_t coded_picture_size;
    uint32_t display_picture_size;
    uint32_t operation_mode;

    uint32_t *lldma_idx; /* Index in command stream for LLDMA pointer */
    uint32_t residual_pendingDMA;
    IMG_INT32   residual_sent;

    psb_buffer_p residual_buf;
    uint32_t blk_in_buffer;/* buffer elements */
    uint32_t blk_size;/* buffer elements size */

    uint32_t fstmb_slice;
};

typedef struct context_MPEG2MC_s *context_MPEG2MC_p;

#define INIT_CONTEXT_MPEG2MC    context_MPEG2MC_p ctx = (context_MPEG2MC_p) obj_context->format_data
#define SURFACE(id)     ((object_surface_p) object_heap_lookup( &ctx->obj_context->driver_data->surface_heap, id ))

static void     psb__MPEG2MC_send_interPB_prediction(
    context_MPEG2MC_p ctx,
    psb_cmdbuf_p const  cmdbuf,
    VAMacroblockParameterBufferMPEG2 * const    mb_param,
    int second_pred
)
{
    uint32_t    cmd;
    uint32_t    blk_size;
    uint32_t    pred_offset;

    /* Determine residual data's block size (16x8 or 16x16)     */
    if (FRAME_PICTURE == ctx->picture_structure) {
        if ((1 == MBPARAM_MotionType(mb_param)) ||  /* Field MC */
            (3 == MBPARAM_MotionType(mb_param))) { /* Dual Prime        */
            blk_size = 1;       /* 16 x 8 */
        } else {
            blk_size = 0;       /* 16 x 16 */
        }
    } else {
        if (2 == MBPARAM_MotionType(mb_param)) { /* Non-Frame MC        */
            blk_size = 1;       /* 16 x 8 */
        } else {
            blk_size = 0; /* 16 x 16 */
        }
    }

    /* Determine whether this is for 1st MV or 2nd MV */
    if (TRUE == second_pred) {
        pred_offset = 8;
    } else {
        pred_offset = 0;
    }

    cmd = 0;

    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, INTER_PRED_BLOCK_SIZE,   blk_size);

    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_A, ctx->ref_indexA);
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_B, ctx->ref_indexB);

    if (3 == MBPARAM_MotionType(mb_param)) {  /* Dual Prime */
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_A_VALID, 1);
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_B_VALID, 1);
    } else {
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_A_VALID,
                          MBPARAM_MotionForward(mb_param));
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_B_VALID,
                          MBPARAM_MotionBackward(mb_param));
    }

    if (FRAME_PICTURE == ctx->picture_structure) {
        /* Frame picture processing */
        if ((1 == MBPARAM_MotionType(mb_param)) ||  /* Field MC */
            (3 == MBPARAM_MotionType(mb_param))) { /* Dual Prime        */
            if ((1 == MBPARAM_MotionForward(mb_param)) ||
                (3 == MBPARAM_MotionType(mb_param))) {  /* Dual Prime */
                if (second_pred) {
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION,
                                      REF_INDEX_FIELD_A, MBPARAM_MvertFieldSel_2(mb_param));
                } else {
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION,
                                      REF_INDEX_FIELD_A, MBPARAM_MvertFieldSel_0(mb_param));
                }
            }

            if ((1 == MBPARAM_MotionBackward(mb_param)) ||
                (3 == MBPARAM_MotionType(mb_param))) {  /* Dual Prime   */
                if (second_pred) {
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION,
                                      REF_INDEX_FIELD_B, MBPARAM_MvertFieldSel_3(mb_param));
                } else {
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION,
                                      REF_INDEX_FIELD_B, MBPARAM_MvertFieldSel_1(mb_param));
                }
            }
        }
    } else {
        /* Field picture processing */
        if ((0 == MBPARAM_MotionForward(mb_param)) &&
            (0 == MBPARAM_MotionBackward(mb_param))) {
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_A, ctx->ref_indexA);
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_B, ctx->ref_indexB);
        }

        if (1 == MBPARAM_MotionForward(mb_param)) {
            if (second_pred) {
                if ((0 == MBPARAM_MvertFieldSel_2(mb_param)) &&
                    (PICTURE_CODING_P == ctx->picture_coding_type)) { /* Top field of this frame        */
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_A, ctx->ref_indexB);
                } else { /* Bottom field of previous frame*/
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_A, ctx->ref_indexA);
                }

                REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_FIELD_A,
                                  MBPARAM_MvertFieldSel_2(mb_param));
            } else {
                if (((ctx->picture_structure == BOTTOM_FIELD) != MBPARAM_MvertFieldSel_0(mb_param)) &&
                    (PICTURE_CODING_P == ctx->picture_coding_type)) {   /* Top field of this frame      */
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_A, ctx->ref_indexB);
                } else { /* Bottom field of previous frame*/
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_A, ctx->ref_indexA);
                }

                REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_FIELD_A,
                                  MBPARAM_MvertFieldSel_0(mb_param));
            }
        }


        if (1 == MBPARAM_MotionBackward(mb_param)) {
            if (second_pred) {
                if ((1 == MBPARAM_MvertFieldSel_3(mb_param)) &&
                    (PICTURE_CODING_P == ctx->picture_coding_type)) { /* Top field of this frame        */
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_B, ctx->ref_indexA);
                } else {        /* Bottom field of previous frame*/
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_B, ctx->ref_indexB);
                }

                REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_FIELD_B,
                                  MBPARAM_MvertFieldSel_3(mb_param));
            } else {
                if ((1 == MBPARAM_MvertFieldSel_1(mb_param)) &&
                    (PICTURE_CODING_P == ctx->picture_coding_type)) { /* Top field of this frame        */
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_B, ctx->ref_indexA);
                } else { /* Bottom field of previous frame*/
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_B, ctx->ref_indexB);
                }

                REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_FIELD_B,
                                  MBPARAM_MvertFieldSel_1(mb_param));
            }
        }
    }

    /* Dual Prime */
    if (3 == MBPARAM_MotionType(mb_param) && (ctx->picture_structure != FRAME_PICTURE)) {
        if (ctx->picture_structure == TOP_FIELD) {
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_A, 0);
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_FIELD_A, 0);
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_FIELD_B, 1);
        } else {
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_A, 1);
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_FIELD_A, 1);
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTER_BLOCK_PREDICTION, REF_INDEX_FIELD_B, 0);
        }
    }

    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, INTER_BLOCK_PREDICTION) + pred_offset, cmd);
}

typedef struct _PSB_MVvalue {
    short  horz;
    short  vert;
} psb_MVvalue, *psb_MVvalue_p;

#define MV_LIBVA2PSB(mb_param)                  \
do {                                            \
    MVector[0].horz  = mb_param->PMV[0][0][0];  \
    MVector[0].vert  = mb_param->PMV[0][0][1];  \
                                                \
    MVector[1].horz  = mb_param->PMV[0][1][0];  \
    MVector[1].vert  = mb_param->PMV[0][1][1];  \
                                                \
    MVector[2].horz  = mb_param->PMV[1][0][0];  \
    MVector[2].vert  = mb_param->PMV[1][0][1];  \
                                                \
    MVector[3].horz  = mb_param->PMV[1][1][0];  \
    MVector[3].vert  = mb_param->PMV[1][1][1];  \
} while (0)

static void     psb__MPEG2MC_send_motion_vectores(
    context_MPEG2MC_p   const   ctx,
    psb_cmdbuf_p cmdbuf,
    VAMacroblockParameterBufferMPEG2 * const mb_param
)
{
    uint32_t            cmd = 0;
    uint32_t            MV1Address = 0;
    uint32_t            MV2Address = 0;

    psb_MVvalue  MVector[4];

    MV_LIBVA2PSB(mb_param);

    MV1Address = 0x00;
    MV2Address = 0x00;

    if (FRAME_PICTURE == ctx->picture_structure) {
        /* FRAME PICTURE PROCESSING */
        if (2 == MBPARAM_MotionType(mb_param)) {  /* Frame MC */
            if ((0 == MBPARAM_MotionForward(mb_param)) &&
                (0 == MBPARAM_MotionBackward(mb_param))) {
                cmd = 0;
                REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_X, 0);
                REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, 0);
                psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, MOTION_VECTOR) , cmd);
            } else {
                if (1 == MBPARAM_MotionForward(mb_param)) {
                    cmd = 0;
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_X, MVector[0].horz << 1);
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[0].vert << 1);
                    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, MOTION_VECTOR) , cmd);
                }

                if (1 == MBPARAM_MotionBackward(mb_param)) {
                    cmd = 0;
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_X, MVector[1].horz << 1);
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[1].vert << 1);
                    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, MOTION_VECTOR) + 0x10 , cmd);
                }
            }
        } else  {
            if ((1 == MBPARAM_MotionType(mb_param)) ||  /* Field MC */
                (3 == MBPARAM_MotionType(mb_param))) {  /* Dual Prime */
                /*
                 * Vertical motion vectors for fields located in frame pictures
                 * should be divided by 2 (MPEG-2 7.6.3.1). Thus the original value
                 * contained in the stream is equivalent to 1/4-pel     format; the
                 * resolution required by MSVDX.
                 */
                if ((1 == MBPARAM_MotionForward(mb_param)) ||
                    (3 == MBPARAM_MotionType(mb_param))) {  /* Dual Prime       */
                    cmd = 0;
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_X, MVector[0].horz << 1);
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[0].vert);
                    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, MOTION_VECTOR) , cmd);
                }

                if ((1 == MBPARAM_MotionBackward(mb_param)) ||
                    (3 == MBPARAM_MotionType(mb_param))) {  /* Dual Prime */
                    cmd = 0;

                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_X, MVector[1].horz << 1);

                    if (3 == MBPARAM_MotionType(mb_param)) {  /* Dual Prime */
                        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[1].vert << 1);
                    } else {
                        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[1].vert);
                    }
                    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, MOTION_VECTOR) + 0x10 , cmd);
                }

                /* Fields and Dual Prime are 16x8 and need 2nd inter_block_pred cmd     */
                psb__MPEG2MC_send_interPB_prediction(ctx, cmdbuf, mb_param, IMG_TRUE);

                if ((1 == MBPARAM_MotionForward(mb_param)) ||
                    (3 == MBPARAM_MotionType(mb_param))) {  /* Dual Prime */
                    cmd = 0;
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_X, MVector[2].horz << 1);
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[2].vert);
                    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, MOTION_VECTOR) + 0x40 , cmd);
                }

                if ((1 == MBPARAM_MotionBackward(mb_param)) ||
                    (3 == MBPARAM_MotionType(mb_param))) {      /* Dual Prime                   */
                    cmd = 0;
                    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_X, MVector[3].horz << 1);
                    if (3 == MBPARAM_MotionType(mb_param)) {    /* Dual Prime                   */
                        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[3].vert << 1);
                    } else {
                        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[3].vert);
                    }
                    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, MOTION_VECTOR) + 0x50 , cmd);
                }
            }
        }
    } else {
        /* FIELD PICTURE PROCESSING */
        int MV0index = 0, MV1index = 1;

        if ((ctx->picture_structure == BOTTOM_FIELD) && (3 == MBPARAM_MotionType(mb_param))) {
            MV0index = 1;
            MV1index = 0;
        }

        if ((1 == MBPARAM_MotionForward(mb_param)) ||   /* Forward MV   */
            (3 == MBPARAM_MotionType(mb_param))) { /* Dual Prime        */
            cmd = 0;
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_X, MVector[MV0index].horz << 1);
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[MV0index].vert << 1);
            psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, MOTION_VECTOR) , cmd);

        }

        if ((1 == MBPARAM_MotionBackward(mb_param)) ||  /* Backward MV  */
            (3 == MBPARAM_MotionType(mb_param))) { /* Dual Prime        */
            cmd = 0;
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_X, MVector[MV1index].horz << 1);
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[MV1index].vert << 1);
            psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, MOTION_VECTOR)  + 0x10, cmd);
        }

        if (2 == MBPARAM_MotionType(mb_param)) { /* 16x8 MC */
            psb__MPEG2MC_send_interPB_prediction(ctx, cmdbuf, mb_param, IMG_TRUE);

            if ((1 == MBPARAM_MotionForward(mb_param)) ||  /* Forward MV */
                (3 == MBPARAM_MotionType(mb_param))) {  /* Dual Prime */
                cmd = 0;
                REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_X, MVector[2].horz << 1);
                REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[2].vert << 1);
                psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, MOTION_VECTOR)  + 0x40, cmd);
            }

            if ((1 == MBPARAM_MotionBackward(mb_param)) ||      /* Backward MV                          */
                (3 == MBPARAM_MotionType(mb_param))) {  /* Dual Prime                           */
                cmd = 0;
                REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_X, MVector[3].horz << 1);
                REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MOTION_VECTOR, MV_Y, MVector[3].vert << 1);

                psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, MOTION_VECTOR)  + 0x50, cmd);
            }
        }
    }
}


/* Send macroblock_number command to MSVDX. */
static void     psb__MPEG2MC_send_mb_number(
    context_MPEG2MC_p   const   ctx,
    psb_cmdbuf_p cmdbuf,
    const uint32_t mb_addr,
    const uint32_t motion_type,
    const eMB_CODE_TYPE MB_coding_type
)
{
    uint32_t cmd;
    uint32_t mb_x;

    /* 3.3.21.  Macroblock Number */
    cmd = 0;

    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_NUMBER, MB_ERROR_FLAG, 0);

    /*
      MB_FIELD_CODE (MC + VDEB): Indicate if the macroblock is field predicted (when = 1),
                                 or frame predicted (when VDEB = 0).
          MPEG2: For Interlaced frame, derived from ‘frame_motion_type’, else same
                 frame/filed type as SLICE_FIELD_TYPE

     */
    if (FRAME_PICTURE == ctx->picture_structure) {
        if ((0 == motion_type)  ||
            (2 == motion_type)) {
            /* MB is frame predicted    */
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_NUMBER, MB_FIELD_CODE, 0);
        } else { /* MB is field predicted       */
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_NUMBER, MB_FIELD_CODE, 1);
        }
    } else { /* MB is field predicted   */
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_NUMBER, MB_FIELD_CODE, 1);
    }

    ASSERT((0 != ctx->picture_coding_type) && (4 != ctx->picture_coding_type));

    /*
      MB_CODE_TYPE (MC + VDEB): Indicate macroblock type is I, P, B or MPEG4 S(GMC).
                  0x0: I or SI macroblock
                  0x1: P or SP macroblock
                  0x2: B macroblock
                  0x3: MPEG4 GMC
           MPEG2: Derived from ‘macroblock_type’
     */
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_NUMBER, MB_CODE_TYPE, (uint32_t) MB_coding_type);


    /*
      MB_NO_Y (MC + VDEB): Vertical offset of current Macroblock (where 0 = topmost macroblock of picture)
                           Derived from macroblock number divided by picture width in macroblocks.
      MB_NO_X (MC + VDEB): Horizontal offset of current Macroblock (where 0 = leftmost macroblock of picture)
                           Derived from macroblock number mod picture width in macroblocks.
     */
    if ((FRAME_PICTURE != ctx->picture_structure) &&
        (mb_addr / ctx->picture_width_mb)) {
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_NUMBER, MB_NO_Y, (mb_addr / ctx->picture_width_mb) * 2);
    } else {
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_NUMBER, MB_NO_Y, mb_addr / ctx->picture_width_mb);
    }

    mb_x = mb_addr % ctx->picture_width_mb;
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_NUMBER, MB_NO_X, mb_x);
    /*
      Only defined for current MB, set to 0 for above1 and above2.
              Indicate if MB is on Left Hand Side of slice or picture
              0: MB is not on left hand side of slice or picture
              1: MB is on left hand side of slice or picture
    */
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_NUMBER , MB_SLICE_LHS, ctx->fstmb_slice || (mb_x == 0));
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_NUMBER , MB_SLICE_TOP, 1);

    psb_cmdbuf_reg_set(cmdbuf, REGISTER_OFFSET(MSVDX_CMDS, MACROBLOCK_NUMBER) , cmd);
}

static void psb__MPEG2MC_finalise_residDMA(
    context_MPEG2MC_p   const   ctx
)
{
    uint32_t *save_idx = ctx->obj_context->cmdbuf->cmd_idx;
    ctx->obj_context->cmdbuf->cmd_idx = ctx->lldma_idx;
    if (ctx->residual_pendingDMA) {
#if 0
        if (ctx->residual_pendingDMA != (ctx->blk_size * ctx->blk_in_buffer)) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "psb__MPEG2MC_finalise_residDMA:residual_pendingDMA=%d(block:%d),"
                               "actual data size=%d (block:%d)\n",
                               ctx->residual_pendingDMA, ctx->residual_pendingDMA / ctx->blk_size,
                               ctx->blk_size * ctx->blk_in_buffer,
                               ctx->blk_in_buffer);
        }
#endif

        psb_cmdbuf_lldma_write_cmdbuf(ctx->obj_context->cmdbuf,
                                      ctx->residual_buf,
                                      ctx->residual_sent,
                                      ctx->residual_pendingDMA,
                                      0,
                                      LLDMA_TYPE_RESIDUAL);
    } else {
        //*ctx->obj_context->cmdbuf->cmd_idx = CMD_NOP;
        *ctx->obj_context->cmdbuf->cmd_idx = 0xf000000;
    }
    ctx->obj_context->cmdbuf->cmd_idx = save_idx;
    ctx->residual_sent += ctx->residual_pendingDMA;
    ctx->residual_pendingDMA = 0;
}

static void psb__MPEG2MC_check_segment_residDMA(
    context_MPEG2MC_p   const   ctx,
    uint32_t min_space
)
{
    if (psb_cmdbuf_segment_space(ctx->obj_context->cmdbuf) < min_space) {
        psb__MPEG2MC_finalise_residDMA(ctx);

        psb_cmdbuf_next_segment(ctx->obj_context->cmdbuf);

        ctx->lldma_idx = ctx->obj_context->cmdbuf->cmd_idx++; /* Insert the LLDMA record here later */
    }
}


/* Send residual difference data to MSVDX. */
static void     psb__MPEG2MC_send_residual(
    context_MPEG2MC_p   ctx,
    uint32_t    pattern_code)
{
    uint8_t pattern_code_byte = (uint8_t)(pattern_code >> 6);
    uint8_t blocks = 0;

    while (pattern_code_byte) {
        blocks += (pattern_code_byte & 1);
        pattern_code_byte >>= 1;
    }

    if (PICTURE_CODING_I == ctx->picture_coding_type) {
        ctx->residual_pendingDMA += blocks * (8 * 8); /* 8bit */
    } else {
        /* We do not suport ConfigSpatialResid8==1  */
        /* ASSERT(ConfigSpatialResid8 == 0); */
        ctx->residual_pendingDMA += blocks * (8 * 8) * 2;/*  16 bit */
    }
}


static void     psb__MPEG2MC_send_slice_parameters(
    context_MPEG2MC_p   const   ctx
)
{
    psb_cmdbuf_p cmdbuf = ctx->obj_context->cmdbuf;
    uint32_t cmd;

    ctx->lldma_idx = ctx->obj_context->cmdbuf->cmd_idx++; /* Insert the LLDMA record here later */

    psb_cmdbuf_reg_start_block(cmdbuf, 0);

    /* 3.3.19.  Slice Params*/
    /*
      3:2 SLICE_FIELD_TYPE (MC+VDEB)  Indicate if slice is a frame, top fie
                      0x0: Top field picture
                      0x1: Bottom field picture
                      0x2: Frame picture
      1:0 SLICE_CODE_TYPE  (MC+VDEB)  Indicate if it is an I, P, B  slice
                      0x0: I slice
                      0x1: P slice
                      0x2: B slice
                      0x3: S(GMC) (MPEG4 only)
     */
    cmd = 0;
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, SLICE_PARAMS, SLICE_FIELD_TYPE,
                      ft_libva2msvdx[ctx->picture_structure]);
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, SLICE_PARAMS, SLICE_CODE_TYPE,
                      pict_libva2msvdx[ctx->picture_coding_type]);
    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, SLICE_PARAMS), cmd);

    psb_cmdbuf_reg_end_block(cmdbuf);
}

static void psb__MPEG2MC_send_slice_picture_endcommand(
    context_MPEG2MC_p   const   ctx,
    int end_picture
)
{
    psb_cmdbuf_p cmdbuf = ctx->obj_context->cmdbuf;
    uint32_t cmd;

    /*
      3.3.22.         End Slice/Picture
      Offset:           0x0404
      This command is sent at the end of a slice or picture. The final macroblock of slice/picture will not be
      processed until this command is sent. The End Slice/Picture commands can be sent more than once
      at the end of a slice (provided no other commands are interleaved).
      If the command is sent more than once at the end of a slice, the first command should indicate the
      end of the slice (no data bits set) with repeat commands used to indicate end of slice, flushing VDEB
      buffers or picture end. The FLUSH_VDEB_BUFFERS bit should not be set in any repeat commands
      sent after a command in which the PICTURE_END bit is set.
      31:2 -                       Reserved
      1    FLUSH_VDEB_BUFFERS(VDEB) If set, indicates VDEB should flush its internal buffers to system memory
                                    when slice processing is complete
      0    PICTURE_END(MC + VDEB)   If set, indicates both Picture and Slice end, otherwise Slice end
     */
    psb_cmdbuf_reg_start_block(cmdbuf, 0);

    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, END_SLICE_PICTURE), 0);

    if (end_picture) {
        cmd = 0;
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, END_SLICE_PICTURE, PICTURE_END, end_picture);
        psb_cmdbuf_reg_set(cmdbuf, REGISTER_OFFSET(MSVDX_CMDS, END_SLICE_PICTURE), cmd);
    }

    psb_cmdbuf_reg_end_block(cmdbuf);
}

static void     psb__MPEG2MC_send_highlevel_commands(
    context_MPEG2MC_p   const   ctx
)
{
    psb_cmdbuf_p cmdbuf = ctx->obj_context->cmdbuf;
    psb_surface_p target_surface = ctx->obj_context->current_render_target->psb_surface;

    psb_cmdbuf_reg_start_block(cmdbuf, 0);

    /* 3.3.1.   Display Picture Size */
    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, DISPLAY_PICTURE_SIZE), ctx->display_picture_size);

    /* 3.3.2.   Coded Picture Size*/
    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, CODED_PICTURE_SIZE), ctx->coded_picture_size);

    /* 3.3.3.   Operating Mode */
    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, OPERATING_MODE), ctx->obj_context->operating_mode);

    /* 3.3.4.   Luma Reconstructed Picture Base Address */
    psb_cmdbuf_reg_set_RELOC(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, LUMA_RECONSTRUCTED_PICTURE_BASE_ADDRESSES),
                             &target_surface->buf, 0);

    /* 3.3.5.   Chroma Reconstructed Picture Base Address */
    psb_cmdbuf_reg_set_RELOC(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, CHROMA_RECONSTRUCTED_PICTURE_BASE_ADDRESSES),
                             &target_surface->buf, target_surface->chroma_offset);

    /* 3.3.13.  Reference Picture Base Addresses */
    /*
      Reference Picture Base Addresses
              Offset:          0x0100 + N*8 = Luma base addresses
              Offset:          0x0104 + N*8 = Chroma base addresses
      This register can store up to 16 luma picture base addresses and 16 chroma picture base addresses.
      Luma and chroma ref base registers are interleaved.

      Bit      Symbol                    Used     Description
              31:12    LUMA_REF_BASE_ADDR        MC       Luma picture base byte address [31:12]
              11:0     -                                  Reserved
      Bit      Symbol                     Used     Description
              31:12    CHROMA_REF_BASE_ADDR       MC       Chroma picture base byte address [31:12]
              11:0     -                                   Reserved
      -   In MPEG2, the registers at N=0 are always used to store the base address of the luma and
      chroma buffers of the most recently decoded reference picture. The registers at N=1 are used
      to store the base address of the luma and chroma buffers of the older reference picture, if
      more than one reference picture is used. This means that when decoding a P picture the
      forward reference picture’s address is at index 0. When decoding a B-picture the backward
      reference picture’s address is at index 0 and the address of the forward reference picture –
      which was decoded earlier than the backward reference – is at index 1.
     */

    /* WABA: backward / forward refs are always set, even when they aren't strictly needed */
    psb_surface_p forward_surface = ctx->forward_ref_surface->psb_surface;
    psb_surface_p backward_surface = ctx->backward_ref_surface->psb_surface;

    if (backward_surface) {
        psb_cmdbuf_reg_set_RELOC(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, REFERENCE_PICTURE_BASE_ADDRESSES) + (0 * 8),
                                 &backward_surface->buf, 0);

        psb_cmdbuf_reg_set_RELOC(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, REFERENCE_PICTURE_BASE_ADDRESSES) + 4 + (0 * 8),
                                 &backward_surface->buf, backward_surface->chroma_offset);
    }
    if (forward_surface) {
        psb_cmdbuf_reg_set_RELOC(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, REFERENCE_PICTURE_BASE_ADDRESSES) + (1 * 8),
                                 &forward_surface->buf, 0);

        psb_cmdbuf_reg_set_RELOC(cmdbuf , REGISTER_OFFSET(MSVDX_CMDS, REFERENCE_PICTURE_BASE_ADDRESSES) + 4 + (1 * 8),
                                 &forward_surface->buf, forward_surface->chroma_offset);
    }

    /*
     * VDMC_RESIDUAL_DIRECT_INSERT_CONTROL, spec p 159
     * 0 VDMC_RESIDUAL_DIRECT_CONTROL (VDMC) residual direct insert control.
     *           Control insertion of spatial residual data. When set to 1 residual
     *           data taken from writes to VDMC_RESIDUAL_DIRECT_INSERT_DATA
     *           when set to 0, residual data taken from vEC
     */
    psb_cmdbuf_reg_set(cmdbuf , REGISTER_OFFSET(MSVDX_VDMC, CR_VDMC_RESIDUAL_DIRECT_INSERT_CONTROL), 1);

    psb_cmdbuf_reg_end_block(cmdbuf);
}



/* Control building of the MSVDX command buffer for a B and P pictures.*/
static void     psb__MPEG2MC_interPB_mb(
    context_MPEG2MC_p const     ctx,
    VAMacroblockParameterBufferMPEG2 * const    mb_param
)
{
    psb_cmdbuf_p cmdbuf = ctx->obj_context->cmdbuf;
    uint32_t cmd;

    psb_cmdbuf_reg_start_block(cmdbuf, 0);

    /* 3.3.21.  Macroblock Number */
    psb__MPEG2MC_send_mb_number(ctx, cmdbuf, mb_param->macroblock_address, MBPARAM_MotionType(mb_param),
                                MBPARAM_IntraMacroblock(mb_param) ? MB_CODE_TYPE_I : pict_libva2msvdx[ctx->picture_coding_type]
                               );

    cmd = 0;
    /* Only used for direct insert of residual data;-
       00 = 8-bit signed data
       01 = 8-bit unsigned data
       10 = 16-bit signed
       11 = Reserved
    */
    /* TODO:: ASSERT(ConfigSpatialResid8==0 ); */
    if (MBPARAM_IntraMacroblock(mb_param)) {
        if (1/* TODO:: ConfigIntraResidUnsigned == 0 */) {
            /* sent as as 16 bit signed relative to 128*/
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, VA_ADD_128, 1);
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, VA_DATA_FORMAT, 2);
        } else { /* ConfigIntraResidUnsigned == 1 */
            /* 16 bit unsigned unsigned relative to 0*/
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, VA_ADD_128, 0);
            REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, VA_DATA_FORMAT,       2);
        }
    } else {
        /* For non-intra in Inter frames : 16 bit signed */
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, VA_ADD_128, 0);
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, VA_DATA_FORMAT, 2);
    }


    if (FRAME_PICTURE == ctx->picture_structure) {
        /* mb_param->macroblock_modes.bits.dct_type =1: field DCT */
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, RESIDUAL_FIELD_CODED, mb_param->macroblock_modes.bits.dct_type ? 1 : 0);
    } else {
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, RESIDUAL_FIELD_CODED, 1);
    }

    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, CR_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x40) ? 1 : 0));
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, CB_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x80) ? 1 : 0));
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, Y3_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x100) ? 1 : 0));
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, Y2_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x200) ? 1 : 0));
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, Y1_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x400) ? 1 : 0));
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, Y0_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x800) ? 1 : 0));
    psb_cmdbuf_reg_set(cmdbuf, REGISTER_OFFSET(MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT) , cmd);

    /* Send Residuals   */
    if (MBPARAM_IntraMacroblock(mb_param)) {
        cmd = 0;
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTRA_BLOCK_PREDICTION, INTRA_PRED_MODE0, 0);
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTRA_BLOCK_PREDICTION, INTRA_PRED_MODE1, 0);
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTRA_BLOCK_PREDICTION, INTRA_PRED_MODE2, 0);
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTRA_BLOCK_PREDICTION, INTRA_PRED_MODE3, 0);
        psb_cmdbuf_reg_set(cmdbuf, REGISTER_OFFSET(MSVDX_CMDS, INTRA_BLOCK_PREDICTION) , cmd);

        psb__MPEG2MC_send_residual(ctx, MBPARAM_CodedBlockPattern(mb_param));
    } else {
        psb__MPEG2MC_send_interPB_prediction(ctx, cmdbuf, mb_param, IMG_FALSE);

        /* Send motion vectors  */
        psb__MPEG2MC_send_motion_vectores(ctx, cmdbuf, mb_param);

        psb__MPEG2MC_send_residual(ctx, MBPARAM_CodedBlockPattern(mb_param));
    }

    psb_cmdbuf_reg_end_block(cmdbuf);
}



/* Process Macroblocks of a B or P picture.*/
static VAStatus psb__MPEG2MC_process_mbs_interPB(
    context_MPEG2MC_p   const   ctx
)
{
    uint32_t    skip_count = 0;
    VAMacroblockParameterBufferMPEG2 *mb_param  = NULL;
    VAMacroblockParameterBufferMPEG2 mbparam_skip;

    uint32_t mb_pending = ctx->mb_in_buffer;
    uint32_t mb_addr = ctx->mb_first_addr;
    uint32_t mb_last = mb_addr + mb_pending;
    mb_param = (VAMacroblockParameterBufferMPEG2 *) ctx->mb_param;

    /* Proccess all VA_MBctrl_P_HostResidDiff_1 structure in the buffer
     * - this may genererate more macroblocks due to skipped
     */
    while (mb_pending || skip_count) {
        uint32_t mb_in_buffer = (ctx->picture_width_mb);
        psb_cmdbuf_p cmdbuf;
        unsigned char *cmd_start;

        ctx->fstmb_slice = IMG_TRUE;

        psb_context_get_next_cmdbuf(ctx->obj_context);
        cmdbuf = ctx->obj_context->cmdbuf;
        cmd_start = (unsigned char *) cmdbuf->cmd_idx;

        /* Build the high-level commands */
        psb__MPEG2MC_send_highlevel_commands(ctx);

        psb__MPEG2MC_send_slice_parameters(ctx);

        /* Process all the macroblocks in the slice */
        while ((mb_pending || skip_count) && mb_in_buffer--) {
            /* Check for segment space - do we have space for at least one more
             * worst case InterMB plus completion
            */
            psb__MPEG2MC_check_segment_residDMA(ctx, INTER_MB_WORST_CASE + 2);

            if (skip_count) {  /* Skipped macroblock processing */
                mbparam_skip.macroblock_address++;

                ASSERT(mb_param->macroblock_address < ctx->size_mb);
                ASSERT(mbparam_skip.macroblock_address  == mb_addr);

                psb__MPEG2MC_interPB_mb(ctx, &mbparam_skip);

                skip_count--;
            } else {
                ASSERT(mb_pending);
                ASSERT(mb_param->macroblock_address < ctx->size_mb);
                ASSERT(mb_param->macroblock_address  == mb_addr);

                psb__MPEG2MC_interPB_mb(ctx, mb_param);

                skip_count = MBPARAM_MBskipsFollowing(mb_param);
                if (skip_count) {
                    memcpy(&mbparam_skip, mb_param, sizeof(mbparam_skip));
                }

                mb_param++;
                mb_pending--;
            }

            ctx->fstmb_slice = IMG_FALSE;

            mb_addr++;
        }

        /* Tell hardware we're done     */
        psb__MPEG2MC_send_slice_picture_endcommand(ctx, (mb_pending == 0) && (skip_count == 0) && (ctx->size_mb == mb_last));
        psb__MPEG2MC_finalise_residDMA(ctx);

        /* write_kick */
        *cmdbuf->cmd_idx++ = CMD_COMPLETION;

        ctx->obj_context->video_op = psb_video_mc;
        ctx->obj_context->flags = (mb_pending == 0) && (skip_count == 0) && (ctx->size_mb == mb_last) ? FW_VA_RENDER_IS_LAST_SLICE : 0;
        ctx->obj_context->first_mb = 0;
        ctx->obj_context->last_mb = 0;
        psb_context_submit_cmdbuf(ctx->obj_context);

        /* check if the remained cmdbuf size can fill the commands of next slice */
        if (1 || (cmdbuf->lldma_base - (unsigned char *) cmdbuf->cmd_idx) < ((unsigned char *) cmdbuf->cmd_idx - cmd_start))
            psb_context_flush_cmdbuf(ctx->obj_context);
    }

    return VA_STATUS_SUCCESS;
}


static void     psb__MPEG2MC_intra_mb(
    context_MPEG2MC_p const ctx,
    const VAMacroblockParameterBufferMPEG2* const mb_param
)
{
    psb_cmdbuf_p cmdbuf = ctx->obj_context->cmdbuf;
    uint32_t cmd;

    psb_cmdbuf_reg_start_block(cmdbuf, 0);

    /* 3.3.21.  Macroblock Number */
    psb__MPEG2MC_send_mb_number(ctx, cmdbuf, mb_param->macroblock_address, MBPARAM_MotionType(mb_param), MB_CODE_TYPE_I);

    /*3.3.25.   Macroblock Residual Format */
    cmd = 0;

    /* In INTRA pictures, spatial-blocks are always 8bit when BBP is 8 */
    /*  00 = 8-bit signed data
     *  01 = 8-bit unsigned data
     */
    if (1/* TODO:: ConfigIntraResidUnsigned==0 */) {
        /* spec p67:
         * VA_ADD_128 MC Only used for direct insert of residual data;-
         *       0: add 0 to residual data input
         *       1: indicates 128 should be added to residual data input
         *
         * VA_DATA_FORMAT:Only used for direct insert of residual data;-
         *       0x0: 8-bit signed data
         *       0x1: 8-bit unsigned data
         *       0x2: 16-bit signed
         */
        /* Sent as  8 bit signed relative to 128 */
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, VA_DATA_FORMAT, 0);  /* ok ish */
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, VA_ADD_128,       1);   /* ok ish */
    } else {
        /* Sent as 8 bit unsigned relative to 0 */
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, VA_DATA_FORMAT, 1);
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, VA_ADD_128,       0);
    }

    if (FRAME_PICTURE == ctx->picture_structure) {
        /*
         * RESIDUAL_FIELD_CODED MC
         *               0: residual data frame coded
         *               1: (luma)residual data field coded
         *       N.B. For VC1, MPEG4 and MPEG2, if SLICE_FIELD_TYPE =
         *            frame, chroma residual will be frame coded, even if the luma
         *            residual is field coded.
         */
        /*
         * (VA:wMBType bit 5: FieldResidual, wMBtype & 0x20)/libVA(dct_type:1 field DCT):
         * Indicates whether the residual difference blocks use a field IDCT structure as specified in MPEG-2.
         *
         * Must be 1 if the bPicStructure member of VA_PictureParameters is 1 or 2. When used for MPEG-2,
         * FieldResidual must be zero if the frame_pred_frame_DCT variable in the MPEG-2 syntax is 1, and
         * must be equal to the dct_type variable in the MPEG-2 syntax if dct_type is present for the macroblock.
         */
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT,
                          RESIDUAL_FIELD_CODED, ((mb_param->macroblock_modes.bits.dct_type) ? 1 : 0));
    } else {
        REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, RESIDUAL_FIELD_CODED, 1);
    }

    /*   CR_FROM_VEC MC  0: constant zero residual
                        1: block Cr provided by VEC
                        (If REVERSE_FLAG_ORDER=1, this bit indicates if Y0 provided)
         CB_FROM_VEC MC 0: constant zero residual
                        1: block Cb provided by VEC
                     (If REVERSE_FLAG_ORDER=1, this bit indicates if Y1 provided)
         Y3_FROM_VEC MC 0: constant zero residual
                        1: block Y3 provided by VEC
                        (If REVERSE_FLAG_ORDER=1, this bit indicates if Y2 provided)
         Y2_FROM_VEC MC 0: constant zero residual
                        1: block Y2 provided by VEC
                         (If REVERSE_FLAG_ORDER=1, this bit indicates if Y3 provided)
         Y1_FROM_VEC MC 0: constant zero residual
                        1: block Y1 provided by VEC
                         (If REVERSE_FLAG_ORDER=1, this bit indicates if Cb provided)
         Y0_FROM_VEC MC 0: constant zero residual
                        1: block Y0 provided by VEC
                         (If REVERSE_FLAG_ORDER=1, this bit indicates if Cr provided)
    */
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, CR_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x40) ? 1 : 0));
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, CB_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x80) ? 1 : 0));
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, Y3_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x100) ? 1 : 0));
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, Y2_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x200) ? 1 : 0));
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, Y1_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x400) ? 1 : 0));
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT, Y0_FROM_VEC,
                      ((MBPARAM_CodedBlockPattern(mb_param) & 0x800) ? 1 : 0));
    psb_cmdbuf_reg_set(cmdbuf, REGISTER_OFFSET(MSVDX_CMDS, MACROBLOCK_RESIDUAL_FORMAT) , cmd);

    /* Send Residuals, spec p69,h264 only */
    cmd = 0;
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTRA_BLOCK_PREDICTION, INTRA_PRED_MODE0, 0);
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTRA_BLOCK_PREDICTION, INTRA_PRED_MODE1, 0);
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTRA_BLOCK_PREDICTION, INTRA_PRED_MODE2, 0);
    REGIO_WRITE_FIELD(cmd, MSVDX_CMDS, INTRA_BLOCK_PREDICTION, INTRA_PRED_MODE3, 0);
    psb_cmdbuf_reg_set(cmdbuf, REGISTER_OFFSET(MSVDX_CMDS, INTRA_BLOCK_PREDICTION) , cmd);

    psb__MPEG2MC_send_residual(ctx, MBPARAM_CodedBlockPattern(mb_param));

    psb_cmdbuf_reg_end_block(cmdbuf);
}


static VAStatus psb__MPEG2MC_process_mbs_intra(
    context_MPEG2MC_p   const   ctx
)
{
    const VAMacroblockParameterBufferMPEG2* mb_param = NULL;
    uint32_t mb_pending = ctx->mb_in_buffer;
    uint32_t mb_addr = ctx->mb_first_addr;
    uint32_t mb_last = mb_addr + mb_pending;

    mb_param = (VAMacroblockParameterBufferMPEG2*) ctx->mb_param;

    while (mb_pending) { /* one slice per loop */
        uint32_t mb_in_buffer =  min(mb_pending, ctx->picture_width_mb);
        psb_cmdbuf_p cmdbuf;
        unsigned char *cmd_start;

        mb_pending -= mb_in_buffer;

        psb_context_get_next_cmdbuf(ctx->obj_context);
        cmdbuf = ctx->obj_context->cmdbuf;
        cmd_start = (unsigned char *) cmdbuf->cmd_idx;

        ctx->fstmb_slice = IMG_TRUE;

        /* Build the high-level commands */
        psb__MPEG2MC_send_highlevel_commands(ctx);

        psb__MPEG2MC_send_slice_parameters(ctx);

        /* Process all the macroblocks in the slice */
        while (mb_in_buffer--) { /* for every MB */
            ASSERT(mb_param->macroblock_address < ctx->size_mb);
            ASSERT(mb_param->macroblock_address  == mb_addr);

            /* Check for segment space - do we have space for at least one more
             * worst case IntraMB plus completion
            */
            psb__MPEG2MC_check_segment_residDMA(ctx, INTRA_MB_WORST_CASE + 2);

            psb__MPEG2MC_intra_mb(ctx, mb_param);

            mb_param++; /* next macroblock parameter */
            mb_addr++;

            ctx->fstmb_slice = IMG_FALSE;
        }

        psb__MPEG2MC_send_slice_picture_endcommand(ctx, (mb_pending == 0) && (ctx->size_mb == mb_last)); /* Tell hardware we're done    */

        psb__MPEG2MC_finalise_residDMA(ctx);

        /* write_kick */
        *cmdbuf->cmd_idx++ = CMD_COMPLETION;

        ctx->obj_context->video_op = psb_video_mc;
        ctx->obj_context->flags = (mb_pending == 0) && (ctx->size_mb == mb_last) ? FW_VA_RENDER_IS_LAST_SLICE : 0;
        ctx->obj_context->first_mb = 0;
        ctx->obj_context->last_mb = 0;
        psb_context_submit_cmdbuf(ctx->obj_context);

        /* check if the remained cmdbuf size can fill the commands of next slice */
        if (1 || (cmdbuf->lldma_base - (unsigned char *) cmdbuf->cmd_idx) < ((unsigned char *) cmdbuf->cmd_idx - cmd_start))
            psb_context_flush_cmdbuf(ctx->obj_context);
    }

    //ASSERT(ctx->residual_bytes == 0); /* There should be no more data left */

    return VA_STATUS_SUCCESS;
}


static VAStatus psb__MPEG2MC_process_picture_param(context_MPEG2MC_p ctx, object_buffer_p obj_buffer)
{
    int coded_pic_height;

    /* Take a copy of the picture parameters */
    ctx->pic_params = (VAPictureParameterBufferMPEG2 *) obj_buffer->buffer_data;
    obj_buffer->buffer_data = NULL;
    obj_buffer->size = 0;

    ctx->picture_coding_type = ctx->pic_params->picture_coding_type;
    ctx->picture_structure = ctx->pic_params->picture_coding_extension.bits.picture_structure;

    ctx->forward_ref_surface = SURFACE(ctx->pic_params->forward_reference_picture);
    ctx->backward_ref_surface = SURFACE(ctx->pic_params->backward_reference_picture);

    /* Set picture type and reference indices for reference frames */
    if (ctx->picture_coding_type != PICTURE_CODING_I) {
        if (ctx->pic_params->picture_coding_extension.bits.is_first_field) { /* first field */
            if (ctx->backward_ref_surface) {
                ctx->picture_coding_type = PICTURE_CODING_B;
                ctx->ref_indexA = 0x01;/* Forward reference frame*/
                ctx->ref_indexB = 0x00;/* Backward reference frame*/
            } else {
                ctx->picture_coding_type =  PICTURE_CODING_P;
                ctx->ref_indexA = 0x00; /* Always reference frame 0*/
            }
        } else {
            if ((PICTURE_CODING_B == ctx->picture_coding_type) && (ctx->backward_ref_surface)) {
                ctx->picture_coding_type = PICTURE_CODING_B;
                ctx->ref_indexA = 0x01; /* Forward reference frame*/
                ctx->ref_indexB = 0x00;/* Backward reference frame */
            } else {
                ctx->picture_coding_type = PICTURE_CODING_P;
                if (ctx->forward_ref_surface) {
                    ctx->ref_indexA = 0x00;
                } else {
                    ctx->ref_indexA = 0x01;
                    ctx->ref_indexB = 0x00;
                }
            }
        }
    }
    ctx->pic_params->picture_coding_type = ctx->picture_coding_type;

    /* residual data size per element */
    if (ctx->picture_coding_type == PICTURE_CODING_I) {
        ctx->blk_size = 64; /* unsigned char */
    } else {
        ctx->blk_size = 2 * 64; /* unsigned short */
    }

    if (ctx->picture_coding_type != PICTURE_CODING_I) {
        if (ctx->backward_ref_surface) {
            if (ctx->forward_ref_surface == NULL)
                ctx->forward_ref_surface = ctx->backward_ref_surface;
        } else {
            ctx->backward_ref_surface = ctx->forward_ref_surface;
        }
    }
    if (NULL == ctx->backward_ref_surface) {
        ctx->backward_ref_surface = ctx->obj_context->current_render_target;
    }
    if (NULL == ctx->forward_ref_surface) {
        ctx->forward_ref_surface = ctx->obj_context->current_render_target;
    }

    ctx->coded_picture_width = ctx->pic_params->horizontal_size;
    ctx->coded_picture_height = ctx->pic_params->vertical_size;
    ctx->picture_width_mb = ctx->pic_params->horizontal_size / 16;
    if (ctx->pic_params->picture_coding_extension.bits.progressive_frame == 1) /* should be progressive_sequence? */
        ctx->picture_height_mb = (ctx->coded_picture_height + 15) / 16;
    else {
        if (FRAME_PICTURE != ctx->picture_structure) { /*Interlaced Field Pictures */
            ctx->picture_height_mb = ((ctx->coded_picture_height + 31) / 32);
        } else {
            ctx->picture_height_mb = 2 * ((ctx->coded_picture_height + 31) / 32);
        }
    }
    coded_pic_height = (ctx->picture_structure != FRAME_PICTURE) ?
                       ((ctx->picture_height_mb) * 32) : ((ctx->picture_height_mb) * 16);
    ctx->size_mb = ctx->picture_width_mb * (coded_pic_height >> 4);

    ctx->display_picture_size = 0;
    REGIO_WRITE_FIELD_LITE(ctx->display_picture_size, MSVDX_CMDS,
                           DISPLAY_PICTURE_SIZE, DISPLAY_PICTURE_HEIGHT, ctx->coded_picture_height - 1);
    REGIO_WRITE_FIELD_LITE(ctx->display_picture_size, MSVDX_CMDS,
                           DISPLAY_PICTURE_SIZE, DISPLAY_PICTURE_WIDTH, ctx->coded_picture_width - 1);

    ctx->coded_picture_size = 0;
    REGIO_WRITE_FIELD_LITE(ctx->coded_picture_size, MSVDX_CMDS,
                           CODED_PICTURE_SIZE, CODED_PICTURE_HEIGHT, ctx->coded_picture_height - 1);
    REGIO_WRITE_FIELD_LITE(ctx->coded_picture_size, MSVDX_CMDS,
                           CODED_PICTURE_SIZE, CODED_PICTURE_WIDTH, ctx->coded_picture_width - 1);

    ctx->obj_context->operating_mode = 0;
    REGIO_WRITE_FIELD(ctx->obj_context->operating_mode, MSVDX_CMDS, OPERATING_MODE, CHROMA_FORMAT,  1);
    REGIO_WRITE_FIELD(ctx->obj_context->operating_mode, MSVDX_CMDS, OPERATING_MODE, ASYNC_MODE,     1);
    /* 0 = VDMC and VDEB active.  1 = VDEB pass-thru. */
    REGIO_WRITE_FIELD(ctx->obj_context->operating_mode, MSVDX_CMDS, OPERATING_MODE,
                      CODEC_MODE, CODEC_MODE_MPEG2);
    REGIO_WRITE_FIELD(ctx->obj_context->operating_mode, MSVDX_CMDS, OPERATING_MODE,
                      CODEC_PROFILE,  CODEC_PROFILE_MPEG2_MAIN);
    REGIO_WRITE_FIELD(ctx->obj_context->operating_mode, MSVDX_CMDS, OPERATING_MODE,
                      ROW_STRIDE, (ctx->obj_context->current_render_target->psb_surface->stride_mode));

    return VA_STATUS_SUCCESS;
}



static void psb_MPEG2MC_QueryConfigAttributes(
    VAProfile profile,
    VAEntrypoint entrypoint,
    VAConfigAttrib *attrib_list,
    int num_attribs)
{
    /* No MPEG2 specific attributes */
}

static VAStatus psb_MPEG2MC_ValidateConfig(
    object_config_p obj_config)
{
    int i;
    /* Check all attributes */
    for (i = 0; i < obj_config->attrib_count; i++) {
        switch (obj_config->attrib_list[i].type) {
        case VAConfigAttribRTFormat:
            /* Ignore */
            break;

        default:
            return VA_STATUS_ERROR_ATTR_NOT_SUPPORTED;
        }
    }

    return VA_STATUS_SUCCESS;
}

static VAStatus psb__MPEG2MC_check_legal_picture(object_context_p obj_context, object_config_p obj_config)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    if (NULL == obj_context) {
        vaStatus = VA_STATUS_ERROR_INVALID_CONTEXT;
        DEBUG_FAILURE;
        return vaStatus;
    }

    if (NULL == obj_config) {
        vaStatus = VA_STATUS_ERROR_INVALID_CONFIG;
        DEBUG_FAILURE;
        return vaStatus;
    }

    /* MSVDX decode capability for MPEG2:
     *     MP@HL
     *
     * Refer to Table 8-11 (Upper bounds for luminance sample rate) of ISO/IEC 13818-2: 1995(E),
     * and the "MSVDX MPEG2 decode capability" table of "Poulsbo Media Software Overview"
     */

    switch (obj_config->profile) {
    case VAProfileMPEG2Simple:
        if ((obj_context->picture_width <= 0) || (obj_context->picture_width > 352)
            || (obj_context->picture_height <= 0) || (obj_context->picture_height > 288)) {
            vaStatus = VA_STATUS_ERROR_RESOLUTION_NOT_SUPPORTED;
        }
        break;

    case VAProfileMPEG2Main:
        if ((obj_context->picture_width <= 0) || (obj_context->picture_width > 1920)
            || (obj_context->picture_height <= 0) || (obj_context->picture_height > 1088)) {
            vaStatus = VA_STATUS_ERROR_RESOLUTION_NOT_SUPPORTED;
        }
        break;

    default:
        vaStatus = VA_STATUS_ERROR_UNSUPPORTED_PROFILE;
        break;
    }

    return vaStatus;
}

static VAStatus psb_MPEG2MC_CreateContext(
    object_context_p obj_context,
    object_config_p obj_config)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    context_MPEG2MC_p ctx;

    /* Validate flag */
    /* Validate picture dimensions */
    vaStatus = psb__MPEG2MC_check_legal_picture(obj_context, obj_config);
    if (VA_STATUS_SUCCESS != vaStatus) {
        DEBUG_FAILURE;
        return vaStatus;
    }

    if (obj_context->num_render_targets < 1) {
        vaStatus = VA_STATUS_ERROR_UNKNOWN;
        DEBUG_FAILURE;
        return vaStatus;
    }

    ctx = (context_MPEG2MC_p) calloc(1, sizeof(struct context_MPEG2MC_s));
    if (NULL == ctx) {
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DEBUG_FAILURE;
        return vaStatus;
    }

    /* TODO: initialize ctx content */
    obj_context->format_data = (void*) ctx;
    ctx->obj_context = obj_context;
    ctx->pic_params = NULL;
    /* TODO create and map buffer */

    return vaStatus;
}


static void psb_MPEG2MC_DestroyContext(
    object_context_p obj_context)
{
    INIT_CONTEXT_MPEG2MC;

    /* TODO:unmap and destroy buffers */
    if (ctx->pic_params) {
        free(ctx->pic_params);
        ctx->pic_params = NULL;
    }

    free(obj_context->format_data);
    obj_context->format_data = NULL;
}

static VAStatus psb_MPEG2MC_BeginPicture(
    object_context_p obj_context)
{
    INIT_CONTEXT_MPEG2MC;

#if 0 /* clear surface for debugging */
    unsigned char *surface_data = NULL;
    static psb_surface_p target_surface = NULL;
    psb_surface_p tmp = ctx->obj_context->current_render_target->psb_surface;
    if (target_surface != tmp) { /* for field picture, only reset one time */
        target_surface = tmp;

        int ret = psb_buffer_map(&target_surface->buf, &surface_data);
        if (ret) {
            goto out;
        }
        memset(surface_data, 0x33, target_surface->size);
        psb_buffer_unmap(&target_surface->buf);
    }
out:
#endif

    if (ctx->pic_params) {
        free(ctx->pic_params);
        ctx->pic_params = NULL;
    }

    /* TODO: others */
    return VA_STATUS_SUCCESS;
}


static VAStatus psb_MPEG2MC_RenderPicture(
    object_context_p obj_context,
    object_buffer_p *buffers,
    int num_buffers)
{
    int i;
    INIT_CONTEXT_MPEG2MC;
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    ctx->mb_param = NULL; /* Init. compressed buffer pointers   */
    ctx->residual_buf = NULL;

    for (i = 0; i < num_buffers; i++) {
        object_buffer_p obj_buffer = buffers[i];

        switch (obj_buffer->type) {
            /* Picture parameters processing */
        case VAPictureParameterBufferType: {
            vaStatus = psb__MPEG2MC_process_picture_param(ctx, obj_buffer);
            DEBUG_FAILURE;
            break;
        }
        /* Macroblock Data processing */
        case VAMacroblockParameterBufferType: {
            ctx->mb_param = (VAMacroblockParameterBufferMPEG2 *)obj_buffer->buffer_data;
            ctx->mb_first_addr = ctx->mb_param->macroblock_address;
            ctx->mb_in_buffer = obj_buffer->num_elements;
            /* drv_debug_msg(VIDEO_DEBUG_GENERAL, "Macroblock count %d\n",ctx->mb_in_buffer); */
            break;
        }
        /* Residual Difference Data processing */
        case VAResidualDataBufferType: {
            /* store the data after VLD+IDCT */
            ctx->residual_buf = obj_buffer->psb_buffer;
            ctx->blk_in_buffer = obj_buffer->num_elements;
            break;
        }
        default:
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Unhandled buffer type 0x%x\n", obj_buffer->type);
            break;
        }
    }

    /* Assuming residual and MB control buffers have been located */
    if (ctx->residual_buf && ctx->mb_param) {
        ctx->residual_sent = 0;
        ctx->residual_pendingDMA = 0;

        if (ctx->picture_coding_type == PICTURE_CODING_I) {
            psb__MPEG2MC_process_mbs_intra(ctx);
        } else {
            psb__MPEG2MC_process_mbs_interPB(ctx);
        }
        ctx->mb_param = NULL;
    }

    return VA_STATUS_SUCCESS;
}


static VAStatus psb_MPEG2MC_EndPicture(
    object_context_p obj_context)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    INIT_CONTEXT_MPEG2MC;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "psb_MPEG2MC_EndPicture\n");

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

    return vaStatus;
}

/* TODO: integrate with VLD */
struct format_vtable_s psb_MPEG2MC_vtable = {
queryConfigAttributes:
    psb_MPEG2MC_QueryConfigAttributes,
validateConfig:
    psb_MPEG2MC_ValidateConfig,
createContext:
    psb_MPEG2MC_CreateContext,
destroyContext:
    psb_MPEG2MC_DestroyContext,
beginPicture:
    psb_MPEG2MC_BeginPicture,
renderPicture:
    psb_MPEG2MC_RenderPicture,
endPicture:
    psb_MPEG2MC_EndPicture
};