/*
 *
 * Copyright 2012 Samsung Electronics S.LSI Co. LTD
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * @file        csc.c
 *
 * @brief       color space convertion abstract source
 *
 * @author      Pyoungjae Jung(pjet.jung@samsung.com)
 *
 * @version     1.0.0
 *
 * @history
 *   2012.1.11 : Create
 */
#define LOG_TAG "libcsc"
#include <cutils/log.h>

#include <stdio.h>
#include <stdlib.h>
#include <utils/Log.h>
#include <system/graphics.h>

#include "csc.h"
#include "exynos_format.h"
#include "swconverter.h"

#ifdef EXYNOS_OMX
#include "Exynos_OMX_Def.h"
#else
#include "SEC_OMX_Def.h"
#endif

#ifdef ENABLE_FIMC
#include "hwconverter_wrapper.h"
#endif

#ifdef ENABLE_GSCALER
#include "exynos_gscaler.h"
#endif

#ifdef ENABLE_G2D
#include <fcntl.h>
#include <sys/ioctl.h>
#include "fimg2d.h"

typedef struct
{
    struct fimg2d_image src;
    struct fimg2d_image dst;
    int fd;
} g2d_data;
#endif

#define GSCALER_IMG_ALIGN 16
#define ALIGN(x, a)       (((x) + (a) - 1) & ~((a) - 1))

typedef enum _CSC_PLANE {
    CSC_Y_PLANE = 0,
    CSC_RGB_PLANE = 0,
    CSC_U_PLANE = 1,
    CSC_UV_PLANE = 1,
    CSC_V_PLANE = 2
} CSC_PLANE;

typedef struct _CSC_FORMAT {
    unsigned int width;
    unsigned int height;
    unsigned int crop_left;
    unsigned int crop_top;
    unsigned int crop_width;
    unsigned int crop_height;
    unsigned int color_format;
    unsigned int cacheable;
    unsigned int mode_drm;
} CSC_FORMAT;

typedef struct _CSC_BUFFER {
    void *planes[CSC_MAX_PLANES];
} CSC_BUFFER;

typedef struct _CSC_HW_PROPERTY {
    int fixed_node;
    int mode_drm;
} CSC_HW_PROPERTY;

typedef struct _CSC_HANDLE {
    CSC_FORMAT      dst_format;
    CSC_FORMAT      src_format;
    CSC_BUFFER      dst_buffer;
    CSC_BUFFER      src_buffer;
    CSC_METHOD      csc_method;
    CSC_HW_TYPE     csc_hw_type;
    void           *csc_hw_handle;
    CSC_HW_PROPERTY hw_property;
} CSC_HANDLE;

/* source is RGB888 */
static CSC_ERRORCODE conv_sw_src_argb888(
    CSC_HANDLE *handle)
{
    CSC_ERRORCODE ret = CSC_ErrorNone;

    switch (handle->dst_format.color_format) {
    case HAL_PIXEL_FORMAT_YCbCr_420_P:
        csc_ARGB8888_to_YUV420P(
            (unsigned char *)handle->dst_buffer.planes[CSC_Y_PLANE],
            (unsigned char *)handle->dst_buffer.planes[CSC_U_PLANE],
            (unsigned char *)handle->dst_buffer.planes[CSC_V_PLANE],
            (unsigned char *)handle->src_buffer.planes[CSC_RGB_PLANE],
            handle->src_format.width,
            handle->src_format.height);
        ret = CSC_ErrorNone;
        break;
    case HAL_PIXEL_FORMAT_YCbCr_420_SP:
        csc_ARGB8888_to_YUV420SP_NEON(
            (unsigned char *)handle->dst_buffer.planes[CSC_Y_PLANE],
            (unsigned char *)handle->dst_buffer.planes[CSC_UV_PLANE],
            (unsigned char *)handle->src_buffer.planes[CSC_RGB_PLANE],
            handle->src_format.width,
            handle->src_format.height);
        ret = CSC_ErrorNone;
        break;
    default:
        ret = CSC_ErrorUnsupportFormat;
        break;
    }

    return ret;
}

