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

#include <sys/types.h>
#include "psb_buffer.h"

#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <wsbm/wsbm_manager.h>

#ifdef ANDROID
#ifdef BAYTRAIL
#include <linux/vxd_drm.h>
#else
#include <drm/ttm/ttm_placement.h>
#include <linux/psb_drm.h>
#endif
#else
#include <psb_drm.h>
#endif

#include "psb_def.h"
#include "psb_drv_debug.h"
#include "tng_cmdbuf.h"

#ifndef BAYTRAIL
#include <pnw_cmdbuf.h>
#include "pnw_jpeg.h"
#include "pnw_H264ES.h"
#include "tng_jpegES.h"
#endif

#include "vsp_fw.h"
/*
 * Create buffer
 */
VAStatus psb_buffer_create(psb_driver_data_p driver_data,
                           unsigned int size,
                           psb_buffer_type_t type,
                           psb_buffer_p buf
                          )
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    int allignment;
    uint32_t placement;
    int ret;

    /* reset rar_handle to NULL */
    buf->rar_handle = 0;
    buf->buffer_ofs = 0;

    buf->type = type;
    buf->driver_data = driver_data; /* only for RAR buffers */
    buf->size = size;
    /* TODO: Mask values are a guess */
    switch (type) {
    case psb_bt_cpu_vpu:
        allignment = 1;
        placement = DRM_PSB_FLAG_MEM_MMU;
        break;
    case psb_bt_cpu_vpu_shared:
        allignment = 1;
        placement = DRM_PSB_FLAG_MEM_MMU | WSBM_PL_FLAG_SHARED;
        break;
    case psb_bt_surface:
        allignment = 0;
        placement = DRM_PSB_FLAG_MEM_MMU | WSBM_PL_FLAG_SHARED;
        if (IS_CTP(driver_data))  /* CTP support cache snoop */
            placement |= WSBM_PL_FLAG_CACHED;
        break;
    case psb_bt_surface_tt:
        allignment = 0;
        placement = WSBM_PL_FLAG_TT | WSBM_PL_FLAG_NO_EVICT | WSBM_PL_FLAG_SHARED;
        break;
#ifdef PSBVIDEO_MSVDX_DEC_TILING
    case psb_bt_surface_tiling:
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "Allocate tiled surface from TT heap\n");
            placement =  WSBM_PL_FLAG_TT | WSBM_PL_FLAG_SHARED;
            allignment = 2048 * 16; /* Tiled row aligned */
        break;
    case psb_bt_mmu_tiling:
            placement =  DRM_PSB_FLAG_MEM_MMU_TILING | WSBM_PL_FLAG_SHARED;
            allignment = 2048 * 16; /* Tiled row aligned */
        break;
#endif
    case psb_bt_cpu_vpu_cached:
        allignment = 1;
        placement = DRM_PSB_FLAG_MEM_MMU | WSBM_PL_FLAG_CACHED;
        break;
    case psb_bt_vpu_only:
        allignment = 1;
        placement = DRM_PSB_FLAG_MEM_MMU;
        break;
    case psb_bt_cpu_only:
        allignment = 1;
        placement = WSBM_PL_FLAG_SYSTEM | WSBM_PL_FLAG_CACHED;
        break;
#if PSB_MFLD_DUMMY_CODE
    case psb_bt_camera:
        allignment = 1;
        placement = WSBM_PL_FLAG_SHARED;
        break;
#endif
#ifdef ANDROID
#ifndef BAYTRAIL
    case psb_bt_imr:
        allignment = 1;
        placement = TTM_PL_FLAG_IMR | WSBM_PL_FLAG_SHARED;
        break;
#endif
#endif
    default:
        vaStatus = VA_STATUS_ERROR_UNKNOWN;
        DEBUG_FAILURE;
        return vaStatus;
    }
    ret = LOCK_HARDWARE(driver_data);
    if (ret) {
        UNLOCK_HARDWARE(driver_data);
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DEBUG_FAILURE_RET;
        return vaStatus;
    }

#ifdef VA_EMULATOR
    placement |= WSBM_PL_FLAG_SHARED;
#endif

