/*
Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above
      copyright notice, this list of conditions and the following
      disclaimer in the documentation and/or other materials provided
      with the distribution.
    * Neither the name of The Linux Foundation nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <pthread.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h>
#include <semaphore.h>

#include "mm_camera_dbg.h"
#include "mm_camera_interface.h"
#include "mm_camera.h"

#define MM_CAMERA_MAX_NUM_FRAMES     16

/* internal function decalre */
int32_t mm_stream_qbuf(mm_stream_t *my_obj,
                       mm_camera_buf_def_t *buf);
int32_t mm_stream_set_ext_mode(mm_stream_t * my_obj);
int32_t mm_stream_set_fmt(mm_stream_t * my_obj);
int32_t mm_stream_get_offset(mm_stream_t *my_obj);
int32_t mm_stream_set_cid(mm_stream_t *my_obj,stream_cid_t *in_value);
int32_t mm_stream_init_bufs(mm_stream_t * my_obj);
int32_t mm_stream_deinit_bufs(mm_stream_t * my_obj);
int32_t mm_stream_request_buf(mm_stream_t * my_obj);
int32_t mm_stream_unreg_buf(mm_stream_t * my_obj);
int32_t mm_stream_release(mm_stream_t *my_obj);
int32_t mm_stream_get_crop(mm_stream_t *my_obj,
                         mm_camera_rect_t *crop);
int32_t mm_stream_get_cid(mm_stream_t *my_obj,
                          stream_cid_t *out_value);
int32_t mm_stream_set_parm_acquire(mm_stream_t *my_obj,
                         void *value);
int32_t mm_stream_get_parm_acquire(mm_stream_t *my_obj,
                         void *value);
int32_t mm_stream_set_parm_config(mm_stream_t *my_obj,
                         void *value);
int32_t mm_stream_get_parm_config(mm_stream_t *my_obj,
                         void *value);
int32_t mm_stream_set_parm_start(mm_stream_t *my_obj,
                         void *value);
int32_t mm_stream_get_parm_start(mm_stream_t *my_obj,
                         void *value);
int32_t mm_stream_streamon(mm_stream_t *my_obj);
int32_t mm_stream_streamoff(mm_stream_t *my_obj);
int32_t mm_stream_read_msm_frame(mm_stream_t * my_obj,
                                 mm_camera_buf_info_t* buf_info);
int32_t mm_stream_config(mm_stream_t *my_obj,
                         mm_camera_stream_config_t *config);
uint8_t mm_stream_need_stream_on(mm_stream_t *my_obj);
int32_t mm_stream_reg_buf(mm_stream_t * my_obj);
int32_t mm_stream_buf_done(mm_stream_t * my_obj,
                           mm_camera_buf_def_t *frame);


/* state machine function declare */
int32_t mm_stream_fsm_inited(mm_stream_t * my_obj,
                             mm_stream_evt_type_t evt,
                             void * in_val,
                             void * out_val);
int32_t mm_stream_fsm_acquired(mm_stream_t * my_obj,
                               mm_stream_evt_type_t evt,
                               void * in_val,
                               void * out_val);
int32_t mm_stream_fsm_cfg(mm_stream_t * my_obj,
                          mm_stream_evt_type_t evt,
                          void * in_val,
                          void * out_val);
int32_t mm_stream_fsm_buffed(mm_stream_t * my_obj,
                             mm_stream_evt_type_t evt,
                             void * in_val,
                             void * out_val);
int32_t mm_stream_fsm_reg(mm_stream_t * my_obj,
                          mm_stream_evt_type_t evt,
                          void * in_val,
                          void * out_val);
int32_t mm_stream_fsm_active_stream_on(mm_stream_t * my_obj,
                                       mm_stream_evt_type_t evt,
                                       void * in_val,
                                       void * out_val);
int32_t mm_stream_fsm_active_stream_off(mm_stream_t * my_obj,
                                        mm_stream_evt_type_t evt,
                                        void * in_val,
                                        void * out_val);

extern int32_t mm_camera_send_native_ctrl_cmd(mm_camera_obj_t * my_obj,
                                       cam_ctrl_type type,
                                       uint32_t length,
                                       void *value);

static int get_stream_inst_handle(mm_stream_t *my_obj)
{
  int rc = 0;
  uint32_t inst_handle;
  struct msm_camera_v4l2_ioctl_t v4l2_ioctl;

  v4l2_ioctl.id = MSM_V4L2_PID_INST_HANDLE;
  v4l2_ioctl.ioctl_ptr = &inst_handle;
  v4l2_ioctl.len = sizeof(inst_handle);
  rc = ioctl(my_obj->fd, MSM_CAM_V4L2_IOCTL_PRIVATE_G_CTRL, &v4l2_ioctl);
  if (rc) {
    CDBG_ERROR("%s Error getting mctl pp inst handle", __func__);
    return rc;
  }

  my_obj->inst_hdl = inst_handle;
  CDBG("%s: inst handle = %x rc = %d\n", __func__,
    my_obj->inst_hdl, rc);
  return rc;
}

void mm_stream_handle_rcvd_buf(mm_stream_t *my_obj,
                                  mm_camera_buf_info_t *buf_info)
{
    int32_t i;
    uint8_t has_cb = 0;

    /* enqueue to super buf thread */
    if (my_obj->is_bundled) {
        mm_camera_cmdcb_t* node = NULL;

        /* send sem_post to wake up channel cmd thread to enqueue to super buffer */
        node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
        if (NULL != node) {
            memset(node, 0, sizeof(mm_camera_cmdcb_t));
            node->cmd_type = MM_CAMERA_CMD_TYPE_DATA_CB;
            memcpy(&node->u.buf, buf_info, sizeof(mm_camera_buf_info_t));

            /* enqueue to cmd thread */
            mm_camera_queue_enq(&(my_obj->ch_obj->cmd_thread.cmd_queue), node);

            /* wake up cmd thread */
            sem_post(&(my_obj->ch_obj->cmd_thread.cmd_sem));
        } else {
            CDBG_ERROR("%s: No memory for mm_camera_node_t", __func__);
        }
    }

    /* check if has CB */
    for (i=0 ; i< MM_CAMERA_STREAM_BUF_CB_MAX; i++) {
        if(NULL != my_obj->buf_cb[i].cb) {
            has_cb = 1;
        }
    }
    if(has_cb) {
        mm_camera_cmdcb_t* node = NULL;

        /* send sem_post to wake up cmd thread to dispatch dataCB */
        node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
        if (NULL != node) {
            memset(node, 0, sizeof(mm_camera_cmdcb_t));
            node->cmd_type = MM_CAMERA_CMD_TYPE_DATA_CB;
            memcpy(&node->u.buf, buf_info, sizeof(mm_camera_buf_info_t));

            /* enqueue to cmd thread */
            mm_camera_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

            /* wake up cmd thread */
            sem_post(&(my_obj->cmd_thread.cmd_sem));
        } else {
            CDBG_ERROR("%s: No memory for mm_camera_node_t", __func__);
        }
    }
}

