
#ifndef __DMA_H
# define __DMA_H

typedef struct DMAChannel {

 volatile unsigned int m_CHCR __attribute__(   (  aligned( 16 )  )   );
 volatile unsigned int m_MADR __attribute__(   (  aligned( 16 )  )   );
 volatile unsigned int m_QWC  __attribute__(   (  aligned( 16 )  )   );
 volatile unsigned int m_TADR __attribute__(   (  aligned( 16 )  )   );
 volatile unsigned int m_ASR0 __attribute__(   (  aligned( 16 )  )   );
 volatile unsigned int m_ASR1 __attribute__(   (  aligned( 16 )  )   );

} DMAChannel;

typedef struct DMACRegs {

 volatile unsigned int m_CTRL  __attribute__(   (  aligned( 16 )  )   );
 volatile unsigned int m_STAT  __attribute__(   (  aligned( 16 )  )   );
 volatile unsigned int m_PCR   __attribute__(   (  aligned( 16 )  )   );
 volatile unsigned int m_SQWC  __attribute__(   (  aligned( 16 )  )   );
 volatile unsigned int m_RBSR  __attribute__(   (  aligned( 16 )  )   );
 volatile unsigned int m_RBOR  __attribute__(   (  aligned( 16 )  )   );
 volatile unsigned int m_STADR __attribute__(   (  aligned( 16 )  )   );

} DMACRegs;

# define DMAC (  ( volatile DMACRegs* )0x1000E000  )

# define DMATAG_ID_REFE 0x0
# define DMATAG_ID_CNT  0x1
# define DMATAG_ID_NEXT 0x2
# define DMATAG_ID_REF  0x3
# define DMATAG_ID_REFS 0x4
# define DMATAG_ID_CALL 0x5
# define DMATAG_ID_RET  0x6
# define DMATAG_ID_END  0x7

typedef struct DMATag {

 struct {

  unsigned long QWC  : 16 __attribute__ (  ( packed )  );
  unsigned long m_Pad: 10 __attribute__ (  ( packed )  );
  unsigned long PCE  :  2 __attribute__ (  ( packed )  );
  unsigned long ID   :  3 __attribute__ (  ( packed )  );
  unsigned long IRQ  :  1 __attribute__ (  ( packed )  );
  unsigned long ADDR : 31 __attribute__ (  ( packed )  );
  unsigned long SPR  :  1 __attribute__ (  ( packed )  );

 } __attribute__ (  ( packed )  );

 unsigned long m_Value __attribute__ (  ( packed )  );

} DMATag __attribute__(   (  aligned( 16 )  )   );

# define DMA_TAG( QWC, PCE, ID, IRQ, ADDR, SPR ) 	(                      		\
 ( (unsigned long)(               QWC  )<< 0 ) | ( (unsigned long)( PCE )<<26 )| 	\
 ( (unsigned long)(               ID   )<<28 ) | ( (unsigned long)( IRQ )<<31 )| 	\
 ( (unsigned long)( (unsigned int)ADDR )<<32 ) | ( (unsigned long)( SPR )<<63 )  	\
							)

# define DMA_SET_CTRL( A, B, C, D, E, F )                                                        \
 ( unsigned int )( A ) << 0 | ( unsigned int )( B ) << 1 | \
 ( unsigned int )( C ) << 2 | ( unsigned int )( D ) << 4 | \
 ( unsigned int )( E ) << 6 | ( unsigned int )( F ) << 8

# define D_CTRL_RELE_OFF 0
# define D_CTRL_RELE_ON  1

# define D_CTRL_MFD_OFF 0
# define D_CTRL_MFD_RES 1
# define D_CTRL_MFD_VIF 2
# define D_CTRL_MFD_GIF 3

# define D_CTRL_STS_UNSPEC 0
# define D_CTRL_STS_SIF    1
# define D_CTRL_STS_SPR    2
# define D_CTRL_STS_IPU    3