/* source is NV12T */
static CSC_ERRORCODE conv_sw_src_nv12t(
    CSC_HANDLE *handle)
{
    CSC_ERRORCODE ret = CSC_ErrorNone;

    switch (handle->dst_format.color_format) {
    case HAL_PIXEL_FORMAT_YCbCr_420_P:
        csc_tiled_to_linear_y_neon(
            (unsigned char *)handle->dst_buffer.planes[CSC_Y_PLANE],
            (unsigned char *)handle->src_buffer.planes[CSC_Y_PLANE],
            handle->src_format.width,
            handle->src_format.height);
        csc_tiled_to_linear_uv_deinterleave_neon(
            (unsigned char *)handle->dst_buffer.planes[CSC_U_PLANE],
            (unsigned char *)handle->dst_buffer.planes[CSC_V_PLANE],
            (unsigned char *)handle->src_buffer.planes[CSC_UV_PLANE],
            handle->src_format.width,
            handle->src_format.height / 2);
        ret = CSC_ErrorNone;
        break;
    case HAL_PIXEL_FORMAT_YCbCr_420_SP:
        csc_tiled_to_linear_y_neon(
            (unsigned char *)handle->dst_buffer.planes[CSC_Y_PLANE],
            (unsigned char *)handle->src_buffer.planes[CSC_Y_PLANE],
            handle->src_format.width,
            handle->src_format.height);
        csc_tiled_to_linear_uv_neon(
            (unsigned char *)handle->dst_buffer.planes[CSC_UV_PLANE],
            (unsigned char *)handle->src_buffer.planes[CSC_UV_PLANE],
            handle->src_format.width,
            handle->src_format.height / 2);
        ret = CSC_ErrorNone;
        break;
    default:
        ret = CSC_ErrorUnsupportFormat;
        break;
    }

    return ret;
}

/* source is YUV420P */
static CSC_ERRORCODE conv_sw_src_yuv420p(
    CSC_HANDLE *handle)
{
    CSC_ERRORCODE ret = CSC_ErrorNone;

    switch (handle->dst_format.color_format) {
    case HAL_PIXEL_FORMAT_YCbCr_420_P:  /* bypass */
        memcpy((unsigned char *)handle->dst_buffer.planes[CSC_Y_PLANE],
               (unsigned char *)handle->src_buffer.planes[CSC_Y_PLANE],
               handle->src_format.width * handle->src_format.height);
        memcpy((unsigned char *)handle->dst_buffer.planes[CSC_U_PLANE],
               (unsigned char *)handle->src_buffer.planes[CSC_U_PLANE],
               (handle->src_format.width * handle->src_format.height) >> 2);
        memcpy((unsigned char *)handle->dst_buffer.planes[CSC_V_PLANE],
               (unsigned char *)handle->src_buffer.planes[CSC_V_PLANE],
               (handle->src_format.width * handle->src_format.height) >> 2);
        ret = CSC_ErrorNone;
        break;
    case HAL_PIXEL_FORMAT_YCbCr_420_SP:
        memcpy((unsigned char *)handle->dst_buffer.planes[CSC_Y_PLANE],
               (unsigned char *)handle->src_buffer.planes[CSC_Y_PLANE],
               handle->src_format.width * handle->src_format.height);
        csc_interleave_memcpy_neon(
            (unsigned char *)handle->dst_buffer.planes[CSC_UV_PLANE],
            (unsigned char *)handle->src_buffer.planes[CSC_U_PLANE],
            (unsigned char *)handle->src_buffer.planes[CSC_V_PLANE],
            (handle->src_format.width * handle->src_format.height) >> 2);
        ret = CSC_ErrorNone;
        break;
    default:
        ret = CSC_ErrorUnsupportFormat;
        break;
    }

    return ret;
}