static void mm_stream_data_notify(void* user_data)
{
    mm_stream_t *my_obj = (mm_stream_t*)user_data;
    int32_t idx = -1, i, rc;
    uint8_t has_cb = 0;
    mm_camera_buf_info_t buf_info;

    if (NULL == my_obj) {
        return;
    }

    if (MM_STREAM_STATE_ACTIVE_STREAM_ON != my_obj->state) {
        /* this Cb will only received in active_stream_on state
         * if not so, return here */
        CDBG_ERROR("%s: ERROR!! Wrong state (%d) to receive data notify!",
                   __func__, my_obj->state);
        return;
    }

    memset(&buf_info, 0, sizeof(mm_camera_buf_info_t));

    pthread_mutex_lock(&my_obj->buf_lock);
    rc = mm_stream_read_msm_frame(my_obj, &buf_info);
    if (rc != 0) {
        pthread_mutex_unlock(&my_obj->buf_lock);
        return;
    }
    idx = buf_info.buf->buf_idx;

    /* update buffer location */
    my_obj->buf_status[idx].in_kernel = 0;

    /* update buf ref count */
    if (my_obj->is_bundled) {
        /* need to add into super buf since bundled, add ref count */
        my_obj->buf_status[idx].buf_refcnt++;
    }

    for (i=0; i < MM_CAMERA_STREAM_BUF_CB_MAX; i++) {
        if(NULL != my_obj->buf_cb[i].cb) {
            /* for every CB, add ref count */
            my_obj->buf_status[idx].buf_refcnt++;
            has_cb = 1;
        }
    }
    pthread_mutex_unlock(&my_obj->buf_lock);

    mm_stream_handle_rcvd_buf(my_obj, &buf_info);
}

/* special function for dataCB registered at other stream */
static void mm_stream_buf_notify(mm_camera_super_buf_t *super_buf,
                                 void *user_data)
{
    mm_stream_t * my_obj = (mm_stream_t*)user_data;
    mm_camera_buf_info_t buf_info;
    int8_t i;
    mm_camera_buf_def_t *buf = super_buf->bufs[0];

    CDBG("%s : E",__func__);
    if (my_obj == NULL) {
        return;
    }

    if (MM_STREAM_STATE_ACTIVE_STREAM_OFF != my_obj->state) {
        /* this CB will only received in active_stream_off state
         * if not so, return here */
        return;
    }

    /* 1) copy buf into local buf */
    if (my_obj->buf_num <= 0) {
        CDBG_ERROR("%s: Local buf is not allocated yet", __func__);
        return;
    }

    my_obj->buf[0].buf_idx = 0;
    my_obj->buf[0].stream_id = my_obj->my_hdl;
    my_obj->buf[0].frame_idx = buf->frame_idx;

    memcpy(&my_obj->buf[0].ts, &buf->ts, sizeof(buf->ts));

    memcpy(&my_obj->buf[0].planes,&buf->planes,buf->num_planes * sizeof(struct v4l2_plane));

    /* set flag to indicate buf be to sent out is from local */
    my_obj->is_local_buf = 1;

    /* 2) buf_done the buf from other stream */
    mm_channel_qbuf(my_obj->ch_obj, buf);

    /* 3) handle received buf */
    memset(&buf_info, 0, sizeof(mm_camera_buf_info_t));
    buf_info.frame_idx =my_obj->buf[0].frame_idx;
    buf_info.buf = &my_obj->buf[0];
    buf_info.stream_id = my_obj->my_hdl;
    mm_stream_handle_rcvd_buf(my_obj, &buf_info);
}

static void mm_stream_dispatch_app_data(mm_camera_cmdcb_t *cmd_cb,
                                        void* user_data)
{
    int i;
    mm_stream_t * my_obj = (mm_stream_t *)user_data;
    mm_camera_buf_info_t* buf_info = NULL;
    mm_camera_super_buf_t super_buf;

    if (NULL == my_obj) {
        return;
    }

    if (MM_CAMERA_CMD_TYPE_DATA_CB != cmd_cb->cmd_type) {
        CDBG_ERROR("%s: Wrong cmd_type (%d) for dataCB",
                   __func__, cmd_cb->cmd_type);
        return;
    }

    buf_info = &cmd_cb->u.buf;
    memset(&super_buf, 0, sizeof(mm_camera_super_buf_t));
    super_buf.num_bufs = 1;
    super_buf.bufs[0] = buf_info->buf;
    super_buf.camera_handle = my_obj->ch_obj->cam_obj->my_hdl;
    super_buf.ch_id = my_obj->ch_obj->my_hdl;


    pthread_mutex_lock(&my_obj->cb_lock);
    for(i = 0; i < MM_CAMERA_STREAM_BUF_CB_MAX; i++) {
        if(NULL != my_obj->buf_cb[i].cb) {
            if (my_obj->buf_cb[i].cb_count != 0) {
                /* if <0, means infinite CB
                 * if >0, means CB for certain times
                 * both case we need to call CB */
                my_obj->buf_cb[i].cb(&super_buf,
                                     my_obj->buf_cb[i].user_data);
            }

            /* if >0, reduce count by 1 every time we called CB until reaches 0
             * when count reach 0, reset the buf_cb to have no CB */
            if (my_obj->buf_cb[i].cb_count > 0) {
                my_obj->buf_cb[i].cb_count--;
                if (0 == my_obj->buf_cb[i].cb_count) {
                    my_obj->buf_cb[i].cb = NULL;
                    my_obj->buf_cb[i].user_data = NULL;
                }
            }
        }
    }
    pthread_mutex_unlock(&my_obj->cb_lock);
}

/* state machine entry */
int32_t mm_stream_fsm_fn(mm_stream_t *my_obj,
                         mm_stream_evt_type_t evt,
                         void * in_val,
                         void * out_val)
{
    int32_t rc = -1;

    CDBG("%s : E - evt = %d, my_obj->state = %d",__func__,evt,my_obj->state);
    switch (my_obj->state) {
    case MM_STREAM_STATE_NOTUSED:
        CDBG("%s: Not handling evt in unused state", __func__);
        break;
    case MM_STREAM_STATE_INITED:
        rc = mm_stream_fsm_inited(my_obj, evt, in_val, out_val);
        break;
    case MM_STREAM_STATE_ACQUIRED:
        rc = mm_stream_fsm_acquired(my_obj, evt, in_val, out_val);
        break;
    case MM_STREAM_STATE_CFG:
        rc = mm_stream_fsm_cfg(my_obj, evt, in_val, out_val);
        break;
    case MM_STREAM_STATE_BUFFED:
        rc = mm_stream_fsm_buffed(my_obj, evt, in_val, out_val);
        break;
    case MM_STREAM_STATE_REG:
        rc = mm_stream_fsm_reg(my_obj, evt, in_val, out_val);
        break;
    case MM_STREAM_STATE_ACTIVE_STREAM_ON:
        rc = mm_stream_fsm_active_stream_on(my_obj, evt, in_val, out_val);
        break;
    case MM_STREAM_STATE_ACTIVE_STREAM_OFF:
        rc = mm_stream_fsm_active_stream_off(my_obj, evt, in_val, out_val);
        break;
    default:
        CDBG("%s: Not a valid state (%d)", __func__, my_obj->state);
        break;
    }
    CDBG("%s : X rc =%d",__func__,rc);
    return rc;
}

