/**
 * @brief       SSP Manager
 * @author      AXELL CORPORATION
 * @description SSP Manager Layer
 * @note        none 
 * @history     2017_02_22  初版
 * @history     2017_10_26  Ver2.0
 * @history     2025_03_06  [SDK3.7] SSPで1ワードの送信ができない不具合を修正 (#5633)
 * @history     2025_03_06  [SDK3.7] SSPライブラリにSPIモードでの送受信アクセス機能を追加 (#5662)
 * @history     2025_03_06  [SDK3.7] SSPライブラリにDMAによる送受信関数を追加 (#5780)
*/
/* DOM-IGNORE-BEGIN */
/*
 * This program was created by Axell Corporation.
 * Copyright (C) 2017-2025 Axell Corporation, all rights reserved.
 */
/* DOM-IGNORE-END */

#include "AG903_errno.h"
#include "AG903_intno.h"
#include "ssp/sspmgr.h"
#include "ssp/sspprm.h"
#include "int/intmgr.h"
#include "dmac/dmacprm.h"
#include "sys/sscprm.h"


#define AG903_SSP_TXQUE_ARY_LEN (AG903_SSP_TXQUE_NUM+1)
#define AG903_SSP_RXQUE_ARY_LEN (AG903_SSP_RXQUE_NUM+1)
#if AG903_SSP_TXQUE_ARY_LEN <= 1
#error AG903_SSP_TXQUE_ARY_LEN
#endif
#if AG903_SSP_RXQUE_ARY_LEN <= 1
#error AG903_SSP_RXQUE_ARY_LEN
#endif


/** Data Types **/
typedef void (*AG903_SSPMgrIntHdr)(void);

typedef struct _AG903_SSPMgrQue{
	uint8_t*	buf;			/** バッファ */
	uint32_t	size;			/** サイズ */
} AG903_SSPMgrQue;

typedef struct _AG903_SSPMgrRxQue{
	AG903_SSPMgrQue	que[AG903_SSP_RXQUE_ARY_LEN]; /** Que */
	uint32_t	wp;				/** Write Pointer */
	uint32_t	rp;				/** Read Pointer */
} AG903_SSPMgrRxQue;

typedef struct _AG903_SSPMgrTxQue{
	AG903_SSPMgrQue	que[AG903_SSP_TXQUE_ARY_LEN]; /** Que */
	uint32_t	wp;				/** Write Pointer */
	uint32_t	rp;				/** Read Pointer */
} AG903_SSPMgrTxQue;

typedef struct _AG903_SSPMgrTransferStat{
	uint8_t*	buf;			/** バッファ */
	uint8_t*	bufp;			/** バッファポインタ */
	uint32_t	size;			/** サイズ */
	uint32_t	cnt;			/** カウンタ */
} AG903_SSPMgrTransferStat;

typedef struct _AG903_SSPMgrChStat{
	AG903_SSPMgrTransferStat tx;	/** 送信状態 */
	AG903_SSPMgrTransferStat rx;	/** 受信状態 */
	int32_t		hdrid;			/** ハンドラID */
	uint8_t		format;			/** 伝送フォーマット */
	uint8_t		wordlen;		/** Word長 */
	uint8_t		slave;			/** SLAVE動作 */
	uint8_t		tx_thod;		/** 送信FIFOしきい値 */
	uint8_t		rx_thod;		/** 受信FIFOしきい値 */
	uint8_t		pio;			/** PIO動作モード */
	uint8_t		dma;			/** DMA動作モード */
	uint8_t		reserve[1];		/** 予約 */
} AG903_SSPMgrChStat;

typedef struct _AG903_SSPMgrRegStat{
	AG903_SSPPrmIntCtrl	ictrl;
	AG903_SSPPrmCtrl2	ctrl2;
} AG903_SSPMgrRegStat;

typedef struct _AG903_SSPMgrDmaStat{
	uint8_t		attach;			/** SSPへのDMA取り付けフラグ */
	uint8_t		stat;			/** DMAの動作状態 */
	uint8_t		dma_ch;			/** DMAチェンネル */
	uint8_t		dma_if;			/** DMA要求のインターフェース */
	uint8_t		ctrlwidth;		/** データ型サイズの設定値 */
	uint8_t		ctrlbeat;		/** DMA要求あたりビート数の設定値 */
	uint32_t    txrxdr;			/** SSPのSSPTxRxDRレジスタのアドレス */
	int32_t		hdrid;			/** ハンドラID */
} AG903_SSPMgrDmaStat;

typedef struct _AG903_SSPMgrHandleStat{
	AG903_SSPMgrClbk	clbk;	/** コールバック */
	uint8_t		lock;			/** ロック */
	uint8_t		reserve[3];		/** 予約 */
} AG903_SSPMgrHandleStat;


static AG903_SSPMgrChStat		SspChStat[AG903_SSP_CH_NUM];
static AG903_SSPMgrRegStat		SspRegStat[AG903_SSP_CH_NUM];
static AG903_SSPMgrDmaStat		SspDmaStatTx[AG903_SSP_CH_NUM];
static AG903_SSPMgrDmaStat		SspDmaStatRx[AG903_SSP_CH_NUM];
static AG903_SSPMgrSpiParam		SspSpiParam[AG903_SSP_CH_NUM];
static AG903_SSPMgrHandleStat	SspHandleStat[AG903_SSP_CH_NUM];
static AG903_SSPMgrRxQue		SspRxQue[AG903_SSP_CH_NUM];
static AG903_SSPMgrTxQue		SspTxQue[AG903_SSP_CH_NUM];

/* SspChStat[].pioの定義 (PIOのとき送受信の方向を示す) */
#define AG903_SSP_ENABLE_SEND		(1<<0)  /** SetSendBufによる送信中のバッファ残あり */
#define AG903_SSP_ENABLE_RECEIVE	(1<<1)  /** SetReceiveBufによる受信中のバッファ残あり */

/* SspChStat[].dmaの定義 */
#define AG903_SSP_DMA_SEND			(1<<0)  /** 送信にDMAを使用 */
#define AG903_SSP_DMA_RECEIVE		(1<<1)  /** 受信にDMAを使用 */

/* SspDmaStat[].dma_dirの定義 */
/** DMA転送方向 */	
#define AG903_SSP_DMA_DIR_TX		(1<<0)	/** 内部DMAを送信方向に設定 */
#define AG903_SSP_DMA_DIR_RX		(1<<1)	/** 内部DMAを受信方向に設定 */

/* SspDmaStat[].statの定義 */
#define AG903_SSP_DMA_STAT_STOP		0  /** DMAは停止中 */
#define AG903_SSP_DMA_STAT_XMIT		1  /** DMAは転送中 */

static void SSPMgr_Init(uint8_t ch);
static _Bool SSPMgr_CheckIdle(uint8_t ch);
static _Bool SSPMgr_CheckBusy(uint8_t ch);
static int32_t SSPMgr_CheckHandle(AG903_SSPMgrHandle* handle, uint8_t* ch);
static int32_t SSPMgr_SetTxData(uint8_t ch, _Bool que);
static int32_t SSPMgr_GetRxData(uint8_t ch, _Bool que);
static uint8_t* SSPMgr_SetTxFifo(uint8_t ch, uint8_t* buf, uint32_t size, uint32_t* setnum);
static uint8_t* SSPMgr_GetRxFifo(uint8_t ch, uint8_t* buf, uint32_t size, uint32_t* getnum);
static int32_t SSPMgr_DisableTransfer(uint8_t ch);
static void SSPMgr_Inthdr0(void);
static void SSPMgr_Inthdr1(void);
static void SSPMgr_Inthdr2(void);
static void SSPMgr_Inthdr3(void);
static void SSPMgr_IntProcess(uint8_t ch);
static void SSPMgr_IntSend(uint8_t ch, uint32_t* event);
static void SSPMgr_IntReceive(uint8_t ch, uint32_t* event);
static int32_t SSPMgr_SetTxQue(uint8_t ch, AG903_SSPMgrQue* que);
static int32_t SSPMgr_GetTxQue(uint8_t ch, AG903_SSPMgrQue* que);
static uint32_t SSPMgr_GetNextTxQuePoint(uint32_t current);
static void SSPMgr_GetTxQueEntry(uint8_t ch, uint32_t* entry);
static void SSPMgr_ClearTxQue(uint8_t ch);
static int32_t SSPMgr_SetRxQue(uint8_t ch, AG903_SSPMgrQue* que);
static int32_t SSPMgr_GetRxQue(uint8_t ch, AG903_SSPMgrQue* que);
static uint32_t SSPMgr_GetNextRxQuePoint(uint32_t current);
static void SSPMgr_GetRxQueEntry(uint8_t ch, uint32_t* entry);
static void SSPMgr_ClearRxQue(uint8_t ch);

static int32_t SSPMgr_WriteTxFifoBest(uint8_t ch, const void* buf, uint32_t num);
static int32_t SSPMgr_ReadRxFifoBest(uint8_t ch, void* buf, uint32_t num);
static int32_t SSPMgr_SendDma(uint8_t ch, const void* buf, uint32_t num);
static int32_t SSPMgr_ReceiveDma(uint8_t ch, void* buf, uint32_t num);
static int32_t SSPMgr_DisableDma(uint8_t ch, uint32_t dma_dir);
static int32_t SSPMgr_SpiSendTemplate(uint8_t ch, const void* buf, uint32_t num, uint8_t dma_tx_enable);
static int32_t SSPMgr_SpiReceiveTemplate(uint8_t ch, void* buf, uint32_t num, uint8_t dma_rx_enable);
static int32_t SSPMgr_SpiSendReceiveTemplate(uint8_t ch, const void *tx_buf, uint32_t tx_num, void* rx_buf, uint32_t rx_num, uint8_t dma_tx_enable, uint8_t dma_rx_enable);
static void SSPMgr_SetDmaInterface(uint8_t ssp_ch, uint8_t dma_if, uint8_t dir);
static int32_t SSPMgr_AttachDma(uint8_t ch, uint8_t dma_dir, uint8_t dma_ch, uint8_t dma_if);
static int32_t SSPMgr_DetachDma(uint8_t ch, uint8_t dma_dir);
static int32_t SSPMgr_SetDmaBeat(uint8_t ch, uint8_t dma_dir, uint8_t beat);
static void SSPMgr_IntHdr0TxDma(void);
static void SSPMgr_IntHdr0RxDma(void);
static void SSPMgr_IntHdr1TxDma(void);
static void SSPMgr_IntHdr1RxDma(void);
static void SSPMgr_IntHdr2TxDma(void);
static void SSPMgr_IntHdr2RxDma(void);
static void SSPMgr_IntHdr3TxDma(void);
static void SSPMgr_IntHdr3RxDma(void);
static void SSPMgr_IntProcessDma(uint8_t ch, uint8_t dma_dir);
static int32_t SSPMgr_IsSpiFlashLike(uint8_t ch);

static const AG903_SSPMgrIntHdr SspIntHdr[AG903_SSP_CH_NUM] =
{ SSPMgr_Inthdr0, SSPMgr_Inthdr1, SSPMgr_Inthdr2, SSPMgr_Inthdr3 };

/**
 * @brief           SSPモジュール初期化
 * @param           ch [in] SSPチャネル番号（0〜3）
 * @return          初期化結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常、デフォルト設定値異常
 * @retval          -AG903_EFAULT 処理失敗（割込みハンドラ登録失敗）
 * @description     内部状態の初期化を行います。システム起動時にCALLして下さい。
 * @note            この関数はINTMgrを初期化してから呼び出して下さい。
 * @note            ライブラリの使用中にアプリから割り込みをマスクしないで下さい。
*/
int32_t	AG903_SSPMgrInit(uint8_t ch)
{
	AG903_INTMgrHdrPrm inthdr;
	int32_t	 retval = AG903_ENONE;
	int32_t	 hdrid;
	
	if(AG903_SSP_CH_NUM <= ch) {
		return -AG903_EINVAL;
	}
	
	SSPMgr_Init(ch);		/* 内部状態初期化 */
	
	if(0 >= SspChStat[ch].hdrid) {
		inthdr.atr   = AG903_INT_HLNG;
		inthdr.intno = AG903_IRQ12_SSP0+ch;
		inthdr.func  = (void*)SspIntHdr[ch];
		hdrid = AG903_INTMgrSetHandler(&inthdr);
		if(0 >= hdrid) {
			return -AG903_EFAULT;
		}
		SspChStat[ch].hdrid = hdrid;
	}
	
	AG903_INTMgrEnableInt(AG903_IRQ12_SSP0+ch);
	
	return retval;
}

/**
 * @brief           SSPハンドル取得
 * @param           ch [in] SSPチャネル番号（0〜3）
 * @param           handle [out] 該当のSSPハンドル
 * @return          SSPハンドル取得結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中
 * @description     指定したCHのハンドルを取得します。
*/
int32_t	AG903_SSPMgrGetHandle(uint8_t ch, AG903_SSPMgrHandle** handle)
{
	int32_t	 retval = AG903_ENONE;

	if( (AG903_SSP_CH_NUM <= ch) ||
		(NULL == handle) ) {
		return -AG903_EINVAL;
	}
	if(true == SspHandleStat[ch].lock) {
		return -AG903_EBUSY;
	}
	
	SspHandleStat[ch].lock = true;
	
	(*handle) = (AG903_SSPMgrHandle*)&SspHandleStat[ch];
	
	return retval;
}

/**
 * @brief           SSPハンドル解放
 * @param           handle [in] SSPハンドル
 * @return          SSPハンドル解放結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中（解放不可）
 * @description     ハンドルを解放します。<p>動作中にハンドルが解放された場合はエラーを返します。
 * @note            必要に応じアプリでAG903_INTMgrDisableIntによりSSPの割り込みを無効にして下さい。
*/
int32_t	AG903_SSPMgrReleaseHandle(AG903_SSPMgrHandle* handle)
{
	int32_t	 retval = AG903_ENONE;
	_Bool	 idle;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	idle = SSPMgr_CheckIdle(ch);
	if(true != idle) {
		return -AG903_EBUSY;
	}
	
	SspHandleStat[ch].lock = false;
	
	return retval;
}

/**
 * @brief           コールバック登録
 * @param           handle [in] SSPハンドル
 * @param           clbk [in] コールバック関数ポインタ
 * @return          コールバック登録結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @description     コールバック関数を登録します。
 * @note            コールバック関数をNULLとして登録した場合は解除になります。
*/
int32_t	AG903_SSPMgrSetCallback(AG903_SSPMgrHandle* handle, AG903_SSPMgrClbk clbk)
{
	int32_t	 retval = AG903_ENONE;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	
	SspHandleStat[ch].clbk = clbk;
	
	return retval;
}

/**
 * @brief           SSPモード設定
 * @param           handle [in] SSPハンドル
 * @param           param [in] 設定パラメータ
 * @return          SSPモード設定結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中
 * @description     通信フォーマットをSSPに設定します。<p>
 *                  param に従い通信の各パラメータを設定します。
*/
int32_t	AG903_SSPMgrSetSspMode(AG903_SSPMgrHandle* handle, AG903_SSPMgrSspParam* param)
{
	AG903_SSPPrmCtrl ctrl = {0};
	int32_t	 retval = AG903_ENONE;
	_Bool	 idle;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if(NULL == param) {
		return -AG903_EINVAL;
	}
	if( (AG903_SSP_SDL_MAX < param->wordlen)   ||
		(AG903_SSP_SSPDIV_MIN > param->clkdiv) ||
		(param->clkdiv & 0x01) ){
		return -AG903_EINVAL;
	}
	idle = SSPMgr_CheckIdle(ch);
	if(true != idle) {
		return -AG903_EBUSY;
	}

	ctrl.format = AG903_SSP_FORMAT_SSP;
	if(true == param->slave) {
		ctrl.opm = AG903_SSP_OPM_SLAVE;
	}
	else {
		ctrl.opm = AG903_SSP_OPM_MASTER;
	}
	ctrl.sdl = param->wordlen-1;
	ctrl.sclk_div = (param->clkdiv>>1)-1;
		
	AG903_SSPPrmSetControl(ch, &ctrl);

	SspChStat[ch].format   = AG903_SSP_FORMAT_SSP;
	SspChStat[ch].wordlen  = param->wordlen;
	SspChStat[ch].slave    = param->slave;
	SspChStat[ch].tx_thod  = AG903_SSP_TXFIFO_THRESHOLD;
	SspChStat[ch].rx_thod  = AG903_SSP_RXFIFO_THRESHOLD;

	return retval;
}

