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

/******************************************************************************
 * @file     dma.h
 * @brief    header file for dma driver
 * @version  V1.0
 * @date     08. Apr 2020
 * @model    dma
 ******************************************************************************/

#ifndef _DRV_DMA_H_
#define _DRV_DMA_H_

#include <stdint.h>
#include <stdio.h>
#include <drv/common.h>
#include <drv/list.h>

#ifdef __cplusplus
extern "C" {
#endif

/****** DMA Event *****/
typedef enum {
    DMA_EVENT_TRANSFER_DONE       = 0,  ///< transfer complete
    DMA_EVENT_TRANSFER_BLOCK_DONE,       ///< transfer block done
    DMA_EVENT_TRANSFER_HALF_DONE,       ///< transfer half done
    DMA_EVENT_TRANSFER_ERROR,           ///< transfer error
} csi_dma_event_t;

typedef enum {
    DMA_ADDR_INC    = 0,
    DMA_ADDR_DEC,
    DMA_ADDR_CONSTANT
} csi_dma_addr_inc_t;

typedef enum {
    DMA_DATA_WIDTH_8_BITS  = 0,
    DMA_DATA_WIDTH_16_BITS,
    DMA_DATA_WIDTH_32_BITS,
    DMA_DATA_WIDTH_64_BITS,
    DMA_DATA_WIDTH_128_BITS,
    DMA_DATA_WIDTH_512_BITS
} csi_dma_data_width_t;

typedef enum {
    DMA_MEM2MEM     = 0,
    DMA_MEM2PERH,
    DMA_PERH2MEM,
} csi_dma_trans_dir_t;

typedef struct {
    uint16_t ctrl_idx;
    uint8_t ch_idx;
} csi_dma_ch_desc_t;

 typedef struct {
     uint16_t dev_tag;
     uint16_t  ctrl_idx;
     const csi_dma_ch_desc_t *ch_list;
 } csi_dma_ch_spt_list_t;

typedef struct {
    uint16_t ctrl_idx;
    uint64_t ch_bit_info;
} csi_dma_ch_bit_desc_t;


typedef struct {
    uint16_t ctrl_idx;
    uint8_t ch_num;
} csi_dma_ch_info_t;

typedef struct {
    uint16_t   parent_dev_id;
    uint16_t   dev_tag;
    const csi_dma_ch_bit_desc_t *ch_list;
} csi_dma_ch_bit_spt_list_t;

#define DMA_HANDSHAKE_NONE 0xff

typedef enum {
    DMA_HANDSHAKE_TYPE_RX = 0x0,
    DMA_HANDSHAKE_TYPE_TX = 0x1,
} csi_dma_handshake_type_t;

typedef struct  {
    uint16_t   parent_dev_id;
    uint16_t   dev_tag;
    uint8_t   rx_hs;
    uint8_t   tx_hs;
} csi_dma_handshake_ctrl_t;

typedef struct  {
    uint16_t   ctrl_idx;
    const csi_dma_handshake_ctrl_t* handshake_ctrl_list;
} csi_dma_handshake_list_t;

typedef struct {
     void*  cpu_base_addr;
     void*  dma_base_addr;
     size_t mem_size;
} csi_dma_mem_desc_t;

typedef struct  {
   uint16_t  ctrl_idx;
   const csi_dma_mem_desc_t *mem_list;
} csi_dma_mem_list_t;

typedef enum {
    DMA_LINK_LIST_STOP     = 0,
    DMA_LINK_LIST_RUNNING,
    DMA_LINK_LIST_READY,
} csi_dma_link_list_state_t;

typedef enum {
    DMA_CYCLIC_STOP        = 0,
    DMA_CYCLIC_RUNNING,
    DMA_CYCLIC_READY,
} csi_dma_cyclic_state_t;

typedef struct {
    void                        *srcaddr;
    void                        *dstaddr;
    uint32_t                    length;
} csi_dma_link_list_item_t;

typedef struct {
    csi_dma_addr_inc_t          src_inc;        ///< source address increment
    csi_dma_addr_inc_t          dst_inc;        ///< destination address increment
    csi_dma_data_width_t        src_tw;         ///< source transfer width in byte
    csi_dma_data_width_t        dst_tw;         ///< destination transfer width in byte
    csi_dma_trans_dir_t         trans_dir;      ///< transfer direction
    uint16_t                    handshake;      ///< handshake id
    uint16_t                    group_len;      ///< group transaction length (unit: bytes)
    uint8_t                     src_reload_en;  ///< 1:dma enable src addr auto reload, 0:disable
    uint8_t                     dst_reload_en;  ///< 1:dma enable dst addr auto reload, 0:disable
    uint8_t                     half_int_en;    ///< 1:dma enable half interrupt, 0: disable
    uint8_t                     lli_src_en;     ///< 1:dma enable llp, 0 disable
    uint8_t                     lli_dst_en;     ///< 1:dma enable llp, 0 disable
    uint8_t                     link_list_en;     ///< 1:dma enable link list mode, 0: disable
    void                        *lli_buf;         ///< link list config
} csi_dma_ch_config_t;


typedef struct {
    csi_dma_addr_inc_t          src_inc;        ///< source address increment
    csi_dma_addr_inc_t          dst_inc;        ///< destination address increment
    csi_dma_data_width_t        src_tw;         ///< source transfer width in byte
    csi_dma_data_width_t        dst_tw;         ///< destination transfer width in byte
    uint16_t                    group_len;      ///< group transaction length (unit: bytes)
    uint8_t                     lli_src_en;     ///< 1:dma enable llp, 0 disable
    uint8_t                     lli_dst_en;     ///< 1:dma enable llp, 0 disable
} csi_dma_lli_config_t;

#ifndef DMA_LLI_SIZE
#define DMA_LLI_SIZE 64
#endif

#define DEFINE_DESC_BUF(buf_name, num) uint8_t buf_name[num * DMA_LLI_SIZE] __attribute__((aligned(64)));

typedef struct csi_dma_ch csi_dma_ch_t;

struct csi_dma_ch {
    void                *parent;
    int8_t              ctrl_id;
    int8_t              ch_id;
    void                (*callback)(csi_dma_ch_t *dma_ch, csi_dma_event_t event, void *arg);
    void                *arg;
    uint32_t            lli_num;                    //lli buffer len
    uint32_t            lli_count;                  //lli data count
    int32_t             lli_w_p;                    //write position
    int32_t             lli_r_p;                    //read position
    void                *lli;                       //lli buffer
    uint32_t            lli_loop_buf0;              //lli loop data
    uint32_t            lli_loop_buf1;              //lli loop data
    uint8_t             lli_loop[DMA_LLI_SIZE];     //lli loop handle
    int16_t             etb_ch_id;
    csi_dma_trans_dir_t trans_dir;
    csi_dma_link_list_state_t link_list_state;
    slist_t             next;
};

typedef struct {
    csi_dev_t           dev;
    slist_t             head;
    uint64_t            alloc_status;
    uint32_t            ch_num;
    void                *priv;
} csi_dma_t;

/**
  \brief       Init dma controller
  \param[in]   dma     the dma controller operate handle
  \param[in]   ctrl_id  the dma controller id
  \return      csi error code
*/
csi_error_t csi_dma_init(csi_dma_t *dma, int8_t ctrl_id);

/**
  \brief       Uninit dma controller
  \param[in]   dma     the dma controller operate handle
  \return      none
*/
void csi_dma_uninit(csi_dma_t *dma);

/**
  \brief       Alloc a dma channel
  \param[in]   dma_ch   the dma channel operate handle
  \param[in]   ch_id    the channel id  of dma; when set -1, means auto alloc
  \param[in]   ctrl_id  the dma controller id; when set -1, means auto alloc
  \return      csi error code
*/
csi_error_t csi_dma_ch_alloc(csi_dma_ch_t *dma_ch, int8_t ch_id, int8_t ctrl_id);

/**
  \brief       Free a dma channel
  \param[in]   dma_ch   the dma channel operate handle
  \return      none
*/
void csi_dma_ch_free(csi_dma_ch_t *dma_ch);

/**
  \brief       Config a dma channel
  \param[in]   dma_ch   the dma channel operate handle
  \param[in]   config   the config structure for dma channel
  \return      csi error code
*/
csi_error_t csi_dma_ch_config(csi_dma_ch_t *dma_ch, csi_dma_ch_config_t *config);

/**
  \brief       Start a dma channel
  \param[in]   dma_ch       the dma channel operate handle
  \param[in]   psrcaddr     transfer source address
  \param[in]   pdstaddr     transfer destination address
  \param[in]   length       transfer length (unit: bytes), if set data_width is 16, the length should be the multiple of 2, and
                            if set data_width is 32, the length should be the multiple of 4
  \return      none
*/
void csi_dma_ch_start(csi_dma_ch_t *dma_ch, void *srcaddr, void *dstaddr, uint32_t length);

/**
  \brief       Stop a dma channel
  \param[in]   dma_ch       the dma channel operate handle
  \return      none
*/
void csi_dma_ch_stop(csi_dma_ch_t *dma_ch);

/**
  \brief       add dma lli
  \param[in]   dma_ch       the dma channel operate handle
  \param[in]   config       lli config
  \param[in]   item         lli
  \return      csi error code
*/
csi_error_t csi_dma_add_link_list_item(csi_dma_ch_t *dma_ch, csi_dma_lli_config_t *config, csi_dma_link_list_item_t *item);

/**
  \brief       Attach the callback handler to DMA channel
  \param[in]   dma_ch     operate handle.
  \param[in]   callback   callback function
  \param[in]   arg        user can define it by himself as callback's param
  \return      error code
*/
csi_error_t csi_dma_ch_attach_callback(csi_dma_ch_t *dma_ch, void *callback, void *arg);

/**
  \brief       detach the callback handler
  \param[in]   uart  operate handle.
*/
void        csi_dma_ch_detach_callback(csi_dma_ch_t *dma_ch);

/**
  \brief       enable dma power manage
  \param[in]   dma  dma handle to operate.
  \return      error code
*/
csi_error_t csi_dma_enable_pm(csi_dma_t *dma);

/**
  \brief       disable dma power manage
  \param[in]   dma  dma handle to operate.
*/
void        csi_dma_disable_pm(csi_dma_t *dma);

#ifdef __cplusplus
}
#endif

#endif /* _CSI_DMA_H_ */
