/*
 * Copyright (c) 2011 Intel Corporation. All Rights Reserved.
 *
 * 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>
 *    Zhaohan Ren  <zhaohan.ren@intel.com>
 *    Jason Hu <jason.hu@intel.com>
 *
 */

#ifndef ANDROID
#include <X11/Xutil.h>
#include <X11/extensions/Xrandr.h>
#include <va/va_dricommon.h>
#include "x11/psb_x11.h"
#include "x11/psb_xrandr.h"
#endif
#include <va/va_backend.h>
#include <dlfcn.h>
#include <stdlib.h>
#include "psb_output.h"
#include "psb_surface.h"
#include "psb_buffer.h"
#include "psb_surface_ext.h"
#include "pnw_rotate.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <wsbm/wsbm_manager.h>
#include "psb_drv_debug.h"
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <math.h>

#define INIT_DRIVER_DATA        psb_driver_data_p driver_data = (psb_driver_data_p) ctx->pDriverData;

#define SURFACE(id)     ((object_surface_p) object_heap_lookup( &driver_data->surface_heap, id ))
#define BUFFER(id)  ((object_buffer_p) object_heap_lookup( &driver_data->buffer_heap, id ))
#define IMAGE(id)  ((object_image_p) object_heap_lookup( &driver_data->image_heap, id ))
#define SUBPIC(id)  ((object_subpic_p) object_heap_lookup( &driver_data->subpic_heap, id ))
#define CONTEXT(id) ((object_context_p) object_heap_lookup( &driver_data->context_heap, id ))


/* surfaces link list associated with a subpicture */
typedef struct _subpic_surface {
    VASurfaceID surface_id;
    struct _subpic_surface *next;
} subpic_surface_s, *subpic_surface_p;


static VAImageFormat psb__SubpicFormat[] = {
    psb__ImageRGBA,
    //psb__ImageAYUV,
    //psb__ImageAI44
};

static VAImageFormat psb__CreateImageFormat[] = {
    psb__ImageNV12,
    psb__ImageRGBA,
    //psb__ImageAYUV,
    //psb__ImageAI44,
    psb__ImageYV16,
    psb__ImageYV32
};

unsigned char *psb_x11_output_init(VADriverContextP ctx);
VAStatus psb_x11_output_deinit(VADriverContextP ctx);
unsigned char *psb_android_output_init(VADriverContextP ctx);
VAStatus psb_android_output_deinit(VADriverContextP ctx);

int psb_coverlay_init(VADriverContextP ctx);
int psb_coverlay_deinit(VADriverContextP ctx);

VAStatus psb_initOutput(VADriverContextP ctx)
{
    INIT_DRIVER_DATA;
    unsigned char *ws_priv = NULL;
    char env_value[1024];

    pthread_mutex_init(&driver_data->output_mutex, NULL);

    if (psb_parse_config("PSB_VIDEO_PUTSURFACE_DUMMY", &env_value[0]) == 0) {
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "vaPutSurface: dummy mode, return directly\n");
        driver_data->dummy_putsurface = 0;

        return VA_STATUS_SUCCESS;
    }

    if (psb_parse_config("PSB_VIDEO_FPS", &env_value[0]) == 0) {
        driver_data->fixed_fps = atoi(env_value);
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Throttling at FPS=%d\n", driver_data->fixed_fps);
    } else
        driver_data->fixed_fps = 0;

    driver_data->outputmethod_checkinterval = 1;
    if (psb_parse_config("PSB_VIDEO_INTERVAL", &env_value[0]) == 0) {
        driver_data->outputmethod_checkinterval = atoi(env_value);
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Check output method at %d frames interval\n",
                                 driver_data->outputmethod_checkinterval);
    }

    driver_data->cur_displaying_surface = VA_INVALID_SURFACE;
    driver_data->last_displaying_surface = VA_INVALID_SURFACE;

    psb_InitOutLoop(ctx);

#ifdef ANDROID
    ws_priv = psb_android_output_init(ctx);
    driver_data->is_android = 1;
#else
    ws_priv = psb_x11_output_init(ctx);
    driver_data->is_android = 0;
#endif
    driver_data->ws_priv = ws_priv;


#if 0
    //use client textureblit
    if (driver_data->ctexture == 1) {
        int ret = psb_ctexture_init(ctx);
        if (ret != 0)
            driver_data->ctexture = 0;
    }
#endif

    /*
    //use texture streaming
    if (driver_data->ctexstreaming == 1)
    psb_ctexstreaing_init(ctx);
    */

    return VA_STATUS_SUCCESS;
}

VAStatus psb_deinitOutput(
    VADriverContextP ctx
)
{
    INIT_DRIVER_DATA;

#if 0
    //use client textureblit
    if (driver_data->ctexture == 1)
        psb_ctexture_deinit(ctx);
#endif

    if (driver_data->coverlay_init) {
        psb_coverlay_deinit(ctx);
        driver_data->coverlay_init = 0;
    }

#ifndef ANDROID
    psb_x11_output_deinit(ctx);
#else
    psb_android_output_deinit(ctx);
#endif
    /* free here, but allocate in window system specific */
    free(driver_data->ws_priv);
    /*
    //use texture streaming
    if (driver_data->ctexstreaming == 1)
        psb_ctexstreaing_deinit(ctx);
    */
    /* clean the displaying surface information in kernel */
#ifndef _FOR_FPGA_
    psb_surface_set_displaying(driver_data, 0, 0, NULL);
#endif
    pthread_mutex_destroy(&driver_data->output_mutex);

    return VA_STATUS_SUCCESS;
}

#ifndef VA_STATUS_ERROR_INVALID_IMAGE_FORMAT
#define VA_STATUS_ERROR_INVALID_IMAGE_FORMAT VA_STATUS_ERROR_UNKNOWN
#endif

static VAImageFormat *psb__VAImageCheckFourCC(
    VAImageFormat       *src_format,
    VAImageFormat       *dst_format,
    int                 dst_num
)
{
    int i;
    if (NULL == src_format || dst_format == NULL) {
        return NULL;
    }

    /* check VAImage at first */
    for (i = 0; i < dst_num; i++) {
        if (dst_format[i].fourcc == src_format->fourcc)
            return &dst_format[i];
    }

    drv_debug_msg(VIDEO_DEBUG_ERROR, "Unsupport fourcc 0x%x\n", src_format->fourcc);
    return NULL;
}

static void psb__VAImageCheckRegion(
    object_surface_p surface,
    VAImage *image,
    int *src_x,
    int *src_y,
    int *dest_x,
    int *dest_y,
    int *width,
    int *height
)
{
    /* check for image */
    if (*src_x < 0) *src_x = 0;
    if (*src_x > image->width) *src_x = image->width - 1;
    if (*src_y < 0) *src_y = 0;
    if (*src_y > image->height) *src_y = image->height - 1;

    if (((*width) + (*src_x)) > image->width) *width = image->width - *src_x;
    if (((*height) + (*src_y)) > image->height) *height = image->height - *src_x;

    /* check for surface */
    if (*dest_x < 0) *dest_x = 0;
    if (*dest_x > surface->width) *dest_x = surface->width - 1;
    if (*dest_y < 0) *dest_y = 0;
    if (*dest_y > surface->height) *dest_y = surface->height - 1;

    if (((*width) + (*dest_x)) > surface->width) *width = surface->width - *dest_x;
    if (((*height) + (*dest_y)) > surface->height) *height = surface->height - *dest_x;
}


VAStatus psb_QueryImageFormats(
    VADriverContextP __maybe_unused ctx,
    VAImageFormat *format_list,        /* out */
    int *num_formats           /* out */
)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    CHECK_INVALID_PARAM(format_list == NULL);
    CHECK_INVALID_PARAM(num_formats == NULL);

    memcpy(format_list, psb__CreateImageFormat, sizeof(psb__CreateImageFormat));
    *num_formats = PSB_MAX_IMAGE_FORMATS;

    return VA_STATUS_SUCCESS;
}

inline int min_POT(int n)
{
    if ((n & (n - 1)) == 0) /* already POT */
        return n;

    return n |= n >> 16, n |= n >> 8, n |= n >> 4, n |= n >> 2, n |= n >> 1, n + 1;
    /* return ((((n |= n>>16) |= n>>8) |= n>>4) |= n>>2) |= n>>1, n + 1; */
}

