/* SPDX-License-Identifier: Apache-2.0 OR MIT */
/*
 * Copyright (c) 2015 Rockchip Electronics Co., Ltd.
 */

#define  MODULE_TAG "mpp_buf_slot"

#include <string.h>

#include "mpp_mem.h"
#include "mpp_env.h"
#include "mpp_list.h"
#include "mpp_debug.h"
#include "mpp_common.h"

#include "mpp_frame_impl.h"
#include "mpp_buf_slot.h"
#include "mpp_compat_impl.h"
#include "rk_mpp_cfg.h"

#define BUF_SLOT_DBG_FUNCTION           (0x00000001)
#define BUF_SLOT_DBG_SETUP              (0x00000002)
#define BUF_SLOT_DBG_INFO_CHANGE        (0x00000004)
#define BUF_SLOT_DBG_OPS_RUNTIME        (0x00000010)
#define BUF_SLOT_DBG_BUFFER             (0x00000100)
#define BUF_SLOT_DBG_FRAME              (0x00000200)
#define BUF_SLOT_DBG_BUF_UESD           (0x00000400)
#define BUF_SLOT_DBG_INFO_SET           (0x00000800)
#define BUF_SLOT_DBG_OPS_HISTORY        (0x10000000)
#define BUF_SLOT_DBG_ALL                (0x10000011)

#define buf_slot_dbg(flag, fmt, ...)    _mpp_dbg(buf_slot_debug, flag, fmt, ## __VA_ARGS__)

static RK_U32 buf_slot_debug = 0;
static RK_U32 buf_slot_idx = 0;
static RK_U32 use_legacy_align = 0;

#define slot_assert(impl, cond) do {                                    \
    if (!(cond)) {                                                      \
        dump_slots(impl);                                               \
        mpp_err("Assertion %s failed at %s:%d\n",                       \
               MPP_STRINGS(cond), __FUNCTION__, __LINE__);              \
        abort();                                                        \
    }                                                                   \
} while (0)

typedef struct MppBufSlotEntry_t MppBufSlotEntry;
typedef struct MppBufSlotsImpl_t MppBufSlotsImpl;

#define SLOT_OPS_MAX_COUNT              1024

typedef enum MppBufSlotOps_e {
    // status opertaion
    SLOT_INIT,
    SLOT_SET_ON_USE,
    SLOT_CLR_ON_USE,
    SLOT_SET_NOT_READY,
    SLOT_CLR_NOT_READY,
    SLOT_SET_CODEC_READY,
    SLOT_CLR_CODEC_READY,
    SLOT_SET_CODEC_USE,
    SLOT_CLR_CODEC_USE,
    SLOT_SET_HAL_INPUT,
    SLOT_CLR_HAL_INPUT,
    SLOT_SET_HAL_OUTPUT,
    SLOT_CLR_HAL_OUTPUT,
    SLOT_SET_QUEUE_USE,
    SLOT_CLR_QUEUE_USE,

    // queue operation
    SLOT_ENQUEUE,
    SLOT_ENQUEUE_OUTPUT = SLOT_ENQUEUE,
    SLOT_ENQUEUE_DISPLAY,
    SLOT_ENQUEUE_DEINTER,
    SLOT_ENQUEUE_CONVERT,
    SLOT_DEQUEUE,
    SLOT_DEQUEUE_OUTPUT = SLOT_DEQUEUE,
    SLOT_DEQUEUE_DISPLAY,
    SLOT_DEQUEUE_DEINTER,
    SLOT_DEQUEUE_CONVERT,

    // value operation
    SLOT_SET_EOS,
    SLOT_CLR_EOS,
    SLOT_SET_FRAME,
    SLOT_CLR_FRAME,
    SLOT_SET_BUFFER,
    SLOT_CLR_BUFFER,
} MppBufSlotOps;

static const char op_string[][16] = {
    "init           ",
    "set on use     ",
    "clr on use     ",
    "set not ready  ",
    "set ready      ",
    "set codec ready",
    "clr codec ready",
    "set codec use  ",
    "clr codec use  ",
    "set hal input  ",
    "clr hal input  ",
    "set hal output ",
    "clr hal output ",
    "set queue use  ",
    "clr queue use  ",

    "enqueue output ",
    "enqueue display",
    "enqueue deint  ",
    "enqueue convert",
    "dequeue output ",
    "dequeue display",
    "dequeue deint  ",
    "dequeue convert",

    "set eos        ",
    "clr eos        ",
    "set frame      ",
    "clr frame      ",
    "set buffer     ",
    "clr buffer     ",
};

static const MppBufSlotOps set_flag_op[SLOT_USAGE_BUTT] = {
    SLOT_SET_CODEC_READY,
    SLOT_SET_CODEC_USE,
    SLOT_SET_HAL_INPUT,
    SLOT_SET_HAL_OUTPUT,
    SLOT_SET_QUEUE_USE,
};

static const MppBufSlotOps clr_flag_op[SLOT_USAGE_BUTT] = {
    SLOT_CLR_CODEC_READY,
    SLOT_CLR_CODEC_USE,
    SLOT_CLR_HAL_INPUT,
    SLOT_CLR_HAL_OUTPUT,
    SLOT_CLR_QUEUE_USE,
};

static const MppBufSlotOps set_val_op[SLOT_PROP_BUTT] = {
    SLOT_SET_EOS,
    SLOT_SET_FRAME,
    SLOT_SET_BUFFER,
};

typedef union SlotStatus_u {
    RK_U32 val;
    struct {
        // status flags
        RK_U32  on_used     : 1;
        RK_U32  not_ready   : 1;        // buffer slot is filled or not
        RK_U32  codec_use   : 1;        // buffer slot is used by codec ( dpb reference )
        RK_U32  hal_output  : 2;        // buffer slot is set to hw output will ready when hw done
        RK_U32  hal_use     : 8;        // buffer slot is used by hardware
        RK_U32  queue_use   : 5;        // buffer slot is used in different queue

        // value flags
        RK_U32  eos         : 1;        // buffer slot is last buffer slot from codec
        RK_U32  has_buffer  : 1;
        RK_U32  has_frame   : 1;
    };
} SlotStatus;

typedef struct MppBufSlotLog_t {
    RK_S32              index;
    MppBufSlotOps       ops;
    SlotStatus          status_in;
    SlotStatus          status_out;
} MppBufSlotLog;

typedef struct MppBufSlotLogs_t {
    pthread_mutex_t     lock;
    RK_U16              max_count;
    RK_U16              log_count;
    RK_U16              log_write;
    RK_U16              log_read;
    MppBufSlotLog       *logs;
} MppBufSlotLogs;

struct MppBufSlotEntry_t {
    MppBufSlotsImpl     *slots;
    struct list_head    list;
    SlotStatus          status;
    RK_S32              index;