/* source is YUV420SP */
static CSC_ERRORCODE conv_sw_src_yuv420sp(
    CSC_HANDLE *handle)
{
    CSC_ERRORCODE ret = CSC_ErrorNone;

    switch (handle->dst_format.color_format) {
    case HAL_PIXEL_FORMAT_YCbCr_420_P:
        memcpy((unsigned char *)handle->dst_buffer.planes[CSC_Y_PLANE],
               (unsigned char *)handle->src_buffer.planes[CSC_Y_PLANE],
               handle->src_format.width * handle->src_format.height);
        csc_deinterleave_memcpy(
            (unsigned char *)handle->dst_buffer.planes[CSC_U_PLANE],
            (unsigned char *)handle->dst_buffer.planes[CSC_V_PLANE],
            (unsigned char *)handle->src_buffer.planes[CSC_UV_PLANE],
            handle->src_format.width * handle->src_format.height >> 1);
        ret = CSC_ErrorNone;
        break;
    case HAL_PIXEL_FORMAT_YCbCr_420_SP: /* bypass */
        memcpy((unsigned char *)handle->dst_buffer.planes[CSC_Y_PLANE],
               (unsigned char *)handle->src_buffer.planes[CSC_Y_PLANE],
               handle->src_format.width * handle->src_format.height);
        memcpy((unsigned char *)handle->dst_buffer.planes[CSC_UV_PLANE],
               (unsigned char *)handle->src_buffer.planes[CSC_UV_PLANE],
               handle->src_format.width * handle->src_format.height >> 1);
        ret = CSC_ErrorNone;
        break;
    default:
        ret = CSC_ErrorUnsupportFormat;
        break;
    }

    return ret;
}

static CSC_ERRORCODE conv_sw(
    CSC_HANDLE *handle)
{
    CSC_ERRORCODE ret = CSC_ErrorNone;

    switch (handle->src_format.color_format) {
    case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
        ret = conv_sw_src_nv12t(handle);
        break;
    case HAL_PIXEL_FORMAT_YCbCr_420_P:
        ret = conv_sw_src_yuv420p(handle);
        break;
    case HAL_PIXEL_FORMAT_YCbCr_420_SP:
        ret = conv_sw_src_yuv420sp(handle);
        break;
    case HAL_PIXEL_FORMAT_CUSTOM_ARGB_8888:
        ret = conv_sw_src_argb888(handle);
        break;
    default:
        ret = CSC_ErrorUnsupportFormat;
        break;
    }

    return ret;
}

static CSC_ERRORCODE conv_hw(
    CSC_HANDLE *handle)
{
    CSC_ERRORCODE ret = CSC_ErrorNone;
    switch (handle->csc_hw_type) {
#ifdef ENABLE_FIMC
    case CSC_HW_TYPE_FIMC:
    {
        void *src_addr[3];
        void *dst_addr[3];
        OMX_COLOR_FORMATTYPE src_omx_format;
        OMX_COLOR_FORMATTYPE dst_omx_format;
        src_addr[0] = handle->src_buffer.planes[CSC_Y_PLANE];
        src_addr[1] = handle->src_buffer.planes[CSC_UV_PLANE];
        dst_addr[0] = handle->dst_buffer.planes[CSC_Y_PLANE];
        dst_addr[1] = handle->dst_buffer.planes[CSC_U_PLANE];
        dst_addr[2] = handle->dst_buffer.planes[CSC_V_PLANE];
        src_omx_format = hal_2_omx_pixel_format(handle->src_format.color_format);
        dst_omx_format = hal_2_omx_pixel_format(handle->dst_format.color_format);
        csc_hwconverter_convert_nv12t(
            handle->csc_hw_handle,
            dst_addr,
            src_addr,
            handle->dst_format.width,
            handle->dst_format.height,
            dst_omx_format,
            src_omx_format);
        break;
    }
#endif
#ifdef ENABLE_GSCALER
    case CSC_HW_TYPE_GSCALER:
        if (exynos_gsc_convert(handle->csc_hw_handle) != 0) {
            ALOGE("%s:: exynos_gsc_convert() fail", __func__);
            ret = CSC_Error;
        }
        break;
#endif
#ifdef ENABLE_G2D
    case CSC_HW_TYPE_G2D:
    {
        g2d_data *g2d = (g2d_data *)handle->csc_hw_handle;
        struct fimg2d_blit blit;
        int err;

        memset(&blit, 0, sizeof(blit));
        blit.op = BLIT_OP_SRC_COPY;
        blit.param.g_alpha = 0xFF;
        blit.src = &g2d->src;
        blit.dst = &g2d->dst;
        blit.sync = BLIT_SYNC;

        err = ioctl(g2d->fd, FIMG2D_BITBLT_BLIT, &blit);
        if (err < 0) {
            ALOGE("FIMG2D_BITBLT_BLIT ioctl failed: %s", strerror(errno));
            ret = CSC_Error;
        }

        break;
    }
#endif
    default:
        ALOGE("%s:: unsupported csc_hw_type(%d)", __func__, handle->csc_hw_type);
        ret = CSC_ErrorNotImplemented;
        break;
    }

    return ret;
}

