/*
 * Copyright (c) 2011 Intel Corporation. All Rights Reserved.
 * Copyright (c) Imagination Technologies Limited, UK
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Zeng Li <zeng.li@intel.com>
 *    Shengquan Yuan  <shengquan.yuan@intel.com>
 *    Binglin Chen <binglin.chen@intel.com>
 *
 */


#include "img_types.h"
#include "psb_drv_video.h"
#include "psb_surface.h"
#include "lnc_cmdbuf.h"

#define MAX_SLICES_PER_PICTURE 72

#define CODED_BUFFER_EOSEQ_FLAG (0x1u<<0)
#define CODED_BUFFER_EOSTREAM_FLAG (0x1u<<1)

/* commands for topaz,shared with user space driver */
enum drm_lnc_topaz_cmd {
    MTX_CMDID_NULL = 0,
    MTX_CMDID_DO_HEADER = 1,
    MTX_CMDID_ENCODE_SLICE = 2,
    MTX_CMDID_WRITEREG = 3,
    MTX_CMDID_START_PIC = 4,
    MTX_CMDID_END_PIC = 5,
    MTX_CMDID_SYNC = 6,
    MTX_CMDID_ENCODE_ONE_ROW = 7,
    MTX_CMDID_FLUSH = 8,
    MTX_CMDID_SW_LEAVE_LOWPOWER = 0x7c,
    MTX_CMDID_SW_ENTER_LOWPOWER = 0x7e,
    MTX_CMDID_SW_NEW_CODEC = 0x7f
};

/* codecs topaz supports,shared with user space driver */
enum drm_lnc_topaz_codec {
    IMG_CODEC_JPEG = 0,
    IMG_CODEC_H264_NO_RC,
    IMG_CODEC_H264_VBR,
    IMG_CODEC_H264_CBR,
    IMG_CODEC_H264_VCM,
    IMG_CODEC_H263_NO_RC,
    IMG_CODEC_H263_VBR,
    IMG_CODEC_H263_CBR,
    IMG_CODEC_MPEG4_NO_RC,
    IMG_CODEC_MPEG4_VBR,
    IMG_CODEC_MPEG4_CBR,
    IMG_CODEC_NUM
};

typedef enum _img_format_ {
    IMG_CODEC_IYUV, /* IYUV */
    IMG_CODEC_IMC2, /* IMC2 */
    IMG_CODEC_PL8,
    IMG_CODEC_PL12,
    IMG_CODEC_NV12,
} IMG_FORMAT;


typedef struct _RC_PARAMS_ {
    IMG_UINT32  BitsPerSecond;
    IMG_UINT32  InitialQp;
    IMG_UINT32  BUSize;
    IMG_UINT32  FrameRate;
    IMG_UINT32  BufferSize;
    IMG_UINT32  BitsConsumed;
    IMG_UINT32  IntraFreq;
    IMG_UINT16  IDRFreq;

    IMG_UINT16   MinQP;
    IMG_BOOL    RCEnable;
    IMG_BOOL    FrameSkip;

    IMG_UINT8   Slices;
    IMG_UINT8   VCMBitrateMargin;
    IMG_UINT32   InitialLevel;
    IMG_UINT32   InitialDelay;
} IMG_RC_PARAMS;