#ifndef ANDROID
    if(!(placement & WSBM_PL_FLAG_SYSTEM)) {
        //drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: buffer->pl_flags 0x%08x\n", __func__, placement);
        placement &= ~WSBM_PL_MASK_MEM;
        placement &= ~WSBM_PL_FLAG_NO_EVICT;
        placement |= TTM_PL_FLAG_VRAM;
        //drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: repleace buffer->pl_flags 0x%08x\n", __func__, placement);
    }
#endif

#ifdef MSVDX_VA_EMULATOR
    placement |= WSBM_PL_FLAG_SHARED;
#endif

    if(allignment < 4096)
        allignment = 4096; /* temporily more safe */

    //drv_debug_msg(VIDEO_DEBUG_ERROR, "FIXME: should use geetpagesize() ?\n");
    ret = wsbmGenBuffers(driver_data->main_pool, 1, &buf->drm_buf,
                         allignment, placement);
    if (!buf->drm_buf) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "failed to gen wsbm buffers\n");
        UNLOCK_HARDWARE(driver_data);
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

    /* here use the placement when gen buffer setted */
    ret = wsbmBOData(buf->drm_buf, size, NULL, NULL, 0);
    UNLOCK_HARDWARE(driver_data);
    if (ret) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "failed to alloc wsbm buffers\n");
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

    if (placement & WSBM_PL_FLAG_TT)
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Create BO with TT placement (%d byte),BO GPU offset hint=0x%08x\n",
                                 size, wsbmBOOffsetHint(buf->drm_buf));

    buf->pl_flags = placement;
    buf->status = psb_bs_ready;
    buf->wsbm_synccpu_flag = 0;

    return VA_STATUS_SUCCESS;
}

/*
 * Create buffer
 */
VAStatus psb_buffer_create_from_ub(psb_driver_data_p driver_data,
                           unsigned int size,
                           psb_buffer_type_t type,
                           psb_buffer_p buf,
                           void * vaddr,
                           int fd,
                           unsigned int flags
                          )
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    int allignment;
    uint32_t placement;
    int ret;

    /* reset rar_handle to NULL */
    buf->rar_handle = 0;
    buf->buffer_ofs = 0;

    buf->type = type;
    buf->driver_data = driver_data; /* only for RAR buffers */
    buf->user_ptr = vaddr;
    buf->fd = fd;

    /* Xvideo will share surface buffer, set SHARED flag
    */
    placement =  DRM_PSB_FLAG_MEM_MMU | WSBM_PL_FLAG_SHARED ;

    ret = LOCK_HARDWARE(driver_data);
    if (ret) {
        UNLOCK_HARDWARE(driver_data);
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DEBUG_FAILURE_RET;
        return vaStatus;
    }

    allignment = 4096; /* temporily more safe */
#ifdef PSBVIDEO_MSVDX_DEC_TILING
    if (type == psb_bt_mmu_tiling) {
        placement =  DRM_PSB_FLAG_MEM_MMU_TILING | WSBM_PL_FLAG_SHARED ;
        allignment = 2048 * 16; /* Tiled row aligned */
    }
#endif

    if (flags & PSB_USER_BUFFER_WC)
	placement |= WSBM_PL_FLAG_WC;
    else if (flags & PSB_USER_BUFFER_UNCACHED)
	placement |= WSBM_PL_FLAG_UNCACHED;
    else
	placement |= WSBM_PL_FLAG_CACHED;

    //drv_debug_msg(VIDEO_DEBUG_ERROR, "FIXME: should use geetpagesize() ?\n");
    ret = wsbmGenBuffers(driver_data->main_pool, 1, &buf->drm_buf,
    allignment, placement);
    if (!buf->drm_buf) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "failed to gen wsbm buffers\n");
        UNLOCK_HARDWARE(driver_data);
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

    /* here use the placement when gen buffer setted */
    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Create BO from user buffer %p, size=%d, fd = %d\n", vaddr, size, fd);

    ret = wsbmBODataUB(buf->drm_buf, size, NULL, NULL, 0, vaddr, fd);
    if (ret) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Failed to alloc wsbm buffers, buf->drm_buf is 0x%x, size is %d, vaddr is 0x%x, fd=%d\n", buf->drm_buf, size, vaddr, fd);
        return 1;
    }

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Create BO from user buffer 0x%08x (%d byte), fd=%d, BO GPU offset hint=0x%08x\n",
    vaddr, size, fd, wsbmBOOffsetHint(buf->drm_buf));

    buf->pl_flags = placement;
    buf->status = psb_bs_ready;
    buf->wsbm_synccpu_flag = 0;

    return VA_STATUS_SUCCESS;
}

