/**************************************************************************//** * @file i2s.c * @brief N9H30 I2S driver source file * * @note * SPDX-License-Identifier: Apache-2.0 * Copyright (C) 2018 Nuvoton Technology Corp. All rights reserved. *****************************************************************************/ #include #include #include #include "N9H30.h" #include "nu_sys.h" #include "nu_i2s.h" /** @addtogroup N9H30_Device_Driver N9H30 Device Driver @{ */ /** @addtogroup N9H30_I2S_Driver I2S Driver @{ */ /** @addtogroup N9H30_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 N9H30_I2S_EXPORTED_CONSTANTS */ /** @addtogroup N9H30_I2S_EXPORTED_FUNCTIONS I2S Exported Functions @{ */ /// @cond HIDDEN_SYMBOLS /** * @brief Start to play * @param None * @return None */ static void i2sStartPlay(void) { /* start playing */ //sysprintf("IIS start playing...\n"); outpw(REG_ACTL_PSR, 0x1); outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | (1 << 5)); } /** * @brief Stop to play * @param None * @return None */ static void i2sStopPlay(void) { //sysprintf("IIS stop playing\n"); /* stop playing */ outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~(1 << 5)); } /** * @brief Start to record * @param None * @return None */ static void i2sStartRecord(void) { /* start recording */ //sysprintf("IIS start recording...\n"); outpw(REG_ACTL_RSR, 0x1); outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | (1 << 6)); } /** * @brief Stop to record * @param None * @return None */ static void i2sStopRecord(void) { //sysprintf("I2S stop recording\n"); /* stop recording */ outpw(REG_ACTL_RESET, inpw(REG_ACTL_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_ACTL_CON) & (1 << 10)) { outpw(REG_ACTL_CON, inpw(REG_ACTL_CON) | (1 << 10)); //Clear TX INT if (inpw(REG_ACTL_PSR) & (1 << 4)) { outpw(REG_ACTL_PSR, (1 << 4)); //sysprintf("\ndebug:DMA_COUNTER_IRQ occur"); } if (inpw(REG_ACTL_PSR) & (1 << 3)) { outpw(REG_ACTL_PSR, (1 << 3)); //sysprintf("\ndebug:DMA_DATA_ZERO_IRQ occur"); } if (inpw(REG_ACTL_PSR) & 0x1) { outpw(REG_ACTL_PSR, 0x1); u8SN = (inpw(REG_ACTL_PSR) >> 5) & 0x7; g_fnPlayCallBack(u8SN); } } if (inpw(REG_ACTL_CON) & (1 << 11)) { outpw(REG_ACTL_CON, inpw(REG_ACTL_CON) | (1 << 11)); //Clear RX INT if (inpw(REG_ACTL_RSR) & 0x1) { outpw(REG_ACTL_RSR, 0x1); u8SN = (inpw(REG_ACTL_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_ACTL_RESET, inpw(REG_ACTL_RESET) | (1 << 16)); Delay(100); outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~(1 << 16)); Delay(100); /* reset IIS interface */ outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | 0x1); Delay(100); outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~0x1); Delay(100); outpw(REG_ACTL_CON, inpw(REG_ACTL_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 sysSetInterruptType(ACTL_IRQn, HIGH_LEVEL_SENSITIVE); sysInstallISR(IRQ_LEVEL_1, ACTL_IRQn, (PVOID)i2sISR); sysEnableInterrupt(ACTL_IRQn); 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 Command 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_ACTL_CON, (inpw(REG_ACTL_CON) & ~0x3) | 0x1); else outpw(REG_ACTL_CON, (inpw(REG_ACTL_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_ACTL_CON, (inpw(REG_ACTL_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_ACTL_CON, (inpw(REG_ACTL_CON) & ~0x3000) | (arg0 << 12)); break; case I2S_SET_REC_DMA_INT_SEL: outpw(REG_ACTL_CON, (inpw(REG_ACTL_CON) & ~0xc000) | (arg0 << 14)); break; case I2S_SET_ZEROCROSS: if (arg0 == I2S_ENABLE) outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | 0x8); else outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~0x8); break; case I2S_SET_DMACOUNTER: if (arg0 == I2S_ENABLE) outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | 0x10); else outpw(REG_ACTL_RESET, inpw(REG_ACTL_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_ACTL_RESET, inpw(REG_ACTL_RESET) & ~(0x3 << 12) | (arg1 << 12)); else outpw(REG_ACTL_RESET, inpw(REG_ACTL_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_ACTL_I2SCON, inpw(REG_ACTL_I2SCON) & ~(0x1 << 20)); else outpw(REG_ACTL_I2SCON, inpw(REG_ACTL_I2SCON) | (0x1 << 20)); break; case I2S_SET_SPLITDATA: if (arg0 == I2S_ENABLE) outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | (0x1 << 20)); else outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~(0x1 << 20)); break; case I2S_SET_DMA_ADDRESS: if (arg0 == I2S_PLAY) outpw(REG_ACTL_PDESB, arg1 | 0x80000000); else if (arg0 == I2S_REC) outpw(REG_ACTL_RDESB, arg1 | 0x80000000); else if (arg0 == PCM_PLAY) outpw(REG_ACTL_PDESB2, arg1 | 0x80000000); else outpw(REG_ACTL_RDESB2, arg1 | 0x80000000); break; case I2S_SET_DMA_LENGTH: if (arg0 == I2S_PLAY) outpw(REG_ACTL_PDES_LENGTH, arg1); else outpw(REG_ACTL_RDES_LENGTH, arg1); break; case I2S_GET_DMA_CUR_ADDRESS: buf = (uint32_t *)arg0; if (arg0 == I2S_PLAY) *buf = inpw(REG_ACTL_PDESC); else *buf = inpw(REG_ACTL_RDESC); break; // #define I2S_FORMAT_I2S 0 // #define I2S_FORMAT_MSB 1 case I2S_SET_I2S_FORMAT: if (arg0 == I2S_FORMAT_I2S) outpw(REG_ACTL_I2SCON, inpw(REG_ACTL_I2SCON) & ~ 0x8); else outpw(REG_ACTL_I2SCON, inpw(REG_ACTL_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_ACTL_PCMS1ST, (inpw(REG_ACTL_PCMS1ST) & ~0x3ff) | (arg1 & 0x3ff)); else if (arg0 == PCM_SLOT1_OUT) outpw(REG_ACTL_PCMS1ST, (inpw(REG_ACTL_PCMS1ST) & ~0x3ff0000) | ((arg1 & 0x3ff) << 16)); else if (arg0 == PCM_SLOT2_IN) outpw(REG_ACTL_PCMS2ST, (inpw(REG_ACTL_PCMS2ST) & ~0x3ff) | (arg1 & 0x3ff)); else outpw(REG_ACTL_PCMS2ST, (inpw(REG_ACTL_PCMS2ST) & ~0x3ff0000) | ((arg1 & 0x3ff) << 16)); break; case I2S_SET_PCM_FS_PERIOD: outpw(REG_ACTL_PCMCON, (inpw(REG_ACTL_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_ACTL_I2SCON, (inpw(REG_ACTL_I2SCON) & ~0x000F0000) | (u32MCLKDiv - 1) << 16); u32BCLKDiv = u32MCLK / (u32SampleRate * u32DataBit * u32Channel); u32BCLKDiv = u32BCLKDiv / 2 - 1; outpw(REG_ACTL_I2SCON, (inpw(REG_ACTL_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_ACTL_I2SCON, (inpw(REG_ACTL_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_ACTL_PCMCON, (inpw(REG_ACTL_PCMCON) & ~0x0000FF00) | (u32BCLKDiv << 8)); } /*@}*/ /* end of group N9H30_I2S_EXPORTED_FUNCTIONS */ /*@}*/ /* end of group N9H30_I2S_Driver */ /*@}*/ /* end of group N9H30_Device_Driver */ /*** (C) COPYRIGHT 2018 Nuvoton Technology Corp. ***/