/**
 * @brief           SPIモード設定
 * @param           handle [in] SSPハンドル
 * @param           param [in] 設定パラメータ
 * @return          SPIモード設定結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中
 * @description     通信フォーマットをSPIに設定します。<p>
 *                  param に従い通信の各パラメータを設定します。
*/
int32_t	AG903_SSPMgrSetSpiMode(AG903_SSPMgrHandle* handle, AG903_SSPMgrSpiParam* param)
{
	AG903_SSPPrmCtrl ctrl = {0};
	AG903_SSPPrmCtrl2 ctrl2;
	int32_t	 retval = AG903_ENONE;
	_Bool	 idle;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if(NULL == param) {
		return -AG903_EINVAL;
	}
	if( (AG903_SSP_SDL_MAX          < param->wordlen)   ||
		(AG903_SSP_SPIDIV_MIN       > param->clkdiv)    ||
		(0x01 & param->clkdiv) 	||
		(AG903_SSP_POL_TYPENUM      <= param->polarity) ||
		(AG903_SSP_FIRSTBIT_TYPENUM <= param->firstbit) ||
		(AG903_SSP_SPICLK_MODENUM   <= param->sclktype) ){
		return -AG903_EINVAL;
	}
	idle = SSPMgr_CheckIdle(ch);
	if(true != idle) {
		return -AG903_EBUSY;
	}

	ctrl.format = AG903_SSP_FORMAT_SPI;
	if (param->flash) {
		ctrl.spi_flash = 1;
	}
	if(AG903_SSP_FIRSTBIT_LSB == param->firstbit) {
		ctrl.lsb = 1;
	}
	if(AG903_SSP_POL_NEGATIVE == param->polarity) {
		ctrl.spi_fspo = 0; /* SPIのSPIFSPOはI2SのFSPOと論理が逆 */
	}
	if(true == param->slave) {
		ctrl.opm = AG903_SSP_OPM_SLAVE;
	}
	else {
		ctrl.opm = AG903_SSP_OPM_MASTER;
	}
	switch(param->sclktype) {
		case AG903_SSP_SPICLK_MODE0:
			ctrl.sclkpo = 0;
			ctrl.sclkph = 0;
			break;
		case AG903_SSP_SPICLK_MODE1:
			ctrl.sclkpo = 0;
			ctrl.sclkph = 1;
			break;
		case AG903_SSP_SPICLK_MODE2:
			ctrl.sclkpo = 1;
			ctrl.sclkph = 0;
			break;
		case AG903_SSP_SPICLK_MODE3:
			ctrl.sclkpo = 1;
			ctrl.sclkph = 1;
			break;
		default:
			/* 引数チェックしているのでここには来ない */
			break;
	}
	ctrl.sdl = param->wordlen-1;
	ctrl.sclk_div = (param->clkdiv>>1)-1;

	AG903_SSPPrmSetControl(ch, &ctrl);

	AG903_SSPPrmGetControl2(ch, &ctrl2);
	ctrl2.fsos = 0;
	ctrl2.fs   = (AG903_SSP_POL_NEGATIVE==param->polarity) ? 1 : 0; /* ネゲート */
	AG903_SSPPrmSetControl2(ch, &ctrl2);

	SspChStat[ch].format   = AG903_SSP_FORMAT_SPI;
	SspChStat[ch].wordlen  = param->wordlen;
	SspChStat[ch].slave    = param->slave;
	SspChStat[ch].tx_thod  = AG903_SSP_TXFIFO_THRESHOLD;
	SspChStat[ch].rx_thod  = AG903_SSP_RXFIFO_THRESHOLD;

	SspSpiParam[ch]        = *param;

	return retval;
}

/**
 * @brief           I2Sモード設定
 * @param           handle [in] SSPハンドル
 * @param           param [in] 設定パラメータ
 * @return          I2Sモード設定結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中
 * @description     通信フォーマットをI2Sに設定します。<p>
 *                  param に従い通信の各パラメータを設定します。
*/
int32_t	AG903_SSPMgrSetI2sMode(AG903_SSPMgrHandle* handle, AG903_SSPMgrI2sParam* param)
{
	AG903_SSPPrmCtrl ctrl = {0};
	int32_t	 retval = AG903_ENONE;
	_Bool	 idle;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if(NULL == param) {
		return -AG903_EINVAL;
	}
	if( (AG903_SSP_SDL_MAX          < param->wordlen)   ||
		(AG903_SSP_SSPDIV_MIN       > param->clkdiv)    ||
		(0x01 & param->clkdiv) 	||
		(AG903_SSP_POL_TYPENUM      <= param->polarity) ||
		(AG903_SSP_FIRSTBIT_TYPENUM <= param->firstbit) ||
		(AG903_SSP_JUSTIFIED_TYPENUM <= param->justified) ) {
		return -AG903_EINVAL;
	}
	idle = SSPMgr_CheckIdle(ch);
	if(true != idle) {
		return -AG903_EBUSY;
	}
	
	ctrl.format = AG903_SSP_FORMAT_I2S;
	ctrl.fsdist = 1;
	if(AG903_SSP_FIRSTBIT_LSB == param->firstbit) {
		ctrl.lsb = 1;
	}
	if(AG903_SSP_POL_NEGATIVE == param->polarity) {
		ctrl.fspo = 1;
	}
	if(AG903_SSP_RIGHT_JUSTIFIED == param->justified) {
		ctrl.fsjstfy = 1;
	}
	if(true == param->slave) {
		if(true == param->mono) {
			ctrl.opm = AG903_SSP_OPM_SLAVE;
		}
		else {
			ctrl.opm = AG903_SSP_OPM_SLAVE_STEREO;
		}
	}
	else {
		if(true == param->mono) {
			ctrl.opm = AG903_SSP_OPM_MASTER;
		}
		else {
			ctrl.opm = AG903_SSP_OPM_MASTER_STEREO;
		}
	}
	ctrl.sdl = param->wordlen-1;
	ctrl.pdl = param->padlen;
	ctrl.sclk_div = (param->clkdiv>>1)-1;

	AG903_SSPPrmSetControl(ch, &ctrl);

	SspChStat[ch].format   = AG903_SSP_FORMAT_I2S;
	SspChStat[ch].wordlen  = param->wordlen;
	SspChStat[ch].slave    = param->slave;
	SspChStat[ch].tx_thod  = AG903_SSP_TXFIFO_THRESHOLD;
	SspChStat[ch].rx_thod  = AG903_SSP_RXFIFO_THRESHOLD;

	return retval;
}

/**
 * @brief           S/PDIFモード設定
 * @param           handle [in] SSPハンドル
 * @param           param [in] 設定パラメータ
 * @return          S/PDIFモード設定結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中
 * @description     通信フォーマットをS/PDIFに設定します。
 *                  param に従い通信の各パラメータを設定します。
*/
int32_t	AG903_SSPMgrSetSpdifMode(AG903_SSPMgrHandle* handle, AG903_SSPMgrSpdifParam* param)
{
	AG903_SSPPrmCtrl ctrl = {0};
	int32_t	 retval = AG903_ENONE;
	_Bool	 idle;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if(NULL == param) {
		return -AG903_EINVAL;
	}
	if((16!=param->datalen)&&(20!=param->datalen)&&(24!=param->datalen)) {
		return -AG903_EINVAL;
	}
	idle = SSPMgr_CheckIdle(ch);
	if(true != idle) {
		return -AG903_EBUSY;
	}
	ctrl.format = AG903_SSP_FORMAT_SPDIF;
	if(false == param->validity) {
		ctrl.validity = 1;
	}
	ctrl.sdl = param->datalen-1;

	AG903_SSPPrmSetControl(ch, &ctrl);
	
	SspChStat[ch].format   = AG903_SSP_FORMAT_SPDIF;
	SspChStat[ch].wordlen  = param->datalen;
	SspChStat[ch].slave    = false;
	SspChStat[ch].tx_thod  = AG903_SSP_TXFIFO_THRESHOLD;
	SspChStat[ch].rx_thod  = AG903_SSP_RXFIFO_THRESHOLD;
	
	return retval;
}

/**
 * @brief           Channel Status Bit設定 (S/PDIF)
 * @param           handle [in] SSPハンドル
 * @param           cbit [in] 設定値
 * @return          Channel Status Bit設定 (S/PDIF)結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @description     Channel Status Bitの設定をします。
*/
int32_t	AG903_SSPMgrSetSpdifCbit(AG903_SSPMgrHandle* handle, AG903_SSPMgrSpdifCbit* cbit)
{
	int32_t	 retval = AG903_ENONE;
	uint32_t status_0;
	uint32_t status_1;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if(NULL == cbit) {
		return -AG903_EINVAL;
	}

	status_0  = (uint32_t)(cbit->status[0]);
	status_0 |= (uint32_t)(cbit->status[1]<<8);
	status_0 |= (uint32_t)(cbit->status[2]<<16);
	status_0 |= (uint32_t)(cbit->status[3]<<24);

	status_1  = (uint32_t)(cbit->status[4]);
	
	AG903_SSPPrmSetStatusBit(ch, status_0, status_1);

	return retval;
}

/**
 * @brief           User Bit設定 (S/PDIF)
 * @param           handle [in] SSPハンドル
 * @param           offset [in] オフセット
 * @param           ubit [in] 設定値
 * @return          User Bit設定 (S/PDIF)結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @description     User Bitの設定をします。
 * @note            データはLSB側から出力されます。
*/
int32_t	AG903_SSPMgrSetSpdifUbit(AG903_SSPMgrHandle* handle, uint8_t offset, uint32_t ubit) 
{
	int32_t	 retval = AG903_ENONE;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if(AG903_SSP_USERBIT_REGMAX <= offset) {
		return -AG903_EINVAL;
	}
	
	AG903_SSPPrmSetUserBit(ch, offset, ubit);

	return retval;
}

/**
 * @brief           User Bit(1ブロック分)設定 (S/PDIF)
 * @param           handle [in] SSPハンドル
 * @param           ubit [in] 設定値
 * @return          User Bit(1ブロック分)設定 (S/PDIF)結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @description     User Bit(1ブロック分)の設定をします。
*/
int32_t	AG903_SSPMgrSetSpdifUbitALL(AG903_SSPMgrHandle* handle, AG903_SSPMgrSpdifUbit* ubit)
{
	int32_t	 retval = AG903_ENONE;
	uint8_t  loop;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if(NULL == ubit) {
		return -AG903_EINVAL;
	}
	
	for(loop=0; loop<AG903_SSP_USERBIT_REGMAX; loop++) {
		AG903_SSPPrmSetUserBit(ch, loop, ubit->userbit[loop]);
	}

	return retval;
}

/**
 * @brief           送信バッファ設定 (PIO)
 * @param           handle [in] SSPハンドル
 * @param           buf [in] データ先頭アドレス
 * @param           size [in] 送信サイズ(単位[Word] 下記参照)
 * @return          送信バッファ設定 (PIO)結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中（キューイング数がMAX）
 * @retval          -AG903_EPERM  状態異常（DMA有効状態）
 * @description     送信バッファの設定をします。<p>
 * @note            データはWord単位でシリアライズされて出力されます。
 * @note            Wordのビット長、シリアライズのビットシフトの方向等は別途モード設定に従います。
 * @note            データはコピーせずに指定アドレスから順に直接出力します。<p>
 *                  サイズ分の送信終了までバッファを保持して下さい。<p>
 * @note            Wordのビット長が8の倍数でない場合、次のWordはバッファ上では
 *                  バイト単位で切り上げたアドレスから出力されます。
 * @note            Wordのビット長が32ビットに満たない場合は右詰めになります。
*/
int32_t	AG903_SSPMgrSetSendBuf(AG903_SSPMgrHandle* handle, uint8_t* buf, uint32_t size)
{
	AG903_SSPMgrQue que;
	int32_t	 retval = AG903_ENONE;
	uint32_t status;
	uint32_t div;
	uint8_t	 enable;
	uint8_t	 ch;
	uint8_t	 entry;
	uint8_t	 threshold;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if((NULL == buf) || (0 >= size)) {
		return -AG903_EINVAL;
	}
	if(0 != SspChStat[ch].dma) {
		return -AG903_EPERM;
	}
	
	div = SspChStat[ch].wordlen / 8;	/* byte単位 */
	if(0 != (SspChStat[ch].wordlen%8)) {
		div++;
	}
	
	do {
		que.size = size*div;
		que.buf  = buf;
		retval   = SSPMgr_SetTxQue(ch, &que);
		if(AG903_ENONE != retval) {
			break;
		}
		
		if(AG903_SSP_ENABLE_SEND & SspChStat[ch].pio) {
			break; /* 送信中 */
		}
		
		retval = SSPMgr_SetTxData(ch, true);		/* FIFOセット */
		if(AG903_ENONE != retval) {
			break;
		}
		AG903_SSPPrmGetTxFifoEntry(ch, &entry);
		if(AG903_SSP_TXFIFO_THRESHOLD >= entry) {
			threshold = 1;
		}
		else {
			threshold = AG903_SSP_TXFIFO_THRESHOLD;
		}
		SspChStat[ch].pio |= AG903_SSP_ENABLE_SEND;
		AG903_SSPPrmSetTxFifoThreshold(ch, threshold);
		AG903_SSPPrmCheckEnable(ch, &enable);
		if(0 == enable) {
			AG903_SSPPrmGetIntStatus(ch, &status);	/* Read Clear */
		}
		else {
			AG903_SSPPrmEnableTxInt(ch);			/* 割込み許可 */
		}
	}while(0);

	return retval;
}

/**
 * @brief           受信バッファ設定 (PIO)
 * @param           handle [in] SSPハンドル
 * @param           buf [in] 格納先頭アドレス
 * @param           size [in] 受信サイズ(単位[Word] 下記参照)
 * @return          受信バッファ設定 (PIO)結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中（キューイング数がMAX）
 * @retval          -AG903_EPERM  状態異常（DMA有効状態）
 * @description     受信バッファの設定をします。<p>
 * @note            データはWord単位でシリアライズされて受信されます。
 * @note            Wordのビット長、シリアライズのビットシフトの方向等は別途モード設定に従います。
 *                  Wordのビット長は各モード設定APIで設定したwordlenになります。
 * @note            データはコピーせずに指定アドレスから順に直接受信します。<p>
 *                  サイズ分の受信終了までバッファを保持して下さい。<p>
 * @note            Wordのビット長が8の倍数でない場合、次のWordはバッファ上では
 *                  バイト単位で切り上げたアドレスへ受信されます。
 * @note            Wordのビット長が32ビットに満たない場合は右詰めになります。
*/
int32_t	AG903_SSPMgrSetReceiveBuf(AG903_SSPMgrHandle* handle, uint8_t* buf, uint32_t size)
{
	AG903_SSPMgrQue que;
	int32_t	 retval = AG903_ENONE;
	uint32_t status;
	uint32_t div;
	uint8_t	 enable;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if((NULL == buf) || (0 >= size)) {
		return -AG903_EINVAL;
	}
	if(0 != SspChStat[ch].dma) {
		return -AG903_EPERM;
	}

	div = SspChStat[ch].wordlen / 8;	/* byte単位 */
	if(0 != (SspChStat[ch].wordlen%8)) {
		div++;
	}

	do {
		que.size = size*div;
		que.buf  = buf;
		retval   = SSPMgr_SetRxQue(ch, &que);
		if(AG903_ENONE != retval) {
			break;
		}
		
		if(AG903_SSP_ENABLE_RECEIVE & SspChStat[ch].pio) {
			break; /* 受信中 */
		}
		
		retval = SSPMgr_GetRxData(ch, true);		/* 受信バッファ設定 */
		if(AG903_ENONE != retval) {
			break;
		}
		SspChStat[ch].pio |= AG903_SSP_ENABLE_RECEIVE;
		AG903_SSPPrmSetRxFifoThreshold(ch, AG903_SSP_RXFIFO_THRESHOLD);

		AG903_SSPPrmCheckEnable(ch, &enable);
		if(0 == enable) {
			AG903_SSPPrmGetIntStatus(ch, &status);	/* Read Clear */
		}
		else {
			AG903_SSPPrmEnableRxInt(ch);			/* 割込み許可 */
		}
	}while(0);

	return retval;
}