static CSC_ERRORCODE csc_init_hw(
    void *handle)
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    csc_handle = (CSC_HANDLE *)handle;
    if (csc_handle->csc_method == CSC_METHOD_HW) {
        switch (csc_handle->csc_hw_type) {
#ifdef ENABLE_FIMC
        case CSC_HW_TYPE_FIMC:
            csc_handle->csc_hw_handle = csc_hwconverter_open();
            ALOGV("%s:: CSC_HW_TYPE_FIMC", __func__);
            break;
#endif
#ifdef ENABLE_GSCALER
        case CSC_HW_TYPE_GSCALER:
            if (csc_handle->hw_property.fixed_node >= 0)
                csc_handle->csc_hw_handle = exynos_gsc_create_exclusive(csc_handle->hw_property.fixed_node, GSC_M2M_MODE, 0, 0);
            else
            csc_handle->csc_hw_handle = exynos_gsc_create();
            ALOGV("%s:: CSC_HW_TYPE_GSCALER", __func__);
            break;
#endif
#ifdef ENABLE_G2D
        case CSC_HW_TYPE_G2D:
        {
            g2d_data *g2d = calloc(1, sizeof(g2d_data));
            if (!g2d) {
                ALOGE("failed to allocate G2D data");
                break;
            }
            g2d->fd = open("/dev/fimg2d", O_RDWR);
            if (g2d->fd < 0) {
                ALOGE("failed to open G2D: %s", strerror(errno));
                free(g2d);
            } else {
                csc_handle->csc_hw_handle = g2d;
            }
            break;
        }
#endif
        default:
            ALOGE("%s:: unsupported csc_hw_type, csc use sw", __func__);
            csc_handle->csc_hw_handle = NULL;
            break;
        }
    }

    if (csc_handle->csc_method == CSC_METHOD_HW) {
        if (csc_handle->csc_hw_handle == NULL) {
            ALOGE("%s:: CSC_METHOD_HW can't open HW", __func__);
            ret = CSC_Error;
        }
    }

    ALOGV("%s:: CSC_METHOD=%d", __func__, csc_handle->csc_method);

    return ret;
}