VAStatus psb_CreateImage(
    VADriverContextP ctx,
    VAImageFormat *format,
    int width,
    int height,
    VAImage *image     /* out */
)
{
    INIT_DRIVER_DATA;
    VAImageID imageID;
    object_image_p obj_image;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    VAImageFormat *img_fmt;
    int pitch_pot;

    (void)driver_data;

    img_fmt = psb__VAImageCheckFourCC(format, psb__CreateImageFormat,
                                      sizeof(psb__CreateImageFormat) / sizeof(VAImageFormat));
    if (img_fmt == NULL)
        return VA_STATUS_ERROR_UNKNOWN;

    CHECK_INVALID_PARAM(image == NULL);

    imageID = object_heap_allocate(&driver_data->image_heap);
    obj_image = IMAGE(imageID);
    CHECK_ALLOCATION(obj_image);

    MEMSET_OBJECT(obj_image, struct object_image_s);

    obj_image->image.image_id = imageID;
    obj_image->image.format = *img_fmt;
    obj_image->subpic_ref = 0;

    pitch_pot = min_POT(width);

    switch (format->fourcc) {
    case VA_FOURCC_NV12: {
        obj_image->image.width = width;
        obj_image->image.height = height;
        obj_image->image.data_size = pitch_pot * height /*Y*/ + 2 * (pitch_pot / 2) * (height / 2);/*UV*/
        obj_image->image.num_planes = 2;
        obj_image->image.pitches[0] = pitch_pot;
        obj_image->image.pitches[1] = pitch_pot;
        obj_image->image.offsets[0] = 0;
        obj_image->image.offsets[1] = pitch_pot * height;
        obj_image->image.num_palette_entries = 0;
        obj_image->image.entry_bytes = 0;
        obj_image->image.component_order[0] = 'Y';
        obj_image->image.component_order[1] = 'U';/* fixed me: packed UV packed here! */
        obj_image->image.component_order[2] = 'V';
        obj_image->image.component_order[3] = '\0';
        break;
    }
    case VA_FOURCC_AYUV: {
        obj_image->image.width = width;
        obj_image->image.height = height;
        obj_image->image.data_size = 4 * pitch_pot * height;
        obj_image->image.num_planes = 1;
        obj_image->image.pitches[0] = 4 * pitch_pot;
        obj_image->image.num_palette_entries = 0;
        obj_image->image.entry_bytes = 0;
        obj_image->image.component_order[0] = 'V';
        obj_image->image.component_order[1] = 'U';
        obj_image->image.component_order[2] = 'Y';
        obj_image->image.component_order[3] = 'A';
        break;
    }
    case VA_FOURCC_RGBA: {
        obj_image->image.width = width;
        obj_image->image.height = height;
        obj_image->image.data_size = 4 * pitch_pot * height;
        obj_image->image.num_planes = 1;
        obj_image->image.pitches[0] = 4 * pitch_pot;
        obj_image->image.num_palette_entries = 0;
        obj_image->image.entry_bytes = 0;
        obj_image->image.component_order[0] = 'R';
        obj_image->image.component_order[1] = 'G';
        obj_image->image.component_order[2] = 'B';
        obj_image->image.component_order[3] = 'A';
        break;
    }
    case VA_FOURCC_AI44: {
        obj_image->image.width = width;
        obj_image->image.height = height;
        obj_image->image.data_size = pitch_pot * height;/* one byte one element */
        obj_image->image.num_planes = 1;
        obj_image->image.pitches[0] = pitch_pot;
        obj_image->image.num_palette_entries = 16;
        obj_image->image.entry_bytes = 4; /* AYUV */
        obj_image->image.component_order[0] = 'I';
        obj_image->image.component_order[1] = 'A';
        obj_image->image.component_order[2] = '\0';
        obj_image->image.component_order[3] = '\0';
        break;
    }
    case VA_FOURCC_IYUV: {
        obj_image->image.width = width;
        obj_image->image.height = height;
        obj_image->image.data_size = pitch_pot * height /*Y*/ + 2 * (pitch_pot / 2) * (height / 2);/*UV*/
        obj_image->image.num_planes = 3;
        obj_image->image.pitches[0] = pitch_pot;
        obj_image->image.pitches[1] = pitch_pot / 2;
        obj_image->image.pitches[2] = pitch_pot / 2;
        obj_image->image.offsets[0] = 0;
        obj_image->image.offsets[1] = pitch_pot * height;
        obj_image->image.offsets[2] = pitch_pot * height + (pitch_pot / 2) * (height / 2);
        obj_image->image.num_palette_entries = 0;
        obj_image->image.entry_bytes = 0;
        obj_image->image.component_order[0] = 'Y';
        obj_image->image.component_order[1] = 'U';
        obj_image->image.component_order[2] = 'V';
        obj_image->image.component_order[3] = '\0';
        break;
    }
    case VA_FOURCC_YV32: {
        obj_image->image.width = width;
        obj_image->image.height = height;
        obj_image->image.data_size = 4 * pitch_pot * height;
        obj_image->image.num_planes = 4;
        obj_image->image.pitches[0] = pitch_pot;
        obj_image->image.pitches[1] = pitch_pot;
        obj_image->image.pitches[2] = pitch_pot;
        obj_image->image.extra_pitch = pitch_pot;
        obj_image->image.offsets[0] = 0;
        obj_image->image.offsets[1] = pitch_pot * height;
        obj_image->image.offsets[2] = pitch_pot * height * 2;
        obj_image->image.extra_offset = pitch_pot * height * 3;
        obj_image->image.num_palette_entries = 0;
        obj_image->image.entry_bytes = 0;
        obj_image->image.component_order[0] = 'V';
        obj_image->image.component_order[1] = 'U';
        obj_image->image.component_order[2] = 'Y';
        obj_image->image.component_order[3] = 'A';
        break;
    }
    default: {
        vaStatus = VA_STATUS_ERROR_INVALID_IMAGE_FORMAT;
        break;
    }
    }

    if (VA_STATUS_SUCCESS == vaStatus) {
        /* create the buffer */
        vaStatus = psb__CreateBuffer(driver_data, NULL, VAImageBufferType,
                                     obj_image->image.data_size, 1, NULL, &obj_image->image.buf);
    }

    obj_image->derived_surface = 0;

    if (VA_STATUS_SUCCESS != vaStatus) {
        object_heap_free(&driver_data->image_heap, (object_base_p) obj_image);
    } else {
        memcpy(image, &obj_image->image, sizeof(VAImage));
    }

    return vaStatus;
}

static int psb_CheckIEDStatus(VADriverContextP ctx)
{
    INIT_DRIVER_DATA;
    struct drm_lnc_video_getparam_arg arg;
    unsigned long temp;
    int ret = 0;

    /* not settled, we get it from current HW FRAMESKIP flag */
    arg.key = IMG_VIDEO_IED_STATE;
    arg.value = (uint64_t)((unsigned long) & temp);
    ret = drmCommandWriteRead(driver_data->drm_fd, driver_data->getParamIoctlOffset,
                              &arg, sizeof(arg));
    if (ret == 0) {
        if (temp == 1) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "IED is enabled, image is encrypted.\n");
            return 1;
        } else {
            return 0;
        }
    } else {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Failed to call IMG_VIDEO_IED_STATE.\n");
        return -1;
    }
}

VAStatus psb_DeriveImage(
    VADriverContextP ctx,
    VASurfaceID surface,
    VAImage *image     /* out */
)
{
    INIT_DRIVER_DATA;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    VABufferID bufferID;
    object_buffer_p obj_buffer;
    VAImageID imageID;
    object_image_p obj_image;
    object_surface_p obj_surface = SURFACE(surface);
    unsigned int fourcc, fourcc_index = ~0, i;
    uint32_t srf_buf_ofs = 0;

    CHECK_SURFACE(obj_surface);
    CHECK_INVALID_PARAM(image == NULL);
    /* Can't derive image from reconstrued frame which is in tiled format */
    if (obj_surface->is_ref_surface == 1 || obj_surface->is_ref_surface == 2) {
	if (getenv("PSB_VIDEO_IGNORE_TILED_FORMAT")) {
	    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Ignore tiled memory format" \
			"of rec-frames\n");
	} else {
	    drv_debug_msg(VIDEO_DEBUG_ERROR, "Can't derive reference surface" \
			"which is tiled format\n");
	    return VA_STATUS_ERROR_OPERATION_FAILED;
	}
    }

    if (IS_MFLD(driver_data) && (psb_CheckIEDStatus(ctx) == 1)) {
        vaStatus = VA_STATUS_ERROR_INVALID_SURFACE;
        return vaStatus;
    }

    fourcc = obj_surface->psb_surface->extra_info[4];
    for (i = 0; i < PSB_MAX_IMAGE_FORMATS; i++) {
        if (psb__CreateImageFormat[i].fourcc == fourcc) {
            fourcc_index = i;
            break;
        }
    }
    if (i == PSB_MAX_IMAGE_FORMATS) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Can't support the Fourcc\n");
        vaStatus = VA_STATUS_ERROR_OPERATION_FAILED;
        return vaStatus;
    }

    /* create the image */
    imageID = object_heap_allocate(&driver_data->image_heap);
    obj_image = IMAGE(imageID);
    CHECK_ALLOCATION(obj_image);

    MEMSET_OBJECT(obj_image, struct object_image_s);

    /* create a buffer to represent surface buffer */
    bufferID = object_heap_allocate(&driver_data->buffer_heap);
    obj_buffer = BUFFER(bufferID);
    if (NULL == obj_buffer) {
        object_heap_free(&driver_data->image_heap, (object_base_p) obj_image);
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DEBUG_FAILURE;
        return vaStatus;
    }
    MEMSET_OBJECT(obj_buffer, struct object_buffer_s);

    obj_buffer->type = VAImageBufferType;
    obj_buffer->buffer_data = NULL;
    obj_buffer->psb_buffer = &obj_surface->psb_surface->buf;
    obj_buffer->size = obj_surface->psb_surface->size;
    obj_buffer->max_num_elements = 0;
    obj_buffer->alloc_size = obj_buffer->size;

    /* fill obj_image data structure */
    obj_image->image.image_id = imageID;
    obj_image->image.format = psb__CreateImageFormat[fourcc_index];
    obj_image->subpic_ref = 0;

    obj_image->image.buf = bufferID;
    obj_image->image.width = obj_surface->width;
    obj_image->image.height = obj_surface->height;
    obj_image->image.data_size = obj_surface->psb_surface->size;

    srf_buf_ofs = obj_surface->psb_surface->buf.buffer_ofs;

    switch (fourcc) {
    case VA_FOURCC_NV12: {
        obj_image->image.num_planes = 2;
        obj_image->image.pitches[0] = obj_surface->psb_surface->stride;
        obj_image->image.pitches[1] = obj_surface->psb_surface->stride;

        obj_image->image.offsets[0] = srf_buf_ofs;
        obj_image->image.offsets[1] = srf_buf_ofs + obj_surface->height * obj_surface->psb_surface->stride;
        obj_image->image.num_palette_entries = 0;
        obj_image->image.entry_bytes = 0;
        obj_image->image.component_order[0] = 'Y';
        obj_image->image.component_order[1] = 'U';/* fixed me: packed UV packed here! */
        obj_image->image.component_order[2] = 'V';
        obj_image->image.component_order[3] = '\0';
        break;
    }
    case VA_FOURCC_YV16: {
        obj_image->image.num_planes = 3;
        obj_image->image.pitches[0] = obj_surface->psb_surface->stride;
        obj_image->image.pitches[1] = obj_surface->psb_surface->stride / 2;
        obj_image->image.pitches[2] = obj_surface->psb_surface->stride / 2;

        obj_image->image.offsets[0] = srf_buf_ofs;
        obj_image->image.offsets[1] = srf_buf_ofs + obj_surface->height * obj_surface->psb_surface->stride;
        obj_image->image.offsets[2] = srf_buf_ofs + obj_surface->height * obj_surface->psb_surface->stride * 3 / 2;
        obj_image->image.num_palette_entries = 0;
        obj_image->image.entry_bytes = 0;
        obj_image->image.component_order[0] = 'Y';
        obj_image->image.component_order[1] = 'V';/* fixed me: packed UV packed here! */
        obj_image->image.component_order[2] = 'U';
        obj_image->image.component_order[3] = '\0';
        break;
    }
    case VA_FOURCC_YV32: {
        obj_image->image.num_planes = 3;
        obj_image->image.pitches[0] = obj_surface->psb_surface->stride;
        obj_image->image.pitches[1] = obj_surface->psb_surface->stride;
        obj_image->image.pitches[2] = obj_surface->psb_surface->stride;

        obj_image->image.offsets[0] = srf_buf_ofs;
        obj_image->image.offsets[1] = srf_buf_ofs + obj_surface->height * obj_surface->psb_surface->stride;
        obj_image->image.offsets[2] = srf_buf_ofs + obj_surface->height * obj_surface->psb_surface->stride * 2;
        obj_image->image.num_palette_entries = 0;
        obj_image->image.entry_bytes = 0;
        obj_image->image.component_order[0] = 'Y';
        obj_image->image.component_order[1] = 'U';/* fixed me: packed UV packed here! */
        obj_image->image.component_order[2] = 'V';
        obj_image->image.component_order[3] = '\0';
        break;
    }
    default:
        break;
    }

    obj_image->derived_surface = surface; /* this image is derived from a surface */
    obj_surface->derived_imgcnt++;

    memcpy(image, &obj_image->image, sizeof(VAImage));

    return vaStatus;
}