/**
 * @brief           DMAモード有効設定
 * @param           handle [in] SSPハンドル
 * @param           mode [in] DMAモード(ビット指定)
 * @return          DMAモード有効設定結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中
 * @retval          -AG903_EPERM  状態異常（PIO有効状態）
 * @description     DMAモード有効の設定をします。<p>
 *                  mode で指定したDMAモードが有効になります。<p>
 *                  <table>
 *                  <tr><th>ビット</th><th>DMAモード</th></tr>
 *                  <tr><td>1</td><td>受信DMA</td></tr>
 *                  <tr><td>0</td><td>送信DMA</td></tr>
 *                  </table>
 *                  ビット位置に1を設定すると有効になります。               
 * @note            伝送許可（AG903_SSPMgrEnableTransfer）前に設定して下さい。
*/
int32_t	AG903_SSPMgrEnableDmaMode(AG903_SSPMgrHandle* handle, uint8_t mode)
{
	int32_t	 retval = AG903_ENONE;
	_Bool	 idle;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if(0 != SspChStat[ch].pio) {
		return -AG903_EPERM;
	}
	
	idle = SSPMgr_CheckIdle(ch);
	if(true != idle) {
		return -AG903_EBUSY;
	}
	
	SspChStat[ch].dma = mode;
	
	return retval;
}

/**
 * @brief           DMAモード無効設定
 * @param           handle [in] SSPハンドル
 * @return          DMAモード無効の設定をします。
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中
 * @description     DMAモード無効の設定をします。<p>
 * @note            伝送許可（AG903_SSPMgrEnableTransfer）前に設定して下さい。
*/
int32_t	AG903_SSPMgrDisableDmaMode(AG903_SSPMgrHandle* handle)
{
	int32_t	 retval = AG903_ENONE;
	int32_t	 result;
	_Bool	 idle;
	uint8_t	 ch;

	result = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != result) {
		return -AG903_EINVAL;
	}
	idle = SSPMgr_CheckIdle(ch);
	if(true != idle) {
		return -AG903_EBUSY;
	}
	
	SspChStat[ch].dma = 0;

	return retval;
}

/**
 * @brief           伝送許可
 * @param           handle [in] SSPハンドル
 * @return          伝送許可結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中
 * @retval          -AG903_EPERM  状態異常（DMA or PIO設定異常）
 * @description     伝送許可の設定をします。<p>
 *                  次の関数で設定されたフォーマットで伝送します。<p>
 *                  AG903_SSPMgrSetSspMode , AG903_SSPMgrSetSpiMode , AG903_SSPMgrSetI2sMode , AG903_SSPMgrSetSpdifMode
*/
int32_t	AG903_SSPMgrEnableTransfer(AG903_SSPMgrHandle* handle)
{
	AG903_SSPPrmIntCtrl ictrl;
	AG903_SSPPrmCtrl2   ctrl2;
	int32_t	 retval = AG903_ENONE;
	uint32_t status;
	_Bool	 idle;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	idle = SSPMgr_CheckIdle(ch);
	if(true != idle) {
		return -AG903_EBUSY;
	}
	if( (0 != SspChStat[ch].dma) && (0 != SspChStat[ch].pio) ) {
		return -AG903_EPERM; /* DMA EnableなのにPIOバッファが設定されている */
	}
	if( (0 == SspChStat[ch].dma) && (0 == SspChStat[ch].pio) ) {
		return -AG903_EPERM; /* DMA DisableなのにPIOバッファが設定されていない */
	}
	
	AG903_SSPPrmGetIntStatus(ch, &status);	/* Read Clear */

	AG903_SSPPrmGetIntControl(ch, &ictrl);
	AG903_SSPPrmGetControl2(ch, &ctrl2);

	if(0 != SspChStat[ch].dma) { /* DMA転送 */
		ictrl.rfthien = 0; /* AG903_SSPPrmDisbleRxInt(ch); */
		ictrl.rforien = 0; /* AG903_SSPPrmDisbleRxInt(ch); */
		ictrl.tfthien = 0; /* AG903_SSPPrmDisbleTxInt(ch); */
		ictrl.tfurien = 0; /* AG903_SSPPrmDisbleTxInt(ch); */
		if(AG903_SSP_DMA_RECEIVE & SspChStat[ch].dma) {
			ctrl2.txdoe   = 0; /* AG903_SSPPrmDisableOutput(ch); */
			ictrl.tfdmaen = 0;
			ictrl.rfdmaen = 1; /* AG903_SSPPrmEnableRxDmareq(ch); */
			ctrl2.txen    = 0;
			ctrl2.rxen    = 1; /* AG903_SSPPrmEnableRxFunc(ch); */
		}
		if(AG903_SSP_DMA_SEND & SspChStat[ch].dma) {
			ctrl2.txdoe   = 1; /* AG903_SSPPrmEnableOutput(ch); */
			ictrl.tfdmaen = 1; /* AG903_SSPPrmEnableTxDmareq(ch); */
			ictrl.rfdmaen = 0;
			ctrl2.txen    = 1; /* AG903_SSPPrmEnableTxFunc(ch); */
			ctrl2.rxen    = 0;
		}
	}
	else { /* PIO転送 */
		ictrl.tfdmaen = 0; /* AG903_SSPPrmDisableTxDmareq(ch); */
		ictrl.rfdmaen = 0; /* AG903_SSPPrmDisableRxDmareq(ch); */
		if(AG903_SSP_ENABLE_RECEIVE & SspChStat[ch].pio) {
			ctrl2.txdoe   = 0; /* AG903_SSPPrmDisableOutput(ch); */
			ctrl2.txen    = 0;
			ctrl2.rxen    = 1; /* AG903_SSPPrmEnableRxFunc(ch); */
			ictrl.tfthien = 0;
			ictrl.tfurien = 0;
			ictrl.rfthien = 1; /* AG903_SSPPrmEnableRxInt(ch); */
			ictrl.rforien = 1; /* AG903_SSPPrmEnableRxInt(ch); */
		}
		if(AG903_SSP_ENABLE_SEND & SspChStat[ch].pio) {
			ctrl2.txdoe   = 1; /* AG903_SSPPrmEnableOutput(ch); */
			ctrl2.txen    = 1; /* AG903_SSPPrmEnableTxFunc(ch); */
			ctrl2.rxen    = 0;
			ictrl.tfthien = 1; /* AG903_SSPPrmEnableTxInt(ch); */
			ictrl.tfurien = 1; /* AG903_SSPPrmEnableTxInt(ch); */
			ictrl.rfthien = 0;
			ictrl.rforien = 0;
		}
	}

	ctrl2.sspen = 1; /* AG903_SSPPrmEnableTransfer(ch); */

	AG903_SSPPrmSetIntControl(ch, &ictrl);
	AG903_SSPPrmSetControl2(ch, &ctrl2);

	return retval;
}

/**
 * @brief           伝送停止 (Disable)
 * @param           handle [in] SSPハンドル
 * @return          伝送停止 (Disable)結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @description     伝送停止 (Disable)の設定をします<p>
 * @note            伝送中の場合LastBit伝送後に停止します。<p>
 *                  ただしバッファが空になっていることを確認してから実行して下さい。<p>
 *                  伝送中か否かは AG903_SSPMgrGetStatus で確認できます。
*/
int32_t	AG903_SSPMgrDisableTransfer(AG903_SSPMgrHandle* handle)
{
	int32_t	 retval = AG903_ENONE;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	
	retval = SSPMgr_DisableTransfer(ch);
	
	return retval;
}

/**
 * @brief           ステータス取得
 * @param           handle [in] SSPハンドル
 * @param           status [out] ステータス
 * @return          ステータス取得結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @description     ステータスを取得します。
*/
int32_t	AG903_SSPMgrGetStatus(AG903_SSPMgrHandle* handle, AG903_SSPMgrStatus* status)
{
	AG903_SSPPrmStatus getsts;
	int32_t	 retval = AG903_ENONE;
	uint8_t	 ch;
	
	if(NULL == status) {
		return -AG903_EINVAL;
	}
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	
	AG903_SSPPrmGetStatus(ch, &getsts);
	status->txfifo_num  = getsts.txfifo_num;
	status->rxfifo_num  = getsts.rxfifo_num;
	status->busy        = getsts.busy;
	status->txfifo_notfull = getsts.txfifo_notfull;
	status->rxfifo_full = getsts.rxfifo_full;

	return retval;
}

/**
 * @brief           送信キュークリア
 * @param           handle [in] SSPハンドル
 * @return          送信キュークリア結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @description     送信キューをクリアをします。
 * @note            転送中にクリアすることはできません。
*/
int32_t	AG903_SSPMgrClearTxQue(AG903_SSPMgrHandle* handle)
{
	int32_t	 retval = AG903_ENONE;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	SSPMgr_ClearTxQue(ch);

	return retval;
}

/**
 * @brief           受信キュークリア
 * @param           handle [in] SSPハンドル
 * @return          受信キュークリア結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @description     受信キューをクリアします。
 * @note            転送中にクリアすることはできません。
*/
int32_t	AG903_SSPMgrClearRxQue(AG903_SSPMgrHandle* handle)
{
	int32_t	 retval = AG903_ENONE;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	SSPMgr_ClearRxQue(ch);

	return retval;
}

/**
 * @brief           キュー状態取得
 * @param           handle [in] SSPハンドル
 * @param           status [out] ステータス
 * @return          キュー状態取得結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @description     キュー状態を取得します。
*/
int32_t	AG903_SSPMgrGetQueStatus(AG903_SSPMgrHandle* handle, AG903_SSPMgrQueStatus* status)
{
	int32_t	 retval = AG903_ENONE;
	uint32_t div;
	uint32_t remain;
	uint8_t	 ch;
	
	if(NULL == status) {
		return -AG903_EINVAL;
	}
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	
	div = SspChStat[ch].wordlen / 8;	/* byte単位 */
	if(0 != (SspChStat[ch].wordlen%8)) {
		div++;
	}
	
	remain = SspChStat[ch].tx.size - SspChStat[ch].tx.cnt;
	status->txremain = remain/div;
	if(0 != (remain%div)) {
		status->txremain ++;
	}
	status->received = (SspChStat[ch].rx.cnt/div);
	SSPMgr_GetTxQueEntry(ch, &status->txquecnt);
	SSPMgr_GetRxQueEntry(ch, &status->rxquecnt);
	
	return retval;
}

/**
 * @brief           送信FIFOクリア
 * @param           handle [in] SSPハンドル
 * @return          送信FIFOクリア結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @description     送信FIFOをクリアします。
 * @note            転送中にクリアすることはできません。
*/
int32_t	AG903_SSPMgrClearTxFifo(AG903_SSPMgrHandle* handle)
{
	int32_t	 retval = AG903_ENONE;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	AG903_SSPPrmClearTxFifo(ch);

	return retval;
}

/**
 * @brief           受信FIFOクリア
 * @param           handle [in] SSPハンドル
 * @return          受信FIFOクリア結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @description     受信FIFOをクリアします。
 * @note            転送中にクリアすることはできません。
*/
int32_t	AG903_SSPMgrClearRxFifo(AG903_SSPMgrHandle* handle)
{
	int32_t	 retval = AG903_ENONE;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	AG903_SSPPrmClearRxFifo(ch);

	return retval;
}

/**
 * @brief           送信FIFOしきい値設定
 * @param           handle [in] SSPハンドル
 * @return          送信FIFOしきい値設定結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常 (転送中)
 * @description     送信FIFOが空であると見なすしきい値を設定します。<p>
 *                  送信FIFOの有効なデータが設定値以下になると送信バッファからの読み出しやDMA要求が起こります。
 * @note            設定可能な値は1〜16です。
 *                  ただしDMAモードを有効にしている場合はしきい値-1とDMA転送ビート数との和が16を超えず
 *                  かつ16としきい値との差がDMA転送ビート数の倍数になるように設定してください。
 * @note            転送中に設定することはできません。
*/
int32_t AG903_SSPMgrSetTxFifoThreshold(AG903_SSPMgrHandle* handle, uint8_t threshold)
{
	int32_t	retval = AG903_ENONE;
	uint8_t	ch;
	uint8_t	enable;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	if (0 >= threshold || AG903_SSP_FIFO_DEPTH < threshold) {
		return -AG903_EINVAL;
	}

	AG903_SSPPrmCheckEnable(ch, &enable);
	if (0 != enable) {
		return -AG903_EPERM;
	}

	SspChStat[ch].tx_thod = threshold;
	AG903_SSPPrmSetTxFifoThreshold(ch, threshold);

	return retval;
}

/**
 * @brief           受信FIFOしきい値設定
 * @param           handle [in] SSPハンドル
 * @return          受信FIFOしきい値設定結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常 (転送中)
 * @description     受信FIFOが一杯であると見なすしきい値を設定します。<p>
 *                  受信FIFOの有効なデータが設定値以上になると受信バッファへの書き込みやDMA要求が起こります。
 * @note            設定可能な値は1〜16です。
 *                  ただしDMAモードを有効にしている場合はしきい値がDMA転送ビート数以上
 *                  かつしきい値がDMA転送ビート数の倍数になるように設定してください。
 * @note            転送中に設定することはできません。
*/
int32_t AG903_SSPMgrSetRxFifoThreshold(AG903_SSPMgrHandle* handle, uint8_t threshold)
{
	int32_t	retval = AG903_ENONE;
	uint8_t	ch;
	uint8_t	enable;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	if (0 >= threshold || AG903_SSP_FIFO_DEPTH < threshold) {
		return -AG903_EINVAL;
	}

	AG903_SSPPrmCheckEnable(ch, &enable);
	if (0 != enable) {
		return -AG903_EPERM;
	}

	SspChStat[ch].rx_thod = threshold;
	AG903_SSPPrmSetRxFifoThreshold(ch, threshold);

	return retval;
}