#if 0
/*
 * buffer setstatus
 *
 * Returns 0 on success
 */
int psb_buffer_setstatus(psb_buffer_p buf, uint32_t set_placement, uint32_t clr_placement)
{
    int ret = 0;

    ASSERT(buf);
    ASSERT(buf->driver_data);

    ret = wsbmBOSetStatus(buf->drm_buf, set_placement, clr_placement);
    if (ret == 0)
        buf->pl_flags = set_placement;

    return ret;
}
#endif

VAStatus psb_buffer_reference(psb_driver_data_p driver_data,
                              psb_buffer_p buf,
                              psb_buffer_p reference_buf
                             )
{
    int ret = 0;
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    memcpy(buf, reference_buf, sizeof(*buf));
    buf->drm_buf = NULL;

    ret = LOCK_HARDWARE(driver_data);
    if (ret) {
        UNLOCK_HARDWARE(driver_data);
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DEBUG_FAILURE_RET;
        return vaStatus;
    }

    ret = wsbmGenBuffers(driver_data->main_pool,
                         1,
                         &buf->drm_buf,
                         4096,  /* page alignment */
                         0);
    if (!buf->drm_buf) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "failed to gen wsbm buffers\n");
        UNLOCK_HARDWARE(driver_data);
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

    ret = wsbmBOSetReferenced(buf->drm_buf, wsbmKBufHandle(wsbmKBuf(reference_buf->drm_buf)));
    UNLOCK_HARDWARE(driver_data);
    if (ret) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "failed to alloc wsbm buffers\n");
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

    return VA_STATUS_SUCCESS;
}

VAStatus psb_kbuffer_reference(psb_driver_data_p driver_data,
                               psb_buffer_p buf,
                               int kbuf_handle
                              )
{
    int ret = 0;
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    buf->drm_buf = NULL;

    ret = LOCK_HARDWARE(driver_data);
    if (ret) {
        UNLOCK_HARDWARE(driver_data);
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DEBUG_FAILURE_RET;
        return vaStatus;
    }

    ret = wsbmGenBuffers(driver_data->main_pool,
                         1,
                         &buf->drm_buf,
                         4096,  /* page alignment */
                         0);
    if (!buf->drm_buf) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "failed to gen wsbm buffers\n");
        UNLOCK_HARDWARE(driver_data);
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

    ret = wsbmBOSetReferenced(buf->drm_buf, kbuf_handle);
    UNLOCK_HARDWARE(driver_data);
    if (ret) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "failed to alloc wsbm buffers\n");
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }
    buf->pl_flags = wsbmBOPlacementHint(buf->drm_buf);
    buf->type = psb_bt_surface;
    buf->status = psb_bs_ready;

    return VA_STATUS_SUCCESS;
}
/*
 * Destroy buffer
 */
void psb_buffer_destroy(psb_buffer_p buf)
{
    ASSERT(buf);
    if (buf->drm_buf == NULL)
        return;
    if (psb_bs_unfinished != buf->status) {
        ASSERT(buf->driver_data);
        wsbmBOUnreference(&buf->drm_buf);
        if (buf->rar_handle)
            buf->rar_handle = 0;
        buf->driver_data = NULL;
        buf->status = psb_bs_unfinished;
    }
}

/*
 * Map buffer
 *
 * Returns 0 on success
 */
int psb_buffer_map(psb_buffer_p buf, unsigned char **address /* out */)
{
    int ret;

    ASSERT(buf);
    ASSERT(buf->driver_data);

    /* multiple mapping not allowed */
    if (buf->wsbm_synccpu_flag) {
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Multiple mapping request detected, unmap previous mapping\n");
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Need to fix application to unmap at first, then request second mapping request\n");

        psb_buffer_unmap(buf);
    }

    /* don't think TG deal with READ/WRITE differently */
    buf->wsbm_synccpu_flag = WSBM_SYNCCPU_READ | WSBM_SYNCCPU_WRITE;
    if (psb_video_trace_fp) {
        wsbmBOWaitIdle(buf->drm_buf, 0);
    } else {
        ret = wsbmBOSyncForCpu(buf->drm_buf, buf->wsbm_synccpu_flag);
        if (ret) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "faild to sync bo for cpu\n");
            return ret;
        }
    }

    if (buf->user_ptr) /* user mode buffer */
        *address = buf->user_ptr;
    else
        *address = wsbmBOMap(buf->drm_buf, buf->wsbm_synccpu_flag);

    if (*address == NULL) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "failed to map buffer\n");
        return -1;
    }

    return 0;
}