/*!
*****************************************************************************
*
* @Description    Struct describing rate control input parameters
*
* @Brief          Rate control input parameters
*
****************************************************************************/
typedef struct {
    IMG_UINT8   SeInitQP;               //!< Initial QP for Sequence
    IMG_UINT8   MinQPVal;               //!< Minimum QP value to use
    IMG_UINT8   MaxQPVal;               //!< Maximum QP value to use

    IMG_UINT8   MBPerRow;               /* Number of MBs Per Row */
    IMG_UINT16  MBPerFrm;               /* Number of MBs Per Frame */
    IMG_UINT16  MBPerBU;                /* Number of MBs Per BU */
    IMG_UINT16  BUPerFrm;               /* Number of BUs Per Frame */

    IMG_UINT16  IntraPeriod;    /* Intra frame frequency */

    IMG_INT32   BitsPerFrm;             /* Bits Per Frame */
    IMG_INT32   BitsPerBU;              /* Bits Per BU */
    IMG_INT32   BitsPerMB;              /* Bits Per MB */

    IMG_INT32   BitRate;                        /* Bit Rate (bps) */
    IMG_INT32   BufferSize;             /* Size of Buffer */
    IMG_UINT32   InitialLevel;   /* Initial Level of Buffer */
    IMG_INT32   InitialDelay;   /* Initial Delay of Buffer */

    IMG_UINT8   ScaleFactor;            /* Scale Factor (H264 only) */
    IMG_UINT8   VCMBitrateMargin; /* Bitrate that should be
                                        targetted as a fraction of
                                        128 relative to maximum bitrate
                                        i32BitRate (VCM mode only) */
    IMG_UINT8   HalfFrameRate;  /* Half Frame Rate (MP4 only) */
    IMG_UINT8   FCode;                  /* F Code (MP4 only) */

    /* TO BE DELETED -- ONCE MP4 RC CODE IS OPTIMISED */
    IMG_INT32   BitsPerGOP;             /* Bits Per GOP (MP4 only) */
    IMG_UINT16  AvQPVal;                /* Average QP in Current Picture */
    IMG_UINT16  MyInitQP;               /* Initial Quantizer */

    IMG_INT32   ForceSkipMargin; /* The number of bits of margin
                                        to leave before forcing skipped
                                        macroblocks (VCM mode only) */
    IMG_UINT32  RCScaleFactor;  /* A constant used in rate control = (GopSize/(BufferSize-InitialLevel))*256 */
} IN_RC_PARAMS;

struct coded_buf_aux_info {
    object_buffer_p buf;
    uint32_t aux_flag;          /*Indicate which operation should be applied when map coded buffer.*/
    struct coded_buf_aux_info *next;
};

struct context_ENC_s {
    object_context_p obj_context; /* back reference */

    IMG_UINT32       Width;
    IMG_UINT32       Height;
    IMG_UINT16       RawWidth;
    IMG_UINT16       RawHeight;
    IMG_UINT16       Slices;
    enum drm_lnc_topaz_codec eCodec;
    IMG_FORMAT      eFormat;
    unsigned int    FCode;
    IMG_RC_PARAMS   sRCParams;

    IMG_INT16       HeightMinus16MinusLRBTopOffset;
    IMG_INT16       HeightMinus32MinusLRBTopOffset;
    IMG_INT16       HeightMinusLRB_TopAndBottom_OffsetsPlus16;
    IMG_INT16       HeightMinusLRBSearchHeight;
    IMG_UINT32      IPEControl;

    object_surface_p    src_surface;
    object_surface_p    ref_surface;
    object_surface_p    dest_surface;/* reconstructed surface */
    object_buffer_p     coded_buf;

    /* save previous settings */
    object_surface_p    previous_src_surface;
    object_surface_p    previous_ref_surface;
    object_surface_p    previous_dest_surface; /* reconstructed surface */
    object_buffer_p     previous_coded_buf;
    object_buffer_p     pprevious_coded_buf;

    /* point to the place in cmdbuf following START_PIC, the initial_qp will fill into it later */
    uint32_t *initial_qp_in_cmdbuf;


    /* global topaz_params buffer shared by every cmdbuffer
     * it is because filling InParams for every MB is very time-consuming
     * and in most cases, we can reuse previous frames buffer
     */
    /* 0 and 1 are for in_parms, 2 is for bellow and above params*/
    struct psb_buffer_s topaz_in_params_I;
    struct psb_buffer_s topaz_in_params_P;
    struct psb_buffer_s topaz_above_bellow_params;
    uint32_t topaz_buffer_size;
    uint32_t in_params_size;
    uint32_t bellow_params_size;
    uint32_t above_params_size;

    /* offset in topaz_param buffer */
    uint32_t in_params_ofs;
    uint32_t bellow_params_ofs;
    uint32_t above_params_ofs;

    uint32_t pic_params_size;

    uint32_t header_buffer_size;

    uint32_t seq_header_ofs;
    uint32_t pic_header_ofs;
    uint32_t eoseq_header_ofs;
    uint32_t eostream_header_ofs;
    uint32_t slice_header_ofs;

    uint32_t sliceparam_buffer_size;

    IN_RC_PARAMS in_params_cache; /* following frames reuse the first frame's IN_RC_PARAMS, cache it */

    VAEncSliceParameterBuffer *slice_param_cache;
    uint16_t slice_param_num;

    IMG_UINT16 MPEG4_vop_time_increment_resolution;