/**
 * @brief           リセット
 * @param           handle [in] SSPハンドル
 * @return          リセット結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中
 * @description     各種状態のリセットをします。<p>
 *                  コントローラリセットに加えて、内部状態（キュー含む）がクリアされます。
 * @note            伝送停止中（Busy=0）時のみ有効です。
*/
int32_t	AG903_SSPMgrReset(AG903_SSPMgrHandle* handle)
{
	AG903_SSPPrmIntCtrl		ictrl_clr = {0};
	AG903_SSPPrmCtrl2		ctrl2_clr = {0};
	AG903_SSPMgrDmaStat		dma_clr = {0};
	AG903_SSPMgrSpiParam	spi_clr = {0};
	int32_t	 retval = AG903_ENONE;
	_Bool	 busy;
	uint8_t	 ch;
	
	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	busy = SSPMgr_CheckBusy(ch);
	if(true == busy) {
		return -AG903_EBUSY;
	}

	SSPMgr_DisableTransfer(ch);
	AG903_SSPPrmClearTxFifo(ch);
	AG903_SSPPrmClearRxFifo(ch);

	AG903_SSPPrmReset(ch);
	SspChStat[ch].tx.buf    = NULL;
	SspChStat[ch].tx.size   = 0;
	SspChStat[ch].tx.cnt    = 0;
	SspChStat[ch].rx.buf    = NULL;
	SspChStat[ch].rx.size   = 0;
	SspChStat[ch].rx.cnt    = 0;
	SspRegStat[ch].ictrl    = ictrl_clr;
	SspRegStat[ch].ctrl2    = ctrl2_clr;
	SspDmaStatTx[ch]        = dma_clr;
	SspDmaStatRx[ch]        = dma_clr;
	SspSpiParam[ch]         = spi_clr;
	SSPMgr_ClearTxQue(ch);
	SSPMgr_ClearRxQue(ch);

	return retval;
}

/*
	状態初期化
	ch [in] SSPチャネル
*/
static void SSPMgr_Init(uint8_t ch)
{
	if(AG903_SSP_CH_NUM <= ch) {
		return;
	}
	
	SspHandleStat[ch].lock = false;
	SspHandleStat[ch].clbk = NULL;
	SspChStat[ch].tx.buf   = NULL;
	SspChStat[ch].tx.size  = 0;
	SspChStat[ch].tx.cnt   = 0;
	SspChStat[ch].rx.buf   = NULL;
	SspChStat[ch].rx.size  = 0;
	SspChStat[ch].rx.cnt   = 0;
	SspChStat[ch].format   = AG903_SSP_FORMAT_SSP;
	SspChStat[ch].wordlen  = 0;
	SspChStat[ch].slave    = false;
	SspChStat[ch].tx_thod  = 2; /* SSP reset value */
	SspChStat[ch].rx_thod  = 2; /* SSP reset value */
	SspChStat[ch].pio      = 0;
	SspChStat[ch].dma      = 0;
	
	SspRegStat[ch].ictrl.tfthod     = 2; /* SSP reset value */
	SspRegStat[ch].ictrl.rfthod     = 2; /* SSP reset value */
	SspRegStat[ch].ictrl.tfdmaen    = 0;
	SspRegStat[ch].ictrl.rfdmaen    = 0;
	SspRegStat[ch].ictrl.tfthien    = 0;
	SspRegStat[ch].ictrl.rfthien    = 0;
	SspRegStat[ch].ictrl.tfurien    = 0;
	SspRegStat[ch].ictrl.rforien    = 0;
	SspRegStat[ch].ctrl2.fsos   = 0;
	SspRegStat[ch].ctrl2.fs     = 0;
	SspRegStat[ch].ctrl2.txen   = 0;
	SspRegStat[ch].ctrl2.rxen   = 0;
	SspRegStat[ch].ctrl2.ssprst = 0;
	SspRegStat[ch].ctrl2.txfclr = 0;
	SspRegStat[ch].ctrl2.rxfclr = 0;
	SspRegStat[ch].ctrl2.txdoe  = 0;
	SspRegStat[ch].ctrl2.sspen  = 0;

	SspDmaStatTx[ch].attach       = 0;
	SspDmaStatTx[ch].stat         = AG903_SSP_DMA_STAT_STOP;
	SspDmaStatTx[ch].dma_ch       = 3 + ch;
	SspDmaStatTx[ch].dma_if       = 3 + ch;
	SspDmaStatTx[ch].ctrlwidth    = 0;
	SspDmaStatTx[ch].ctrlbeat     = 0;
	SspDmaStatTx[ch].txrxdr       = 0xE0400018 + 0x100000*ch;
	SspDmaStatTx[ch].hdrid        = -1;

	SspDmaStatRx[ch].attach       = 0;
	SspDmaStatRx[ch].stat         = AG903_SSP_DMA_STAT_STOP;
	SspDmaStatRx[ch].dma_ch       = 4 + ch;
	SspDmaStatRx[ch].dma_if       = 4 + ch;
	SspDmaStatRx[ch].ctrlwidth    = 0;
	SspDmaStatRx[ch].ctrlbeat     = 0;
	SspDmaStatRx[ch].txrxdr       = 0xE0400018 + 0x100000*ch;
	SspDmaStatRx[ch].hdrid        = -1;

	SspSpiParam[ch].clkdiv      = 2;
	SspSpiParam[ch].wordlen     = 8;
	SspSpiParam[ch].polarity    = AG903_SSP_POL_NEGATIVE;
	SspSpiParam[ch].firstbit    = AG903_SSP_FIRSTBIT_MSB;
	SspSpiParam[ch].slave       = false;
	SspSpiParam[ch].flash       = false;

	SSPMgr_ClearTxQue(ch);
	SSPMgr_ClearRxQue(ch);
	
	return;
}

/*
	IDLEチェック
	ch [in] SSPチャネル
*/
static _Bool SSPMgr_CheckIdle(uint8_t ch)
{
	_Bool	 retval=false;
	uint8_t  enable;
	
	AG903_SSPPrmCheckEnable(ch, &enable);
	if(0 == enable) {
		retval=true;
	}
	return retval;
}

/*
	Busyチェック
	ch [in] SSPチャネル
*/
static _Bool SSPMgr_CheckBusy(uint8_t ch)
{
	AG903_SSPPrmStatus status;
	_Bool	 retval=false;
	
	AG903_SSPPrmGetStatus(ch, &status);
	if(true == status.busy) {
		retval = true;
	}
	return retval;
}

/*
	ハンドルチェック
	handle [in] SSPハンドル
	ch [out] SSPチャネル
*/
static int32_t SSPMgr_CheckHandle(AG903_SSPMgrHandle* handle, uint8_t* ch)
{
	uint32_t get_ch;
	
	get_ch = ((uint32_t)handle - (uint32_t)SspHandleStat) / sizeof(AG903_SSPMgrHandleStat);
	
	if( (AG903_SSP_CH_NUM <= get_ch) ||
		(&SspHandleStat[get_ch] != (AG903_SSPMgrHandleStat*)handle) ) {
		return -AG903_EINVAL;
	}
	(*ch) = (uint8_t)get_ch;
	
	return AG903_ENONE;
}

/*
	送信データセット
	ch [in] SSPチャネル
	que [in] キュー読込み有無
*/
static int32_t SSPMgr_SetTxData(uint8_t ch, _Bool que)
{
	AG903_SSPMgrQue txque;
	int32_t	 retval = AG903_ENONE;
	uint32_t cnt;

	if(AG903_SSP_CH_NUM <= ch) {
		return -AG903_EINVAL;
	}
	
	if(true == que) {
		retval = SSPMgr_GetTxQue(ch, &txque);
		if(AG903_ENONE == retval) {
			SspChStat[ch].tx.buf  = txque.buf;
			SspChStat[ch].tx.size = txque.size;
			SspChStat[ch].tx.bufp = SSPMgr_SetTxFifo(ch, txque.buf, txque.size, &SspChStat[ch].tx.cnt);
		}
	}
	else {
		if( (NULL != SspChStat[ch].tx.bufp) && (SspChStat[ch].tx.size > SspChStat[ch].tx.cnt) ){
			SspChStat[ch].tx.bufp = SSPMgr_SetTxFifo(ch, SspChStat[ch].tx.bufp, 
				                                    (SspChStat[ch].tx.size-SspChStat[ch].tx.cnt), &cnt);
			SspChStat[ch].tx.cnt += cnt;
		}
	}

	return retval;
}

/*
	受信データ取得
	ch [in] SSPチャネル
	que [in] キュー読込み有無
*/
static int32_t SSPMgr_GetRxData(uint8_t ch, _Bool que)
{
	AG903_SSPMgrQue rxque;
	int32_t	 retval = AG903_ENONE;
	uint32_t cnt;

	if(AG903_SSP_CH_NUM <= ch) {
		return -AG903_EINVAL;
	}
	
	if(true == que) {
		retval = SSPMgr_GetRxQue(ch, &rxque);
		if(AG903_ENONE == retval) {
			SspChStat[ch].rx.buf  = rxque.buf;
			SspChStat[ch].rx.size = rxque.size;
			SspChStat[ch].rx.bufp = SSPMgr_GetRxFifo(ch, rxque.buf, rxque.size, &SspChStat[ch].rx.cnt);
		}
	}
	else {
		if( (NULL != SspChStat[ch].rx.bufp) && (SspChStat[ch].rx.size > SspChStat[ch].rx.cnt) ){
			SspChStat[ch].rx.bufp = SSPMgr_GetRxFifo(ch, SspChStat[ch].rx.bufp, 
				                                    (SspChStat[ch].rx.size-SspChStat[ch].rx.cnt), &cnt);
			SspChStat[ch].rx.cnt += cnt;
		}
	}
	return retval;
}

/*
	送信FIFOセット
	ch [in] SSPチャネル
	buf [in] データ
	size [in] サイズ
	setnum [out] セット数
*/
static uint8_t* SSPMgr_SetTxFifo(uint8_t ch, uint8_t* buf, uint32_t size, uint32_t* setnum)
{
	AG903_SSPPrmStatus status;
	uint8_t* bufp = buf;
	uint32_t cnt=0;
	uint32_t loop;
	uint32_t val=0;
	
	if( (AG903_SSP_CH_NUM <= ch) ||
		(NULL  == buf) ||
		 (NULL == setnum) ) {
		return NULL;
	}

	for(loop=0; loop<AG903_SSP_FIFO_DEPTH; loop++) {
		if(size <= cnt) {
			break;	/* Set All */
		}
		AG903_SSPPrmGetStatus(ch, &status);
		if(true == status.txfifo_notfull) {
			val = (*bufp++);
			cnt ++;
			if(8 < SspChStat[ch].wordlen) {	 /* 8bitより大きい */
				val |= ((*bufp++)<<8);
				cnt ++;
			}
			if(16 < SspChStat[ch].wordlen) { /* 16bitより大きい */
				val |= ((*bufp++)<<16);
				cnt ++;
			}
			if(24 < SspChStat[ch].wordlen) { /* 24bitより大きい */
				val |= ((*bufp++)<<24);
				cnt ++;
			}
			AG903_SSPPrmSetData(ch, val);
		}
		else {
			break; /* FIFO Full */
		}
	}
	
	(*setnum) = cnt;
	
	return bufp;
}

/*
	受信FIFOデータ取得.
	ch [in] SSPチャネル.
	buf [in] データ.
	size [in] サイズ.
	setnum [out] 取得数.
*/
static uint8_t* SSPMgr_GetRxFifo(uint8_t ch, uint8_t* buf, uint32_t size, uint32_t* getnum)
{
	AG903_SSPPrmStatus status;
	uint8_t* bufp = buf;
	uint32_t cnt=0;
	uint32_t loop;
	uint32_t val;

	if( (AG903_SSP_CH_NUM <= ch) ||
		(NULL  == buf) ||
		 (NULL == getnum) ) {
		return NULL;
	}

	for(loop=0; loop<AG903_SSP_FIFO_DEPTH; loop++) {
		if(size <= cnt) {
			break;	/* Get All */
		}
		AG903_SSPPrmGetStatus(ch, &status);
		if(0 < status.rxfifo_num) {
			AG903_SSPPrmGetData(ch, &val);
			(*bufp++) = (uint8_t)val;
			cnt++;
			if(8 < SspChStat[ch].wordlen) {	 /* 8bitより大きい */
				(*bufp++) = (uint8_t)(val>>8);
				cnt++;
			}
			if(16 < SspChStat[ch].wordlen) { /* 16bitより大きい */
				(*bufp++) = (uint8_t)(val>>16);
				cnt++;
			}
			if(24 < SspChStat[ch].wordlen) { /* 24bitより大きい */
				(*bufp++) = (uint8_t)(val>>24);
				cnt++;
			}
		}
		else {
			break; /* FIFO Empty */
		}
	}

	(*getnum) = cnt;
	
	return bufp;
}

/*
	伝送停止.
	ch [in] SSPチャネル.
*/
static int32_t SSPMgr_DisableTransfer(uint8_t ch)
{
	AG903_SSPPrmIntCtrl ictrl;
	AG903_SSPPrmCtrl2   ctrl2;
	int32_t	 retval = AG903_ENONE;

	if(AG903_SSP_CH_NUM <= ch) {
		return -AG903_EINVAL;
	}

	SspChStat[ch].dma = 0;
	SspChStat[ch].pio = 0;
	
	AG903_SSPPrmGetIntControl(ch, &ictrl);
	AG903_SSPPrmGetControl2(ch, &ctrl2);

	ictrl.rfthien = 0; /* AG903_SSPPrmDisbleRxInt(ch); */
	ictrl.rforien = 0; /* AG903_SSPPrmDisbleRxInt(ch); */
	ictrl.tfthien = 0; /* AG903_SSPPrmDisbleTxInt(ch); */
	ictrl.tfurien = 0; /* AG903_SSPPrmDisbleTxInt(ch); */
	ctrl2.txen    = 0; /* AG903_SSPPrmDisbleTxFunc(ch); */
	ctrl2.rxen    = 0; /* AG903_SSPPrmDisbleRxFunc(ch); */
	ctrl2.sspen   = 0; /* AG903_SSPPrmDisableTransfer(ch); */

	AG903_SSPPrmSetIntControl(ch, &ictrl);
	AG903_SSPPrmSetControl2(ch, &ctrl2);

	return retval;
}


/*
	割込みハンドラ(CH0).
*/
static void SSPMgr_Inthdr0(void)
{
	SSPMgr_IntProcess(0);	/* CH0 */
	return;
}

/*
	割込みハンドラ(CH1).
*/
static void SSPMgr_Inthdr1(void)
{
	SSPMgr_IntProcess(1);	/* CH1 */
	return;
}

/*
	割込みハンドラ(CH2).
*/
static void SSPMgr_Inthdr2(void)
{
	SSPMgr_IntProcess(2);	/* CH2 */
	return;
}

/*
	割込みハンドラ(CH3).
*/
static void SSPMgr_Inthdr3(void)
{
	SSPMgr_IntProcess(3);	/* CH3 */
	return;
}

/*
	割込み処理.
	ch [in] SSPチャネル.
*/
static void SSPMgr_IntProcess(uint8_t ch)
{
	uint32_t event = 0;
	uint32_t intstatus;
	
	AG903_SSPPrmGetIntStatus(ch, &intstatus);
	
	if(AG903_SSP_INTSTS_TFTHIBIT & intstatus) { /* 送信FIFO空あり */
		SSPMgr_IntSend(ch, &event);
	}
	
	if(AG903_SSP_INTSTS_RFTHIBIT & intstatus) {	/* 受信データあり */
		SSPMgr_IntReceive(ch, &event);
	}
	
	if(AG903_SSP_INTSTS_TFURIBIT & intstatus) {
		event |= AG903_SSP_EVENT_UNDERRUN;
	}
	
	if(AG903_SSP_INTSTS_RFORIBIT & intstatus) {
		event |= AG903_SSP_EVENT_OVERRUN;
	}
	
	if((0 != event) && (NULL != SspHandleStat[ch].clbk)) {
		SspHandleStat[ch].clbk((AG903_SSPMgrHandle*)&SspHandleStat[ch], event);
	}
	
	return;
}