    RK_U32              eos;
    MppFrame            frame;
    MppBuffer           buffer;
};

struct MppBufSlotsImpl_t {
    MppMutex            lock;
    RK_U32              slots_idx;

    // status tracing
    RK_U32              decode_count;
    RK_U32              display_count;

    MppCodingType       coding_type;

    // if slot changed, all will be hold until all slot is unused
    RK_U32              info_changed;
    RK_S32              info_change_slot_idx;
    RK_S32              new_count;

    // slot infomation for info change and eos
    RK_U32              eos;

    // buffer parameter, default alignement is 16
    MppSysCfg           sys_cfg;
    AlignFunc           hal_hor_align;          // default NULL
    AlignFunc           hal_ver_align;          // default NULL
    AlignFunc           hal_len_align;          // default NULL
    AlignFunc           hal_width_align;        // default NULL
    SlotHalFbcAdjCfg    hal_fbc_adj_cfg;        // hal fbc frame adjust config
    size_t              buf_size;
    RK_S32              buf_count;
    RK_S32              used_count;
    RK_U32              align_chk_log_env;
    RK_U32              align_chk_log_en;
    // buffer size equal to (h_stride * v_stride) * numerator / denominator
    // internal parameter
    RK_U32              numerator;
    RK_U32              denominator;

    // callback for free slot notify
    MppCbCtx            callback;

    // NOTE: use MppFrame to store the buffer/display infomation
    //       any buffer related infomation change comparing to previous frame will
    //       trigger a buffer info changed requirement
    //       any display related infomation change comparing to pevious frame will
    //       trigger a display info changed requirement
    MppFrame            info;
    MppFrame            info_set;

    // list for display
    struct list_head    queue[QUEUE_BUTT];

    // list for log
    MppBufSlotLogs      *logs;

    MppBufSlotEntry     *slots;
};

typedef struct MppBufSlotInfoSet_t {
    RK_U32 h_stride_by_pixel;
    RK_U32 h_stride_by_byte;
    RK_U32 v_stride;
    RK_U32 size_total;
} MppBufSlotInfoSet;

static RK_U32 default_align_16(RK_U32 val)
{
    return MPP_ALIGN(val, 16);
}

/* Based on drm_gem_framebuffer_helper.c drm_gem_afbc_min_size() */
static RK_S32 get_afbc_min_size(RK_S32 width, RK_S32 height, RK_S32 bpp)
{
#define AFBC_HEADER_SIZE 16
#define AFBC_HDR_ALIGN 64
#define AFBC_SUPERBLOCK_PIXELS 256
#define AFBC_SUPERBLOCK_ALIGNMENT 128

    RK_S32 n_blocks, hdr_alignment, size;

    /* AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 and !AFBC_FORMAT_MOD_TILED */
    width = MPP_ALIGN(width, 16);
    height = MPP_ALIGN(height, 16);
    hdr_alignment = AFBC_HDR_ALIGN;

    n_blocks = (width * height) / AFBC_SUPERBLOCK_PIXELS;

    size = MPP_ALIGN(n_blocks * AFBC_HEADER_SIZE, hdr_alignment);
    size += n_blocks * MPP_ALIGN(bpp * AFBC_SUPERBLOCK_PIXELS / 8,
                                 AFBC_SUPERBLOCK_ALIGNMENT);
    return size;
}

static void prepare_info_set_legacy(MppBufSlotsImpl *impl, MppFrame frame,
                                    MppBufSlotInfoSet *info_set,
                                    RK_U32 force_def_align)
{
    const RK_U32 width  = mpp_frame_get_width(frame);
    const RK_U32 height = mpp_frame_get_height(frame);
    const MppFrameFormat fmt = mpp_frame_get_fmt(frame);
    RK_U32 depth = ((fmt & MPP_FRAME_FMT_MASK) == MPP_FMT_YUV420SP_10BIT ||
                    (fmt & MPP_FRAME_FMT_MASK) == MPP_FMT_YUV422SP_10BIT ||
                    (fmt & MPP_FRAME_FMT_MASK) == MPP_FMT_YUV444SP_10BIT) ? 10 : 8;
    RK_U32 codec_hor_stride = mpp_frame_get_hor_stride(frame);
    RK_U32 codec_ver_stride = mpp_frame_get_ver_stride(frame);
    RK_U32 coded_width = (impl->hal_width_align) ?
                         (impl->hal_width_align(width)) : width;

    RK_U32 hal_hor_stride = (codec_hor_stride) ?
                            (impl->hal_hor_align(codec_hor_stride)) :
                            (impl->hal_hor_align(coded_width * depth >> 3));
    RK_U32 hal_ver_stride = (codec_ver_stride) ?
                            (impl->hal_ver_align(codec_ver_stride)) :
                            (impl->hal_ver_align(height));
    RK_U32 hor_stride_pixel;
    RK_S32 size;

    hal_hor_stride = (force_def_align && codec_hor_stride) ? codec_hor_stride : hal_hor_stride;
    hal_ver_stride = (force_def_align && codec_ver_stride) ? codec_ver_stride : hal_ver_stride;

    if (MPP_FRAME_FMT_IS_FBC(fmt)) {
        /*fbc stride default 64 align*/
        if (*compat_ext_fbc_hdr_256_odd)
            hal_hor_stride = (MPP_ALIGN(width, 256) | 256) * depth >> 3;
        else
            hal_hor_stride = MPP_ALIGN(width, 64) * depth >> 3;
    }

    switch (fmt & MPP_FRAME_FMT_MASK) {
    case MPP_FMT_YUV420SP_10BIT:
    case MPP_FMT_YUV422SP_10BIT:
    case MPP_FMT_YUV444SP_10BIT: {
        hor_stride_pixel = hal_hor_stride * 8 / 10;
    } break;
    case MPP_FMT_YUV422_YVYU:
    case MPP_FMT_YUV422_YUYV:
    case MPP_FMT_RGB565:
    case MPP_FMT_BGR565: {
        hor_stride_pixel = hal_hor_stride / 2;
    } break;
    case MPP_FMT_RGB888:
    case MPP_FMT_BGR888: {
        hor_stride_pixel = hal_hor_stride / 3;
    } break;
    default : {
        hor_stride_pixel = hal_hor_stride;
    } break;
    }

    size = hal_hor_stride * hal_ver_stride;

    if (MPP_FRAME_FMT_IS_FBC(fmt)) {
        hor_stride_pixel = MPP_ALIGN(hor_stride_pixel, 64);
        switch ((fmt & MPP_FRAME_FMT_MASK)) {
        case MPP_FMT_YUV420SP_10BIT : {
            size = get_afbc_min_size(hor_stride_pixel, hal_ver_stride, 15);
        } break;
        case MPP_FMT_YUV422SP_10BIT : {
            size = get_afbc_min_size(hor_stride_pixel, hal_ver_stride, 20);
        } break;
        case MPP_FMT_YUV420SP : {
            size = get_afbc_min_size(hor_stride_pixel, hal_ver_stride, 12);
        } break;
        case MPP_FMT_YUV422SP : {
            size = get_afbc_min_size(hor_stride_pixel, hal_ver_stride, 16);
        } break;
        case MPP_FMT_YUV444SP : {
            size = get_afbc_min_size(hor_stride_pixel, hal_ver_stride, 24);
        } break;
        case MPP_FMT_YUV444SP_10BIT : {
            size = get_afbc_min_size(hor_stride_pixel, hal_ver_stride, 30);
        } break;
        default : {
            size = hal_hor_stride * hal_ver_stride * 3 / 2;
            mpp_err("dec out fmt is no support");
        } break;
        }
        mpp_frame_set_fbc_size(frame, size);
    } else {
        size *= impl->numerator;
        size /= impl->denominator;
        size = impl->hal_len_align ? (RK_S32)impl->hal_len_align(hal_hor_stride * hal_ver_stride) : size;
    }

    info_set->h_stride_by_byte = hal_hor_stride;
    info_set->v_stride = hal_ver_stride;
    info_set->h_stride_by_pixel = hor_stride_pixel;
    info_set->size_total = size;
}

