/*
 * Copyright (C) 2008 The Android Open Source Project
 * Copyright@ Samsung Electronics 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      exynos_rotator.c
 * \brief     source file for exynos_rotator HAL
 * \author    Sunmi Lee (carrotsm.lee@samsung.com)
 * \date      2012/03/05
 *
 * <b>Revision History: </b>
 * - 2012/03/05 : Sunmi Lee (carrotsm.lee@samsung.com) \n
 *   Create
 *
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "libexynosrotator"
#include <cutils/log.h>

#include <sys/types.h>
#include <sys/ioctl.h>
#include <videodev2.h>
#include <fcntl.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "exynos_rotator.h"

#include "exynos_format.h"
#include "ExynosMutex.h"
#include "exynos_v4l2.h"

#define NUM_OF_ROTATOR_PLANES           (3)
#define NODE_NUM_ROTATOR                (21)
#define PFX_NODE_ROTATOR                "/dev/video"

#define ROTATOR_MIN_W_SIZE (8)
#define ROTATOR_MIN_H_SIZE (8)

#define MAX_ROTATOR_WAITING_TIME_FOR_TRYLOCK (16000) // 16msec
#define ROTATOR_WAITING_TIME_FOR_TRYLOCK      (8000) //  8msec

struct rotator_info {
    unsigned int       width;
    unsigned int       height;
    unsigned int       crop_left;
    unsigned int       crop_top;
    unsigned int       crop_width;
    unsigned int       crop_height;
    unsigned int       v4l2_colorformat;
    unsigned int       cacheable;

    int                rotation;

    void              *addr[NUM_OF_ROTATOR_PLANES];
    bool               stream_on;

    enum v4l2_buf_type buf_type;
    struct v4l2_format format;
    struct v4l2_buffer buffer;
    struct v4l2_plane  planes[NUM_OF_ROTATOR_PLANES];
    struct v4l2_crop   crop;
};

struct ROTATOR_HANDLE {
    int              rotator_fd;
    struct rotator_info  src;
    struct rotator_info  dst;
    void                *op_mutex;
    void                *obj_mutex;
    void                *cur_obj_mutex;
};

static unsigned int m_rotator_get_plane_count(
    int v4l_pixel_format)
{
    int plane_count = 0;

    switch (v4l_pixel_format) {
    case V4L2_PIX_FMT_RGB32:
    case V4L2_PIX_FMT_BGR32:
    case V4L2_PIX_FMT_RGB24:
    case V4L2_PIX_FMT_RGB565:
    case V4L2_PIX_FMT_RGB555X:
    case V4L2_PIX_FMT_RGB444:
        plane_count = 1;
        break;
    case V4L2_PIX_FMT_NV12M:
    case V4L2_PIX_FMT_NV12MT_16X16:
    case V4L2_PIX_FMT_NV21:
    case V4L2_PIX_FMT_NV16:
    case V4L2_PIX_FMT_NV61:
        plane_count = 2;
        break;
    case V4L2_PIX_FMT_YVU420M:
    case V4L2_PIX_FMT_YUV422P:
    case V4L2_PIX_FMT_YUYV:
    case V4L2_PIX_FMT_UYVY:
        plane_count = 3;
        break;
    default:
        ALOGE("%s::unmatched v4l_pixel_format color_space(0x%x)",
             __func__, v4l_pixel_format);
        plane_count = -1;
        break;
    }

    return plane_count;
}

static unsigned int m_rotator_get_plane_size(
    unsigned int *plane_size,
    unsigned int  width,
    unsigned int  height,
    int           v4l_pixel_format)
{
    switch (v4l_pixel_format) {
    case V4L2_PIX_FMT_RGB32:
    case V4L2_PIX_FMT_BGR32:
        plane_size[0] = width * height * 4;
        break;
    case V4L2_PIX_FMT_RGB24:
        plane_size[0] = width * height * 3;
        break;
    case V4L2_PIX_FMT_RGB565:
    case V4L2_PIX_FMT_RGB555X:
    case V4L2_PIX_FMT_RGB444:
        plane_size[0] = width * height * 2;
        break;
    case V4L2_PIX_FMT_NV12M:
    case V4L2_PIX_FMT_NV21:
    case V4L2_PIX_FMT_NV16:
    case V4L2_PIX_FMT_NV61:
        plane_size[0] = width * height;
        plane_size[1] = width * (height / 2);
        break;
    case V4L2_PIX_FMT_NV12MT_16X16:
        plane_size[0] = ALIGN(width, 16) * ALIGN(height, 16);
        plane_size[1] = ALIGN(width, 16) * ALIGN(height / 2, 8);
        break;
    case V4L2_PIX_FMT_YVU420M:
    case V4L2_PIX_FMT_YUV422P:
    case V4L2_PIX_FMT_YUYV:
    case V4L2_PIX_FMT_UYVY:
        plane_size[0] = width * height;
        plane_size[1] = (width / 2) * (height / 2);
        plane_size[2] = (width / 2) * (height / 2);
        break;
    default:
        ALOGE("%s::unmatched v4l_pixel_format color_space(0x%x)",
             __func__, v4l_pixel_format);
        return -1;
        break;
    }

    return 0;
}

static int m_exynos_rotator_multiple_of_n(
    int number, int N)
{
    int result = number;
    switch (N) {
    case 1:
    case 2:
    case 4:
    case 8:
    case 16:
    case 32:
    case 64:
    case 128:
    case 256:
        result = (number - (number & (N-1)));
        break;
    default:
        result = number - (number % N);
        break;
    }
    return result;
}

static bool m_exynos_rotator_check_src_size(
    unsigned int *w,      unsigned int *h,
    unsigned int *crop_x, unsigned int *crop_y,
    unsigned int *crop_w, unsigned int *crop_h,
    int v4l2_colorformat)
{
    if (*w < ROTATOR_MIN_W_SIZE || *h < ROTATOR_MIN_H_SIZE) {
        ALOGE("%s::too small size (w : %d < %d) (h : %d < %d)",
            __func__, ROTATOR_MIN_W_SIZE, *w, ROTATOR_MIN_H_SIZE, *h);
        return false;
    }

    if (*crop_w < ROTATOR_MIN_W_SIZE || *crop_h < ROTATOR_MIN_H_SIZE) {
        ALOGE("%s::too small size (w : %d < %d) (h : %d < %d)",
            __func__, ROTATOR_MIN_W_SIZE,* crop_w, ROTATOR_MIN_H_SIZE, *crop_h);
        return false;
    }

    switch (v4l2_colorformat) {
    // YUV420 3p
    case V4L2_PIX_FMT_YUV420M:
    case V4L2_PIX_FMT_YVU420M:
        *w = ALIGN(*w, 16);
        *h = ALIGN(*h, 16);
        break;
    // YUV420 2p
    case V4L2_PIX_FMT_NV12M:
    case V4L2_PIX_FMT_NV12MT:
    case V4L2_PIX_FMT_NV21M:
        *w = ALIGN(*w, 8);
        *h = ALIGN(*h, 8);
        break;
    // YUV422
    case V4L2_PIX_FMT_YUYV:
    case V4L2_PIX_FMT_YUV422P:
    case V4L2_PIX_FMT_UYVY:
    case V4L2_PIX_FMT_NV21:
    case V4L2_PIX_FMT_NV16:
    case V4L2_PIX_FMT_YVYU:
    case V4L2_PIX_FMT_VYUY:
    // RGB
    case V4L2_PIX_FMT_RGB32:
    case V4L2_PIX_FMT_RGB24:
    case V4L2_PIX_FMT_RGB565:
    case V4L2_PIX_FMT_BGR32:
    case V4L2_PIX_FMT_RGB555X:
    case V4L2_PIX_FMT_RGB444:
    default:
        *w = ALIGN(*w, 4);
        *h = ALIGN(*h, 4);
        break;
    }
    *crop_w = m_exynos_rotator_multiple_of_n(*crop_w, 4);
    *crop_h = m_exynos_rotator_multiple_of_n(*crop_h, 4);

    return true;
}

static bool m_exynos_rotator_check_dst_size(
    unsigned int *w,      unsigned int *h,
    unsigned int *crop_x, unsigned int *crop_y,
    unsigned int *crop_w, unsigned int *crop_h,
    int v4l2_colorformat,
    int rotation)
{
    unsigned int *new_w;
    unsigned int *new_h;
    unsigned int *new_crop_w;
    unsigned int *new_crop_h;

    if (rotation == 90 || rotation == 270) {
        new_w = h;
        new_h = w;
        new_crop_w = crop_h;
        new_crop_h = crop_w;
    } else {
        new_w = w;
        new_h = h;
        new_crop_w = crop_w;
        new_crop_h = crop_h;
    }

    if (*w < ROTATOR_MIN_W_SIZE || *h < ROTATOR_MIN_H_SIZE) {
        ALOGE("%s::too small size (w : %d < %d) (h : %d < %d)",
            __func__, ROTATOR_MIN_W_SIZE, *w, ROTATOR_MIN_H_SIZE, *h);
        return false;
    }

    if (*crop_w < ROTATOR_MIN_W_SIZE || *crop_h < ROTATOR_MIN_H_SIZE) {
        ALOGE("%s::too small size (w : %d < %d) (h : %d < %d)",
            __func__, ROTATOR_MIN_W_SIZE,* crop_w, ROTATOR_MIN_H_SIZE, *crop_h);
        return false;
    }

    switch (v4l2_colorformat) {
    // YUV420 3p
    case V4L2_PIX_FMT_YUV420M:
    case V4L2_PIX_FMT_YVU420M:
        *new_w = ALIGN(*new_w, 16);
        *new_h = ALIGN(*new_h, 16);
        break;
    // YUV420 2p
    case V4L2_PIX_FMT_NV12M:
    case V4L2_PIX_FMT_NV12MT:
    case V4L2_PIX_FMT_NV21M:
        *new_w = ALIGN(*new_w, 8);
        *new_h = ALIGN(*new_h, 8);
        break;
    // YUV422
    case V4L2_PIX_FMT_YUYV:
    case V4L2_PIX_FMT_YUV422P:
    case V4L2_PIX_FMT_UYVY:
    case V4L2_PIX_FMT_NV21:
    case V4L2_PIX_FMT_NV16:
    case V4L2_PIX_FMT_YVYU:
    case V4L2_PIX_FMT_VYUY:
    // RGB
    case V4L2_PIX_FMT_RGB32:
    case V4L2_PIX_FMT_RGB24:
    case V4L2_PIX_FMT_RGB565:
    case V4L2_PIX_FMT_BGR32:
    case V4L2_PIX_FMT_RGB555X:
    case V4L2_PIX_FMT_RGB444:
    default:
        *new_w = ALIGN(*new_w, 4);
        *new_h = ALIGN(*new_h, 4);
        break;
    }
    *new_crop_w = m_exynos_rotator_multiple_of_n(*new_crop_w, 4);
    *new_crop_h = m_exynos_rotator_multiple_of_n(*new_crop_h, 4);

    return true;
}

static int m_exynos_rotator_create(void)
{
    int          fd = 0;
    unsigned int cap;
    char         node[32];

    sprintf(node, "%s%d", PFX_NODE_ROTATOR, NODE_NUM_ROTATOR);
    fd = exynos_v4l2_open(node, O_RDWR);
    if (fd < 0) {
        ALOGE("%s::exynos_v4l2_open(%s) fail", __func__, node);
        return -1;
    }

    cap = V4L2_CAP_STREAMING |
          V4L2_CAP_VIDEO_OUTPUT_MPLANE |
          V4L2_CAP_VIDEO_CAPTURE_MPLANE;

    if (exynos_v4l2_querycap(fd, cap) == false) {
        ALOGE("%s::exynos_v4l2_querycap() fail", __func__);
        if (0 < fd)
            close(fd);
        fd = 0;
        return -1;
    }
    return fd;
}

static bool m_exynos_rotator_destroy(
    struct ROTATOR_HANDLE *rotator_handle)
{
    if (rotator_handle->src.stream_on == true) {
        if (exynos_v4l2_streamoff(rotator_handle->rotator_fd, rotator_handle->src.buf_type) < 0)
            ALOGE("%s::exynos_v4l2_streamoff() fail", __func__);

        rotator_handle->src.stream_on = false;
    }

    if (rotator_handle->dst.stream_on == true) {
        if (exynos_v4l2_streamoff(rotator_handle->rotator_fd, rotator_handle->dst.buf_type) < 0)
            ALOGE("%s::exynos_v4l2_streamoff() fail", __func__);

        rotator_handle->dst.stream_on = false;
    }

    if (0 < rotator_handle->rotator_fd)
        close(rotator_handle->rotator_fd);
    rotator_handle->rotator_fd = 0;

    return true;
}

bool m_exynos_rotator_find_and_trylock_and_create(
    struct ROTATOR_HANDLE *rotator_handle)
{
    int          i                 = 0;
    bool         flag_find_new_rotator = false;
    unsigned int total_sleep_time  = 0;

    do {
        if (exynos_mutex_trylock(rotator_handle->obj_mutex) == true) {

            // destroy old one.
            m_exynos_rotator_destroy(rotator_handle);

            // create new one.
            rotator_handle->rotator_fd = m_exynos_rotator_create();
            if (rotator_handle->rotator_fd < 0) {
                rotator_handle->rotator_fd = 0;
                exynos_mutex_unlock(rotator_handle->obj_mutex);
                continue;
            }

            if (rotator_handle->cur_obj_mutex)
                exynos_mutex_unlock(rotator_handle->cur_obj_mutex);

            rotator_handle->cur_obj_mutex = rotator_handle->obj_mutex;

            flag_find_new_rotator = true;
            break;
        }

        // waiting for another process doesn't use rotator.
        // we need to make decision how to do.
        if (flag_find_new_rotator == false) {
            usleep(ROTATOR_WAITING_TIME_FOR_TRYLOCK);
            total_sleep_time += ROTATOR_WAITING_TIME_FOR_TRYLOCK;
            ALOGV("%s::waiting for anthere process doens't use rotator", __func__);
        }

    } while(   flag_find_new_rotator == false
            && total_sleep_time < MAX_ROTATOR_WAITING_TIME_FOR_TRYLOCK);

    if (flag_find_new_rotator == false)
        ALOGE("%s::we don't have no available rotator.. fail", __func__);

    return flag_find_new_rotator;
}

static bool m_exynos_rotator_set_format(
    int                  fd,
    struct rotator_info *info,
    bool                 force)
{
    struct v4l2_requestbuffers req_buf;
    int                        plane_count;

    plane_count = m_rotator_get_plane_count(info->v4l2_colorformat);
    if (plane_count < 0) {
        ALOGE("%s::not supported v4l2_colorformat", __func__);
        return false;
    }

    if (force == false) {
        // format
        info->format.type = info->buf_type;
        if (exynos_v4l2_g_fmt(fd, &info->format) < 0) {
            ALOGE("%s::exynos_v4l2_g_fmt() fail type=%d", __func__, info->buf_type);
            return false;
        }

        if (info->width            != info->format.fmt.pix_mp.width ||
            info->height           != info->format.fmt.pix_mp.height ||
            info->v4l2_colorformat != info->format.fmt.pix_mp.pixelformat) {
            ALOGV("%s::info is different..)", __func__);
            goto set_hw;
        }

        // crop
        if (info->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
        info->crop.type = info->buf_type;
        if (exynos_v4l2_g_crop(fd, &info->crop) < 0) {
            ALOGE("%s::exynos_v4l2_g_crop() fail", __func__);
            return false;
        }

        if (info->crop_left   != info->crop.c.left ||
            info->crop_top    != info->crop.c.top ||
            info->crop_width  != info->crop.c.width ||
            info->crop_height != info->crop.c.height) {
            ALOGV("%s::crop is different..", __func__);
            goto set_hw;
        }
        }

        // rotation value;

        int value = 0;

        if (exynos_v4l2_g_ctrl(fd, V4L2_CID_ROTATE, &value) < 0) {
            ALOGE("%s::exynos_v4l2_g_ctrl(V4L2_CID_ROTATE) fail");
            return false;
        }

        if (info->rotation != value) {
            ALOGV("%s::rotation is different..", __func__);
            goto set_hw;
        }

        // skip s_fmt
        ALOGV("%s::fmt, crop is same with old-one, so skip s_fmt crop..", __func__);
        return true;
    }

set_hw:

    if (info->stream_on == true) {
        if (exynos_v4l2_streamoff(fd, info->buf_type) < 0) {
            ALOGE("%s::exynos_v4l2_streamoff() fail", __func__);
            return false;
        }
        info->stream_on = false;
    }

    if (exynos_v4l2_s_ctrl(fd, V4L2_CID_ROTATE, info->rotation) < 0) {
        ALOGE("%s::exynos_v4l2_s_ctrl(V4L2_CID_ROTATE) fail", __func__);
        return false;
    }

    info->format.fmt.pix_mp.width       = info->width;
    info->format.fmt.pix_mp.height      = info->height;
    info->format.fmt.pix_mp.pixelformat = info->v4l2_colorformat;
    info->format.fmt.pix_mp.field       = V4L2_FIELD_ANY;
    info->format.fmt.pix_mp.num_planes  = plane_count;

    if (exynos_v4l2_s_fmt(fd, &info->format) < 0) {
        ALOGE("%s::exynos_v4l2_s_fmt() fail", __func__);
        return false;
    }

    if (info->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
    info->crop.type     = info->buf_type;
    info->crop.c.left   = info->crop_left;
    info->crop.c.top    = info->crop_top;
    info->crop.c.width  = info->crop_width;
    info->crop.c.height = info->crop_height;

    if (exynos_v4l2_s_crop(fd, &info->crop) < 0) {
        ALOGE("%s::exynos_v4l2_s_crop() fail", __func__);
        return false;
    }
    }

    if (exynos_v4l2_s_ctrl(fd, V4L2_CID_CACHEABLE, info->cacheable) < 0) {
        ALOGE("%s::exynos_v4l2_s_ctrl() fail", __func__);
        return false;
    }

    req_buf.count  = 1;
    req_buf.type   = info->buf_type;
    req_buf.memory = V4L2_MEMORY_USERPTR;
    if (exynos_v4l2_reqbufs(fd, &req_buf) < 0) {
        ALOGE("%s::exynos_v4l2_reqbufs() fail", __func__);
        return false;
    }

    return true;
}

static bool m_exynos_rotator_set_addr(
    int                  fd,
    struct rotator_info *info)
{
    unsigned int i;
    unsigned int plane_size[NUM_OF_ROTATOR_PLANES];

    m_rotator_get_plane_size(plane_size,
                         info->width,
                         info->height,
                         info->v4l2_colorformat);

    info->buffer.index    = 0;
    info->buffer.type     = info->buf_type;
    info->buffer.memory   = V4L2_MEMORY_USERPTR;
    info->buffer.m.planes = info->planes;
    info->buffer.length   = info->format.fmt.pix_mp.num_planes;

    for (i = 0; i < info->format.fmt.pix_mp.num_planes; i++) {
        info->buffer.m.planes[i].m.userptr = (unsigned long)info->addr[i];
        info->buffer.m.planes[i].length    = plane_size[i];
        info->buffer.m.planes[i].bytesused = 0;
    }

    if (exynos_v4l2_qbuf(fd, &info->buffer) < 0) {
        ALOGE("%s::exynos_v4l2_qbuf() fail", __func__);
        return false;
    }

    return true;
}

void *exynos_rotator_create(void)
{
    int i     = 0;
    int op_id = 0;
    char mutex_name[32];

    struct ROTATOR_HANDLE *rotator_handle = (struct ROTATOR_HANDLE *)malloc(sizeof(struct ROTATOR_HANDLE));
    if (rotator_handle == NULL) {
        ALOGE("%s::malloc(struct ROTATOR_HANDLE) fail", __func__);
        goto err;
    }

    rotator_handle->rotator_fd = 0;
    memset(&rotator_handle->src, 0, sizeof(struct rotator_info));
    memset(&rotator_handle->dst, 0, sizeof(struct rotator_info));

    rotator_handle->src.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
    rotator_handle->dst.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;

    rotator_handle->op_mutex = NULL;
    rotator_handle->obj_mutex = NULL;
    rotator_handle->cur_obj_mutex = NULL;

    srand(time(NULL));
    op_id = rand() % 1000000; // just make random id
    sprintf(mutex_name, "%sOp%d", LOG_TAG, op_id);
    rotator_handle->op_mutex = exynos_mutex_create(EXYNOS_MUTEX_TYPE_PRIVATE, mutex_name);
    if (rotator_handle->op_mutex == NULL) {
        ALOGE("%s::exynos_mutex_create(%s) fail", __func__, mutex_name);
        goto err;
    }

    exynos_mutex_lock(rotator_handle->op_mutex);

    sprintf(mutex_name, "%sObject%d", LOG_TAG, i);

    rotator_handle->obj_mutex = exynos_mutex_create(EXYNOS_MUTEX_TYPE_SHARED, mutex_name);
    if (rotator_handle->obj_mutex == NULL) {
        ALOGE("%s::exynos_mutex_create(%s) fail", __func__, mutex_name);
        goto err;
    }

    if (m_exynos_rotator_find_and_trylock_and_create(rotator_handle) == false) {
        ALOGE("%s::m_exynos_rotator_find_and_trylock_and_create() fail", __func__);
        goto err;
    }

    exynos_mutex_unlock(rotator_handle->cur_obj_mutex);
    exynos_mutex_unlock(rotator_handle->op_mutex);

    return (void *)rotator_handle;

err:
    if (rotator_handle) {
        m_exynos_rotator_destroy(rotator_handle);

        if (rotator_handle->cur_obj_mutex)
            exynos_mutex_unlock(rotator_handle->cur_obj_mutex);

        if ((rotator_handle->obj_mutex != NULL) &&
            (exynos_mutex_get_created_status(rotator_handle->obj_mutex) == true)) {
            if (exynos_mutex_destroy(rotator_handle->obj_mutex) == false)
                ALOGE("%s::exynos_mutex_destroy() fail", __func__);
        }

        if (rotator_handle->op_mutex)
            exynos_mutex_unlock(rotator_handle->op_mutex);

        free(rotator_handle);
    }

    return NULL;
}

void exynos_rotator_destroy(
    void *handle)
{
    int i = 0;
    struct ROTATOR_HANDLE *rotator_handle = (struct ROTATOR_HANDLE *)handle;

    if (handle == NULL) {
        ALOGE("%s::handle == NULL() fail", __func__);
        return;
    }

    exynos_mutex_lock(rotator_handle->op_mutex);
    exynos_mutex_lock(rotator_handle->cur_obj_mutex);

    m_exynos_rotator_destroy(rotator_handle);

    exynos_mutex_unlock(rotator_handle->cur_obj_mutex);

    if ((rotator_handle->obj_mutex != NULL) &&
        (exynos_mutex_get_created_status(rotator_handle->obj_mutex) == true)) {
        if (exynos_mutex_destroy(rotator_handle->obj_mutex) == false)
            ALOGE("%s::exynos_mutex_destroy() fail", __func__);
    }

    exynos_mutex_unlock(rotator_handle->op_mutex);

    if (rotator_handle)
        free(rotator_handle);
}

int exynos_rotator_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 v4l2_colorformat,
    unsigned int cacheable)
{
    struct ROTATOR_HANDLE *rotator_handle;
    rotator_handle = (struct ROTATOR_HANDLE *)handle;

    if (handle == NULL) {
        ALOGE("%s::handle == NULL() fail", __func__);
        return -1;
    }

    exynos_mutex_lock(rotator_handle->op_mutex);

    rotator_handle->src.width            = width;
    rotator_handle->src.height           = height;
    rotator_handle->src.crop_left        = crop_left;
    rotator_handle->src.crop_top         = crop_top;
    rotator_handle->src.crop_width       = crop_width;
    rotator_handle->src.crop_height      = crop_height;
    rotator_handle->src.v4l2_colorformat = v4l2_colorformat;
    rotator_handle->src.cacheable        = cacheable;

    exynos_mutex_unlock(rotator_handle->op_mutex);

    return 0;
}

int exynos_rotator_set_dst_format(
    void        *handle,
    unsigned int width,
    unsigned int height,
    unsigned int crop_left,
    unsigned int crop_top,
    unsigned int v4l2_colorformat,
    unsigned int cacheable)
{
    struct ROTATOR_HANDLE *rotator_handle;
    rotator_handle = (struct ROTATOR_HANDLE *)handle;

    if (handle == NULL) {
        ALOGE("%s::handle == NULL() fail", __func__);
        return -1;
    }

    exynos_mutex_lock(rotator_handle->op_mutex);

    rotator_handle->dst.width            = width;
    rotator_handle->dst.height           = height;
    rotator_handle->dst.crop_left        = crop_left;
    rotator_handle->dst.crop_top         = crop_top;
    rotator_handle->dst.crop_width       = rotator_handle->src.crop_width;
    rotator_handle->dst.crop_height      = rotator_handle->src.crop_height;
    rotator_handle->dst.v4l2_colorformat = v4l2_colorformat;
    rotator_handle->dst.cacheable        = cacheable;

    exynos_mutex_unlock(rotator_handle->op_mutex);

    return 0;
}

int exynos_rotator_set_rotation(
    void *handle,
    int   rotation)
{
    int ret = -1;
    struct ROTATOR_HANDLE *rotator_handle;
    rotator_handle = (struct ROTATOR_HANDLE *)handle;

    if (handle == NULL) {
        ALOGE("%s::handle == NULL() fail", __func__);
        return ret;
    }

    exynos_mutex_lock(rotator_handle->op_mutex);

    int new_rotation = rotation % 360;

    if (new_rotation % 90 != 0) {
        ALOGE("%s::rotation(%d) cannot be acceptable fail", __func__, rotation);
        goto done;
    }

    if(new_rotation < 0)
        new_rotation = -new_rotation;

    rotator_handle->src.rotation = new_rotation;
    rotator_handle->dst.rotation = new_rotation;

    ret = 0;
done:
    exynos_mutex_unlock(rotator_handle->op_mutex);

    return ret;
}

int exynos_rotator_set_src_addr(
    void *handle,
    void *addr[3])
{
    struct ROTATOR_HANDLE *rotator_handle;
    rotator_handle = (struct ROTATOR_HANDLE *)handle;

    if (handle == NULL) {
        ALOGE("%s::handle == NULL() fail", __func__);
        return -1;
    }

    exynos_mutex_lock(rotator_handle->op_mutex);

    rotator_handle->src.addr[0] = addr[0];
    rotator_handle->src.addr[1] = addr[1];
    rotator_handle->src.addr[2] = addr[2];

    exynos_mutex_unlock(rotator_handle->op_mutex);

    return 0;
}

int exynos_rotator_set_dst_addr(
    void *handle,
    void *addr[3])
{
    struct ROTATOR_HANDLE *rotator_handle;
    rotator_handle = (struct ROTATOR_HANDLE *)handle;

    if (handle == NULL) {
        ALOGE("%s::handle == NULL() fail", __func__);
        return -1;
    }

    exynos_mutex_lock(rotator_handle->op_mutex);

    rotator_handle->dst.addr[0] = addr[0];
    rotator_handle->dst.addr[1] = addr[1];
    rotator_handle->dst.addr[2] = addr[2];

    exynos_mutex_unlock(rotator_handle->op_mutex);

    return 0;
}

int exynos_rotator_convert(
    void *handle)
{
    struct ROTATOR_HANDLE *rotator_handle;
    int ret    = -1;
    int i      = 0;
    rotator_handle = (struct ROTATOR_HANDLE *)handle;

    if (handle == NULL) {
        ALOGE("%s::handle == NULL() fail", __func__);
        return -1;
    }

    char mutex_name[32];
    bool flag_new_rotator = false;

    exynos_mutex_lock(rotator_handle->op_mutex);

    if (exynos_mutex_trylock(rotator_handle->cur_obj_mutex) == false) {
        if (m_exynos_rotator_find_and_trylock_and_create(rotator_handle) == false) {
            ALOGE("%s::m_exynos_rotator_find_and_trylock_and_create() fail", __func__);
            goto done;
        }
        flag_new_rotator = true;
    }

    if (m_exynos_rotator_check_src_size(&rotator_handle->src.width, &rotator_handle->src.width,
                                    &rotator_handle->src.crop_left, &rotator_handle->src.crop_top,
                                    &rotator_handle->src.crop_width, &rotator_handle->src.crop_height,
                                    rotator_handle->src.v4l2_colorformat) == false) {
        ALOGE("%s::m_exynos_rotator_check_size(src) fail", __func__);
        goto done;
    }

    if (m_exynos_rotator_check_dst_size(&rotator_handle->dst.width, &rotator_handle->dst.height,
                                    &rotator_handle->dst.crop_left, &rotator_handle->dst.crop_top,
                                    &rotator_handle->dst.crop_width, &rotator_handle->dst.crop_height,
                                    rotator_handle->dst.v4l2_colorformat,
                                    rotator_handle->dst.rotation) == false) {
        ALOGE("%s::m_exynos_rotator_check_size(dst) fail", __func__);
        goto done;
    }

    if (m_exynos_rotator_set_format(rotator_handle->rotator_fd, &rotator_handle->src, flag_new_rotator) == false) {
        ALOGE("%s::m_exynos_rotator_set_format(src) fail", __func__);
        goto done;
    }

    if (m_exynos_rotator_set_format(rotator_handle->rotator_fd, &rotator_handle->dst, flag_new_rotator) == false) {
        ALOGE("%s::m_exynos_rotator_set_format(dst) fail", __func__);
        goto done;
    }

    if (m_exynos_rotator_set_addr(rotator_handle->rotator_fd, &rotator_handle->src) == false) {
        ALOGE("%s::m_exynos_rotator_set_addr(src) fail", __func__);
        goto done;
    }

    if (m_exynos_rotator_set_addr(rotator_handle->rotator_fd, &rotator_handle->dst) == false) {
        ALOGE("%s::m_exynos_rotator_set_addr(dst) fail", __func__);
        goto done;
    }

    if (rotator_handle->src.stream_on == false) {
        if (exynos_v4l2_streamon(rotator_handle->rotator_fd, rotator_handle->src.buf_type) < 0) {
            ALOGE("%s::exynos_v4l2_streamon(src) fail", __func__);
            goto done;
        }
        rotator_handle->src.stream_on = true;
    }

    if (rotator_handle->dst.stream_on == false) {
        if (exynos_v4l2_streamon(rotator_handle->rotator_fd, rotator_handle->dst.buf_type) < 0) {
            ALOGE("%s::exynos_v4l2_streamon(dst) fail", __func__);
            goto done;
        }
        rotator_handle->dst.stream_on = true;
    }

    if (exynos_v4l2_dqbuf(rotator_handle->rotator_fd, &rotator_handle->src.buffer) < 0) {
        ALOGE("%s::exynos_v4l2_dqbuf(src) fail", __func__);
        goto done;
    }

    if (exynos_v4l2_dqbuf(rotator_handle->rotator_fd, &rotator_handle->dst.buffer) < 0) {
        ALOGE("%s::exynos_v4l2_dqbuf(dst) fail", __func__);
        goto done;
    }

    if (rotator_handle->src.stream_on == true) {
        if (exynos_v4l2_streamoff(rotator_handle->rotator_fd, rotator_handle->src.buf_type) < 0) {
            ALOGE("%s::exynos_v4l2_streamon(src) fail", __func__);
            goto done;
        }
        rotator_handle->src.stream_on = false;
    }

    if (rotator_handle->dst.stream_on == true) {
        if (exynos_v4l2_streamoff(rotator_handle->rotator_fd, rotator_handle->dst.buf_type) < 0) {
            ALOGE("%s::exynos_v4l2_streamon(dst) fail", __func__);
            goto done;
        }
        rotator_handle->dst.stream_on = false;
    }

    ret = 0;

done:
    exynos_mutex_unlock(rotator_handle->cur_obj_mutex);
    exynos_mutex_unlock(rotator_handle->op_mutex);

    return ret;
}

int exynos_rotator_connect(
    void *handle,
    void *hw)
{
    struct ROTATOR_HANDLE *rotator_handle;
    int ret    = -1;
    rotator_handle = (struct ROTATOR_HANDLE *)handle;

    if (handle == NULL) {
        ALOGE("%s::handle == NULL() fail", __func__);
        return -1;
    }

    exynos_mutex_lock(rotator_handle->op_mutex);

    if (exynos_mutex_trylock(rotator_handle->cur_obj_mutex) == false) {
        if (m_exynos_rotator_find_and_trylock_and_create(rotator_handle) == false) {
            ALOGE("%s::m_exynos_rotator_find_and_trylock_and_create() fail", __func__);
            goto done;
        }
    }

    ret = 0;

done:
    exynos_mutex_unlock(rotator_handle->op_mutex);

    return ret;
}

int exynos_rotator_disconnect(
    void *handle,
    void *hw)
{
    struct ROTATOR_HANDLE *rotator_handle;
    rotator_handle = (struct ROTATOR_HANDLE *)handle;

    if (handle == NULL) {
        ALOGE("%s::handle == NULL() fail", __func__);
        return -1;
    }

    exynos_mutex_lock(rotator_handle->op_mutex);

    exynos_mutex_unlock(rotator_handle->cur_obj_mutex);
    exynos_mutex_unlock(rotator_handle->op_mutex);

    return 0;
}