/*
	データ送信（割込み）.
	ch [in] SSPチャネル.
	event [out] 発生イベント.
*/
static void SSPMgr_IntSend(uint8_t ch, uint32_t* event)
{
	int32_t  result = AG903_ENONE;

	if(AG903_SSP_ENABLE_SEND & SspChStat[ch].pio) {	/* PIO送信中 */
		SSPMgr_SetTxData(ch, false);
		if(SspChStat[ch].tx.cnt >= SspChStat[ch].tx.size) {
			(*event) |= AG903_SSP_EVENT_SNDEND;
			result = SSPMgr_SetTxData(ch, true);
			if(-AG903_ENOMSG == result) { 				/* 送信キュー空 */
				AG903_SSPPrmDisbleTxInt(ch);
				SspChStat[ch].pio &= ~AG903_SSP_ENABLE_SEND;
			}
		}
	}
	
	return;
}

/*
	データ受信（割込み）.
	ch [in] SSPチャネル.
	event [out] 発生イベント.
*/
static void SSPMgr_IntReceive(uint8_t ch, uint32_t* event)
{
	int32_t  result = AG903_ENONE;
	
	if(AG903_SSP_ENABLE_RECEIVE & SspChStat[ch].pio) {	/* PIO受信中 */
		SSPMgr_GetRxData(ch, false);
		if(SspChStat[ch].rx.size <= SspChStat[ch].rx.cnt) {
			(*event) |= AG903_SSP_EVENT_RCVEND;
			result = SSPMgr_GetRxData(ch, true);
			if(-AG903_ENOMSG == result) {						/* 受信キュー空 */
				AG903_SSPPrmDisbleRxInt(ch);
				SspChStat[ch].pio &= ~AG903_SSP_ENABLE_RECEIVE;
			}
		}
	}
	
	return;
}

/*
	送信キュー書込み.
	ch [in] SSPチャネル.
	que [in] 書込みキュー.
*/
static int32_t SSPMgr_SetTxQue(uint8_t ch, AG903_SSPMgrQue* que)
{
	int32_t	 	retval = AG903_ENONE;
	uint32_t	wp;
	uint32_t	next_wp;
	
	if( (AG903_SSP_CH_NUM <= ch) ||
		(NULL == que) ){
		return -AG903_EINVAL;
	}

	wp      = SspTxQue[ch].wp;
	next_wp = SSPMgr_GetNextTxQuePoint(wp);
	if(next_wp == SspTxQue[ch].rp) {
		retval = -AG903_EBUSY;	/* 空なし */
	}
	else {
		SspTxQue[ch].que[wp] = (*que);	/* 構造体コピー */
		SspTxQue[ch].wp = next_wp;
	}
	
	return retval;
}

/*
	送信キュー 読出し.
	ch [in] SSPチャネル.
	que [out] 読出しキュー.
*/
static int32_t SSPMgr_GetTxQue(uint8_t ch, AG903_SSPMgrQue* que)
{
	int32_t	 retval = AG903_ENONE;
	uint32_t rp;
	uint32_t next_rp;
	
	if( (AG903_SSP_CH_NUM <= ch) ||
		(NULL == que) ){
		return -AG903_EINVAL;
	}

	rp      = SspTxQue[ch].rp;
	if(SspTxQue[ch].wp == rp) {
		retval = -AG903_ENOMSG;	/* キュー空 */
	}
	else {
		next_rp = SSPMgr_GetNextTxQuePoint(rp);
		(*que)  = SspTxQue[ch].que[rp];	/* 構造体コピー */
		SspTxQue[ch].rp = next_rp;
	}
	
	return retval;
}

/*
	次送信キューポインタ取得.
	current [in] 現在値.
*/
static uint32_t SSPMgr_GetNextTxQuePoint(uint32_t current)
{
	uint32_t next;
	
	next = current+1;
	if(AG903_SSP_TXQUE_ARY_LEN <= next) {
		next = 0;
	}
	
	return next;
}

/*
	送信キューエントリ数取得.
	ch [in] SSPチャネル.
	entry [out] エントリ数.
*/
static void SSPMgr_GetTxQueEntry(uint8_t ch, uint32_t* entry)
{

	if(AG903_SSP_CH_NUM <= ch) {
		return;
	}
	
	if(SspTxQue[ch].wp >= SspTxQue[ch].rp) {
		(*entry) = SspTxQue[ch].wp - SspTxQue[ch].rp;
	}
	else {
		(*entry) = AG903_SSP_TXQUE_ARY_LEN - SspTxQue[ch].rp + SspTxQue[ch].wp;
	}
	
	return;
}

/*
	送信キュークリア.
	ch [in] SSPチャネル.
*/
static void SSPMgr_ClearTxQue(uint8_t ch)
{
	if(AG903_SSP_CH_NUM <= ch) {
		return;
	}
	SspTxQue[ch].wp = 0;
	SspTxQue[ch].rp = 0;
	return;
}

/*
	受信キュー書込み.
	ch [in] SSPチャネル.
	que [in] 書込みキュー.
*/
static int32_t SSPMgr_SetRxQue(uint8_t ch, AG903_SSPMgrQue* que)
{
	int32_t	 	retval = AG903_ENONE;
	uint32_t	wp;
	uint32_t	next_wp;
	
	if( (AG903_SSP_CH_NUM <= ch) ||
		(NULL == que) ){
		return -AG903_EINVAL;
	}

	wp      = SspRxQue[ch].wp;
	next_wp = SSPMgr_GetNextRxQuePoint(wp);
	if(next_wp == SspRxQue[ch].rp) {
		retval = -AG903_EBUSY;	/* 空なし */
	}
	else {
		SspRxQue[ch].que[wp] = (*que);	/* 構造体コピー */
		SspRxQue[ch].wp = next_wp;
	}
	
	return retval;
}

/*
	受信キュー 読出し.
	ch [in] SSPチャネル.
	que [out] 読出しキュー.
*/
static int32_t SSPMgr_GetRxQue(uint8_t ch, AG903_SSPMgrQue* que)
{
	int32_t	 retval = AG903_ENONE;
	uint32_t rp;
	uint32_t next_rp;
	
	if( (AG903_SSP_CH_NUM <= ch) ||
		(NULL == que) ){
		return -AG903_EINVAL;
	}

	rp      = SspRxQue[ch].rp;
	if(SspRxQue[ch].wp == rp) {
		retval = -AG903_ENOMSG;	/* キュー空 */
	}
	else {
		next_rp = SSPMgr_GetNextRxQuePoint(rp);
		(*que)  = SspRxQue[ch].que[rp];	/* 構造体コピー */
		SspRxQue[ch].rp = next_rp;
	}
	
	return retval;
}

/*
	次受信キューポインタ取得.
	current [in] 現在値.
*/
static uint32_t SSPMgr_GetNextRxQuePoint(uint32_t current)
{
	uint32_t next;
	
	next = current+1;
	if(AG903_SSP_RXQUE_ARY_LEN <= next) {
		next = 0;
	}
	
	return next;
}

/*
	受信キューエントリ数取得.
	ch [in] SSPチャネル.
	entry [out] エントリ数.
*/
static void SSPMgr_GetRxQueEntry(uint8_t ch, uint32_t* entry)
{
	
	if(AG903_SSP_CH_NUM <= ch) {
		return;
	}
	
	if(SspRxQue[ch].wp >= SspRxQue[ch].rp) {
		(*entry) = SspRxQue[ch].wp - SspRxQue[ch].rp;
	}
	else {
		(*entry) = AG903_SSP_RXQUE_ARY_LEN - SspRxQue[ch].rp + SspRxQue[ch].wp;
	}
	
	return;
}

/*
	受信キュークリア.
	ch [in] SSPチャネル.
*/
static void SSPMgr_ClearRxQue(uint8_t ch)
{
	if(AG903_SSP_CH_NUM <= ch) {
		return;
	}
	SspRxQue[ch].wp = 0;
	SspRxQue[ch].rp = 0;
	return;
}

/**
 * @brief           データ送信 (SPIモード用)
 * @param           handle [in] SSPハンドル
 * @param           buf [in] データ送信先頭アドレス
 * @param           num [in] 送信サイズ(単位[Word] 下記参照)
 * @return          送信結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常 (SPIモードではない等)
 * @description     SPIモードのときデータを送信します。
 * @note            SPIモード以外では使用できません。
 * @note            この関数は AG903_SSPMgrSpiEnableTransfer と組み合わせて使用します。
 * @note            完全に転送が完了するまで戻りません。
 *                  AG903_SSPMgrSpiSendDma の使用を推奨します。
 * @note            転送の完了時の AG903_SSPMgrSetCallback によるコールバックはRTOSのタスクモードで呼ばれます。
 * @note            データワードのビット長とシリアライズについては AG903_SSPMgrSetSendBuf での説明と同様です。
*/
int32_t AG903_SSPMgrSpiSend(AG903_SSPMgrHandle* handle, const void* buf, uint32_t num)
{
	int32_t	retval = AG903_ENONE;	
	uint8_t	ch;
	uint8_t	dma_tx_enable;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	dma_tx_enable = 0;
	retval = SSPMgr_SpiSendTemplate(ch, buf, num, dma_tx_enable);

	return retval;
}	

/**
 * @brief           データ受信 (SPIモード用)
 * @param           handle [in] SSPハンドル
 * @param           buf [in] データ受信先頭アドレス
 * @param           num [in] 受信サイズ(単位[Word] 下記参照)
 * @return          受信結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常 (SPIモードではない等)
 * @description     SPIモードのときデータを受信します。
 * @note            この関数による受信を開始すると指定サイズでの受信完了後も AG903_SSPMgrSpiDisableTransfer で停止するまで無効な受信が継続します。
 *                  また受信中は常に不定なデータが送信されます。
 * @note            SPIモード以外では使用できません。
 * @note            完全に転送が完了するまで戻りません。
 *                  AG903_SSPMgrSpiReceiveDma の使用を推奨します。
 * @note            この関数は AG903_SSPMgrSpiEnableTransfer と組み合わせて使用します。
 * @note            転送の完了時の AG903_SSPMgrSetCallback によるコールバックはRTOSのタスクモードで呼ばれます。
 * @note            データワードのビット長とシリアライズについては AG903_SSPMgrSetReceiveBuf での説明と同様です。
*/
int32_t AG903_SSPMgrSpiReceive(AG903_SSPMgrHandle* handle, void* buf, uint32_t num)
{
	int32_t	retval = AG903_ENONE;	
	uint8_t	ch;
	uint8_t	dma_rx_enable;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	dma_rx_enable = 0;
	retval = SSPMgr_SpiReceiveTemplate(ch, buf, num, dma_rx_enable);

	return retval;
}

/**
 * @brief           データ送受信 (SPIモード用)
 * @param           handle [in] SSPハンドル
 * @param           tx_buf [in] データ送信先頭アドレス
 * @param           tx_num [in] 送信サイズ(単位[Word] 下記参照)
 * @param           rx_buf [in] データ受信先頭アドレス
 * @param           rx_num [in] 受信サイズ(単位[Word] 下記参照)
 * @return          送信結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常 (SPIモードではない等)
 * @description     SPIモードのときデータを全二重で送受信します。
 * @note            送信と受信は同時に開始されます。
 * @note            原則としてtx_numとrx_numは同じサイズにしてください。
 *                  デバイスによりますが一般的に送信データの後半は受信待ちのためのダミー、受信データの前半はコマンド送信中のための無効なデータとして運用されます。
 * @note            この関数をSPIフラッシュ特化モードで使用した場合指定サイズでの送受信完了後も AG903_SSPMgrSpiDisableTransfer で停止するまで無効な送受信が継続します。
 * @note            SPIモード以外では使用できません。
 * @note            この関数は AG903_SSPMgrSpiEnableTransfer と組み合わせて使用します。
 * @note            この関数からは完全に転送が完了するまで戻りません。
 *                  AG903_SSPMgrSpiSendReceiveDma の使用を推奨します。
 * @note            転送完了時の AG903_SSPMgrSetCallback によるコールバックはRTOSのタスクモードで呼ばれます。
 * @note            データワードのビット長とシリアライズについては AG903_SSPMgrSetSendBuf での説明と同様です。
*/
int32_t AG903_SSPMgrSpiSendReceive(AG903_SSPMgrHandle* handle, const void* tx_buf, uint32_t tx_num, void *rx_buf, uint32_t rx_num)
{
	int32_t	retval = AG903_ENONE;	
	uint8_t	ch;
	uint8_t	dma_tx_enable;
	uint8_t	dma_rx_enable;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	dma_tx_enable = 0;
	dma_rx_enable = 0;
	retval = SSPMgr_SpiSendReceiveTemplate(ch, tx_buf, tx_num, rx_buf, rx_num, dma_tx_enable, dma_rx_enable);

	return retval;
}

/**
 * @brief           DMAによるデータ送信 (SPIモード用)
 * @param           handle [in] SSPハンドル
 * @param           buf [in] 送信データ先頭アドレス
 * @param           size [in] 送信サイズ(単位[Word] 下記参照)
 * @return          送信結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常
 * @description     SPIモードのときDMAによりデータを送信します。<p>
 * @note            SPIモード以外では使用できません。
 * @note            この関数は AG903_SSPMgrSpiEnableTransfer と組み合わせて使用します。
 * @note            その他の仕様は AG903_SSPMgrSendDma と同様です。
*/
int32_t AG903_SSPMgrSpiSendDma(AG903_SSPMgrHandle* handle, const void* buf, uint32_t num)
{
	int32_t	retval = AG903_ENONE;	
	uint8_t	ch;
	uint8_t	dma_tx_enable;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	dma_tx_enable = 1;
	retval = SSPMgr_SpiSendTemplate(ch, buf, num, dma_tx_enable);

	return retval;
}

/**
 * @brief           DMAによるデータ受信 (SPIモード用)
 * @param           handle [in] SSPハンドル
 * @param           buf [in] 受信データ先頭アドレス
 * @param           size [in] 受信サイズ(単位[Word] 下記参照)
 * @return          受信結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常 (SPIモードではない等)
 * @description     SPIモードのときDMAによりデータを受信します。<p>
 * @note            SPIモード以外では使用できません。
 * @note            この関数は AG903_SSPMgrSpiEnableTransfer と組み合わせて使用します。
 * @note            この関数による受信を開始すると指定サイズでの受信完了後も AG903_SSPMgrSpiDisableTransfer で停止するまで無効な受信が継続します。
 *                  また受信中は常に不定なデータが送信されます。
 * @note            その他の仕様は AG903_SSPMgrReceiveDma と同様です。
*/
int32_t AG903_SSPMgrSpiReceiveDma(AG903_SSPMgrHandle* handle, void* buf, uint32_t num)
{
	int32_t	retval = AG903_ENONE;	
	uint8_t	ch;
	uint8_t	dma_rx_enable;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	dma_rx_enable = 1;
	retval = SSPMgr_SpiReceiveTemplate(ch, buf, num, dma_rx_enable);

	return retval;
}

/**
 * @brief           DMAによるデータ送受信 (SPIモード用)
 * @param           handle [in] SSPハンドル
 * @param           tx_buf [in] データ送信先頭アドレス
 * @param           tx_num [in] 送信サイズ(単位[Word] 下記参照)
 * @param           rx_buf [in] データ受信先頭アドレス
 * @param           rx_num [in] 受信サイズ(単位[Word] 下記参照)
 * @return          送信結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常 (SPIモードではない等)
 * @description     SPIモードのときDMAによりデータを全二重で送受信します。
 * @note            DMAによる転送は AG903_SSPMgrAttachTxDma または AG903_SSPMgrAttachRxDma でDMAが取り付けられている転送方向に対してのみ有効です。
 * @note            DMAが取り付けられていない転送方向に対してはCPU転送になります。
 * @note            この関数のDMAはライブラリ内部から制御されユーザーが外部から制御することはできません。
 * @note            送信と受信の両方にDMAが取り付けられている場合は転送開始後直ちに戻ります。
 *                  少なくともどちらか片方にDMAが取り付けられていない場合は完全に転送が完了するまで戻りません。
 * @note            転送完了時に AG903_SSPMgrSetCallback によりコールバックされるとき
 *                  DMAが取り付けられていない転送方向に対してはRTOSのタスクモードで呼ばれます。
 * @note            その他の仕様は AG903_SSPMgrSpiSendReceive と同様です。
*/
int32_t AG903_SSPMgrSpiSendReceiveDma(AG903_SSPMgrHandle* handle, const void* tx_buf, uint32_t tx_num, void *rx_buf, uint32_t rx_num)
{
	int32_t	retval = AG903_ENONE;	
	uint8_t	ch;
	uint8_t	dma_tx_enable;
	uint8_t	dma_rx_enable;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	dma_tx_enable = 1;
	dma_rx_enable = 1;
	retval = SSPMgr_SpiSendReceiveTemplate(ch, tx_buf, tx_num, rx_buf, rx_num, dma_tx_enable, dma_rx_enable);

	return retval;
}