VAStatus psb__destroy_image(psb_driver_data_p driver_data, object_image_p obj_image)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    if (obj_image->subpic_ref > 0) {
        vaStatus = VA_STATUS_ERROR_OPERATION_FAILED;
        return vaStatus;
    }

    object_surface_p obj_surface = SURFACE(obj_image->derived_surface);

    if (obj_surface == NULL) { /* destroy the buffer */
        object_buffer_p obj_buffer = BUFFER(obj_image->image.buf);
        CHECK_BUFFER(obj_buffer);
        psb__suspend_buffer(driver_data, obj_buffer);
    } else {
        object_buffer_p obj_buffer = BUFFER(obj_image->image.buf);
        object_heap_free(&driver_data->buffer_heap, &obj_buffer->base);
        obj_surface->derived_imgcnt--;
    }
    object_heap_free(&driver_data->image_heap, (object_base_p) obj_image);

    return VA_STATUS_SUCCESS;
}

VAStatus psb_DestroyImage(
    VADriverContextP ctx,
    VAImageID image
)
{
    INIT_DRIVER_DATA
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    object_image_p obj_image;

    obj_image = IMAGE(image);
    CHECK_IMAGE(obj_image);
    return psb__destroy_image(driver_data, obj_image);
}

VAStatus psb_SetImagePalette(
    VADriverContextP ctx,
    VAImageID image,
    /*
     * pointer to an array holding the palette data.  The size of the array is
     * num_palette_entries * entry_bytes in size.  The order of the components
     * in the palette is described by the component_order in VAImage struct
     */
    unsigned char *palette
)
{
    INIT_DRIVER_DATA;
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    object_image_p obj_image = IMAGE(image);
    CHECK_IMAGE(obj_image);

    if (obj_image->image.format.fourcc != VA_FOURCC_AI44) {
        /* only support AI44 palette */
        vaStatus = VA_STATUS_ERROR_OPERATION_FAILED;
        return vaStatus;
    }

    if (obj_image->image.num_palette_entries > 16) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "image.num_palette_entries(%d) is too big\n", obj_image->image.num_palette_entries);
        memcpy(obj_image->palette, palette, 16);
    } else
        memcpy(obj_image->palette, palette, obj_image->image.num_palette_entries * sizeof(unsigned int));

    return vaStatus;
}