static CSC_ERRORCODE csc_set_format(
    void *handle)
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (handle == NULL)
        return CSC_ErrorNotInit;

    csc_handle = (CSC_HANDLE *)handle;
    if (csc_handle->csc_method == CSC_METHOD_HW) {
        switch (csc_handle->csc_hw_type) {
        case CSC_HW_TYPE_FIMC:
            break;
#ifdef ENABLE_GSCALER
        case CSC_HW_TYPE_GSCALER:
            exynos_gsc_set_src_format(
                csc_handle->csc_hw_handle,
                ALIGN(csc_handle->src_format.width, GSCALER_IMG_ALIGN),
                ALIGN(csc_handle->src_format.height, GSCALER_IMG_ALIGN),
                csc_handle->src_format.crop_left,
                csc_handle->src_format.crop_top,
                csc_handle->src_format.crop_width,
                csc_handle->src_format.crop_height,
                HAL_PIXEL_FORMAT_2_V4L2_PIX(csc_handle->src_format.color_format),
                csc_handle->src_format.cacheable,
                csc_handle->hw_property.mode_drm);

            exynos_gsc_set_dst_format(
                csc_handle->csc_hw_handle,
                ALIGN(csc_handle->dst_format.width, GSCALER_IMG_ALIGN),
                ALIGN(csc_handle->dst_format.height, GSCALER_IMG_ALIGN),
                csc_handle->dst_format.crop_left,
                csc_handle->dst_format.crop_top,
                csc_handle->dst_format.crop_width,
                csc_handle->dst_format.crop_height,
                HAL_PIXEL_FORMAT_2_V4L2_PIX(csc_handle->dst_format.color_format),
                csc_handle->dst_format.cacheable,
                csc_handle->hw_property.mode_drm,
                0);
            break;
#endif
#ifdef ENABLE_G2D
        case CSC_HW_TYPE_G2D:
        {
            g2d_data *g2d = (g2d_data *)csc_handle->csc_hw_handle;

            g2d->src.width = ALIGN(csc_handle->src_format.width,
                    GSCALER_IMG_ALIGN);
            g2d->src.height = csc_handle->src_format.height;
            g2d->src.stride = g2d->src.width *
                    hal_2_g2d_bpp(csc_handle->src_format.color_format) >> 3;
            g2d->src.order = hal_2_g2d_pixel_order(csc_handle->src_format.color_format);
            g2d->src.fmt = hal_2_g2d_color_format(csc_handle->src_format.color_format);
            g2d->src.rect.x1 = csc_handle->src_format.crop_left;
            g2d->src.rect.y1 = csc_handle->src_format.crop_top;
            g2d->src.rect.x2 = csc_handle->src_format.crop_left +
                    csc_handle->src_format.crop_width;
            g2d->src.rect.y2 = csc_handle->src_format.crop_top +
                    csc_handle->src_format.crop_height;

            g2d->dst.width = ALIGN(csc_handle->dst_format.width,
                    GSCALER_IMG_ALIGN);
            g2d->dst.height = csc_handle->dst_format.height;
            g2d->dst.stride = g2d->dst.width *
                    hal_2_g2d_bpp(csc_handle->dst_format.color_format) >> 3;
            g2d->dst.order = hal_2_g2d_pixel_order(csc_handle->dst_format.color_format);
            g2d->dst.fmt = hal_2_g2d_color_format(csc_handle->dst_format.color_format);
            g2d->dst.rect.x1 = csc_handle->dst_format.crop_left;
            g2d->dst.rect.y1 = csc_handle->dst_format.crop_top;
            g2d->dst.rect.x2 = csc_handle->dst_format.crop_left +
                    csc_handle->dst_format.crop_width;
            g2d->dst.rect.y2 = csc_handle->dst_format.crop_top +
                    csc_handle->dst_format.crop_height;

            break;
        }
#endif
        default:
            ALOGE("%s:: unsupported csc_hw_type", __func__);
            break;
        }
    }

    return ret;
}

static CSC_ERRORCODE csc_set_buffer(
    void *handle)
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (handle == NULL)
        return CSC_ErrorNotInit;

    csc_handle = (CSC_HANDLE *)handle;
    if (csc_handle->csc_method == CSC_METHOD_HW) {
        switch (csc_handle->csc_hw_type) {
        case CSC_HW_TYPE_FIMC:
            break;
#ifdef ENABLE_GSCALER
        case CSC_HW_TYPE_GSCALER:
            exynos_gsc_set_src_addr(csc_handle->csc_hw_handle, csc_handle->src_buffer.planes, -1);
            exynos_gsc_set_dst_addr(csc_handle->csc_hw_handle, csc_handle->dst_buffer.planes, -1);
            break;
#endif
#ifdef ENABLE_G2D
        case CSC_HW_TYPE_G2D:
        {
            g2d_data *g2d = (g2d_data *)csc_handle->csc_hw_handle;

            g2d->src.addr.type = ADDR_DMA_BUF;
            g2d->src.addr.fd[0] = (int)csc_handle->src_buffer.planes[0];
            g2d->src.addr.fd[1] = (int)csc_handle->src_buffer.planes[1];

            g2d->dst.addr.type = ADDR_DMA_BUF;
            g2d->dst.addr.fd[0] = (int)csc_handle->dst_buffer.planes[0];
            g2d->dst.addr.fd[1] = (int)csc_handle->dst_buffer.planes[1];

            break;
        }
#endif
        default:
            ALOGE("%s:: unsupported csc_hw_type", __func__);
            break;
        }
    }

    return ret;
}

void *csc_init(
    CSC_METHOD method)
{
    CSC_HANDLE *csc_handle;
    csc_handle = (CSC_HANDLE *)malloc(sizeof(CSC_HANDLE));
    if (csc_handle == NULL)
        return NULL;

    memset(csc_handle, 0, sizeof(CSC_HANDLE));
    csc_handle->hw_property.fixed_node = -1;
    csc_handle->hw_property.mode_drm = 0;
    csc_handle->csc_method = method;

    return (void *)csc_handle;
}