static void prepare_info_set_by_sys_cfg(MppBufSlotsImpl *impl, MppFrame frame,
                                        MppBufSlotInfoSet *info_set)
{
    const RK_U32 width  = mpp_frame_get_width(frame);
    const RK_U32 height = mpp_frame_get_height(frame);
    const RK_U32 codec_hor_stride = mpp_frame_get_hor_stride(frame);
    const RK_U32 codec_ver_stride = mpp_frame_get_ver_stride(frame);
    const MppFrameFormat fmt = mpp_frame_get_fmt(frame);

    /* set correct parameter */
    mpp_sys_cfg_set_u32(impl->sys_cfg, "dec_buf_chk:enable", 1);
    mpp_sys_cfg_set_u32(impl->sys_cfg, "dec_buf_chk:type", impl->coding_type);
    mpp_sys_cfg_set_u32(impl->sys_cfg, "dec_buf_chk:fmt_codec", fmt & MPP_FRAME_FMT_MASK);
    mpp_sys_cfg_set_u32(impl->sys_cfg, "dec_buf_chk:fmt_fbc", fmt & MPP_FRAME_FBC_MASK);
    mpp_sys_cfg_set_u32(impl->sys_cfg, "dec_buf_chk:fmt_hdr", fmt & MPP_FRAME_HDR_MASK);
    mpp_sys_cfg_set_u32(impl->sys_cfg, "dec_buf_chk:width", width);
    mpp_sys_cfg_set_u32(impl->sys_cfg, "dec_buf_chk:height", height);
    mpp_sys_cfg_set_u32(impl->sys_cfg, "dec_buf_chk:h_stride_by_byte", codec_hor_stride);
    mpp_sys_cfg_set_u32(impl->sys_cfg, "dec_buf_chk:v_stride", codec_ver_stride);

    /* get result */
    mpp_sys_cfg_ioctl(impl->sys_cfg);

    mpp_sys_cfg_get_u32(impl->sys_cfg, "dec_buf_chk:h_stride_by_byte", &info_set->h_stride_by_byte);
    mpp_sys_cfg_get_u32(impl->sys_cfg, "dec_buf_chk:h_stride_by_pixel", &info_set->h_stride_by_pixel);
    mpp_sys_cfg_get_u32(impl->sys_cfg, "dec_buf_chk:v_stride", &info_set->v_stride);
    mpp_sys_cfg_get_u32(impl->sys_cfg, "dec_buf_chk:size_total", &info_set->size_total);

    return;
}

static void generate_info_set(MppBufSlotsImpl *impl, MppFrame frame, RK_U32 force_def_align)
{
    const RK_U32 width  = mpp_frame_get_width(frame);
    const RK_U32 height = mpp_frame_get_height(frame);
    const MppFrameFormat fmt = mpp_frame_get_fmt(frame);
    MppBufSlotInfoSet legacy_info_set;
    MppBufSlotInfoSet sys_cfg_info_set;
    MppBufSlotInfoSet *info_set_ptr = NULL;
    MppFrameImpl *info_set_impl = NULL;
    MppFrameImpl *frame_impl = NULL;

    prepare_info_set_legacy(impl, frame, &legacy_info_set, force_def_align);
    prepare_info_set_by_sys_cfg(impl, frame, &sys_cfg_info_set);

    mpp_frame_set_width(impl->info_set, width);
    mpp_frame_set_height(impl->info_set, height);
    mpp_frame_set_fmt(impl->info_set, fmt);
    info_set_ptr = use_legacy_align ? &legacy_info_set : &sys_cfg_info_set;
    mpp_frame_set_hor_stride(impl->info_set, info_set_ptr->h_stride_by_byte);
    mpp_frame_set_ver_stride(impl->info_set, info_set_ptr->v_stride);
    mpp_frame_set_hor_stride_pixel(impl->info_set, info_set_ptr->h_stride_by_pixel);
    mpp_frame_set_buf_size(impl->info_set, info_set_ptr->size_total);
    mpp_frame_set_buf_size(frame, info_set_ptr->size_total);
    mpp_frame_set_hor_stride(frame, info_set_ptr->h_stride_by_byte);
    mpp_frame_set_ver_stride(frame, info_set_ptr->v_stride);
    mpp_frame_set_hor_stride_pixel(frame, info_set_ptr->h_stride_by_pixel);
    impl->buf_size = info_set_ptr->size_total;

    if (mpp_frame_get_thumbnail_en(frame) == MPP_FRAME_THUMBNAIL_MIXED) {
        /*
         * The decode hw only support 1/2 scaling in width and height,
         * downscale output image only support raster mode with 8bit depth.
         */
        RK_U32 down_scale_ver = MPP_ALIGN(mpp_frame_get_height(frame) >> 1, 16);
        RK_U32 down_scale_hor = MPP_ALIGN(mpp_frame_get_width(frame) >> 1, 16);
        RK_U32 downscale_buf_size;
        RK_U32 down_scale_y_virstride = down_scale_ver * down_scale_hor;

        switch ((fmt & MPP_FRAME_FMT_MASK)) {
        case MPP_FMT_YUV400 : {
            downscale_buf_size = down_scale_y_virstride;
        } break;
        case MPP_FMT_YUV420SP_10BIT :
        case MPP_FMT_YUV420SP : {
            downscale_buf_size = down_scale_y_virstride * 3 / 2;
        } break;
        case MPP_FMT_YUV422SP_10BIT :
        case MPP_FMT_YUV422SP : {
            downscale_buf_size = down_scale_y_virstride * 2;
        } break;
        case MPP_FMT_YUV444SP_10BIT :
        case MPP_FMT_YUV444SP : {
            downscale_buf_size = down_scale_y_virstride * 3;
        } break;
        default : {
            downscale_buf_size = down_scale_y_virstride * 3 / 2;
        } break;
        }
        downscale_buf_size = MPP_ALIGN(downscale_buf_size, 16);
        impl->buf_size += downscale_buf_size;
        mpp_frame_set_buf_size(impl->info_set, impl->buf_size);
        mpp_frame_set_buf_size(frame, impl->buf_size);
    }
    info_set_impl = (MppFrameImpl *)impl->info_set;
    frame_impl    = (MppFrameImpl *)frame;
    info_set_impl->color_range      = frame_impl->color_range;
    info_set_impl->color_primaries  = frame_impl->color_primaries;
    info_set_impl->color_trc        = frame_impl->color_trc;
    info_set_impl->colorspace       = frame_impl->colorspace;
    info_set_impl->chroma_location  = frame_impl->chroma_location;

    if (impl->align_chk_log_en) {
        impl->align_chk_log_en = 0;
        if (legacy_info_set.h_stride_by_pixel != sys_cfg_info_set.h_stride_by_pixel)
            mpp_logi("mismatch h_stride_by_pixel %d - %d\n",
                     legacy_info_set.h_stride_by_pixel,
                     sys_cfg_info_set.h_stride_by_pixel);
        if (legacy_info_set.h_stride_by_byte != sys_cfg_info_set.h_stride_by_byte)
            mpp_logi("mismatch h_stride_by_byte %d - %d\n",
                     legacy_info_set.h_stride_by_byte,
                     sys_cfg_info_set.h_stride_by_byte);
        if (legacy_info_set.v_stride != sys_cfg_info_set.v_stride)
            mpp_logi("mismatch v_stride %d - %d\n",
                     legacy_info_set.v_stride,
                     sys_cfg_info_set.v_stride);
        if (legacy_info_set.size_total != sys_cfg_info_set.size_total)
            mpp_logi("mismatch size_total %d - %d\n",
                     legacy_info_set.size_total,
                     sys_cfg_info_set.size_total);
    }
}