    /* saved information for FrameSkip redo */
    uint32_t MPEG4_vop_time_increment_frameskip;
    uint32_t MPEG4_picture_type_frameskip;
    uint8_t profile_idc;
    uint8_t force_idr_h264;
    uint32_t update_rc_control;
    uint8_t OptionalCustomPCF;
    uint32_t max_slice_size;
    uint16_t num_air_mbs;
    uint16_t air_threshold;
    uint32_t autotune_air_flag;
    uint32_t delta_change;

    struct coded_buf_aux_info *p_coded_buf_info;
    unsigned char *save_seq_header_p;

    /*For H264 only.*/
    uint16_t idr_pic_id;
};

typedef struct context_ENC_s *context_ENC_p;

/*#define BELOW_PARAMS_SIZE 8*/

#define HEADER_SIZE             128

#define BELOW_PARAMS_SIZE       16
#define REGION_TYPE_2D          1
#define REGION_TYPE_LINEAR      0
#define REGION_TYPE_2DREF       3


#define MAX_RESIDUAL_PER_MB_H264        1260
#define ISINTER_FLAGS           0x1
#define ISH264_FLAGS            0x2
#define ISMPEG4_FLAGS           0x4
#define ISH263_FLAGS            0x8
#define DEBLOCK_FRAME           0x10
#define ISRC_FLAGS              0x20
#define ISCBR_FLAGS             0x40
#define ISVBR_FLAGS             0x80
#define ISRC_I16BIAS            0x100
#define INTERLEAVE_TARGET       0x200
#define ISVCM_FLAGS             0x400
#define AUTOTUNE_AIR            0x800


#define SPE_EDGE_LEFT   1       /* ->bMinXRealEdge*/
#define SPE_EDGE_RIGHT  2       /* ->bMaxXRealEdge*/
#define SPE_EDGE_TOP    4   /* ->bMinYRealEdge*/
#define SPE_EDGE_BOTTOM 8       /* ->bMaxYRealEdge*/

typedef struct {
    /* Transferred into the input params area of the macroblock parameter structure*/
    IMG_BYTE    CurBlockAddr;
    IMG_BYTE    IPEMin[2];
    IMG_BYTE    IPEMax[2];
    IMG_BYTE    RealEdge;  /*bMinXRealEdge, bMaxXRealEdge, bMinXRealEdge and bMinYRealEdge*/
    /* Surrounding block availability */
    IMG_BYTE    MVValid;
    IMG_BYTE    ParamsValid;
    IMG_BYTE     bySliceQP;
    IMG_BYTE     bySliceQPC;

    IMG_BYTE     Reserved[6]; /* This is padding to make the transfers 16 bytes aligned*/
    /* Transferred into the SW communication section of the macroblock
     * parameter structure We shall EDMA the whole lot of this into eiob
     * in one go, and then use two TDMA's to put it into seperate locations
     * within the macroblock structure
     */
    IMG_UINT32   IPEControl;
    IMG_UINT32   SPEControl;
    IMG_UINT32   JMCompControl;
    IMG_UINT32   VLCControl;
} MTX_CURRENT_IN_PARAMS;

typedef struct { /* corresponding bytes inside the MB_IN structure: */
    IMG_BYTE    BlockSizes;              /****************/
    IMG_BYTE    IntraMode;               /*              */
    IMG_BYTE    Intra4x4ModesBottom[2];  /*              */
    IMG_BYTE    CodeType;                /*  [64 : 71]   */
    IMG_BYTE    Reserved2;               /*              */
    /*IMG_BYTE   SAD;*/
    IMG_BYTE    QPy;                     /*              */
    IMG_BYTE    QPc;                     /****************/

    IMG_BYTE    Reserved3[8];    /* This is padding to make the transfers 16 byte aligned*/

    IMG_UINT16  LumaSubBlockCoded;      /****************/
    IMG_BYTE    ChromaSubBlockCoded;    /*              */
    IMG_BYTE    LumaChromaDCCoded;      /*              */
    /*  [129 : 143] */
    IMG_BYTE    Lambda;                 /*              */
    IMG_BYTE    Reserved[3];            /*              */
    /*              */
    IMG_BYTE    Intra4x4ModeDeltas[8];  /****************/

    /* Motion vectors */
    IMG_UINT16  IntegerMV[16][2];      /* [207 : 144]  */
    /* input region from host */
} MTX_CURRENT_OUT_PARAMS;



typedef enum _TH_SKIP_SCALE_ {
    TH_SKIP_0 = 0,
    TH_SKIP_12 = 1,
    TH_SKIP_24 = 2
} TH_SKIP_SCALE;