CSC_ERRORCODE csc_deinit(
    void *handle)
{
    CSC_ERRORCODE ret = CSC_ErrorNone;
    CSC_HANDLE *csc_handle;

    csc_handle = (CSC_HANDLE *)handle;
    if (csc_handle->csc_hw_handle) {
        switch (csc_handle->csc_hw_type) {
#ifdef ENABLE_FIMC
        case CSC_HW_TYPE_FIMC:
            csc_hwconverter_close(csc_handle->csc_hw_handle);
            break;
#endif
#ifdef ENABLE_GSCALER
        case CSC_HW_TYPE_GSCALER:
            exynos_gsc_destroy(csc_handle->csc_hw_handle);
            break;
#endif
#ifdef ENABLE_G2D
        case CSC_HW_TYPE_G2D:
        {
            g2d_data *g2d = (g2d_data *)csc_handle->csc_hw_handle;
            close(g2d->fd);
            free(g2d);
            break;
        }
#endif
        default:
            ALOGE("%s:: unsupported csc_hw_type", __func__);
            break;
        }
    }

    if (csc_handle != NULL) {
        free(csc_handle);
        ret = CSC_ErrorNone;
    }

    return ret;
}

CSC_ERRORCODE csc_get_method(
    void           *handle,
    CSC_METHOD     *method)
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (handle == NULL)
        return CSC_ErrorNotInit;

    csc_handle = (CSC_HANDLE *)handle;
    *method = csc_handle->csc_method;

    return ret;
}

CSC_ERRORCODE csc_set_method(
    void           *handle,
    CSC_METHOD     method)
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (handle == NULL)
        return CSC_ErrorNotInit;

    csc_handle = (CSC_HANDLE *)handle;
    csc_handle->csc_method = method;

    return ret;
}

CSC_ERRORCODE csc_set_hw_property(
    void                *handle,
    CSC_HW_PROPERTY_TYPE property,
    int                  value)
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (handle == NULL)
        return CSC_ErrorNotInit;

    csc_handle = (CSC_HANDLE *)handle;

    if (csc_handle->csc_hw_handle) {
        ALOGE("%s:: cannot set hw property after hw is already initialized", __func__);
        return CSC_ErrorUnsupportFormat;
    }

    switch (property) {
    case CSC_HW_PROPERTY_FIXED_NODE:
        csc_handle->hw_property.fixed_node = value;
        break;
    case CSC_HW_PROPERTY_MODE_DRM:
        csc_handle->hw_property.mode_drm = value;
        break;
    case CSC_HW_PROPERTY_HW_TYPE:
        csc_handle->csc_hw_type = value;
        break;
    default:
        ALOGE("%s:: not supported hw property", __func__);
        ret = CSC_ErrorUnsupportFormat;
    }

    return ret;
}

CSC_ERRORCODE csc_get_src_format(
    void           *handle,
    unsigned int   *width,
    unsigned int   *height,
    unsigned int   *crop_left,
    unsigned int   *crop_top,
    unsigned int   *crop_width,
    unsigned int   *crop_height,
    unsigned int   *color_format,
    unsigned int   *cacheable)
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (handle == NULL)
        return CSC_ErrorNotInit;

    csc_handle = (CSC_HANDLE *)handle;
    *width = csc_handle->src_format.width;
    *height = csc_handle->src_format.height;
    *crop_left = csc_handle->src_format.crop_left;
    *crop_top = csc_handle->src_format.crop_top;
    *crop_width = csc_handle->src_format.crop_width;
    *crop_height = csc_handle->src_format.crop_height;
    *color_format = csc_handle->src_format.color_format;
    *cacheable = csc_handle->src_format.cacheable;

    return ret;
}

CSC_ERRORCODE csc_set_src_format(
    void           *handle,
    unsigned int    width,
    unsigned int    height,
    unsigned int    crop_left,
    unsigned int    crop_top,
    unsigned int    crop_width,
    unsigned int    crop_height,
    unsigned int    color_format,
    unsigned int    cacheable)
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (handle == NULL)
        return CSC_ErrorNotInit;

    csc_handle = (CSC_HANDLE *)handle;
    csc_handle->src_format.width = width;
    csc_handle->src_format.height = height;
    csc_handle->src_format.crop_left = crop_left;
    csc_handle->src_format.crop_top = crop_top;
    csc_handle->src_format.crop_width = crop_width;
    csc_handle->src_format.crop_height = crop_height;
    csc_handle->src_format.color_format = color_format;
    csc_handle->src_format.cacheable = cacheable;

    return ret;
}