/**
   @brief           DMAチャンネルの強制停止
*/
static
int32_t SSPMgr_DisableDma(uint8_t ch, uint32_t dma_dir)
{
	int32_t	 retval = AG903_ENONE;
	AG903_SSPMgrDmaStat*	dma_stat;
	uint8_t		dma_ch;
	uint32_t	enable;
	
	if (AG903_SSP_DMA_DIR_TX == dma_dir) {
		dma_stat    = &SspDmaStatTx[ch];
	} else if (AG903_SSP_DMA_DIR_RX == dma_dir) {
		dma_stat    = &SspDmaStatRx[ch];
	} else {
		return -AG903_EPERM;;
	}

	if (0 == dma_stat->attach) {
		return -AG903_EPERM;;
	}

	dma_ch = dma_stat->dma_ch;

	AG903_DMACPrmGetCHANNEL_ENABLE(&enable);
	enable &= ~(1<<dma_ch);
	AG903_DMACPrmSetCHANNEL_ENABLE(enable);

	return retval;
}

/**
 * @brief           SPIフォーマットでの送受信開始 (SPIモード用)
 * @param           handle [in] SSPハンドル
 * @return          伝送許可結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EBUSY  ハンドル使用中
 * @retval          -AG903_EPERM  許可されていない状態でのアクセス
 * @description     SPIフォーマットでの送受信を開始します。<p>
 *                  特にSPI設定時にSPIフラッシュ特化モードが指定されていた場合
 *                  AG903_SSPMgrSpiDisableTransfer で停止するまで
 *                  SSP_FS信号をアサートし続けたまま任意に送信または受信を繰り返すことができます。<p>
 *                  指定されていなかった場合は送信中の期間のみSSP_FS信号をアサートし
 *                  それが終わるとネゲートします。
 * @note            この関数はSSPをSPIモードにしたときにだけ使用できます。
 *                  またこの関数によりSSPを有効にした場合は下記のSPI専用の関数を使用してください。
 *                  - AG903_SSPMgrSpiSend
 *                  - AG903_SSPMgrSpiReceive
 *                  - AG903_SSPMgrSpiSendReceive
 *                  - AG903_SSPMgrSpiSendDma
 *                  - AG903_SSPMgrSpiReceiveDma
 *                  - AG903_SSPMgrSpiSendReceiveDma
 * @note            Spiが付かない通常の AG903_SSPMgrEnableTransfer で有効にした場合は
 *                  送信または受信のどちらか一方向のみ可能であるため
 *                  送受信両方でのアクセスが必要なデバイスでは使用できません。
*/
int32_t AG903_SSPMgrSpiEnableTransfer(AG903_SSPMgrHandle* handle)
{
	int32_t retval = AG903_ENONE;
	AG903_SSPPrmIntCtrl*	ictrl;
	AG903_SSPPrmCtrl2*		ctrl2;
	uint8_t ch;
	uint8_t cs;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	if (AG903_SSP_FORMAT_SPI != SspChStat[ch].format) {
		return -AG903_EPERM;
	}

	if (0 != SSPMgr_IsSpiFlashLike(ch)) {
		cs = (AG903_SSP_POL_NEGATIVE==SspSpiParam[ch].polarity) ? 0 : 1; /* Assert */
	} else {
		cs = 0;
	}

	ictrl = &SspRegStat[ch].ictrl;
	ictrl->tfthod   = SspChStat[ch].tx_thod;
	ictrl->rfthod   = SspChStat[ch].rx_thod;
	ictrl->tfdmaen  = 0;
	ictrl->rfdmaen  = 0;
	ictrl->tfthien  = 0;
	ictrl->rfthien  = 0;
	ictrl->tfurien  = 0;
	ictrl->rforien  = 0;
	AG903_SSPPrmSetIntControl(ch, ictrl);

	ctrl2 = &SspRegStat[ch].ctrl2;
	ctrl2->fsos     = 0;
	ctrl2->fs       = cs;
	ctrl2->txen     = 0;
	ctrl2->rxen     = 0;
	ctrl2->ssprst   = 0;
	ctrl2->txfclr   = 1; /* Clear Tx FIFO */
	ctrl2->rxfclr   = 1; /* Clear Rx FIFO */
	ctrl2->txdoe    = 0;
	ctrl2->sspen    = 1; /* Enable SSP */
	AG903_SSPPrmSetControl2(ch, ctrl2);
	ctrl2->txfclr   = 0;
	ctrl2->rxfclr   = 0;

	return retval;
}

/**
 * @brief           SPIフォーマットでの送受信停止 (SPIモード用)
 * @param           handle [in] SSPハンドル
 * @return          伝送停止結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  許可されていない状態でのアクセス
 * @description     SPIフォーマットでの送受信を停止します。<p>
 *                  このときSPIモードの設定時にSPIフラッシュ特化モードが指定されていた場合は
 *                  SSP_FS信号をネゲートします。<p>
*/
int32_t AG903_SSPMgrSpiDisableTransfer(AG903_SSPMgrHandle* handle)
{
	int32_t retval = AG903_ENONE;
	AG903_SSPPrmIntCtrl*	ictrl;
	AG903_SSPPrmCtrl2*		ctrl2;
	uint8_t ch;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	if (AG903_SSP_FORMAT_SPI != SspChStat[ch].format) {
		return -AG903_EPERM;
	}

	ictrl = &SspRegStat[ch].ictrl;
	ictrl->tfthod   = 2; /* SSP reset value */
	ictrl->rfthod   = 2; /* SSP reset value */
	ictrl->tfdmaen  = 0;
	ictrl->rfdmaen  = 0;
	ictrl->tfthien  = 0;
	ictrl->rfthien  = 0;
	ictrl->tfurien  = 0;
	ictrl->rforien  = 0;
	AG903_SSPPrmSetIntControl(ch, ictrl);

	ctrl2 = &SspRegStat[ch].ctrl2;
	ctrl2->fsos     = 0;
	ctrl2->fs       = 0;
	ctrl2->txen     = 0;
	ctrl2->rxen     = 0;
	ctrl2->ssprst   = 0;
	ctrl2->txfclr   = 1; /* Clear Tx FIFO */
	ctrl2->rxfclr   = 1; /* Clear Rx FIFO */
	ctrl2->txdoe    = 0;
	ctrl2->sspen    = 0; /* Enable SSP */
	AG903_SSPPrmSetControl2(ch, ctrl2);
	ctrl2->txfclr   = 0;
	ctrl2->rxfclr   = 0;

	return retval;
}

/**
 * @brief           SSPへの送信DMA取り付け
 * @param           handle [in] SSPハンドル
 * @param           dma_ch [in] DMAチャンネル
 * @param           dma_if [in] DMAインターフェース
 * @return          DMA取り付け結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常（DMA取り付け済み等)
 * @description     SSPのデータ送信でライブラリが内部的に使用するDMAを取り付けます。<p>
 *                  実際にDMA転送するときは AG903_SSPMgrEnableDmaMode で転送方向を指定したうえで
 *                  この関数でDMAを取り付け
 *                  SSPライブラリにあるDMA転送用関数で送信してください。
 * @note            この関数でDMAを取り付けない場合はCPU転送または外部プログラムによるDMA転送になります。
 * @note            DMAC割り込み(IRQ26)がシステム全体で禁止になっているときは別途許可してください。
 * @note            dma_ch,dma_ifはシステム全体で重複がないように選んでください。
 */
int32_t AG903_SSPMgrAttachTxDma(AG903_SSPMgrHandle* handle, uint8_t dma_ch, uint8_t dma_if)
{
	int32_t	retval = AG903_ENONE;
	uint8_t	ch;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	return SSPMgr_AttachDma(ch, AG903_SSP_DMA_DIR_TX, dma_ch, dma_if);
}

/**
 * @brief           SSPへの受信DMA取り付け
 * @param           handle [in] SSPハンドル
 * @param           dma_ch [in] DMAチャンネル
 * @param           dma_if [in] DMAインターフェース
 * @return          DMA取り付け結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常（DMA取り付け済み等)
 * @description     SSPのデータ受信でライブラリが内部的に使用するDMAを取り付けます。<p>
 *                  実際にDMA転送するときは AG903_SSPMgrEnableDmaMode で転送方向を指定したうえで
 *                  この関数でDMAを取り付け
 *                  SSPライブラリにあるDMA転送用関数で受信してください。
 * @note            この関数でDMAを取り付けない場合はCPU転送または外部プログラムによるDMA転送になります。
 * @note            DMAC割り込み(IRQ26)がシステム全体で禁止になっているときは別途許可してください。
 * @note            dma_ch,dma_ifはシステム全体で重複がないように選んでください。
 */
int32_t AG903_SSPMgrAttachRxDma(AG903_SSPMgrHandle* handle, uint8_t dma_ch, uint8_t dma_if)
{
	int32_t	retval = AG903_ENONE;
	uint8_t	ch;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	return SSPMgr_AttachDma(ch, AG903_SSP_DMA_DIR_RX, dma_ch, dma_if);
}

static
int32_t SSPMgr_AttachDma(uint8_t ch, uint8_t dma_dir, uint8_t dma_ch, uint8_t dma_if)
{
	AG903_SSPMgrDmaStat	dmastat_clr = {0};
	AG903_INTMgrHdrPrm	inthdr = {0};
	int32_t	int_hnd;
	int32_t	retval = AG903_ENONE;
	uint8_t	ssp_enable;
	AG903_SSPMgrDmaStat*	dma_stat;
	AG903_SSPMgrIntHdr	isr;
	static const AG903_SSPMgrIntHdr	isr_tx[AG903_SSP_CH_NUM] = {
		SSPMgr_IntHdr0TxDma,
		SSPMgr_IntHdr1TxDma,
		SSPMgr_IntHdr2TxDma,
		SSPMgr_IntHdr3TxDma,
	};
	static const AG903_SSPMgrIntHdr	isr_rx[AG903_SSP_CH_NUM] = {
		SSPMgr_IntHdr0RxDma,
		SSPMgr_IntHdr1RxDma,
		SSPMgr_IntHdr2RxDma,
		SSPMgr_IntHdr3RxDma,
	};

	if (dma_if >= 16) {
		return -AG903_EINVAL;
	}

	AG903_SSPPrmCheckEnable(ch, &ssp_enable);
	if (0 != ssp_enable) {
		return -AG903_EPERM;
	}

	
	if (AG903_SSP_DMA_DIR_TX == dma_dir) {
		dma_stat    = &SspDmaStatTx[ch];
		isr         = isr_tx[ch];
	} else if (AG903_SSP_DMA_DIR_RX == dma_dir) {
		dma_stat    = &SspDmaStatRx[ch];
		isr         = isr_rx[ch];
	} else {
		return -AG903_EPERM;
	}

	if (0 != dma_stat->attach) {
		return -AG903_EPERM;
	}

	inthdr.atr   = AG903_INT_HLNG;
	inthdr.intno = AG903_IRQ26_DMAC;
	inthdr.func  = (void*)isr;
	retval = AG903_INTMgrSetHandler(&inthdr);
	if(0 > retval) {
		return retval;
	}
	int_hnd = retval;

	*dma_stat               = dmastat_clr;
	dma_stat->attach        = 1;
	dma_stat->stat          = AG903_SSP_DMA_STAT_STOP;
	dma_stat->dma_ch        = dma_ch;
	dma_stat->dma_if        = dma_if;
	dma_stat->ctrlwidth     = 0;                          /* 転送時確定 */
	dma_stat->ctrlbeat      = 0;
	dma_stat->txrxdr        = 0xE0400018 + 0x100000*ch;
	dma_stat->hdrid         = int_hnd;

	return AG903_ENONE;
}

/**
 * @brief           SSPからの送信DMA取り外し
 * @param           handle [in] SSPハンドル
 * @return          送信DMA取り外し結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常（DMA取り外し済み等)
 * @description     SSPのデータ転送でライブラリが内部的に使用するDMAを取り外します。<p>
 * @note            DMAC割り込み(IRQ26)はシステム全体で不要になったとき別途禁止にしてください。
 */
int32_t AG903_SSPMgrDetachTxDma(AG903_SSPMgrHandle* handle)
{
	int32_t	retval = AG903_ENONE;
	uint8_t	ch;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	return SSPMgr_DetachDma(ch, AG903_SSP_DMA_DIR_TX);
}

/**
 * @brief           SSPからの受信DMA取り外し
 * @param           handle [in] SSPハンドル
 * @return          受信DMA取り外し結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常（DMA取り外し済み等)
 * @description     SSPのデータ転送でライブラリが内部的に使用するDMAを取り外します。<p>
 * @note            DMAC割り込み(IRQ26)はシステム全体で不要になったとき別途禁止にしてください。
 */
int32_t AG903_SSPMgrDetachRxDma(AG903_SSPMgrHandle* handle)
{
	int32_t	retval = AG903_ENONE;
	uint8_t	ch;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	return SSPMgr_DetachDma(ch, AG903_SSP_DMA_DIR_RX);
}

static
int32_t SSPMgr_DetachDma(uint8_t ch, uint8_t dma_dir)
{
	AG903_SSPMgrDmaStat	dmastat_clr = {0};
	int32_t	retval = AG903_ENONE;
	uint8_t	ssp_enable;
	AG903_SSPMgrDmaStat*	dma_stat;

	AG903_SSPPrmCheckEnable(ch, &ssp_enable);
	if (0 != ssp_enable) {
		return -AG903_EPERM;
	}

	if (AG903_SSP_DMA_DIR_TX == dma_dir) {
		dma_stat    = &SspDmaStatTx[ch];
	} else if (AG903_SSP_DMA_DIR_RX == dma_dir) {
		dma_stat    = &SspDmaStatRx[ch];
	} else {
		return -AG903_EPERM;
	}

	if (0 == dma_stat->attach) {
		return -AG903_EPERM;
	}

	AG903_INTMgrDeleteHandler(dma_stat->hdrid);

	*dma_stat               = dmastat_clr;
	dma_stat->attach        = 0;
	dma_stat->stat          = AG903_SSP_DMA_STAT_STOP;
	dma_stat->dma_ch        = 0;
	dma_stat->dma_if        = 0;
	dma_stat->ctrlwidth     = 0;
	dma_stat->ctrlbeat      = 0;
	dma_stat->txrxdr        = 0;
	dma_stat->hdrid         = -1;

	return retval;
}

/**
 * @brief           送信DMA転送ビート数の設定
 * @param           handle [in] SSPハンドル
 * @return          送信DMA転送ビート数の設定結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常（DMA取り付け無し等)
 * @description     SSPのデータ転送でライブラリが内部的に使用するDMAのDMA転送ビート数を設定します。
 * @note            ビート数はDMA要求あたりの転送ワード数を意味し大きいほど転送効率がよくなります。
 * @note            設定可能な値は1,2,4,8,16のいずれかで次の条件を満たす値です。
 *                  - 送信FIFOのしきい値による制限を満たすこと
 *                  - 転送データ数がビート数で割り切れること
 *                  条件を満たせない場合は1を設定してください。
 * @note            しきい値の制限は AG903_SSPMgrSetTxFifoThreshold をご覧ください。
 */