static VAStatus lnc_unpack_topaz_rec(int src_width, int src_height,
                                     unsigned char *p_srcY, unsigned char *p_srcUV,
                                     unsigned char *p_dstY, unsigned char *p_dstU, unsigned char *p_dstV,
                                     int dstY_stride, int dstU_stride, int dstV_stride,
                                     int surface_height)
{
    unsigned char *tmp_dstY = NULL;
    unsigned char *tmp_dstUV = NULL;

    int n, i, index;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Unpack reconstructed frame to image\n");

    /* do this one column at a time. */
    tmp_dstY = (unsigned char *)calloc(1, 16 * src_height);
    if (tmp_dstY == NULL)
        return  VA_STATUS_ERROR_ALLOCATION_FAILED;

    tmp_dstUV = (unsigned char*)calloc(1, 16 * src_height / 2);
    if (tmp_dstUV == NULL) {
        free(tmp_dstY);
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

    /*  Copy Y data */
    for (n = 0; n < src_width / 16; n++) {
        memcpy((void*)tmp_dstY, p_srcY, 16 * src_height);
        p_srcY += (16 * surface_height);
        for (i = 0; i < src_height; i++) {
            memcpy(p_dstY + dstY_stride * i + n * 16, tmp_dstY + 16 * i, 16);
        }
    }

    /* Copy U/V data */
    for (n = 0; n < src_width / 16; n++) {
        memcpy((void*)tmp_dstUV, p_srcUV, 16 * src_height / 2);
        p_srcUV += (16 * surface_height / 2);
        for (i = 0; i < src_height / 2; i++) {
            for (index = 0; index < 8; index++) {
                p_dstU[i*dstU_stride + n*8 + index] = tmp_dstUV[index*2 + i*16];
                p_dstV[i*dstV_stride + n*8 + index] = tmp_dstUV[index*2 + i*16+1];
            }
        }
    }
    if (tmp_dstY)
        free(tmp_dstY);
    if (tmp_dstUV)
        free(tmp_dstUV);

    return VA_STATUS_SUCCESS;
}

/*
* Convert the memroy format from tiled to linear
*/
static VAStatus tng_unpack_vsp_rec(
    int src_width, int src_height,
    unsigned char *p_srcY, unsigned char *p_srcUV,
    unsigned char *p_dstY, unsigned char *p_dstU,
    int dstY_stride, int dstU_stride, int __maybe_unused dstV_stride,
    int __maybe_unused surface_height)
{
    unsigned char *tmp_dstY = p_dstY;
    unsigned char *tmp_dstU = p_dstU;

    int n, t,x,y;

   //"	Y_address(x,y) =    Y_base + (((H            +64+63)/64) * (x/64) + (y/64))*4096 + (y%64)*64 + (x%64)
   //"	U_address(x,y) = UV_base + ((((H+1)/2+32+63)/64) * (x/32) + (y/64))*4096 + (y%64)*64 + (x%32)*2
   //"	V_address(x,y) = UV_base + ((((H+1)/2+32+63)/64) * (x/32) + (y/64))*4096 + (y%64)*64 + (x%32)*2 + 1

    /*  Copy Y data */
    for (y = 32; y < src_height + 32; y++) {
        for (x= 32;x < src_width + 32; x++) {
            * tmp_dstY++ =  *(p_srcY + (((src_height+64+63)/64) * (x/64) + (y/64))*4096 + (y%64)*64 + (x%64));
            }
            tmp_dstY += dstY_stride - src_width;
    }

    /*  Copy UV data */
    for (y = 16; y < 16 + ((src_height+1)>>1) ; y++) {
        for (x= 16;x < 16 + ( (src_width+1)>>1); x++) {
            * tmp_dstU++ = * (p_srcUV + ((((src_height+1)/2+32+63)/64) * (x/32) + (y/64))*4096 + (y%64)*64 + (x%32)*2);
            * tmp_dstU++ = * (p_srcUV + ((((src_height+1)/2+32+63)/64) * (x/32) + (y/64))*4096 + (y%64)*64 + (x%32)*2 +1);
            }
           tmp_dstU += dstU_stride - ((src_width));
    }

    return VA_STATUS_SUCCESS;
}
/*
* Convert the memroy format from tiled to linear
*/
static VAStatus tng_unpack_topaz_rec(
    int src_width, int src_height,
    unsigned char *p_srcY, unsigned char *p_srcUV,
    unsigned char *p_dstY, unsigned char *p_dstU, unsigned char *p_dstV,
    int dstY_stride, int __maybe_unused dstU_stride, int __maybe_unused dstV_stride,
    int __maybe_unused surface_height)
{
    unsigned char *tmp_dstY = p_dstY;
    unsigned char *tmp_dstU = p_dstU;
    unsigned char *tmp_dstV = p_dstV;
    unsigned char *tmp_srcY = p_srcY;
    unsigned char *tmp_srcX = p_srcUV;

    int i, j, n, t;
    int mb_src_y_w = src_width >> 4;
    int mb_src_y_h = src_height >> 5;
    int mb_src_y_p = src_height - (mb_src_y_h << 5);

    /*  Copy Y data */
    for (j = 0; j < mb_src_y_h; j++) {
        tmp_dstY = p_dstY + j * dstY_stride * 32;
        for (i = 0; i < mb_src_y_w; i++) {
            for (n = 0; n < 32; n++) {
                memcpy(tmp_dstY + dstY_stride * n, tmp_srcY,16);
                tmp_srcY += 16;
            }
            tmp_dstY += 16;
        }
    }

    if(mb_src_y_p != 0) {
        tmp_dstY = p_dstY + j * dstY_stride * 32;
        for (i = 0; i < mb_src_y_w; i++) {
            for (n = 0; n < mb_src_y_p; n++) {
                memcpy(tmp_dstY + dstY_stride * n, tmp_srcY,16);
                tmp_srcY += 16;
            }
            for (; n < 32; n++) {
                tmp_srcY += 16;
            }
            tmp_dstY += 16;
        }
    }

    for (j = 0; j < mb_src_y_h; j++) {
        tmp_dstU = p_dstU + j * dstY_stride * 16;
        for (i = 0; i < mb_src_y_w; i++) {
            for (n = 0; n < 16; n++) {
                for (t = 0; t < 16; t++) {
                    tmp_dstU[(n * dstY_stride) + t] = tmp_srcX[t];
                }
                tmp_srcX += 16;
            }
            tmp_dstU += 16;
        }
    }
    mb_src_y_p >>= 1;
    if(mb_src_y_p != 0) {
        tmp_dstU = p_dstU + j * dstY_stride * 16;
        for (i = 0; i < mb_src_y_w; i++) {
            for (n = 0; n < mb_src_y_p; n++) {
                for (t = 0; t < 16; t++) {
                   tmp_dstU[(n * dstY_stride) + t] = tmp_srcX[t];
                }
                tmp_srcX += 16;
            }

            for (; n < 16; n++) {
                tmp_srcX += 16;
            }

            tmp_dstU += 16;
        }
    }

    return VA_STATUS_SUCCESS;
}

VAStatus psb_GetImage(
    VADriverContextP ctx,
    VASurfaceID surface,
    int x,     /* coordinates of the upper left source pixel */
    int y,
    unsigned int width, /* width and height of the region */
    unsigned int height,
    VAImageID image_id
)
{
    INIT_DRIVER_DATA;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    int ret, src_x = 0, src_y = 0, dest_x = 0, dest_y = 0;

    (void)driver_data;
    (void)lnc_unpack_topaz_rec;

    object_image_p obj_image = IMAGE(image_id);
    CHECK_IMAGE(obj_image);

    if (IS_MFLD(driver_data) && (psb_CheckIEDStatus(ctx) == 1)) {
        vaStatus = VA_STATUS_ERROR_INVALID_SURFACE;
        return vaStatus;
    }

    if (obj_image->image.format.fourcc != VA_FOURCC_NV12) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "target VAImage fourcc should be NV12 or IYUV\n");
        vaStatus = VA_STATUS_ERROR_OPERATION_FAILED;
        return vaStatus;
    }

    object_surface_p obj_surface = SURFACE(surface);
    CHECK_SURFACE(obj_surface);

    psb__VAImageCheckRegion(obj_surface, &obj_image->image, &src_x, &src_y, &dest_x, &dest_y,
                            (int *)&width, (int *)&height);

    psb_surface_p psb_surface = obj_surface->psb_surface;
    unsigned char *surface_data;
    ret = psb_buffer_map(&psb_surface->buf, &surface_data);
    if (ret) {
        return VA_STATUS_ERROR_UNKNOWN;
    }

    object_buffer_p obj_buffer = BUFFER(obj_image->image.buf);
    CHECK_BUFFER(obj_buffer);

    unsigned char *image_data;
    ret = psb_buffer_map(obj_buffer->psb_buffer, &image_data);
    if (ret) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Map buffer failed\n");

        psb_buffer_unmap(&psb_surface->buf);
        return VA_STATUS_ERROR_UNKNOWN;
    }


    image_data += obj_surface->psb_surface->buf.buffer_ofs;


    switch (obj_image->image.format.fourcc) {
    case VA_FOURCC_NV12: {
        unsigned char *src_y, *src_uv, *dst_y, *dst_uv;
	unsigned char *dst_u, *dst_v;
        unsigned int i;

	/*
	 * For reconstructed frame, tiled to linear conversion
	 * must be done.
	*/
	if (obj_surface->is_ref_surface == 1) {
	    src_y = surface_data + y * psb_surface->stride + x;
	    src_uv = surface_data + ((height + 0x1f) & (~0x1f)) * width;

	    dst_y = image_data;
	    dst_u = image_data + obj_image->image.offsets[1],
	    dst_v = dst_u + (height * width) / 4;

	    tng_unpack_topaz_rec(width, height, \
				 src_y, src_uv, \
			         dst_y, dst_u, dst_v, \
			         obj_image->image.pitches[0], \
			         obj_image->image.width / 2, \
			         obj_image->image.width / 2, \
			         obj_surface->height);
	} else if (obj_surface->is_ref_surface == 2) {
	    src_y = surface_data + y * psb_surface->stride + x;
	    src_uv = surface_data + ((height + 2*32 + 63)/64*64) * ((width  + 2*32 + 63)/64*64);
	    dst_y = image_data;
	    dst_u = image_data +  obj_image->image.offsets[1];

	    tng_unpack_vsp_rec(width, height, \
				 src_y, src_uv, \
			         dst_y, dst_u, \
			         obj_image->image.pitches[0], \
			         obj_image->image.pitches[1], \
			         obj_image->image.pitches[1], \
			         obj_surface->height);
	} else{
            /* copy Y plane */
            dst_y = image_data;
            src_y = surface_data + y * psb_surface->stride + x;
            for (i = 0; i < height; i++)  {
		memcpy(dst_y, src_y, width);
		dst_y += obj_image->image.pitches[0];
		src_y += psb_surface->stride;
            }

	    /* copy UV plane */
	    dst_uv = image_data + obj_image->image.offsets[1];
            src_uv = surface_data + psb_surface->stride * obj_surface->height + (y / 2) * psb_surface->stride + x;;
            for (i = 0; i < obj_image->image.height / 2; i++) {
		memcpy(dst_uv, src_uv, width);
		dst_uv += obj_image->image.pitches[1];
		src_uv += psb_surface->stride;
	    }
	}
        break;
    }
#if 0
    case VA_FOURCC_IYUV: {
        unsigned char *source_y, *dst_y;
        unsigned char *source_uv, *source_u, *source_v, *dst_u, *dst_v;
        unsigned int i;

        if (psb_surface->extra_info[4] == VA_FOURCC_IREC) {
            /* copy Y plane */
            dst_y = image_data + obj_image->image.offsets[0] + src_y * obj_image->image.pitches[0] + src_x;
            dst_u = image_data + obj_image->image.offsets[1] + src_y * obj_image->image.pitches[1] + src_x;
            dst_v = image_data + obj_image->image.offsets[2] + src_y * obj_image->image.pitches[2] + src_x;

            source_y = surface_data + dest_y * psb_surface->stride + dest_x;
            source_uv = surface_data + obj_surface->height * psb_surface->stride
                        + dest_y * (psb_surface->stride / 2) + dest_x;

            vaStatus = lnc_unpack_topaz_rec(width, height, source_y, source_uv,
                                            dst_y, dst_u, dst_v,
                                            obj_image->image.pitches[0],
                                            obj_image->image.pitches[1],
                                            obj_image->image.pitches[2],
                                            obj_surface->height);
        }

        break;
    }
#endif
    default:
        break;
    }
    psb_buffer_unmap(obj_buffer->psb_buffer);
    psb_buffer_unmap(&psb_surface->buf);

    return vaStatus;
}

static VAStatus psb_PutImage2(
    VADriverContextP ctx,
    VASurfaceID surface,
    VAImageID image_id,
    int src_x,
    int src_y,
    unsigned int width,
    unsigned int height,
    int dest_x,
    int dest_y
)
{
    INIT_DRIVER_DATA;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    int ret;

    object_image_p obj_image = IMAGE(image_id);
    CHECK_IMAGE(obj_image);

    object_surface_p obj_surface = SURFACE(surface);
    CHECK_SURFACE(obj_surface);

    if (obj_image->image.format.fourcc != VA_FOURCC_NV12) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "target VAImage fourcc should be NV12 or IYUV\n");
        vaStatus = VA_STATUS_ERROR_OPERATION_FAILED;
        return vaStatus;
    }

    psb__VAImageCheckRegion(obj_surface, &obj_image->image, &src_x, &src_y, &dest_x, &dest_y,
                            (int *)&width, (int *)&height);

    psb_surface_p psb_surface = obj_surface->psb_surface;
    unsigned char *surface_data;
    ret = psb_buffer_map(&psb_surface->buf, &surface_data);
    if (ret) {
        return VA_STATUS_ERROR_UNKNOWN;
    }

    object_buffer_p obj_buffer = BUFFER(obj_image->image.buf);
    CHECK_BUFFER(obj_buffer);

    unsigned char *image_data;
    ret = psb_buffer_map(obj_buffer->psb_buffer, &image_data);
    if (ret) {
        psb_buffer_unmap(&psb_surface->buf);
        return VA_STATUS_ERROR_UNKNOWN;
    }

    image_data += obj_surface->psb_surface->buf.buffer_ofs;

    switch (obj_image->image.format.fourcc) {
    case VA_FOURCC_NV12: {
        unsigned char *source_y, *src_uv, *dst_y, *dst_uv;
        unsigned int i;

        /* copy Y plane */
        source_y = image_data + obj_image->image.offsets[0] + src_y * obj_image->image.pitches[0] + src_x;
        dst_y = surface_data + dest_y * psb_surface->stride + dest_x;
        for (i = 0; i < height; i++)  {
            memcpy(dst_y, source_y, width);
            source_y += obj_image->image.pitches[0];
            dst_y += psb_surface->stride;
        }

        /* copy UV plane */
        src_uv = image_data + obj_image->image.offsets[1] + (src_y / 2) * obj_image->image.pitches[1] + src_x;
        dst_uv = surface_data + psb_surface->stride * obj_surface->height + (dest_y / 2) * psb_surface->stride + dest_x;
        for (i = 0; i < obj_image->image.height / 2; i++) {
            memcpy(dst_uv, src_uv, width);
            src_uv += obj_image->image.pitches[1];
            dst_uv += psb_surface->stride;
        }
        break;
    }
#if 0
    case VA_FOURCC_IYUV: {
        char *source_y, *dst_y;
        char *source_u, *source_v, *dst_u, *dst_v;
        unsigned int i;

        /* copy Y plane */
        source_y = image_data + obj_image->image.offsets[0] + src_y * obj_image->image.pitches[0] + src_x;
        source_u = image_data + obj_image->image.offsets[1] + src_y * obj_image->image.pitches[1] + src_x;
        source_v = image_data + obj_image->image.offsets[2] + src_y * obj_image->image.pitches[2] + src_x;

        dst_y = surface_data + dest_y * psb_surface->stride + dest_x;
        dst_u = surface_data + obj_surface->height * psb_surface->stride
                + dest_y * (psb_surface->stride / 2) + dest_x;
        dst_v = surface_data + obj_surface->height * psb_surface->stride
                + (obj_surface->height / 2) * (psb_surface->stride / 2)
                + dest_y * (psb_surface->stride / 2) + dest_x;

        for (i = 0; i < height; i++)  {
            memcpy(dst_y, source_y, width);
            source_y += obj_image->image.pitches[0];
            dst_y += psb_surface->stride;
        }

        /* copy UV plane */
        for (i = 0; i < obj_image->image.height / 2; i++) {
            memcpy(dst_u, source_u, width);
            memcpy(dst_v, source_v, width);

            source_u += obj_image->image.pitches[1];
            source_v += obj_image->image.pitches[2];

            dst_u += psb_surface->stride / 2;
            dst_v += psb_surface->stride / 2;
        }
        break;
    }
#endif
    default:
        break;
    }

    psb_buffer_unmap(obj_buffer->psb_buffer);
    psb_buffer_unmap(&psb_surface->buf);

    return VA_STATUS_SUCCESS;
}