/*
 * Unmap buffer
 *
 * Returns 0 on success
 */
int psb_buffer_unmap(psb_buffer_p buf)
{
    ASSERT(buf);
    ASSERT(buf->driver_data);

    if (buf->wsbm_synccpu_flag)
        (void) wsbmBOReleaseFromCpu(buf->drm_buf, buf->wsbm_synccpu_flag);

    buf->wsbm_synccpu_flag = 0;

    if ((buf->type != psb_bt_user_buffer) && !buf->handle)
        wsbmBOUnmap(buf->drm_buf);

    return 0;
}

#define _MRFL_DEBUG_CODED_

#ifdef _MRFL_DEBUG_CODED_
static void psb__trace_coded(VACodedBufferSegment *vaCodedBufSeg)
{
    int i, j;
    int uiPipeIndex = -1;
    unsigned int *pBuf = NULL;
    do {
        ++uiPipeIndex;
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: pipe num %d, size = %d\n", __FUNCTION__, uiPipeIndex, vaCodedBufSeg[uiPipeIndex].size);
        pBuf = (unsigned int *)(vaCodedBufSeg[uiPipeIndex].buf);
        pBuf -= 16;
        for (i = 0; i < 6; i++) {
            for (j = 0; j < 4; j++) {
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: 0x%08x\n", __FUNCTION__, pBuf[(i*4) + j]);
            }
         }
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: \n", __FUNCTION__);
    } while (vaCodedBufSeg[uiPipeIndex].next);

	return ;
}
#endif

#define PROFILE_H264(profile) ((profile>=VAProfileH264Baseline && profile <=VAProfileH264High) || \
                               (profile == VAProfileH264ConstrainedBaseline))
static void tng_get_coded_data(
    object_buffer_p obj_buffer,
    unsigned char *raw_codedbuf
)
{
    object_context_p obj_context = obj_buffer->context;
    VACodedBufferSegment *vaCodedBufSeg = &obj_buffer->codedbuf_mapinfo[0];
    int iPipeIndex = 0;
    unsigned int uiPipeNum = tng_get_pipe_number(obj_context);
    unsigned int uiBufOffset = tng_align_KB(obj_buffer->size >> 1);
    unsigned long *ptmp = NULL;
    int tmp;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s pipenum = 0x%x\n", __FUNCTION__, uiPipeNum);
    drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s offset  = 0x%x\n", __FUNCTION__, uiBufOffset);

    tmp = vaCodedBufSeg[iPipeIndex].size = *(unsigned int *)((unsigned long)raw_codedbuf);

    /*
     * This is used for DRM over WiDi which only uses H264 BP
     * Tangier IED encryption operates on the chunks with 16bytes, and we must include
     * the extra bytes beyond slice data as a whole chunk for decrption
     * We simply include the padding bytes regardless of IED enable or disable
     */
    if (PROFILE_H264(obj_context->profile) && (tmp % 16 != 0)) {
	tmp = (tmp + 15) & (~15);
	drv_debug_msg(VIDEO_DEBUG_GENERAL, "Force slice size from %d to %d\n",
                      vaCodedBufSeg[iPipeIndex].size, tmp);
	vaCodedBufSeg[iPipeIndex].size  = tmp;
    }

    vaCodedBufSeg[iPipeIndex].buf = (unsigned char *)(((unsigned int *)((unsigned long)raw_codedbuf)) + 16); /* skip 4DWs */

    ptmp = (unsigned long *)((unsigned long)raw_codedbuf); 
    vaCodedBufSeg[iPipeIndex].reserved = (ptmp[1] >> 6) & 0xf;
    vaCodedBufSeg[iPipeIndex].next = NULL;


    if (uiPipeNum == 2) {
        /*The second part of coded buffer which generated by core 2 is the
         * first part of encoded clip, while the first part of coded buffer
         * is the second part of encoded clip.*/
        ++iPipeIndex;
        vaCodedBufSeg[iPipeIndex - 1].next = &vaCodedBufSeg[iPipeIndex];
        tmp = vaCodedBufSeg[iPipeIndex].size = *(unsigned int *)((unsigned long)raw_codedbuf + uiBufOffset);

        /*
         * This is used for DRM over WiDi which only uses H264 BP
         * Tangier IED encryption operates on the chunks with 16bytes, and we must include
         * the extra bytes beyond slice data as a whole chunk for decryption
         * We simply include the padding bytes regardless of IED enable or disable
         */
        if (PROFILE_H264(obj_context->profile) && (tmp % 16 != 0)) {
            tmp = (tmp + 15) & (~15);
            drv_debug_msg(VIDEO_DEBUG_GENERAL,"Force slice size from %d to %d\n",
                          vaCodedBufSeg[iPipeIndex].size, tmp);

            vaCodedBufSeg[iPipeIndex].size  = tmp;
        }

        vaCodedBufSeg[iPipeIndex].buf = (unsigned char *)(((unsigned int *)((unsigned long)raw_codedbuf + uiBufOffset)) + 16); /* skip 4DWs */
        vaCodedBufSeg[iPipeIndex].reserved = vaCodedBufSeg[iPipeIndex - 1].reserved;
        vaCodedBufSeg[iPipeIndex].next = NULL;
    }

#ifdef _MRFL_DEBUG_CODED_
    psb__trace_coded(vaCodedBufSeg);
#endif

    return ;
}