int32_t AG903_SSPMgrSetTxDmaBeat(AG903_SSPMgrHandle* handle, uint8_t beat)
{
	int32_t	retval = AG903_ENONE;
	uint8_t	ch;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	return SSPMgr_SetDmaBeat(ch, AG903_SSP_DMA_DIR_TX, beat);
}

/**
 * @brief           受信DMA転送ビート数の設定
 * @param           handle [in] SSPハンドル
 * @return          受信DMA転送ビート数の設定結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常（DMA取り付け無し等)
 * @description     SSPのデータ転送でライブラリが内部的に使用するDMAのDMA転送ビート数を設定します。
 * @note            ビート数はDMA要求あたりの転送ワード数を意味し大きいほど転送効率がよくなります。
 * @note            設定可能な値は1,2,4,8,16のいずれかで次の条件を満たす値です。
 *                  - 受信FIFOのしきい値による制限を満たすこと
 *                  - 転送データ数がビート数で割り切れること
 *                  条件を満たせない場合は1を設定してください。
 * @note            しきい値の制限は AG903_SSPMgrSetRxFifoThreshold をご覧ください。
 */
int32_t AG903_SSPMgrSetRxDmaBeat(AG903_SSPMgrHandle* handle, uint8_t beat)
{
	int32_t	retval = AG903_ENONE;
	uint8_t	ch;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}

	return SSPMgr_SetDmaBeat(ch, AG903_SSP_DMA_DIR_RX, beat);
}

static
int32_t SSPMgr_SetDmaBeat(uint8_t ch, uint8_t dma_dir, uint8_t beat)
{
	int32_t	 retval = AG903_ENONE;
	AG903_SSPMgrDmaStat*	dma_stat;
	
	if (AG903_SSP_DMA_DIR_TX == dma_dir) {
		dma_stat    = &SspDmaStatTx[ch];
	} else if (AG903_SSP_DMA_DIR_RX == dma_dir) {
		dma_stat    = &SspDmaStatRx[ch];
	} else {
		return -AG903_EPERM;
	}

	if (0 == dma_stat->attach) {
		return -AG903_EPERM;
	}

	switch (beat) {
	case 1:
		dma_stat->ctrlbeat  = 0;
		break;
	case 2:
		dma_stat->ctrlbeat  = 1;
		break;
	case 4:
		dma_stat->ctrlbeat  = 2;
		break;
	case 8:
		dma_stat->ctrlbeat  = 3;
		break;
	case 16:
		dma_stat->ctrlbeat  = 4;
		break;
	default:
		return -AG903_EINVAL;
	}

	return retval;
}

/**
 * @brief           DMAによるデータ送信
 * @param           handle [in] SSPハンドル
 * @param           buf [in] 送信データ先頭アドレス
 * @param           size [in] 送信サイズ(単位[Word] 下記参照)
 * @return          送信結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常
 * @description     DMAによりデータを送信します。<p>
 * @note            この関数は AG903_SSPMgrEnableTransfer と組み合わせて使用します。
 * @note            実行前に AG903_SSPMgrEnableDmaMode によりDMAによる送信を有効にし、
 *                  AG903_SSPMgrAttachDma でSSPにDMAを取り付けてください。
 * @note            転送を開始すると直ちに戻ります。
 *                  転送の完了は AG903_SSPMgrSetCallback によるコールバックで確認できます。
 * @note            送信データのバッファはキャッシュ不可メモリに確保するか送信前にCleanしてください。
 * @note            SSPからのDMA要求に対し AG903_DMACMgrSetSyncPeripheral と同様の同期が設定されます。
 * @note            データワードのビット長とシリアライズについては AG903_SSPMgrSetSendBuf での説明と同様です。
*/
int32_t AG903_SSPMgrSendDma(AG903_SSPMgrHandle* handle, const uint8_t* buf, uint32_t size)
{
	int32_t	 retval = AG903_ENONE;
	uint8_t	 ch;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if((NULL == buf) || (0 >= size)) {
		return -AG903_EINVAL;
	}

	if (AG903_SSP_DMA_SEND & SspChStat[ch].dma) {
		return SSPMgr_SendDma(ch, buf, size);
	} else {
		return -AG903_EPERM;
	}
}

/**
 * @brief           DMAによるデータ受信
 * @param           handle [in] SSPハンドル
 * @param           buf [in] 受信データ先頭アドレス
 * @param           size [in] 受信サイズ(単位[Word] 下記参照)
 * @return          受信結果
 * @retval          AG903_ENONE   正常終了
 * @retval          -AG903_EINVAL 引数異常
 * @retval          -AG903_EPERM  状態異常
 * @description     DMAによりデータを受信します。<p>
 * @note            SPIモード以外では使用できません。
 * @note            この関数は AG903_SSPMgrEnableTransfer と組み合わせて使用します。
 * @note            実行前に AG903_SSPMgrEnableDmaMode によりDMAによる受信を有効にし、
 *                  AG903_SSPMgrAttachDma でSSPにDMAを取り付けてください。
 * @note            転送を開始すると直ちに戻ります。
 *                  転送の完了は AG903_SSPMgrSetCallback によるコールバックで確認できます。
 * @note            受信データのバッファはキャッシュ不可メモリに確保するか受信後にInvalidateしてください。
 * @note            SSPからのDMA要求に対し AG903_DMACMgrSetSyncPeripheral と同様の同期が設定されます。
 * @note            データワードのビット長とシリアライズについては AG903_SSPMgrSetReceiveBuf での説明と同様です。
*/
int32_t AG903_SSPMgrReceiveDma(AG903_SSPMgrHandle* handle, uint8_t* buf, uint32_t size)
{
	int32_t	 retval = AG903_ENONE;
	uint8_t	 ch;

	retval = SSPMgr_CheckHandle(handle, &ch);
	if(AG903_ENONE != retval) {
		return -AG903_EINVAL;
	}
	if((NULL == buf) || (0 >= size)) {
		return -AG903_EINVAL;
	}

	if (AG903_SSP_DMA_RECEIVE & SspChStat[ch].dma) {
		return SSPMgr_ReceiveDma(ch, buf, size);
	} else {
		return -AG903_EPERM;
	}
}

static int32_t SSPMgr_WriteTxFifoBest(uint8_t ch, const void* buf, uint32_t num)
{
	const uint8_t *src;
	AG903_SSPPrmStatus ssp_status;
	uint32_t free_num;
	uint32_t tx_num;
	uint32_t val;

	src = buf;
	AG903_SSPPrmGetStatus(ch, &ssp_status);
	free_num = AG903_SSP_FIFO_DEPTH - ssp_status.txfifo_num;
	tx_num = (num < free_num) ? num : free_num;
	for (uint32_t ii = 0; ii < tx_num; ii++) {
		val = 0;
		for (int ib = 0; ib < SspChStat[ch].wordlen; ib += 8) {
			val |= (uint32_t)((*src++)<<ib);
		}
		AG903_SSPPrmSetData(ch, val);
	}

	return tx_num;
}

static int32_t SSPMgr_ReadRxFifoBest(uint8_t ch, void* buf, uint32_t num)
{
	uint8_t *dst;
	AG903_SSPPrmStatus ssp_status;
	uint32_t valid_num;
	uint32_t rx_num;
	uint32_t val;

	dst = buf;
	AG903_SSPPrmGetStatus(ch, &ssp_status);
	valid_num = ssp_status.rxfifo_num;
	rx_num = (num < valid_num) ? num : valid_num;
	for (uint32_t ii = 0; ii < rx_num; ii++) {
		AG903_SSPPrmGetData(ch, &val);
		for (int ib = 0; ib < SspChStat[ch].wordlen; ib += 8) {
			*dst++ = (uint8_t)(val>>ib & 0xff);
		}
	}

	return rx_num;
}

/**
   @brief AG903_SSPMgrSendのDMA転送部
   呼び出し元で確認している変数の妥当性は再確認していません。
*/
static int32_t SSPMgr_SendDma(uint8_t ch, const void* buf, uint32_t num)
{
	uint32_t	sizeof_word;
	const uint8_t*	src;
	uint32_t	txed_num;
	uint32_t	tx_num;
	DMACPrmParamCFG		dcfg_tx = {0};
	DMACPrmParamCTRL	dctrl_tx = {0};
	uint8_t	dma_ch;
	uint8_t	dma_if;

	if (0 == SspDmaStatTx[ch].attach) {
		return -AG903_EPERM;
	}
		
	sizeof_word = (SspChStat[ch].wordlen + 8-1)/8;
	src         = buf;
	txed_num    = 0;
	dma_ch      = SspDmaStatTx[ch].dma_ch;

	/* Initial setup */
	if (AG903_SSP_DMA_STAT_STOP == SspDmaStatTx[ch].stat) {
		SspDmaStatTx[ch].stat = AG903_SSP_DMA_STAT_XMIT;

		/* DMA setup */
		dma_if  = SspDmaStatTx[ch].dma_if;
		SSPMgr_SetDmaInterface(ch, dma_if, AG903_SSP_DMA_DIR_TX);

		if (SspChStat[ch].wordlen > 16) {
			SspDmaStatTx[ch].ctrlwidth = 2;
		} else if (SspChStat[ch].wordlen > 8) {
			SspDmaStatTx[ch].ctrlwidth = 1;
		} else {
			SspDmaStatTx[ch].ctrlwidth = 0;
		}

		dcfg_tx.TCIntMsk    = 0; /* Terminal count interrupt mask 0:Disable */
		dcfg_tx.ErrIntMsk   = 1; /* Error interrupt mask 0:Disable */
		dcfg_tx.AbtIntMsk   = 1; /* Abort interrupt mask 0:Disable */
		dcfg_tx.SrcRS       = 0; /* No use; no handshake with source device */
		dcfg_tx.SrcHEn      = 0; /* Hadshake with source 0:Disable */
		dcfg_tx.DstRS       = SspDmaStatTx[ch].dma_if; /* DMA request select */
		dcfg_tx.DstHEn      = 1; /* Hnadshake with destination 1:Enable */
		//dcfg_tx.LLPCnt    = 0; /* (Read only) */
		dcfg_tx.ChGntWin    = 0; /* Grant window */
		dcfg_tx.ChPri       = 0; /* 0:Low priority */
		dcfg_tx.WOMode      = 0; /* Write only mode 0:Disable */
		dcfg_tx.UnalignMode = 0; /* Unaligned mode 0:Disable */
		AG903_DMACPrmSetCFG_REG(dma_ch, &dcfg_tx);

		AG903_DMACPrmSetLINK_LIST_POINTER(dma_ch, 0x00000000);

		AG903_DMACPrmSetDST_ADDR(dma_ch, SspDmaStatTx[ch].txrxdr);
	}

	AG903_DMACPrmSetSRC_ADDR(dma_ch, (uint32_t)&src[txed_num*sizeof_word]);

	tx_num = num - txed_num;
	AG903_DMACPrmSetTRNS_SIZE_1D(dma_ch, tx_num);

	dctrl_tx.ExpEn      = 0; /* Expand 0:Disable bits[14:0] */
	dctrl_tx.ChEn       = 1; /* Channel 1:Enable */
	dctrl_tx.WDTEn      = 0; /* 0:Disable */
	dctrl_tx.DstCtrl    = 2; /* 2:Fixed */
	dctrl_tx.SrcCtrl    = 0; /* 0:Incremental */
	dctrl_tx.DstWidth   = SspDmaStatTx[ch].ctrlwidth;
	dctrl_tx.SrcWidth   = SspDmaStatTx[ch].ctrlwidth;
	dctrl_tx.TCMsk      = 0; /* 0:Don't mask TCnt status */
	dctrl_tx.SrcTcnt    = SspDmaStatTx[ch].ctrlbeat;
	AG903_DMACPrmSetCTRL_REG(dma_ch, &dctrl_tx);
		
	return AG903_ENONE;
}

/**
   @brief AG903_SSPMgrReceiveのDMA転送部
   呼び出し元で確認している変数の妥当性は再確認していません。
*/
static int32_t SSPMgr_ReceiveDma(uint8_t ch, void* buf, uint32_t num)
{
	uint32_t	sizeof_word;
	uint8_t*	dst;
	uint32_t	rxed_num;
	uint32_t	rx_num;
	DMACPrmParamCFG		dcfg_rx = {0};
	DMACPrmParamCTRL	dctrl_rx = {0};
	uint8_t	dma_ch;
	uint8_t	dma_if;

	if (0 == SspDmaStatRx[ch].attach) {
		return -AG903_EPERM;
	}

	sizeof_word = (SspChStat[ch].wordlen + 8-1)/8;
	dst         = buf;
	rxed_num    = 0;
	dma_ch      = SspDmaStatRx[ch].dma_ch;

	/* Initial setup */
	if (AG903_SSP_DMA_STAT_STOP == SspDmaStatRx[ch].stat) {
		SspDmaStatRx[ch].stat = AG903_SSP_DMA_STAT_XMIT;

		/* DMA setup */
		dma_if  = SspDmaStatRx[ch].dma_if;
		SSPMgr_SetDmaInterface(ch, dma_if, AG903_SSP_DMA_DIR_RX);

		if (SspChStat[ch].wordlen > 16) {
			SspDmaStatRx[ch].ctrlwidth = 2;
		} else if (SspChStat[ch].wordlen > 8) {
			SspDmaStatRx[ch].ctrlwidth = 1;
		} else {
			SspDmaStatRx[ch].ctrlwidth = 0;
		}

		dcfg_rx.TCIntMsk    = 0; /* Terminal count interrupt mask 0:Disable */
		dcfg_rx.ErrIntMsk   = 1; /* Error interrupt mask 0:Disable */
		dcfg_rx.AbtIntMsk   = 1; /* Abort interrupt mask 0:Disable */
		dcfg_rx.SrcRS       = SspDmaStatRx[ch].dma_if; /* DMA request select */
		dcfg_rx.SrcHEn      = 1; /* Hnadshake with destination 1:Enable */
		dcfg_rx.DstRS       = 0; /* No use; no handshake with source device */
		dcfg_rx.DstHEn      = 0; /* Hadshake with source 0:Disable */
		//dcfg_rx.LLPCnt    = 0; /* (Read only) */
		dcfg_rx.ChGntWin    = 0; /* Grant window */
		dcfg_rx.ChPri       = 0; /* 0:Low priority */
		dcfg_rx.WOMode      = 0; /* Write only mode 0:Disable */
		dcfg_rx.UnalignMode = 0; /* Unaligned mode 0:Disable */
		AG903_DMACPrmSetCFG_REG(dma_ch, &dcfg_rx);

		AG903_DMACPrmSetLINK_LIST_POINTER(dma_ch, 0x00000000);

		AG903_DMACPrmSetSRC_ADDR(dma_ch, SspDmaStatRx[ch].txrxdr);
	}

	AG903_DMACPrmSetDST_ADDR(dma_ch, (uint32_t)&dst[rxed_num*sizeof_word]);

	rx_num = num - rxed_num;
	AG903_DMACPrmSetTRNS_SIZE_1D(dma_ch, rx_num);

	dctrl_rx.ExpEn      = 0; /* Expand 0:Disable bits[14:0] */
	dctrl_rx.ChEn       = 1; /* Channel 1:Enable */
	dctrl_rx.WDTEn      = 0; /* 0:Disable */
	dctrl_rx.DstCtrl    = 0; /* 0:Incremental */
	dctrl_rx.SrcCtrl    = 2; /* 2:Fixed */
	dctrl_rx.DstWidth   = SspDmaStatRx[ch].ctrlwidth;
	dctrl_rx.SrcWidth   = SspDmaStatRx[ch].ctrlwidth;
	dctrl_rx.TCMsk      = 0; /* 0:Don't mask TCnt status */
	dctrl_rx.SrcTcnt    = SspDmaStatRx[ch].ctrlbeat;
	AG903_DMACPrmSetCTRL_REG(dma_ch, &dctrl_rx);
		
	return AG903_ENONE;
}