int32_t mm_stream_fsm_inited(mm_stream_t *my_obj,
                             mm_stream_evt_type_t evt,
                             void * in_val,
                             void * out_val)
{
    int32_t rc = 0;
    char dev_name[MM_CAMERA_DEV_NAME_LEN];

    CDBG("%s :E evt = %d",__func__,evt);
    switch(evt) {
    case MM_STREAM_EVT_ACQUIRE:
        if ((NULL == my_obj->ch_obj) || (NULL == my_obj->ch_obj->cam_obj)) {
            CDBG_ERROR("%s: NULL channel or camera obj\n", __func__);
            rc = -1;
            break;
        }

        snprintf(dev_name, sizeof(dev_name), "/dev/%s",
                 mm_camera_util_get_dev_name(my_obj->ch_obj->cam_obj->my_hdl));

        my_obj->fd = open(dev_name, O_RDWR | O_NONBLOCK);
        if (my_obj->fd <= 0) {
            CDBG_ERROR("%s: open dev returned %d\n", __func__, my_obj->fd);
            rc = -1;
            break;
        }
        CDBG("%s: open dev fd = %d, ext_image_mode = %d, sensor_idx = %d\n",
                 __func__, my_obj->fd, my_obj->ext_image_mode, my_obj->sensor_idx);
        rc = mm_stream_set_ext_mode(my_obj);
        if (0 == rc) {
            my_obj->state = MM_STREAM_STATE_ACQUIRED;
        } else {
            /* failed setting ext_mode
             * close fd */
            if(my_obj->fd > 0) {
                close(my_obj->fd);
                my_obj->fd = -1;
            }
            break;
        }
        rc = get_stream_inst_handle(my_obj);
        if(rc) {
            if(my_obj->fd > 0) {
                close(my_obj->fd);
                my_obj->fd = -1;
            }
        }
        break;
    default:
        CDBG_ERROR("%s: Invalid evt=%d, stream_state=%d",
                   __func__,evt,my_obj->state);
        rc = -1;
        break;
    }
    return rc;
}