static void psb__VAImageCheckRegion2(
    object_surface_p surface,
    VAImage *image,
    int *src_x,
    int *src_y,
    unsigned int *src_width,
    unsigned int *src_height,
    int *dest_x,
    int *dest_y,
    int *dest_width,
    int *dest_height
)
{
    /* check for image */
    if (*src_x < 0) *src_x = 0;
    if (*src_x > image->width) *src_x = image->width - 1;
    if (*src_y < 0) *src_y = 0;
    if (*src_y > image->height) *src_y = image->height - 1;

    if (((*src_width) + (*src_x)) > image->width) *src_width = image->width - *src_x;
    if (((*src_height) + (*src_y)) > image->height) *src_height = image->height - *src_x;

    /* check for surface */
    if (*dest_x < 0) *dest_x = 0;
    if (*dest_x > surface->width) *dest_x = surface->width - 1;
    if (*dest_y < 0) *dest_y = 0;
    if (*dest_y > surface->height) *dest_y = surface->height - 1;

    if (((*dest_width) + (*dest_x)) > (int)surface->width) *dest_width = surface->width - *dest_x;
    if (((*dest_height) + (*dest_y)) > (int)surface->height) *dest_height = surface->height - *dest_x;
}

VAStatus psb_PutImage(
    VADriverContextP ctx,
    VASurfaceID surface,
    VAImageID image_id,
    int src_x,
    int src_y,
    unsigned int src_width,
    unsigned int src_height,
    int dest_x,
    int dest_y,
    unsigned int dest_width,
    unsigned int dest_height
)
{
    INIT_DRIVER_DATA;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    int ret;
    CHECK_INVALID_PARAM(((int)src_width == -1) ||
                        ((int)src_height == -1) ||
                        ((int)dest_width == ~0) ||
                        ((int)dest_height == ~0));

    if ((src_width == dest_width) && (src_height == dest_height)) {
        /* Shortcut if scaling is not required */
        return psb_PutImage2(ctx, surface, image_id, src_x, src_y, src_width, src_height, dest_x, dest_y);
    }

    object_image_p obj_image = IMAGE(image_id);
    CHECK_IMAGE(obj_image);

    if (obj_image->image.format.fourcc != VA_FOURCC_NV12) {
        /* only support NV12 getImage/putImage */
        vaStatus = VA_STATUS_ERROR_OPERATION_FAILED;
        return vaStatus;
    }

    object_surface_p obj_surface = SURFACE(surface);
    CHECK_SURFACE(obj_surface);

    psb__VAImageCheckRegion2(obj_surface, &obj_image->image,
                             &src_x, &src_y, &src_width, &src_height,
                             &dest_x, &dest_y, (int *)&dest_width, (int *)&dest_height);

    psb_surface_p psb_surface = obj_surface->psb_surface;
    unsigned char *surface_data;
    ret = psb_buffer_map(&psb_surface->buf, &surface_data);
    if (ret) {
        return VA_STATUS_ERROR_UNKNOWN;
    }

    object_buffer_p obj_buffer = BUFFER(obj_image->image.buf);
    CHECK_BUFFER(obj_buffer);

    unsigned char *image_data;
    ret = psb_buffer_map(obj_buffer->psb_buffer, &image_data);
    if (ret) {
        psb_buffer_unmap(&psb_surface->buf);
        return VA_STATUS_ERROR_UNKNOWN;
    }

    /* just a prototype, the algorithm is ugly and not optimized */
    switch (obj_image->image.format.fourcc) {
    case VA_FOURCC_NV12: {
        unsigned char *source_y, *dst_y;
        unsigned short *source_uv, *dst_uv;
        unsigned int i, j;
        float xratio = (float) src_width / dest_width;
        float yratio = (float) src_height / dest_height;

        /* dst_y/dst_uv: Y/UV plane of destination */
        dst_y = (unsigned char *)(surface_data + dest_y * psb_surface->stride + dest_x);
        dst_uv = (unsigned short *)(surface_data + psb_surface->stride * obj_surface->height
                                    + (dest_y / 2) * psb_surface->stride + dest_x);

        for (j = 0; j < dest_height; j++)  {
            unsigned char *dst_y_tmp = dst_y;
            unsigned short *dst_uv_tmp = dst_uv;

            for (i = 0; i < dest_width; i++)  {
                int x = (int)(i * xratio);
                int y = (int)(j * yratio);

                source_y = image_data + obj_image->image.offsets[0]
                           + (src_y + y) * obj_image->image.pitches[0]
                           + (src_x + x);
                *dst_y_tmp = *source_y;
                dst_y_tmp++;

                if (((i & 1) == 0)) {
                    source_uv = (unsigned short *)(image_data + obj_image->image.offsets[1]
                                                   + ((src_y + y) / 2) * obj_image->image.pitches[1])
                                + ((src_x + x) / 2);
                    *dst_uv_tmp = *source_uv;
                    dst_uv_tmp++;
                }
            }
            dst_y += psb_surface->stride;

            if (j & 1)
                dst_uv = (unsigned short *)((unsigned char *)dst_uv + psb_surface->stride);
        }
        break;
    }
    default:/* will not reach here */
        break;
    }

    psb_buffer_unmap(obj_buffer->psb_buffer);
    psb_buffer_unmap(&psb_surface->buf);

    return VA_STATUS_SUCCESS;
}

/*
 * Link supbicture into one surface, when update is zero, not need to
 * update the location information
 * The image informatio and its BO of subpicture will copied to surface
 * so need to update it when a vaSetSubpictureImage is called
 */
static VAStatus psb__LinkSubpictIntoSurface(
    psb_driver_data_p driver_data,
    object_surface_p obj_surface,
    object_subpic_p obj_subpic,
    short src_x,
    short src_y,
    unsigned short src_w,
    unsigned short src_h,
    short dest_x,
    short dest_y,
    unsigned short dest_w,
    unsigned short dest_h,
    int update /* update subpicture location */
)
{
    PsbVASurfaceRec *surface_subpic;
    object_image_p obj_image = IMAGE(obj_subpic->image_id);
    if (NULL == obj_image) {
        return VA_STATUS_ERROR_INVALID_IMAGE;
    }

    VAImage *image = &obj_image->image;
    object_buffer_p obj_buffer = BUFFER(image->buf);
    if (NULL == obj_buffer) {
        return VA_STATUS_ERROR_INVALID_BUFFER;
    }

    int found = 0;

    if (obj_surface->subpictures != NULL) {
        surface_subpic = (PsbVASurfaceRec *)obj_surface->subpictures;
        do {
            if (surface_subpic->subpic_id == obj_subpic->subpic_id) {
                found = 1;
                break;
            } else
                surface_subpic = surface_subpic->next;
        } while (surface_subpic);
    }

    if (found == 0) { /* new node */
        if (obj_surface->subpic_count >= PSB_SUBPIC_MAX_NUM) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "can't support so many sub-pictures for the surface\n");
            return VA_STATUS_ERROR_UNKNOWN;
        }

        surface_subpic = (PsbVASurfaceRec *)calloc(1, sizeof(*surface_subpic));
        if (NULL == surface_subpic)
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

    surface_subpic->subpic_id = obj_subpic->subpic_id;
    surface_subpic->fourcc = image->format.fourcc;
    surface_subpic->size = image->data_size;
    surface_subpic->bo = obj_buffer->psb_buffer->drm_buf;
    surface_subpic->bufid = wsbmKBufHandle(wsbmKBuf(obj_buffer->psb_buffer->drm_buf));
    surface_subpic->pl_flags = obj_buffer->psb_buffer->pl_flags;
    surface_subpic->subpic_flags = obj_subpic->flags;

    surface_subpic->width = image->width;
    surface_subpic->height = image->height;
    switch (surface_subpic->fourcc) {
    case VA_FOURCC_AYUV:
        surface_subpic->stride = image->pitches[0] / 4;
        break;
    case VA_FOURCC_RGBA:
        surface_subpic->stride = image->pitches[0] / 4;
        break;
    case VA_FOURCC_AI44:
        surface_subpic->stride = image->pitches[0];
        /* point to Image palette */
        surface_subpic->palette_ptr = (PsbAYUVSample8 *) & obj_image->palette[0];
        break;
    }

    if (update) {
        surface_subpic->subpic_srcx = src_x;
        surface_subpic->subpic_srcy = src_y;
        surface_subpic->subpic_dstx = dest_x;
        surface_subpic->subpic_dsty = dest_y;
        surface_subpic->subpic_srcw = src_w;
        surface_subpic->subpic_srch = src_h;
        surface_subpic->subpic_dstw = dest_w;
        surface_subpic->subpic_dsth = dest_h;
    }

    if (found == 0) { /* new node, link into the list */
        if (NULL == obj_surface->subpictures) {
            obj_surface->subpictures = (void *)surface_subpic;
        } else { /* insert as the head */
            surface_subpic->next = (PsbVASurfacePtr)obj_surface->subpictures;
            obj_surface->subpictures = (void *)surface_subpic;
        }
        obj_surface->subpic_count++;
    }

    return VA_STATUS_SUCCESS;
}