#define dump_slots(...) _dump_slots(__FUNCTION__, ## __VA_ARGS__)

static void buf_slot_logs_reset(MppBufSlotLogs *logs)
{
    logs->log_count = 0;
    logs->log_write = 0;
    logs->log_read = 0;
}

static MppBufSlotLogs *buf_slot_logs_init(RK_U32 max_count)
{
    MppBufSlotLogs *logs = NULL;

    if (!max_count)
        return NULL;

    logs = mpp_malloc_size(MppBufSlotLogs, sizeof(MppBufSlotLogs) +
                           max_count * sizeof(MppBufSlotLog));
    if (!logs) {
        mpp_err_f("failed to create %d buf slot logs\n", max_count);
        return NULL;
    }

    logs->max_count = max_count;
    logs->logs = (MppBufSlotLog *)(logs + 1);
    buf_slot_logs_reset(logs);

    return logs;
}

static void buf_slot_logs_deinit(MppBufSlotLogs *logs)
{
    MPP_FREE(logs);
}

static void buf_slot_logs_write(MppBufSlotLogs *logs, RK_S32 index, MppBufSlotOps op,
                                SlotStatus before, SlotStatus after)
{
    MppBufSlotLog *log = NULL;

    log = &logs->logs[logs->log_write];
    log->index      = index;
    log->ops        = op;
    log->status_in  = before;
    log->status_out = after;

    logs->log_write++;
    if (logs->log_write >= logs->max_count)
        logs->log_write = 0;

    if (logs->log_count < logs->max_count)
        logs->log_count++;
    else {
        logs->log_read++;
        if (logs->log_read >= logs->max_count)
            logs->log_read = 0;
    }
}

static void buf_slot_logs_dump(MppBufSlotLogs *logs)
{
    while (logs->log_count) {
        MppBufSlotLog *log = &logs->logs[logs->log_read];

        mpp_log("index %2d op: %s status in %08x out %08x",
                log->index, op_string[log->ops], log->status_in.val, log->status_out.val);

        logs->log_read++;
        if (logs->log_read >= logs->max_count)
            logs->log_read = 0;
        logs->log_count--;
    }
    mpp_assert(logs->log_read == logs->log_write);
}

static void _dump_slots(const char *caller, MppBufSlotsImpl *impl)
{
    MppBufSlotEntry *slot = impl->slots;
    RK_S32 i;

    mpp_log("\ncaller %s is dumping slots\n", caller, impl->slots_idx);
    mpp_log("slots %d %p buffer count %d buffer size %d\n", impl->slots_idx,
            impl, impl->buf_count, impl->buf_size);
    mpp_log("decode  count %d\n", impl->decode_count);
    mpp_log("display count %d\n", impl->display_count);

    for (i = 0; i < impl->buf_count; i++, slot++) {
        SlotStatus status = slot->status;
        mpp_log("slot %2d used %d refer %d decoding %d display %d status %08x\n",
                i, status.on_used, status.codec_use, status.hal_use, status.queue_use, status.val);
    }

    mpp_log("\nslot operation history:\n\n");

    if (impl->logs)
        buf_slot_logs_dump(impl->logs);

    mpp_assert(0);

    return;
}

