/*******************************************************************************
 * Copyright (c) 2022 Think Silicon S.A.
 *
   Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this header file and/or associated documentation files to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies of the
 * Materials, and to permit persons to whom the Materials are furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
 * NEMAGFX API. THE UNMODIFIED, NORMATIVE VERSIONS OF THINK-SILICON NEMAGFX
 * SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT:
 *   https://think-silicon.com/products/software/nemagfx-api
 *
 *  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 noninfringement. In no event shall
 *  Think Silicon S.A. 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.
 ******************************************************************************/


#ifndef NEMA_CMDLIST_H__
#define NEMA_CMDLIST_H__

#include "nema_sys_defs.h"
#include "nema_hal.h"

#ifdef __cplusplus
extern "C" {
#endif

#define CL_NOP      0x010000U
#define CL_PUSH     0x020000U
#define CL_RETURN   0x040000U
#define CL_ABORT    0x080000U

#define CL_BATCH_SHIFT      12
#define CL_BATCH_LOOP       0x8000

#define SUBMISSION_ID_MASK 0xffffff

#define CL_ALIGNMENT_MASK  (0x00000007U) // CL buffer must be 8 byte aligned

//---------------------------------------------------------------------------
typedef struct nema_cmdlist_t_ {
    nema_buffer_t bo;
    int size;                       /**< Number of entries in the command list */
    int offset;                     /**< Points to the next address to write */
    uint32_t flags;                 /**< Flags */
    int32_t  submission_id;
    struct nema_cmdlist_t_ *next;   /**< Points to next command list */
    struct nema_cmdlist_t_ *root;   /**< Points to the head of the list */
} nema_cmdlist_t;

/** \brief Create a new Command List into a preallocated space
 *
 * \param addr_virt  Command List's address (preallocated)
 * \param size_bytes Command List's size in bytes
 * \return The instance of the new Command List
 *
 */
nema_cmdlist_t nema_cl_create_prealloc(nema_buffer_t *bo);

/** \brief Create a new, non expandable Command List of specific size
 *
 * \param size_bytes Command List's size in bytes
 * \return The instance of the new Command List
 *
 */
nema_cmdlist_t nema_cl_create_sized(int size_bytes);

/** \brief Create a new expandable Command List
 *
 * \return The instance of the new Command List
 *
 */
nema_cmdlist_t nema_cl_create(void);

/** \brief Destroy/Free a Command List
 *
 * \param cl Pointer to the Command List
 *
 */
void nema_cl_destroy(nema_cmdlist_t *cl);

/** \brief Reset position of next command to be written to the beginning. Doesn't clear the List's contents.
 *
 * \param cl Pointer to the Command List
 *
 */
void nema_cl_rewind(nema_cmdlist_t *cl);

/** \brief Define in which Command List each subsequent commands are going to be inserted.
 *
 * \param cl Pointer to the Command List
 *
 */
void nema_cl_bind(nema_cmdlist_t *cl);

/** \brief Define in which Command List each subsequent commands are going to be inserted.
 *         Bind this command list as Circular. It never gets full, it never expands,
 *         it may get implicitly submitted, it cannot be reused. No other CL should be submitted
 *         while a circular CL is bound
 *
 * \param cl Pointer to the Command List
 *
 */
void nema_cl_bind_circular(nema_cmdlist_t *cl);

/** \brief Unbind current bound Command List, if any.
 *
 *
 */
void nema_cl_unbind(void);


/** \brief Get bound Command List
 *
 * \return Pointer to the bound Command List
 *
 */
nema_cmdlist_t *nema_cl_get_bound(void);

/** \private */
void nema_cl_submit_no_irq(nema_cmdlist_t *cl);

/** \brief Enqueue Command List to the Ring Buffer for execution
 *
 * \param cl Pointer to the Command List
 *
 */
void nema_cl_submit(nema_cmdlist_t *cl);

/** \brief Wait for Command List to finish
 *
 * \param cl Pointer to the Command List
 * \return 0 if no error has occurred
 *
 */
int nema_cl_wait(nema_cmdlist_t *cl);

/** \brief Add a command to the bound Command List
 *
 * \param reg Hardware register to be written
 * \param data Data to be written
 *
 */
void nema_cl_add_cmd(uint32_t reg, uint32_t data);

/** \brief Add multiple commands to the bound Command List
 *
 * \param cmd_no Numbers of commands to add
 * \param cmd Pointer to the commands to be added
 * \return 0 if no error has occurred
 *
 */
int nema_cl_add_multiple_cmds(int cmd_no, uint32_t *cmd);

/** private */
uint32_t * nema_cl_get_space(int cmd_no);

/** \brief Branch from the bound Command List to a different one. Return is implied.
 *
 * \param cl Pointer to the Command List to branch to
 *
 */
void nema_cl_branch(nema_cmdlist_t *cl);

/** \brief Jump from the bound Command List to a different one. No return is implied.
 *
 * \param cl Pointer to the Command List to jump to
 *
 */
void nema_cl_jump(nema_cmdlist_t *cl);

/** \brief Add an explicit return command to the bound Command List
 *
 *
 */
void nema_cl_return(void);

/** \brief Returns positive number if the Command List is almost full, otherwise returns 0.
 *
 * \param cl Pointer to the Command List
 *
 */
int nema_cl_almost_full(nema_cmdlist_t *cl);

/** \brief Check if there is enough space or expansion can be performed for
 * required commands.
 *
 * \param cmd_no Numbers of commands to be checked if they fit
 * \reurn zero is commands fit or expansion xan be performed else return negative
 */
int nema_cl_enough_space(int cmd_no);

#ifdef __cplusplus
}
#endif

#endif