static VAStatus psb__LinkSurfaceIntoSubpict(
    object_subpic_p obj_subpic,
    VASurfaceID surface_id
)
{
    subpic_surface_s *subpic_surface;
    int found = 0;

    if (obj_subpic->surfaces != NULL) {
        subpic_surface = (subpic_surface_s *)obj_subpic->surfaces;
        do  {
            if (subpic_surface->surface_id == surface_id) {
                found = 1;
                return VA_STATUS_SUCCESS; /* reture directly */
            } else
                subpic_surface = subpic_surface->next;
        } while (subpic_surface);
    }

    /* not found */
    subpic_surface = (subpic_surface_s *)calloc(1, sizeof(*subpic_surface));
    if (NULL == subpic_surface)
        return VA_STATUS_ERROR_ALLOCATION_FAILED;

    subpic_surface->surface_id = surface_id;
    subpic_surface->next = NULL;

    if (NULL == obj_subpic->surfaces) {
        obj_subpic->surfaces = (void *)subpic_surface;
    } else { /* insert as the head */
        subpic_surface->next = (subpic_surface_p)obj_subpic->surfaces;
        obj_subpic->surfaces = (void *)subpic_surface;
    }

    return VA_STATUS_SUCCESS;
}

static VAStatus psb__DelinkSubpictFromSurface(
    object_surface_p obj_surface,
    VASubpictureID subpic_id
)
{
    PsbVASurfaceRec *surface_subpic, *pre_surface_subpic = NULL;
    int found = 0;

    if (obj_surface->subpictures != NULL) {
        surface_subpic = (PsbVASurfaceRec *)obj_surface->subpictures;
        do  {
            if (surface_subpic->subpic_id == subpic_id) {
                found = 1;
                break;
            } else {
                pre_surface_subpic = surface_subpic;
                surface_subpic = surface_subpic->next;
            }
        } while (surface_subpic);
    }

    if (found == 1) {
        if (pre_surface_subpic == NULL) { /* remove the first node */
            obj_surface->subpictures = (void *)surface_subpic->next;
        } else {
            pre_surface_subpic->next = surface_subpic->next;
        }
        free(surface_subpic);
        obj_surface->subpic_count--;
    }

    return VA_STATUS_SUCCESS;
}


static VAStatus psb__DelinkSurfaceFromSubpict(
    object_subpic_p obj_subpic,
    VASurfaceID surface_id
)
{
    subpic_surface_s *subpic_surface, *pre_subpic_surface = NULL;
    int found = 0;

    if (obj_subpic->surfaces != NULL) {
        subpic_surface = (subpic_surface_s *)obj_subpic->surfaces;
        do {
            if (subpic_surface->surface_id == surface_id) {
                found = 1;
                break;
            } else {
                pre_subpic_surface = subpic_surface;
                subpic_surface = subpic_surface->next;
            }
        } while (subpic_surface);
    }

    if (found == 1) {
        if (pre_subpic_surface == NULL) { /* remove the first node */
            obj_subpic->surfaces = (void *)subpic_surface->next;
        } else {
            pre_subpic_surface->next = subpic_surface->next;
        }
        free(subpic_surface);
    }

    return VA_STATUS_SUCCESS;
}


VAStatus psb_QuerySubpictureFormats(
    VADriverContextP __maybe_unused ctx,
    VAImageFormat *format_list,        /* out */
    unsigned int *flags,       /* out */
    unsigned int *num_formats  /* out */
)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    CHECK_INVALID_PARAM(format_list == NULL);
    CHECK_INVALID_PARAM(flags == NULL);
    CHECK_INVALID_PARAM(num_formats == NULL);

    memcpy(format_list, psb__SubpicFormat, sizeof(psb__SubpicFormat));
    *num_formats = PSB_MAX_SUBPIC_FORMATS;
    *flags = PSB_SUPPORTED_SUBPIC_FLAGS;

    return VA_STATUS_SUCCESS;
}


VAStatus psb_CreateSubpicture(
    VADriverContextP ctx,
    VAImageID image,
    VASubpictureID *subpicture   /* out */
)
{
    INIT_DRIVER_DATA;
    VASubpictureID subpicID;
    object_subpic_p obj_subpic;
    object_image_p obj_image;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    VAImageFormat *img_fmt;

    obj_image = IMAGE(image);
    CHECK_IMAGE(obj_image);
    CHECK_SUBPICTURE(subpicture);

    img_fmt = psb__VAImageCheckFourCC(&obj_image->image.format, psb__SubpicFormat,
                                      sizeof(psb__SubpicFormat) / sizeof(VAImageFormat));
    if (img_fmt == NULL)
        return VA_STATUS_ERROR_UNKNOWN;

    subpicID = object_heap_allocate(&driver_data->subpic_heap);
    obj_subpic = SUBPIC(subpicID);
    CHECK_ALLOCATION(obj_subpic);

    MEMSET_OBJECT(obj_subpic, struct object_subpic_s);

    obj_subpic->subpic_id = subpicID;
    obj_subpic->image_id = obj_image->image.image_id;
    obj_subpic->surfaces = NULL;
    obj_subpic->global_alpha = 255;

    obj_image->subpic_ref ++;

    *subpicture = subpicID;

    return VA_STATUS_SUCCESS;
}



VAStatus psb__destroy_subpicture(psb_driver_data_p driver_data, object_subpic_p obj_subpic)
{
    subpic_surface_s *subpic_surface = (subpic_surface_s *)obj_subpic->surfaces;
    VASubpictureID subpicture = obj_subpic->subpic_id;

    if (subpic_surface) {
        do {
            subpic_surface_s *tmp = subpic_surface;
            object_surface_p obj_surface = SURFACE(subpic_surface->surface_id);

            if (obj_surface) { /* remove subpict from surface */
                psb__DelinkSubpictFromSurface(obj_surface, subpicture);
            }
            subpic_surface = subpic_surface->next;
            free(tmp);
        } while (subpic_surface);
    }

    object_heap_free(&driver_data->subpic_heap, (object_base_p) obj_subpic);
    return VA_STATUS_SUCCESS;
}


VAStatus psb_DestroySubpicture(
    VADriverContextP ctx,
    VASubpictureID subpicture
)
{
    INIT_DRIVER_DATA;
    object_subpic_p obj_subpic;
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    obj_subpic = SUBPIC(subpicture);
    CHECK_SUBPICTURE(obj_subpic);

    return psb__destroy_subpicture(driver_data, obj_subpic);
}

VAStatus psb_SetSubpictureImage(
    VADriverContextP ctx,
    VASubpictureID subpicture,
    VAImageID image
)
{
    INIT_DRIVER_DATA;
    object_subpic_p obj_subpic;
    object_image_p obj_image;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    subpic_surface_s *subpic_surface;
    VAImageFormat *img_fmt;

    obj_image = IMAGE(image);
    CHECK_IMAGE(obj_image);

    img_fmt = psb__VAImageCheckFourCC(&obj_image->image.format,
                                      psb__SubpicFormat,
                                      sizeof(psb__SubpicFormat) / sizeof(VAImageFormat));
    CHECK_IMAGE(img_fmt);

    obj_subpic = SUBPIC(subpicture);
    CHECK_SUBPICTURE(obj_subpic);

    object_image_p old_obj_image = IMAGE(obj_subpic->image_id);
    if (old_obj_image) {
        old_obj_image->subpic_ref--;/* decrease reference count */
    }

    /* reset the image */
    obj_subpic->image_id = obj_image->image.image_id;
    obj_image->subpic_ref ++;

    /* relink again */
    if (obj_subpic->surfaces != NULL) {
        /* the subpicture already linked into surfaces
         * so not check the return value of psb__LinkSubpictIntoSurface
         */
        subpic_surface = (subpic_surface_s *)obj_subpic->surfaces;
        do {
            object_surface_p obj_surface = SURFACE(subpic_surface->surface_id);
            CHECK_SURFACE(obj_surface);

            psb__LinkSubpictIntoSurface(driver_data, obj_surface, obj_subpic,
                                        0, 0, 0, 0, 0, 0, 0, 0,
                                        0 /* not update location */
                                       );
            subpic_surface = subpic_surface->next;
        } while (subpic_surface);
    }


    return VA_STATUS_SUCCESS;
}


