/* Digital Audio Interface(DAI) module driver @file dai.c @ingroup mIDrvAud_DAI @brief DAI module driver @note Nothing. Copyright Novatek Microelectronics Corp. 2016. All rights reserved. */ #ifdef __KERNEL__ #include #include //#include //#include "kwrap/type.h"//a header for basic variable type //#include "kwrap/semaphore.h" //#include "kwrap/flag.h" //#include "dai_dbg.h" //#include "dai_drv.h" #include "dai_reg.h" #include "dai_int.h" //static ID FLG_ID_DAI; //static SEM_HANDLE SEMID_DAI; static DEFINE_SPINLOCK(my_lock); #define loc_cpu(myflags) spin_lock_irqsave(&my_lock, myflags) #define unl_cpu(myflags) spin_unlock_irqrestore(&my_lock, myflags) #define DBG_WRN(fmt, args...) printk(fmt, ##args) #define DBG_ERR(fmt, args...) printk(fmt, ##args) UINT32 _DAI_REG_BASE_ADDR[1]; #define OS_CONFIG_FLAG(x) #define SEM_CREATE(x, y) #define rel_flg(x) #define SEM_DESTROY(x) #define iset_flg(x,y) #define SEM_WAIT(x) 0 #define clr_flg(x,y) #define wai_flg(a,b,c,d) #define SEM_SIGNAL(x) #define FLGPTN UINT32 #elif defined(__FREERTOS) #define __MODULE__ rtos_dai #define __DBGLVL__ 8 // 0=FATAL, 1=ERR, 2=WRN, 3=UNIT, 4=FUNC, 5=IND, 6=MSG, 7=VALUE, 8=USER #define __DBGFLT__ "*" #include "kwrap/error_no.h" #include "kwrap/semaphore.h" #include "kwrap/flag.h" #include "kwrap/debug.h" #include "kwrap/task.h" #include "kwrap/spinlock.h" #include "kwrap/type.h" #include "include/dai.h" #include "include/dai_reg.h" #include "include/dai_int.h" #include "pll_protected.h" #include "dma_protected.h" #include "cache_protected.h" #include "io_address.h" static ID FLG_ID_DAI; static SEM_HANDLE SEMID_DAI; static VK_DEFINE_SPINLOCK(my_lock); #define loc_cpu(flags) vk_spin_lock_irqsave(&my_lock, flags) #define unl_cpu(flags) vk_spin_unlock_irqrestore(&my_lock, flags) unsigned int rtos_dai_debug_level = NVT_DBG_WRN; static BOOL rtos_init = 0; #endif /** @addtogroup mIDrvAud_DAI */ //@{ #ifdef __KERNEL__ void dai_create_resource(void) { OS_CONFIG_FLAG(FLG_ID_DAI); SEM_CREATE(SEMID_DAI, 1); } void dai_release_resource(void) { rel_flg(FLG_ID_DAI); SEM_DESTROY(SEMID_DAI); } #endif #ifdef __KERNEL__ void dai_enableclk(void) { struct clk *dai_clk, *source_clk; dai_clk = clk_get(NULL, "f0630000.dai"); if (IS_ERR(dai_clk)) { DBG_ERR("failed to get dai clk\n"); } clk_enable(dai_clk); source_clk = clk_get(NULL, "pll7"); if (IS_ERR(source_clk)) { DBG_ERR("failed to get pll7 clk\n"); } clk_set_parent(dai_clk, source_clk); clk_put(dai_clk); clk_put(source_clk); } void dai_disableclk(void) { struct clk *dai_clk; dai_clk = clk_get(NULL, "f0630000.dai"); if (IS_ERR(dai_clk)) { DBG_ERR("failed to get dai clk\n"); } clk_disable(dai_clk); clk_put(dai_clk); } void dai_setclkrate(unsigned long clkrate) { struct clk *dai_clk; dai_clk = clk_get(NULL, "f0630000.dai"); if (IS_ERR(dai_clk)) { DBG_ERR("failed to get dai clk\n"); } clk_set_rate(dai_clk, clkrate); clk_put(dai_clk); } #else void dai_enableclk(void) { pll_enableClock(DAI_CLK); } void dai_disableclk(void) { } void dai_setclkrate(unsigned long clkrate) { } #endif static DRV_CB dai_cb_funciton; static BOOL b_dai_opened = FALSE; static UINT32 size_error[2] = {0, 0}; //static UINT32 dai_pinmux_flag = PIN_AUDIO_CFG_NONE; static UINT32 dai_tasklet_event; void dai_tasklet(void) { UINT32 evt; unsigned long flag; loc_cpu(flag); evt = dai_tasklet_event; dai_tasklet_event = 0; unl_cpu(flag); if (dai_cb_funciton != NULL) { dai_cb_funciton(evt); } } /* DAI ISt It's DAI Interrupt Service Routine @param void @return void */ #if defined(__FREERTOS) irq_bh_handler_t dai_bh_ist(int irq, unsigned long event, void *data) { dai_tasklet(); return (irq_bh_handler_t) IRQ_HANDLED; } #endif /* DAI ISR It's DAI Interrupt Service Routine @param void @return void */ #if defined (__FREERTOS) irqreturn_t dai_isr(int irq, void *devid) #else void dai_isr(void) #endif { T_DAI_INTSTATUS_REG reg_status; T_DAI_INTSTATUS2_REG reg_status_2; //DBG_IND("\r\n"); // Get interrupt status reg_status.reg = DAI_GETREG(DAI_INTSTATUS_REG_OFS); reg_status_2.reg = DAI_GETREG(DAI_INTSTATUS2_REG_OFS); // Only handle interrupts which are enabled reg_status.reg &= DAI_GETREG(DAI_INTEN_REG_OFS); reg_status_2.reg &= DAI_GETREG(DAI_INTEN2_REG_OFS); if ((reg_status.reg == 0) && (reg_status_2.reg == 0)) { #if defined(__FREERTOS) return IRQ_NONE; #else return; #endif } // Clear interrupt status DAI_SETREG(DAI_INTSTATUS_REG_OFS, reg_status.reg); DAI_SETREG(DAI_INTSTATUS2_REG_OFS, reg_status_2.reg); // INT2 remap if (reg_status_2.bit.TXLB_DMABFI) { reg_status.reg |= DAI_TXLBDMADONE_INT; } if (reg_status_2.bit.TXLB_DMALOAD) { reg_status.reg |= DAI_TXLBDMALOAD_INT; } if (reg_status_2.bit.TXLB_STOP) { reg_status.reg |= DAI_TXLBSTOP_INT; } if (reg_status_2.bit.TXLB_BWERR) { reg_status.reg |= DAI_TXLBBWERR_INT; } // Call the isr handler dai_tasklet_event |= reg_status.reg; #if defined(__FREERTOS) kick_bh(INT_ID_DAI, reg_status.reg, NULL); #endif // Set DAI Flag iset_flg(FLG_ID_DAI, reg_status.reg); #if defined(__FREERTOS) return IRQ_HANDLED; #endif } #if 1 /* Lock DAI module Use semaphore lock for the DAI module @return @b E_OK: success @b Else: fail */ ER dai_lock(void) { ER er_ret; er_ret = SEM_WAIT(SEMID_DAI); if (er_ret != E_OK) { DBG_ERR("wait semaphore fail\r\n"); return er_ret; } return E_OK; } /* Unlock DAI module Release semaphore lock for the DAI module @return @b E_OK: success @b Else: fail */ ER dai_unlock(void) { SEM_SIGNAL(SEMID_DAI); return E_OK; } /* Wait DAI Interrupt Event */ DAI_INTERRUPT dai_wait_interrupt(DAI_INTERRUPT waited_flag) { FLGPTN ui_flag = 0; wai_flg(&ui_flag, FLG_ID_DAI, waited_flag, TWF_ORW | TWF_CLR); return ui_flag; } /* Select DAI I2S pinmux @return void */ void dai_select_pinmux(BOOL b_en) {/* PIN_GROUP_CONFIG pinmux_i2s[1]; int ret = 0; unsigned long flag; pinmux_i2s->pin_function = PIN_FUNC_AUDIO; if (b_en) { loc_cpu(flag); dai_pinmux_flag |= PIN_AUDIO_CFG_I2S; unl_cpu(flag); } else { loc_cpu(flag); dai_pinmux_flag &= ~PIN_AUDIO_CFG_I2S; unl_cpu(flag); } pinmux_i2s->config = dai_pinmux_flag; ret = nvt_pinmux_update(pinmux_i2s, 1); if (ret) { DBG_WRN("pinmux_mclk update error! \r\n"); }*/ } /* Select DAI I2S MCLK pinmux @return void */ void dai_select_mclk_pinmux(BOOL b_en) {/* PIN_GROUP_CONFIG pinmux_mclk[1]; int ret = 0; unsigned long flag; pinmux_mclk->pin_function = PIN_FUNC_AUDIO; if (b_en) { loc_cpu(flag); dai_pinmux_flag |= PIN_AUDIO_CFG_MCLK; unl_cpu(flag); } else { loc_cpu(flag); dai_pinmux_flag &= ~PIN_AUDIO_CFG_MCLK; unl_cpu(flag); } pinmux_mclk->config = dai_pinmux_flag; ret = nvt_pinmux_update(pinmux_mclk, 1); if (ret) { DBG_WRN("pinmux_mclk update error! \r\n"); } */ } #endif #if 1 /** Open digital audio controller driver. Open digital auiod controller. @param[in] p_isr_handler Callback function registered for interrupt notification. @return void */ void dai_open(DRV_CB p_isr_handler) { #if defined(__FREERTOS) if (!rtos_init) { rtos_init = 1; cre_flg(&FLG_ID_DAI, NULL, "FLG_ID_DAI"); vos_sem_create(&SEMID_DAI, 1, "SEMID_DAI"); } #else dai_create_resource(); #endif if (b_dai_opened) { return; } // log driver opened b_dai_opened = TRUE; //set dai interrupt handler dai_cb_funciton = p_isr_handler; // Clear Interrupt Flag clr_flg(FLG_ID_DAI, DAI_INTERRUPT_ALL); // Disable DAI Reset //pll_disableSystemReset(DAI_RSTN); // Enable DAI clock dai_enableclk(); dai_tasklet_event = 0; #if defined(__FREERTOS) // Enable dai interrupt request_irq(INT_ID_DAI, dai_isr ,IRQF_TRIGGER_HIGH, "dai", 0); request_irq_bh(INT_ID_DAI, (irq_bh_handler_t) dai_bh_ist, IRQF_BH_PRI_HIGH); #endif } /** Close digital audio controller driver. Close digital auiod controller. @return void */ void dai_close(void) { if (!b_dai_opened) { return; } // Disable dai interrupt //drv_disableInt(DRV_INT_DAI);linux no need // Disable DAI clock dai_disableclk(); #if defined(__FREERTOS) rtos_init = 0; rel_flg(FLG_ID_DAI); vos_sem_destroy(SEMID_DAI); #else dai_release_resource(); #endif //clear dai interrupt handler dai_cb_funciton = NULL; // log driver closed b_dai_opened = FALSE; } /** Set Digital Audio interface(DAI) General Configurations. Set Digital Audio interface(DAI) general configuration. Use DAI_CONFIG_ID as configuration selection and config_value is the configuration parameter. @param[in] config_id Configuration selection. Please refer to DAI_CONFIG_ID for details. @param[in] config_value configuration parameter. Please refer to DAI_CONFIG_ID for details. @return void */ void dai_set_config(DAI_CONFIG_ID config_id, UINT32 config_value) { T_DAI_CONFIG_REG reg_ctrl; unsigned long flag; loc_cpu(flag); reg_ctrl.reg = DAI_GETREG(DAI_CONFIG_REG_OFS); switch (config_id) { case DAI_CONFIG_ID_EXTCODEC_EN: { if (config_value == TRUE) { reg_ctrl.bit.EXCODEC_EN = 1; } else { reg_ctrl.bit.EXCODEC_EN = 0; // embedded codec fix using CH0/1 reg_ctrl.bit.HDMI_CH_SEL = DAI_I2SHDMI_SEL_CH01; } } break; case DAI_CONFIG_ID_ISRCB: { if (!b_dai_opened) { break; } //set dai interrupt handler dai_cb_funciton = (DRV_CB)config_value; } break; case DAI_CONFIG_ID_HDMI_TXEN: { if (config_value == TRUE) { reg_ctrl.bit.TX_MUX_SEL = 1; // playback to EAC/HDMI } else { reg_ctrl.bit.TX_MUX_SEL = 0; // playback to EAC only } } break; case DAI_CONFIG_ID_AVSYNC_EN: { if (config_value == TRUE) { reg_ctrl.bit.AVSYNC = 1; } else { reg_ctrl.bit.AVSYNC = 0; } } break; case DAI_CONFIG_ID_AVSYNC_SRC: { reg_ctrl.bit.AVSYNC_SRC = config_value; } break; case DAI_CONFIG_ID_SET_INTEN: { T_DAI_INTEN_REG reg_int_en; T_DAI_INTEN2_REG reg_int_en_2; #if defined (__FREERTOS) // wait for rtos flag issue fixed can be removed. if (config_value & DAI_TXLBDMADONE_INT){ reg_int_en_2.reg = DAI_GETREG(DAI_INTEN2_REG_OFS); reg_int_en_2.reg |= 0x1; DAI_SETREG(DAI_INTEN2_REG_OFS, reg_int_en_2.reg); } if (config_value & DAI_TXLBDMALOAD_INT){ reg_int_en_2.reg = DAI_GETREG(DAI_INTEN2_REG_OFS); reg_int_en_2.reg |= 0x10; DAI_SETREG(DAI_INTEN2_REG_OFS, reg_int_en_2.reg); } if (config_value & DAI_TXLBSTOP_INT){ reg_int_en_2.reg = DAI_GETREG(DAI_INTEN2_REG_OFS); reg_int_en_2.reg |= 0x100; DAI_SETREG(DAI_INTEN2_REG_OFS, reg_int_en_2.reg); } if (config_value & DAI_TXLBBWERR_INT){ reg_int_en_2.reg = DAI_GETREG(DAI_INTEN2_REG_OFS); reg_int_en_2.reg |= 0x1000; DAI_SETREG(DAI_INTEN2_REG_OFS, reg_int_en_2.reg); } #else UINT32 i; if (config_value & DAI_INTERRUPT_TXLB_ALL) { reg_int_en_2.reg = DAI_GETREG(DAI_INTEN2_REG_OFS); for (i = 0; i < 4; i++) { if (config_value & (0x1 << (i + 24))) { reg_int_en_2.reg |= (0x1 << (i << 2)); } } DAI_SETREG(DAI_INTEN2_REG_OFS, reg_int_en_2.reg); config_value &= ~DAI_INTERRUPT_TXLB_ALL; } #endif if (config_value & DAI_INTERRUPT_ALL) { reg_int_en.reg = DAI_GETREG(DAI_INTEN_REG_OFS); reg_int_en.reg |= (config_value & DAI_INTERRUPT_ALL); DAI_SETREG(DAI_INTEN_REG_OFS, reg_int_en.reg); } } break; case DAI_CONFIG_ID_CLR_INTEN: { T_DAI_INTEN_REG reg_int_en; T_DAI_INTEN2_REG reg_int_en_2; UINT32 i; if (config_value & DAI_INTERRUPT_TXLB_ALL) { reg_int_en_2.reg = DAI_GETREG(DAI_INTEN2_REG_OFS); for (i = 0; i < 4; i++) { if (config_value & (0x1 << (i + 24))) { reg_int_en_2.reg &= ~(0x1 << (i << 2)); } } DAI_SETREG(DAI_INTEN2_REG_OFS, reg_int_en_2.reg); config_value &= ~DAI_INTERRUPT_TXLB_ALL; } if (config_value & DAI_INTERRUPT_ALL) { reg_int_en.reg = DAI_GETREG(DAI_INTEN_REG_OFS); reg_int_en.reg &= ~(config_value & DAI_INTERRUPT_ALL); DAI_SETREG(DAI_INTEN_REG_OFS, reg_int_en.reg); } } break; case DAI_CONFIG_ID_CLR_INTSTS: { T_DAI_INTSTATUS_REG reg_status; T_DAI_INTSTATUS2_REG reg_status_2; // Get interrupt status 2 reg_status_2.reg = DAI_GETREG(DAI_INTSTATUS2_REG_OFS); // Only allow to clear interrupt status which are disabled reg_status_2.reg &= ~(DAI_GETREG(DAI_INTEN2_REG_OFS)); // Clear interrupt status 2 DAI_SETREG(DAI_INTSTATUS2_REG_OFS, reg_status_2.reg); // Get interrupt status reg_status.reg = DAI_GETREG(DAI_INTSTATUS_REG_OFS); // Only allow to clear interrupt status which are disabled reg_status.reg &= ~(DAI_GETREG(DAI_INTEN_REG_OFS)); // Clear interrupt status DAI_SETREG(DAI_INTSTATUS_REG_OFS, reg_status.reg); } break; case DAI_CONFIG_ID_CLKSRC: { if (config_value != DAI_CODESCK_INT) { unl_cpu(flag); DBG_WRN("Only support internal audio MCLK\r\n"); return; } } break; case DAI_CONFIG_ID_RX_SRC_SEL: { if (config_value == DAI_RX_SRC_I2S) { reg_ctrl.bit.RX_SRC_MUX_SEL = 1; } else { reg_ctrl.bit.RX_SRC_MUX_SEL = 0; } } break; default: unl_cpu(flag); DBG_WRN("CfgID Err = %d\r\n", (int)config_id); return; } DAI_SETREG(DAI_CONFIG_REG_OFS, reg_ctrl.reg); unl_cpu(flag); } /** Get Digital Audio interface(DAI) General Configurations. Get Digital Audio interface(DAI) general configuration. Use DAI_CONFIG_ID as configuration selection and config_value is the configuration parameter. @param[in] config_id Configuration selection. Please refer to DAI_CONFIG_ID for details. @return void */ UINT32 dai_get_config(DAI_CONFIG_ID config_id) { UINT32 ret = 0; T_DAI_CONFIG_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CONFIG_REG_OFS); switch (config_id) { case DAI_CONFIG_ID_EXTCODEC_EN: { ret = reg_ctrl.bit.EXCODEC_EN; } break; case DAI_CONFIG_ID_RX_SRC_SEL: { ret = reg_ctrl.bit.RX_SRC_MUX_SEL; } break; case DAI_CONFIG_ID_ISRCB: { //set dai interrupt handler ret = (UINT32)dai_cb_funciton; } break; case DAI_CONFIG_ID_HDMI_TXEN: { ret = reg_ctrl.bit.TX_MUX_SEL; } break; case DAI_CONFIG_ID_AVSYNC_EN: { ret = reg_ctrl.bit.AVSYNC; } break; case DAI_CONFIG_ID_AVSYNC_SRC: { ret = reg_ctrl.bit.AVSYNC_SRC; } break; default: DBG_WRN("config_id Err = %d\r\n", (int)config_id); break; } return ret; } /** Set Digital Audio I2S interface configurations Use DAI_I2SCONFIG_ID as configuration selection and config_value is the configuration parameter. @param[in] config_id Configuration selection. Please refer to DAI_I2SCONFIG_ID for details. @param[in] config_value configuration parameter. Please refer to DAI_I2SCONFIG_ID for details. @return void */ void dai_set_i2s_config(DAI_I2SCONFIG_ID config_id, UINT32 config_value) { T_DAI_I2SCONFIG_REG reg_i2s_ctrl; T_DAI_I2S_TDM_ORDER_REG reg_i2s_order; unsigned long flag; UINT32 ret = 0; loc_cpu(flag); reg_i2s_ctrl.reg = DAI_GETREG(DAI_I2SCONFIG_REG_OFS); reg_i2s_order.reg = DAI_GETREG(DAI_I2S_TDM_ORDER_REG_OFS); switch (config_id) { case DAI_I2SCONFIG_ID_CLKRATIO: { if (config_value == DAI_I2SCLKR_256FS_32BIT) { reg_i2s_ctrl.bit.CKRATIO = 0; } else if (config_value == DAI_I2SCLKR_256FS_64BIT) { reg_i2s_ctrl.bit.CKRATIO = 1; } else if (config_value == DAI_I2SCLKR_256FS_128BIT) { reg_i2s_ctrl.bit.CKRATIO = 2; } else if (config_value == DAI_I2SCLKR_256FS_256BIT) { reg_i2s_ctrl.bit.CKRATIO = 3; } else { unl_cpu(flag); DBG_ERR("f/m clk ratio 0x%x not support\r\n", (unsigned int)config_value); return; } } break; case DAI_I2SCONFIG_ID_FORMAT: { if (config_value != DAI_I2SFMT_STANDARD) { unl_cpu(flag); DBG_ERR("only supports I2S standard format\r\n"); return; } } break; case DAI_I2SCONFIG_ID_OPMODE: { if (config_value == DAI_OP_SLAVE) { reg_i2s_ctrl.bit.SLAVE = 1; } else { reg_i2s_ctrl.bit.SLAVE = 0; } } break; case DAI_I2SCONFIG_ID_CHANNEL_LEN: { reg_i2s_ctrl.bit.CHANNEL_LEN = config_value > 0; } break; case DAI_I2SCONFIG_ID_HDMICH_SEL: { T_DAI_CONFIG_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CONFIG_REG_OFS); if (reg_ctrl.bit.EXCODEC_EN == 0) { // When embedded codec, fix using CH0/1 reg_ctrl.bit.HDMI_CH_SEL = DAI_I2SHDMI_SEL_CH01; } else { reg_ctrl.bit.HDMI_CH_SEL = config_value; } DAI_SETREG(DAI_CONFIG_REG_OFS, reg_ctrl.reg); } break; case DAI_I2SCONFIG_ID_CLK_INV: { if (config_value == 0) { reg_i2s_ctrl.bit.I2S_ASFCK_INV = 0; } else { reg_i2s_ctrl.bit.I2S_ASFCK_INV = 1; } }break; case DAI_I2SCONFIG_ID_DATA_ORDER: { if (config_value == DAI_I2S_DATAORDER_TYPE1) { switch ((32 << reg_i2s_ctrl.bit.CKRATIO) / (16 << reg_i2s_ctrl.bit.CHANNEL_LEN)) { case 2: reg_i2s_order.bit.I2S_TDM_ORDER = 0x00000010; break; case 4: reg_i2s_order.bit.I2S_TDM_ORDER = 0x00003120; break; case 6: reg_i2s_order.bit.I2S_TDM_ORDER = 0x00531420; break; case 8: reg_i2s_order.bit.I2S_TDM_ORDER = 0x75316420; break; default: reg_i2s_order.bit.I2S_TDM_ORDER = 0x75316420; break; } } else if (config_value == DAI_I2S_DATAORDER_TYPE2){ switch ((32 << reg_i2s_ctrl.bit.CKRATIO) / (16 << reg_i2s_ctrl.bit.CHANNEL_LEN)) { case 2: reg_i2s_order.bit.I2S_TDM_ORDER = 0x00000010; break; case 4: reg_i2s_order.bit.I2S_TDM_ORDER = 0x00003210; break; case 6: reg_i2s_order.bit.I2S_TDM_ORDER = 0x00543210; break; case 8: reg_i2s_order.bit.I2S_TDM_ORDER = 0x76543210; break; default: reg_i2s_order.bit.I2S_TDM_ORDER = 0x76543210; break; } } else { reg_i2s_order.bit.I2S_TDM_ORDER = config_value; } }break; case DAI_I2SCONFIG_ID_CLK_OFS: { if(config_value > DAI_I2S_CLK_OFS_MAX) { config_value = DAI_I2S_CLK_OFS_MAX; } ret = reg_i2s_ctrl.bit.CKRATIO; // make sure ofs is at least one bit clk. if (config_value < (UINT32)(0x7>>ret)) { config_value = (UINT32)(0x7>>ret); } reg_i2s_ctrl.bit.I2S_ASFCK_OFS = config_value; }break; default: unl_cpu(flag); DBG_WRN("CfgID Err = %d\r\n", (int)config_id); return; } DAI_SETREG(DAI_I2SCONFIG_REG_OFS, reg_i2s_ctrl.reg); DAI_SETREG(DAI_I2S_TDM_ORDER_REG_OFS, reg_i2s_order.reg); unl_cpu(flag); } /** Get Digital Audio I2S interface configurations Use DAI_I2SCONFIG_ID as configuration selection and config_value is the configuration parameter. @param[in] config_id Configuration selection. Please refer to DAI_I2SCONFIG_ID for details. @return void */ UINT32 dai_get_i2s_config(DAI_I2SCONFIG_ID config_id) { UINT32 ret = 0; T_DAI_I2SCONFIG_REG reg_i2s_ctrl; T_DAI_I2S_TDM_ORDER_REG reg_i2s_order; reg_i2s_ctrl.reg = DAI_GETREG(DAI_I2SCONFIG_REG_OFS); switch (config_id) { case DAI_I2SCONFIG_ID_CLKRATIO: { ret = reg_i2s_ctrl.bit.CKRATIO; } break; case DAI_I2SCONFIG_ID_FORMAT: { ret = DAI_I2SFMT_STANDARD; } break; case DAI_I2SCONFIG_ID_OPMODE: { ret = !reg_i2s_ctrl.bit.SLAVE; } break; case DAI_I2SCONFIG_ID_CHANNEL_LEN: { ret = reg_i2s_ctrl.bit.CHANNEL_LEN; } break; case DAI_I2SCONFIG_ID_HDMICH_SEL: { T_DAI_CONFIG_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CONFIG_REG_OFS); ret = reg_ctrl.bit.HDMI_CH_SEL; } break; case DAI_I2SCONFIG_ID_SLAVEMATCH: { ret = reg_i2s_ctrl.bit.I2SCKR_MATCH; } break; case DAI_I2SCONFIG_ID_CURRENT_CLKRATIO: { ret = reg_i2s_ctrl.bit.I2SCKR_CUR; } break; case DAI_I2SCONFIG_ID_CLK_INV: { ret = reg_i2s_ctrl.bit.I2S_ASFCK_INV; }break; case DAI_I2SCONFIG_ID_DATA_ORDER: { ret = reg_i2s_order.bit.I2S_TDM_ORDER; }break; case DAI_I2SCONFIG_ID_CLK_OFS: { ret = reg_i2s_ctrl.bit.I2S_ASFCK_OFS; }break; default: DBG_WRN("CfgID Err = %d\r\n", (int)config_id); break; } return ret; } #endif #if 1 /** Set DAI Playback Chaneels Configurations. Set DAI Playback Chaneels(TX1 and TX2) Configurations. @param[in] channel playback Channel selection @param[in] config_id playback config ID selection @param[in] config_value configuration value @return void */ void dai_set_tx_config(DAI_TXCH channel, DAI_TXCFG_ID config_id, UINT32 config_value) { T_DAI_FMTCFG0_REG reg_fmt_0; unsigned long flag; loc_cpu(flag); reg_fmt_0.reg = DAI_GETREG(DAI_FMTCFG0_REG_OFS); switch (config_id) { case DAI_TXCFG_ID_CHANNEL: { if (config_value != DAI_CH_DUAL_MONO) { if (channel == DAI_TXCH_TX1) { reg_fmt_0.bit.TX1_SOUNDM = config_value; } else if (channel == DAI_TXCH_TX2) { reg_fmt_0.bit.TX2_SOUNDM = config_value; } } else { unl_cpu(flag); DBG_WRN("DAI TX no support dual mono\r\n"); return; } } break; case DAI_TXCFG_ID_TOTAL_CH: { T_DAI_CONFIG_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CONFIG_REG_OFS); if ((reg_ctrl.bit.EXCODEC_EN == 0) && (config_value > DAI_TOTCH_2CH)) { DBG_WRN("Embedded codec 2CH only\r\n"); config_value = DAI_TOTCH_2CH; } if (channel == DAI_TXCH_TX1) { reg_fmt_0.bit.TX1_SOUNDCH = config_value; } else if (channel == DAI_TXCH_TX2) { reg_fmt_0.bit.TX2_SOUNDCH = config_value; } } break; case DAI_TXCFG_ID_PCMLEN: { if (channel == DAI_TXCH_TX1) { reg_fmt_0.bit.TX1_PCMLEN = config_value; } else if (channel == DAI_TXCH_TX2) { reg_fmt_0.bit.TX2_PCMLEN = config_value; } } break; case DAI_TXCFG_ID_DRAMCH: { if (channel == DAI_TXCH_TX1) { reg_fmt_0.bit.TX1_DRAMCH = config_value; } else if (channel == DAI_TXCH_TX2) { reg_fmt_0.bit.TX2_DRAMCH = config_value; } } break; case DAI_TXCFG_ID_TIMECODE_TRIG: { if (channel == DAI_TXCH_TX1) { DAI_SETREG(DAI_TX1TCTRIGGER_REG_OFS, config_value); } else { unl_cpu(flag); DBG_ERR("Only TX CH1 has TimeCode function (1)\r\n"); return; } } break; case DAI_TXCFG_ID_TIMECODE_OFS: { if (channel == DAI_TXCH_TX1) { DAI_SETREG(DAI_TX1TCOFFSET_REG_OFS, config_value); } else { unl_cpu(flag); DBG_ERR("Only TX CH1 has TimeCode function (2)\r\n"); return; } } break; default: unl_cpu(flag); DBG_WRN("CfgID Err = %d\r\n", (int)config_id); return; } DAI_SETREG(DAI_FMTCFG0_REG_OFS, reg_fmt_0.reg); unl_cpu(flag); } /** Get DAI Playback Chaneels Configurations. Get DAI Playback Chaneels(TX1 and TX2) Configurations. @param[in] channel playback Channel selection @param[in] config_id playback config ID selection @return configuration value */ UINT32 dai_get_tx_config(DAI_TXCH channel, DAI_TXCFG_ID config_id) { T_DAI_FMTCFG0_REG reg_fmt_0; UINT32 ret = 0; reg_fmt_0.reg = DAI_GETREG(DAI_FMTCFG0_REG_OFS); switch (config_id) { case DAI_TXCFG_ID_CHANNEL: { if (channel == DAI_TXCH_TX1) { ret = reg_fmt_0.bit.TX1_SOUNDM; } else if (channel == DAI_TXCH_TX2) { ret = reg_fmt_0.bit.TX2_SOUNDM; } } break; case DAI_TXCFG_ID_TOTAL_CH: { if (channel == DAI_TXCH_TX1) { ret = reg_fmt_0.bit.TX1_SOUNDCH; } else if (channel == DAI_TXCH_TX2) { ret = reg_fmt_0.bit.TX2_SOUNDCH; } } break; case DAI_TXCFG_ID_PCMLEN: { if (channel == DAI_TXCH_TX1) { ret = reg_fmt_0.bit.TX1_PCMLEN; } else if (channel == DAI_TXCH_TX2) { ret = reg_fmt_0.bit.TX2_PCMLEN; } } break; case DAI_TXCFG_ID_DRAMCH: { if (channel == DAI_TXCH_TX1) { ret = reg_fmt_0.bit.TX1_DRAMCH; } else if (channel == DAI_TXCH_TX2) { ret = reg_fmt_0.bit.TX2_DRAMCH; } } break; case DAI_TXCFG_ID_TIMECODE_TRIG: { if (channel == DAI_TXCH_TX1) { ret = DAI_GETREG(DAI_TX1TCTRIGGER_REG_OFS); } } break; case DAI_TXCFG_ID_TIMECODE_OFS: { if (channel == DAI_TXCH_TX1) { ret = DAI_GETREG(DAI_TX1TCOFFSET_REG_OFS); } } break; case DAI_TXCFG_ID_TIMECODE_VAL: { if (channel == DAI_TXCH_TX1) { ret = DAI_GETREG(DAI_TX1TCVALUE_REG_OFS); } } break; default: break; } return ret; } /** Set DAI Playback Loopback Chaneel Configurations. Set DAI Playback Loopback Chaneels(TXLB) Configurations. @param[in] channel playback Loopback Channel selection @param[in] config_id playback Loopback config ID selection @param[in] config_value configuration value @return void */ void dai_set_txlb_config(DAI_TXLBCFG_ID config_id, UINT32 config_value) { T_DAI_FMTCFG2_REG reg_fmt_2; unsigned long flag; loc_cpu(flag); reg_fmt_2.reg = DAI_GETREG(DAI_FMTCFG2_REG_OFS); switch (config_id) { case DAI_TXLBCFG_ID_CHANNEL: { if (config_value != DAI_CH_DUAL_MONO) { reg_fmt_2.bit.TXLB_SOUNDM = config_value; } else { unl_cpu(flag); DBG_WRN("DAI TXLB no support dual mono\r\n"); return; } } break; case DAI_TXLBCFG_ID_TOTAL_CH: { T_DAI_CONFIG_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CONFIG_REG_OFS); if ((reg_ctrl.bit.EXCODEC_EN == 0) && (config_value > DAI_TOTCH_2CH)) { DBG_WRN("Embedded codec 2CH only\r\n"); config_value = DAI_TOTCH_2CH; } reg_fmt_2.bit.TXLB_SOUNDCH = config_value; } break; case DAI_TXLBCFG_ID_PCMLEN: { reg_fmt_2.bit.TXLB_PCMLEN = config_value; } break; case DAI_TXLBCFG_ID_DRAMCH: { reg_fmt_2.bit.TXLB_DRAMCH = config_value; } break; case DAI_TXLBCFG_ID_RXSYNC: { T_DAI_CONFIG_REG reg_cfg; reg_cfg.reg = DAI_GETREG(DAI_CONFIG_REG_OFS); reg_cfg.bit.TXLB_SYNC = config_value > 0; DAI_SETREG(DAI_CONFIG_REG_OFS, reg_cfg.reg); unl_cpu(flag); } return; default: unl_cpu(flag); DBG_WRN("CfgID Err = %d\r\n", (int)config_id); return; } DAI_SETREG(DAI_FMTCFG2_REG_OFS, reg_fmt_2.reg); unl_cpu(flag); } /** Set DAI Playback Loopback Chaneel Configurations. Set DAI Playback Loopback Chaneels(TXLB) Configurations. @param[in] channel playback Loopback Channel selection @param[in] config_id playback Loopback config ID selection @param[in] config_value configuration value @return void */ UINT32 dai_get_txlb_config(DAI_TXLBCFG_ID config_id) { T_DAI_FMTCFG2_REG reg_fmt_2; UINT32 ret = 0; reg_fmt_2.reg = DAI_GETREG(DAI_FMTCFG2_REG_OFS); switch (config_id) { case DAI_TXLBCFG_ID_CHANNEL: { ret = reg_fmt_2.bit.TXLB_SOUNDM; } break; case DAI_TXLBCFG_ID_TOTAL_CH: { ret = reg_fmt_2.bit.TXLB_SOUNDCH; } break; case DAI_TXLBCFG_ID_PCMLEN: { ret = reg_fmt_2.bit.TXLB_PCMLEN; } break; case DAI_TXLBCFG_ID_DRAMCH: { ret = reg_fmt_2.bit.TXLB_DRAMCH; } break; case DAI_TXLBCFG_ID_RXSYNC: { T_DAI_CONFIG_REG reg_cfg; reg_cfg.reg = DAI_GETREG(DAI_CONFIG_REG_OFS); ret = reg_cfg.bit.TXLB_SYNC; } break; default: break; } return ret; } /** Set DAI Record Channels Configurations. Set DAI Record Channels (RX1 & RX2) Configurations. @param[in] config_id record config ID selection @param[in] config_value configuration value @return void */ void dai_set_rx_config(DAI_RXCFG_ID config_id, UINT32 config_value) { T_DAI_FMTCFG1_REG reg_fmt_1; unsigned long flag; loc_cpu(flag); reg_fmt_1.reg = DAI_GETREG(DAI_FMTCFG1_REG_OFS); switch (config_id) { case DAI_RXCFG_ID_CHANNEL: { reg_fmt_1.bit.RX_SOUNDM = config_value; } break; case DAI_RXCFG_ID_TOTAL_CH: { T_DAI_CONFIG_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CONFIG_REG_OFS); if ((reg_ctrl.bit.EXCODEC_EN == 0) && (config_value > DAI_TOTCH_2CH)) { DBG_WRN("Embedded codec 2CH only\r\n"); config_value = DAI_TOTCH_2CH; } reg_fmt_1.bit.RX_SOUNDCH = config_value; } break; case DAI_RXCFG_ID_PCMLEN: { reg_fmt_1.bit.RX_PCMLEN = config_value; } break; case DAI_RXCFG_ID_DRAMCH: { reg_fmt_1.bit.RX_DRAMCH = config_value; } break; case DAI_RXCFG_ID_TIMECODE_TRIG: { DAI_SETREG(DAI_RXTCTRIGGER_REG_OFS, config_value); } break; case DAI_RXCFG_ID_TIMECODE_OFS: { DAI_SETREG(DAI_RXTCOFFSET_REG_OFS, config_value); } break; default: unl_cpu(flag); DBG_WRN("CfgID Err = %d\r\n", (int)config_id); return; } DAI_SETREG(DAI_FMTCFG1_REG_OFS, reg_fmt_1.reg); unl_cpu(flag); } /** Get DAI Record Channels Configurations. Get DAI Record Channels (RX1 & RX2) Configurations. @param[in] config_id record config ID selection @return configuration value */ UINT32 dai_get_rx_config(DAI_RXCFG_ID config_id) { T_DAI_FMTCFG1_REG reg_fmt_1; UINT32 ret = 0; reg_fmt_1.reg = DAI_GETREG(DAI_FMTCFG1_REG_OFS); switch (config_id) { case DAI_RXCFG_ID_CHANNEL: { ret = reg_fmt_1.bit.RX_SOUNDM; } break; case DAI_RXCFG_ID_TOTAL_CH: { ret = reg_fmt_1.bit.RX_SOUNDCH; } break; case DAI_RXCFG_ID_PCMLEN: { ret = reg_fmt_1.bit.RX_PCMLEN; } break; case DAI_RXCFG_ID_DRAMCH: { ret = reg_fmt_1.bit.RX_DRAMCH; } break; case DAI_RXCFG_ID_TIMECODE_TRIG: { ret = DAI_GETREG(DAI_RXTCTRIGGER_REG_OFS); } break; case DAI_RXCFG_ID_TIMECODE_OFS: { ret = DAI_GETREG(DAI_RXTCOFFSET_REG_OFS); } break; case DAI_RXCFG_ID_TIMECODE_VAL: { ret = DAI_GETREG(DAI_RXTCVALUE_REG_OFS); } break; default: break; } return ret; } /** Enable/Disable DAI playback DMA Channel Enable/Disable DAI playback DMA Channel @param[in] channel playback Channel selection @param[in] b_en TRUE is Enable. FALSE is Disable. @return void */ void dai_enable_tx_dma(DAI_TXCH channel, BOOL b_en) { T_DAI_CTRL_REG reg_ctrl; unsigned long flag; loc_cpu(flag); reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); if (channel == DAI_TXCH_TX1) { if (b_en == TRUE) { reg_ctrl.bit.DMA_TX1_EN = 1; } else { reg_ctrl.bit.DMA_TX1_EN = 0; } } else if (channel == DAI_TXCH_TX2) { if (b_en == TRUE) { reg_ctrl.bit.DMA_TX2_EN = 1; } else { reg_ctrl.bit.DMA_TX2_EN = 0; } } DAI_SETREG(DAI_CTRL_REG_OFS, reg_ctrl.reg); unl_cpu(flag); } /** Enable/Disable DAI record DMA Channel Enable/Disable DAI record DMA Channel @param[in] b_en TRUE is Enable. FALSE is Disable. @return void */ void dai_enable_rx_dma(BOOL b_en) { T_DAI_CTRL_REG reg_ctrl; unsigned long flag; loc_cpu(flag); reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); if (b_en == TRUE) { reg_ctrl.bit.DMA_RX_EN = 1; } else { reg_ctrl.bit.DMA_RX_EN = 0; } DAI_SETREG(DAI_CTRL_REG_OFS, reg_ctrl.reg); unl_cpu(flag); } /** Enable/Disable DAI playback loopback DMA Channel Enable/Disable DAI playback loopback DMA Channel @param[in] b_en TRUE is Enable. FALSE is Disable. @return void */ void dai_enable_txlb_dma(BOOL b_en) { T_DAI_CTRL_REG reg_ctrl; unsigned long flag; loc_cpu(flag); reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); if (b_en == TRUE) { reg_ctrl.bit.DMA_TXLB_EN = 1; } else { reg_ctrl.bit.DMA_TXLB_EN = 0; } DAI_SETREG(DAI_CTRL_REG_OFS, reg_ctrl.reg); unl_cpu(flag); } /** Start/Stop DAI Playback Start/Stop DAI Specified Channel Playback @param[in] channel playback Channel selection @param[in] b_en TRUE is Start. FALSE is Stop. @return void */ void dai_enable_tx(DAI_TXCH channel, BOOL b_en) { T_DAI_CTRL_REG reg_ctrl; unsigned long flag; loc_cpu(flag); reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); if (channel == DAI_TXCH_TX1) { if (b_en == TRUE) { reg_ctrl.bit.TX1_EN = 1; } else { reg_ctrl.bit.TX1_EN = 0; size_error[0] = 0; } } else if (channel == DAI_TXCH_TX2) { if (b_en == TRUE) { reg_ctrl.bit.TX2_EN = 1; } else { reg_ctrl.bit.TX2_EN = 0; size_error[1] = 0; } } DAI_SETREG(DAI_CTRL_REG_OFS, reg_ctrl.reg); unl_cpu(flag); } /** Start/Stop DAI Record Start/Stop DAI Specified Channel Record @param[in] b_en TRUE is Start. FALSE is Stop. @return void */ void dai_enable_rx(BOOL b_en) { T_DAI_CTRL_REG reg_ctrl; unsigned long flag; loc_cpu(flag); reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); if (b_en == TRUE) { reg_ctrl.bit.RX_EN = 1; } else { reg_ctrl.bit.RX_EN = 0; } DAI_SETREG(DAI_CTRL_REG_OFS, reg_ctrl.reg); unl_cpu(flag); } /** Start/Stop DAI playback loopback Start/Stop DAI Specified Channel Record @param[in] b_en TRUE is Start. FALSE is Stop. @return void */ void dai_enable_txlb(BOOL b_en) { T_DAI_CTRL_REG reg_ctrl; unsigned long flag; loc_cpu(flag); reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); if (b_en == TRUE) { reg_ctrl.bit.TXLB_EN = 1; } else { reg_ctrl.bit.TXLB_EN = 0; } DAI_SETREG(DAI_CTRL_REG_OFS, reg_ctrl.reg); unl_cpu(flag); } /** Set DAI module enable/disable Set DAI module enable/disable @param[in] b_en DAI module enable/disable - @b TRUE: module enable - @b FALSE: module disable @return void */ void dai_enable_dai(BOOL b_en) { T_DAI_CTRL_REG reg_ctrl; unsigned long flag; loc_cpu(flag); reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); if (b_en == TRUE) { reg_ctrl.bit.DAIEN = 1; } else { reg_ctrl.bit.DAIEN = 0; } DAI_SETREG(DAI_CTRL_REG_OFS, reg_ctrl.reg); unl_cpu(flag); } #endif #if 1 /** Check whether DAI is enabled or not If DAI is enabled this function will return TRUE. @return @b TRUE: DAI is enabled @b FALSE: DAI is disabled */ BOOL dai_is_dai_enable(void) { T_DAI_CTRL_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); return reg_ctrl.bit.DAIEN; } /** Check if DAI Playback Channel is enabled Check if DAI Playback Channel is enabled @param[in] channel playback Channel selection @return @b TRUE: Specified Playback channel is enabled @b FALSE: Specified Playback channel is disabled */ BOOL dai_is_tx_enable(DAI_TXCH channel) { T_DAI_CTRL_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); if (channel == DAI_TXCH_TX2) { return reg_ctrl.bit.TX2_EN; } else { return reg_ctrl.bit.TX1_EN; } } /** Check if DAI record Channel is enabled Check if DAI record Channel is enabled @return @b TRUE: Record is enabled @b FALSE: Record is disabled */ BOOL dai_is_rx_enable(void) { T_DAI_CTRL_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); return reg_ctrl.bit.RX_EN; } /** Check if DAI playback loopback Channel is enabled Check if DAI playback loopback Channel is enabled @return @b TRUE: playback loopback is enabled @b FALSE: playback loopback is disabled */ BOOL dai_is_txlb_enable(void) { T_DAI_CTRL_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); return reg_ctrl.bit.TXLB_EN; } /** Check whether DAI is under tx/rx If DAI is under tx/rx, this function will return TRUE. @return @b TRUE: DAI is under tx/rx. @b FALSE: DAI is not under tx/rx. */ BOOL dai_is_txrx_enable(void) { T_DAI_CTRL_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); return (reg_ctrl.bit.TX1_EN || reg_ctrl.bit.TX2_EN || reg_ctrl.bit.RX_EN || reg_ctrl.bit.TXLB_EN); } /** Check if playback DMA Channel is Enabled Check if playback DMA Channel is Enabled @param[in] channel playback Channel selection @return @b TRUE: Specified Playback DMA channel is enabled @b FALSE: Specified Playback DMA channel is disabled */ BOOL dai_is_tx_dma_enable(DAI_TXCH channel) { T_DAI_CTRL_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); if (channel == DAI_TXCH_TX2) { return reg_ctrl.bit.DMA_TX2_EN; } else { return reg_ctrl.bit.DMA_TX1_EN; } } /** Check if Record DMA Channel is Enabled Check if Record DMA Channel is Enabled @return @b TRUE: Specified Record DMA channel is enabled @b FALSE: Specified Record DMA channel is disabled */ BOOL dai_is_rx_dma_enable(void) { T_DAI_CTRL_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); return reg_ctrl.bit.DMA_RX_EN; } /** Check if Playback Loopback DMA Channel is Enabled Check if Playback Loopback DMA Channel is Enabled @return @b TRUE: Specified Playback Loopback DMA channel is enabled @b FALSE: Specified Playback Loopback DMA channel is disabled */ BOOL dai_is_txlb_dma_enable(void) { T_DAI_CTRL_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); return reg_ctrl.bit.DMA_TXLB_EN; } /** Check whether Tx or Rx DMA is enabled or not If Tx or Rx DMA is enabled this function will return TRUE. @return @b TRUE: DMA is enabled @b FALSE: DMA is disabled */ BOOL dai_is_dma_enable(void) { T_DAI_CTRL_REG reg_ctrl; reg_ctrl.reg = DAI_GETREG(DAI_CTRL_REG_OFS); return (reg_ctrl.bit.DMA_RX_EN || reg_ctrl.bit.DMA_TX1_EN || reg_ctrl.bit.DMA_TX2_EN || reg_ctrl.bit.DMA_TXLB_EN); } #endif #if 1 /** Set Playback DMA parameter Set Playback DMA starting address, buffer size. @param[in] dma_channel Playback DMA Channel Selection. Valid value is 0 or 1 for NT96680. @param[in] dma_start_addr DMA start address. (unit: byte, should be word-alignment) @param[in] dma_buffer_size DMA buffer size (unit: 16 words aligned) @return void */ void dai_set_tx_dma_para(UINT32 dma_channel, UINT32 dma_start_addr, UINT32 dma_buffer_size) { T_DAI_TX1DMASTART_REG reg_dma_start = {0}; T_DAI_TX1DMABUFSIZE_REG reg_dma_bufsz = {0}; if (dma_start_addr & 0x03) { DBG_WRN("DMA address should be word align, but 0x%x, align it to 0x%x\r\n", (unsigned int)dma_start_addr, (unsigned int)(dma_start_addr & (~0x03))); } if (size_error[dma_channel]) { DBG_WRN("Previous DMA size must be multiples of 2 words (%d)(%d)\r\n", (int)dma_channel, (int)size_error[dma_channel]); } if (dma_buffer_size & 0x0F) { size_error[dma_channel] = dma_buffer_size; } //#ifdef __KERNEL__ // fmem_dcache_sync((void *)dma_start_addr, dma_buffer_size * 4, DMA_BIDIRECTIONAL); //#elif defined(__FREERTOS) // dma_flushWriteCache(dma_start_addr, dma_buffer_size * 4); //#endif reg_dma_start.bit.TX1DMASTADR = dma_get_phy_addr(dma_start_addr); reg_dma_bufsz.bit.TX1DMABUFSZ = dma_buffer_size; if (dma_channel == 0) { DAI_SETREG(DAI_TX1DMASTART_REG_OFS, reg_dma_start.reg); DAI_SETREG(DAI_TX1DMABUFSIZE_REG_OFS, reg_dma_bufsz.reg); } else if (dma_channel == 1) { DAI_SETREG(DAI_TX2DMASTART_REG_OFS, reg_dma_start.reg); DAI_SETREG(DAI_TX2DMABUFSIZE_REG_OFS, reg_dma_bufsz.reg); } #if DAI_DBG_MSG DBG_WRN("[TX%d] Addr=0x%08X Size=0x%08X(words)\r\n", (int)dma_channel + 1, (unsigned int)dma_start_addr, (int)dma_buffer_size); #endif } /** Set Record DMA parameter Set Record DMA starting address, buffer size. @param[in] dma_channel Record DMA Channel Selection. Valid value is 0 or 1 for NT96680. @param[in] dma_start_addr DMA start address. (unit: byte, should be word-alignment) @param[in] dma_buffer_size DMA buffer size (unit: 16 words aligned) @return void */ void dai_set_rx_dma_para(UINT32 dma_channel, UINT32 dma_start_addr, UINT32 dma_buffer_size) { T_DAI_RX1DMASTART_REG reg_dma_start = {0}; T_DAI_RX1DMABUFSIZE_REG reg_dma_bufsz = {0}; if (dma_start_addr & 0x03) { DBG_WRN("DMA address should be word align, but 0x%x, align it to 0x%x\r\n", (unsigned int)dma_start_addr, (unsigned int)(dma_start_addr & (~0x03))); } if (dma_buffer_size & 0x0F) { DBG_WRN("DMA size must be multiples of 2 words (%d)\r\n", (int)dma_buffer_size); } //#ifdef __KERNEL__ // fmem_dcache_sync((void *)dma_start_addr, dma_buffer_size * 4, DMA_BIDIRECTIONAL); //#elif defined(__FREERTOS) // dma_flushReadCache(dma_start_addr, dma_buffer_size * 4); //#endif reg_dma_start.bit.RX1DMASTADR = dma_get_phy_addr(dma_start_addr); reg_dma_bufsz.bit.RX1DMABUFSZ = dma_buffer_size; if (dma_channel == 0) { DAI_SETREG(DAI_RX1DMASTART_REG_OFS, reg_dma_start.reg); DAI_SETREG(DAI_RX1DMABUFSIZE_REG_OFS, reg_dma_bufsz.reg); } else if (dma_channel == 1) { DAI_SETREG(DAI_RX2DMASTART_REG_OFS, reg_dma_start.reg); DAI_SETREG(DAI_RX2DMABUFSIZE_REG_OFS, reg_dma_bufsz.reg); } #if DAI_DBG_MSG DBG_WRN("[RX%d] Addr=0x%08X Size=0x%08X(words)\r\n", (int)dma_channel + 1, (unsigned int)dma_start_addr, (int)dma_buffer_size); #endif } /** Set Playback Loopback DMA parameter Set Playback Loopback DMA starting address, buffer size. @param[in] dma_start_addr DMA start address. (unit: byte, should be word-alignment) @param[in] dma_buffer_size DMA buffer size (unit: 16 words aligned) @return void */ void dai_set_txlb_dma_para(UINT32 dma_start_addr, UINT32 dma_buffer_size) { T_DAI_TXLBDMASTART_REG reg_dma_start = {0}; T_DAI_TXLBDMABUFSIZE_REG reg_dma_bufsz = {0}; if (dma_start_addr & 0x03) { DBG_WRN("DMA address should be word align, but 0x%x, align it to 0x%x\r\n", (unsigned int)dma_start_addr, (unsigned int)(dma_start_addr & (~0x03))); } if (dma_buffer_size & 0x0F) { DBG_WRN("DMA size must be multiples of 2 words (%d)\r\n", (int)dma_buffer_size); } //#ifdef __KERNEL__ // fmem_dcache_sync((void *)dma_start_addr, dma_buffer_size * 4, DMA_BIDIRECTIONAL); //#elif defined(__FREERTOS) // dma_flushReadCache(dma_start_addr, dma_buffer_size * 4); //#endif reg_dma_start.bit.TXLBDMASTADR = dma_get_phy_addr(dma_start_addr); reg_dma_bufsz.bit.TXLBDMABUFSZ = dma_buffer_size; DAI_SETREG(DAI_TXLBDMASTART_REG_OFS, reg_dma_start.reg); DAI_SETREG(DAI_TXLBDMABUFSIZE_REG_OFS, reg_dma_bufsz.reg); #if DAI_DBG_MSG DBG_WRN("[TXLB] Addr=0x%08X Size=0x%08X(words)\r\n", (unsigned int)dma_start_addr, (int)dma_buffer_size); #endif } /** Get Playback DMA parameter Get Playback DMA starting address, buffer size. @param[in] dma_channel Playback DMA Channel Selection. Valid value is 0 or 1 for NT96680. @param[out] dma_start_addr DMA start address. (unit: byte, should be word-alignment) @param[out] dma_buffer_size DMA buffer size (unit: word) @return void */ void dai_get_tx_dma_para(UINT32 dma_channel, UINT32 *p_start_addr, UINT32 *p_buffer_size) { if (dma_channel == 0) { if (p_start_addr != NULL) { *p_start_addr = dma_get_noncache_addr(DAI_GETREG(DAI_TX1DMASTART_REG_OFS)); } if (p_buffer_size != NULL) { *p_buffer_size = DAI_GETREG(DAI_TX1DMABUFSIZE_REG_OFS); } } else if (dma_channel == 1) { if (p_start_addr != NULL) { *p_start_addr = dma_get_noncache_addr(DAI_GETREG(DAI_TX2DMASTART_REG_OFS)); } if (p_buffer_size != NULL) { *p_buffer_size = DAI_GETREG(DAI_TX2DMABUFSIZE_REG_OFS); } } } /** Get Record DMA parameter Get Record DMA starting address, buffer size. @param[in] dma_channel Record DMA Channel Selection. Valid value is 0 or 1 for NT96680. @param[out] dma_start_addr DMA start address. (unit: byte, should be word-alignment) @param[out] dma_buffer_size DMA buffer size (unit: word) @return void */ void dai_get_rx_dma_para(UINT32 dma_channel, UINT32 *p_start_addr, UINT32 *p_buffer_size) { if (dma_channel == 0) { if (p_start_addr != NULL) { *p_start_addr = dma_get_noncache_addr(DAI_GETREG(DAI_RX1DMASTART_REG_OFS)); } if (p_buffer_size != NULL) { *p_buffer_size = DAI_GETREG(DAI_RX1DMABUFSIZE_REG_OFS); } } else if (dma_channel == 1) { if (p_start_addr != NULL) { *p_start_addr = dma_get_noncache_addr(DAI_GETREG(DAI_RX2DMASTART_REG_OFS)); } if (p_buffer_size != NULL) { *p_buffer_size = DAI_GETREG(DAI_RX2DMABUFSIZE_REG_OFS); } } } /** Get Playback Loopback DMA parameter Get Playback Loopback DMA starting address, buffer size. @param[in] dma_start_addr DMA start address. (unit: byte, should be word-alignment) @param[in] dma_buffer_size DMA buffer size (unit: word) @return void */ void dai_get_txlb_dma_para(UINT32 *p_start_addr, UINT32 *p_buffer_size) { if (p_start_addr != NULL) { *p_start_addr = dma_get_noncache_addr(DAI_GETREG(DAI_TXLBDMASTART_REG_OFS)); } if (p_buffer_size != NULL) { *p_buffer_size = DAI_GETREG(DAI_TXLBDMABUFSIZE_REG_OFS); } } /** Get Playback DMA currrent address Get Playback DMA currrent address @param[in] dma_channel Playback DMA Channel Selection. Valid value is 0 or 1 for NT96660. @return Playback DMA current address */ UINT32 dai_get_tx_dma_curaddr(UINT32 dma_channel) { if (dma_channel == 1) { return dma_get_noncache_addr(DAI_GETREG(DAI_TX2DMACURRENT_REG_OFS)); } else { return dma_get_noncache_addr(DAI_GETREG(DAI_TX1DMACURRENT_REG_OFS)); } } /** Get Record DMA currrent address Get Record DMA currrent address @param[in] dma_channel Record DMA Channel Selection. Valid value is 0 or 1 for NT96660. @return Record DMA current address */ UINT32 dai_get_rx_dma_curaddr(UINT32 dma_channel) { if (dma_channel == 1) { return dma_get_noncache_addr(DAI_GETREG(DAI_RX2DMACURRENT_REG_OFS)); } else { return dma_get_noncache_addr(DAI_GETREG(DAI_RX1DMACURRENT_REG_OFS)); } } /** Get Playback Loopback DMA currrent address Get Playback Loopback DMA currrent address @return Playback Loopback DMA current address */ UINT32 dai_get_txlb_dma_curaddr(void) { return dma_get_noncache_addr(DAI_GETREG(DAI_TXLBDMACURRENT_REG_OFS)); } /** Clear DAI flag Clear DAI flag @param[in] int_flag interrupt flags to clear. @return void */ void dai_clr_flg(DAI_INTERRUPT int_flag) { // Clear Interrupt Flag clr_flg(FLG_ID_DAI, int_flag); } #endif //@} /* DAI self Debug Mode Enable After DAI Debug enabled, the DAI RX channel's input is switched from CODEC interface to DAI Playback Mixer output. */ void dai_debug(BOOL b_en) { T_DAI_DBG_CONFIG_REG reg_dbg; reg_dbg.reg = DAI_GETREG(DAI_DBG_CONFIG_REG_OFS); reg_dbg.bit.DBGEN = b_en > 0; DAI_SETREG(DAI_DBG_CONFIG_REG_OFS, reg_dbg.reg); } /* DAI Debug Channel Enable for EAC b_mode_ad is TRUE for AD. b_mode_ad is FALSE for DA. */ void dai_debug_eac(BOOL b_en, BOOL b_mode_ad) { T_DAI_DBG_CONFIG_REG reg_dbg; reg_dbg.reg = DAI_GETREG(DAI_DBG_CONFIG_REG_OFS); reg_dbg.bit.DBGEN = 0; reg_dbg.bit.EAC_DBG_EN = b_en > 0; reg_dbg.bit.EAC_DBG_MODE = b_mode_ad > 0; DAI_SETREG(DAI_DBG_CONFIG_REG_OFS, reg_dbg.reg); } /* Set Debug Channel DMA parameter Set Debug Channel DMA starting address, buffer size. */ void dai_set_debug_dma_para(UINT32 dma_start_addr, UINT32 dma_buffer_size) { T_DAI_DBG_ADDR_REG reg_dma_start = {0}; T_DAI_DBG_SIZE_REG reg_dma_bufsz = {0}; reg_dma_start.bit.DMASTADR = dma_get_phy_addr(dma_start_addr); reg_dma_bufsz.bit.DMABUFSZ = dma_buffer_size; if (dma_start_addr) { DAI_SETREG(DAI_DBG_ADDR_REG_OFS, reg_dma_start.reg); } if (dma_buffer_size) { DAI_SETREG(DAI_DBG_SIZE_REG_OFS, reg_dma_bufsz.reg); } } /* Get Debug Done Status parameter */ BOOL dai_get_debug_status(void) { T_DAI_DBG_STS_REG reg_status = {0}; reg_status.reg = DAI_GETREG(DAI_DBG_STS_REG_OFS); return reg_status.bit.DONE; } /* Get Debug Done Status parameter */ void dai_clr_debug_status(void) { T_DAI_DBG_STS_REG reg_status; reg_status.reg = DAI_GETREG(DAI_DBG_STS_REG_OFS); reg_status.bit.DONE = 1; DAI_SETREG(DAI_DBG_STS_REG_OFS, reg_status.reg); } #if 0//def __KERNEL__ EXPORT_SYMBOL(dai_create_resource); EXPORT_SYMBOL(dai_release_resource); EXPORT_SYMBOL(dai_enableclk); EXPORT_SYMBOL(dai_disableclk); EXPORT_SYMBOL(dai_setclkrate); EXPORT_SYMBOL(dai_isr); EXPORT_SYMBOL(dai_lock); EXPORT_SYMBOL(dai_unlock); EXPORT_SYMBOL(dai_wait_interrupt); EXPORT_SYMBOL(dai_select_pinmux); EXPORT_SYMBOL(dai_select_mclk_pinmux); EXPORT_SYMBOL(dai_open); EXPORT_SYMBOL(dai_close); EXPORT_SYMBOL(dai_set_config); EXPORT_SYMBOL(dai_get_config); EXPORT_SYMBOL(dai_set_i2s_config); EXPORT_SYMBOL(dai_get_i2s_config); EXPORT_SYMBOL(dai_set_tx_config); EXPORT_SYMBOL(dai_get_tx_config); EXPORT_SYMBOL(dai_set_txlb_config); EXPORT_SYMBOL(dai_get_txlb_config); EXPORT_SYMBOL(dai_set_rx_config); EXPORT_SYMBOL(dai_get_rx_config); EXPORT_SYMBOL(dai_enable_tx_dma); EXPORT_SYMBOL(dai_enable_rx_dma); EXPORT_SYMBOL(dai_enable_txlb_dma); EXPORT_SYMBOL(dai_enable_tx); EXPORT_SYMBOL(dai_enable_rx); EXPORT_SYMBOL(dai_enable_txlb); EXPORT_SYMBOL(dai_enable_dai); EXPORT_SYMBOL(dai_is_dai_enable); EXPORT_SYMBOL(dai_is_tx_enable); EXPORT_SYMBOL(dai_is_rx_enable); EXPORT_SYMBOL(dai_is_txlb_enable); EXPORT_SYMBOL(dai_is_txrx_enable); EXPORT_SYMBOL(dai_is_tx_dma_enable); EXPORT_SYMBOL(dai_is_rx_dma_enable); EXPORT_SYMBOL(dai_is_txlb_dma_enable); EXPORT_SYMBOL(dai_is_dma_enable); EXPORT_SYMBOL(dai_set_tx_dma_para); EXPORT_SYMBOL(dai_set_rx_dma_para); EXPORT_SYMBOL(dai_set_txlb_dma_para); EXPORT_SYMBOL(dai_get_tx_dma_para); EXPORT_SYMBOL(dai_get_rx_dma_para); EXPORT_SYMBOL(dai_get_txlb_dma_para); EXPORT_SYMBOL(dai_get_tx_dma_curaddr); EXPORT_SYMBOL(dai_get_rx_dma_curaddr); EXPORT_SYMBOL(dai_get_txlb_dma_curaddr); EXPORT_SYMBOL(dai_clr_flg); EXPORT_SYMBOL(dai_debug); EXPORT_SYMBOL(dai_debug_eac); EXPORT_SYMBOL(dai_set_debug_dma_para); EXPORT_SYMBOL(dai_get_debug_status); EXPORT_SYMBOL(dai_clr_debug_status); #endif