int32_t mm_stream_fsm_acquired(mm_stream_t *my_obj,
                               mm_stream_evt_type_t evt,
                               void * in_val,
                               void * out_val)
{
    int32_t rc = 0;

    CDBG("%s :E evt = %d",__func__,evt);
    switch(evt) {
    case MM_STREAM_EVT_SET_FMT:
        {
            mm_camera_stream_config_t *config =
                (mm_camera_stream_config_t *)in_val;

            rc = mm_stream_config(my_obj, config);

            /* change state to configed */
            my_obj->state = MM_STREAM_STATE_CFG;

            break;
        }
    case MM_STREAM_EVT_RELEASE:
        rc = mm_stream_release(my_obj);
        /* change state to not used */
         my_obj->state = MM_STREAM_STATE_NOTUSED;
        break;
    case MM_STREAM_EVT_SET_PARM:
        rc = mm_stream_set_parm_acquire(my_obj, in_val);
        break;
    case MM_STREAM_EVT_GET_PARM:
        rc = mm_stream_get_parm_acquire(my_obj,out_val);
        break;
    default:
        CDBG_ERROR("%s: Invalid evt=%d, stream_state=%d",
                   __func__, evt, my_obj->state);
        rc = -1;
    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

int32_t mm_stream_fsm_cfg(mm_stream_t * my_obj,
                          mm_stream_evt_type_t evt,
                          void * in_val,
                          void * out_val)
{
    int32_t rc = 0;
    CDBG("%s :E evt = %d",__func__,evt);
    switch(evt) {
    case MM_STREAM_EVT_SET_FMT:
        {
            mm_camera_stream_config_t *config =
                (mm_camera_stream_config_t *)in_val;

            rc = mm_stream_config(my_obj, config);

            /* change state to configed */
            my_obj->state = MM_STREAM_STATE_CFG;

            break;
        }
    case MM_STREAM_EVT_RELEASE:
        rc = mm_stream_release(my_obj);
        my_obj->state = MM_STREAM_STATE_NOTUSED;
        break;
    case MM_STREAM_EVT_SET_PARM:
        rc = mm_stream_set_parm_config(my_obj, in_val);
        break;
    case MM_STREAM_EVT_GET_PARM:
        rc = mm_stream_get_parm_config(my_obj, out_val);
        break;
    case MM_STREAM_EVT_GET_BUF:
        rc = mm_stream_init_bufs(my_obj);
        /* change state to buff allocated */
        if(0 == rc) {
            my_obj->state = MM_STREAM_STATE_BUFFED;
        }
        break;
    default:
        CDBG_ERROR("%s: Invalid evt=%d, stream_state=%d",
                   __func__, evt, my_obj->state);
        rc = -1;
    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

int32_t mm_stream_fsm_buffed(mm_stream_t * my_obj,
                             mm_stream_evt_type_t evt,
                             void * in_val,
                             void * out_val)
{
    int32_t rc = 0;
    CDBG("%s :E evt = %d",__func__,evt);
    switch(evt) {
    case MM_STREAM_EVT_PUT_BUF:
        rc = mm_stream_deinit_bufs(my_obj);
        /* change state to configed */
        if(0 == rc) {
            my_obj->state = MM_STREAM_STATE_CFG;
        }
        break;
    case MM_STREAM_EVT_REG_BUF:
        rc = mm_stream_reg_buf(my_obj);
        /* change state to regged */
        if(0 == rc) {
            my_obj->state = MM_STREAM_STATE_REG;
        }
        break;
    case MM_STREAM_EVT_SET_PARM:
        rc = mm_stream_set_parm_config(my_obj, in_val);
        break;
    case MM_STREAM_EVT_GET_PARM:
        rc = mm_stream_get_parm_config(my_obj, out_val);
        break;
    default:
        CDBG_ERROR("%s: Invalid evt=%d, stream_state=%d",
                   __func__, evt, my_obj->state);
        rc = -1;
    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

int32_t mm_stream_fsm_reg(mm_stream_t * my_obj,
                          mm_stream_evt_type_t evt,
                          void * in_val,
                          void * out_val)
{
    int32_t rc = 0;
    CDBG("%s :E evt = %d",__func__,evt);

    switch(evt) {
    case MM_STREAM_EVT_UNREG_BUF:
        rc = mm_stream_unreg_buf(my_obj);

        /* change state to buffed */
        my_obj->state = MM_STREAM_STATE_BUFFED;
        break;
    case MM_STREAM_EVT_START:
        {
            /* launch cmd thread if CB is not null */
            if (NULL != my_obj->buf_cb) {
                mm_camera_cmd_thread_launch(&my_obj->cmd_thread,
                                mm_stream_dispatch_app_data,
                                (void *)my_obj);

            }

            if(mm_stream_need_stream_on(my_obj)) {
                rc = mm_stream_streamon(my_obj);
                if (0 != rc) {
                    /* failed stream on, need to release cmd thread if it's launched */
                    if (NULL != my_obj->buf_cb) {
                        mm_camera_cmd_thread_release(&my_obj->cmd_thread);

                    }
                    break;
                }
                my_obj->state = MM_STREAM_STATE_ACTIVE_STREAM_ON;
            } else {
                /* register one time CB at video fd */
                CDBG("%s : Video Size snapshot Enabled",__func__);
                mm_stream_data_cb_t cb;
                memset(&cb, 0, sizeof(mm_stream_data_cb_t));
                cb.cb_count = 1; /* one time reigstration */
                cb.user_data = (void*)my_obj;
                cb.cb = mm_stream_buf_notify;
                rc = mm_channel_reg_stream_cb(my_obj->ch_obj, &cb,
                                              MSM_V4L2_EXT_CAPTURE_MODE_VIDEO,
                                              my_obj->sensor_idx);
                my_obj->state = MM_STREAM_STATE_ACTIVE_STREAM_OFF;
            }
        }
        break;
    case MM_STREAM_EVT_SET_PARM:
        rc = mm_stream_set_parm_config(my_obj, in_val);
        break;
    case MM_STREAM_EVT_GET_PARM:
        rc = mm_stream_get_parm_config(my_obj, out_val);
        break;
    default:
        CDBG_ERROR("%s: Invalid evt=%d, stream_state=%d",
                   __func__, evt, my_obj->state);
        rc = -1;
    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

int32_t mm_stream_fsm_active_stream_on(mm_stream_t * my_obj,
                                              mm_stream_evt_type_t evt,
                                              void * in_val,
                                              void * out_val)
{
    int32_t rc = 0;
    CDBG("%s :E evt = %d",__func__,evt);
    switch(evt) {
    case MM_STREAM_EVT_QBUF:
        rc = mm_stream_buf_done(my_obj, (mm_camera_buf_def_t *)in_val);
        break;
    case MM_STREAM_EVT_STOP:
        {
            rc = mm_stream_streamoff(my_obj);
            if (NULL != my_obj->buf_cb) {
                mm_camera_cmd_thread_release(&my_obj->cmd_thread);

            }
            my_obj->state = MM_STREAM_STATE_REG;
        }
        break;
    case MM_STREAM_EVT_SET_PARM:
        rc = mm_stream_set_parm_start(my_obj, in_val);
        break;
    case MM_STREAM_EVT_GET_PARM:
        rc = mm_stream_get_parm_start(my_obj, out_val);
        break;
    default:
        CDBG_ERROR("%s: Invalid evt=%d, stream_state=%d",
                   __func__, evt, my_obj->state);
        return -1;
    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

int32_t mm_stream_fsm_active_stream_off(mm_stream_t * my_obj,
                                               mm_stream_evt_type_t evt,
                                               void * in_val,
                                               void * out_val)
{
    int32_t rc = 0;
    CDBG("%s :E evt = %d",__func__,evt);
    switch(evt) {
    case MM_STREAM_EVT_QBUF:
        rc = mm_stream_buf_done(my_obj, (mm_camera_buf_def_t *)in_val);
        break;
    case MM_STREAM_EVT_STOP:
        {
            if (NULL != my_obj->buf_cb) {
                rc = mm_camera_cmd_thread_release(&my_obj->cmd_thread);

            }
            my_obj->state = MM_STREAM_STATE_REG;
        }
        break;
   case MM_STREAM_EVT_SET_PARM:
        rc = mm_stream_set_parm_config(my_obj, in_val);
        break;
    case MM_STREAM_EVT_GET_PARM:
        rc = mm_stream_get_parm_config(my_obj, out_val);
        break;
    default:
        CDBG_ERROR("%s: Invalid evt=%d, stream_state=%d",
                   __func__, evt, my_obj->state);
        return -1;
    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

int32_t mm_stream_config(mm_stream_t *my_obj,
                         mm_camera_stream_config_t *config)
{
    int32_t rc = 0;
    memcpy(&my_obj->fmt, &config->fmt, sizeof(mm_camera_image_fmt_t));
    my_obj->hal_requested_num_bufs = config->num_of_bufs;
    my_obj->need_stream_on = config->need_stream_on;

    rc = mm_stream_get_offset(my_obj);
    if(rc != 0) {
        CDBG_ERROR("%s: Error in offset query",__func__);
        return rc;
    }
    if(mm_stream_need_stream_on(my_obj)) {
        /* only send fmt to backend if we need streamon */
        rc = mm_stream_set_fmt(my_obj);
    }
    return rc;
}

int32_t mm_stream_release(mm_stream_t *my_obj)
{
    int32_t rc;

    /* close fd */
    if(my_obj->fd > 0)
    {
        close(my_obj->fd);
    }

    /* destroy mutex */
    pthread_mutex_destroy(&my_obj->buf_lock);
    pthread_mutex_destroy(&my_obj->cb_lock);

    /* reset stream obj */
    memset(my_obj, 0, sizeof(mm_stream_t));
    my_obj->fd = -1;

    return 0;
}

uint8_t mm_stream_need_stream_on(mm_stream_t *my_obj)
{
    return my_obj->need_stream_on;
}

int32_t mm_stream_streamon(mm_stream_t *my_obj)
{
    int32_t rc;
    enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;

    CDBG("%s :E",__func__);
    /* Add fd to data poll thread */
    rc = mm_camera_poll_thread_add_poll_fd(&my_obj->ch_obj->poll_thread[0],
                                           my_obj->my_hdl,
                                           my_obj->fd,
                                           mm_stream_data_notify,
                                           (void*)my_obj);
    if (rc < 0) {
        return rc;
    }
    rc = ioctl(my_obj->fd, VIDIOC_STREAMON, &buf_type);
    if (rc < 0) {
        CDBG_ERROR("%s: ioctl VIDIOC_STREAMON failed: rc=%d\n",
                   __func__, rc);
        /* remove fd from data poll thread in case of failure */
        mm_camera_poll_thread_del_poll_fd(&my_obj->ch_obj->poll_thread[0], my_obj->my_hdl);
    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

int32_t mm_stream_streamoff(mm_stream_t *my_obj)
{
    int32_t rc;
    enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;

    /* step1: remove fd from data poll thread */
    mm_camera_poll_thread_del_poll_fd(&my_obj->ch_obj->poll_thread[0], my_obj->my_hdl);

    /* step2: stream off */
    rc = ioctl(my_obj->fd, VIDIOC_STREAMOFF, &buf_type);
    if (rc < 0) {
        CDBG_ERROR("%s: STREAMOFF failed: %s\n",
                __func__, strerror(errno));
    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

static uint32_t mm_stream_util_get_v4l2_fmt(cam_format_t fmt,
                                            uint8_t *num_planes)
{
    uint32_t val;
    switch(fmt) {
    case CAMERA_YUV_420_NV12:
        val = V4L2_PIX_FMT_NV12;
        *num_planes = 2;
        break;
    case CAMERA_YUV_420_NV21:
        val = V4L2_PIX_FMT_NV21;
        *num_planes = 2;
        break;
    case CAMERA_BAYER_SBGGR10:
    case CAMERA_RDI:
        val= V4L2_PIX_FMT_SBGGR10;
        *num_planes = 1;
        break;
    case CAMERA_YUV_422_NV61:
        val= V4L2_PIX_FMT_NV61;
        *num_planes = 2;
        break;
    default:
        val = 0;
        *num_planes = 0;
        break;
    }
    return val;
}

int32_t mm_stream_read_msm_frame(mm_stream_t * my_obj,
                                 mm_camera_buf_info_t* buf_info)
{
    int32_t idx = -1, rc = 0;
    struct v4l2_buffer vb;
    struct v4l2_plane planes[VIDEO_MAX_PLANES];
    uint32_t i = 0;
    uint8_t num_planes = 0;

    mm_stream_util_get_v4l2_fmt(my_obj->fmt.fmt,
                                &num_planes);

    memset(&vb,  0,  sizeof(vb));
    vb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    vb.memory = V4L2_MEMORY_USERPTR;
    vb.m.planes = &planes[0];
    vb.length = num_planes;

    rc = ioctl(my_obj->fd, VIDIOC_DQBUF, &vb);
    if (rc < 0) {
        CDBG_ERROR("%s: VIDIOC_DQBUF ioctl call failed (rc=%d)\n",
                   __func__, rc);
    } else {
        int8_t idx = vb.index;
        buf_info->buf = &my_obj->buf[idx];
        buf_info->frame_idx = vb.sequence;
        buf_info->stream_id = my_obj->my_hdl;
        buf_info->need_pp = my_obj->is_pp_needed;

        buf_info->buf->stream_id = my_obj->my_hdl;
        buf_info->buf->buf_idx = idx;
        buf_info->buf->frame_idx = vb.sequence;
        buf_info->buf->ts.tv_sec  = vb.timestamp.tv_sec;
        buf_info->buf->ts.tv_nsec = vb.timestamp.tv_usec * 1000;

        for(i = 0; i < vb.length; i++) {
            CDBG("%s plane %d addr offset: %d data offset:%d\n",
                 __func__, i, vb.m.planes[i].reserved[0],
                 vb.m.planes[i].data_offset);
            buf_info->buf->planes[i].reserved[0] =
                vb.m.planes[i].reserved[0];
            buf_info->buf->planes[i].data_offset =
                vb.m.planes[i].data_offset;
        }


    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

int32_t mm_stream_get_crop(mm_stream_t *my_obj,
                           mm_camera_rect_t *crop)
{
    struct v4l2_crop crop_info;
    int32_t rc = 0;

    memset(&crop_info, 0, sizeof(crop_info));
    crop_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    rc = ioctl(my_obj->fd, VIDIOC_G_CROP, &crop_info);
    if (0 == rc) {
        crop->left = crop_info.c.left;
        crop->top = crop_info.c.top;
        crop->width = crop_info.c.width;
        crop->height = crop_info.c.height;
    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

int32_t mm_stream_set_parm_acquire(mm_stream_t *my_obj,
                         void *in_value)
{
    int32_t rc = 0;
    mm_evt_paylod_stream_parm_t *payload = (mm_evt_paylod_stream_parm_t *)in_value;
    mm_camera_stream_parm_t parm_type = payload->parm_type;

    CDBG("%s: parm_type = %d",__func__,(int)parm_type);
    switch(parm_type) {
    case MM_CAMERA_STREAM_CID:{
            stream_cid_t *value = (stream_cid_t *)in_value;
            mm_stream_set_cid(my_obj,value);
            break;
        }
        default:
            CDBG_ERROR("%s : Parm -%d set is not supported here",__func__,(int)parm_type);
            break;
    }
    return rc;
}
int32_t mm_stream_get_parm_acquire(mm_stream_t *my_obj,
                         void *out_value)
{
    int32_t rc = 0;
    mm_evt_paylod_stream_parm_t *payload = (mm_evt_paylod_stream_parm_t *)out_value;
    mm_camera_stream_parm_t parm_type = payload->parm_type;

    CDBG("%s: parm_type = %d",__func__,(int)parm_type);
    switch(parm_type) {
    case MM_CAMERA_STREAM_CID:{
            stream_cid_t *value = (stream_cid_t *)out_value;
            rc = mm_stream_get_cid(my_obj,value);
            break;
        }
        case MM_CAMERA_STREAM_CROP:{
            mm_camera_rect_t *crop = (mm_camera_rect_t *)out_value;
            rc = mm_stream_get_crop(my_obj, crop);
            break;
        }
        default:
            CDBG_ERROR("%s : Parm -%d get is not supported here",__func__,(int)parm_type);
            break;
    }
    return rc;
}

int32_t mm_stream_set_parm_config(mm_stream_t *my_obj,
                         void *in_value)
{
    int32_t rc = 0;
    mm_evt_paylod_stream_parm_t *payload = (mm_evt_paylod_stream_parm_t *)in_value;
    mm_camera_stream_parm_t parm_type = payload->parm_type;
    void *value = payload->value;

    CDBG("%s: parm_type = %d",__func__,(int)parm_type);
    switch(parm_type) {
        default:
            CDBG_ERROR("%s : Parm -%d set is not supported here",__func__,(int)parm_type);
            break;
    }
    return rc;
}
int32_t mm_stream_get_parm_config(mm_stream_t *my_obj,
                         void *out_value)
{
    int32_t rc = 0;
    mm_evt_paylod_stream_parm_t *payload = (mm_evt_paylod_stream_parm_t *)out_value;

    if(payload == NULL) {
        CDBG_ERROR("%s : Invalid Argument",__func__);
        return -1;
    }
    CDBG("%s: parm_type = %d",__func__,(int)payload->parm_type);
    switch(payload->parm_type) {
        case MM_CAMERA_STREAM_OFFSET:
            memcpy(payload->value,(void *)&my_obj->frame_offset,sizeof(mm_camera_frame_len_offset));
            break;
        case MM_CAMERA_STREAM_CROP:{
            mm_camera_rect_t *crop = (mm_camera_rect_t *)payload->value;
            rc = mm_stream_get_crop(my_obj, crop);
            break;
        }
        default:
            CDBG_ERROR("%s : Parm -%d get is not supported here",__func__,(int)payload->parm_type);
            break;
    }
    return rc;
}

int32_t mm_stream_set_parm_start(mm_stream_t *my_obj,
                         void *in_value)
{
    int32_t rc = 0;
    mm_evt_paylod_stream_parm_t *payload = (mm_evt_paylod_stream_parm_t *)in_value;
    mm_camera_stream_parm_t parm_type = payload->parm_type;
    void *value = payload->value;

    CDBG("%s: parm_type = %d",__func__,(int)parm_type);
    switch(parm_type) {
        default:
            CDBG_ERROR("%s : Parm -%d set is not supported here",__func__,(int)parm_type);
            break;
    }
    return rc;
}
int32_t mm_stream_get_parm_start(mm_stream_t *my_obj,
                         void *out_value)
{
    int32_t rc = 0;
    mm_evt_paylod_stream_parm_t *payload = (mm_evt_paylod_stream_parm_t *)out_value;

    if(payload == NULL) {
        CDBG_ERROR("%s : Invalid Argument",__func__);
        return -1;
    }
    CDBG("%s: parm_type = %d",__func__,(int)payload->parm_type);
    switch(payload->parm_type) {
        case MM_CAMERA_STREAM_OFFSET:
            memcpy(payload->value,(void *)&my_obj->frame_offset,sizeof(mm_camera_frame_len_offset));
            break;
        case MM_CAMERA_STREAM_CROP:{
            mm_camera_rect_t *crop = (mm_camera_rect_t *)payload->value;
            rc = mm_stream_get_crop(my_obj, crop);
            break;
        }
        default:
            CDBG_ERROR("%s : Parm -%d get is not supported here",__func__,(int)payload->parm_type);
            break;
    }
    return rc;
}
int32_t mm_stream_set_ext_mode(mm_stream_t * my_obj)
{
    int32_t rc = 0;
    struct v4l2_streamparm s_parm;

    s_parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    s_parm.parm.capture.extendedmode = my_obj->ext_image_mode;

    rc = ioctl(my_obj->fd, VIDIOC_S_PARM, &s_parm);
        CDBG("%s:stream fd=%d, rc=%d, extended_mode=%d\n",
                 __func__, my_obj->fd, rc,
                 s_parm.parm.capture.extendedmode);
    return rc;
}

int32_t mm_stream_qbuf(mm_stream_t *my_obj, mm_camera_buf_def_t *buf)
{
    int32_t i, rc = 0;
    int *ret;
    struct v4l2_buffer buffer;

    memset(&buffer, 0, sizeof(buffer));
    buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    buffer.memory = V4L2_MEMORY_USERPTR;
    buffer.index = buf->buf_idx;
    buffer.m.planes = &buf->planes[0];
    buffer.length = buf->num_planes;

    CDBG("%s:stream_hdl=%d,fd=%d,frame idx=%d,num_planes = %d\n", __func__,
         buf->stream_id, buf->fd, buffer.index, buffer.length);

    rc = ioctl(my_obj->fd, VIDIOC_QBUF, &buffer);
    CDBG("%s: qbuf idx:%d, rc:%d", __func__, buffer.index, rc);
    return rc;
}

/* This function let kernel know amount of buffers will be registered */
int32_t mm_stream_request_buf(mm_stream_t * my_obj)
{
    int32_t rc = 0;
    uint8_t i,reg = 0;
    struct v4l2_requestbuffers bufreq;
    uint8_t buf_num = my_obj->buf_num;

    if(buf_num > MM_CAMERA_MAX_NUM_FRAMES) {
        CDBG_ERROR("%s: buf num %d > max limit %d\n",
                 __func__, buf_num, MM_CAMERA_MAX_NUM_FRAMES);
        return -1;
    }
    pthread_mutex_lock(&my_obj->buf_lock);
    for(i = 0; i < buf_num; i++){
        if (my_obj->buf_status[i].initial_reg_flag){
            reg = 1;
            break;
        }
    }
    pthread_mutex_unlock(&my_obj->buf_lock);
    if(!reg) {
        //No need to register a buffer
        CDBG_ERROR("No Need to register this buffer");
        return rc;
    }
    memset(&bufreq, 0, sizeof(bufreq));
    bufreq.count = buf_num;
    bufreq.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    bufreq.memory = V4L2_MEMORY_USERPTR;
    rc = ioctl(my_obj->fd, VIDIOC_REQBUFS, &bufreq);
    if (rc < 0) {
      CDBG_ERROR("%s: fd=%d, ioctl VIDIOC_REQBUFS failed: rc=%d\n",
           __func__, my_obj->fd, rc);
    }
    CDBG("%s :X rc = %d",__func__,rc);
    return rc;
}

int32_t mm_stream_get_frame_len_offset(mm_camera_image_fmt_t* img_fmt,
                                       camera_mode_t mode,
                                       int image_type,
                                       mm_camera_frame_len_offset * frame_offset)
{
    /* TODO : this function should query the frame len from backend using cmd */
    /* for now, it's still use hardcoded value */
    uint32_t width, height;
    int local_height;

    width = img_fmt->width;
    height = img_fmt->height;

    switch (img_fmt->fmt) {
    case CAMERA_YUV_420_NV12:
    case CAMERA_YUV_420_NV21:
        frame_offset->num_planes = 2;
        if (image_type == OUTPUT_TYPE_V) {
            frame_offset->mp[0].len = PAD_TO_2K(width * height);
            frame_offset->mp[1].len = PAD_TO_2K(width * height/2);
            /* TODO: offset to count in meta header*/
            frame_offset->mp[0].offset = 0;
            frame_offset->mp[1].offset = 0;
        } else if (image_type == OUTPUT_TYPE_P) {
            frame_offset->mp[0].len = PAD_TO_WORD(width * height);
            frame_offset->mp[1].len = PAD_TO_WORD(width * height/2);
            /* TODO: offset to count in meta header*/
            frame_offset->mp[0].offset = 0;
            frame_offset->mp[1].offset = 0;
        } else {
            frame_offset->mp[0].len = PAD_TO_WORD(width * CEILING16(height));
            frame_offset->mp[1].len = PAD_TO_WORD(width * CEILING16(height)/2);
            /* TODO: offset to count in meta header*/
            frame_offset->mp[0].offset = 0;
            frame_offset->mp[1].offset = 0;
        }
        frame_offset->frame_len =
            PAD_TO_4K(frame_offset->mp[0].len + frame_offset->mp[1].len);
        break;
    case CAMERA_BAYER_SBGGR10:
        frame_offset->num_planes = 1;
        frame_offset->mp[0].len = PAD_TO_WORD(width * height);
        frame_offset->frame_len = frame_offset->mp[0].len;
        break;
    case CAMERA_YUV_422_NV16:
    case CAMERA_YUV_422_NV61:
        if( image_type == OUTPUT_TYPE_S || image_type == OUTPUT_TYPE_V) {
            local_height = CEILING16(height);
        } else {
            local_height = height;
        }
        frame_offset->num_planes = 2;
        frame_offset->mp[0].len = PAD_TO_WORD(width * height);
        frame_offset->mp[1].len = PAD_TO_WORD(width * height);
        /* TODO: offset to count in meta header*/
        frame_offset->mp[0].offset = 0;
        frame_offset->mp[1].offset = 0;
        frame_offset->frame_len =
            frame_offset->mp[0].len + frame_offset->mp[1].len;
        break;
    default:
        CDBG("%s: format %d not supported.\n",
            __func__, img_fmt->fmt);
        frame_offset->frame_len = 0;
    }
    CDBG("%s:fmt=%d,image_type=%d,width=%d,height=%d,frame_len=%d\n",
        __func__, img_fmt->fmt, image_type, width, height, frame_offset->frame_len);
    return 0;
}

int32_t mm_stream_init_bufs(mm_stream_t * my_obj)
{
    int32_t i, rc = 0, j;
    int image_type;
    mm_camear_mem_vtbl_t *mem_vtbl = NULL;
    mm_camera_frame_len_offset frame_offset;
    uint8_t *reg_flags = NULL;

    /* deinit buf if it's not NULL*/
    if (NULL != my_obj->buf) {
        mm_stream_deinit_bufs(my_obj);
    }

    my_obj->buf_num = my_obj->hal_requested_num_bufs;
    /* if stream needs do pp, allocate extra one buf for pp*/
    if (my_obj->is_pp_needed) {
        my_obj->buf_num++;
    }

    my_obj->buf =
        (mm_camera_buf_def_t*)malloc(sizeof(mm_camera_buf_def_t) * my_obj->buf_num);
    my_obj->buf_status =
        (mm_stream_buf_status_t*)malloc(sizeof(mm_stream_buf_status_t) * my_obj->buf_num);
    reg_flags = (uint8_t *)malloc(sizeof(uint8_t) * my_obj->buf_num);

    if (NULL == my_obj->buf ||
        NULL == my_obj->buf_status ||
        NULL == reg_flags) {
        CDBG_ERROR("%s: No memory for buf", __func__);
        rc = -1;
        goto error_malloc;
    }

    memset(my_obj->buf, 0, sizeof(mm_camera_buf_def_t) * my_obj->buf_num);
    memset(my_obj->buf_status, 0, sizeof(mm_stream_buf_status_t) * my_obj->buf_num);
    memset(reg_flags, 0, sizeof(uint8_t) * my_obj->buf_num);

    mem_vtbl = my_obj->ch_obj->cam_obj->mem_vtbl;
    rc = mem_vtbl->get_buf(my_obj->ch_obj->cam_obj->my_hdl,
                           my_obj->ch_obj->my_hdl,
                           my_obj->my_hdl,
                           mem_vtbl->user_data,
                           &my_obj->frame_offset,
                           my_obj->buf_num,
                           reg_flags,
                           my_obj->buf);

    if (0 != rc) {
        CDBG_ERROR("%s: Error get buf, rc = %d\n", __func__, rc);
        goto error_malloc;
    }

    for (i=0; i < my_obj->buf_num; i++) {
        my_obj->buf_status[i].initial_reg_flag = reg_flags[i];
        my_obj->buf[i].stream_id = my_obj->my_hdl;
    }

    free(reg_flags);
    reg_flags = NULL;

    for (i=0; i < my_obj->buf_num; i++) {
        if (my_obj->buf[i].fd > 0) {
            if(0 >= (rc = mm_camera_map_buf(my_obj->ch_obj->cam_obj,
                          my_obj->ext_image_mode,
                          i,
                          my_obj->buf[i].fd,
                          my_obj->buf[i].frame_len)))
            {
                CDBG_ERROR("%s: Error mapping buf (rc = %d)", __func__, rc);
                goto error_map;
            }
        } else {
            CDBG_ERROR("%s: Invalid fd for buf idx (%d)", __func__, i);
        }
    }

    return 0;

error_map:
    /* error, unmapping previously mapped bufs */
    for (j=0; j<i; j++) {
        if (my_obj->buf[j].fd > 0) {
            mm_camera_unmap_buf(my_obj->ch_obj->cam_obj,
                                my_obj->ext_image_mode,
                                j);
        }
    }

    /* put buf back */
    mem_vtbl->put_buf(my_obj->ch_obj->cam_obj->my_hdl,
                      my_obj->ch_obj->my_hdl,
                      my_obj->my_hdl,
                      mem_vtbl->user_data,
                      my_obj->buf_num,
                      my_obj->buf);

error_malloc:
    if (NULL != my_obj->buf) {
        free(my_obj->buf);
        my_obj->buf = NULL;
    }
    if (NULL != my_obj->buf_status) {
        free(my_obj->buf_status);
        my_obj->buf_status = NULL;
    }
    if (NULL != reg_flags) {
        free(reg_flags);
        reg_flags = NULL;
    }

    return rc;
}

/* return buffers to surface or release buffers allocated */
int32_t mm_stream_deinit_bufs(mm_stream_t * my_obj)
{
    int32_t rc = 0, i;
    mm_camear_mem_vtbl_t *mem_vtbl = NULL;

    if (NULL == my_obj->buf) {
        CDBG("%s: Buf is NULL, no need to deinit", __func__);
        return rc;
    }

    /* IOMMU unmapping */
    for (i=0; i<my_obj->buf_num; i++) {
        if (my_obj->buf[i].fd > 0) {
            rc = mm_camera_unmap_buf(my_obj->ch_obj->cam_obj,
                                     my_obj->ext_image_mode,
                                     i);
            if (rc < 0 ) {
                CDBG_ERROR("%s: Error unmapping bufs at idx(%d) rc=%d",
                           __func__, i, rc);
            }
        }
    }

    /* release bufs */
    mem_vtbl = my_obj->ch_obj->cam_obj->mem_vtbl;
    if (NULL != mem_vtbl) {
        rc = mem_vtbl->put_buf(my_obj->ch_obj->cam_obj->my_hdl,
                          my_obj->ch_obj->my_hdl,
                          my_obj->my_hdl,
                          mem_vtbl->user_data,
                          my_obj->buf_num,
                          my_obj->buf);
    } else {
        CDBG_ERROR("%s: mem table is NULL, cannot release buf", __func__);
        rc = -1;
    }

    free(my_obj->buf);
    my_obj->buf = NULL;
    free(my_obj->buf_status);
    my_obj->buf_status = NULL;

    return rc;
}

int32_t mm_stream_reg_buf(mm_stream_t * my_obj)
{
    int32_t rc = 0;
    uint8_t i;

    rc = mm_stream_request_buf(my_obj);
    if (rc != 0) {
        return rc;
    }

    pthread_mutex_lock(&my_obj->buf_lock);
    for(i = 0; i < my_obj->buf_num; i++){
        my_obj->buf[i].buf_idx = i;

        /* check if need to qbuf initially */
        if (my_obj->buf_status[i].initial_reg_flag) {
            rc = mm_stream_qbuf(my_obj, &my_obj->buf[i]);
            if (rc != 0) {
                CDBG_ERROR("%s: VIDIOC_QBUF rc = %d\n", __func__, rc);
                return rc;
            }
        }

        my_obj->buf_status[i].buf_refcnt = 0;
        my_obj->buf_status[i].in_kernel = 1;
    }
    pthread_mutex_unlock(&my_obj->buf_lock);
    return rc;
}

int32_t mm_stream_unreg_buf(mm_stream_t * my_obj)
{
    struct v4l2_requestbuffers bufreq;
    int32_t i, rc = 0,reg = 0;

    pthread_mutex_lock(&my_obj->buf_lock);
    if (NULL != my_obj->buf_status) {
        for(i = 0; i < my_obj->buf_num; i++){
            if (my_obj->buf_status[i].initial_reg_flag){
                reg = 1;
                break;
            }
        }
    }
    pthread_mutex_unlock(&my_obj->buf_lock);
    if(!reg) {
        //No need to unregister a buffer
        goto end;
    }
    bufreq.count = 0;
    bufreq.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    bufreq.memory = V4L2_MEMORY_USERPTR;
    rc = ioctl(my_obj->fd, VIDIOC_REQBUFS, &bufreq);
    if (rc < 0) {
        CDBG_ERROR("%s: fd=%d, VIDIOC_REQBUFS failed, rc=%d\n",
              __func__, my_obj->fd, rc);
    }

end:
    /* reset buf reference count */
    pthread_mutex_lock(&my_obj->buf_lock);
    if (NULL != my_obj->buf_status) {
        for(i = 0; i < my_obj->buf_num; i++){
            my_obj->buf_status[i].buf_refcnt = 0;
            my_obj->buf_status[i].in_kernel = 0;
        }
    }
    pthread_mutex_unlock(&my_obj->buf_lock);

    return rc;
}

int32_t mm_stream_set_fmt(mm_stream_t *my_obj)
{
    int32_t rc = 0;
    struct v4l2_format fmt;

    if(my_obj->fmt.width == 0 || my_obj->fmt.height == 0) {
        CDBG_ERROR("%s:invalid input[w=%d,h=%d,fmt=%d]\n",
                 __func__, my_obj->fmt.width, my_obj->fmt.height, my_obj->fmt.fmt);
        return -1;
    }

    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    fmt.fmt.pix_mp.width = my_obj->fmt.width;
    fmt.fmt.pix_mp.height= my_obj->fmt.height;
    fmt.fmt.pix_mp.field = V4L2_FIELD_NONE;
    fmt.fmt.pix_mp.pixelformat =
            mm_stream_util_get_v4l2_fmt(my_obj->fmt.fmt,
                                      &(fmt.fmt.pix_mp.num_planes));
    rc = ioctl(my_obj->fd, VIDIOC_S_FMT, &fmt);
    CDBG("%s:fd=%d, ext_image_mode=%d, rc=%d\n",
             __func__, my_obj->fd, my_obj->ext_image_mode, rc);

    return rc;
}

int32_t mm_stream_get_offset(mm_stream_t *my_obj)
{
    int32_t rc = 0;
    cam_frame_resolution_t frame_offset;

    memset(&my_obj->frame_offset,0,sizeof(mm_camera_frame_len_offset));

    frame_offset.format = my_obj->fmt.fmt;
    frame_offset.image_mode = my_obj->ext_image_mode;
    frame_offset.rotation = my_obj->fmt.rotation;
    frame_offset.width = my_obj->fmt.width;
    frame_offset.height = my_obj->fmt.height;

    switch (my_obj->ext_image_mode) {
    case MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW:
    case MSM_V4L2_EXT_CAPTURE_MODE_MAIN:
    case MSM_V4L2_EXT_CAPTURE_MODE_RAW:
    case MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL:
    case MSM_V4L2_EXT_CAPTURE_MODE_RDI:
        frame_offset.padding_format = CAMERA_PAD_TO_WORD;
        break;
    case MSM_V4L2_EXT_CAPTURE_MODE_VIDEO:
    default:
        frame_offset.padding_format = CAMERA_PAD_TO_2K;
        break;
    }

    CDBG("%s: format = %d, image_mode = %d, padding_format = %d, rotation = %d,"
            "width = %d height = %d",
             __func__,frame_offset.format,frame_offset.image_mode,frame_offset.padding_format,
             frame_offset.rotation,frame_offset.width,frame_offset.height);

    rc = mm_camera_send_native_ctrl_cmd(my_obj->ch_obj->cam_obj,
                                            CAMERA_GET_PARM_FRAME_RESOLUTION,
                                            sizeof(cam_frame_resolution_t),
                                            &frame_offset);
    if(rc != 0) {
        CDBG_ERROR("%s: Failed to get the stream offset and frame length",__func__);
        return rc;
    }
    my_obj->fmt.width = frame_offset.width;
    my_obj->fmt.height = frame_offset.height;
    memcpy(&my_obj->frame_offset,&frame_offset.frame_offset,sizeof(mm_camera_frame_len_offset));
    CDBG("%s: Frame length = %d width = %d, height = %d, rc = %d",
         __func__,my_obj->frame_offset.frame_len,my_obj->fmt.width,my_obj->fmt.height,rc);
    return rc;
}


int32_t mm_stream_set_cid(mm_stream_t *my_obj,stream_cid_t *value)
{
    int32_t rc = 0;
    cam_cid_info_t cam_cid_info;

    cam_cid_info.num_cids = 1;
    cam_cid_info.cid_entries[0].cid = value->cid;
    cam_cid_info.cid_entries[0].dt = value->dt;
    cam_cid_info.cid_entries[0].inst_handle = my_obj->inst_hdl;

    rc = mm_camera_send_native_ctrl_cmd(my_obj->ch_obj->cam_obj,
                                            CAMERA_SET_PARM_CID,
                                            sizeof(cam_cid_info_t),
                                            &cam_cid_info);
    if(rc != 0) {
        CDBG_ERROR("%s: Failed to set the CID",__func__);
        return rc;
    }
    return rc;
}

int32_t mm_stream_get_cid(mm_stream_t *my_obj,stream_cid_t *out_value)
{
    //TODO: Need to use sensor structure init in camera query
    int32_t rc = 0;
    return rc;
}

int32_t mm_stream_buf_done(mm_stream_t * my_obj,
                           mm_camera_buf_def_t *frame)
{
    int32_t rc = 0;

    if (my_obj->is_local_buf) {
        /* special case for video-sized live snapshot
         * buf is local, no need to qbuf to kernel */
        return 0;
    }

    pthread_mutex_lock(&my_obj->buf_lock);

    if(my_obj->buf_status[frame->buf_idx].buf_refcnt == 0) {
        CDBG("%s: Error Trying to free second time?(idx=%d) count=%d, ext_image_mode=%d\n",
                   __func__, frame->buf_idx,
                   my_obj->buf_status[frame->buf_idx].buf_refcnt,
                   my_obj->ext_image_mode);
        rc = -1;
    }else{
        my_obj->buf_status[frame->buf_idx].buf_refcnt--;
        if (0 == my_obj->buf_status[frame->buf_idx].buf_refcnt) {
            CDBG("<DEBUG> : Buf done for buffer:%d:%d", my_obj->ext_image_mode, frame->buf_idx);
            rc = mm_stream_qbuf(my_obj, frame);
            if(rc < 0) {
                CDBG_ERROR("%s: mm_camera_stream_qbuf(idx=%d) err=%d\n",
                     __func__, frame->buf_idx, rc);
            } else {
                my_obj->buf_status[frame->buf_idx].in_kernel = 1;
            }
        }else{
            CDBG("<DEBUG> : Still ref count pending count :%d",
                 my_obj->buf_status[frame->buf_idx].buf_refcnt);
            CDBG("<DEBUG> : for buffer:%p:%d, ext_image_mode=%d",
                 my_obj, frame->buf_idx, my_obj->ext_image_mode);
        }
    }
    pthread_mutex_unlock(&my_obj->buf_lock);
    return rc;
}

int32_t mm_stream_reg_buf_cb(mm_stream_t *my_obj,
                             mm_stream_data_cb_t *val)
{
    int32_t rc = -1;
    uint8_t i;

    pthread_mutex_lock(&my_obj->cb_lock);
    for (i=0 ;i < MM_CAMERA_STREAM_BUF_CB_MAX; i++) {
        if(NULL == my_obj->buf_cb[i].cb) {
            memcpy(&my_obj->buf_cb[i], val, sizeof(mm_stream_data_cb_t));
            rc = 0;
            break;
        }
    }
    pthread_mutex_unlock(&my_obj->cb_lock);

    return rc;
}