typedef struct _PIC_PARAMS_ {
    IMG_UINT32          SrcYBase;
    IMG_UINT32          SrcUBase;
    IMG_UINT32          SrcVBase;
    IMG_UINT32          DstYBase;
    IMG_UINT32          DstUVBase;

    IMG_UINT16          SrcYStride;
    IMG_UINT16          SrcUVStride;
    IMG_UINT16          SrcYRowStride;
    IMG_UINT16          SrcUVRowStride;

    IMG_UINT16          DstYStride;
    IMG_UINT16          DstUVStride;
    IMG_UINT16          DstYRowStride;
    IMG_UINT16          DstUVRowStride;

    IMG_UINT32          InParamsBase;
    IMG_UINT32          InParamsRowStride;

    IMG_UINT32          OutParamsBase;
    IMG_UINT32          CodedBase;

    IMG_UINT32          BelowParamsBase;
    IMG_UINT32          BelowParamRowStride;

    IMG_UINT32          AboveParamsBase;
    IMG_UINT32          AboveParamRowStride;
    IMG_UINT16          Width;
    IMG_UINT16          Height;
    IMG_UINT16          Flags;

    IN_RC_PARAMS        sInParams;
    TH_SKIP_SCALE       THSkip;

    IMG_UINT16          NumSlices;                      //!< Number of slices in the picture
} PIC_PARAMS;


/* This holds the data that is needed at the start of a slice
 */
typedef struct _SLICE_PARAMS_ {

    IMG_UINT16  SliceStartRowNum;
    IMG_UINT16  SliceHeight;

    IMG_UINT32  RefYBase;
    IMG_UINT32  RefUVBase;
    IMG_UINT16  RefYStride;
    IMG_UINT16  RefUVStride;
    IMG_UINT16  RefYRowStride;
    IMG_UINT16  RefUVRowStride;

    IMG_UINT32  CodedData;
    IMG_UINT32  Flags;
    IMG_UINT32  CodedDataPos;
    IMG_UINT32  TotalCoded;
    IMG_UINT32  FCode;

    IMG_UINT32  MaxSliceSize;
    IMG_INT16   NumAirMBs;                      //!< Maximum number of Adaptive intra refresh macroblocks for this slice
    IMG_INT16   AirThreshold;           //!< Theshold value used in Adaptive intra refresh calculation.

} SLICE_PARAMS;


typedef struct _ROW_PARAMS_ {
    IMG_UINT32  TargetYBase;
    IMG_UINT32  TargetYStride;
    IMG_UINT32  TargetUBase;
    IMG_UINT32  TargetVBase;
    IMG_UINT32  TargetUVStride;

    IMG_UINT32  ReferenceYBase;
    IMG_UINT32  ReferenceYStride;
    IMG_UINT32  ReferenceUVBase;
    IMG_UINT32  ReferenceUVStride;

    IMG_UINT32  ReconstructedYBase;
    IMG_UINT32  ReconstructedYStride;
    IMG_UINT32  ReconstructedUVBase;
    IMG_UINT32  ReconstructedUVStride;

    IMG_UINT32  AboveParamsBase;
    IMG_UINT32  OutAboveParamsBase;
    IMG_UINT32  MacroblockInParamsBase;
    IMG_UINT32  MacroblockOutParamsBase;
    IMG_UINT32  BelowParamsBase;
    IMG_UINT32  OutBelowParamsBase;
    IMG_UINT32  CodedData;

    IMG_UINT32  Flags;
    IMG_UINT32  BlockWidth;
    IMG_UINT32  BlockHeight;
    IMG_UINT32  YPos;
    IMG_UINT32  FrameNum;

    IMG_INT     BelowParamsOffset;
    IMG_UINT32  CodedDataPos;
    IMG_UINT32  BaseResidual;
    IMG_UINT32  TotalCoded;

    IMG_UINT32  PADDING[5];

    IMG_UINT32  IPESkipVecBias;
    IMG_UINT32  SPESkipVecBias;
    IMG_INT32   InterMBBias;
    IMG_INT32   Intra16Bias;
    IMG_UINT32  SpeZeroThld;
    IMG_UINT32  SpeZeroThreshold;

} ROW_PARAMS;

#define ROW_PARAMS_TDMA_DIMENSIONS  16,16,sizeof(ROW_PARAMS)