CSC_ERRORCODE csc_get_dst_format(
    void           *handle,
    unsigned int   *width,
    unsigned int   *height,
    unsigned int   *crop_left,
    unsigned int   *crop_top,
    unsigned int   *crop_width,
    unsigned int   *crop_height,
    unsigned int   *color_format,
    unsigned int   *cacheable)
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (handle == NULL)
        return CSC_ErrorNotInit;

    csc_handle = (CSC_HANDLE *)handle;
    *width = csc_handle->dst_format.width;
    *height = csc_handle->dst_format.height;
    *crop_left = csc_handle->dst_format.crop_left;
    *crop_top = csc_handle->dst_format.crop_top;
    *crop_width = csc_handle->dst_format.crop_width;
    *crop_height = csc_handle->dst_format.crop_height;
    *color_format = csc_handle->dst_format.color_format;
    *cacheable = csc_handle->dst_format.cacheable;

    return ret;
}

CSC_ERRORCODE csc_set_dst_format(
    void           *handle,
    unsigned int    width,
    unsigned int    height,
    unsigned int    crop_left,
    unsigned int    crop_top,
    unsigned int    crop_width,
    unsigned int    crop_height,
    unsigned int    color_format,
    unsigned int    cacheable)
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (handle == NULL)
        return CSC_ErrorNotInit;

    csc_handle = (CSC_HANDLE *)handle;
    csc_handle->dst_format.width = width;
    csc_handle->dst_format.height = height;
    csc_handle->dst_format.crop_left = crop_left;
    csc_handle->dst_format.crop_top = crop_top;
    csc_handle->dst_format.crop_width = crop_width;
    csc_handle->dst_format.crop_height = crop_height;
    csc_handle->dst_format.color_format = color_format;
    csc_handle->dst_format.cacheable = cacheable;

    return ret;
}

CSC_ERRORCODE csc_set_src_buffer(
    void *handle,
    void *addr[3])
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (handle == NULL)
        return CSC_ErrorNotInit;

    csc_handle = (CSC_HANDLE *)handle;
    csc_handle->src_buffer.planes[CSC_Y_PLANE] = addr[0];
    csc_handle->src_buffer.planes[CSC_U_PLANE] = addr[1];
    csc_handle->src_buffer.planes[CSC_V_PLANE] = addr[2];

    return ret;
}

CSC_ERRORCODE csc_set_dst_buffer(
    void *handle,
    void *addr[3])
{
    CSC_HANDLE *csc_handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (handle == NULL)
        return CSC_ErrorNotInit;

    csc_handle = (CSC_HANDLE *)handle;
    csc_handle->dst_buffer.planes[CSC_Y_PLANE] = addr[0];
    csc_handle->dst_buffer.planes[CSC_U_PLANE] = addr[1];
    csc_handle->dst_buffer.planes[CSC_V_PLANE] = addr[2];

    return ret;
}

CSC_ERRORCODE csc_convert(
    void *handle)
{
    CSC_HANDLE *csc_handle = (CSC_HANDLE *)handle;
    CSC_ERRORCODE ret = CSC_ErrorNone;

    if (csc_handle == NULL)
        return CSC_ErrorNotInit;

    if ((csc_handle->csc_method == CSC_METHOD_HW) &&
        (csc_handle->csc_hw_handle == NULL)) {
        ret = csc_init_hw(handle);
        if (ret != CSC_ErrorNone)
            return ret;
    }

    ret = csc_set_format(csc_handle);
    if (ret != CSC_ErrorNone)
        return ret;

    ret = csc_set_buffer(csc_handle);
    if (ret != CSC_ErrorNone)
        return ret;

    if (csc_handle->csc_method == CSC_METHOD_HW)
        ret = conv_hw(csc_handle);
    else
        ret = conv_sw(csc_handle);

    return ret;
}