static void slot_ops_with_log(MppBufSlotsImpl *impl, MppBufSlotEntry *slot, MppBufSlotOps op, void *arg)
{
    RK_U32 error = 0;
    RK_S32 index = slot->index;
    SlotStatus status = slot->status;
    SlotStatus before = status;
    switch (op) {
    case SLOT_INIT : {
        status.val = 0;
    } break;
    case SLOT_SET_ON_USE : {
        status.on_used = 1;
    } break;
    case SLOT_CLR_ON_USE : {
        status.on_used = 0;
    } break;
    case SLOT_SET_NOT_READY : {
        status.not_ready = 1;
    } break;
    case SLOT_CLR_NOT_READY : {
        status.not_ready = 0;
    } break;
    case SLOT_SET_CODEC_READY : {
        status.not_ready = 0;
    } break;
    case SLOT_CLR_CODEC_READY : {
        status.not_ready = 1;
    } break;
    case SLOT_SET_CODEC_USE : {
        status.codec_use = 1;
    } break;
    case SLOT_CLR_CODEC_USE : {
        status.codec_use = 0;
    } break;
    case SLOT_SET_HAL_INPUT : {
        status.hal_use++;
    } break;
    case SLOT_CLR_HAL_INPUT : {
        if (status.hal_use)
            status.hal_use--;
        else {
            mpp_err("can not clr hal_input on slot %d\n", slot->index);
            error = 1;
        }
    } break;
    case SLOT_SET_HAL_OUTPUT : {
        status.hal_output++;
        status.not_ready  = 1;
    } break;
    case SLOT_CLR_HAL_OUTPUT : {
        if (status.hal_output)
            status.hal_output--;
        else
            mpp_err("can not clr hal_output on slot %d\n", slot->index);

        // NOTE: set output index ready here
        if (!status.hal_output)
            status.not_ready  = 0;
    } break;
    case SLOT_SET_QUEUE_USE :
    case SLOT_ENQUEUE_OUTPUT :
    case SLOT_ENQUEUE_DISPLAY :
    case SLOT_ENQUEUE_DEINTER :
    case SLOT_ENQUEUE_CONVERT : {
        status.queue_use++;
    } break;
    case SLOT_CLR_QUEUE_USE :
    case SLOT_DEQUEUE_OUTPUT :
    case SLOT_DEQUEUE_DISPLAY :
    case SLOT_DEQUEUE_DEINTER :
    case SLOT_DEQUEUE_CONVERT : {
        if (status.queue_use)
            status.queue_use--;
        else {
            mpp_err("can not clr queue_use on slot %d\n", slot->index);
            error = 1;
        }
    } break;
    case SLOT_SET_EOS : {
        status.eos = 1;
    } break;
    case SLOT_CLR_EOS : {
        status.eos = 0;
        slot->eos = 0;
    } break;
    case SLOT_SET_FRAME : {
        status.has_frame = (arg) ? (1) : (0);
    } break;
    case SLOT_CLR_FRAME : {
        status.has_frame = 0;
    } break;
    case SLOT_SET_BUFFER : {
        status.has_buffer = (arg) ? (1) : (0);
    } break;
    case SLOT_CLR_BUFFER : {
        status.has_buffer = 0;
    } break;
    default : {
        mpp_err("found invalid operation code %d\n", op);
        error = 1;
    } break;
    }
    slot->status = status;
    buf_slot_dbg(BUF_SLOT_DBG_OPS_RUNTIME, "slot %3d index %2d op: %s arg %010p status in %08x out %08x",
                 impl->slots_idx, index, op_string[op], arg, before.val, status.val);
    if (impl->logs)
        buf_slot_logs_write(impl->logs, index, op, before, status);
    if (error)
        dump_slots(impl);
}

static void init_slot_entry(MppBufSlotsImpl *impl, RK_S32 pos, RK_S32 count)
{
    MppBufSlotEntry *slot = impl->slots;
    RK_S32 i;

    for (i = 0; i < count; i++, slot++) {
        slot->slots = impl;
        INIT_LIST_HEAD(&slot->list);
        slot->index = pos + i;
        slot->frame = NULL;
        slot_ops_with_log(impl, slot, SLOT_INIT, NULL);
    }
}

/*
 * only called on unref / displayed / decoded
 *
 * NOTE: MppFrame will be destroyed outside mpp
 *       but MppBuffer must dec_ref here
 */
static RK_S32 check_entry_unused(MppBufSlotsImpl *impl, MppBufSlotEntry *entry)
{
    SlotStatus status = entry->status;

    if (status.on_used &&
        !status.not_ready &&
        !status.codec_use &&
        !status.hal_output &&
        !status.hal_use &&
        !status.queue_use) {
        if (entry->frame) {
            slot_ops_with_log(impl, entry, SLOT_CLR_FRAME, entry->frame);
            mpp_frame_deinit(&entry->frame);
        }
        if (entry->buffer) {
            mpp_buffer_put(entry->buffer);
            slot_ops_with_log(impl, entry, SLOT_CLR_BUFFER, entry->buffer);
            entry->buffer = NULL;
        }

        slot_ops_with_log(impl, entry, SLOT_CLR_ON_USE, NULL);
        impl->used_count--;
        return 1;
    }

    return 0;
}

static void clear_slots_impl(MppBufSlotsImpl *impl)
{
    MppBufSlotEntry *slot = (MppBufSlotEntry *)impl->slots;
    RK_S32 i;

    if (impl->sys_cfg)
        mpp_sys_cfg_put(impl->sys_cfg);

    for (i = 0; i < (RK_S32)MPP_ARRAY_ELEMS(impl->queue); i++) {
        if (!list_empty(&impl->queue[i]))
            dump_slots(impl);

        mpp_assert(list_empty(&impl->queue[i]));
    }

    for (i = 0; i < impl->buf_count; i++, slot++) {
        mpp_assert(!slot->status.on_used);
        if (slot->status.on_used) {
            dump_slots(impl);
            mpp_buf_slot_reset(impl, i);
        }
    }

    impl->used_count = 0;

    if (impl->info)
        mpp_frame_deinit(&impl->info);

    if (impl->info_set)
        mpp_frame_deinit(&impl->info_set);

    if (impl->logs) {
        buf_slot_logs_deinit(impl->logs);
        impl->logs = NULL;
    }

    mpp_mutex_destroy(&impl->lock);

    mpp_free(impl->slots);
    mpp_free(impl);
}

MPP_RET mpp_buf_slot_init(MppBufSlots *slots)
{
    MppBufSlotsImpl *impl;
    RK_U32 i;

    if (!slots) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    impl = mpp_calloc(MppBufSlotsImpl, 1);
    if (!impl) {
        *slots = NULL;
        return MPP_NOK;
    }

    mpp_env_get_u32("buf_slot_debug", &buf_slot_debug,
                    BUF_SLOT_DBG_OPS_HISTORY | BUF_SLOT_DBG_INFO_SET);
    mpp_env_get_u32("use_legacy_align", &use_legacy_align, 0);

    do {
        if (mpp_sys_cfg_get(&impl->sys_cfg)) {
            mpp_err_f("mpp_sys_cfg_get failed\n");
            break;
        }

        mpp_mutex_init(&impl->lock);

        for (i = 0; i < MPP_ARRAY_ELEMS(impl->queue); i++) {
            INIT_LIST_HEAD(&impl->queue[i]);
        }

        if (buf_slot_debug & BUF_SLOT_DBG_OPS_HISTORY) {
            impl->logs = buf_slot_logs_init(SLOT_OPS_MAX_COUNT);
            if (!impl->logs)
                break;
        }

        if (mpp_frame_init(&impl->info))
            break;

        if (mpp_frame_init(&impl->info_set))
            break;

        // slots information default setup
        impl->hal_hor_align = default_align_16;
        impl->hal_ver_align = default_align_16;
        impl->hal_len_align = NULL;
        impl->numerator     = 9;
        impl->denominator   = 5;
        impl->slots_idx     = buf_slot_idx++;
        impl->info_change_slot_idx = -1;
        impl->align_chk_log_env = (buf_slot_debug & BUF_SLOT_DBG_INFO_SET) ? 1 : 0;
        impl->align_chk_log_en = impl->align_chk_log_env;

        *slots = impl;
        return MPP_OK;
    } while (0);

    clear_slots_impl(impl);

    *slots = NULL;
    return MPP_NOK;
}