# define D_CTRL_STD_OFF 0
# define D_CTRL_STD_VIF 1
# define D_CTRL_STD_GIF 2
# define D_CTRL_STD_SIF 3

# define D_CTRL_RCYC_8   0
# define D_CTRL_RCYC_16  1
# define D_CTRL_RCYC_32  2
# define D_CTRL_RCYC_64  3
# define D_CTRL_RCYC_128 4
# define D_CTRL_RCYC_256 5

# define DMAC_VIF0      (  ( volatile DMAChannel* )0x10008000  )
# define DMAC_VIF1      (  ( volatile DMAChannel* )0x10009000  )
# define DMAC_GIF       (  ( volatile DMAChannel* )0x1000A000  )
# define DMAC_FROM_IPU  (  ( volatile DMAChannel* )0x1000B000  )
# define DMAC_TO_IPU    (  ( volatile DMAChannel* )0x1000B400  )
# define DMAC_FROM_SIF0 (  ( volatile DMAChannel* )0x1000C000  )
# define DMAC_TO_SIF1   (  ( volatile DMAChannel* )0x1000C400  )
# define DMAC_SIF2      (  ( volatile DMAChannel* )0x1000C800  )
# define DMAC_FROM_SPR  (  ( volatile DMAChannel* )0x1000D000  )
# define DMAC_TO_SPR    (  ( volatile DMAChannel* )0x1000D400  )

# define DMAC_I_VIF0      0
# define DMAC_I_VIF1      1
# define DMAC_I_GIF       2
# define DMAC_I_FROM_IPU  3
# define DMAC_I_TO_IPU    4
# define DMAC_I_FROM_SIF0 5
# define DMAC_I_TO_SIF1   6
# define DMAC_I_SIF2      7
# define DMAC_I_FROM_SPR  8
# define DMAC_I_TO_SPR    9

