rt-thread-official/bsp/nuvoton/libraries/nuc980/Driver/Source/nu_i2s.c

464 lines
13 KiB
C
Raw Normal View History

/**************************************************************************//**
* @file i2s.c
* @version V1.00
* $Revision: 4 $
* $Date: 18/08/05 2:12p $
* @brief I2S driver source file
*
* @note
* SPDX-License-Identifier: Apache-2.0
* Copyright (C) 2018 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nuc980.h"
#include "nu_sys.h"
#include "nu_i2s.h"
/** @addtogroup Standard_Driver Standard Driver
@{
*/
/** @addtogroup I2S_Driver I2S Driver
@{
*/
/** @addtogroup I2S_EXPORTED_CONSTANTS I2S Exported Constants
@{
*/
/// @cond HIDDEN_SYMBOLS
typedef uint32_t (AU_CB_FUNC_T)(uint32_t);
static AU_CB_FUNC_T *g_fnPlayCallBack;
static AU_CB_FUNC_T *g_fnRecCallBack;
static uint8_t i2sOpened = 0;
/// @endcond /* HIDDEN_SYMBOLS */
/*@}*/ /* end of group ARM9_I2S_EXPORTED_CONSTANTS */
/** @addtogroup ARM9_I2S_EXPORTED_FUNCTIONS I2S Exported Functions
@{
*/
/// @cond HIDDEN_SYMBOLS
/**
* @brief Start to play
* @param None
* @return None
*/
static void i2sStartPlay(void)
{
/* start playing */
printf("IIS start playing...\n");
outpw(REG_I2S_PSR, 0x1);
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) | (1 << 5));
}
/**
* @brief Stop to play
* @param None
* @return None
*/
static void i2sStopPlay(void)
{
printf("IIS stop playing\n");
/* stop playing */
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) & ~(1 << 5));
}
/**
* @brief Start to record
* @param None
* @return None
*/
static void i2sStartRecord(void)
{
/* start recording */
printf("IIS start recording...\n");
outpw(REG_I2S_RSR, 0x1);
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) | (1 << 6));
}
/**
* @brief Stop to record
* @param None
* @return None
*/
static void i2sStopRecord(void)
{
printf("I2S stop recording\n");
/* stop recording */
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) & ~(1 << 6));
}
/**
* @brief Delay function
* @param None
* @return None
*/
static void Delay(int nCnt)
{
int volatile loop;
for (loop = 0; loop < nCnt * 10; loop++);
}
/**
* @brief Interrupt service routine for i2s
* @param None
* @return None
*/
static void i2sISR(void)
{
uint8_t u8SN;
if (inpw(REG_I2S_CON) & (1 << 10))
{
outpw(REG_I2S_CON, inpw(REG_I2S_CON) | (1 << 10)); //Clear TX INT
if (inpw(REG_I2S_PSR) & (1 << 4))
{
outpw(REG_I2S_PSR, (1 << 4));
printf("\ndebug:DMA_COUNTER_IRQ occur");
}
if (inpw(REG_I2S_PSR) & (1 << 3))
{
outpw(REG_I2S_PSR, (1 << 3));
printf("\ndebug:DMA_DATA_ZERO_IRQ occur");
}
if (inpw(REG_I2S_PSR) & 0x1)
{
outpw(REG_I2S_PSR, 0x1);
u8SN = (inpw(REG_I2S_PSR) >> 5) & 0x7;
g_fnPlayCallBack(u8SN);
}
}
if (inpw(REG_I2S_CON) & (1 << 11))
{
outpw(REG_I2S_CON, inpw(REG_I2S_CON) | (1 << 11)); //Clear RX INT
if (inpw(REG_I2S_RSR) & 0x1)
{
outpw(REG_I2S_RSR, 0x1);
u8SN = (inpw(REG_I2S_RSR) >> 5) & 0x7;
g_fnRecCallBack(u8SN);
}
}
}
/// @endcond /* HIDDEN_SYMBOLS */
/**
* @brief Open i2s interface
* @return open status
* @retval I2S_ERR_BUSY error.
* @retval 0 success.
*/
int32_t i2sOpen(void)
{
if (i2sOpened)
return I2S_ERR_BUSY;
/* reset audio interface */
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) | (1 << 16));
Delay(100);
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) & ~(1 << 16));
Delay(100);
/* reset IIS interface */
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) | 0x1);
Delay(100);
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) & ~0x1);
Delay(100);
outpw(REG_I2S_CON, inpw(REG_I2S_CON) | (1 << 21) | (1 << 20));
i2sOpened = 1;
return 0;
}
/**
* @brief Close i2s interface
* @return None
*/
void i2sClose(void)
{
// reset some variables
i2sOpened = 0;
g_fnPlayCallBack = NULL;
g_fnRecCallBack = NULL;
// reset i2s interface
outpw(REG_SYS_AHBIPRST, inpw(REG_SYS_AHBIPRST) | (1 << 8));
outpw(REG_SYS_AHBIPRST, inpw(REG_SYS_AHBIPRST) & ~(1 << 8));
}
/**
* @brief Initialize i2s interface and setup interrupt
* @return None
*/
void i2sInit(void)
{
// enable i2s engine clock
outpw(REG_CLK_HCLKEN, inpw(REG_CLK_HCLKEN) | (1 << 24));
// enable interrupt and set ISR
sysInstallISR(IRQ_LEVEL_1, IRQ_I2S, (PVOID)i2sISR);
sysEnableInterrupt(IRQ_I2S);
sysSetLocalInterrupt(ENABLE_IRQ);
}
/**
* @brief IO control for i2s interface
* @param[in] cmd command for io control, value could be
* - \ref I2S_SET_PLAY
* - \ref I2S_SET_RECORD
* - \ref I2S_SELECT_BLOCK
* - \ref I2S_SELECT_BIT
* - \ref I2S_SET_PLAY_DMA_INT_SEL
* - \ref I2S_SET_REC_DMA_INT_SEL
* - \ref I2S_SET_ZEROCROSS
* - \ref I2S_SET_DMACOUNTER
* - \ref I2S_SET_CHANNEL
* - \ref I2S_SET_MODE
* - \ref I2S_SET_SPLITDATA
* - \ref I2S_SET_DMA_ADDRESS
* - \ref I2S_SET_DMA_LENGTH
* - \ref I2S_GET_DMA_CUR_ADDRESS
* - \ref I2S_SET_I2S_FORMAT
* - \ref I2S_SET_I2S_CALLBACKFUN
* - \ref I2S_SET_PCMSLOT
* @param[in] arg0 argument 0 for io control
* @param[in] arg1 argument 1 for io control
* @retval I2S_ERR_IO error.
* @retval 0 success.
*/
int32_t i2sIoctl(uint32_t cmd, uint32_t arg0, uint32_t arg1)
{
uint32_t *buf;
AU_CB_FUNC_T *ptr;
switch (cmd)
{
// #define I2S_START_PLAY 0
// #define I2S_STOP_PLAY 1
case I2S_SET_PLAY:
if (arg0 == I2S_START_PLAY)
i2sStartPlay();
else
i2sStopPlay();
break;
// #define I2S_START_REC 0
// #define I2S_STOP_REC 1
case I2S_SET_RECORD:
if (arg0 == I2S_START_REC)
i2sStartRecord();
else
i2sStopRecord();
break;
// #define I2S_BLOCK_I2S 0
// #define I2S_BLOCK_PCM 1
case I2S_SELECT_BLOCK:
if (arg0 == I2S_BLOCK_I2S)
outpw(REG_I2S_CON, (inpw(REG_I2S_CON) & ~0x3) | 0x1);
else
outpw(REG_I2S_CON, (inpw(REG_I2S_CON) & ~0x3) | 0x2);
break;
// #define I2S_BIT_WIDTH_8 0
// #define I2S_BIT_WIDTH_16 1
// #define I2S_BIT_WIDTH_24 2
case I2S_SELECT_BIT:
outpw(REG_I2S_CON, (inpw(REG_I2S_CON) & ~0x300) | (arg0 << 8));
break;
// #define I2S_DMA_INT_END 0
// #define I2S_DMA_INT_HALF 1
// #define I2S_DMA_INT_QUARTER 2
// #define I2S_DMA_INT_EIGTH 3
case I2S_SET_PLAY_DMA_INT_SEL:
outpw(REG_I2S_CON, (inpw(REG_I2S_CON) & ~0x3000) | (arg0 << 12));
break;
case I2S_SET_REC_DMA_INT_SEL:
outpw(REG_I2S_CON, (inpw(REG_I2S_CON) & ~0xc000) | (arg0 << 14));
break;
case I2S_SET_ZEROCROSS:
if (arg0 == I2S_ENABLE)
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) | 0x8);
else
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) & ~0x8);
break;
case I2S_SET_DMACOUNTER:
if (arg0 == I2S_ENABLE)
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) | 0x10);
else
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) & ~0x10);
break;
// #define I2S_CHANNEL_I2S_ONE 2
// #define I2S_CHANNEL_I2S_TWO 3
// #define I2S_CHANNEL_PCM_TWO 3
// #define I2S_CHANNEL_PCM_TWO_SLOT1 0
// #define I2S_CHANNEL_PCM_TWO_SLOT0 1
// #define I2S_CHANNEL_PCM_ONE_SLOT0 2
case I2S_SET_CHANNEL:
if (arg0 == I2S_PLAY)
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) & ~(0x3 << 12) | (arg1 << 12));
else
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) & ~(0x3 << 14) | (arg1 << 14));
break;
// #define I2S_MODE_MASTER 0
// #define I2S_MODE_SLAVE 1
case I2S_SET_MODE:
if (arg0 == I2S_MODE_MASTER)
outpw(REG_I2S_I2SCON, inpw(REG_I2S_I2SCON) & ~(0x1 << 20));
else
outpw(REG_I2S_I2SCON, inpw(REG_I2S_I2SCON) | (0x1 << 20));
break;
case I2S_SET_SPLITDATA:
if (arg0 == I2S_ENABLE)
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) | (0x1 << 20));
else
outpw(REG_I2S_RESET, inpw(REG_I2S_RESET) & ~(0x1 << 20));
break;
case I2S_SET_DMA_ADDRESS:
if (arg0 == I2S_PLAY)
outpw(REG_I2S_PDESB, arg1 | 0x80000000);
else if (arg0 == I2S_REC)
outpw(REG_I2S_RDESB, arg1 | 0x80000000);
else if (arg0 == PCM_PLAY)
outpw(REG_I2S_PDESB2, arg1 | 0x80000000);
else
outpw(REG_I2S_RDESB2, arg1 | 0x80000000);
break;
case I2S_SET_DMA_LENGTH:
if (arg0 == I2S_PLAY)
outpw(REG_I2S_PDES_LENGTH, arg1);
else
outpw(REG_I2S_RDES_LENGTH, arg1);
break;
case I2S_GET_DMA_CUR_ADDRESS:
buf = (uint32_t *)arg0;
if (arg0 == I2S_PLAY)
*buf = inpw(REG_I2S_PDESC);
else
*buf = inpw(REG_I2S_RDESC);
break;
// #define I2S_FORMAT_I2S 0
// #define I2S_FORMAT_MSB 1
case I2S_SET_I2S_FORMAT:
if (arg0 == I2S_FORMAT_I2S)
outpw(REG_I2S_I2SCON, inpw(REG_I2S_I2SCON) & ~ 0x8);
else
outpw(REG_I2S_I2SCON, inpw(REG_I2S_I2SCON) | 0x8);
break;
case I2S_SET_I2S_CALLBACKFUN:
ptr = (AU_CB_FUNC_T *)arg1;
if (arg0 == I2S_PLAY)
g_fnPlayCallBack = ptr;
else
g_fnRecCallBack = ptr;
break;
// #define PCM_SLOT1_IN 0
// #define PCM_SLOT1_OUT 1
// #define PCM_SLOT2_IN 2
// #define PCM_SLOT2_OUT 3
case I2S_SET_PCMSLOT:
if (arg0 == PCM_SLOT1_IN)
outpw(REG_I2S_PCMS1ST, (inpw(REG_I2S_PCMS1ST) & ~0x3ff) | (arg1 & 0x3ff));
else if (arg0 == PCM_SLOT1_OUT)
outpw(REG_I2S_PCMS1ST, (inpw(REG_I2S_PCMS1ST) & ~0x3ff0000) | ((arg1 & 0x3ff) << 16));
else if (arg0 == PCM_SLOT2_IN)
outpw(REG_I2S_PCMS2ST, (inpw(REG_I2S_PCMS2ST) & ~0x3ff) | (arg1 & 0x3ff));
else
outpw(REG_I2S_PCMS2ST, (inpw(REG_I2S_PCMS2ST) & ~0x3ff0000) | ((arg1 & 0x3ff) << 16));
break;
case I2S_SET_PCM_FS_PERIOD:
outpw(REG_I2S_PCMCON, (inpw(REG_I2S_PCMCON) & ~0x03FF0000 | (((arg0 - 1) & 0x3ff) << 16)));
break;
default:
return I2S_ERR_IO;
}
return 0;
}
/**
* @brief Configure sampling rate for audio
* @param[in] u32SourceClockRate source speed to i2s interface
* @param[in] u32SampleRate sampling rate
* @param[in] u32DataBit data width
* @param[in] u32Channel channel number
* @return None
*/
void i2sSetSampleRate(uint32_t u32SourceClockRate, uint32_t u32SampleRate, uint32_t u32DataBit, uint32_t u32Channel)
{
uint32_t u32BCLKDiv;
uint32_t u32MCLK, u32MCLKDiv;
u32MCLK = (u32SampleRate * 256);
u32MCLKDiv = u32SourceClockRate / u32MCLK;
outpw(REG_I2S_I2SCON, (inpw(REG_I2S_I2SCON) & ~0x000F0000) | (u32MCLKDiv - 1) << 16);
u32BCLKDiv = u32MCLK / (u32SampleRate * u32DataBit * u32Channel);
u32BCLKDiv = u32BCLKDiv / 2 - 1;
outpw(REG_I2S_I2SCON, (inpw(REG_I2S_I2SCON) & ~0xF0) | u32BCLKDiv << 5);
}
/**
* @brief Configure MCLK frequency (master mode)
* @param[in] u32SourceClockRate source clock rate
* @param[in] u32SampleRate sampling rate
* @return None
*/
void i2sSetMCLKFrequency(uint32_t u32SourceClockRate, uint32_t u32SampleRate)
{
uint32_t u32MCLK, u32MCLKDiv;
u32MCLK = (u32SampleRate * 256);
u32MCLKDiv = u32SourceClockRate / u32MCLK;
outpw(REG_I2S_I2SCON, (inpw(REG_I2S_I2SCON) & ~0x000F0000) | (u32MCLKDiv - 1) << 16);
}
/**
* @brief Configure PCM BCLK frequency (master mode)
* @param[in] u32SourceClockRate source clock rate
* @param[in] u32Rate target rate
* @return None
*/
void i2sSetPCMBCLKFrequency(uint32_t u32SourceClockRate, uint32_t u32Rate)
{
uint32_t u32BCLKDiv;
u32BCLKDiv = (u32SourceClockRate / (2 * u32Rate)) - 1;
outpw(REG_I2S_PCMCON, (inpw(REG_I2S_PCMCON) & ~0x0000FF00) | (u32BCLKDiv << 8));
}
/*@}*/ /* end of group I2S_EXPORTED_FUNCTIONS */
/*@}*/ /* end of group I2S_Driver */
/*@}*/ /* end of group Standard_Driver */
/*** (C) COPYRIGHT 2018 Nuvoton Technology Corp. ***/