MPP_RET mpp_buf_slot_deinit(MppBufSlots slots)
{
    if (!slots) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    clear_slots_impl((MppBufSlotsImpl *)slots);
    return MPP_OK;
}

MPP_RET mpp_buf_slot_setup(MppBufSlots slots, RK_S32 count)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;

    if (!impl) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    buf_slot_dbg(BUF_SLOT_DBG_SETUP, "slot %p setup: count %d\n", impl, count);

    mpp_mutex_lock(&impl->lock);

    if (!impl->slots) {
        // first slot setup
        impl->buf_count = impl->new_count = count;
        impl->slots = mpp_calloc(MppBufSlotEntry, count);
        init_slot_entry(impl, 0, count);
        impl->used_count = 0;
    } else {
        // record the slot count for info changed ready config
        if (count > impl->buf_count) {
            impl->slots = mpp_realloc(impl->slots, MppBufSlotEntry, count);
            mpp_assert(impl->slots);
            init_slot_entry(impl, impl->buf_count, (count - impl->buf_count));
        }
        impl->new_count = count;
    }

    mpp_mutex_unlock(&impl->lock);

    return MPP_OK;
}

RK_U32 mpp_buf_slot_is_changed(MppBufSlots slots)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    RK_U32 info_changed = 0;

    if (!impl) {
        mpp_err_f("found NULL input\n");
        return 0;
    }

    mpp_mutex_lock(&impl->lock);
    info_changed = impl->info_changed;
    mpp_mutex_unlock(&impl->lock);

    return info_changed;
}

MPP_RET mpp_buf_slot_ready(MppBufSlots slots)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;

    if (!impl) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    buf_slot_dbg(BUF_SLOT_DBG_SETUP, "slot %p is ready now\n", impl);

    mpp_mutex_lock(&impl->lock);

    slot_assert(impl, impl->slots);
    if (!impl->info_changed)
        mpp_log("found info change ready set without internal info change\n");

    // ready mean the info_set will be copy to info as the new configuration
    if (impl->buf_count != impl->new_count) {
        impl->slots = mpp_realloc(impl->slots, MppBufSlotEntry, impl->new_count);
        mpp_assert(impl->slots);
        init_slot_entry(impl, 0, impl->new_count);
    }
    impl->buf_count = impl->new_count;

    mpp_frame_copy(impl->info, impl->info_set);

    if (impl->logs)
        buf_slot_logs_reset(impl->logs);

    impl->info_changed = 0;
    impl->info_change_slot_idx = -1;

    mpp_mutex_unlock(&impl->lock);

    return MPP_OK;
}

size_t mpp_buf_slot_get_size(MppBufSlots slots)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    size_t size = 0;

    if (!impl) {
        mpp_err_f("found NULL input\n");
        return 0;
    }

    mpp_mutex_lock(&impl->lock);
    size = impl->buf_size;
    mpp_mutex_unlock(&impl->lock);

    return size;
}

RK_S32 mpp_buf_slot_get_count(MppBufSlots slots)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    size_t count = 0;

    if (!impl) {
        mpp_err_f("found NULL input\n");
        return -1;
    }

    mpp_mutex_lock(&impl->lock);
    count = impl->buf_count;
    mpp_mutex_unlock(&impl->lock);

    return count;
}

MPP_RET mpp_buf_slot_set_callback(MppBufSlots slots, MppCbCtx *cb_ctx)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;

    if (!impl) {
        mpp_err_f("found NULL input\n");
        return MPP_NOK;
    }

    mpp_mutex_lock(&impl->lock);
    impl->callback = *cb_ctx;
    mpp_mutex_unlock(&impl->lock);

    return MPP_OK;
}

MPP_RET mpp_buf_slot_get_unused(MppBufSlots slots, RK_S32 *index)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    MppBufSlotEntry *slot;
    RK_S32 i;

    if (!impl || !index) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    slot = impl->slots;

    mpp_mutex_lock(&impl->lock);

    for (i = 0; i < impl->buf_count; i++, slot++) {
        if (!slot->status.on_used) {
            *index = i;
            slot_ops_with_log(impl, slot, SLOT_SET_ON_USE, NULL);
            slot_ops_with_log(impl, slot, SLOT_SET_NOT_READY, NULL);
            impl->used_count++;
            mpp_mutex_unlock(&impl->lock);
            return MPP_OK;
        }
    }

    *index = -1;
    mpp_err_f("failed to get a unused slot\n");
    dump_slots(impl);
    slot_assert(impl, 0);

    mpp_mutex_unlock(&impl->lock);

    return MPP_NOK;
}

MPP_RET mpp_buf_slot_set_flag(MppBufSlots slots, RK_S32 index, SlotUsageType type)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;

    if (!impl) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    mpp_mutex_lock(&impl->lock);

    slot_assert(impl, (index >= 0) && (index < impl->buf_count));
    slot_ops_with_log(impl, &impl->slots[index], set_flag_op[type], NULL);

    mpp_mutex_unlock(&impl->lock);

    return MPP_OK;
}

MPP_RET mpp_buf_slot_clr_flag(MppBufSlots slots, RK_S32 index, SlotUsageType type)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    MppBufSlotEntry *slot;
    RK_S32 unused = 0;

    if (!impl) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    mpp_mutex_lock(&impl->lock);

    slot_assert(impl, (index >= 0) && (index < impl->buf_count));
    slot = &impl->slots[index];
    slot_ops_with_log(impl, slot, clr_flag_op[type], NULL);

    if (type == SLOT_HAL_OUTPUT)
        impl->decode_count++;

    unused = check_entry_unused(impl, slot);

    mpp_mutex_unlock(&impl->lock);

    if (unused)
        mpp_callback(&impl->callback, impl);
    return MPP_OK;
}

MPP_RET mpp_buf_slot_enqueue(MppBufSlots slots, RK_S32 index, SlotQueueType type)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    MppBufSlotEntry *slot;

    if (!impl) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    mpp_mutex_lock(&impl->lock);

    slot_assert(impl, (index >= 0) && (index < impl->buf_count));
    slot = &impl->slots[index];
    slot_ops_with_log(impl, slot, (MppBufSlotOps)(SLOT_ENQUEUE + type), NULL);

    // add slot to display list
    list_del_init(&slot->list);
    list_add_tail(&slot->list, &impl->queue[type]);

    mpp_mutex_unlock(&impl->lock);

    return MPP_OK;
}