/*
 * Return special data structure for codedbuffer
 *
 * Returns 0 on success
 */
#define CONFIG(id)  ((object_config_p) object_heap_lookup( &driver_data->config_heap, id ))
#define INIT_DRIVER_DATA    psb_driver_data_p driver_data = (psb_driver_data_p) ctx->pDriverData;
int psb_codedbuf_map_mangle(
    VADriverContextP ctx,
    object_buffer_p obj_buffer,
    void **pbuf /* out */
)
{
    object_context_p obj_context = obj_buffer->context;
    INIT_DRIVER_DATA;
    VACodedBufferSegment *p = &obj_buffer->codedbuf_mapinfo[0];
    unsigned char *raw_codedbuf;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    unsigned int next_buf_off;
    uint32_t i;

    CHECK_INVALID_PARAM(pbuf == NULL);

    if (NULL == obj_context) {
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DEBUG_FAILURE;

        psb_buffer_unmap(obj_buffer->psb_buffer);
        obj_buffer->buffer_data = NULL;

        return vaStatus;
    }

    raw_codedbuf = *pbuf;
    /* reset the mapinfo */
    memset(obj_buffer->codedbuf_mapinfo, 0, sizeof(obj_buffer->codedbuf_mapinfo));

    *pbuf = p = &obj_buffer->codedbuf_mapinfo[0];
#ifdef PSBVIDEO_MRFL
    if (IS_MRFL(driver_data)) {
        object_config_p obj_config = CONFIG(obj_context->config_id);
        if (NULL == obj_config) {
            vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
            DEBUG_FAILURE;

            psb_buffer_unmap(obj_buffer->psb_buffer);
            obj_buffer->buffer_data = NULL;

            return vaStatus;
        }

        if (VAProfileJPEGBaseline != obj_config->profile
            && (*((unsigned long *) raw_codedbuf + 1) & SKIP_NEXT_FRAME) != 0) {
            /*Set frame skip flag*/
            tng_set_frame_skip_flag(obj_context);
        }
        switch (obj_config->profile) {
            case VAProfileMPEG4Simple:
            case VAProfileMPEG4AdvancedSimple:
            case VAProfileMPEG4Main:

            case VAProfileH264Baseline:
            case VAProfileH264Main:
            case VAProfileH264High:
            case VAProfileH264StereoHigh:
            case VAProfileH264ConstrainedBaseline:
            case VAProfileH263Baseline:
                /* 1st segment */
                tng_get_coded_data(obj_buffer, raw_codedbuf);
#if 0
                p->size = *((unsigned long *) raw_codedbuf);
                p->buf = (unsigned char *)((unsigned long *) raw_codedbuf + 16); /* skip 16DWs */
                p->next = NULL;
#ifdef _MRFL_DEBUG_CODED_
                psb__trace_coded((unsigned int*)raw_codedbuf);
                psb__trace_coded(p);
#endif
#endif
                break;
            case VAProfileVP8Version0_3:
            {
                /* multi segments*/
		struct VssVp8encEncodedFrame *t = (struct VssVp8encEncodedFrame *) (raw_codedbuf);
		int concatenate = 1;
#if 0
		for (i = 0; i < t->partitions - 1; i++) {
                    if (t->partition_start[i+1] != t->partition_start[i] + t->partition_size[i])
                        concatenate = 0;
		}
#endif
		/* reference frame surface_id */
                /* default is recon_buffer_mode ==0 */
                p->reserved = t->surfaceId_of_ref_frame[3];

		if (concatenate) {
                    /* partitions are concatenate */
                    p->buf = t->coded_data;
                    p->size = t->frame_size;
                    if(t->frame_size == 0){
                        drv_debug_msg(VIDEO_DEBUG_ERROR,"Frame size is zero, Force it to 3, encoder status is 0x%x\n", t->status);
                        p->size = 3;
                        t->coded_data[0]=0;
                    }
                    p->next = NULL;
		} else {
                    for (i = 0; i < t->partitions; i++) {
                        /* partition not consecutive */
                        p->buf = t->coded_data + t->partition_start[i] - t->partition_start[0];
                        p->size += t->partition_size[i];
                        p->next = &p[1];
                        p++;
		    }
		    p--;
		    p->next = NULL;
		}

		break;
            }
            case VAProfileJPEGBaseline:
                /* 3~6 segment */
                tng_jpeg_AppendMarkers(obj_context, raw_codedbuf);
                next_buf_off = 0;
                /*Max resolution 4096x4096 use 6 segments*/
                for (i = 0; i < PTG_JPEG_MAX_SCAN_NUM + 1; i++) {
                    p->size = *(unsigned long *)((unsigned long)raw_codedbuf + next_buf_off);  /* ui32BytesUsed in HEADER_BUFFER*/
                    p->buf = (unsigned char *)((unsigned long *)((unsigned long)raw_codedbuf + next_buf_off) + 4);  /* skip 4DWs (HEADER_BUFFER) */
                    next_buf_off = *((unsigned long *)((unsigned long)raw_codedbuf + next_buf_off) + 3);  /* ui32Reserved3 in HEADER_BUFFER*/

                    drv_debug_msg(VIDEO_DEBUG_GENERAL, "JPEG coded buffer segment %d size: %d\n", i, p->size);
                    drv_debug_msg(VIDEO_DEBUG_GENERAL, "JPEG coded buffer next segment %d offset: %d\n", i + 1, next_buf_off);

                    if (next_buf_off == 0) {
                        p->next = NULL;
                        break;
                    } else
                        p->next = &p[1];
                    p++;
                }
                break;

            default:
                drv_debug_msg(VIDEO_DEBUG_ERROR, "unexpected case\n");

                psb_buffer_unmap(obj_buffer->psb_buffer);
                obj_buffer->buffer_data = NULL;
                break;
        }
    }
#endif
#ifdef PSBVIDEO_MFLD
    if (IS_MFLD(driver_data)){ /* MFLD */
        object_config_p obj_config = CONFIG(obj_context->config_id);

        if (NULL == obj_config) {
            vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
            DEBUG_FAILURE;

            psb_buffer_unmap(obj_buffer->psb_buffer);
            obj_buffer->buffer_data = NULL;

            return vaStatus;
        }

        if (VAProfileJPEGBaseline != obj_config->profile
            && (*((unsigned long *) raw_codedbuf + 1) & SKIP_NEXT_FRAME) != 0) {
            /*Set frame skip flag*/
            pnw_set_frame_skip_flag(obj_context);
        }
        switch (obj_config->profile) {
        case VAProfileMPEG4Simple:
        case VAProfileMPEG4AdvancedSimple:
        case VAProfileMPEG4Main:
            /* one segment */
            p->size = *((unsigned long *) raw_codedbuf);
            p->buf = (unsigned char *)((unsigned long *) raw_codedbuf + 4); /* skip 4DWs */
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "coded buffer size %d\n", p->size);
            break;

        case VAProfileH264Baseline:
        case VAProfileH264Main:
        case VAProfileH264High:
        case VAProfileH264ConstrainedBaseline:
            i = 0;
            next_buf_off = ~0xf & (obj_buffer->size / pnw_get_parallel_core_number(obj_context));
            if (pnw_get_parallel_core_number(obj_context) == 2) {
                /*The second part of coded buffer which generated by core 2 is the
                 * first part of encoded clip, while the first part of coded buffer
                 * is the second part of encoded clip.*/
                p[i].next = &p[i + 1];
                p[i].size = *(unsigned long *)((unsigned long)raw_codedbuf + next_buf_off);
                p[i].buf = (unsigned char *)(((unsigned long *)((unsigned long)raw_codedbuf + next_buf_off)) + 4); /* skip 4DWs */

                if (GET_CODEDBUF_INFO(SLICE_NUM, obj_buffer->codedbuf_aux_info) <= 2 &&
                        GET_CODEDBUF_INFO(NONE_VCL_NUM, obj_buffer->codedbuf_aux_info) == 0) {
                    p[i].status =  VA_CODED_BUF_STATUS_SINGLE_NALU;
                    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Only VCL NAL in this segment %i of coded buffer\n",
                            i);
                }
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "2nd segment coded buffer offset: 0x%08x,  size: %d\n",
                        next_buf_off, p[i].size);

              i++;

            }
            /* 1st segment */
            p[i].size = *((unsigned long *) raw_codedbuf);
            p[i].buf = (unsigned char *)((unsigned long *) raw_codedbuf + 4); /* skip 4DWs */
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "1st segment coded buffer size %d\n", p[i].size);
            if (GET_CODEDBUF_INFO(SLICE_NUM, obj_buffer->codedbuf_aux_info) <= 2 &&
                    GET_CODEDBUF_INFO(NONE_VCL_NUM, obj_buffer->codedbuf_aux_info) == 0) {
                p[i].status =  VA_CODED_BUF_STATUS_SINGLE_NALU;
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "Only VCL NAL in this segment %i of coded buffer\n",
                        i);
            }
            for (i = 0; i < pnw_get_parallel_core_number(obj_context); i++) {
                if (p[i].size > (next_buf_off - sizeof(unsigned long) * 4)) {
                    drv_debug_msg(VIDEO_DEBUG_ERROR, "Coded segment %d is too large(%d)"
                            " and exceed segment boundary(offset %d)", i, p[i].size, next_buf_off);
                    p[i].size = next_buf_off - sizeof(unsigned long) * 4;
                }
            }

            break;

        case VAProfileH263Baseline:
                /* one segment */
            p->size = *((unsigned long *) raw_codedbuf);
            p->buf = (unsigned char *)((unsigned long *) raw_codedbuf + 4); /* skip 4DWs */
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "coded buffer size %d\n", p->size);
            break;

        case VAProfileJPEGBaseline:
            /* 3~6 segment
                 */
            pnw_jpeg_AppendMarkers(obj_context, raw_codedbuf);
            next_buf_off = 0;
            /*Max resolution 4096x4096 use 6 segments*/
            for (i = 0; i < PNW_JPEG_MAX_SCAN_NUM + 1; i++) {
                p->size = *(unsigned long *)((unsigned long)raw_codedbuf + next_buf_off);
                p->buf = (unsigned char *)((unsigned long *)((unsigned long)raw_codedbuf + next_buf_off) + 4);  /* skip 4DWs */
                next_buf_off = *((unsigned long *)((unsigned long)raw_codedbuf + next_buf_off) + 3);

                drv_debug_msg(VIDEO_DEBUG_GENERAL, "JPEG coded buffer segment %d size: %d\n", i, p->size);
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "JPEG coded buffer next segment %d offset: %d\n", i + 1, next_buf_off);

                if (next_buf_off == 0) {
                    p->next = NULL;
                    break;
                } else
                    p->next = &p[1];
                p++;
            }
            break;

        default:
            drv_debug_msg(VIDEO_DEBUG_ERROR, "unexpected case\n");

            psb_buffer_unmap(obj_buffer->psb_buffer);
            obj_buffer->buffer_data = NULL;
            break;
        }
    }
#endif

    return 0;
}