diff --git a/boards/arm/cxd56xx/common/src/cxd56_audio.c b/boards/arm/cxd56xx/common/src/cxd56_audio.c index c6d22853f9e..d6889a0c4e1 100644 --- a/boards/arm/cxd56xx/common/src/cxd56_audio.c +++ b/boards/arm/cxd56xx/common/src/cxd56_audio.c @@ -476,7 +476,7 @@ void board_audio_finalize(void) * ****************************************************************************/ -static struct cxd56_lower_s g_cxd56_lower; +static struct cxd56_lower_s g_cxd56_lower[2]; int board_audio_initialize_driver(int minor) { @@ -485,9 +485,9 @@ int board_audio_initialize_driver(int minor) char devname[12]; int ret; - /* Initialize CXD56 device driver */ + /* Initialize CXD56 output device driver */ - cxd56 = cxd56_initialize(&g_cxd56_lower); + cxd56 = cxd56_initialize(&g_cxd56_lower[0]); if (!cxd56) { auderr("ERROR: Failed to initialize the CXD56 audio\n"); @@ -518,5 +518,30 @@ int board_audio_initialize_driver(int minor) devname, ret); } + /* Initialize CXD56 input device driver */ + + cxd56 = cxd56_initialize(&g_cxd56_lower[1]); + if (!cxd56) + { + auderr("ERROR: Failed to initialize the CXD56 audio\n"); + + return -ENODEV; + } + + /* No decoder support at the moment, only raw PCM data. */ + + /* Create a device name */ + + snprintf(devname, 12, "pcm_in%d", minor); + + /* Finally, we can register the CXD56 audio input device. */ + + ret = audio_register(devname, cxd56); + if (ret < 0) + { + auderr("ERROR: Failed to register /dev/%s device: %d\n", + devname, ret); + } + return ret; } diff --git a/drivers/audio/cxd56.c b/drivers/audio/cxd56.c index 10a8f5f8611..97db024ee84 100644 --- a/drivers/audio/cxd56.c +++ b/drivers/audio/cxd56.c @@ -36,9 +36,11 @@ * Included Files ****************************************************************************/ -#include #include +#include +#include #include +#include #include #include @@ -91,9 +93,20 @@ AUDIO_SAMP_RATE_192K) >> 8) #define CXD56_SUPP_RATES (CXD56_SUPP_RATES_L | CXD56_SUPP_RATES_H) -/* Mic selections */ +/* Mic setting definitions */ -#define CXD56_ACA_MIC_AMIC 1 /* Analog MIC */ +#define CXD56_ACA_MIC_AMIC 1 /* Analog MIC */ +#define CXD56_MIC_TRANS_CH_24BIT 8 +#define CXD56_MIC_TRANS_CH_16BIT 4 + +#define CXD56_AUDIO_CFG_MIC_MODE_64FS 0 +#define CXD56_AUDIO_CFG_MIC_MODE_128FS 1 + +#define CXD56_MIC_GAIN_MAX 150 +#define CXD56_MIC_PGA_GAIN_MAX 60 +#define CXD56_MIC_CH_BITNUM 4 +#define CXD56_MIC_CH_BITMAP 0xf +#define CXD56_CIC_MIC_CH_NUM 2 /* External XTAL */ @@ -109,10 +122,15 @@ /* Control IDs for external as_aca_control */ #define CXD56_ACA_CTL_CHECK_ID 0 #define CXD56_ACA_CTL_POWER_ON_COMMON 1 +#define CXD56_ACA_CTL_POWER_ON_INPUT 2 #define CXD56_ACA_CTL_POWER_ON_OUTPUT 3 +#define CXD56_ACA_CTL_SET_SERDES 4 #define CXD56_ACA_CTL_SET_SMASTER 5 #define CXD56_ACA_CTL_POWER_OFF_COMMON 6 +#define CXD56_ACA_CTL_POWER_OFF_INPUT 7 #define CXD56_ACA_CTL_POWER_OFF_OUTPUT 8 +#define CXD56_ACA_CTL_POWER_ON_MICBIAS 9 +#define CXD56_ACA_CTL_POWER_OFF_MICBIAS 10 #define CXD56_ACA_CTL_SET_OUTPUT_DEVICE 13 #define CXD56_EXP_REVID 0x20 @@ -133,10 +151,18 @@ #define CXD56_ACA_SP_LOOP_MODE_UNKNOWN 0 #define CXD56_ACA_SP_DLY_FREE_UNKNOWN 0 +#define CXD56_AUDAT_SEL_MIC1 0 +#define CXD56_AUDAT_SEL_MIC2 1 +#define CXD56_AUDAT_SEL_MIC3 2 +#define CXD56_AUDAT_SEL_MIC4 3 #define CXD56_AUDAT_SEL_BUSIF1 4 #define CXD56_AUDAT_SEL_BUSIF2 4 -#define CXD56_DMA_MSTATE_BUF_EMPTY 3 +#define CXD56_DMA_MSTATE_ERR_NO_ENABLE_CH 1 +#define CXD56_DMA_MSTATE_ERR_CH1_4_INVALID 2 +#define CXD56_DMA_MSTATE_ERR_CH5_8_INVALID 3 +#define CXD56_DMA_MSTATE_BUF_EMPTY 3 + #define CXD56_DMA_TIMEOUT 10000 #define CXD56_DMA_START_RETRY_CNT 10 #define CXD56_DMA_SMP_WAIT_HIRES 10 /* usec per sample. */ @@ -144,10 +170,10 @@ #define CXD56_DMA_CMD_FIFO_NOT_FULL 1 #define CXD56_DMA_START_ADDR_MASK 0x3fffffff -/* Queue extensions */ +/* Queue helpers */ -#define dq_push(q,n) (dq_addlast((dq_entry_t*)n,(q))) -#define dq_pop(q) (dq_remfirst(q)) +#define dq_put(q,n) (dq_addlast((dq_entry_t*)n,(q))) +#define dq_get(q) (dq_remfirst(q)) #define dq_clear(q) \ do \ { \ @@ -202,6 +228,92 @@ enum cxd56_vol_id_e CXD56_VOL_ID_MIXER_OUT /* DAC_VOL */ }; +enum cxd56_pulco_ser_mode_id_e +{ + CXD56_SER_MODE_UNKNOWN, + CXD56_SER_MODE_8CH, /* 8ch */ + CXD56_SER_MODE_4CH, /* 4ch */ + CXD56_SER_MODE_MAX_ENTRY +}; + +enum csd56_pulco_ser_fs_id_e +{ + CXD56_SER_FS_UNKNOWN, + CXD56_SER_FS_128, /* 128fs */ + CXD56_SER_FS_64, /* 64fs */ + CXD56_SER_FS_MAX_ENTRY +}; + +enum cxd56_pulco_ser_sel_ch_id_e +{ + CXD56_SER_SEL_FIX0 = 0, + CXD56_SER_SEL_AMIC1 = 1, + CXD56_SER_SEL_AMIC2 = 2, + CXD56_SER_SEL_AMIC3 = 3, + CXD56_SER_SEL_AMIC4 = 4, + CXD56_SER_SEL_DMIC1 = 5, + CXD56_SER_SEL_DMIC2 = 6, + CXD56_SER_SEL_DMIC3 = 7, + CXD56_SER_SEL_DMIC4 = 8, + CXD56_SER_SEL_DMIC5 = 9, + CXD56_SER_SEL_DMIC6 = 10, + CXD56_SER_SEL_DMIC7 = 11, + CXD56_SER_SEL_DMIC8 = 12, + CXD56_SER_SEL_UNKNOWN = 15, + CXD56_SER_SEL_MAX_ENTRY = 16 +}; + +enum cxd56_sdes_des_sel_out_id_e +{ + CXD56_SDES_DES_SEL_UNKNOWN, + CXD56_SDES_DES_SEL_CH1, + CXD56_SDES_DES_SEL_CH2, + CXD56_SDES_DES_SEL_CH3, + CXD56_SDES_DES_SEL_CH4, + CXD56_SDES_DES_SEL_CH5, + CXD56_SDES_DES_SEL_CH6, + CXD56_SDES_DES_SEL_CH7, + CXD56_SDES_DES_SEL_CH8, + CXD56_SDES_DES_SEL_MAX_ENTRY +}; + +struct cxd56_ser_des_param_s +{ + enum cxd56_pulco_ser_mode_id_e ser_mode; + enum csd56_pulco_ser_fs_id_e ser_fs; + union + { + enum cxd56_pulco_ser_sel_ch_id_e in[CXD56_IN_CHANNELS_MAX]; + enum cxd56_sdes_des_sel_out_id_e out[CXD56_IN_CHANNELS_MAX]; + } sel_ch; +}; + +enum cxd56_mic_type_e +{ + CXD56_AUDIO_CFG_MIC_DEV_NONE = 0, + CXD56_AUDIO_CFG_MIC_DEV_ANALOG, + CXD56_AUDIO_CFG_MIC_DEV_DIGITAL, + CXD56_AUDIO_CFG_MIC_DEV_ANADIG +}; + +#if 0 +/* TODO: Implement mic gain handling */ + +struct cxd56_audio_mic_gain_s +{ + int32_t gain[CXD56_IN_CHANNELS_MAX]; +}; +#endif + +struct cxd56_aca_pwinput_param_s +{ + enum cxd56_mic_type_e mic_dev; + uint8_t mic_bias_sel; + uint32_t mic_gain[4]; + uint32_t pga_gain[4]; + int32_t vgain[4]; +}; + struct cxd56_aca_pwon_param_s { uint8_t osc_mode; @@ -289,20 +401,37 @@ static int cxd56_ioctl(FAR struct audio_lowerhalf_s *lower, int cmd, static int cxd56_attach_irq(bool attach); static void cxd56_enable_irq(bool enable); static uint32_t cxd56_get_i2s_rate(uint32_t samplerate); +static void cxd56_get_mic_config(uint8_t *count, + uint8_t *dev, + uint8_t *mode); static void cxd56_init_dma(FAR struct cxd56_dev_s *dev); static void cxd56_init_i2s1_output(uint8_t bits); +static void cxd56_init_mic_input(uint8_t mic_num, uint8_t bits); static int cxd56_init_worker(FAR struct audio_lowerhalf_s *lower); +static uint8_t cxd56_get_mic_mode(void); static int cxd56_power_off(FAR struct cxd56_dev_s *dev); static int cxd56_power_on(FAR struct cxd56_dev_s *dev); static int cxd56_power_on_aca(uint32_t samplerate); static int cxd56_power_on_analog_output(FAR struct cxd56_dev_s *dev); +static void cxd56_audio_power_on_cic(uint8_t mic_in, + uint8_t mic_mode, + uint8_t cic_num, + FAR struct cxd56_audio_mic_gain_s *gain); +static int cxd56_power_on_decim(uint8_t mic_mode, uint16_t samplerate); static void cxd56_power_on_i2s1(FAR struct cxd56_dev_s *dev); +static int cxd56_power_on_input(FAR struct cxd56_dev_s *dev); +static int cxd56_power_on_micbias(FAR struct cxd56_dev_s *dev); static int cxd56_start_dma(FAR struct cxd56_dev_s *dev); static int cxd56_stop_dma(FAR struct cxd56_dev_s *priv); static void cxd56_set_dma_int_en(bool enabled); static void cxd56_set_dma_running(cxd56_dmahandle_t handle, bool running); +static void cxd56_set_mic_gains(uint8_t gain, + struct cxd56_aca_pwinput_param_s *param); +static void cxd56_set_mic_out_channel(FAR struct cxd56_dev_s *dev); static int cxd56_set_volume(enum cxd56_vol_id_e id, int16_t vol); static void cxd56_swap_buffer_rl(uint32_t addr, uint16_t size); +static int cxd56_take_sem(sem_t *sem); +#define cxd56_give_sem(s) (nxsem_post(s)) static void *cxd56_workerthread(pthread_addr_t pvarg); /**************************************************************************** @@ -350,6 +479,22 @@ const cxd56_aureg_t REG_AC_DEVICEID = { REG_BASE + 0x0000, 24, 8 }; +const cxd56_aureg_t REG_AC_PDN_AMICEXT = +{ + REG_BASE + 0x0100, 4, 1 +}; +const cxd56_aureg_t REG_AC_PDN_AMIC1 = +{ + REG_BASE + 0x0100, 5, 1 +}; +const cxd56_aureg_t REG_AC_PDN_AMIC2 = +{ + REG_BASE + 0x0100, 6, 1 +}; +const cxd56_aureg_t REG_AC_PDN_DMIC = +{ + REG_BASE + 0x0100, 15, 1 +}; const cxd56_aureg_t REG_AC_PDN_DSPB = { REG_BASE + 0x0100, 16, 1 @@ -370,11 +515,11 @@ const cxd56_aureg_t REG_AC_PDN_SMSTR = { REG_BASE + 0x0100, 20, 1 }; -const cxd56_aureg_t REG_AC_PDN_DSPS1 = +const cxd56_aureg_t REG_AC_PDN_DSPS2 = { REG_BASE + 0x0100, 21, 1 }; -const cxd56_aureg_t REG_AC_PDN_DSPS2 = +const cxd56_aureg_t REG_AC_PDN_DSPS1 = { REG_BASE + 0x0100, 22, 1 }; @@ -386,11 +531,11 @@ const cxd56_aureg_t REG_AC_FS_FS = { REG_BASE + 0x0104, 0, 1 }; -const cxd56_aureg_t REG_AC_DECIM0 = +const cxd56_aureg_t REG_AC_DECIM0_EN = { REG_BASE + 0x0104, 16, 1 }; -const cxd56_aureg_t REG_AC_DECIM1 = +const cxd56_aureg_t REG_AC_DECIM1_EN = { REG_BASE + 0x0104, 17, 1 }; @@ -398,7 +543,7 @@ const cxd56_aureg_t REG_AC_SDES_EN = { REG_BASE + 0x0104, 18, 1 }; -const cxd56_aureg_t REG_AC_MCK_AMBMSTR_EN = +const cxd56_aureg_t REG_AC_MCK_AHBMSTR_EN = { REG_BASE + 0x0104, 19, 1 }; @@ -450,6 +595,82 @@ const cxd56_aureg_t REG_AC_SDCK_OUTENX = { REG_BASE + 0x0200, 30, 1 }; +const cxd56_aureg_t REG_AC_HPF2_MODE = +{ + REG_BASE + 0x0204, 0, 2 +}; +const cxd56_aureg_t REG_AC_CIC2_GAIN_MODE = +{ + REG_BASE + 0x0204, 8, 1 +}; +const cxd56_aureg_t REG_AC_CIC2IN_SEL = +{ + REG_BASE + 0x0204, 9, 1 +}; +const cxd56_aureg_t REG_AC_HPF1_MODE = +{ + REG_BASE + 0x0204, 16, 2 +}; +const cxd56_aureg_t REG_AC_CIC1_GAIN_MODE = +{ + REG_BASE + 0x0204, 24, 1 +}; +const cxd56_aureg_t REG_AC_CIC1IN_SEL = +{ + REG_BASE + 0x0204, 25, 1 +}; +const cxd56_aureg_t REG_AC_ADC_FS = +{ + REG_BASE + 0x0204, 28, 2 +}; +const cxd56_aureg_t REG_AC_HPF4_MODE = +{ + REG_BASE + 0x0208, 0, 2 +}; +const cxd56_aureg_t REG_AC_CIC4IN_SEL = +{ + REG_BASE + 0x0208, 9, 1 +}; +const cxd56_aureg_t REG_AC_HPF3_MODE = +{ + REG_BASE + 0x0208, 16, 2 +}; +const cxd56_aureg_t REG_AC_CIC3IN_SEL = +{ + REG_BASE + 0x0208, 25, 1 +}; +const cxd56_aureg_t REG_AC_CIC1_RGAIN = +{ + REG_BASE + 0x020c, 0, 16 +}; +const cxd56_aureg_t REG_AC_CIC1_LGAIN = +{ + REG_BASE + 0x020c, 16, 16 +}; +const cxd56_aureg_t REG_AC_CIC2_RGAIN = +{ + REG_BASE + 0x0210, 0, 16 +}; +const cxd56_aureg_t REG_AC_CIC2_LGAIN = +{ + REG_BASE + 0x0210, 16, 16 +}; +const cxd56_aureg_t REG_AC_CIC3_RGAIN = +{ + REG_BASE + 0x0214, 0, 16 +}; +const cxd56_aureg_t REG_AC_CIC3_LGAIN = +{ + REG_BASE + 0x0214, 16, 16 +}; +const cxd56_aureg_t REG_AC_CIC4_RGAIN = +{ + REG_BASE + 0x0218, 0, 16 +}; +const cxd56_aureg_t REG_AC_CIC4_LGAIN = +{ + REG_BASE + 0x0218, 16, 16 +}; const cxd56_aureg_t REG_AC_SPC_EN = { REG_BASE + 0x021c, 15, 1 @@ -466,11 +687,11 @@ const cxd56_aureg_t REG_AC_CS_SIGN = { REG_BASE + 0x0228, 7, 1 }; -const cxd56_aureg_t REG_AC_SDIN1_VOL = +const cxd56_aureg_t REG_AC_SDIN2_VOL = { REG_BASE + 0x0228, 16, 8 }; -const cxd56_aureg_t REG_AC_SDIN2_VOL = +const cxd56_aureg_t REG_AC_SDIN1_VOL = { REG_BASE + 0x0228, 24, 8 }; @@ -514,6 +735,14 @@ const cxd56_aureg_t REG_AC_DNC1_START = { REG_BASE + 0x0304, 31, 1 }; +const cxd56_aureg_t REG_AC_DCMFS_34 = +{ + REG_BASE + 0x0308, 22, 2 +}; +const cxd56_aureg_t REG_AC_DCMFS = +{ + REG_BASE + 0x0308, 30, 2 +}; const cxd56_aureg_t REG_AC_NSX2 = { REG_BASE + 0x0340, 30, 1 @@ -530,21 +759,9 @@ const cxd56_aureg_t REG_AC_TEST_OUT_SEL0 = { REG_BASE + 0x0400, 6, 1 }; -const cxd56_aureg_t REG_AC_DSPRAM4_CLR = +const cxd56_aureg_t REG_AC_SER_MODE = { - REG_BASE + 0x0400, 28, 1 -}; -const cxd56_aureg_t REG_AC_DSPRAM2_CLR = -{ - REG_BASE + 0x0400, 29, 1 -}; -const cxd56_aureg_t REG_AC_DSPRAM1_CLR = -{ - REG_BASE + 0x0400, 31, 1 -}; -const cxd56_aureg_t REG_AC_S_RESET = -{ - REG_BASE + 0x0400, 16, 1 + REG_BASE + 0x0500, 0, 1 }; const cxd56_aureg_t REG_AC_PDM_OUT_EN = { @@ -554,6 +771,82 @@ const cxd56_aureg_t REG_AC_FS_CLK_EN = { REG_BASE + 0x0500, 24, 1 }; +const cxd56_aureg_t REG_AC_SEL_OUT4_R = +{ + REG_BASE + 0x0504, 0, 3 +}; +const cxd56_aureg_t REG_AC_SEL_OUT4_L = +{ + REG_BASE + 0x0504, 4, 3 +}; +const cxd56_aureg_t REG_AC_SEL_OUT3_R = +{ + REG_BASE + 0x0504, 8, 3 +}; +const cxd56_aureg_t REG_AC_SEL_OUT3_L = +{ + REG_BASE + 0x0504, 12, 3 +}; +const cxd56_aureg_t REG_AC_SEL_OUT2_R = +{ + REG_BASE + 0x0504, 16, 3 +}; +const cxd56_aureg_t REG_AC_SEL_OUT2_L = +{ + REG_BASE + 0x0504, 20, 3 +}; +const cxd56_aureg_t REG_AC_SEL_OUT1_R = +{ + REG_BASE + 0x0504, 24, 3 +}; +const cxd56_aureg_t REG_AC_SEL_OUT1_L = +{ + REG_BASE + 0x0504, 28, 3 +}; +const cxd56_aureg_t REG_AC_OUTEN_MIC1L_B = +{ + REG_BASE + 0x0580, 0, 1 +}; +const cxd56_aureg_t REG_AC_OUTEN_MIC1R_B = +{ + REG_BASE + 0x0580, 1, 1 +}; +const cxd56_aureg_t REG_AC_OUTEN_MIC2L_B = +{ + REG_BASE + 0x0580, 2, 1 +}; +const cxd56_aureg_t REG_AC_OUTEN_MIC2R_B = +{ + REG_BASE + 0x0580, 3, 1 +}; +const cxd56_aureg_t REG_AC_OUTEN_MIC1L_A = +{ + REG_BASE + 0x0580, 4, 1 +}; +const cxd56_aureg_t REG_AC_OUTEN_MIC1R_A = +{ + REG_BASE + 0x0580, 5, 1 +}; +const cxd56_aureg_t REG_AC_OUTEN_MIC2L_A = +{ + REG_BASE + 0x0580, 6, 1 +}; +const cxd56_aureg_t REG_AC_OUTEN_MIC2R_A = +{ + REG_BASE + 0x0580, 7, 1 +}; +const cxd56_aureg_t REG_AC_SEL_OUTF = +{ + REG_BASE + 0x0580, 16, 2 +}; +const cxd56_aureg_t REG_AC_SEL_INF = +{ + REG_BASE + 0x0580, 20, 1 +}; +const cxd56_aureg_t REG_AC_SEL_DECIM = +{ + REG_BASE + 0x0580, 24, 1 +}; const cxd56_aureg_t REG_AC_DEQ_EN = { REG_BASE + 0x0600, 31, 1 @@ -565,19 +858,59 @@ const cxd56_aureg_t REG_AC_LR_SWAP1 = /* BCA registers */ +const cxd56_aureg_t REG_MIC_IN_START_ADR = +{ + REG_BASE + 0x1000, 0, 30 +}; +const cxd56_aureg_t REG_MIC_IN_SAMPLE_NO = +{ + REG_BASE + 0x1004, 0, 32 +}; const cxd56_aureg_t REG_MIC_RTD_TRG = { REG_BASE + 0x1008, 0, 3 }; +const cxd56_aureg_t REG_MIC_IN_BITWT = +{ + REG_BASE + 0x100c, 0, 1 +}; const cxd56_aureg_t REG_MIC_CH8_SEL = { REG_BASE + 0x1010, 0, 4 }; +const cxd56_aureg_t REG_MIC_CH7_SEL = +{ + REG_BASE + 0x1010, 4, 4 +}; +const cxd56_aureg_t REG_MIC_CH6_SEL = +{ + REG_BASE + 0x1010, 8, 4 +}; +const cxd56_aureg_t REG_MIC_CH5_SEL = +{ + REG_BASE + 0x1010, 12, 4 +}; +const cxd56_aureg_t REG_MIC_CH4_SEL = +{ + REG_BASE + 0x1010, 16, 4 +}; +const cxd56_aureg_t REG_MIC_CH3_SEL = +{ + REG_BASE + 0x1010, 20, 4 +}; +const cxd56_aureg_t REG_MIC_CH2_SEL = +{ + REG_BASE + 0x1010, 24, 4 +}; +const cxd56_aureg_t REG_MIC_CH1_SEL = +{ + REG_BASE + 0x1010, 28, 4 +}; const cxd56_aureg_t REG_MIC_MON_START = { - REG_BASE + 0x1014, 3, 1 + REG_BASE + 0x1014, 0, 1 }; -const cxd56_aureg_t REG_MIC_MON_ERROR_SET = +const cxd56_aureg_t REG_MIC_MON_ERRSET = { REG_BASE + 0x1014, 8, 8 }; @@ -625,14 +958,6 @@ const cxd56_aureg_t REG_I2S_ENSEL = { REG_BASE + 0x1110, 0, 1 }; -const cxd56_aureg_t REG_CLK_EN_AHBMSTR_MIC = -{ - REG_BASE + 0x11f0, 0, 1 -}; -const cxd56_aureg_t REG_CLK_EN_AHBMSTR_I2S1 = -{ - REG_BASE + 0x11f0, 1, 1 -}; const cxd56_aureg_t REG_MIC_INT_CTRL_DONE = { REG_BASE + 0x1140, 0, 1 @@ -641,6 +966,10 @@ const cxd56_aureg_t REG_MIC_INT_CTRL_ERR = { REG_BASE + 0x1140, 1, 1 }; +const cxd56_aureg_t REG_MIC_INT_CTRL_SMP = +{ + REG_BASE + 0x1140, 2, 1 +}; const cxd56_aureg_t REG_MIC_INT_CTRL_CMB = { REG_BASE + 0x1140, 3, 1 @@ -709,6 +1038,14 @@ const cxd56_aureg_t REG_INT_HRESP_ERR = { REG_BASE + 0x1160, 0, 1 }; +const cxd56_aureg_t REG_CLK_EN_AHBMSTR_MIC = +{ + REG_BASE + 0x11f0, 0, 1 +}; +const cxd56_aureg_t REG_CLK_EN_AHBMSTR_I2S1 = +{ + REG_BASE + 0x11f0, 1, 1 +}; const cxd56_aureg_t REG_AHB_MASTER_MIC_MASK = { REG_BASE + 0x1730, 0, 32 @@ -760,43 +1097,32 @@ static void write_reg_addr(const cxd56_aureg_t *reg, uint32_t val) #define write_reg(reg, val) (write_reg_addr(&(reg), (val))) #define write_reg32(reg, val) (*((volatile uint32_t *)(reg).addr) = (val)) -/* Aquire semaphore */ +/**************************************************************************** + * Name: cxd56_take_sem + * + * Description: + * Take a semaphore count, handling the nasty EINTR return if we are + * interrupted by a signal. + * + ****************************************************************************/ -static void cxd56_take_sem(sem_t *sem) +static int cxd56_take_sem(sem_t *sem) { - int ret; - - do - { - ret = nxsem_wait(sem); - if (ret != 0 && ret != -EINTR) - { - auderr("ERROR: Take semaphore failed (%d)\n", ret); - } - } - while (ret == -EINTR); -} - -/* Release semaphore */ - -static void cxd56_give_sem(sem_t *sem) -{ - int ret; - - do - { - ret = nxsem_post(sem); - if (ret != 0 && ret != -EINTR) - { - auderr("ERROR: Give semaphore failed (%d)\n", ret); - } - } - while (ret == -EINTR); + return nxsem_wait_uninterruptible(sem); } static void cxd56_int_clear(cxd56_dmahandle_t handle, uint8_t intbits) { - if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + if (handle == CXD56_AUDIO_DMA_MIC) + { + if (intbits > CXD56_DMA_INT_ERR) + { + intbits = (intbits & 0x0f) | ((intbits & 0xf0) >> 2); + } + + write_reg32(REG_MIC_INT_CTRL_DONE, intbits); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) { write_reg32(REG_I2S1_INT_CTRL_DONE, intbits); } @@ -804,7 +1130,17 @@ static void cxd56_int_clear(cxd56_dmahandle_t handle, uint8_t intbits) static void cxd56_int_mask(cxd56_dmahandle_t handle, uint8_t intbits) { - if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + if (handle == CXD56_AUDIO_DMA_MIC) + { + if (intbits > CXD56_DMA_INT_ERR) + { + intbits = (intbits & 0x0f) | ((intbits & 0xf0) >> 2); + } + + intbits |= read_reg32(REG_MIC_INT_MASK_DONE); + write_reg32(REG_MIC_INT_MASK_DONE, intbits); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) { intbits |= read_reg32(REG_I2S1_INT_MASK_DONE); write_reg32(REG_I2S1_INT_MASK_DONE, intbits); @@ -815,7 +1151,17 @@ static void cxd56_int_unmask(cxd56_dmahandle_t handle, uint8_t intbits) { uint32_t curr; - if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + if (handle == CXD56_AUDIO_DMA_MIC) + { + if (intbits > CXD56_DMA_INT_ERR) + { + intbits = (intbits & 0x0f) | ((intbits & 0xf0) >> 2); + } + + curr = read_reg32(REG_MIC_INT_MASK_DONE); + write_reg32(REG_MIC_INT_MASK_DONE, curr & ~intbits); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) { curr = read_reg32(REG_I2S1_INT_MASK_DONE); write_reg32(REG_I2S1_INT_MASK_DONE, curr & ~intbits); @@ -824,7 +1170,11 @@ static void cxd56_int_unmask(cxd56_dmahandle_t handle, uint8_t intbits) static void cxd56_int_unmask_ahb(cxd56_dmahandle_t handle) { - if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + if (handle == CXD56_AUDIO_DMA_MIC) + { + write_reg32(REG_AHB_MASTER_MIC_MASK, 0x00000303); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) { write_reg32(REG_AHB_MASTER_I2S1_MASK, 0x00000202); } @@ -832,7 +1182,12 @@ static void cxd56_int_unmask_ahb(cxd56_dmahandle_t handle) static uint32_t cxd56_int_get_state(cxd56_dmahandle_t handle) { - if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + if (handle == CXD56_AUDIO_DMA_MIC) + { + return (0x3f & read_reg32(REG_MIC_INT_CTRL_DONE) + & ~(read_reg32(REG_MIC_INT_MASK_DONE))); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) { return (0x3f & read_reg32(REG_I2S1_INT_CTRL_DONE) & ~(read_reg32(REG_I2S1_INT_MASK_DONE))); @@ -841,9 +1196,55 @@ static uint32_t cxd56_int_get_state(cxd56_dmahandle_t handle) return 0; } -static uint8_t cxd56_get_monbuf_state(cxd56_dmahandle_t handle) +static uint32_t cxd56_int_has_error(cxd56_dmahandle_t handle) { - if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + if (handle == CXD56_AUDIO_DMA_MIC) + { + return read_reg(REG_MIC_INT_CTRL_ERR); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + { + return read_reg(REG_I2S1_INT_CTRL_ERR); + } + + return 0; +} + +static uint32_t cxd56_int_is_done(cxd56_dmahandle_t handle) +{ + if (handle == CXD56_AUDIO_DMA_MIC) + { + return read_reg(REG_MIC_INT_CTRL_DONE); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + { + return read_reg(REG_I2S1_INT_CTRL_DONE); + } + + return 0; +} + +static uint32_t cxd56_int_has_smp(cxd56_dmahandle_t handle) +{ + if (handle == CXD56_AUDIO_DMA_MIC) + { + return read_reg(REG_MIC_INT_CTRL_SMP); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + { + return read_reg(REG_I2S1_INT_CTRL_SMP); + } + + return 0; +} + +static uint8_t cxd56_get_mon_buf(cxd56_dmahandle_t handle) +{ + if (handle == CXD56_AUDIO_DMA_MIC) + { + return read_reg(REG_MIC_MON_MONBUF); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) { return read_reg(REG_I2S1_OUT_MON_MONBUF); } @@ -851,11 +1252,45 @@ static uint8_t cxd56_get_monbuf_state(cxd56_dmahandle_t handle) return 0; } +static uint8_t cxd56_get_mon_err(cxd56_dmahandle_t handle) +{ + if (handle == CXD56_AUDIO_DMA_MIC) + { + return read_reg(REG_MIC_MON_ERRSET); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + { + return read_reg(REG_I2S1_OUT_MON_ERRSET); + } + + return 0; +} + +static uint8_t cxd56_dma_is_busy(cxd56_dmahandle_t handle) +{ + if (handle == CXD56_AUDIO_DMA_MIC) + { + return read_reg(REG_MIC_RTD_TRG) != CXD56_DMA_CMD_FIFO_NOT_FULL; + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + { + return read_reg(REG_I2S1_OUT_RTD_TRG) != CXD56_DMA_CMD_FIFO_NOT_FULL; + } + + return 0; +} + static void cxd56_reset_channel_sel(cxd56_dmahandle_t handle) { uint32_t sel; - if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + if (handle == CXD56_AUDIO_DMA_MIC) + { + sel = read_reg32(REG_MIC_CH8_SEL); + write_reg32(REG_MIC_CH8_SEL, 0xffffffff); + write_reg32(REG_MIC_CH8_SEL, sel); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) { sel = read_reg32(REG_I2S1_OUT_SD1_R_SEL); write_reg32(REG_I2S1_OUT_SD1_R_SEL, 0xffffffff); @@ -868,13 +1303,46 @@ static void cxd56_dma_int_handler(void) uint16_t err_code; uint32_t int_irq; uint32_t int_i2s; + uint32_t int_mic; cxd56_dmahandle_t hdl; err_code = CXD56_AUDIO_ECODE_DMA_HANDLE_INV; int_irq = read_reg32(REG_INT_IRQ1); int_i2s = cxd56_int_get_state(CXD56_AUDIO_DMA_I2S0_DOWN); + int_mic = cxd56_int_get_state(CXD56_AUDIO_DMA_MIC); - if ((int_irq & CXD56_IRQ1_BIT_I2S1) && (int_i2s != 0)) + if ((int_irq & CXD56_IRQ1_BIT_MIC) && (int_mic != 0)) + { + hdl = CXD56_AUDIO_DMA_MIC; + + write_reg32(REG_MIC_INT_CTRL_DONE, int_mic); + + if (int_mic & (1 << REG_MIC_INT_CTRL_DONE.pos)) + { + err_code = CXD56_AUDIO_ECODE_DMA_CMPLT; + } + + if (int_mic & (1 << REG_MIC_INT_CTRL_ERR.pos)) + { + /* Mask and clear transfer error interrupt */ + + write_reg(REG_MIC_INT_MASK_ERR, 1); + write_reg(REG_MIC_INT_CTRL_ERR, 1); + + err_code = CXD56_AUDIO_ECODE_DMA_TRANS; + } + + if (int_mic & (1 << REG_MIC_INT_CTRL_CMB.pos)) + { + /* Mask and clear bus error interrupt */ + + write_reg(REG_MIC_INT_MASK_CMB, 1); + write_reg(REG_MIC_INT_CTRL_CMB, 1); + + err_code = CXD56_AUDIO_ECODE_DMA_CMB; + } + } + else if ((int_irq & CXD56_IRQ1_BIT_I2S1) && (int_i2s != 0)) { hdl = CXD56_AUDIO_DMA_I2S0_DOWN; @@ -922,30 +1390,17 @@ static void cxd56_dma_int_handler(void) /* Trigger new DMA job */ cxd56_take_sem(&dev->pendsem); + if (dq_count(&dev->runningq) > 0) { FAR struct ap_buffer_s *apb; - apb = (struct ap_buffer_s *) dq_pop(&dev->runningq); + apb = (struct ap_buffer_s *) dq_get(&dev->runningq); dev->dev.upper(dev->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK); } cxd56_give_sem(&dev->pendsem); - if (dev->mq != NULL) - { - /* Request more data */ - - msg.msg_id = AUDIO_MSG_DATA_REQUEST; - msg.u.data = 0; - ret = nxmq_send(dev->mq, (FAR const char *) &msg, - sizeof(msg), CONFIG_CXD56_MSG_PRIO); - if (ret != OK) - { - auderr("ERROR: nxmq_send to request failed (%d)\n", ret); - } - } - if (err_code == CXD56_AUDIO_ECODE_DMA_TRANS) { /* Notify end of data */ @@ -962,6 +1417,19 @@ static void cxd56_dma_int_handler(void) } } } + else if (dev->mq != NULL) + { + /* Request more data */ + + msg.msg_id = AUDIO_MSG_DATA_REQUEST; + msg.u.data = 0; + ret = nxmq_send(dev->mq, (FAR const char *) &msg, + sizeof(msg), CONFIG_CXD56_MSG_PRIO); + if (ret != OK) + { + auderr("ERROR: nxmq_send to request failed (%d)\n", ret); + } + } } } @@ -1082,6 +1550,52 @@ static int cxd56_set_volume(enum cxd56_vol_id_e id, int16_t vol) return OK; } +static void cxd56_init_mic_input(uint8_t mic_num, uint8_t bits) +{ + uint8_t i; + + cxd56_aureg_t mic_ch_sel[] = + { + REG_MIC_CH1_SEL, + REG_MIC_CH2_SEL, + REG_MIC_CH3_SEL, + REG_MIC_CH4_SEL, + REG_MIC_CH5_SEL, + REG_MIC_CH6_SEL, + REG_MIC_CH7_SEL, + REG_MIC_CH8_SEL + }; + + if (bits == 16) + { + mic_num = (mic_num > (CXD56_MIC_TRANS_CH_16BIT * 2)) ? + CXD56_MIC_TRANS_CH_16BIT : (mic_num + 1) / 2; + + write_reg(REG_MIC_IN_BITWT, 1); + } + else + { + mic_num = (mic_num > CXD56_MIC_TRANS_CH_24BIT) ? + CXD56_MIC_TRANS_CH_24BIT : mic_num; + + write_reg(REG_MIC_IN_BITWT, 0); + } + + for (i = 0; i < mic_num; i++) + { + write_reg(mic_ch_sel[i], i); + } + + for (i = mic_num; i < CXD56_IN_CHANNELS_MAX; i++) + { + write_reg(mic_ch_sel[i], 8); + } + + write_reg(REG_CLK_EN_AHBMSTR_MIC, 1); + write_reg(REG_MIC_IN_START_ADR, 0x00000000); + write_reg(REG_MIC_IN_SAMPLE_NO, 0); +} + static void cxd56_init_i2s1_output(uint8_t bits) { write_reg(REG_I2S1_OUT_SD1_L_SEL, 1); @@ -1108,7 +1622,11 @@ static void cxd56_set_dma_int_en(bool enabled) static void cxd56_set_dma_running(cxd56_dmahandle_t handle, bool running) { - if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) + if (handle == CXD56_AUDIO_DMA_MIC) + { + write_reg(REG_MIC_RTD_TRG, (running ? 0x01 : 0x04)); + } + else if (handle == CXD56_AUDIO_DMA_I2S0_DOWN) { write_reg(REG_I2S1_OUT_RTD_TRG, (running ? 0x01 : 0x04)); } @@ -1116,6 +1634,7 @@ static void cxd56_set_dma_running(cxd56_dmahandle_t handle, bool running) static void cxd56_init_dma(FAR struct cxd56_dev_s *dev) { + uint8_t err; uint8_t ints; audinfo("cxd56_init_dma: state = %d, hdl = %d.\n", @@ -1129,11 +1648,19 @@ static void cxd56_init_dma(FAR struct cxd56_dev_s *dev) /* Enable DMA */ - write_reg(REG_AC_MCK_AMBMSTR_EN, 1); + write_reg(REG_AC_MCK_AHBMSTR_EN, 1); /* Setup output, bit width etc */ - cxd56_init_i2s1_output(dev->bitwidth); + if (dev->dma_handle == CXD56_AUDIO_DMA_MIC) + { + cxd56_set_mic_out_channel(dev); + cxd56_init_mic_input(dev->channels, dev->bitwidth); + } + else + { + cxd56_init_i2s1_output(dev->bitwidth); + } /* Clear interrupt states */ @@ -1144,6 +1671,22 @@ static void cxd56_init_dma(FAR struct cxd56_dev_s *dev) cxd56_int_unmask(dev->dma_handle, ints); cxd56_int_unmask_ahb(dev->dma_handle); + /* Check channel setting. */ + + err = cxd56_get_mon_err(dev->dma_handle); + if (err == CXD56_DMA_MSTATE_ERR_NO_ENABLE_CH) + { + auderr("ERROR: No enabled channel for %d\n", dev->dma_handle); + } + else if (err == CXD56_DMA_MSTATE_ERR_CH1_4_INVALID) + { + auderr("ERROR: Channel 1-4 invalid for %d\n", dev->dma_handle); + } + else if (err == CXD56_DMA_MSTATE_ERR_CH5_8_INVALID) + { + auderr("ERROR: Channel 5-8 invalid for %d\n", dev->dma_handle); + } + cxd56_set_dma_int_en(true); } @@ -1177,7 +1720,11 @@ static void cxd56_power_on_i2s1(FAR struct cxd56_dev_s *dev) static int cxd56_power_on_aca(uint32_t samplerate) { + struct cxd56_ser_des_param_s ser_param; struct cxd56_aca_pwon_param_s pwon_param; + uint8_t mic_mode; + uint8_t mic_sel; + uint8_t i; if (as_aca_control(CXD56_ACA_CTL_CHECK_ID, (uint32_t)NULL) != 0) { @@ -1209,6 +1756,33 @@ static int cxd56_power_on_aca(uint32_t samplerate) return -EBUSY; } + /* MIC SETUP */ + + mic_mode = cxd56_get_mic_mode(); + + if (CXD56_AUDIO_CFG_MIC_MODE_128FS == mic_mode) + { + ser_param.ser_mode = CXD56_SER_MODE_4CH; + ser_param.ser_fs = CXD56_SER_FS_128; + } + else + { + ser_param.ser_mode = CXD56_SER_MODE_8CH; + ser_param.ser_fs = CXD56_SER_FS_64; + } + + for (i = 0; i < CXD56_IN_CHANNELS_MAX; i++) + { + mic_sel = (CXD56_AUDIO_CFG_MIC >> (i * CXD56_MIC_CH_BITNUM)) & + CXD56_MIC_CH_BITMAP; + ser_param.sel_ch.in[i] = (enum cxd56_pulco_ser_sel_ch_id_e)mic_sel; + } + + if (as_aca_control(CXD56_ACA_CTL_SET_SERDES, (uint32_t)&ser_param) != 0) + { + return -EBUSY; + } + return OK; } @@ -1258,13 +1832,8 @@ static int cxd56_power_on_analog_output(FAR struct cxd56_dev_s *dev) /* Power on S-Mster. */ write_reg(REG_AC_PDN_SMSTR, 0); - - /* Set NSDD. */ - write_reg(REG_AC_NSDD, 0x07fb5); - /* Set NSX2. */ - if (dev->samplerate > 48000 && CXD56_AUDIO_MCLK == CXD56_XTAL_49_152MHZ) { write_reg(REG_AC_NSX2, 1); @@ -1285,8 +1854,422 @@ static int cxd56_power_on_analog_output(FAR struct cxd56_dev_s *dev) return OK; } +static void cxd56_set_mic_gains(uint8_t gain, + struct cxd56_aca_pwinput_param_s *param) +{ + uint8_t i; + uint8_t pga_gain; + uint8_t mic_id = 0; + uint8_t mic_sel = 0; + + /* TODO: Replace gain param with array in dev. How to configure? */ + + for (i = 0; i < CXD56_IN_CHANNELS_MAX; i++) + { + mic_sel = (CXD56_AUDIO_CFG_MIC >> (i * CXD56_MIC_CH_BITNUM)) & + CXD56_MIC_CH_BITMAP; + if ((mic_sel >= 1) && (mic_sel <= 4)) + { + mic_id = mic_sel - 1; + param->mic_gain[mic_id] = (gain >= CXD56_MIC_GAIN_MAX) ? + CXD56_MIC_GAIN_MAX : + (gain / 30) * 30; + + pga_gain = gain - param->mic_gain[mic_id]; + param->pga_gain[mic_id] = (pga_gain >= CXD56_MIC_PGA_GAIN_MAX) ? + CXD56_MIC_PGA_GAIN_MAX : pga_gain; + } + } +} + +static void cxd56_get_mic_config(uint8_t *count, uint8_t *dev, uint8_t *mode) +{ + uint8_t i; + uint8_t is_dmic; + uint8_t is_amic; + uint8_t mic_sel = 0; + uint8_t mic_count = 0; + + *dev = 0; + *mode = 0; + + for (i = 0; i < CXD56_IN_CHANNELS_MAX; i++) + { + mic_sel = (CXD56_AUDIO_CFG_MIC >> (i * CXD56_MIC_CH_BITNUM)) & + CXD56_MIC_CH_BITMAP; + if ((mic_sel >= 1) && (mic_sel <= 4)) + { + is_amic = true; + mic_count++; + } + else if ((mic_sel >= 5) && (mic_sel <= 12)) + { + is_dmic = true; + mic_count++; + } + } + + if (is_amic) + { + if (is_dmic) + { + *dev = CXD56_AUDIO_CFG_MIC_DEV_ANADIG; + *mode = CXD56_AUDIO_CFG_MIC_MODE_64FS; + } + else + { + *dev = CXD56_AUDIO_CFG_MIC_DEV_ANALOG; + *mode = CXD56_AUDIO_CFG_MIC_MODE_128FS; + } + } + else + { + if (is_dmic) + { + *dev = CXD56_AUDIO_CFG_MIC_DEV_DIGITAL; + *mode = CXD56_AUDIO_CFG_MIC_MODE_64FS; + } + else + { + *dev = CXD56_AUDIO_CFG_MIC_DEV_NONE; + *mode = CXD56_AUDIO_CFG_MIC_MODE_64FS; + } + } + + *count = mic_count; +} + +static uint8_t cxd56_get_mic_mode(void) +{ + uint8_t count; + uint8_t dev; + uint8_t mode; + + cxd56_get_mic_config(&count, &dev, &mode); + + return mode; +} + +static int cxd56_power_on_micbias(FAR struct cxd56_dev_s *dev) +{ + struct timespec start; + + if (as_aca_control(CXD56_ACA_CTL_POWER_ON_MICBIAS, (uint32_t)NULL) != 0) + { + return -EBUSY; + } + + /* Set mic boot time */ + + if (clock_gettime(CLOCK_REALTIME, &start) < 0) + { + dev->mic_boot_start = 0x0ull; + } + else + { + dev->mic_boot_start = (uint64_t)start.tv_sec * 1000 + + (uint64_t)start.tv_nsec / 1000000; + } + + return OK; +} + +static void cxd56_audio_power_on_cic(uint8_t mic_in, + uint8_t mic_mode, + uint8_t cic_num, + FAR struct cxd56_audio_mic_gain_s *gain) +{ + /* Power on CIC. */ + + if (mic_in == CXD56_AUDIO_CFG_CIC_IN_SEL_CXD) + { + if (cic_num > 3) + { + write_reg(REG_AC_CIC4IN_SEL, 0); + write_reg(REG_AC_HPF4_MODE, 1); + } + + if (cic_num > 2) + { + if (read_reg(REG_AC_PDN_AMICEXT) == 1) + { + write_reg(REG_AC_PDN_AMICEXT, 0); + } + + write_reg(REG_AC_CIC3IN_SEL, 0); + write_reg(REG_AC_HPF3_MODE, 1); + } + + if (cic_num > 1) + { + write_reg(REG_AC_PDN_AMIC2, 0); + write_reg(REG_AC_CIC2IN_SEL, 0); + write_reg(REG_AC_HPF2_MODE, 1); + write_reg(REG_AC_CIC2_GAIN_MODE, 1); + } + + if (cic_num > 0) + { + write_reg(REG_AC_PDN_AMIC1, 0); + write_reg(REG_AC_CIC1IN_SEL, 0); + write_reg(REG_AC_HPF1_MODE, 1); + write_reg(REG_AC_CIC1_GAIN_MODE, 1); + } + } + else if(mic_in == CXD56_AUDIO_CFG_CIC_IN_SEL_DMICIF) + { + if (read_reg(REG_AC_PDN_DMIC) == 1) + { + write_reg(REG_AC_PDN_DMIC, 0); + } + + if (cic_num > 3) + { + write_reg(REG_AC_CIC4IN_SEL, 1); + write_reg(REG_AC_HPF4_MODE, 1); + } + + if (cic_num > 2) + { + write_reg(REG_AC_CIC3IN_SEL, 1); + write_reg(REG_AC_HPF3_MODE, 1); + } + + if (cic_num > 1) + { + write_reg(REG_AC_CIC2IN_SEL, 1); + write_reg(REG_AC_HPF2_MODE, 1); + write_reg(REG_AC_CIC2_GAIN_MODE, 1); + } + + if (cic_num > 0) + { + write_reg(REG_AC_CIC1IN_SEL, 1); + write_reg(REG_AC_HPF1_MODE, 1); + write_reg(REG_AC_CIC1_GAIN_MODE, 1); + } + } + + if (mic_mode == CXD56_AUDIO_CFG_MIC_MODE_128FS) + { + write_reg(REG_AC_ADC_FS, 1); + } + else if (mic_mode == CXD56_AUDIO_CFG_MIC_MODE_64FS) + { + write_reg(REG_AC_ADC_FS, 0); + } +} + +static int cxd56_power_on_decim(uint8_t mic_mode, uint16_t samplerate) +{ + /* Enable AHBMASTER. + * Because the output of DecimationFilter is input to BusIF. + */ + + write_reg(REG_AC_MCK_AHBMSTR_EN, 1); + + /* Power on DECIM. */ + + write_reg(REG_AC_DECIM0_EN, 1); + + /* DECIM param */ + + if ((mic_mode == CXD56_AUDIO_CFG_MIC_MODE_64FS) && (samplerate > 48000)) + { + write_reg(REG_AC_SEL_DECIM, 0); + } + else + { + write_reg(REG_AC_SEL_DECIM, 1); + } + + if (mic_mode == CXD56_AUDIO_CFG_MIC_MODE_128FS) + { + write_reg(REG_AC_SEL_INF, 1); + write_reg(REG_AC_DCMFS, 2); + write_reg(REG_AC_DCMFS_34, 2); + } + else if (mic_mode == CXD56_AUDIO_CFG_MIC_MODE_64FS) + { + write_reg(REG_AC_SEL_INF, 0); + write_reg(REG_AC_DCMFS, 1); + write_reg(REG_AC_DCMFS_34, 1); + } + else + { + return -EINVAL; + } + + if (samplerate <= 48000) + { + write_reg(REG_AC_SEL_OUTF, 0); + } + else + { + write_reg(REG_AC_SEL_OUTF, 2); + } + + /* DECIM_SEL */ + + write_reg(REG_AC_OUTEN_MIC2R_A, ((0x0f >> 3) & 0x01)); + write_reg(REG_AC_OUTEN_MIC2L_A, ((0x0f >> 2) & 0x01)); + write_reg(REG_AC_OUTEN_MIC1R_A, ((0x0f >> 1) & 0x01)); + write_reg(REG_AC_OUTEN_MIC1L_A, ((0x0f >> 0) & 0x01)); + + write_reg(REG_AC_OUTEN_MIC2R_B, ((0x0f >> 3) & 0x01)); + write_reg(REG_AC_OUTEN_MIC2L_B, ((0x0f >> 2) & 0x01)); + write_reg(REG_AC_OUTEN_MIC1R_B, ((0x0f >> 1) & 0x01)); + write_reg(REG_AC_OUTEN_MIC1L_B, ((0x0f >> 0) & 0x01)); + + return OK; +} + +static void cxd56_set_mic_out_channel(FAR struct cxd56_dev_s *dev) +{ + uint8_t i; + uint8_t mic_num; + uint8_t ch_sel[CXD56_IN_CHANNELS_MAX]; + + mic_num = dev->channels; + + if ((dev->bitwidth == 16) && + (CXD56_DMA_FORMAT == CXD56_DMA_FORMAT_RL)) + { + for (i = 0; i < CXD56_IN_CHANNELS_MAX; i++) + { + ch_sel[i] = (i & 1) ? i - 1 : i + 1; + } + } + else + { + for (i = 0; i < CXD56_IN_CHANNELS_MAX; i++) + { + ch_sel[i] = i; + } + } + + /* For uneven mic counts, duplicate last channel (e.g. dual mono) */ + + if ((dev->bitwidth == 16) && ((mic_num & 1) == 1)) + { + if (CXD56_DMA_FORMAT == CXD56_DMA_FORMAT_LR) + { + ch_sel[mic_num] = ch_sel[mic_num - 1]; + } + else + { + ch_sel[mic_num - 1] = ch_sel[mic_num]; + } + } + + write_reg(REG_AC_SEL_OUT1_L, ch_sel[0]); + write_reg(REG_AC_SEL_OUT1_R, ch_sel[1]); + write_reg(REG_AC_SEL_OUT2_L, ch_sel[2]); + write_reg(REG_AC_SEL_OUT2_R, ch_sel[3]); + write_reg(REG_AC_SEL_OUT3_L, ch_sel[4]); + write_reg(REG_AC_SEL_OUT3_R, ch_sel[5]); + write_reg(REG_AC_SEL_OUT4_L, ch_sel[6]); + write_reg(REG_AC_SEL_OUT4_R, ch_sel[7]); +} + +/**************************************************************************** + * Name: cxd56_power_on_input + * + * Description: + * Configure and enable input with selected samplerate and mics. + * + ****************************************************************************/ + +static int cxd56_power_on_input(FAR struct cxd56_dev_s *dev) +{ + uint8_t i; + uint8_t cic_num; + uint8_t cic_write_num; + uint8_t mic_dev; + uint8_t mic_mode; + uint8_t mic_num; + uint8_t ret; + uint32_t val; + struct cxd56_audio_mic_gain_s cic_gain; + struct cxd56_aca_pwinput_param_s param; + + const cxd56_aureg_t cic_gain_reg[CXD56_IN_CHANNELS_MAX] = + { + REG_AC_CIC1_LGAIN, + REG_AC_CIC1_RGAIN, + REG_AC_CIC2_LGAIN, + REG_AC_CIC2_RGAIN, + REG_AC_CIC3_LGAIN, + REG_AC_CIC3_RGAIN, + REG_AC_CIC4_LGAIN, + REG_AC_CIC4_RGAIN + }; + + memset((void *)¶m, 0, sizeof(param)); + + cxd56_get_mic_config(&mic_num, &mic_dev, &mic_mode); + + param.mic_dev = mic_dev; + if (param.mic_dev == CXD56_AUDIO_CFG_MIC_DEV_ANALOG || + param.mic_dev == CXD56_AUDIO_CFG_MIC_DEV_ANADIG) + { + cxd56_power_on_micbias(dev); + } + + param.mic_bias_sel = CXD56_MIC_BIAS; + + /* TODO: Replace hardcoded mic gain with configuration */ + + cxd56_set_mic_gains(120, ¶m); + + if (as_aca_control(CXD56_ACA_CTL_POWER_ON_INPUT, + (uint32_t)¶m) != 0) + { + return -EBUSY; + } + + /* Power on CIC */ + + cic_num = (mic_num + 1) / CXD56_CIC_MIC_CH_NUM; + + for (i = 0; i < CXD56_IN_CHANNELS_MAX; i++) + { + val = (CXD56_AUDIO_CFG_MIC >> (i * CXD56_MIC_CH_BITNUM)) & + CXD56_MIC_CH_BITMAP; + cic_gain.gain[i] = (val > 4) ? param.mic_gain[i] : 0; + } + + cic_write_num = (CXD56_IN_CHANNELS_MAX >= (cic_num * 2)) ? + (cic_num * 2) : CXD56_IN_CHANNELS_MAX; + + for (i = 0; i < cic_write_num; i++) + { + val = (uint32_t)(pow(10.0f, + ((float)cic_gain.gain[i] / + 100.0f / 20.0f)) * 0x4000 + + 0x4000); + + write_reg(cic_gain_reg[i], val); + } + + cxd56_audio_power_on_cic(CXD56_AUDIO_CFG_CIC_IN, + mic_mode, cic_num, &cic_gain); + + /* Power on decim */ + + ret = cxd56_power_on_decim(mic_mode, dev->samplerate); + if (ret != OK) + { + auderr("ERROR: Decim power on failed (%d)\n", ret); + } + + return ret; +} + static int cxd56_power_on(FAR struct cxd56_dev_s *dev) { + uint8_t mic_mode; + if (g_codec_start_count == 0) { board_audio_i2s_enable(); @@ -1328,6 +2311,22 @@ static int cxd56_power_on(FAR struct cxd56_dev_s *dev) write_reg(REG_AC_SDES_EN, 1); + /* Set mic mode */ + + mic_mode = cxd56_get_mic_mode(); + if (mic_mode == CXD56_AUDIO_CFG_MIC_MODE_128FS) + { + write_reg(REG_AC_FS_FS, 0); + write_reg(REG_AC_SER_MODE, 1); + write_reg(REG_AC_ADC_FS, 1); + } + else + { + write_reg(REG_AC_FS_FS, 1); + write_reg(REG_AC_SER_MODE, 0); + write_reg(REG_AC_ADC_FS, 0); + } + /* Power on codec */ write_reg(REG_AC_PDN_DSPC, 0); @@ -1402,7 +2401,7 @@ static int cxd56_power_off(FAR struct cxd56_dev_s *dev) { /* Disable AHBMASTER. */ - write_reg(REG_AC_MCK_AMBMSTR_EN, 0); + write_reg(REG_AC_MCK_AHBMSTR_EN, 0); /* Disable SRC. */ @@ -1521,6 +2520,27 @@ static int cxd56_getcaps(FAR struct audio_lowerhalf_s *lower, int type, } break; + /* Output capabilities */ + + case AUDIO_TYPE_INPUT: + + caps->ac_channels = CXD56_IN_CHANNELS_MAX; + + switch (caps->ac_subtype) + { + case AUDIO_TYPE_QUERY: + + /* Report supported input sample rates */ + + caps->ac_controls.b[0] = CXD56_SUPP_RATES_L; + caps->ac_controls.b[1] = CXD56_SUPP_RATES_H; + break; + + default: + break; + } + break; + /* Feature capabilities */ case AUDIO_TYPE_FEATURE: @@ -1529,7 +2549,8 @@ static int cxd56_getcaps(FAR struct audio_lowerhalf_s *lower, int type, if (caps->ac_subtype == AUDIO_FU_UNDEF) { - caps->ac_controls.b[0] = AUDIO_FU_VOLUME; + caps->ac_controls.b[0] = AUDIO_FU_VOLUME | AUDIO_FU_MUTE; + caps->ac_controls.b[1] = AUDIO_FU_INP_GAIN >> 8; } break; @@ -1593,7 +2614,8 @@ static int cxd56_configure(FAR struct audio_lowerhalf_s *lower, FAR const struct audio_caps_s *caps) #endif { - int ret; + int ret = 0; + uint8_t poweron = 0; FAR struct cxd56_dev_s *priv = (FAR struct cxd56_dev_s *)lower; switch (caps->ac_type) @@ -1624,6 +2646,46 @@ static int cxd56_configure(FAR struct audio_lowerhalf_s *lower, } break; #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */ +#ifndef CONFIG_AUDIO_EXCLUDE_MUTE + case AUDIO_FU_MUTE: + { + /* Set mic mute/unmute status */ + + bool mute = (bool) caps->ac_controls.hw[0]; + audinfo(" Mute: %d\n", mute); + if (mute) + { + cxd56_set_volume(CXD56_AUDIO_VOLID_MIXER_OUT, + CXD56_VOL_MUTE); + cxd56_set_volume(CXD56_AUDIO_VOLID_MIXER_IN1, + CXD56_VOL_MUTE); + cxd56_set_volume(CXD56_AUDIO_VOLID_MIXER_IN2, + CXD56_VOL_MUTE); + } + else + { + cxd56_set_volume(CXD56_AUDIO_VOLID_MIXER_OUT, priv->volume); + cxd56_set_volume(CXD56_AUDIO_VOLID_MIXER_IN1, priv->volume); + cxd56_set_volume(CXD56_AUDIO_VOLID_MIXER_IN2, priv->volume); + } + + if (CXD56_AUDIO_ECODE_OK != ret) + { + return ret; + } + } + break; +#endif /* CONFIG_AUDIO_EXCLUDE_MUTE */ + case AUDIO_FU_INP_GAIN: + { + /* Set the mic gain */ + + priv->mic_gain = caps->ac_controls.hw[0]; + audinfo(" Mic gain: %d\n", priv->mic_gain); + + /* TODO: How to set individual mic gains? */ + } + break; default: auderr("ERROR: Unknown feature unit: %d\n", caps->ac_format.hw); @@ -1648,26 +2710,49 @@ static int cxd56_configure(FAR struct audio_lowerhalf_s *lower, priv->bitwidth = caps->ac_controls.b[2]; g_dev[priv->dma_handle] = priv; + poweron = 1; audinfo("Configured output using %d:\n", priv->dma_handle); audinfo(" Channels: %d\n", priv->channels); audinfo(" Samplerate: %d\n", priv->samplerate); audinfo(" Bit width: %d\n", priv->bitwidth); - - /* Get ready to start receiving buffers */ - - ret = cxd56_power_on(priv); - if (ret != OK) - { - auderr("ERROR: Power on error (%d)\n", ret); - return ret; - } - - cxd56_init_dma(priv); - - priv->state = CXD56_DEV_STATE_STOPPED; } break; + + case AUDIO_TYPE_INPUT: + { + /* Save the configuration */ + + priv->dma_handle = CXD56_AUDIO_DMA_MIC; + priv->samplerate = caps->ac_controls.hw[0]; + priv->channels = caps->ac_channels; + priv->bitwidth = caps->ac_controls.b[2]; + + g_dev[priv->dma_handle] = priv; + poweron = 1; + + audinfo("Configured input using %d:\n", priv->dma_handle); + audinfo(" Channels: %d\n", priv->channels); + audinfo(" Samplerate: %d\n", priv->samplerate); + audinfo(" Bit width: %d\n", priv->bitwidth); + } + break; + } + + if (poweron) + { + /* Get ready to start receiving buffers */ + + ret = cxd56_power_on(priv); + if (ret != OK) + { + auderr("ERROR: Power on error (%d)\n", ret); + return ret; + } + + cxd56_init_dma(priv); + + priv->state = CXD56_DEV_STATE_STOPPED; } return OK; @@ -1690,18 +2775,59 @@ static int cxd56_start(FAR struct audio_lowerhalf_s *lower) int ret; FAR struct cxd56_dev_s *priv = (FAR struct cxd56_dev_s *)lower; - /* Set audio path and enable output */ + /* Set audio path and enable analog input/output */ if (priv->dma_handle == CXD56_AUDIO_DMA_I2S0_DOWN) { write_reg(REG_AC_AU_DAT_SEL1, CXD56_AUDAT_SEL_BUSIF1); - } - ret = cxd56_power_on_analog_output(priv); - if (ret != OK) + ret = cxd56_power_on_analog_output(priv); + if (ret != OK) + { + auderr("ERROR: Power on analog output failed (%d)\n", ret); + return ret; + } + } + else if (priv->dma_handle == CXD56_AUDIO_DMA_MIC) { - auderr("ERROR: Power on analog output failed (%d)\n", ret); - return ret; +#if 0 + /* TODO: Check configuration. From audio_manager.cpp:221 */ + + sel_info.au_dat_sel1 = true; + if (AS_THROUGH_PATH_OUT_MIXER1 == out_path) + sel_info.cod_insel2 = true; + else if (AS_THROUGH_PATH_OUT_MIXER2 == out_path) + sel_info.cod_insel3 = true; + else if (AS_THROUGH_PATH_OUT_I2S1 == out_path) + sel_info.src1in_sel = true; + else + sel_info.src2in_sel = true; +#endif + write_reg(REG_AC_AU_DAT_SEL1, CXD56_AUDAT_SEL_MIC1); + write_reg(REG_AC_AU_DAT_SEL2, CXD56_AUDAT_SEL_MIC1); + + ret = cxd56_power_on_input(priv); + if (ret != OK) + { + auderr("ERROR: Power on analog input failed (%d)\n", ret); + return ret; + } + + if (priv->mic_boot_start != 0x0ull) + { + struct timespec end; + if (clock_gettime(CLOCK_REALTIME, &end) >= 0) + { + uint64_t time = (uint64_t)end.tv_sec * 1000 + + (uint64_t)end.tv_nsec / 1000000 - + priv->mic_boot_start; + + if (time < CXD56_MIC_BOOT_WAIT) + { + nxsig_usleep((CXD56_MIC_BOOT_WAIT - time) * 1000); + } + } + } } ret = cxd56_init_worker(lower); @@ -1731,7 +2857,30 @@ static int cxd56_stop_dma(FAR struct cxd56_dev_s *priv) cxd56_set_dma_running(priv->dma_handle, false); - if (priv->dma_handle == CXD56_AUDIO_DMA_I2S0_DOWN) + if (priv->dma_handle == CXD56_AUDIO_DMA_MIC) + { + /* Power off decimator */ + + write_reg(REG_AC_DECIM0_EN, 0); + + /* Power off CIC. */ + + write_reg(REG_AC_PDN_AMIC1, 1); + write_reg(REG_AC_PDN_AMIC2, 1); + write_reg(REG_AC_PDN_AMICEXT, 1); + write_reg(REG_AC_PDN_DMIC, 1); + + /* Disable input */ + + if (as_aca_control(CXD56_ACA_CTL_POWER_OFF_INPUT, + (uint32_t)NULL) != 0) + { + return -EBUSY; + } + + priv->mic_boot_start = 0x0ull; + } + else if (priv->dma_handle == CXD56_AUDIO_DMA_I2S0_DOWN) { /* Turn off amplifier */ @@ -1747,10 +2896,9 @@ static int cxd56_stop_dma(FAR struct cxd56_dev_s *priv) write_reg(REG_AC_NSPMUTE, 1); write_reg(REG_AC_PDN_SMSTR, 1); - as_aca_control(CXD56_ACA_CTL_POWER_OFF_OUTPUT, (uint32_t)NULL); - if (ret != CXD56_AUDIO_ECODE_OK) + if (as_aca_control(CXD56_ACA_CTL_POWER_OFF_OUTPUT, + (uint32_t)NULL) != 0) { - auderr("ERROR: Analog out power off failed (%d)\n", ret); return -EBUSY; } } @@ -1948,7 +3096,7 @@ static int cxd56_start_dma(FAR struct cxd56_dev_s *dev) while (dq_count(&dev->pendingq) > 0) { - if (read_reg(REG_I2S1_OUT_RTD_TRG) != CXD56_DMA_CMD_FIFO_NOT_FULL) + if (cxd56_dma_is_busy(dev->dma_handle)) { /* DMA busy, will retry next time */ @@ -1958,15 +3106,24 @@ static int cxd56_start_dma(FAR struct cxd56_dev_s *dev) apb = (struct ap_buffer_s *) dq_peek(&dev->pendingq); addr = ((uint32_t)apb->samp) & CXD56_DMA_START_ADDR_MASK; - size = (apb->nbytes / ((dev->bitwidth / 8) + dev->channels)) - 1; + size = (apb->nbytes / (dev->bitwidth / 8) / dev->channels) - 1; - if (dev->bitwidth == 16 && CXD56_DMA_FORMAT == CXD56_DMA_FORMAT_RL) + if (dev->dma_handle == CXD56_AUDIO_DMA_MIC) { - cxd56_swap_buffer_rl((uint32_t)apb->samp, apb->nbytes); + write_reg(REG_MIC_IN_START_ADR, addr); + write_reg(REG_MIC_IN_SAMPLE_NO, size); } + else + { + if (dev->bitwidth == 16 && + CXD56_DMA_FORMAT == CXD56_DMA_FORMAT_RL) + { + cxd56_swap_buffer_rl((uint32_t)apb->samp, apb->nbytes); + } - write_reg(REG_I2S1_OUT_START_ADR, addr); - write_reg(REG_I2S1_OUT_SAMPLE_NO, size); + write_reg(REG_I2S1_OUT_START_ADR, addr); + write_reg(REG_I2S1_OUT_SAMPLE_NO, size); + } /* Start DMA, use workaround with first buffer */ @@ -1997,7 +3154,7 @@ static int cxd56_start_dma(FAR struct cxd56_dev_s *dev) for (timeout = 0; timeout < CXD56_DMA_TIMEOUT; timeout++) { - if (read_reg(REG_I2S1_INT_CTRL_SMP)) + if (cxd56_int_has_smp(dev->dma_handle)) { break; } @@ -2015,7 +3172,7 @@ static int cxd56_start_dma(FAR struct cxd56_dev_s *dev) /* Start DMA */ - cxd56_set_dma_running(CXD56_AUDIO_DMA_I2S0_DOWN, true); + cxd56_set_dma_running(dev->dma_handle, true); /* Unlock interrupt */ @@ -2033,23 +3190,21 @@ static int cxd56_start_dma(FAR struct cxd56_dev_s *dev) up_udelay(CXD56_DMA_SMP_WAIT_NORMALT); } - /* Check whether an error interrupt has occurred */ + /* Check if an error interrupt has occurred */ - if (read_reg(REG_I2S1_INT_CTRL_ERR)) + if (cxd56_int_has_error(dev->dma_handle)) { - cxd56_set_dma_running(CXD56_AUDIO_DMA_I2S0_DOWN, - false); - cxd56_int_clear(dev->dma_handle, - CXD56_DMA_INT_ERR); + cxd56_set_dma_running(dev->dma_handle, false); + cxd56_int_clear(dev->dma_handle, CXD56_DMA_INT_ERR); for (timeout = 0; timeout < CXD56_DMA_TIMEOUT; timeout++) { if (CXD56_DMA_MSTATE_BUF_EMPTY == - cxd56_get_monbuf_state(dev->dma_handle)) + cxd56_get_mon_buf(dev->dma_handle)) { - if (read_reg(REG_I2S1_INT_CTRL_DONE)) + if (cxd56_int_is_done(dev->dma_handle)) { cxd56_int_clear(dev->dma_handle, CXD56_DMA_INT_DONE); @@ -2064,6 +3219,11 @@ static int cxd56_start_dma(FAR struct cxd56_dev_s *dev) } } + if (retry == CXD56_DMA_START_RETRY_CNT) + { + audinfo("Workaround retries maxed out\n"); + } + cxd56_int_unmask(dev->dma_handle, CXD56_DMA_INT_DONE); cxd56_int_clear(dev->dma_handle, CXD56_DMA_INT_ERR); cxd56_int_unmask(dev->dma_handle, CXD56_DMA_INT_ERR); @@ -2072,17 +3232,18 @@ static int cxd56_start_dma(FAR struct cxd56_dev_s *dev) { /* start DMA */ - cxd56_set_dma_running(CXD56_AUDIO_DMA_I2S0_DOWN, true); + cxd56_set_dma_running(dev->dma_handle, true); } - dq_pop(&dev->pendingq); - dq_push(&dev->runningq, &apb->dq_entry); + dq_get(&dev->pendingq); + dq_put(&dev->runningq, &apb->dq_entry); dev->state = CXD56_DEV_STATE_STARTED; } } exit: cxd56_give_sem(&dev->pendsem); + return ret; } @@ -2100,7 +3261,8 @@ static int cxd56_enqueuebuffer(FAR struct audio_lowerhalf_s *lower, struct audio_msg_s msg; cxd56_take_sem(&priv->pendsem); - dq_push(&priv->pendingq, &apb->dq_entry); + apb->dq_entry.flink = NULL; + dq_put(&priv->pendingq, &apb->dq_entry); cxd56_give_sem(&priv->pendsem); if (priv->mq != NULL) diff --git a/drivers/audio/cxd56.h b/drivers/audio/cxd56.h index 8987efe542b..c43e0c6461d 100644 --- a/drivers/audio/cxd56.h +++ b/drivers/audio/cxd56.h @@ -169,6 +169,43 @@ # define CXD56_SP_DRIVER 3 #endif +/* Mic bias voltage select */ + +#define CXD56_MIC_BIAS_20V 1 +#define CXD56_MIC_BIAS_28V 2 + +#if defined(CONFIG_CXD56_AUDIO_MICBIAS_20V) +# define CXD56_MIC_BIAS CXD56_MIC_BIAS_20V +#else +# define CXD56_MIC_BIAS CXD56_MIC_BIAS_28V +#endif + +/* Mic select */ + +#define CXD56_AUDIO_CFG_MIC CONFIG_CXD56_AUDIO_MIC_CHANNEL_SEL + +/* Mic boot wait time */ + +#if defined(CONFIG_CXD56_AUDIO_MIC_BOOT_WAIT) +#define CXD56_MIC_BOOT_WAIT CONFIG_CXD56_AUDIO_MIC_BOOT_WAIT +#else +#define CXD56_MIC_BOOT_WAIT 1100 +#endif + +/* CIC filter input path */ + +#define CXD56_AUDIO_CFG_CIC_IN_SEL_NONE 0 +#define CXD56_AUDIO_CFG_CIC_IN_SEL_CXD 1 +#define CXD56_AUDIO_CFG_CIC_IN_SEL_DMICIF 2 + +#if defined(CONFIG_CXD56_AUDIO_CIC_IN_SEL_CXD) +# define CXD56_AUDIO_CFG_CIC_IN CXD56_AUDIO_CFG_CIC_IN_SEL_CXD +#elif defined (CONFIG_CXD56_AUDIO_CIC_IN_SEL_DMIC) +# define CXD56_AUDIO_CFG_CIC_IN CXD56_AUDIO_CFG_CIC_IN_SEL_DMICIF +#else +# define CXD56_AUDIO_CFG_CIC_IN CXD56_AUDIO_CFG_CIC_IN_SEL_NONE +#endif + /* DMA format */ #define CXD56_DMA_FORMAT_LR 0 @@ -243,7 +280,8 @@ struct cxd56_dev_s #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */ uint8_t channels; /* Number of channels (1..8) */ - /* enum cxd56_audio_samp_fmt_e format; */ /* Output bits per sample (16 or 24) */ + uint16_t mic_gain; /* Mic gain */ + uint64_t mic_boot_start; /* Mic startup wait time */ uint8_t bitwidth; /* Bits per sample (16 or 24) */ bool muted; /* True: Output is muted */