MPP_RET mpp_buf_slot_dequeue(MppBufSlots slots, RK_S32 *index, SlotQueueType type)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    MppBufSlotEntry *slot;

    if (!impl || !index) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    mpp_mutex_lock(&impl->lock);

    if (list_empty(&impl->queue[type])) {
        mpp_mutex_unlock(&impl->lock);
        return MPP_NOK;
    }

    slot = list_entry(impl->queue[type].next, MppBufSlotEntry, list);
    if (slot->status.not_ready) {
        mpp_mutex_unlock(&impl->lock);
        return MPP_NOK;
    }

    // make sure that this slot is just the next display slot
    list_del_init(&slot->list);
    slot_assert(impl, slot->index < impl->buf_count);
    slot_ops_with_log(impl, slot, (MppBufSlotOps)(SLOT_DEQUEUE + type), NULL);
    impl->display_count++;
    *index = slot->index;

    mpp_mutex_unlock(&impl->lock);

    return MPP_OK;
}

MPP_RET mpp_buf_slot_set_prop(MppBufSlots slots, RK_S32 index, SlotPropType type, void *val)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    MppBufSlotEntry *slot;

    if (!impl || !val || type >= SLOT_PROP_BUTT) {
        mpp_err_f("found invalid input slots %p type %d val %p\n", slots, type, val);
        return MPP_ERR_UNKNOW;
    }

    mpp_mutex_lock(&impl->lock);

    slot_assert(impl, (index >= 0) && (index < impl->buf_count));
    slot = &impl->slots[index];
    slot_ops_with_log(impl, slot, set_val_op[type], val);

    switch (type) {
    case SLOT_EOS: {
        RK_U32 eos = *(RK_U32*)val;

        slot->eos = eos;
        if (slot->frame)
            mpp_frame_set_eos(slot->frame, eos);
    } break;
    case SLOT_FRAME: {
        MppFrame frame = val;
        MppFrameImpl *src;
        MppFrameImpl *dst;

        slot_assert(impl, slot->status.not_ready);
        /*
         * we need to detect infomation change here
         * there are two types of info change:
         * 1. buffer size change
         *    this case need to reset buffer group and commit buffer with new size
         * 2. display info change
         *    if only width/height/fmt is change and buffer do not need to be reset
         *    only display info change is need
         */
        generate_info_set(impl, frame, 0);

        if (!slot->frame)
            mpp_frame_init(&slot->frame);

        src = (MppFrameImpl *)frame;
        dst = (MppFrameImpl *)slot->frame;
        mpp_frame_copy(dst, src);
        // NOTE: stride from codec need to be change to hal stride
        //       hor_stride and ver_stride can not be zero
        //       they are the stride required by codec
        //       then hal will modify it according to hardware requirement
        mpp_assert(src->hor_stride);
        mpp_assert(src->ver_stride);
        dst->hor_stride = src->hor_stride;
        dst->ver_stride = src->ver_stride;
        dst->eos = slot->eos;

        if (mpp_frame_info_cmp(impl->info, impl->info_set)) {
            MppFrameImpl *old = (MppFrameImpl *)impl->info;

            impl->info_changed = 1;
            impl->info_change_slot_idx = index;

            if (impl->coding_type != MPP_VIDEO_CodingMJPEG)
                impl->align_chk_log_en = impl->align_chk_log_env;

            if (old->width || old->height) {
                mpp_dbg_info("info change found\n");
                mpp_dbg_info("old width %4d height %4d stride hor %4d ver %4d fmt %4d\n",
                             old->width, old->height, old->hor_stride,
                             old->ver_stride, old->fmt);
            }
            mpp_dbg_info("new width %4d height %4d stride hor %4d ver %4d fmt %4d\n",
                         dst->width, dst->height, dst->hor_stride, dst->ver_stride,
                         dst->fmt);
            // info change found here
        }
    } break;
    case SLOT_BUFFER: {
        MppBuffer buffer = val;

        if (slot->buffer) {
            // NOTE: reset buffer only on stream buffer slot
            slot_assert(impl, !slot->frame);
            mpp_buffer_put(slot->buffer);
        }
        mpp_buffer_inc_ref(buffer);
        slot->buffer = buffer;

        if (slot->frame)
            mpp_frame_set_buffer(slot->frame, buffer);
    } break;
    default : {
    } break;
    }

    mpp_mutex_unlock(&impl->lock);

    return MPP_OK;
}

MPP_RET mpp_buf_slot_get_prop(MppBufSlots slots, RK_S32 index, SlotPropType type, void *val)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    MppBufSlotEntry *slot;

    if (!impl || !val || type >= SLOT_PROP_BUTT) {
        mpp_err_f("found invalid input slots %p type %d val %p\n", slots, type, val);
        return MPP_ERR_UNKNOW;
    }

    mpp_mutex_lock(&impl->lock);

    slot_assert(impl, (index >= 0) && (index < impl->buf_count));
    slot = &impl->slots[index];

    switch (type) {
    case SLOT_EOS: {
        *(RK_U32*)val = slot->eos;
    } break;
    case SLOT_FRAME: {
        MppFrame *frame = (MppFrame *)val;
        //*frame = (slot->status.has_frame) ? (slot->frame) : (NULL);

        mpp_assert(slot->status.has_frame);
        if (slot->status.has_frame) {
            if (!*frame )
                mpp_frame_init(frame);
            if (*frame)
                mpp_frame_copy(*frame, slot->frame);
        } else
            *frame = NULL;
    } break;
    case SLOT_FRAME_PTR: {
        MppFrame *frame = (MppFrame *)val;

        mpp_assert(slot->status.has_frame);
        *frame = (slot->status.has_frame) ? (slot->frame) : (NULL);
    } break;
    case SLOT_BUFFER: {
        MppBuffer *buffer = (MppBuffer *)val;

        *buffer = (slot->status.has_buffer) ? (slot->buffer) : (NULL);
    } break;
    default : {
    } break;
    }

    mpp_mutex_unlock(&impl->lock);

    return MPP_OK;
}

MPP_RET mpp_buf_slot_reset(MppBufSlots slots, RK_S32 index)
{
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    MppBufSlotEntry *slot;

    if (!impl || index < 0) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    buf_slot_dbg(BUF_SLOT_DBG_SETUP, "slot %p reset index %d\n", slots, index);

    mpp_mutex_lock(&impl->lock);

    slot_assert(impl, (index >= 0) && (index < impl->buf_count));
    slot = &impl->slots[index];

    // make sure that this slot is just the next display slot
    list_del_init(&slot->list);
    slot_ops_with_log(impl, slot, SLOT_CLR_QUEUE_USE, NULL);
    slot_ops_with_log(impl, slot, SLOT_DEQUEUE, NULL);
    slot_ops_with_log(impl, slot, SLOT_CLR_ON_USE, NULL);

    mpp_mutex_unlock(&impl->lock);

    return MPP_OK;
}