typedef struct _ENCODER_VARIABLES_ {
    IMG_UINT32        ActionFlags;

    IMG_UINT32        SrcYCurrent;
    IMG_UINT32        SrcUCurrent;
    IMG_UINT32        SrcVCurrent;

    IMG_UINT32        DstYCurrent;
    IMG_UINT32        DstUCurrent;
    IMG_UINT32        DstVCurrent;

    IMG_INT           BelowParamsOffset;
    IMG_UINT32        BaseResidual;
    IMG_UINT32        CodedDataPos;
    IMG_UINT32        TotalCoded;

    IMG_UINT32        SrcYOffset;
    IMG_UINT32        SrcUOffset;
    IMG_UINT32        SrcVOffset;

    IMG_UINT32        PADDING[2];
} ENCODER_VARIABLES;

#define SLICE_FLAGS_ISINTER                     0x00000001
#define SLICE_FLAGS_DEBLOCK                     0x00000002

#define SLICE_FLAGS_ISINTER                     0x00000001
#define SLICE_FLAGS_DEBLOCK                     0x00000002

#define RC_STATUS_FRAME_AVE_QP_MASK             0x0ff   /* the average Qp used in this frame */
#define RC_STATUS_FLAG_LARGE_SLICE              0x100   /* At least one slice in this frame was large enough for the firmware to try to reduce it by increasing Qp or skipping MBs */
#define RC_STATUS_FLAG_SLICE_OVERFLOW           0x200   /* At least one slice in this frame was larger than the slice limit */

enum {
    CBR = 0,
    VBR,
    VCM
} eRCMode;

enum {
    EH263 = 0,
    EMpeg4 = 1,
    EH264 = 2,
    EHJpeg = 3
} eEncodingFormat;

#define VAEncSliceParameter_Equal(src, dst)                             \
    (((src)->start_row_number == (dst)->start_row_number)               \
     && ((src)->slice_height == (dst)->slice_height)                    \
     && ((src)->slice_flags.bits.is_intra == (dst)->slice_flags.bits.is_intra) \
     && ((src)->slice_flags.bits.disable_deblocking_filter_idc == (dst)->slice_flags.bits.disable_deblocking_filter_idc))

#define VAEncSliceParameter_LightEqual(src, dst)                             \
    (((src)->start_row_number == (dst)->start_row_number)               \
     && ((src)->slice_height == (dst)->slice_height)                    \
     && ((src)->slice_flags.bits.disable_deblocking_filter_idc == (dst)->slice_flags.bits.disable_deblocking_filter_idc))



#define SURFACE_INFO_SKIP_FLAG_SETTLED 0X80000000
#define GET_SURFACE_INFO_skipped_flag(psb_surface) ((int) (psb_surface->extra_info[5]))
#define SET_SURFACE_INFO_skipped_flag(psb_surface, value) psb_surface->extra_info[5] = (SURFACE_INFO_SKIP_FLAG_SETTLED | value)
#define CLEAR_SURFACE_INFO_skipped_flag(psb_surface) psb_surface->extra_info[5] = 0

VAStatus lnc_CreateContext(object_context_p obj_context,
                           object_config_p obj_config);


void lnc__setup_rcdata(context_ENC_p ctx, PIC_PARAMS *psPicParams, IMG_RC_PARAMS *rc_params);
void lnc__update_rcdata(context_ENC_p ctx, PIC_PARAMS *psPicParams, IMG_RC_PARAMS *rc_params);

void lnc_DestroyContext(
    object_context_p obj_context
);

VAStatus lnc_BeginPicture(context_ENC_p ctx);
VAStatus lnc_EndPicture(context_ENC_p ctx);

void lnc_setup_slice_params(
    context_ENC_p  ctx, IMG_UINT16 YSliceStartPos,
    IMG_UINT16 SliceHeight, IMG_BOOL IsIntra,
    IMG_BOOL  VectorsValid, int bySliceQP);

IMG_UINT32 lnc__send_encode_slice_params(
    context_ENC_p ctx,
    IMG_BOOL IsIntra,
    IMG_UINT16 CurrentRow,
    IMG_BOOL DeblockSlice,
    IMG_UINT32 FrameNum,
    IMG_UINT16 SliceHeight,
    IMG_UINT16 CurrentSlice,
    IMG_UINT32 MaxSliceSize);

VAStatus lnc_RenderPictureParameter(context_ENC_p ctx);


void lnc_reset_encoder_params(context_ENC_p ctx);
unsigned int lnc__get_ipe_control(enum drm_lnc_topaz_codec  eEncodingFormat);