static int32_t SSPMgr_SpiSendTemplate(uint8_t ch, const void* buf, uint32_t num, uint8_t dma_tx_enable)
{
	int32_t	retval = AG903_ENONE;	
	AG903_SSPPrmIntCtrl*	ictrl;
	AG903_SSPPrmCtrl2*		ctrl2;
	uint32_t	istatus;
	uint32_t	sizeof_word;   
	const uint8_t*	tx_buf;
	uint32_t	tx_num;
	uint32_t	txed_num;
	uint32_t	event;

	if (AG903_SSP_FORMAT_SPI != SspChStat[ch].format) {
		return -AG903_EPERM;
	}

	/* DMAでの0ワード転送は割り込みが発生しない */
	if (0 == num) {
		return -AG903_EINVAL;
	}

	/* SpiEnableTransferでは送受信に関する有効化は実際の転送開始時に設定する */
	AG903_SSPPrmGetIntStatus(ch, &istatus); /* Read clear SSP interrupt status */

	ictrl = &SspRegStat[ch].ictrl;
	ictrl->tfthod   = SspChStat[ch].tx_thod;
	ictrl->rfthod   = SspChStat[ch].rx_thod;
	if (0 == dma_tx_enable) {
		ictrl->tfdmaen  = 0;
		ictrl->rfdmaen  = 0;
	} else {
		ictrl->tfdmaen  = 1;
		ictrl->rfdmaen  = 0;
	}
	ictrl->tfthien  = 0;
	ictrl->rfthien  = 0;
	ictrl->tfurien  = 0;
	ictrl->rforien  = 0;
	AG903_SSPPrmSetIntControl(ch, ictrl);

	ctrl2 = &SspRegStat[ch].ctrl2;
	ctrl2->txen     = 1;
	ctrl2->txdoe    = 1;
	ctrl2->rxen     = 0;
	AG903_SSPPrmSetControl2(ch, ctrl2);

	if (0 == dma_tx_enable) {
		sizeof_word = (SspChStat[ch].wordlen + 8-1)/8;
		tx_buf      = buf;
		tx_num      = num;
		txed_num    = 0;
		while (txed_num < tx_num) {
			txed_num += SSPMgr_WriteTxFifoBest(ch, &tx_buf[txed_num*sizeof_word], tx_num-txed_num);
		}

		if (NULL != SspHandleStat[ch].clbk) {
			event = 0;
			event |= AG903_SSP_EVENT_SNDEND;
			SspHandleStat[ch].clbk((AG903_SSPMgrHandle*)&SspHandleStat[ch], event);
		}

		retval = AG903_ENONE;
	} else {
		retval = SSPMgr_SendDma(ch, buf, num);
	}

	return retval;
}

static int32_t SSPMgr_SpiReceiveTemplate(uint8_t ch, void* buf, uint32_t num, uint8_t dma_rx_enable)
{
	int32_t	retval = AG903_ENONE;	
	AG903_SSPPrmIntCtrl*	ictrl;
	AG903_SSPPrmCtrl2*		ctrl2;
	uint32_t	istatus;
	uint32_t	sizeof_word;   
	uint8_t*	rx_buf;
	uint32_t	rx_num;
	uint32_t	rxed_num;
	uint32_t	event;

	if (AG903_SSP_FORMAT_SPI != SspChStat[ch].format) {
		return -AG903_EPERM;
	}

	/* DMAでの0ワード転送は割り込みが発生しない */
	if (0 == num) {
		return -AG903_EINVAL;
	}

	/* SpiEnableTransferでは送受信に関する有効化は実際の転送開始時に設定する */
	AG903_SSPPrmGetIntStatus(ch, &istatus); /* Read clear SSP interrupt status */

	ictrl = &SspRegStat[ch].ictrl;
	ictrl->tfthod   = SspChStat[ch].tx_thod;
	ictrl->rfthod   = SspChStat[ch].rx_thod;
	if (0 == dma_rx_enable) {
		ictrl->tfdmaen  = 0;
		ictrl->rfdmaen  = 0;
	} else {
		ictrl->tfdmaen  = 0;
		ictrl->rfdmaen  = 1;
	}
	ictrl->tfthien  = 0;
	ictrl->rfthien  = 0;
	ictrl->tfurien  = 0;
	ictrl->rforien  = 0;
	AG903_SSPPrmSetIntControl(ch, ictrl);

	ctrl2 = &SspRegStat[ch].ctrl2;
	ctrl2->txen     = 0;
	ctrl2->txdoe    = 0;
	ctrl2->rxen     = 1;
	AG903_SSPPrmSetControl2(ch, ctrl2);

	if (0 == dma_rx_enable) {
		sizeof_word = (SspChStat[ch].wordlen + 8-1)/8;
		rx_buf      = buf;
		rx_num      = num;
		rxed_num    = 0;
		while (rxed_num < rx_num) {
			rxed_num += SSPMgr_ReadRxFifoBest(ch, &rx_buf[rxed_num*sizeof_word], rx_num-rxed_num);
		}

		if (NULL != SspHandleStat[ch].clbk) {
			event = 0;
			event |= AG903_SSP_EVENT_RCVEND;
			SspHandleStat[ch].clbk((AG903_SSPMgrHandle*)&SspHandleStat[ch], event);
		}
	} else {
		retval = SSPMgr_ReceiveDma(ch, buf, num);
	}

	return retval;
}

static int32_t SSPMgr_SpiSendReceiveTemplate(uint8_t ch, const void *tx_buf, uint32_t tx_num, void* rx_buf, uint32_t rx_num, uint8_t dma_tx_enable, uint8_t dma_rx_enable)
{
	int32_t	retval = AG903_ENONE;	
	AG903_SSPPrmIntCtrl*	ictrl;
	AG903_SSPPrmCtrl2*		ctrl2;
	uint32_t	istatus;
	uint32_t	sizeof_word;   
	const uint8_t*	tx_buf8;
	uint8_t*	rx_buf8;
	uint32_t	txed_num;
	uint32_t	rxed_num;
	uint32_t	event;

	if (AG903_SSP_FORMAT_SPI != SspChStat[ch].format) {
		return -AG903_EPERM;
	}

	if (0 == tx_num || 0 == rx_num || tx_num < rx_num) {
		return -AG903_EINVAL;
	}

	if (0 == SspDmaStatTx[ch].attach || 0 == (SspChStat[ch].dma & AG903_SSP_DMA_SEND)) {
		dma_tx_enable = 0;
	}
	if (0 == SspDmaStatRx[ch].attach || 0 == (SspChStat[ch].dma & AG903_SSP_DMA_RECEIVE)) {
		dma_rx_enable = 0;
	}

	/* SpiEnableTransferでは送受信に関する有効化は実際の転送開始時に設定する */
	AG903_SSPPrmGetIntStatus(ch, &istatus); /* Read clear SSP interrupt status */

	ictrl = &SspRegStat[ch].ictrl;
	ictrl->tfthod   = SspChStat[ch].tx_thod;
	ictrl->rfthod   = SspChStat[ch].rx_thod;
	if (0 == dma_tx_enable) {
		ictrl->tfdmaen  = 0;
	} else {
		ictrl->tfdmaen  = 1;
	}
	if (0 == dma_rx_enable) {
		ictrl->rfdmaen  = 0;
	} else {
		ictrl->rfdmaen  = 1;
	}
	ictrl->tfthien  = 0;
	ictrl->rfthien  = 0;
	ictrl->tfurien  = 0;
	ictrl->rforien  = 0;
	AG903_SSPPrmSetIntControl(ch, ictrl);

	sizeof_word = (SspChStat[ch].wordlen + 8-1)/8;
	tx_buf8     = tx_buf;
	txed_num    = 0;
	rx_buf8     = rx_buf;
	rxed_num    = 0;
	/* .SSPEN=1で送信データ無しでも送信開始するモードがあるため事前準備 */
	txed_num += SSPMgr_WriteTxFifoBest(ch, &tx_buf8[txed_num*sizeof_word], tx_num-txed_num);

	ctrl2 = &SspRegStat[ch].ctrl2;
	ctrl2->txen     = 1;
	ctrl2->txdoe    = 1; /* Masterのとき無効(ただしSPIモードかつLSB firstのときは1) */
	ctrl2->rxen     = 1;
	AG903_SSPPrmSetControl2(ch, ctrl2);

	if (0 == dma_tx_enable && 0 == dma_rx_enable) {
		retval = AG903_ENONE;
		while (txed_num < tx_num || rxed_num < rx_num) {
			if (rxed_num < rx_num) {
				rxed_num += SSPMgr_ReadRxFifoBest(ch, &rx_buf8[rxed_num*sizeof_word], rx_num-rxed_num);
			}
			if (txed_num < tx_num) {
				txed_num += SSPMgr_WriteTxFifoBest(ch, &tx_buf8[txed_num*sizeof_word], tx_num-txed_num);
			}
		}
		if (NULL != SspHandleStat[ch].clbk) {
			event = 0;
			event |= AG903_SSP_EVENT_SNDEND;
			event |= AG903_SSP_EVENT_RCVEND;
			SspHandleStat[ch].clbk((AG903_SSPMgrHandle*)&SspHandleStat[ch], event);
		}
	} else if (0 != dma_tx_enable && 0 == dma_rx_enable) {
		retval = SSPMgr_SendDma(ch, tx_buf, tx_num);
		if (AG903_ENONE != retval) {
			AG903_SSPPrmClearTxFifo(ch); /* clear SSPMgr_WriteTxFifoBest work */
			AG903_SSPPrmClearRxFifo(ch);
			return retval;
		}
		while (rxed_num < rx_num) {
			rxed_num += SSPMgr_ReadRxFifoBest(ch, &rx_buf8[rxed_num*sizeof_word], rx_num-rxed_num);
		}
		if (NULL != SspHandleStat[ch].clbk) {
			event = 0;
			event |= AG903_SSP_EVENT_RCVEND;
			SspHandleStat[ch].clbk((AG903_SSPMgrHandle*)&SspHandleStat[ch], event);
		}
	} else if (0 == dma_tx_enable && 0 != dma_rx_enable) {
		retval = SSPMgr_ReceiveDma(ch, rx_buf, rx_num);
		if (AG903_ENONE != retval) {
			AG903_SSPPrmClearTxFifo(ch); /* clear SSPMgr_WriteTxFifoBest work */
			AG903_SSPPrmClearRxFifo(ch);
			return retval;
		}
		while (txed_num < tx_num) {
			txed_num += SSPMgr_WriteTxFifoBest(ch, &tx_buf8[txed_num*sizeof_word], tx_num-txed_num);
		}
		if (NULL != SspHandleStat[ch].clbk) {
			event = 0;
			event |= AG903_SSP_EVENT_SNDEND;
			SspHandleStat[ch].clbk((AG903_SSPMgrHandle*)&SspHandleStat[ch], event);
		}
	} else {
		retval = SSPMgr_ReceiveDma(ch, rx_buf, rx_num);
		if (AG903_ENONE != retval) {
			AG903_SSPPrmClearTxFifo(ch); /* clear SSPMgr_WriteTxFifoBest work */
			AG903_SSPPrmClearRxFifo(ch);
			return retval;
		}
		retval = SSPMgr_SendDma(ch, tx_buf, tx_num);
		if (AG903_ENONE != retval) {
			SSPMgr_DisableDma(ch, AG903_SSP_DMA_DIR_RX);
			AG903_SSPPrmClearTxFifo(ch); /* clear SSPMgr_WriteTxFifoBest work */
			AG903_SSPPrmClearRxFifo(ch);
			return retval;
		}
	}

	return retval;
}

static int32_t SSPMgr_IsSpiFlashLike(uint8_t ch)
{
	if (AG903_SSP_FORMAT_SPI == SspChStat[ch].format && SspSpiParam[ch].flash) {
		return 1;
	}
	return 0;
}

static void SSPMgr_SetDmaInterface(uint8_t ssp_ch, uint8_t dma_if, uint8_t dma_dir)
{
	int dmasel_if_d4 = dma_if/4;
	int dmasel_if_m4 = dma_if%4;

	int dma_no = 23 + 2*ssp_ch + (AG903_SSP_DMA_DIR_RX==dma_dir ? 0 : 1);
	int dmasel_dev = dma_no + 1;

	uint32_t dmasel;
	AG903_SSCPrmGetDmaInterface(dmasel_if_d4, &dmasel);
	dmasel &= ~(0xff << dmasel_if_m4*8);
	dmasel |= (dmasel_dev<< dmasel_if_m4*8);
	AG903_SSCPrmSetDmaInterface(dmasel_if_d4, dmasel);

	uint32_t synreq;
	AG903_DMACPrmGetSYNC_PERI_IF(&synreq);
	synreq |= 1<<dma_if;
	AG903_DMACPrmSetSYNC_PERI_IF(synreq);
}

static void SSPMgr_IntHdr0TxDma(void)
{
	SSPMgr_IntProcessDma(0, AG903_SSP_DMA_DIR_TX);
	return;
}

static void SSPMgr_IntHdr0RxDma(void)
{
	SSPMgr_IntProcessDma(0, AG903_SSP_DMA_DIR_RX);
	return;
}

static void SSPMgr_IntHdr1TxDma(void)
{
	SSPMgr_IntProcessDma(1, AG903_SSP_DMA_DIR_TX);
	return;
}

static void SSPMgr_IntHdr1RxDma(void)
{
	SSPMgr_IntProcessDma(1, AG903_SSP_DMA_DIR_RX);
	return;
}

static void SSPMgr_IntHdr2TxDma(void)
{
	SSPMgr_IntProcessDma(2, AG903_SSP_DMA_DIR_TX);
	return;
}

static void SSPMgr_IntHdr2RxDma(void)
{
	SSPMgr_IntProcessDma(2, AG903_SSP_DMA_DIR_RX);
	return;
}

static void SSPMgr_IntHdr3TxDma(void)
{
	SSPMgr_IntProcessDma(3, AG903_SSP_DMA_DIR_TX);
	return;
}

static void SSPMgr_IntHdr3RxDma(void)
{
	SSPMgr_IntProcessDma(3, AG903_SSP_DMA_DIR_RX);
	return;
}

static void SSPMgr_IntProcessDma(uint8_t ch, uint8_t dma_dir)
{
	uint8_t	dma_ch;
	uint32_t	event;
	uint32_t	st;
	AG903_SSPMgrDmaStat*	dma_stat;

	if (AG903_SSP_DMA_DIR_TX == dma_dir) {
		dma_stat = &SspDmaStatTx[ch];
	} else if (AG903_SSP_DMA_DIR_RX == dma_dir) {
		dma_stat = &SspDmaStatRx[ch];
	} else {
		return;
	}

	dma_ch = dma_stat->dma_ch;
	AG903_DMACPrmGetTIMECOUNT_INT(&st);
	if (st & (1<<dma_ch)) {
		dma_stat->stat = AG903_SSP_DMA_STAT_STOP;

		event = 0;
		if (AG903_SSP_DMA_DIR_TX == dma_dir) {
			event |= AG903_SSP_EVENT_SNDEND;
		} else if (AG903_SSP_DMA_DIR_RX == dma_dir) {
			event |= AG903_SSP_EVENT_RCVEND;
		}

		if (0 != event && NULL != SspHandleStat[ch].clbk) {
			SspHandleStat[ch].clbk((AG903_SSPMgrHandle*)&SspHandleStat[ch], event);
		}

		AG903_DMACPrmSetTIMECOUNT_INT_CLEAR(1<<dma_ch);
	}

	return;
}