MPP_RET mpp_buf_slot_default_info(MppBufSlots slots, RK_S32 index, void *val)
{
    if (!slots || index < 0) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    mpp_mutex_lock(&impl->lock);
    slot_assert(impl, (index >= 0) && (index < impl->buf_count));
    MppBufSlotEntry *slot = &impl->slots[index];

    slot_assert(impl, slot->status.not_ready);
    slot_assert(impl, !slot->frame);
    slot_assert(impl, impl->info_set);

    if (!slot->frame) {
        mpp_frame_init(&slot->frame);
        mpp_frame_copy(slot->frame, impl->info_set);
    }

    MppFrame *frame = (MppFrame *)val;
    *frame = slot->frame;

    slot_ops_with_log(impl, slot, SLOT_CLR_NOT_READY, NULL);
    slot_ops_with_log(impl, slot, SLOT_SET_FRAME, slot->frame);
    mpp_mutex_unlock(&impl->lock);

    return MPP_OK;
}

RK_U32 mpp_slots_is_empty(MppBufSlots slots, SlotQueueType type)
{
    RK_U32 is_empty = 0;
    if (!slots) {
        mpp_err_f("found NULL input\n");
        return 0;
    }

    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    mpp_mutex_lock(&impl->lock);
    is_empty = list_empty(&impl->queue[type]) ? 1 : 0;
    mpp_mutex_unlock(&impl->lock);

    return is_empty;
}

RK_S32 mpp_slots_get_used_count(MppBufSlots slots)
{
    RK_S32 used_count = 0;
    if (!slots) {
        mpp_err_f("found NULL input\n");
        return 0;
    }
    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    mpp_mutex_lock(&impl->lock);
    used_count = impl->used_count;
    mpp_mutex_unlock(&impl->lock);

    return used_count;
}

RK_S32 mpp_slots_get_unused_count(MppBufSlots slots)
{
    RK_S32 unused_count = 0;
    if (!slots) {
        mpp_err_f("found NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    mpp_mutex_lock(&impl->lock);
    slot_assert(impl, (impl->used_count >= 0) && (impl->used_count <= impl->buf_count));
    unused_count = impl->buf_count - impl->used_count;
    mpp_mutex_unlock(&impl->lock);

    return unused_count;
}

MPP_RET mpp_slots_set_prop(MppBufSlots slots, SlotsPropType type, void *val)
{
    if (!slots || !val || type >= SLOTS_PROP_BUTT) {
        mpp_err_f("found invalid input slots %p type %d val %p\n", slots, type, val);
        return MPP_ERR_UNKNOW;
    }

    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    mpp_mutex_lock(&impl->lock);
    RK_U32 value = *((RK_U32*)val);
    switch (type) {
    case SLOTS_EOS: {
        impl->eos = value;
    } break;
    case SLOTS_NUMERATOR : {
        impl->numerator = value;
    } break;
    case SLOTS_DENOMINATOR : {
        impl->denominator = value;
    } break;
    case SLOTS_HOR_ALIGN: {
        impl->hal_hor_align = (AlignFunc)val;
    } break;
    case SLOTS_VER_ALIGN: {
        impl->hal_ver_align = (AlignFunc)val;
    } break;
    case SLOTS_LEN_ALIGN: {
        impl->hal_len_align = (AlignFunc)val;
    } break;
    case SLOTS_COUNT: {
        impl->buf_count = value;
    } break;
    case SLOTS_SIZE: {
        impl->buf_size = value;
    } break;
    case SLOTS_FRAME_INFO: {
        // do info change detection here
        generate_info_set(impl, (MppFrame)val, 1);
        mpp_frame_copy(impl->info, impl->info_set);
        {
            MppFrameImpl *p = (MppFrameImpl *)impl->info;
            mpp_log("set frame info: w %4d h %4d hor %4d ver %4d\n", p->width, p->height, p->hor_stride, p->ver_stride);
        }
        mpp_frame_copy((MppFrame)val, impl->info_set);
        if (impl->info_change_slot_idx >= 0) {
            MppBufSlotEntry *slot = &impl->slots[impl->info_change_slot_idx];

            if (slot->frame) {
                MppFrameImpl *dst = (MppFrameImpl *)slot->frame;
                MppFrameImpl *src = (MppFrameImpl *)val;

                dst->fmt = src->fmt;
                dst->hor_stride = src->hor_stride;
                dst->hor_stride_pixel = src->hor_stride_pixel;
                dst->ver_stride = src->ver_stride;
                dst->buf_size = src->buf_size;

                if (MPP_FRAME_FMT_IS_FBC(dst->fmt) && impl->hal_fbc_adj_cfg.func)
                    impl->hal_fbc_adj_cfg.func(impl, dst, impl->hal_fbc_adj_cfg.expand);
            }

            impl->info_change_slot_idx = -1;
        }
    } break;
    case SLOTS_HAL_FBC_ADJ : {
        impl->hal_fbc_adj_cfg = *((SlotHalFbcAdjCfg *)val);
    } break;
    case SLOTS_CODING_TYPE : {
        impl->coding_type = *((MppCodingType *)val);
    } break;
    case SLOTS_WIDTH_ALIGN: {
        impl->hal_width_align = (AlignFunc)val;
    } break;
    default : {
    } break;
    }
    mpp_mutex_unlock(&impl->lock);

    return MPP_OK;
}

MPP_RET mpp_slots_get_prop(MppBufSlots slots, SlotsPropType type, void *val)
{
    if (!slots || !val || type >= SLOTS_PROP_BUTT) {
        mpp_err_f("found invalid input slots %p type %d val %p\n", slots, type, val);
        return MPP_NOK;
    }

    MppBufSlotsImpl *impl = (MppBufSlotsImpl *)slots;
    mpp_mutex_lock(&impl->lock);
    MPP_RET ret = MPP_OK;

    switch (type) {
    case SLOTS_EOS: {
        *((RK_U32 *)val) = impl->eos;
    } break;
    case SLOTS_COUNT: {
        *((RK_U32 *)val) = impl->buf_count;
    } break;
    case SLOTS_SIZE: {
        *((RK_U32 *)val) = (RK_U32)impl->buf_size;
    } break;
    case SLOTS_FRAME_INFO: {
        MppFrame frame = (MppFrame)val;
        MppFrame info  = impl->info;
        mpp_frame_copy(frame, info);
    } break;
    default : {
        mpp_err("can not get slots prop type %d\n", type);
        ret = MPP_NOK;
    } break;
    }
    mpp_mutex_unlock(&impl->lock);

    return ret;
}