VAStatus psb_SetSubpictureChromakey(
    VADriverContextP ctx,
    VASubpictureID subpicture,
    unsigned int chromakey_min,
    unsigned int chromakey_max,
    unsigned int chromakey_mask
)
{
    INIT_DRIVER_DATA;
    (void)driver_data;
    /* TODO */
    if ((chromakey_mask < chromakey_min) || (chromakey_mask > chromakey_max)) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Invalid chromakey value %d, chromakey value should between min and max\n", chromakey_mask);
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }
    object_subpic_p obj_subpic = SUBPIC(subpicture);
    if (NULL == obj_subpic) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Invalid subpicture value %d\n", subpicture);
        return VA_STATUS_ERROR_INVALID_SUBPICTURE;
    }

    return VA_STATUS_SUCCESS;
}

VAStatus psb_SetSubpictureGlobalAlpha(
    VADriverContextP ctx,
    VASubpictureID subpicture,
    float global_alpha
)
{
    INIT_DRIVER_DATA;

    if (global_alpha < 0 || global_alpha > 1) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Invalid global alpha value %07f, global alpha value should between 0 and 1\n", global_alpha);
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    object_subpic_p obj_subpic = SUBPIC(subpicture);
    if (NULL == obj_subpic) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Invalid subpicture value %d\n", subpicture);
        return VA_STATUS_ERROR_INVALID_SUBPICTURE;
    }

    obj_subpic->global_alpha = global_alpha * 255;

    return VA_STATUS_SUCCESS;
}


VAStatus psb__AssociateSubpicture(
    VADriverContextP ctx,
    VASubpictureID subpicture,
    VASurfaceID *target_surfaces,
    int num_surfaces,
    short src_x, /* upper left offset in subpicture */
    short src_y,
    unsigned short src_w,
    unsigned short src_h,
    short dest_x, /* upper left offset in surface */
    short dest_y,
    unsigned short dest_w,
    unsigned short dest_h,
    /*
     * whether to enable chroma-keying or global-alpha
     * see VA_SUBPICTURE_XXX values
     */
    unsigned int flags
)
{
    INIT_DRIVER_DATA;

    object_subpic_p obj_subpic;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    int i;

    CHECK_INVALID_PARAM(num_surfaces <= 0);

    obj_subpic = SUBPIC(subpicture);
    CHECK_SUBPICTURE(obj_subpic);
    CHECK_SURFACE(target_surfaces);

    if (flags & ~PSB_SUPPORTED_SUBPIC_FLAGS) {
#ifdef VA_STATUS_ERROR_FLAG_NOT_SUPPORTED
        vaStatus = VA_STATUS_ERROR_FLAG_NOT_SUPPORTED;
#else
        vaStatus = VA_STATUS_ERROR_UNKNOWN;
#endif
        DEBUG_FAILURE;
        return vaStatus;
    } else {

        /* If flags are ok, copy them to the subpicture object */
        obj_subpic->flags = flags;

    }

    /* Validate input params */
    for (i = 0; i < num_surfaces; i++) {
        object_surface_p obj_surface = SURFACE(target_surfaces[i]);
        CHECK_SURFACE(obj_surface);
    }

    VASurfaceID *surfaces = target_surfaces;
    for (i = 0; i < num_surfaces; i++) {
        object_surface_p obj_surface = SURFACE(*surfaces);
        if (obj_surface) {
            vaStatus = psb__LinkSubpictIntoSurface(driver_data, obj_surface, obj_subpic,
                                                   src_x, src_y, src_w, src_h,
                                                   dest_x, dest_y, dest_w, dest_h, 1);
            if (VA_STATUS_SUCCESS == vaStatus) {
                vaStatus = psb__LinkSurfaceIntoSubpict(obj_subpic, *surfaces);
            }
            CHECK_VASTATUS();/* failed with malloc */
        } else {
            /* Should never get here */
            drv_debug_msg(VIDEO_DEBUG_ERROR, "Invalid surfaces,SurfaceID=0x%x\n", *surfaces);
        }

        surfaces++;
    }

    return VA_STATUS_SUCCESS;
}


VAStatus psb_AssociateSubpicture(
    VADriverContextP ctx,
    VASubpictureID subpicture,
    VASurfaceID *target_surfaces,
    int num_surfaces,
    short src_x, /* upper left offset in subpicture */
    short src_y,
    unsigned short src_width,
    unsigned short src_height,
    short dest_x, /* upper left offset in surface */
    short dest_y,
    unsigned short dest_width,
    unsigned short dest_height,
    /*
     * whether to enable chroma-keying or global-alpha
     * see VA_SUBPICTURE_XXX values
     */
    unsigned int flags
)
{
    return psb__AssociateSubpicture(ctx, subpicture, target_surfaces, num_surfaces,
                                    src_x, src_y, src_width, src_height,
                                    dest_x, dest_y, dest_width, dest_height,
                                    flags
                                   );
}


VAStatus psb_DeassociateSubpicture(
    VADriverContextP ctx,
    VASubpictureID subpicture,
    VASurfaceID *target_surfaces,
    int num_surfaces
)
{
    INIT_DRIVER_DATA;

    object_subpic_p obj_subpic;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    object_image_p obj_image;
    int i;

    CHECK_INVALID_PARAM(num_surfaces <= 0);

    obj_subpic = SUBPIC(subpicture);
    CHECK_SUBPICTURE(obj_subpic);
    CHECK_SURFACE(target_surfaces);

    VASurfaceID *surfaces = target_surfaces;
    for (i = 0; i < num_surfaces; i++) {
        object_surface_p obj_surface = SURFACE(*surfaces);

        if (obj_surface) {
            psb__DelinkSubpictFromSurface(obj_surface, subpicture);
            psb__DelinkSurfaceFromSubpict(obj_subpic, obj_surface->surface_id);
        } else {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "vaDeassociateSubpicture: Invalid surface, VASurfaceID=0x%08x\n", *surfaces);
        }

        surfaces++;
    }

    obj_image = IMAGE(obj_subpic->image_id);
    if (obj_image)
        obj_image->subpic_ref--;/* decrease reference count */

    return VA_STATUS_SUCCESS;
}


void psb_SurfaceDeassociateSubpict(
    psb_driver_data_p driver_data,
    object_surface_p obj_surface
)
{
    PsbVASurfaceRec *surface_subpic = (PsbVASurfaceRec *)obj_surface->subpictures;

    if (surface_subpic != NULL) {
        do  {
            PsbVASurfaceRec *tmp = surface_subpic;
            object_subpic_p obj_subpic = SUBPIC(surface_subpic->subpic_id);
            if (obj_subpic)
                psb__DelinkSurfaceFromSubpict(obj_subpic, obj_surface->surface_id);
            surface_subpic = surface_subpic->next;
            free(tmp);
        } while (surface_subpic);
    }
}


static  VADisplayAttribute psb__DisplayAttribute[] = {
    {
        VADisplayAttribBrightness,
        BRIGHTNESS_MIN,
        BRIGHTNESS_MAX,
        BRIGHTNESS_DEFAULT_VALUE,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },

    {
        VADisplayAttribContrast,
        CONTRAST_MIN,
        CONTRAST_MAX,
        CONTRAST_DEFAULT_VALUE,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },

    {
        VADisplayAttribHue,
        HUE_MIN,
        HUE_MAX,
        HUE_DEFAULT_VALUE,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },

    {
        VADisplayAttribSaturation,
        SATURATION_MIN,
        SATURATION_MAX,
        SATURATION_DEFAULT_VALUE,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },
    {
        VADisplayAttribBackgroundColor,
        0x00000000,
        0xffffffff,
        0x00000000,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },
    {
        VADisplayAttribRotation,
        VA_ROTATION_NONE,
        VA_ROTATION_270,
        VA_ROTATION_NONE,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },
    {
        VADisplayAttribOutofLoopDeblock,
        VA_OOL_DEBLOCKING_FALSE,
        VA_OOL_DEBLOCKING_TRUE,
        VA_OOL_DEBLOCKING_FALSE,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },
    {
        VADisplayAttribBlendColor,
        0x00000000,
        0xffffffff,
        0x00000000,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },
    {
        VADisplayAttribOverlayColorKey,
        0x00000000,
        0xffffffff,
        0x00000000,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },
    {
        VADisplayAttribOverlayAutoPaintColorKey,
        0x00000000,
        0xffffffff,
        0x00000000,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },
    {
        VADisplayAttribCSCMatrix,
        0x00000000,
        0xffffffff,
        0x00000000,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },
    {
        VADisplayAttribRenderDevice,
        0x00000000,
        0xffffffff,
        0x00000000,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },
    {
        VADisplayAttribRenderMode,
        0x00000000,
        0xffffffff,
        0x00000000,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    },
    {
        VADisplayAttribRenderRect,
        0x00000000,
        0xffffffff,
        0x00000000,
        VA_DISPLAY_ATTRIB_GETTABLE | VA_DISPLAY_ATTRIB_SETTABLE,
        NULL
    }
};

/*
 * Query display attributes
 * The caller must provide a "attr_list" array that can hold at
 * least vaMaxNumDisplayAttributes() entries. The actual number of attributes
 * returned in "attr_list" is returned in "num_attributes".
 */
VAStatus psb_QueryDisplayAttributes(
    VADriverContextP __maybe_unused ctx,
    VADisplayAttribute *attr_list,      /* out */
    int *num_attributes         /* out */
)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    CHECK_INVALID_PARAM(attr_list == NULL);
    CHECK_INVALID_PARAM(num_attributes == NULL);

    *num_attributes = min(*num_attributes, PSB_MAX_DISPLAY_ATTRIBUTES);
    memcpy(attr_list, psb__DisplayAttribute, (*num_attributes)*sizeof(VADisplayAttribute));
    return VA_STATUS_SUCCESS;
}

/*
 * Get display attributes
 * This function returns the current attribute values in "attr_list".
 * Only attributes returned with VA_DISPLAY_ATTRIB_GETTABLE set in the "flags" field
 * from vaQueryDisplayAttributes() can have their values retrieved.
 */