# ifdef __cplusplus
extern "C" {
# endif  /* __cplusplus */

static void inline DMA_Wait ( volatile DMAChannel* apChan ) {
 while ( apChan -> m_CHCR & 0x00000100 );
}  /* end DMA_WaitGIF */

static void inline DMA_Send ( volatile DMAChannel* apChan, void* apAddr, unsigned int aQWC ) {
 __asm__ __volatile__(
  ".set noreorder\n\t"
  ".set noat\n\t"
  "1:\n\t"
  "lw   $at, 0(%0)\n\t"
  "nop\n\t"
  "nop\n\t"
  "andi $at, $at, 0x0100\n\t"
  "nop\n\t"
  "bne  $at, $zero, 1b\n\t"
  "li   $at, 0x0101\n\t"
  "sw   %1, 16(%0)\n\t"
  "sw   %2, 32(%0)\n\t"
  "sw   $at, 0(%0)\n\t"
  ".set at\n\t"
  ".set reorder\n\t"
  ::"r"( apChan ), "r"( apAddr ), "r"( aQWC ) : "at"
 );
}  /* end DMA_Send */

static void inline DMA_SendA ( volatile DMAChannel* apChan, void* apAddr, unsigned int aQWC ) {
 __asm__ __volatile__(
  ".set noreorder\n\t"
  ".set noat\n\t"
  "li   $at, 0x0101\n\t"
  "sw   %1, 16(%0)\n\t"
  "sw   %2, 32(%0)\n\t"
  "sw   $at, 0(%0)\n\t"
  ".set at\n\t"
  ".set reorder\n\t"
  ::"r"( apChan ), "r"( apAddr ), "r"( aQWC ) : "at"
 );
}  /* end DMA_SendA */

static void inline DMA_SendChain ( volatile DMAChannel* apChan, void* apAddr ) {
 __asm__ __volatile__(
  ".set noreorder\n\t"
  ".set noat\n\t"
  "1:\n\t"
  "lw   $at, 0(%0)\n\t"
  "nop\n\t"
  "nop\n\t"
  "andi $at, $at, 0x0100\n\t"
  "nop\n\t"
  "bne  $at, $zero, 1b\n\t"
  "li   $at, 0x0105\n\t"
  "sw   $zero, 32(%0)\n\t"
  "sw   %1,    48(%0)\n\t"
  "sw   $at,    0(%0)\n\t"
  ".set at\n\t"
  ".set reorder\n\t"
  ::"r"( apChan ), "r"( apAddr ) : "at"
 );
}  /* end DMA_SendChain */

static void inline DMA_SendChainA ( volatile DMAChannel* apChan, void* apAddr ) {
 __asm__ __volatile__(
  ".set noreorder\n\t"
  ".set noat\n\t"
  "li   $at, 0x0105\n\t"
  "sw   $zero, 32(%0)\n\t"
  "sw   %1,    48(%0)\n\t"
  "sw   $at,    0(%0)\n\t"
  ".set at\n\t"
  ".set reorder\n\t"
  ::"r"( apChan ), "r"( apAddr ) : "at"
 );
}  /* end DMA_SendChainA */

static void inline DMA_SendChainT ( volatile DMAChannel* apChan, void* apAddr ) {
 __asm__ __volatile__(
  ".set noreorder\n\t"
  ".set noat\n\t"
  "1:\n\t"
  "lw   $at, 0(%0)\n\t"
  "nop\n\t"
  "nop\n\t"
  "andi $at, $at, 0x0100\n\t"
  "nop\n\t"
  "bne  $at, $zero, 1b\n\t"
  "li   $at, 0x0145\n\t"
  "sw   $zero, 32(%0)\n\t"
  "sw   %1,    48(%0)\n\t"
  "sw   $at,    0(%0)\n\t"
  ".set at\n\t"
  ".set reorder\n\t"
  ::"r"( apChan ), "r"( apAddr ) : "at"
 );
}  /* end DMA_SendChainT */

static void inline DMA_RecvA ( volatile DMAChannel* apChan, void* apAddr, unsigned int aQWC ) {
 __asm__ __volatile__(
  ".set noreorder\n\t"
  ".set noat\n\t"
  "li   $at, 0x0100\n\t"
  "sw   %1, 16(%0)\n\t"
  "sw   %2, 32(%0)\n\t"
  "sw   $at, 0(%0)\n\t"
  ".set at\n\t"
  ".set reorder\n\t"
  ::"r"( apChan ), "r"( apAddr ), "r"( aQWC ) : "at"
 );
}  /* end DMA_RecvA */

static void inline DMA_RecvS ( volatile DMAChannel* apChan, void* apDst, void* apSrc, unsigned int aQWC ) {
 __asm__ __volatile__(
  ".set noreorder\n\t"
  ".set noat\n\t"
  "li   $at, 0x0101\n\t"
  "sw   %2,  16(%0)\n\t"
  "sw   %3,  32(%0)\n\t"
  "sw   %1, 128(%0)\n\t"
  "sw   $at,  0(%0)\n\t"
  ".set at\n\t"
  ".set reorder\n\t"
  ::"r"( apChan ), "r"( apDst ), "r"( apSrc ), "r"( aQWC ) : "at"
 );

}  /* end DMA_RecvS */

static void inline DMA_SendS ( volatile DMAChannel* apChan, void* apDst, void* apSrc, unsigned int aQWC ) {
 __asm__ __volatile__(
  ".set noreorder\n\t"
  ".set noat\n\t"
  "li   $at, 0x0100\n\t"
  "sw   %1,  16(%0)\n\t"
  "sw   %3,  32(%0)\n\t"
  "sw   %2, 128(%0)\n\t"
  "sw   $at,  0(%0)\n\t"
  ".set at\n\t"
  ".set reorder\n\t"
  ::"r"( apChan ), "r"( apDst ), "r"( apSrc ), "r"( aQWC ) : "at"
 );
}  /* end DMA_SendS */

void DMA_Stop ( volatile DMAChannel* );

# ifdef __cplusplus
}
# endif  /* __cplusplus */
#endif  /* __DMA_H */