VAStatus psb_GetDisplayAttributes(
    VADriverContextP ctx,
    VADisplayAttribute *attr_list,      /* in/out */
    int num_attributes
)
{
    INIT_DRIVER_DATA;
    VADisplayAttribute *p = attr_list;
    int i;
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    CHECK_INVALID_PARAM(attr_list == NULL);
    CHECK_INVALID_PARAM(num_attributes <= 0);

    for (i = 0; i < num_attributes; i++) {
        switch (p->type) {
        case VADisplayAttribBrightness:
            /* -50*(1<<10) ~ 50*(1<<10) ==> 0~100*/
            p->value = (driver_data->brightness.value / (1 << 10)) + 50;
            p->min_value = 0;
            p->max_value = 100;
            break;
        case VADisplayAttribContrast:
            /* 0 ~ 2*(1<<25) ==> 0~100 */
            p->value = (driver_data->contrast.value / (1 << 25)) * 50;
            p->min_value = 0;
            p->max_value = 100;
            break;
        case VADisplayAttribHue:
            /* -30*(1<<25) ~ 30*(1<<25) ==> 0~100*/
            p->value = ((driver_data->hue.value / (1 << 25)) + 30) * 10 / 6;
            p->min_value = 0;
            p->max_value = 100;
            break;
        case VADisplayAttribSaturation:
            /* 0 ~ 2*(1<<25) ==> 0~100 */
            p->value = (driver_data->saturation.value / (1 << 25)) * 50;
            p->min_value = 0;
            p->max_value = 100;
            break;
        case VADisplayAttribBackgroundColor:
            p->value = driver_data->clear_color;
            break;
        case VADisplayAttribBlendColor:
            p->value = driver_data->blend_color;
            break;
        case VADisplayAttribOverlayColorKey:
            p->value = driver_data->color_key;
            p->min_value = 0;
            p->max_value = 0xFFFFFF;
            break;
        case VADisplayAttribOverlayAutoPaintColorKey:
            p->value = driver_data->overlay_auto_paint_color_key;
            p->min_value = 0;
            p->max_value = 1;
            break;
        case VADisplayAttribRotation:
            p->value = driver_data->va_rotate = p->value;
            p->min_value = VA_ROTATION_NONE;
            p->max_value = VA_ROTATION_270;
            break;
        case VADisplayAttribOutofLoopDeblock:
            p->value = driver_data->is_oold = p->value;
            p->min_value = VA_OOL_DEBLOCKING_FALSE;
            p->max_value = VA_OOL_DEBLOCKING_TRUE;
            break;
        case VADisplayAttribCSCMatrix:
            p->value = driver_data->load_csc_matrix = p->value;
            p->min_value = 0;
            p->max_value = 255;
            break;
        case VADisplayAttribRenderDevice:
            p->value = driver_data->render_device = p->value;
            p->min_value = 0;
            p->max_value = 255;
            break;
        case VADisplayAttribRenderMode:
            p->value = driver_data->render_mode = p->value;
            p->min_value = 0;
            p->max_value = 255;
            break;
        case VADisplayAttribRenderRect:
            ((VARectangle *)(p->value))->x = driver_data->render_rect.x = ((VARectangle *)(p->value))->x;
            ((VARectangle *)(p->value))->y = driver_data->render_rect.y = ((VARectangle *)(p->value))->y;
            ((VARectangle *)(p->value))->width = driver_data->render_rect.width = ((VARectangle *)(p->value))->width;
            ((VARectangle *)(p->value))->height = driver_data->render_rect.height = ((VARectangle *)(p->value))->height;
            p->min_value = 0;
            p->max_value = 255;
            break;

        default:
            break;
        }
        p++;
    }

    return VA_STATUS_SUCCESS;
}

/*
 * Set display attributes
 * Only attributes returned with VA_DISPLAY_ATTRIB_SETTABLE set in the "flags" field
 * from vaQueryDisplayAttributes() can be set.  If the attribute is not settable or
 * the value is out of range, the function returns VA_STATUS_ERROR_ATTR_NOT_SUPPORTED
 */
#define CLAMP_ATTR(a,max,min) (a>max?max:(a<min?min:a))
VAStatus psb_SetDisplayAttributes(
    VADriverContextP ctx,
    VADisplayAttribute *attr_list,
    int num_attributes
)
{
    INIT_DRIVER_DATA;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    struct psb_texture_s *texture_priv = &driver_data->ctexture_priv;
    PsbPortPrivPtr overlay_priv = (PsbPortPrivPtr)(&driver_data->coverlay_priv);
    int j, k;

    CHECK_INVALID_PARAM(attr_list == NULL);

    VADisplayAttribute *p = attr_list;
    int i, update_coeffs = 0;
    float *p_tmp;

    if (num_attributes <= 0) {
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    for (i = 0; i < num_attributes; i++) {
        switch (p->type) {
        case VADisplayAttribBrightness:
            /* 0~100 ==> -50*(1<<10) ~ 50*(1<<10)*/
            driver_data->brightness.value = (p->value - 50) * (1 << 10);
            /* 0~100 ==> -50~50 */
            overlay_priv->brightness.Value = texture_priv->brightness.Value = p->value - 50;
            update_coeffs = 1;
            break;
        case VADisplayAttribContrast:
            /* 0~100 ==> 0 ~ 2*(1<<25) */
            driver_data->contrast.value = (p->value / 50) * (1 << 25);
            /* 0~100 ==> -100~100 */
            overlay_priv->contrast.Value = texture_priv->contrast.Value = p->value * 2 - 100;
            update_coeffs = 1;
            break;
        case VADisplayAttribHue:
            /* 0~100 ==> -30*(1<<25) ~ 30*(1<<25) */
            driver_data->hue.value = ((p->value * 2 - 100) * 3 / 10) * (1 << 25);
            /* 0~100 ==> -30~30 */
            overlay_priv->hue.Value = texture_priv->hue.Value = (p->value * 2 - 100) * 3 / 10;
            update_coeffs = 1;
            break;
        case VADisplayAttribSaturation:
            /* 0~100 ==> 0 ~ 2*(1<<25) */
            driver_data->contrast.value = (p->value / 50) * (1 << 25);
            /* 0~100 ==> 100~200 */
            overlay_priv->saturation.Value = texture_priv->saturation.Value = p->value + 100;
            update_coeffs = 1;
            break;
        case VADisplayAttribBackgroundColor:
            driver_data->clear_color = p->value;
            break;
        case VADisplayAttribOutofLoopDeblock:
            driver_data->is_oold = p->value;
            break;
        case VADisplayAttribRotation:
            driver_data->va_rotate = p->value;
            driver_data->rotation_dirty |= PSB_NEW_VA_ROTATION;
            break;

        case VADisplayAttribCSCMatrix:
            driver_data->load_csc_matrix = 1;
            p_tmp = (float *)(p->attrib_ptr);
            for (j = 0; j < CSC_MATRIX_Y; j++)
                for (k = 0; k < CSC_MATRIX_X; k++) {
                    if (p_tmp)
                        driver_data->csc_matrix[j][k] = *p_tmp;
                   p_tmp++; 
                }

            for (j = 0; j < CSC_MATRIX_Y; j++)
                for (k = 0; k < CSC_MATRIX_X; k++) {
                    if (fabs(s601[j*CSC_MATRIX_X+k] - driver_data->csc_matrix[j][k]) > 1e-6) {
                        break;
                    }
                    if (k < CSC_MATRIX_X) {
                        break;
                    }
                }

            if (j == CSC_MATRIX_Y && k == CSC_MATRIX_X) {
                driver_data->is_BT601 = 1;
            }
            break;

        case VADisplayAttribColorRange:
            driver_data->set_video_range = 1;
            driver_data->video_range = (p->value == VA_SOURCE_RANGE_FULL);
            break;

        case VADisplayAttribBlendColor:
            driver_data->blend_color = p->value;
            break;
        case VADisplayAttribOverlayColorKey:
            overlay_priv->colorKey = driver_data->color_key = p->value;
            break;
        case VADisplayAttribOverlayAutoPaintColorKey:
            driver_data->overlay_auto_paint_color_key = p->value;
            break;

        case VADisplayAttribRenderDevice:
            driver_data->render_device = p->value & VA_RENDER_DEVICE_MASK;
        case VADisplayAttribRenderMode:
#ifndef ANDROID
            if (p->value & VA_RENDER_MODE_EXTERNAL_GPU) {
                drv_debug_msg(VIDEO_DEBUG_ERROR, "%s:Invalid parameter.VARenderModeExternalGPU is not supported.\n", __FUNCTION__);
                return VA_STATUS_ERROR_INVALID_PARAMETER;
            }
            if (((p->value & VA_RENDER_MODE_LOCAL_OVERLAY) && (p->value & VA_RENDER_MODE_LOCAL_GPU)) ||
                ((p->value & VA_RENDER_MODE_EXTERNAL_OVERLAY) && (p->value & VA_RENDER_MODE_EXTERNAL_GPU))) {
                drv_debug_msg(VIDEO_DEBUG_ERROR, "%s:Invalid parameter. Conflict setting for VADisplayAttribRenderMode.\n", __FUNCTION__);
                return VA_STATUS_ERROR_INVALID_PARAMETER;
            }
#endif
            driver_data->render_mode = p->value & VA_RENDER_MODE_MASK;
            break;
        case VADisplayAttribRenderRect:
            driver_data->render_rect.x = ((VARectangle *)(p->attrib_ptr))->x;
            driver_data->render_rect.y = ((VARectangle *)(p->attrib_ptr))->y;
            driver_data->render_rect.width = ((VARectangle *)(p->attrib_ptr))->width;
            driver_data->render_rect.height = ((VARectangle *)(p->attrib_ptr))->height;
            break;

        default:
            break;
        }
        p++;
    }

    if (update_coeffs) {
        /* TODO */
#ifndef ANDROID
        texture_priv->update_coeffs = 1;
#endif
    }

    return VA_STATUS_SUCCESS;
}