// SPDX-License-Identifier: GPL-2.0+ #pragma GCC push_options #include #pragma GCC diagnostic ignored "-Wunused-function" #include #pragma GCC pop_options #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipq-adss.h" #include "ipq-avm.h" #include #include #define DBG_TDM_RESIZE_CHECK #if defined(CONFIG_AVM_FASTIRQ) #include #include #define avm_rte_local_irq_save(flags) firq_local_irq_save(flags) #define avm_rte_local_irq_restore(flags) firq_local_irq_restore(flags) #else #define avm_rte_local_irq_save(flags) local_irq_save(flags) #define avm_rte_local_irq_restore(flags) local_irq_restore(flags) #endif/*--- #if defined(CONFIG_AVM_FASTIRQ) ---*/ #include #include static void __iomem *avm_adss_audio_local_base; static void __iomem *adss_pcm_base; static struct reset_control *audio_blk_rst; static struct clk *pcm_clk; static void *DebugHandle; static void cmdlineparse(char *cmdline, void *dummy __attribute__((unused))); static unsigned int snprint_all_register(char **txt, unsigned int tx_size, unsigned int id_mask); static struct ipq_configs ipq4019_cfgs = { .txd_oe = { .reg = ADSS_GLB_AUDIO_MODE_REG, .mask = GLB_AUDIO_MODE_I2S0_TXD_OE, }, .rxd_oe = { .reg = ADSS_GLB_AUDIO_MODE_REG, .mask = GLB_AUDIO_MODE_I2S3_RXD_OE, }, .i2s0_fs_oe = { .reg = ADSS_GLB_AUDIO_MODE_REG, .mask = GLB_AUDIO_MODE_I2S0_FS_OE, }, .i2s3_fs_oe = { .reg = ADSS_GLB_AUDIO_MODE_REG, .mask = GLB_AUDIO_MODE_I2S3_FS_OE, }, .i2s_reset_val = { .reg = ADSS_GLB_I2S_RST_REG, .mask = GLB_I2S_RESET_VAL_4019, }, .spdif_enable = 1, }; static struct ipq_configs ipq8074_cfgs = { .txd_oe = { .reg = ADSS_GLB_CLK_I2S_CTRL_REG, .mask = GLB_CLK_I2S_CTRL_I2S0_TXD_OE, }, .rxd_oe = { .reg = ADSS_GLB_CLK_I2S_CTRL_REG, .mask = GLB_CLK_I2S_CTRL_I2S3_RXD_OE, }, .i2s0_fs_oe = { .reg = ADSS_GLB_CLK_I2S_CTRL_REG, .mask = GLB_CLK_I2S_CTRL_I2S0_FS_OE, }, .i2s3_fs_oe = { .reg = ADSS_GLB_CLK_I2S_CTRL_REG, .mask = GLB_CLK_I2S_CTRL_I2S3_FS_OE, }, .i2s_reset_val = { .reg = ADSS_GLB_I2S_RST_REG, .mask = GLB_I2S_RESET_VAL_8074, }, .spdif_enable = 0, }; static struct ipq_configs *ipq_cfgs; #define MBOX_PHYS_DMA_ADDRESS(pmbox, a) (((dma_addr_t)(a)) & pmbox->mbox_mask) /*--- #define DBG_REGISTER ---*/ /*--- #define DBG_IRQ_MEASURE ---*/ /*--- #define DBG_TRC_API(args...) pr_info(args) ---*/ #define DBG_TRC_API(args...) no_printk(args) /*--- #define DBG_TRC(args...) pr_info(args) ---*/ #define DBG_TRC(args...) no_printk(args) /*--- #define DBG_TRC_DESC(args...) pr_info(args) ---*/ #define DBG_TRC_DESC(args...) no_printk(args) #if defined(DBG_REGISTER) static void trace_reg(const char *basename, const char *regname, int id, unsigned int offset, int read_flag, unsigned int towrite, unsigned int read_val); #else /*--- #if defined(DBG_REGISTER) ---*/ #define trace_reg(basename, regname, id, offset, read_flag, towrite, read_val) #endif struct _mbox_desc { unsigned int length : 12, /* bit 11-00 */ size : 12, /* bit 23-12 */ vuc : 1, /* bit 24 */ ei : 1, /* bit 25 */ rsvd1 : 4, /* bit 29-26 */ EOM : 1, /* bit 30 */ OWN : 1; /* bit 31 */ unsigned int BufPtr; unsigned int NextPtr; unsigned int vuc_dword[36]; }; struct _mbox_dma { /*--- dma-alloc-stuff ---*/ void *dma_pool; dma_addr_t phys_desc; dma_addr_t phys_buf[2]; /* Desc array in virtual space */ struct _mbox_desc *virt_desc; unsigned int desc_size; unsigned int mbox_mask; unsigned int ndescs; unsigned char *virt_buf[2]; struct device *dev; unsigned long status; uint32_t err_stats; unsigned int irq; unsigned int fiq_cpumode; unsigned int size; unsigned int slots; enum {irq_uninitialized = 0, irq_initialized, irq_progress} irq_active; unsigned int err_cnt; unsigned long irq_cnt; unsigned long bi_irq_cnt; unsigned int own_udr; unsigned int own_ovr; int dir; uint32_t dma_id; struct _mbox_dma *irqcontext_mbox; /*--- behandle diese mbox im irq-context mit ---*/ /*--- Schnittstelle nach oben: ---*/ void *refhandle; /*--- Rx: empfangenen Buffer abliefern - Tx: diesen Buffer fuellen, ret: 0 ok sonst DMA-Size reduzieren ---*/ int (*RxTxData)(void *refhandle, void *p); unsigned int reoffset; unsigned int force_resize; #if defined(DBG_TDM_RESIZE_CHECK) unsigned int dbg_force_reoffset; #endif/*--- #if defined(DBG_TDM_RESIZE_CHECK) ---*/ unsigned int max_retry; unsigned long max_ts_diff; } mbox_dma[2]; static struct _mbox_data { void __iomem *reg_base; unsigned int irq; } mbox_data[5]; static struct dai_priv_st { int stereo_tx; int stereo_rx; int mbox_tx; int mbox_rx; int tx_enabled; int rx_enabled; struct platform_device *pdev; struct clk *audio_tx_bclk; struct clk *audio_tx_mclk; struct clk *audio_rx_bclk; struct clk *audio_rx_mclk; unsigned int clk_set; } tdm_priv; static void __iomem *stereo_base[MAX_STEREO_ENTRIES]; enum _trace_mode { trace_off = 0, trace_pcm = (1 << 0), trace_tdm = (1 << 1), trace_audio = (1 << 2), trace_stereo = (1 << 3), trace_mbox = (1 << 4), trace_glb = (1 << 5), trace_ipq4019 = (1 << 30), /* dem 4019 (dakota) vorbehalten */ trace_enh = (1 << 31), /*--- include bit-data ---*/ }; static enum _trace_mode trace_mode = trace_enh | /*--- trace_pcm | ---*/ trace_glb | trace_tdm | trace_audio | trace_stereo | trace_mbox | 0; static unsigned int snprintf_dump_descriptors(char **_ptxt, unsigned int txt_size, struct _mbox_dma *pmbox_dma); static unsigned int snprintf_dump_slots(char **_ptxt, unsigned int txt_size, struct _mbox_dma *pmbox_dma); static void adss_glb_audio_mode_B1K(void); static void adss_glb_i2s_interface_en(int enable); static void adss_stereo_config_reset(struct _mbox_dma *pmbox_dma, uint32_t reset); static void adss_stereo_config_enable(struct _mbox_dma *pmbox_dma, uint32_t enable); static void mbox_dma_exit(struct _mbox_dma *pmbox_dma); /** * ptxt == NULL: use printk */ #define snprintf_add(ptxt, txtlen, args...) \ do { \ if (ptxt) { \ int local_add_len = snprintf(ptxt, txtlen, args); \ if (local_add_len > 0) { \ int tail = min_t(int, txtlen, local_add_len); \ (ptxt) += tail, (txtlen) -= tail; \ } \ } else \ pr_err(args); \ } while (0) #define ADSS_AUDIO_PCM_REGS(off) (volatile unsigned int *)(adss_pcm_base + off) #define ADSS_AUDIO_GLB_REGS(off) (volatile unsigned int *)(avm_adss_audio_local_base + off) #define ADSS_AUDIO_MBOX_REGS(idx, off) (volatile unsigned int *)(mbox_data[idx].reg_base + off) #define ADSS_AUDIO_STEREO_REGS(idx, off) (volatile unsigned int *)(stereo_base[idx] + off) /** * Hawkeye */ int is_IPQ807x(void) { return ipq_cfgs == &ipq8074_cfgs; } /* * Dakota */ int is_IPQ4019(void) { return ipq_cfgs == &ipq4019_cfgs; } /** */ static void _mbox_write(int dma_id, unsigned int val, unsigned int off, const char *regname __maybe_unused) { if ((dma_id >= ARRAY_SIZE(mbox_data)) || (mbox_data[dma_id].reg_base == NULL)) { return; } writel(val, ADSS_AUDIO_MBOX_REGS(dma_id, off)); trace_reg("MBOX", regname, dma_id, off, 0, val, readl(ADSS_AUDIO_MBOX_REGS(dma_id, off))); } /** */ static unsigned int _mbox_read(int dma_id, unsigned int off, const char *regname __maybe_unused) { unsigned int val; if ((dma_id >= ARRAY_SIZE(mbox_data)) || (mbox_data[dma_id].reg_base == NULL)) { return (unsigned int)-1; } val = readl(ADSS_AUDIO_MBOX_REGS(dma_id, off)); trace_reg("MBOX", regname, dma_id, off, 1, 0, val); return val; } /** */ static void _adss_pcm_write(unsigned int val, unsigned int off, const char *regname __maybe_unused) { writel(val, ADSS_AUDIO_PCM_REGS(off)); trace_reg("ADSS_PCM_BASE", regname, -1, off, 0, val, readl(ADSS_AUDIO_PCM_REGS(off))); } /** */ static unsigned int _adss_pcm_read(unsigned int off, const char *regname __maybe_unused) { unsigned int val; val = readl(ADSS_AUDIO_PCM_REGS(off)); trace_reg("ADSS_PCM_BASE", regname, -1, off, 1, 0, val); return val; } /** */ void _adss_audio_write(unsigned int val, unsigned int off, const char *regname __maybe_unused) { writel(val, ADSS_AUDIO_GLB_REGS(off)); trace_reg("ADSS_AUDIO_GLB_BASE", regname, -1, off, 0, val, readl(ADSS_AUDIO_GLB_REGS(off))); } EXPORT_SYMBOL(_adss_audio_write); /** */ unsigned int _adss_audio_read(unsigned int off, const char *regname __maybe_unused) { unsigned int val; val = readl(ADSS_AUDIO_GLB_REGS(off)); trace_reg("ADSS_AUDIO_GLB_REGS", regname, -1, off, 1, 0, val); return val; } EXPORT_SYMBOL(_adss_audio_read); /** */ static void _adss_stereo_write(int stereo_id, unsigned int val, unsigned int off, const char *regname __maybe_unused) { if ((stereo_id >= ARRAY_SIZE(stereo_base)) || (stereo_base[stereo_id] == NULL)) { return; } writel(val, ADSS_AUDIO_STEREO_REGS(stereo_id, off)); trace_reg("STEREO", regname, stereo_id, off, 0, val, readl(ADSS_AUDIO_STEREO_REGS(stereo_id, off))); } /** */ static unsigned int _adss_stereo_read(int stereo_id, unsigned int off, const char *regname __maybe_unused) { unsigned int val; if ((stereo_id >= ARRAY_SIZE(stereo_base)) || (stereo_base[stereo_id] == NULL)) { return (unsigned int)-1; } val = readl(ADSS_AUDIO_STEREO_REGS(stereo_id, off)); trace_reg("STEREO", regname, stereo_id, off, 1, 0, val); return val; } #define PCM_IO(off, flag, format) { trace_flag:flag, regname:#off, offset:off, write:_adss_pcm_write, read:_adss_pcm_read, bitformat:format } #define GLB_IO(off, flag, format) { trace_flag:flag, regname:#off, offset:off, write:_adss_audio_write, read:_adss_audio_read, bitformat:format } #define STEREO_IO(off, flag, format) { trace_flag:flag, regname:#off, offset:off, stereo_write:_adss_stereo_write, stereo_read:_adss_stereo_read, bitformat:format } #define MBOX_IO(off, flag, format) { trace_flag:flag, regname:#off, offset:off, _mbox_write:_mbox_write, _mbox_read:_mbox_read, bitformat:format } /** * wenn trace_flag mit trace_ipq4019 verodert: nur bei Dakota auslesen * anhand des Prefixes "8074_" bzw. "4019_" werden die Bitfelder entsprechend anders ausgewertet */ static const struct _debug_register_tab { enum _trace_mode trace_flag; const char *regname; unsigned int offset; void (*write)(unsigned int val, unsigned int off, const char *regname); unsigned int (*read)(unsigned int off, const char *regname); void (*_mbox_write)(int dma_id, unsigned int val, unsigned int off, const char *regname); unsigned int (*_mbox_read)(int dma_id, unsigned int off, const char *regname); void (*stereo_write)(int stereo_id, unsigned int val, unsigned int off, const char *regname); unsigned int (*stereo_read)(int stereo_id, unsigned int off, const char *regname); const char *bitformat; } gReg[] = { PCM_IO(AADSS_PCM_BITMAP_REG, trace_pcm, NULL), PCM_IO(AADSS_PCM_CTRL_REG, trace_pcm, NULL), PCM_IO(AADSS_PCM_OFFSET_REG, trace_pcm, NULL), PCM_IO(AADSS_PCM_START_REG, trace_pcm, NULL), PCM_IO(AADSS_PCM_INT_STATUS_REG, trace_pcm, NULL), PCM_IO(AADSS_PCM_INT_ENABLE_REG, trace_pcm, NULL), PCM_IO(AADSS_PCM_DIVIDER_REG, trace_pcm, NULL), PCM_IO(AADSS_PCM_TH_REG, trace_pcm, NULL), PCM_IO(AADSS_PCM_FIFO_CNT_REG, trace_pcm, NULL), PCM_IO(AADSS_PCM_FIFO_ERR_SLOT_REG, trace_pcm, NULL), PCM_IO(AADSS_PCM_RX_DATA_8BIT_REG, 0, NULL), PCM_IO(AADSS_PCM_TX_DATA_8BIT_REG, 0, NULL), PCM_IO(AADSS_PCM_RX_DATA_16BIT_REG, 0, NULL), PCM_IO(AADSS_PCM_TX_DATA_16BIT_REG, 0, NULL), GLB_IO(ADSS_GLB_PCM_RST_REG, trace_glb, NULL), GLB_IO(ADSS_GLB_PCM_MBOX_CTRL_REG, trace_glb, NULL), GLB_IO(ADSS_GLB_CHIP_CTRL_I2S_REG, trace_glb, "I2S_STEREO2_GLB_EN(3)I2S_STEREO1_GLB_EN(2)I2S_STEREO0_GLB_EN(1)I2S_INTERFACE_EN(0)"), GLB_IO(ADSS_GLB_I2S_RST_REG, trace_glb, "8074_RXBCLK(7)8074_RXMCLK(6)8074_TXBCLK(5)8074_TXMCLK(4)I2S3(3)MBOX3(2)I2S0(1)MBOX0(0)"), GLB_IO(ADSS_GLB_CLK_I2S_CTRL_REG, trace_glb, "TX_BCLK_OE(28)RX_BCLK_OE(27)TX_MCLK_OE(17)RX_MCLK_OE(16)8074_I2S3_FS_OE(15)8074_I2S0_FS_OE(14)8074_I2S3_D_OE(13)8074_I2S0_D_OE(12)"), GLB_IO(ADSS_GLB_TDM_CTRL_REG, trace_glb, "RX_DELAY(26)TX_DELAY(25)RX_SYNC_NUM(20,24)RX_CHAN_NUM(16,19)TX_SYNC_NUM(4,8)TX_CHAN_NUM(0,3)"), GLB_IO(ADSS_GLB_PWM0_CTRL_REG, trace_glb, NULL), GLB_IO(ADSS_GLB_PWM1_CTRL_REG, trace_glb, NULL), GLB_IO(ADSS_GLB_PWM2_CTRL_REG, trace_glb, NULL), GLB_IO(ADSS_GLB_PWM3_CTRL_REG, trace_glb, NULL), GLB_IO(ADSS_GLB_AUDIO_MODE_REG, trace_glb, "B1K(28)8074_I2S3_WR_SWAP(23)8074_I2S3_RD_SWAP(22)8074_I2S0_WR_SWAP(17)8074_I2S0_RD_SWAP(16)4019_SPDIF_OUT_OE(10)4019_I2S3_RXD_OE(9)4019_I2S3_FS_OE(8)4019_I2S0_FS_OE(7)4019_I2S0_TXD_OE(4)RECV_TDM(2)RECV_I2S(~2)XMIT_TDM(0)XMIT_I2S(~0)"), GLB_IO(ADSS_GLB_AUDIO_MODE2_REG, trace_glb, "8074_LOOPBACK_EN(30)8074_RD_ADDR_MAP(2,3)8074_WR_ADDR_MAP(0,1)"), GLB_IO(ADSS_AUDIO_PLL_CONFIG_REG, trace_audio | trace_ipq4019, "POSTPLLDIV(7,9)PLL_PWD(5)REFDIV(0,2)"), GLB_IO(ADSS_AUDIO_PLL_MODULATION_REG, trace_audio | trace_ipq4019, "INT_DIV(1,10)FRAC_DIV(11,29)"), GLB_IO(ADSS_AUDIO_PLL_MOD_STEP_REG, trace_audio | trace_ipq4019, NULL), GLB_IO(ADSS_CURRENT_AUDIO_PLL_MODULATION_REG, trace_audio | trace_ipq4019, NULL), GLB_IO(ADSS_AUDIO_PLL_CONFIG1_REG, trace_audio | trace_ipq4019, NULL), GLB_IO(ADSS_AUDIO_ATB_SETTING_REG, trace_audio, "8074_ATB_ID(8,14)8074_ATB_SEL(0,7)"), GLB_IO(ADSS_AUDIO_RXB_CFG_MUXR_REG, trace_audio, "MUXR_SRC_SEL(8,16)"), GLB_IO(ADSS_AUDIO_RXB_MISC_REG, trace_audio, "B_MDIV(1,31)"), GLB_IO(ADSS_AUDIO_RXB_CBCR_REG, trace_audio, "8074_CLK_OFF(31)ENABLE(0)"), GLB_IO(ADSS_AUDIO_RXM_CMD_RCGR_REG, trace_audio, "8074_ROOT_OFF(31)8074_DIRTY_CFG(4)ROOT_EN(1)UPDATE(0)"), GLB_IO(ADSS_AUDIO_RXM_CFG_RCGR_REG, trace_audio, "M_RDIV(0,7)SRC_SEL(8,10)8074_SRC_DIV(0,4)"), GLB_IO(ADSS_AUDIO_RXM_MISC_REG, trace_audio, "4019_M_MDIV(4,31)AUTO_SCALE_DIV(0,8)"), GLB_IO(ADSS_AUDIO_RXM_CBCR_REG, trace_audio, "8074_CLK_OFF(31)ENABLE(0)"), GLB_IO(ADSS_AUDIO_TXB_CFG_MUXR_REG, trace_audio, "MUXR_SRC_SEL(8,16)"), GLB_IO(ADSS_AUDIO_TXB_MISC_REG, trace_audio, "B_MDIV(1,31)"), GLB_IO(ADSS_AUDIO_TXB_CBCR_REG, trace_audio, "8074_CLK_OFF(31)ENABLE(0)"), GLB_IO(ADSS_AUDIO_TXM_CMD_RCGR_REG, trace_audio, "8074_ROOT_OFF(31)8074_DIRTY_CFG(4)ROOT_EN(1)UPDATE(0)"), GLB_IO(ADSS_AUDIO_TXM_CFG_RCGR_REG, trace_audio, "M_RDIV(0,7)SRC_SEL(8,10)"), GLB_IO(ADSS_AUDIO_TXM_MISC_REG, trace_audio, "M_MDIV(4,31)"), GLB_IO(ADSS_AUDIO_TXM_CBCR_REG, trace_audio, "8074_CLK_OFF(31)ENABLE(0)"), GLB_IO(ADSS_AUDIO_SAMPLE_CBCR_REG, trace_audio | trace_ipq4019, "ENABLE(0)"), STEREO_IO(ADSS_STEREOn_STEREO0_CONFIG_REG, trace_stereo, "MIC_SWAP(24)SPDIF_ENA(23)ENA(21)MIC_RESET(19)RESET(20)I2S_DELAY(~18)PCM_SWAP(17)MIC_W32(16)MIC_W16(~16)MONO(14)STEREO(~14)WS(12,13)I2S_W32(11)I2S_W16(~11)MCK_SEL(10)CLR_CNT(9)MASTER(8)"), /*--- STEREO_IO(ADSS_STEREOn_STEREO0_VOLUME_REG, trace_stereo, "CHAN1(8,12)CHAN0(0,4))"), ---*/ STEREO_IO(ADSS_STEREOn_STEREO0_MASTER_CLOCK_REG, trace_stereo, NULL), /*--- STEREO_IO(ADSS_STEREOn_STEREO0_TX_SAMPLE_CNT_LSB_REG, trace_stereo, NULL), ---*/ /*--- STEREO_IO(ADSS_STEREOn_STEREO0_TX_SAMPLE_CNT_MSB_REG, trace_stereo, NULL), ---*/ /*--- STEREO_IO(ADSS_STEREOn_STEREO0_RX_SAMPLE_CNT_LSB_REG, trace_stereo, NULL), ---*/ /*--- STEREO_IO(ADSS_STEREOn_STEREO0_RX_SAMPLE_CNT_MSB_REG, trace_stereo, NULL), ---*/ STEREO_IO(ADSS_STEREOn_STEREO0_UNDERRUN_CNT_REG, trace_stereo, NULL), STEREO_IO(ADSS_STEREOn_STEREO0_OVERRUN_CNT_REG, trace_stereo, NULL), STEREO_IO(ADSS_STEREOn_STEREO0_TX_COMPLETE_CNT_REG, trace_stereo, NULL), STEREO_IO(ADSS_STEREOn_STEREO0_RX_COMPLETE_CNT_REG, trace_stereo, NULL), MBOX_IO(ADSS_MBOXn_MBOX_FIFO0_REG, trace_mbox, NULL), MBOX_IO(ADSS_MBOXn_MBOX_FIFO_STATUS0_REG, trace_mbox, NULL), MBOX_IO(ADSS_MBOXn_MBOX_DMA_POLICY_REG, trace_mbox, "SW_RESET(31)TX_INT_TYPE(17)RX_INT_TYPE(16)RXD_16BIT_SWAP(10)RXD_END_SWAP(8)SRAM_AC(12,15)TX_FIFO_THRESHOLD(4,7)"), MBOX_IO(ADSS_MBOXn_MBOXn_DMA_RX_DESCRIPTOR_BASE_REG, trace_mbox, NULL), MBOX_IO(ADSS_MBOXn_MBOXn_DMA_RX_CONTROL_REG, trace_mbox, "RESUME(2)START(1)STOP(0)"), MBOX_IO(ADSS_MBOXn_MBOXn_DMA_TX_DESCRIPTOR_BASE_REG, trace_mbox, NULL), MBOX_IO(ADSS_MBOXn_MBOXn_DMA_TX_CONTROL_REG, trace_mbox, "RESUME(2)START(1)STOP(0)"), MBOX_IO(ADSS_MBOXn_MBOX_FRAME_REG, trace_mbox, NULL), MBOX_IO(ADSS_MBOXn_FIFO_TIMEOUT_REG, trace_mbox, NULL), MBOX_IO(ADSS_MBOXn_MBOX_INT_STATUS_REG, trace_mbox, "8074_ERROR_RESP(15)8074_BI_TX_RX(14)TX_FIFO_OVERFLOW(13)RX_FIFO_UNDERFLOW(12)8074_OUT_OF_FSYNC(11)RX_DMA_COMPLETE(10)8074_DMA_EOM_COMPLETE(8)TX_DMA_COMPLETE(6)TX_OVERFLOW(5)RX_UNDERFLOW(4)8074_TX_NOT_EMPTY(2)8074_RX_NOT_FULL(0)"), MBOX_IO(ADSS_MBOXn_MBOX_INT_ENABLE_REG, trace_mbox, "8074_ERROR_RESP(15)8074_BI_TX_RX(14)TX_FIFO_OVERFLOW(13)RX_FIFO_UNDERFLOW(12)8074_OUT_OF_FSYNC(11)RX_DMA_COMPLETE(10)8074_DMA_EOM_COMPLETE(8)TX_DMA_COMPLETE(6)TX_OVERFLOW(5)RX_UNDERFLOW(4)8074_TX_NOT_EMPTY(2)8074_RX_NOT_FULL(0)"), MBOX_IO(ADSS_MBOXn_MBOX_FIFO_RESET_REG, 0, NULL), }; #if defined(DBG_REGISTER) #define DBG_TRC_REG(args...) pr_info(args) /** */ static const struct _debug_register_tab *find_regname(const char *regname) { unsigned int i; for (i = 0; i < ARRAY_SIZE(gReg); i++) { const struct _debug_register_tab *preg = &gReg[i]; if (strcmp(regname, preg->regname) == 0) { return preg; } } return NULL; } /** */ static void trace_reg(const char *basename, const char *regname, int id, unsigned int offset, int read_flag, unsigned int towrite, unsigned int read_val) { if (regname == NULL || (trace_mode == 0)) { return; } if ((trace_mode & trace_enh) == 0) { if (read_flag) { pr_err("%s *(%s%c+0x%x) = 0x%x\n", regname, basename, id < 0 ? ' ' : id + '0', offset, read_val); } else { pr_err("%s *(%s%c+0x%x) = 0x%x => 0x%x\n", regname, basename, id < 0 ? ' ' : id + '0', offset, towrite, read_val); } } else { const struct _debug_register_tab *preg; char txt1[196], txt2[196]; preg = find_regname(regname); if (read_flag) { snprint_register_bitformat(txt1, sizeof(txt1), read_val, preg ? preg->bitformat : NULL); pr_err("%s:(%s%c+0x%02x)%s\n", regname, basename, id < 0 ? ' ' : id + '0', offset, txt1); } else { snprint_register_bitformat(txt1, sizeof(txt1), towrite, preg ? preg->bitformat : NULL); snprint_register_bitformat(txt2, sizeof(txt2), read_val, preg ? preg->bitformat : NULL); pr_err("%s:(%s%c+0x%02x)%s -> %s\n", regname, basename, id < 0 ? ' ' : id + '0', offset, txt1, txt2); } } } #endif/*--- #if defined(DBG_REGISTER) ---*/ #define mbox_read(dma_id, off) _mbox_read(dma_id, off, #off) #define mbox_write(dma_id, val, off) _mbox_write(dma_id, val, off, #off) #define adss_pcm_read(off) _adss_pcm_read(off, #off) #define adss_pcm_write(val, off) _adss_pcm_write(val, off, #off) #define adss_audio_read(off) _adss_audio_read(off, #off) #define adss_audio_write(val, off) _adss_audio_write(val, off, #off) #define adss_audio_read(off) _adss_audio_read(off, #off) #define adss_stereo_read(stereo_id, off) _adss_stereo_read(stereo_id, off, #off) #define adss_stereo_write(stereo_id, val, off) _adss_stereo_write(stereo_id, val, off, #off) #define CHN_STATUS_DISABLE 0xFF /** */ enum _interface_id { intf_audio_adss_ipq4019, intf_audio_adss_ipq8074, intf_pcm, intf_mbox, intf_tdm, intf_stereo, intf_tdm_pins, }; /** */ static const struct of_device_id pcm_avm_id_table[] = { { .compatible = "qca,ipq4019-audio-adss", .data = (void *)intf_audio_adss_ipq4019}, { .compatible = "qca,ipq4019-pcm", .data = (void *)intf_pcm }, { .compatible = "qca,ipq4019-mbox", .data = (void *)intf_mbox }, { .compatible = "qca,ipq4019-tdm", .data = (void *)intf_tdm }, { .compatible = "qca,ipq4019-stereo", .data = (void *)intf_stereo }, { .compatible = "qca,ipq8074-audio-adss", .data = (void *)intf_audio_adss_ipq8074}, { .compatible = "qca,ipq8074-pcm", .data = (void *)intf_pcm }, { .compatible = "qca,ipq8074-mbox", .data = (void *)intf_mbox }, { .compatible = "qca,ipq8074-tdm", .data = (void *)intf_tdm }, { .compatible = "qca,ipq8074-stereo", .data = (void *)intf_stereo }, { .compatible = "qca,ipq8074-audio", .data = (void *)intf_tdm_pins }, {}, }; MODULE_DEVICE_TABLE(of, pcm_avm_id_table); /** */ static void gcc_audio_blk_rst(void) { DBG_TRC("%s\n", __func__); if (!audio_blk_rst) { return; } reset_control_assert(audio_blk_rst); usleep_range(20000, 24000); reset_control_deassert(audio_blk_rst); } /** */ static int intf_audio_adss_probe(struct platform_device *pdev) { struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); avm_adss_audio_local_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(avm_adss_audio_local_base)) { pr_err("%s: error adss_audio_local_base\n", __func__); return PTR_ERR(avm_adss_audio_local_base); } audio_blk_rst = devm_reset_control_get(&pdev->dev, "blk_rst"); if (IS_ERR(audio_blk_rst)) { pr_err("%s: error audio_blk_rst\n", __func__); return PTR_ERR(audio_blk_rst); } gcc_audio_blk_rst(); adss_glb_i2s_interface_en(ENABLE); adss_glb_audio_mode_B1K(); DBG_TRC("%s: adss_audio_local_base=%p(%x-%x)\n", __func__, avm_adss_audio_local_base, res->start, res->end); return 0; } /** */ static int intf_tdm_pins_probe(struct platform_device *pdev, int slave) { struct dev_pin_info *pins; struct pinctrl_state *pin_state; pins = pdev->dev.pins; pr_err("%s: %p\n", __func__, pins); if (slave) pin_state = pinctrl_lookup_state(pins->p, "audio_slave"); else pin_state = pinctrl_lookup_state(pins->p, "audio"); if (IS_ERR(pin_state)) { pr_err("audio pinctrl state not available\n"); return PTR_ERR(pin_state); } pinctrl_select_state(pins->p, pin_state); return 0; } /** */ static int intf_audio_adss_remove(struct platform_device *pdev) { DBG_TRC("%s:\n", __func__); adss_glb_i2s_interface_en(DISABLE); avm_adss_audio_local_base = NULL; audio_blk_rst = NULL; return 0; } /** */ static int intf_pcm_driver_probe(struct platform_device *pdev) { uint32_t tx_channel; uint32_t rx_channel; struct device_node *np = NULL; struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); adss_pcm_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(adss_pcm_base)) { pr_err("%s: error adss_pcm_base\n", __func__); return PTR_ERR(adss_pcm_base); } np = of_node_get(pdev->dev.of_node); if (of_property_read_u32(np, "dma-tx-channel", &tx_channel)) { pr_err("%s: error reading critical device node properties\n", np->name); return -EINVAL; } if (of_property_read_u32(np, "dma-rx-channel", &rx_channel)) { pr_err("%s: error reading critical device node properties\n", np->name); return -EINVAL; } pcm_clk = devm_clk_get(&pdev->dev, "audio_pcm_clk"); if (IS_ERR(pcm_clk)) { pr_err("%s: Error in getting PCM Clk\n", __func__); return PTR_ERR(pcm_clk); } DBG_TRC("%s: adss_pcm_base=%p(%x-%x) tx_channel=%u rx_channel=%u\n", __func__, adss_pcm_base, res->start, res->end, tx_channel, rx_channel); return 0; } /** */ static int intf_pcm_driver_remove(struct platform_device *pdev) { DBG_TRC("%s:\n", __func__); devm_clk_put(&pdev->dev, pcm_clk); return 0; } /** */ static uint32_t mbox_get_desc_size(void) { struct _mbox_desc *desc; uint32_t ipq_desc_size; if (is_IPQ4019()) ipq_desc_size = sizeof(desc[0]); else ipq_desc_size = sizeof(desc[0]) - sizeof(desc[0].vuc_dword); return L1_CACHE_ALIGN(ipq_desc_size); } /** */ struct _mbox_desc *ipq_desc_ptr(struct _mbox_dma *pmbox_dma, void *desc_ptr, int no_of_desc) { return (struct _mbox_desc *)((uint8_t *)desc_ptr + (pmbox_dma->desc_size * no_of_desc)); } /** */ static uint32_t mbox_get_mask(void) { if (is_IPQ4019()) return DMA_BIT_MASK(28); else return DMA_BIT_MASK(32); } /** */ static void mbox_interrupt_ack(int dma_id, unsigned int status, unsigned int mask) { if (is_IPQ807x()) { status &= mask; } else { status &= ~mask; } _mbox_write(dma_id, status, ADSS_MBOXn_MBOX_INT_STATUS_REG, NULL); } #if defined(DBG_IRQ_MEASURE) /** */ static inline int mbox_count_ownzero(struct _mbox_dma *pmbox_dma) { struct _mbox_desc *desc; unsigned int i; int ownzero = 0; desc = pmbox_dma->virt_desc; for (i = 0; i < pmbox_dma->ndescs; i++) { dmac_inv_range(desc, ((unsigned char *)desc) + mbox_get_desc_size() - 1); if (desc->OWN == 0) ownzero++; desc = ipq_desc_ptr(pmbox_dma, desc, 1); } return ownzero; } static void mbox_check_completion_time(void) { unsigned int i, rx_i = 0, tx_i = 0, rx_status, tx_status; struct _priv_ts { unsigned long ts_rx; unsigned long ts_tx; unsigned int own_rxzero; unsigned int own_txzero; } ts[4] = {0}; unsigned long ts_start = avm_get_cycles(); while ((rx_i < ARRAY_SIZE(ts)) || (tx_i < ARRAY_SIZE(ts))) { unsigned long act_ts = avm_get_cycles(); rx_status = _mbox_read(mbox_dma[CAPTURE].dma_id, ADSS_MBOXn_MBOX_INT_STATUS_REG, NULL); if (rx_status & MBOX_INT_STATUS_TX_DMA_COMPLETE) { mbox_interrupt_ack(mbox_dma[CAPTURE].dma_id, rx_status, rx_status); if (rx_i < ARRAY_SIZE(ts)) { ts[rx_i].own_rxzero = mbox_count_ownzero(&mbox_dma[CAPTURE]); ts[rx_i].ts_rx = act_ts; rx_i++; } } tx_status = _mbox_read(mbox_dma[PLAYBACK].dma_id, ADSS_MBOXn_MBOX_INT_STATUS_REG, NULL); if (tx_status & MBOX_INT_STATUS_RX_DMA_COMPLETE) { mbox_interrupt_ack(mbox_dma[PLAYBACK].dma_id, tx_status, tx_status); if (tx_i < ARRAY_SIZE(ts)) { ts[rx_i].own_txzero = mbox_count_ownzero(&mbox_dma[PLAYBACK]); ts[tx_i].ts_tx = act_ts; tx_i++; } } } for (i = 0; i < ARRAY_SIZE(ts); i++) { unsigned long dt_rx, dt_tx; dt_tx = ts[i].ts_tx - ((i == 0) ? ts_start : ts[i-1].ts_tx); dt_rx = ts[i].ts_rx - ((i == 0) ? ts_start : ts[i-1].ts_rx); pr_err("%s: [TX-TS:%06lu] dt-tx %lu (%lu us) ownzero=%d [RX-TS:%06lu] dt-rx %lu (%lu us) ownzero=%d rx-tx-diff= %ld (%ld us)\n", __func__, ts[i].ts_tx - ts_start, dt_tx, avm_cycles_to_usec(dt_tx), ts[i].own_txzero, ts[i].ts_rx - ts_start, dt_rx, avm_cycles_to_usec(dt_rx), ts[i].own_rxzero, (signed long)(ts[i].ts_rx - ts[i].ts_tx), avm_cycles_to_usec((ts[i].ts_rx - ts[i].ts_tx))); } } #endif/*--- #if defined(DBG_IRQ_MEASURE) ---*/ /** * wird initial zum synchronisieren benötigt */ static void mbox_wait_for_dma_completion(struct _mbox_dma *pmbox_dma) { unsigned int mask, status; mask = (pmbox_dma->dir == PLAYBACK) ? MBOX_INT_STATUS_RX_DMA_COMPLETE : MBOX_INT_STATUS_TX_DMA_COMPLETE; do { status = _mbox_read(pmbox_dma->dma_id, ADSS_MBOXn_MBOX_INT_STATUS_REG, NULL); } while ((status & mask) == 0); mbox_interrupt_ack(pmbox_dma->dma_id, status, status); } /** * call the installed rx- and tx-callbacks * also manage cache-validation */ static inline void mbox_ack_desc(struct _mbox_dma *pmbox_dma) { unsigned int i, own_cnt = 0, retry = 0; struct _mbox_desc *desc; unsigned long ts_diff, ts_start; ts_start = avm_get_cycles(); retry_own_check: desc = pmbox_dma->virt_desc; for (i = 0; i < pmbox_dma->ndescs; i++) { dmac_inv_range(desc, ((unsigned char *)desc) + mbox_get_desc_size() - 1); if (desc->OWN == 0) { unsigned int index = (MBOX_PHYS_DMA_ADDRESS(pmbox_dma, pmbox_dma->phys_buf[0]) == MBOX_PHYS_DMA_ADDRESS(pmbox_dma, desc->BufPtr)) ? 0 : 1; int resize = 0; if (pmbox_dma->RxTxData && (own_cnt == 0)) { if (pmbox_dma->dir == CAPTURE) { dmac_inv_range(pmbox_dma->virt_buf[index], pmbox_dma->virt_buf[index] + pmbox_dma->size - 1); } resize = pmbox_dma->RxTxData(pmbox_dma->refhandle, pmbox_dma->virt_buf[index]); #if defined(DBG_TDM_RESIZE_CHECK) if (pmbox_dma->dbg_force_reoffset) { resize = min(pmbox_dma->dbg_force_reoffset, pmbox_dma->size / 2); pmbox_dma->dbg_force_reoffset = 0; } #endif/*--- #if defined(DBG_TDM_RESIZE_CHECK) ---*/ if (resize < 0) { DBG_TRC("[tdmlink]%s: error: no slot-synchronize -> reinit dma(%d)\n", __func__, resize); resize = 0; } if (pmbox_dma->force_resize) { /** * Trittbrettfahrer-Irq(Tx) muss DMA-Ack IMMER vor Hw-Irq(Rx) liefern - * sonst wird der ACK erst mit folgenden Rx-Irq bestaetigt... * und dma 4 ms zu spaet! * Also hier den DMA-Buffer um einen FS verkuerzen */ resize += pmbox_dma->force_resize; pmbox_dma->force_resize = 0; } if (resize) { pmbox_dma->reoffset += resize; } if (pmbox_dma->dir == PLAYBACK) { dmac_flush_range(pmbox_dma->virt_buf[index], pmbox_dma->virt_buf[index] + pmbox_dma->size - 1); } } desc->size = pmbox_dma->size - resize; desc->length = desc->size; desc->OWN = 1; desc->ei = 1; dmac_flush_range(desc, ((unsigned char *)desc) + mbox_get_desc_size() - 1); own_cnt++; } desc = ipq_desc_ptr(pmbox_dma, desc, 1); } if (own_cnt > 1) { /** * to much dma-buffer ready - missing interrupt? */ pmbox_dma->own_ovr += own_cnt - 1; } else if (own_cnt == 0) { /** * the dma-status completed but no dma-buffer ready! what the hell ? * retry dma-buffer-ready-check; timeout after 100 us */ ts_diff = avm_get_cycles() - ts_start; if (retry == 0) pmbox_dma->own_udr++; retry++; if (avm_cycles_to_usec(ts_diff) < 100) goto retry_own_check; pr_err("%s:[%lu]%cx: own-check failed(timeout) retry=%u ts-diff=%lu(%lu us)\n", __func__, pmbox_dma->irq_cnt, pmbox_dma->dir == CAPTURE ? 'R' : 'T', retry, ts_diff, avm_cycles_to_usec(ts_diff)); return; } if (retry) { if (retry > pmbox_dma->max_retry) { pmbox_dma->max_retry = retry; } if (ts_diff > pmbox_dma->max_ts_diff) { pmbox_dma->max_ts_diff = ts_diff; } pr_err("%s:[%lu]%cx: own-check retry=%u ts-diff=%lu(%lu us)" " - udr=%u max-retry=%u max-ts-diff=%lu (%lu us)\n", __func__, pmbox_dma->irq_cnt, pmbox_dma->dir == CAPTURE ? 'R' : 'T', retry, ts_diff, avm_cycles_to_usec(ts_diff), pmbox_dma->own_udr, pmbox_dma->max_retry, pmbox_dma->max_ts_diff, avm_cycles_to_usec(pmbox_dma->max_ts_diff) ); } } /** */ static irqreturn_t mbox_dma_irq(int irq, void *dev_id) { struct _mbox_dma *pmbox_dma = (struct _mbox_dma *)dev_id; uint32_t ret = IRQ_NONE; unsigned int status; unsigned int dma_id = pmbox_dma->dma_id; status = _mbox_read(dma_id, ADSS_MBOXn_MBOX_INT_STATUS_REG, NULL); if (status & MBOX_INT_STATUS_RX_DMA_COMPLETE) { mbox_interrupt_ack(dma_id, status, MBOX_INT_STATUS_RX_DMA_COMPLETE); mbox_ack_desc(pmbox_dma); pmbox_dma->irq_cnt++; ret = IRQ_HANDLED; status &= ~MBOX_INT_STATUS_RX_DMA_COMPLETE; } if (status & MBOX_INT_STATUS_TX_DMA_COMPLETE) { mbox_interrupt_ack(dma_id, status, MBOX_INT_STATUS_TX_DMA_COMPLETE); mbox_ack_desc(pmbox_dma); pmbox_dma->irq_cnt++; ret = IRQ_HANDLED; status &= ~MBOX_INT_STATUS_TX_DMA_COMPLETE; } if ((status & MBOX_INT_STATUS_RX_UNDERFLOW) || (status & MBOX_INT_STATUS_RX_FIFO_UNDERFLOW)) { pmbox_dma->err_cnt++; ret = IRQ_HANDLED; } if ((status & MBOX_INT_STATUS_TX_OVERFLOW) || (status & MBOX_INT_STATUS_TX_FIFO_OVERFLOW)) { pmbox_dma->err_cnt++; ret = IRQ_HANDLED; } if (status & MBOX_INT_STATUS_BI_TX_RX) pmbox_dma->bi_irq_cnt++; if (status) mbox_interrupt_ack(dma_id, status, status); if (pmbox_dma->irqcontext_mbox) { unsigned int status2; struct _mbox_dma *pmbox_dma2 = (struct _mbox_dma *)pmbox_dma->irqcontext_mbox; if (pmbox_dma2->reoffset > pmbox_dma->reoffset) { unsigned int cdir, cdir2; pmbox_dma->force_resize = pmbox_dma->slots * sizeof(short); cdir = pmbox_dma->dir == CAPTURE ? 'R' : 'T'; cdir2 = pmbox_dma2->dir == CAPTURE ? 'R' : 'T'; status2 = _mbox_read(pmbox_dma2->dma_id, ADSS_MBOXn_MBOX_INT_STATUS_REG, NULL); pr_err("[tdm_link][%lu/%lu] %cx-Reoffset %u < %cx-Reoffset %u: force resize to sync %cx(%sCOMPLETED) and %cx(%sCOMPLETED)\n", pmbox_dma->irq_cnt, pmbox_dma2->irq_cnt, cdir, pmbox_dma->reoffset, cdir2, pmbox_dma2->reoffset, cdir, (status & (MBOX_INT_STATUS_TX_DMA_COMPLETE | MBOX_INT_STATUS_RX_DMA_COMPLETE)) ? "" : "NOT ", cdir2, (status2 & (MBOX_INT_STATUS_TX_DMA_COMPLETE | MBOX_INT_STATUS_RX_DMA_COMPLETE)) ? "" : "NOT "); } return mbox_dma_irq(irq, (void *)pmbox_dma2); } return ret; } /** */ static void mbox_dma_policy_reset(int dma_id) { unsigned int val; val = mbox_read(dma_id, ADSS_MBOXn_MBOX_DMA_POLICY_REG); val |= MBOX_DMA_POLICY_SW_RESET; /*--- val |= MBOX_DMA_POLICY_RXD_16BIT_SWAP | MBOX_DMA_POLICY_RXD_END_SWAP; ??? ---*/ mbox_write(dma_id, val, ADSS_MBOXn_MBOX_DMA_POLICY_REG); usleep_range(4000, 5000); val &= ~(MBOX_DMA_POLICY_SW_RESET); mbox_write(dma_id, val, ADSS_MBOXn_MBOX_DMA_POLICY_REG); } /** * nur 16 Bit * irq < 0: kein IRQ aufsetzen */ static int mbox_dma_init(struct _mbox_dma *pmbox_dma, void *refhandle, int (*RxTxData)(void *refhandle, void *p), struct device *dev, int dma_id, int dir, int irq, int cpu, unsigned int slots, unsigned int fs_per_dma, unsigned int n_desc ) { unsigned int DescriptorPoolSize, DMABufferSize, AlignedBufferSize, DMAPoolSize; unsigned int i; int rc = 0; dma_addr_t dmaPhy; void *dmaVirt; if (pmbox_dma->irq_active > irq_uninitialized) { return -1; } memset(pmbox_dma, 0, sizeof(struct _mbox_dma)); pmbox_dma->desc_size = mbox_get_desc_size(); pmbox_dma->ndescs = n_desc; pmbox_dma->mbox_mask = mbox_get_mask(); DMABufferSize = slots * sizeof(short) * fs_per_dma; DescriptorPoolSize = L1_CACHE_ALIGN(pmbox_dma->ndescs * pmbox_dma->desc_size); AlignedBufferSize = L1_CACHE_ALIGN((DMABufferSize)); /*--- aligned dma-buffersize ---*/ DMAPoolSize = DescriptorPoolSize + 2 * AlignedBufferSize; /*--- ping pong buffer ---*/ DBG_TRC("%s: dma_id=%d, dir=%d(%s), irq=%d cpu=%d slots=%u fs_per_dma=%u n_desc=%u DMABufferSize=%u DescriptorPoolSize=%u DMAPoolSize=%u desc_size=%u mbox_mask=%x\n", __func__, dma_id, dir, dir == PLAYBACK ? "tx" : dir == CAPTURE ? "rx" : "?", irq, cpu, slots, fs_per_dma, n_desc, DMABufferSize, DescriptorPoolSize, DMAPoolSize, pmbox_dma->desc_size, pmbox_dma->mbox_mask); pmbox_dma->dma_pool = kzalloc(DMAPoolSize, GFP_KERNEL); if (pmbox_dma->dma_pool == NULL) { pr_err("%s: no dma-memory\n", __func__); return -ENOMEM; } dmaVirt = pmbox_dma->dma_pool; dmaPhy = virt_to_phys(pmbox_dma->dma_pool); pmbox_dma->slots = slots; pmbox_dma->irq = irq; pmbox_dma->dma_id = dma_id; pmbox_dma->dir = dir; pmbox_dma->dev = dev; pmbox_dma->size = DMABufferSize; pmbox_dma->refhandle = refhandle; pmbox_dma->RxTxData = RxTxData; pmbox_dma->virt_desc = dmaVirt; pmbox_dma->phys_desc = dmaPhy; dmaVirt += DescriptorPoolSize; dmaPhy += DescriptorPoolSize; if (!pmbox_dma->virt_desc) { pr_err("%s: no dma-memory\n", __func__); return -ENOMEM; } for (i = 0; i < ARRAY_SIZE(pmbox_dma->virt_buf); i++) { pmbox_dma->virt_buf[i] = dmaVirt; pmbox_dma->phys_buf[i] = dmaPhy; dmaVirt += AlignedBufferSize; dmaPhy += AlignedBufferSize; dmac_flush_range(pmbox_dma->virt_buf[i], pmbox_dma->virt_buf[i] + pmbox_dma->size - 1); } mbox_dma_policy_reset(dma_id); if (irq >= 0) { uint32_t hwirq = irq; struct irq_desc *irq_desc = irq_to_desc(irq); if (irq_desc) hwirq = irq_desc->irq_data.hwirq; pr_info("%s: irq=%d hwirq=%d\n", __func__, irq, hwirq); if (cpu < 0) cpu = raw_smp_processor_id(); #if defined(CONFIG_AVM_FASTIRQ) rc = avm_request_fiq_on(cpumask_of(cpu), hwirq, mbox_dma_irq, FIQ_HWIRQ, dir == CAPTURE ? "ipq40xx-mbox-rx" : "ipq40xx-mbox-tx", (void *)pmbox_dma); if (rc == 0) { pmbox_dma->fiq_cpumode = 1 + cpu; avm_gic_fiq_setup(hwirq, cpumask_of(cpu), FIQ_PRIO_USER, 0, 0); pr_info("%s: %u %pS %p success\n", __func__, cpu, mbox_dma_irq, pmbox_dma); } else #endif rc = request_irq_on(cpu, irq, mbox_dma_irq, IRQF_TRIGGER_HIGH, dir == CAPTURE ? "ipq40xx-mbox-rx" : "ipq40xx-mbox-tx", (void *)pmbox_dma); } if (rc == 0) { pmbox_dma->irq_active = irq_initialized; } else { pr_err("%s: request_irq(%d failed)\n", __func__, irq); } return rc; } /** */ static void mbox_interrupt_enable(struct _mbox_dma *pmbox_dma, unsigned int mask) { unsigned int val; val = mbox_read(pmbox_dma->dma_id, ADSS_MBOXn_MBOX_INT_ENABLE_REG); val |= mask; mbox_write(pmbox_dma->dma_id, val, ADSS_MBOXn_MBOX_INT_ENABLE_REG); } /** */ static void mbox_interrupt_disable(struct _mbox_dma *pmbox_dma, unsigned int mask) { unsigned int val; val = mbox_read(pmbox_dma->dma_id, ADSS_MBOXn_MBOX_INT_ENABLE_REG); val &= ~mask; mbox_write(pmbox_dma->dma_id, val, ADSS_MBOXn_MBOX_INT_ENABLE_REG); } /** */ static void mbox_dma_exit(struct _mbox_dma *pmbox_dma) { unsigned int i; DBG_TRC("%s(%s)\n", __func__, pmbox_dma->dir == CAPTURE ? "rx" : "tx"); if (pmbox_dma->irq_active) { if (pmbox_dma->irq >= 0) { #if defined(CONFIG_AVM_FASTIRQ) if (pmbox_dma->fiq_cpumode) { avm_free_fiq_on(pmbox_dma->fiq_cpumode - 1, pmbox_dma->irq, pmbox_dma); } else #endif/*--- #if defined(CONFIG_AVM_FASTIRQ) ---*/ free_irq(pmbox_dma->irq, pmbox_dma); } pmbox_dma->irq_active = irq_uninitialized; } kfree(pmbox_dma->dma_pool); pmbox_dma->virt_desc = NULL; pmbox_dma->dma_pool = NULL; for (i = 0; i < ARRAY_SIZE(pmbox_dma->virt_buf); i++) { pmbox_dma->virt_buf[i] = NULL; } } /** */ static void mbox_dma_stop(struct _mbox_dma *pmbox_dma) { DBG_TRC("%s(%s active=%d)\n", __func__, pmbox_dma->dir == CAPTURE ? "rx" : pmbox_dma->dir == PLAYBACK ? "tx" : "?", pmbox_dma->irq_active); if (pmbox_dma->irq_active != irq_progress) { return; } pmbox_dma->irq_active = irq_initialized; if (pmbox_dma->dir == PLAYBACK) { mbox_interrupt_disable(pmbox_dma, MBOX_INT_ENABLE_RX_DMA_COMPLETE); mbox_write(pmbox_dma->dma_id, ADSS_MBOXn_DMA_RX_CONTROL_STOP, ADSS_MBOXn_MBOXn_DMA_RX_CONTROL_REG); } else if (pmbox_dma->dir == CAPTURE) { mbox_interrupt_disable(pmbox_dma, MBOX_INT_ENABLE_TX_DMA_COMPLETE); mbox_write(pmbox_dma->dma_id, ADSS_MBOXn_DMA_TX_CONTROL_STOP, ADSS_MBOXn_MBOXn_DMA_TX_CONTROL_REG); } /* Delay for the dynamically calculated max time based on * sample size, channel, sample rate + margin to ensure that the * DMA engine will be truly idle. */ usleep_range(10000, 12000); adss_stereo_config_enable(pmbox_dma, DISABLE); pmbox_dma->reoffset = 0; } /** * interne Clock: REFDIV=5 POSTPLLDIV=1 INT_DIV=0x31 FRAC_DIV=0x9ba6 fref=48e6 fvco= fref / (REFDIV+1) *(INT_DIV + (FRAC_DIV:17,5)/(1<<13) + (FRAC_DIV:4,0) / (25 * (1<<13))) fpll=fvco/ (2 *POSTPLLDIV) M_RDIV=5 M_MDIV=15 fmclk= (fpll / ((M_RDIV + 1) /2)) / (M_MDIV +1) B_MDIV=191 fbclk=fpll/(B_MDIV+1) */ static void adss_config_clksel(unsigned int mux) { unsigned int muxr_cfg, mmisc_cfg, bmisc_cfg, xm_cfg; DBG_TRC("%s(mux=%u)\n", __func__, mux); if (mux == 0) { /*--- use refclock 48 MHz ---*/ muxr_cfg = 0 << 8; bmisc_cfg = (46 << 1); /*--- B_MDIV ---*/ mmisc_cfg = (0 << 4); /*--- M_MDIV ---*/ xm_cfg = (22 << 8) | 0; /*--- SRC_SEL | M_RDIV ---*/ } else if (mux == 2) { /*--- use external clock 10.368 MHz ---*/ muxr_cfg = 2 << 8; /*--- B_SRC_SEL ---*/ xm_cfg = (2 << 8) | 0; /*--- M_SRC_SEL | M_RDIV ---*/ bmisc_cfg = (0 << 1); /*--- B_MDIV ---*/ mmisc_cfg = (0 << 4); /*--- M_MDIV ---*/ } else if (mux == 1) { /*--- use pll 196 MHz ---*/ muxr_cfg = 1 << 8; bmisc_cfg = (191 << 1); /*--- B_MDIV ---*/ mmisc_cfg = (15 << 4); /*--- M_MDIV ---*/ xm_cfg = (1 << 8) | 5; /*--- SRC_SEL | M_RDIV ---*/ } adss_audio_write(0, ADSS_AUDIO_RXB_CBCR_REG); adss_audio_write(0, ADSS_AUDIO_RXM_CBCR_REG); adss_audio_write(0, ADSS_AUDIO_TXB_CBCR_REG); adss_audio_write(0, ADSS_AUDIO_TXM_CBCR_REG); usleep_range(10000, 12000); adss_audio_write(bmisc_cfg, ADSS_AUDIO_RXB_MISC_REG); adss_audio_write(bmisc_cfg, ADSS_AUDIO_TXB_MISC_REG); adss_audio_write(mmisc_cfg, ADSS_AUDIO_RXM_MISC_REG); adss_audio_write(mmisc_cfg, ADSS_AUDIO_TXM_MISC_REG); adss_audio_write(muxr_cfg, ADSS_AUDIO_RXB_CFG_MUXR_REG); adss_audio_write(muxr_cfg, ADSS_AUDIO_TXB_CFG_MUXR_REG); adss_audio_write(xm_cfg, ADSS_AUDIO_RXM_CFG_RCGR_REG); adss_audio_write(xm_cfg, ADSS_AUDIO_TXM_CFG_RCGR_REG); usleep_range(10000, 12000); adss_audio_write(1, ADSS_AUDIO_RXB_CBCR_REG); adss_audio_write(1, ADSS_AUDIO_RXM_CBCR_REG); adss_audio_write(1, ADSS_AUDIO_TXB_CBCR_REG); adss_audio_write(1, ADSS_AUDIO_TXM_CBCR_REG); } /** * Enable the I2S Stereo block for operation */ static void adss_stereo_config_enable(struct _mbox_dma *pmbox_dma, uint32_t enable) { int stereo_id = 0; uint32_t cfg; DBG_TRC("%s(%s enable=%u)\n", __func__, pmbox_dma->dir == CAPTURE ? "rx" : pmbox_dma->dir == PLAYBACK ? "tx" : "?", enable); if (pmbox_dma->dir == PLAYBACK) { stereo_id = tdm_priv.stereo_tx; } else if (pmbox_dma->dir == CAPTURE) { stereo_id = tdm_priv.stereo_rx; } else { return; } cfg = adss_stereo_read(stereo_id, ADSS_STEREOn_STEREO0_CONFIG_REG); cfg &= ~(STEREOn_CONFIG_ENABLE); if (enable) { cfg |= STEREOn_CONFIG_ENABLE; } adss_stereo_write(stereo_id, cfg, ADSS_STEREOn_STEREO0_CONFIG_REG); } /** */ static void adss_stereo_config_reset(struct _mbox_dma *pmbox_dma, uint32_t reset) { int stereo_id = 0; uint32_t cfg; DBG_TRC("%s(%s reset=%u)\n", __func__, pmbox_dma->dir == CAPTURE ? "rx" : pmbox_dma->dir == PLAYBACK ? "tx" : "?", reset); if (pmbox_dma->dir == PLAYBACK) { stereo_id = tdm_priv.stereo_tx; } else if (pmbox_dma->dir == CAPTURE) { stereo_id = tdm_priv.stereo_rx; } else { return; } cfg = adss_stereo_read(stereo_id, ADSS_STEREOn_STEREO0_CONFIG_REG); cfg &= ~(STEREOn_CONFIG_RESET | STEREOn_CONFIG_MIC_RESET); if (reset) { cfg |= (STEREOn_CONFIG_RESET | STEREOn_CONFIG_MIC_RESET); } adss_stereo_write(stereo_id, cfg, ADSS_STEREOn_STEREO0_CONFIG_REG); } /** */ static void adss_stereo_cnt_reset(struct _mbox_dma *pmbox_dma) { int stereo_id = 0; if (pmbox_dma->dir == PLAYBACK) { stereo_id = tdm_priv.stereo_tx; } else if (pmbox_dma->dir == CAPTURE) { stereo_id = tdm_priv.stereo_rx; } else { return; } adss_stereo_write(stereo_id, 0, ADSS_STEREOn_STEREO0_TX_SAMPLE_CNT_LSB_REG); adss_stereo_write(stereo_id, 0, ADSS_STEREOn_STEREO0_TX_SAMPLE_CNT_MSB_REG); adss_stereo_write(stereo_id, 0, ADSS_STEREOn_STEREO0_RX_SAMPLE_CNT_LSB_REG); adss_stereo_write(stereo_id, 0, ADSS_STEREOn_STEREO0_RX_SAMPLE_CNT_MSB_REG); adss_stereo_write(stereo_id, 0, ADSS_STEREOn_STEREO0_RX_COMPLETE_CNT_REG); adss_stereo_write(stereo_id, 0, ADSS_STEREOn_STEREO0_TX_COMPLETE_CNT_REG); adss_stereo_write(stereo_id, 0, ADSS_STEREOn_STEREO0_UNDERRUN_CNT_REG); adss_stereo_write(stereo_id, 0, ADSS_STEREOn_STEREO0_OVERRUN_CNT_REG); } /** */ static void mbox_dma_prepare_desc(struct _mbox_dma *pmbox_dma) { struct _mbox_desc *virt_desc, *phys_desc; unsigned int i; if (pmbox_dma->irq_active != irq_initialized) { return; } phys_desc = (struct _mbox_desc *)pmbox_dma->phys_desc; virt_desc = pmbox_dma->virt_desc; for (i = 0; i < pmbox_dma->ndescs; i++) { virt_desc->OWN = 1; virt_desc->ei = 1; virt_desc->rsvd1 = virt_desc->EOM = 0; virt_desc->BufPtr = MBOX_PHYS_DMA_ADDRESS(pmbox_dma, pmbox_dma->phys_buf[i & 1]); virt_desc->NextPtr = MBOX_PHYS_DMA_ADDRESS(pmbox_dma, ipq_desc_ptr(pmbox_dma, phys_desc, (i + 1) % pmbox_dma->ndescs)); virt_desc->size = pmbox_dma->size; virt_desc->length = virt_desc->size; DBG_TRC_DESC("[%u]desc[%p] BufPtr=%08x size=%u length=%u next_desc[%08x]\n", i, &phys_desc[i], MBOX_PHYS_DMA_ADDRESS(pmbox_dma, virt_desc->BufPtr), virt_desc->size, virt_desc->length, virt_desc->NextPtr); virt_desc = ipq_desc_ptr(pmbox_dma, virt_desc, 1); } dmac_flush_range(pmbox_dma->virt_desc, ((unsigned char *)virt_desc) - 1); pmbox_dma->irq_cnt = 0; pmbox_dma->bi_irq_cnt = 0; pmbox_dma->err_cnt = 0; pmbox_dma->own_udr = 0; pmbox_dma->own_ovr = 0; pmbox_dma->max_retry = 0; pmbox_dma->max_ts_diff = 0; } /** */ static void mbox_dma_prepare_reg(struct _mbox_dma *pmbox_dma) { unsigned int val; mbox_dma_policy_reset(pmbox_dma->dma_id); adss_stereo_config_enable(pmbox_dma, DISABLE); adss_stereo_cnt_reset(pmbox_dma); adss_stereo_config_reset(pmbox_dma, ENABLE); usleep_range(5000, 6000); mbox_write(pmbox_dma->dma_id, MBOX_FIFO_RESET_TX_INIT | MBOX_FIFO_RESET_RX_INIT, ADSS_MBOXn_MBOX_FIFO_RESET_REG); adss_stereo_config_reset(pmbox_dma, DISABLE); val = mbox_read(pmbox_dma->dma_id, ADSS_MBOXn_MBOX_DMA_POLICY_REG); if (pmbox_dma->dir == PLAYBACK) { /* Request the DMA channel to the controller */ val &= ~ADSS_MBOX_DMA_POLICY_TX_FIFO_THRESHOLD(0xF); val &= ~ADSS_MBOX_DMA_POLICY_SRAM_AC(-1); val |= MBOX_DMA_POLICY_RX_INT_TYPE; val |= ADSS_MBOX_DMA_POLICY_SRAM_AC((unsigned long)pmbox_dma->phys_desc); val |= ADSS_MBOX_DMA_POLICY_TX_FIFO_THRESHOLD(8); mbox_write(pmbox_dma->dma_id, val, ADSS_MBOXn_MBOX_DMA_POLICY_REG); mbox_write(pmbox_dma->dma_id, MBOX_PHYS_DMA_ADDRESS(pmbox_dma, pmbox_dma->phys_desc), ADSS_MBOXn_MBOXn_DMA_RX_DESCRIPTOR_BASE_REG); } else if (pmbox_dma->dir == CAPTURE) { /* Request the DMA channel to the controller */ val &= ~ADSS_MBOX_DMA_POLICY_TX_FIFO_THRESHOLD(0xF); val &= ~ADSS_MBOX_DMA_POLICY_SRAM_AC(-1); val |= MBOX_DMA_POLICY_TX_INT_TYPE; val |= ADSS_MBOX_DMA_POLICY_SRAM_AC((unsigned long)pmbox_dma->phys_desc); val |= ADSS_MBOX_DMA_POLICY_TX_FIFO_THRESHOLD(8); mbox_write(pmbox_dma->dma_id, val, ADSS_MBOXn_MBOX_DMA_POLICY_REG); mbox_write(pmbox_dma->dma_id, MBOX_PHYS_DMA_ADDRESS(pmbox_dma, pmbox_dma->phys_desc), ADSS_MBOXn_MBOXn_DMA_TX_DESCRIPTOR_BASE_REG); } } /** */ static void mbox_dma_start(struct _mbox_dma *pmbox_dma) { DBG_TRC("%s(%s)\n", __func__, pmbox_dma->dir == CAPTURE ? "rx" : pmbox_dma->dir == PLAYBACK ? "tx" : "?"); if (pmbox_dma->irq_active != irq_initialized) { return; } pmbox_dma->irq_active = irq_progress; /* The direction is indicated from the DMA engine perspective * i.e. we'll be using the RX registers for Playback and * the TX registers for capture */ if (pmbox_dma->dir == PLAYBACK) { mbox_interrupt_enable(pmbox_dma, MBOX_INT_ENABLE_RX_DMA_COMPLETE); mbox_write(pmbox_dma->dma_id, ADSS_MBOXn_DMA_RX_CONTROL_START, ADSS_MBOXn_MBOXn_DMA_RX_CONTROL_REG); } else if (pmbox_dma->dir == CAPTURE) { mbox_interrupt_enable(pmbox_dma, MBOX_INT_ENABLE_TX_DMA_COMPLETE); mbox_write(pmbox_dma->dma_id, ADSS_MBOXn_DMA_TX_CONTROL_START, ADSS_MBOXn_MBOXn_DMA_TX_CONTROL_REG); } adss_stereo_config_enable(pmbox_dma, ENABLE); } /** */ static int intf_mbox_probe(struct platform_device *pdev) { struct device_node *np = NULL; int irq; uint32_t tx_channel; uint32_t rx_channel; uint32_t id; void __iomem *reg_base; struct resource *res; int rc = 0; np = of_node_get(pdev->dev.of_node); if ((of_property_read_u32(np, "dma-index", &id))) { pr_err("%s: error reading critical device node properties\n", np->name); rc = -EINVAL; goto init_err; } if (id >= ARRAY_SIZE(mbox_data)) { rc = -EINVAL; goto init_err; } if ((of_property_read_u32(np, "tx-channel", &tx_channel))) { tx_channel = CHN_STATUS_DISABLE; } if ((of_property_read_u32(np, "rx-channel", &rx_channel))) { rx_channel = CHN_STATUS_DISABLE; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "%s: %d: Error getting mbox resource\n", __func__, __LINE__); return -ENODEV; } /* Read interrupt and store */ irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "%s: MBOX %d IRQ is not provided\n", __func__, id); rc = -EINVAL; goto init_err; } reg_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(reg_base)) { of_node_put(pdev->dev.of_node); return PTR_ERR(reg_base); } DBG_TRC("%s: reg_base=%p(%x-%x) irq=%u tx_channel=%u rx_channel=%u dma_id=%u\n", __func__, reg_base, res->start, res->end, irq, tx_channel, rx_channel, id); mbox_data[id].reg_base = reg_base; mbox_data[id].irq = irq; init_err: of_node_put(pdev->dev.of_node); return rc; } /** */ static int intf_mbox_remove(struct platform_device *pdev) { struct device_node *np = NULL; uint32_t id; int rc = 0; np = of_node_get(pdev->dev.of_node); if ((of_property_read_u32(np, "dma-index", &id))) { pr_err("%s: error reading critical device node properties\n", np->name); rc = -EINVAL; goto remove_err; } if (id >= ARRAY_SIZE(mbox_data)) { rc = -EINVAL; goto remove_err; } DBG_TRC("%s: id==%u\n", __func__, id); remove_err: of_node_put(pdev->dev.of_node); return rc; } /** */ static void _tdm_audio_clk_disable(struct clk **clk, struct device *dev) { if (!IS_ERR_OR_NULL(*clk)) { DBG_TRC("%s(%s)\n", __func__, __clk_get_name(*clk)); if (__clk_is_enabled(*clk)) { clk_disable_unprepare(*clk); } devm_clk_put(dev, *clk); *clk = NULL; } } /** */ static int intf_tdm_remove(struct platform_device *pdev) { if (tdm_priv.clk_set) { DBG_TRC("%s:\n", __func__); tdm_priv.clk_set = 0; /*--- workarround da __clk_is_enabled() inkonsistent! ---*/ _tdm_audio_clk_disable(&tdm_priv.audio_tx_mclk, &pdev->dev); _tdm_audio_clk_disable(&tdm_priv.audio_tx_bclk, &pdev->dev); _tdm_audio_clk_disable(&tdm_priv.audio_rx_mclk, &pdev->dev); _tdm_audio_clk_disable(&tdm_priv.audio_rx_bclk, &pdev->dev); } return 0; } /** */ static int intf_tdm_probe(struct platform_device *pdev) { struct device_node *np = NULL; int ret = 0; DBG_TRC("%s(%s)\n", __func__, pdev->name); np = of_node_get(pdev->dev.of_node); /* TX is enabled only when both DMA and Stereo TX channel * is specified in the DTSi */ if (!(of_property_read_u32(np, "dma-tx-channel", &tdm_priv.mbox_tx) || of_property_read_u32(np, "stereo-tx-port", &tdm_priv.stereo_tx))) { tdm_priv.tx_enabled = ENABLE; } /* RX is enabled only when both DMA and Stereo RX channel * is specified in the DTSi, except in case of SPDIF RX */ if (!(of_property_read_u32(np, "dma-rx-channel", &tdm_priv.mbox_rx))) { if (!(of_property_read_u32(np, "stereo-rx-port", &tdm_priv.stereo_rx))) { tdm_priv.rx_enabled = ENABLE; } } /* Either TX or Rx should have been enabled for a DMA/Stereo Channel */ if (!(tdm_priv.tx_enabled || tdm_priv.rx_enabled)) { pr_err("%s: error reading critical device node properties\n", np->name); ret = -EFAULT; } else { tdm_priv.audio_tx_bclk = devm_clk_get(&pdev->dev, "audio_tx_bclk"); tdm_priv.audio_tx_mclk = devm_clk_get(&pdev->dev, "audio_tx_mclk"); tdm_priv.audio_rx_bclk = devm_clk_get(&pdev->dev, "audio_rx_bclk"); tdm_priv.audio_rx_mclk = devm_clk_get(&pdev->dev, "audio_rx_mclk"); if (IS_ERR(tdm_priv.audio_tx_bclk) || IS_ERR(tdm_priv.audio_tx_mclk) || IS_ERR(tdm_priv.audio_rx_bclk) || IS_ERR(tdm_priv.audio_rx_mclk)) { pr_err("%s error on get clock\n", __func__); } else { tdm_priv.pdev = pdev; pr_err("%s: %s dma-tx-channel=%u stereo-tx-port=%u %s dma-rx-channel=%u stereo-rx-port=%u\n", __func__, tdm_priv.tx_enabled ? "ena:" : "dis", tdm_priv.mbox_tx, tdm_priv.stereo_tx, tdm_priv.rx_enabled ? "ena:" : "dis", tdm_priv.mbox_rx, tdm_priv.stereo_rx); } } of_node_put(pdev->dev.of_node); return ret; } /** */ static int intf_stereo_probe(struct platform_device *pdev) { uint32_t stereo_port_id = 0; struct resource *res; struct device_node *np = NULL; int ret = 0; DBG_TRC("%s\n", __func__); np = of_node_get(pdev->dev.of_node); if (!(of_property_read_u32(np, "stereo-index", &stereo_port_id))) { if (stereo_port_id >= MAX_STEREO_ENTRIES) { of_node_put(pdev->dev.of_node); pr_err("%s: error: stereo_port_id=%u\n", np->name, stereo_port_id); ret = -EFAULT; } else { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); stereo_base[stereo_port_id] = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(stereo_base[stereo_port_id])) { ret = PTR_ERR(stereo_base[stereo_port_id]); } } } else { pr_err("%s: error reading critical device node properties\n", np->name); ret = -EFAULT; } of_node_put(pdev->dev.of_node); return ret; } /** */ static int intf_stereo_remove(struct platform_device *pdev) { DBG_TRC("%s: todo\n", __func__); return 0; } /** */ static int qca_pcm_avm_probe(struct platform_device *pdev) { const struct of_device_id *match; int intf, ret = -EINVAL; DBG_TRC("%s\n", __func__); match = of_match_device(pcm_avm_id_table, &pdev->dev); if (!match) { pr_err("%s: match error\n", __func__); return -ENODEV; } intf = (uint32_t)match->data; DBG_TRC("%s %u\n", __func__, intf); switch (intf) { case intf_audio_adss_ipq4019: ret = intf_audio_adss_probe(pdev); ipq_cfgs = &ipq4019_cfgs; break; case intf_audio_adss_ipq8074: ret = intf_audio_adss_probe(pdev); ipq_cfgs = &ipq8074_cfgs; break; case intf_pcm: ret = intf_pcm_driver_probe(pdev); break; case intf_mbox: ret = intf_mbox_probe(pdev); break; case intf_tdm: ret = intf_tdm_probe(pdev); break; case intf_stereo: ret = intf_stereo_probe(pdev); break; case intf_tdm_pins: ret = intf_tdm_pins_probe(pdev, 1); break; } if (ret && DebugHandle) { avm_DebugCallUnRegister(DebugHandle); DebugHandle = NULL; } else if (DebugHandle == NULL) { DebugHandle = avm_DebugCallRegister("pcm_", cmdlineparse, NULL); } return ret; } /** */ static int qca_pcm_avm_remove(struct platform_device *pdev) { const struct of_device_id *match; int intf, ret = -EINVAL; if (DebugHandle) { avm_DebugCallUnRegister(DebugHandle); DebugHandle = NULL; } match = of_match_device(pcm_avm_id_table, &pdev->dev); if (!match) { pr_err("%s: match error\n", __func__); return -ENODEV; } intf = (uint32_t)match->data; /*--- DBG_TRC("%s %u\n", __func__, intf); ---*/ switch (intf) { case intf_audio_adss_ipq4019: case intf_audio_adss_ipq8074: ret = intf_audio_adss_remove(pdev); break; case intf_pcm: ret = intf_pcm_driver_remove(pdev); break; case intf_mbox: ret = intf_mbox_remove(pdev); break; case intf_tdm: ret = intf_tdm_remove(pdev); break; case intf_stereo: ret = intf_stereo_remove(pdev); break; } return ret; } #define PCM_MULT_FACTOR 4 /** */ static int adss_set_clk_rate(unsigned int slot_count, unsigned int bit_width, unsigned int rate) { unsigned int clk_rate = slot_count * bit_width * rate * PCM_MULT_FACTOR; int ret = clk_set_rate(pcm_clk, clk_rate); pr_err("%s(slot_count=%u bit_width=%u rate=%u) -> clk_rate=%x\n", __func__, slot_count, bit_width, rate, clk_rate); if (ret) { pr_err("%s : clk_set_rate failed for pcm clock\n", __func__); return ret; } /*--- ret = clk_prepare_enable(pcm_clk); ---*/ return ret; } /** * Enable TDM */ static void adss_glb_tdm_mode(void) { uint32_t cfg; DBG_TRC("%s()\n", __func__); cfg = adss_audio_read(ADSS_GLB_AUDIO_MODE_REG); cfg &= ~GLB_AUDIO_MODE_XMIT_MASK; cfg |= GLB_AUDIO_MODE_XMIT_TDM; cfg &= ~GLB_AUDIO_MODE_RECV_MASK; cfg |= GLB_AUDIO_MODE_RECV_TDM; adss_audio_write(cfg, ADSS_GLB_AUDIO_MODE_REG); } /** * FSYNC Hi Duration for Transmitter/Receiver */ static void adss_glb_tdm_ctrl_sync_num(uint32_t val, uint32_t dir) { uint32_t cfg; DBG_TRC("%s(%u, dir=%s)\n", __func__, val, dir == CAPTURE ? "rx" : dir == PLAYBACK ? "tx" : "?"); cfg = adss_audio_read(ADSS_GLB_TDM_CTRL_REG); if (dir == PLAYBACK) { cfg &= ~(GLB_TDM_CTRL_TX_SYNC_NUM_MASK); cfg |= GLB_TDM_CTRL_TX_SYNC_NUM(val); } else if (dir == CAPTURE) { cfg &= ~(GLB_TDM_CTRL_RX_SYNC_NUM_MASK); cfg |= GLB_TDM_CTRL_RX_SYNC_NUM(val); } adss_audio_write(cfg, ADSS_GLB_TDM_CTRL_REG); } /** * Serial Data Delay for transmitter/receiver */ static void adss_glb_tdm_ctrl_delay(uint32_t delay, uint32_t dir) { uint32_t cfg; DBG_TRC("%s(delay=%u, dir=%s)\n", __func__, delay, dir == CAPTURE ? "rx" : dir == PLAYBACK ? "tx" : "?"); cfg = adss_audio_read(ADSS_GLB_TDM_CTRL_REG); if (dir == PLAYBACK) { cfg &= ~(GLB_TDM_CTRL_TX_DELAY); cfg |= delay ? GLB_TDM_CTRL_TX_DELAY : 0; } else if (dir == CAPTURE) { cfg &= ~(GLB_TDM_CTRL_RX_DELAY); cfg |= delay ? GLB_TDM_CTRL_RX_DELAY : 0; } adss_audio_write(cfg, ADSS_GLB_TDM_CTRL_REG); } /** * Channel Number Per Frame for Transmitter/Receiver * Real value = val + 1 */ static void adss_glb_tdm_ctrl_ch_num(uint32_t val) { uint32_t cfg; DBG_TRC("%s(%u)\n", __func__, val); cfg = adss_audio_read(ADSS_GLB_TDM_CTRL_REG); cfg &= ~(GLB_TDM_CTRL_TX_CHAN_NUM_MASK); cfg |= GLB_TDM_CTRL_TX_CHAN_NUM(val); cfg &= ~(GLB_TDM_CTRL_RX_CHAN_NUM_MASK); cfg |= GLB_TDM_CTRL_RX_CHAN_NUM(val); adss_audio_write(cfg, ADSS_GLB_TDM_CTRL_REG); } static void adss_glb_data_port_en(uint32_t enable, uint32_t dir) { uint32_t cfg; uint32_t reg, val; if (dir == PLAYBACK) { reg = ipq_cfgs->txd_oe.reg; val = ipq_cfgs->txd_oe.mask; } else { reg = ipq_cfgs->rxd_oe.reg; val = ipq_cfgs->rxd_oe.mask; } cfg = adss_audio_read(reg); cfg &= ~val; if (enable) { cfg |= val; } adss_audio_write(cfg, reg); } /** * I2S0 TX Data Port Enable * I2S3 RX Data Port Enable * Todo : Check if bits 6:4 configures only * I2S0 or other channels as well */ static void adss_glb_rxtx_data_port_en(uint32_t enable) { DBG_TRC("%s(%u)\n", __func__, enable); adss_glb_data_port_en(enable, CAPTURE); adss_glb_data_port_en(enable, PLAYBACK); } /** */ static void adss_glb_framesync_port_en(uint32_t enable, uint32_t dir) { uint32_t cfg; uint32_t reg, val; if (dir == PLAYBACK) { reg = ipq_cfgs->i2s0_fs_oe.reg; val = ipq_cfgs->i2s0_fs_oe.mask; } else { reg = ipq_cfgs->i2s3_fs_oe.reg; val = ipq_cfgs->i2s3_fs_oe.mask; } cfg = adss_audio_read(reg); cfg &= ~val; if (enable) { cfg |= val; } adss_audio_write(cfg, reg); } /** * Frame Sync Port Enable for I2S0 TX * Frame Sync Port Enable for I2S3 RX */ static void adss_glb_rxtx_framesync_port_en(uint32_t enable) { DBG_TRC("%s(%u)\n", __func__, enable); adss_glb_framesync_port_en(enable, CAPTURE); adss_glb_framesync_port_en(enable, PLAYBACK); } /** */ static void adss_glb_clk_enable_oe(int enable, uint32_t dir) { uint32_t cfg; uint32_t mask = 0; DBG_TRC("%s(enable=%u)\n", __func__, enable); cfg = adss_audio_read(ADSS_GLB_CLK_I2S_CTRL_REG); if (dir == PLAYBACK) { mask = (GLB_CLK_I2S_CTRL_TX_BCLK_OE | GLB_CLK_I2S_CTRL_TX_MCLK_OE); } else { mask = (GLB_CLK_I2S_CTRL_RX_BCLK_OE | GLB_CLK_I2S_CTRL_RX_MCLK_OE); } if (enable) { cfg |= mask; } else { cfg &= ~mask; } adss_audio_write(cfg, ADSS_GLB_CLK_I2S_CTRL_REG); } /** * I2S Interface Enable */ static void adss_glb_i2s_interface_en(int enable) { uint32_t cfg; DBG_TRC("%s()\n", __func__); cfg = adss_audio_read(ADSS_GLB_CHIP_CTRL_I2S_REG); cfg &= ~(GLB_CHIP_CTRL_I2S_INTERFACE_EN); if (enable) { cfg |= GLB_CHIP_CTRL_I2S_INTERFACE_EN; } adss_audio_write(cfg, ADSS_GLB_CHIP_CTRL_I2S_REG); usleep_range(4000, 5000); } EXPORT_SYMBOL(adss_glb_i2s_interface_en); /** * Cross 1K Boundary */ static void adss_glb_audio_mode_B1K(void) { uint32_t cfg; DBG_TRC("%s()\n", __func__); cfg = adss_audio_read(ADSS_GLB_AUDIO_MODE_REG); cfg |= GLB_AUDIO_MODE_B1K; adss_audio_write(cfg, ADSS_GLB_AUDIO_MODE_REG); } /** * I2S Module Reset */ static void adss_glb_i2s_reset(void) { DBG_TRC("%s()\n", __func__); adss_audio_write(ipq_cfgs->i2s_reset_val.mask, ipq_cfgs->i2s_reset_val.reg); usleep_range(4000, 5000); adss_audio_write(0x0, ipq_cfgs->i2s_reset_val.reg); } /** */ static int _clk_set(struct clk *clk, struct device *dev, uint32_t val) { int ret; DBG_TRC("%s(%s %u) clk_ena=%u\n", __func__, __clk_get_name(clk), val, __clk_is_enabled(clk)); #if 0 if (__clk_is_enabled(clk)) { clk_disable_unprepare(clk); } #endif ret = clk_set_rate(clk, val); if (ret != 0) { dev_err(dev, "%s: Error in setting %s\n", __func__, __clk_get_name(clk)); return ret; } ret = clk_prepare_enable(clk); if (ret != 0) { dev_err(dev, "%s: Error in enable %s\n", __func__, __clk_get_name(clk)); return ret; } return 0; } /** */ static void adss_config_clk(unsigned int ext_clk, unsigned int bclk, unsigned int mclk) { DBG_TRC("%s(ext_clk=%u bclk=%u mclk=%u)\n", __func__, ext_clk, bclk, mclk); adss_glb_i2s_reset(); if (is_IPQ4019()) { /** * JZ-73366: funktioniert bei Dakota zuverlaessiger ? */ if (ext_clk == 0) { tdm_priv.clk_set = 1; /*--- workarround da __clk_is_enabled() inkonsistent! ---*/ _clk_set(tdm_priv.audio_tx_bclk, &tdm_priv.pdev->dev, bclk); _clk_set(tdm_priv.audio_tx_mclk, &tdm_priv.pdev->dev, mclk); _clk_set(tdm_priv.audio_rx_bclk, &tdm_priv.pdev->dev, bclk); _clk_set(tdm_priv.audio_rx_mclk, &tdm_priv.pdev->dev, mclk); usleep_range(8000, 10000); /*--- Workaround bad clk-interface ? ---*/ if (mclk == 4096000) { if (adss_audio_read(ADSS_AUDIO_PLL_CONFIG_REG) != 0x85) { adss_audio_write(0x85, ADSS_AUDIO_PLL_CONFIG_REG); usleep_range(8000, 10000); } if (adss_audio_read(ADSS_AUDIO_PLL_MODULATION_REG) != 0x04dd3062) { adss_audio_write(0x04dd3062, ADSS_AUDIO_PLL_MODULATION_REG); usleep_range(8000, 10000); } } usleep_range(12000, 16000); } else { adss_config_clksel(2); } } else { tdm_priv.clk_set = 1; /*--- workarround da __clk_is_enabled() inkonsistent! ---*/ _clk_set(tdm_priv.audio_tx_mclk, &tdm_priv.pdev->dev, mclk); _clk_set(tdm_priv.audio_tx_bclk, &tdm_priv.pdev->dev, bclk); _clk_set(tdm_priv.audio_rx_mclk, &tdm_priv.pdev->dev, mclk); _clk_set(tdm_priv.audio_rx_bclk, &tdm_priv.pdev->dev, bclk); usleep_range(12000, 16000); } } /** */ static void adss_config_tdm(unsigned int enable, unsigned int master, unsigned int ext_clk) { unsigned int reset_mask, cfg_mode, cfg; DBG_TRC("%s(ena=%u, master=%u ext_clk=%u)\n", __func__, enable, master, ext_clk); reset_mask = STEREOn_CONFIG_MASTER | STEREOn_CONFIG_ENABLE | STEREOn_CONFIG_DATA_WORD_SIZE_MASK | STEREOn_CONFIG_I2S_WORD_SIZE_32 | STEREOn_CONFIG_MIC_WORD_SIZE_32 | STEREOn_CONFIG_MCK_SEL | 0; /*--- only 16 Bit-Mode: ---*/ cfg_mode = STEREOn_CONFIG_DATA_WORD_SIZE(1) | STEREOn_CONFIG_I2S_WORD_SIZE_16 | STEREOn_CONFIG_MIC_WORD_SIZE_16 | /*--- STEREOn_CONFIG_MONO_MODE | ---*/ 0; cfg_mode |= master ? STEREOn_CONFIG_MASTER : 0; cfg_mode |= enable ? STEREOn_CONFIG_ENABLE : 0; cfg_mode |= ext_clk ? STEREOn_CONFIG_MCK_SEL : 0; /*--- wenn dieses Bit nicht gesetzt wird, dann wird extclk "zerstoert" ---*/ cfg = adss_stereo_read(tdm_priv.stereo_tx, ADSS_STEREOn_STEREO0_CONFIG_REG); cfg &= ~reset_mask; cfg |= cfg_mode; adss_stereo_write(tdm_priv.stereo_tx, cfg, ADSS_STEREOn_STEREO0_CONFIG_REG); cfg = adss_stereo_read(tdm_priv.stereo_rx, ADSS_STEREOn_STEREO0_CONFIG_REG); cfg &= ~reset_mask; cfg |= cfg_mode; adss_stereo_write(tdm_priv.stereo_rx, cfg, ADSS_STEREOn_STEREO0_CONFIG_REG); } static struct _tdm_if_config { unsigned int tdm_if_api; unsigned int slots; unsigned int rxdelay; unsigned int txdelay; unsigned int master; } tdm_config; static int __tdm_if_config_init(struct _tdm_if_config *pconf) { unsigned int bclk = 0, mclk = 0; unsigned long flags; int extclk; DBG_TRC_API("%s(slots=%u, rxdelay=%u txdelay=%u master=%u)\n", __func__, pconf->slots, pconf->rxdelay, pconf->txdelay, pconf->master); if (pconf->master == 1) { bclk = pconf->slots * BITS_PER_BYTE * sizeof(short) * 8000; mclk = bclk * 4; extclk = 0; } else { /* * 252 and 254 are dummy frequency values which will make clock * framework to select external pad clock as source. We need to * configure the mclk and bclk to these fixed values when in * slave mode in order to accept clock from master dev. * * mclk should be set to 252 (mclk_pad_in) and bclk can be set * to 252 (mclk_pad_in) or 254 (bclk_pad_in). */ if (is_IPQ807x()) { mclk = 252; } else { mclk = 255; } bclk = 254; extclk = 1; } adss_config_clk(extclk, bclk, mclk); local_irq_save(flags); adss_glb_tdm_ctrl_ch_num(pconf->slots - 1); adss_glb_clk_enable_oe(pconf->master, CAPTURE); adss_glb_clk_enable_oe(pconf->master, PLAYBACK); adss_glb_rxtx_data_port_en(1); adss_glb_rxtx_framesync_port_en(1); adss_glb_tdm_mode(); adss_config_tdm(DISABLE, pconf->master, extclk); adss_glb_tdm_ctrl_sync_num(0, CAPTURE); adss_glb_tdm_ctrl_sync_num(0, PLAYBACK); adss_glb_tdm_ctrl_delay(pconf->rxdelay, CAPTURE); adss_glb_tdm_ctrl_delay(pconf->txdelay, PLAYBACK); local_irq_restore(flags); return 0; } /** * \brief: * initialize tdm-interface (adss) * \param[in] slots only 8 tested * \param[in] rxdelay 1: serial Data Delay for receiver (1 clock) * \param[in] txdelay 1: serial Data Delay for transmitter (1 clock) * \param[in] master * * \retval 0: ok */ int tdm_if_adss_config_init(unsigned int slots, unsigned int rxdelay, unsigned int txdelay, unsigned int master) { struct _tdm_if_config *pconfig = &tdm_config; if (IS_ERR_OR_NULL(avm_adss_audio_local_base) || IS_ERR_OR_NULL(adss_pcm_base)) { pr_err("%s: error: no valid base set - please check your device-tree!\n", __func__); return -EINVAL; } pconfig->slots = slots; pconfig->rxdelay = rxdelay; pconfig->txdelay = txdelay; pconfig->master = master; pconfig->tdm_if_api = 1; return __tdm_if_config_init(pconfig); } static void workarround_reset_tdm_interface(void) { struct _tdm_if_config *pconfig = &tdm_config; if (pconfig->tdm_if_api == 0) return; /*--- pr_err("%s\n", __func__); ---*/ __tdm_if_config_init(pconfig); } /** * \brief: * disable tdm-interface (adss) */ void tdm_if_adss_config_exit(void) { DBG_TRC_API("%s()\n", __func__); adss_config_tdm(DISABLE, 0, 0); adss_glb_tdm_ctrl_ch_num(0); adss_glb_clk_enable_oe(0, CAPTURE); adss_glb_clk_enable_oe(0, PLAYBACK); adss_glb_rxtx_data_port_en(0); adss_glb_rxtx_framesync_port_en(0); tdm_config.tdm_if_api = 0; } /** */ static const char *mbox_dir_name(unsigned int dma_id) { unsigned int i; for (i = 0; i < ARRAY_SIZE(mbox_dma); i++) { if (mbox_dma[i].dma_id == dma_id) { return mbox_dma[i].dir == PLAYBACK ? " PLAYBACK" : mbox_dma[i].dir == CAPTURE ? " CAPTURE" : "?"; } } return ""; } /** * \brief: * initialize dma-interface (adss) * \param[in] refhandle: see callback * \param[in] TxData: callback for data to send * \param[in] RxData: callback for data receive * \param[in] cpu: bind on cpu * \param[in] only_rxirq: use shared interrupt for rx and tx * \param[in] slots: * \param[in] fs_per_dma: count of framesyncs for one dma-buffer (4 ms == 32) * \param[in] desc_num: count of descriptors (>2: prevent irq-lost by long-locks) * * \note: * maybe exist more than 2 descriptors but only ping-pong-dma-buffer (small latency!) * bufsize of dma-buffer for callback: fs_per_dma * slot * sizeof(short) * return of callbacks: > 0: bytes to shrink dma-size for the following decriptor * * \retval 0: ok */ int tdm_if_adss_dma_init(void *refhandle, int (*TxData)(void *refhandle, void *buf), int (*RxData)(void *refhandle, void *buf), unsigned int cpu, unsigned int only_rxirq, unsigned int slots, unsigned int fs_per_dma, unsigned int desc_num) { int res; DBG_TRC_API("%s(%s cpu=%u ref=%p slots=%u, fs_per_dma=%u desc_num=%u)\n", __func__, only_rxirq ? "shared" : "", cpu, refhandle, slots, fs_per_dma, desc_num); if (desc_num < 2) { desc_num = 2; } res = mbox_dma_init(&mbox_dma[PLAYBACK], refhandle, TxData, &tdm_priv.pdev->dev, tdm_priv.stereo_tx, PLAYBACK, only_rxirq ? -1 : mbox_data[tdm_priv.stereo_tx].irq, cpu, slots, fs_per_dma, desc_num); res |= mbox_dma_init(&mbox_dma[CAPTURE], refhandle, RxData, &tdm_priv.pdev->dev, tdm_priv.stereo_rx, CAPTURE, mbox_data[tdm_priv.stereo_rx].irq, cpu, slots, fs_per_dma, desc_num); if ((res == 0) && only_rxirq) { mbox_dma[CAPTURE].irqcontext_mbox = &mbox_dma[PLAYBACK]; } if (res) { mbox_dma_exit(&mbox_dma[PLAYBACK]); mbox_dma_exit(&mbox_dma[CAPTURE]); } return res; } /** * \brief: * start dma (adss) */ void tdm_if_adss_dma_start(void) { unsigned long flags; DBG_TRC_API("%s()\n", __func__); workarround_reset_tdm_interface(); mbox_dma_prepare_desc(&mbox_dma[PLAYBACK]); mbox_dma_prepare_reg(&mbox_dma[PLAYBACK]); mbox_dma_prepare_desc(&mbox_dma[CAPTURE]); mbox_dma_prepare_reg(&mbox_dma[CAPTURE]); /*--- sehr zeitnah starten, damit dma-buffer zur gleichen Zeit fertig! ---*/ avm_rte_local_irq_save(flags); mbox_dma_start(&mbox_dma[PLAYBACK]); /** * die TDM-FIFO ist recht gross, so dass nach Aufsetzen des DMA sofort * Daten die einen Zeitraum von ca. 800 us entsprechen aufgsaugt werden * (Indiz: Completion kommt schon nach 3.2 ms statt 4 ms). * Der Abstand von 800 us ist zwar in die richtige Richtung (erst Tx dann Rx) aber * doch etwas zu gross */ mbox_wait_for_dma_completion(&mbox_dma[PLAYBACK]); udelay(125); /* insgesamt damit ca. 330 us später den Rx aufsetzen (gemessen mit mbox_check_completion_time) */ mbox_dma_start(&mbox_dma[CAPTURE]); #if defined(DBG_IRQ_MEASURE) mbox_check_completion_time(); #endif avm_rte_local_irq_restore(flags); } /** * \brief: * stop dma (adss) */ void tdm_if_adss_dma_stop(void) { DBG_TRC_API("%s()\n", __func__); mbox_dma_stop(&mbox_dma[PLAYBACK]); mbox_dma_stop(&mbox_dma[CAPTURE]); } /** * \brief: * deinitialize dma-interface (adss) */ void tdm_if_adss_dma_exit(void) { DBG_TRC_API("%s()\n", __func__); mbox_dma_stop(&mbox_dma[PLAYBACK]); mbox_dma_stop(&mbox_dma[CAPTURE]); mbox_dma_exit(&mbox_dma[PLAYBACK]); mbox_dma_exit(&mbox_dma[CAPTURE]); } /** * \brief: * get count of interrupts (adss) * \param[out] rx_irqcnt * \param[out] tx_irqcnt */ void tdm_if_adss_dma_irqcnt(unsigned long *rx_irqcnt, unsigned long *tx_irqcnt) { if (rx_irqcnt) *rx_irqcnt = mbox_dma[CAPTURE].irq_cnt; if (tx_irqcnt) *tx_irqcnt = mbox_dma[PLAYBACK].irq_cnt; /*--- DBG_TRC_API("%s(%lu %lu)\n", __func__, mbox_dma[CAPTURE].irq_cnt, mbox_dma[PLAYBACK].irq_cnt); ---*/ } /** * \brief: * reset count of interrupts (adss) */ void tdm_if_adss_dma_irqcnt_reset(void) { mbox_dma[PLAYBACK].irq_cnt = 0; mbox_dma[CAPTURE].irq_cnt = 0; mbox_dma[CAPTURE].bi_irq_cnt = 0; mbox_dma[PLAYBACK].bi_irq_cnt = 0; } /** * \brief: * print tdm-interface registers * * txt: NULL -> use printk */ void tdm_if_adss_print_status(const char *prefix, char *txt, unsigned int txt_size) { struct _mbox_dma *pmbox_dma; pmbox_dma = &mbox_dma[CAPTURE]; snprintf_add(txt, txt_size, "%s\n", prefix); snprintf_add(txt, txt_size, "Rx(%s):Irqs=%lu(BI-Irqs=%lu) ErrCnt=%u OWN-UDR=%u OWN-OVR=%u ReOffset=%u" " max-retry=%u max-ts-diff=%lu (%lu us)\n", pmbox_dma->irq_active == irq_uninitialized ? "off" : pmbox_dma->irq_active == irq_initialized ? "init" : pmbox_dma->irq_active == irq_progress ? "run" : "?", pmbox_dma->irq_cnt, pmbox_dma->bi_irq_cnt, pmbox_dma->err_cnt, pmbox_dma->own_udr, pmbox_dma->own_ovr, pmbox_dma->reoffset, pmbox_dma->max_retry, pmbox_dma->max_ts_diff, avm_cycles_to_usec(pmbox_dma->max_ts_diff)); txt_size = snprintf_dump_slots(txt ? &txt : NULL, txt_size, pmbox_dma); pmbox_dma = &mbox_dma[PLAYBACK]; snprintf_add(txt, txt_size, "Tx(%s):Irqs=%lu ErrCnt=%u OWN-UDR=%u OWN-OVR=%u ReOffset=%u\n", pmbox_dma->irq_active == irq_uninitialized ? "off" : pmbox_dma->irq_active == irq_initialized ? "init" : pmbox_dma->irq_active == irq_progress ? "run" : "?", pmbox_dma->irq_cnt, pmbox_dma->err_cnt, pmbox_dma->own_udr, pmbox_dma->own_ovr, pmbox_dma->reoffset); txt_size = snprintf_dump_slots(txt ? &txt : NULL, txt_size, pmbox_dma); snprint_all_register(txt ? &txt : NULL, txt_size, (1 << 0) | (1 << 3)); } /** */ static unsigned int snprintf_dump_descriptors(char **_ptxt, unsigned int txt_size, struct _mbox_dma *pmbox_dma) { char *ptxt = NULL; struct _mbox_desc *virt_desc, *phys_desc; unsigned int i; if (pmbox_dma->irq_active < irq_initialized) { return txt_size; } if (_ptxt) { ptxt = *_ptxt; } phys_desc = (struct _mbox_desc *)pmbox_dma->phys_desc; virt_desc = pmbox_dma->virt_desc; for (i = 0; i < pmbox_dma->ndescs; i++) { snprintf_add(ptxt, txt_size, "\t[%u]DESC(Virt %p Phy %p): bufPhy 0x%x NextPtr 0x%x length=%u size=%u vuc=%x ei=%x rcvd1=%x EOM=%x OWN=%x\n", i, virt_desc, phys_desc, virt_desc->BufPtr, virt_desc->NextPtr, virt_desc->length, virt_desc->size, virt_desc->vuc, virt_desc->ei, virt_desc->rsvd1, virt_desc->EOM, virt_desc->OWN); virt_desc = ipq_desc_ptr(pmbox_dma, virt_desc, 1); phys_desc = ipq_desc_ptr(pmbox_dma, phys_desc, 1); } if (_ptxt) { *_ptxt = ptxt; } return txt_size; } /** */ static unsigned int snprintf_dump_slots(char **_ptxt, unsigned int txt_size, struct _mbox_dma *pmbox_dma) { char *ptxt = NULL; unsigned int i; if (pmbox_dma->irq_active < irq_initialized) { return txt_size; } if (_ptxt) { ptxt = *_ptxt; } for (i = 0; i < ARRAY_SIZE(pmbox_dma->virt_buf); i++) { unsigned short *p = (unsigned short *)pmbox_dma->virt_buf[i]; if (p) { snprintf_add(ptxt, txt_size, "\t[%lu]%cx[%u] 0x%p:%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n", pmbox_dma->irq_cnt, pmbox_dma->dir == CAPTURE ? 'R' : 'T', i, p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); } } if (_ptxt) { *_ptxt = ptxt; } return txt_size; } /** */ static unsigned int snprint_all_register(char **_ptxt, unsigned int txt_size, unsigned int id_mask) { char txt[256]; char *ptxt = NULL; unsigned int i, id; if (_ptxt) { ptxt = *_ptxt; } for (i = 0; i < ARRAY_SIZE(gReg); i++) { const struct _debug_register_tab *preg = &gReg[i]; if ((preg->trace_flag & trace_mode) == 0) { continue; } if (!is_IPQ4019() && (preg->trace_flag & trace_ipq4019)) { continue; } if (preg->read) { unsigned int val = preg->read(preg->offset, NULL); snprint_register_bitformat(txt, sizeof(txt), val, preg->bitformat); snprintf_add(ptxt, txt_size, "%-50s:%s\n", preg->regname, txt); } } for (id = 0; id < ARRAY_SIZE(stereo_base); id++) { if ((id_mask & (1 << id)) == 0) { continue; } if (stereo_base[id] == NULL) { continue; } for (i = 0; i < ARRAY_SIZE(gReg); i++) { const struct _debug_register_tab *preg = &gReg[i]; if ((preg->trace_flag & trace_mode) == 0) { continue; } if (!is_IPQ4019() && (preg->trace_flag & trace_ipq4019)) { continue; } if (preg->stereo_read) { unsigned int val = preg->stereo_read(id, preg->offset, NULL); snprint_register_bitformat(txt, sizeof(txt), val, preg->bitformat); snprintf_add(ptxt, txt_size, "%-47s[%u]:%s\n", preg->regname, id, txt); } } } for (id = 0; id < ARRAY_SIZE(mbox_data); id++) { if ((id_mask & (1 << id)) == 0) { continue; } if (mbox_data[id].reg_base == NULL) { continue; } snprintf_add(ptxt, txt_size, "MBOX[%u]%s\n", id, mbox_dir_name(id)); for (i = 0; i < ARRAY_SIZE(gReg); i++) { const struct _debug_register_tab *preg = &gReg[i]; if ((preg->trace_flag & trace_mode) == 0) { continue; } if (!is_IPQ4019() && (preg->trace_flag & trace_ipq4019)) { continue; } if (preg->_mbox_read) { unsigned int val = preg->_mbox_read(id, preg->offset, NULL); snprint_register_bitformat(txt, sizeof(txt), val, preg->bitformat); snprintf_add(ptxt, txt_size, "%-47s[%u]:%s\n", preg->regname, id, txt); } } } if (_ptxt) { *_ptxt = ptxt; } return txt_size; } /** */ static void write_register(char *name, unsigned int val, unsigned int id) { unsigned int i; enum _trace_mode old_trace_mode; /*--- pr_info("%s: '%s' val=%u id=%d\n", __func__, name, val, id); ---*/ for (i = 0; i < ARRAY_SIZE(gReg); i++) { const struct _debug_register_tab *preg = &gReg[i]; if (strstr(name, preg->regname)) { old_trace_mode = trace_mode; trace_mode = preg->trace_flag | trace_enh; if (!is_IPQ4019() && (preg->trace_flag & trace_ipq4019)) { /*--- do nothing ---*/ } else if (preg->write) { preg->write(val, preg->offset, preg->regname); } else if (preg->_mbox_write) { preg->_mbox_write(id, val, preg->offset, preg->regname); } else if (preg->stereo_write) { preg->stereo_write(id, val, preg->offset, preg->regname); } trace_mode = old_trace_mode; return; } } } #define SKIP_UNTIL_SPACE(a) { while (*(a) && *(a) != ' ' && *(a) != '\t') (a)++; } /** */ const char *cpy_name(char *name, int name_len, const char *cmd) { const char *cmd_end; int len; SKIP_SPACE(cmd); cmd_end = cmd; SKIP_UNTIL_SPACE(cmd_end); len = min(name_len - 1, cmd_end - cmd); if (len == 0) { return NULL; } memcpy(name, cmd, len); name[len] = 0; return cmd_end; } static unsigned short testpacket[] = { 0xffff, 0x0005, 0x0aff, 0x0002, 0x0214, 0xf600, 0x010e, 0xffff }; static unsigned int send_test_packet = 0xFF; static unsigned int packet_offset = 0x1; /** */ static unsigned short send_testpacket(void) { if (send_test_packet < ARRAY_SIZE(testpacket)) { return testpacket[send_test_packet++]; } return 0; } static unsigned int glb_mclk, glb_bclk, glb_master = 0, glb_extclk = 1; static unsigned int glb_syncslots; static unsigned int testval = 0x80A0; static unsigned int testval_offset = 4; static unsigned int testval_rx_resize; static unsigned int testval_tx_resize; static unsigned int only_rx_irq; static unsigned int start_tdm; /** */ int TestTxData(void *refhandle, void *buf) { unsigned int i; static unsigned int start_j; struct _mbox_dma *pmbox_dma = (struct _mbox_dma *)refhandle; unsigned short *p = (unsigned short *)buf; unsigned int resize = testval_tx_resize; for (i = 0; i < pmbox_dma->size / sizeof(short); i++) { *p = 0; if (glb_syncslots) { *p = (glb_syncslots | ((i % pmbox_dma->slots) << 4)) & 0xFFFF; } else if ((i % pmbox_dma->slots) == testval_offset) { *p = testval & 0xFFFF; } else if ((i % pmbox_dma->slots) == packet_offset) { *p = send_testpacket(); } p++; } p = (unsigned short *)buf; if (jiffies - start_j > 5 * HZ) { unsigned short *p = (unsigned short *)buf; pr_info("tx:%p(%x) (irqs=%lu/err=%u udr=%u ovr=%u):\n" "%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x " "%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n", buf, virt_to_phys(buf), pmbox_dma->irq_cnt, pmbox_dma->err_cnt, pmbox_dma->own_udr, pmbox_dma->own_ovr, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15], p[16], p[17], p[18], p[19], p[20], p[21], p[22], p[23], p[24], p[25], p[26], p[27], p[28], p[29], p[30], p[31]); start_j = jiffies; } testval_tx_resize = 0; return resize; } /** */ int TestRxData(void *refhandle, void *buf) { static unsigned int start_j; unsigned int i; unsigned char *p = buf; struct _mbox_dma *pmbox_dma = (struct _mbox_dma *)refhandle; unsigned int resize = testval_rx_resize; if (jiffies - start_j > 5 * HZ) { unsigned short *p = (unsigned short *)buf; pr_info("rx:%p(%x) (irqs=%lu/err=%u udr=%u ovr=%u):\n" "%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x " "%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n", buf, virt_to_phys(buf), pmbox_dma->irq_cnt, pmbox_dma->err_cnt, pmbox_dma->own_udr, pmbox_dma->own_ovr, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15], p[16], p[17], p[18], p[19], p[20], p[21], p[22], p[23], p[24], p[25], p[26], p[27], p[28], p[29], p[30], p[31]); start_j = jiffies; } for (i = 0; i < pmbox_dma->size; i++) { if (p[i] == 0xAA) { printk_ratelimited("preamble found: ----rx: %*ph\n", pmbox_dma->size - i, &p[i]); break; } } testval_rx_resize = 0; return resize; } /** */ static int debugcmd_startup_tdm(unsigned int bclk, unsigned int mclk, unsigned int slots) { int res = 0; unsigned int rxdelay = 1, txdelay = 1; if (tdm_config.tdm_if_api) { pr_info("tdm-interface locked\n"); return 1; } if (slots == 0) { slots = 8; } if (mclk == 0) { bclk = 1024 * 1000; mclk = bclk * 4; } else { mclk *= 1000; bclk *= 1000; } glb_bclk = bclk, glb_mclk = mclk; if (glb_master) { rxdelay = 0; txdelay = 0; } adss_config_clk(glb_extclk, bclk, mclk); adss_glb_tdm_ctrl_ch_num(slots - 1); adss_glb_clk_enable_oe(glb_master, CAPTURE); adss_glb_clk_enable_oe(glb_master, PLAYBACK); adss_glb_rxtx_data_port_en(1); adss_glb_rxtx_framesync_port_en(1); adss_glb_tdm_mode(); adss_config_tdm(DISABLE, glb_master, glb_extclk); adss_glb_tdm_ctrl_sync_num(0, CAPTURE); adss_glb_tdm_ctrl_sync_num(0, PLAYBACK); adss_glb_tdm_ctrl_delay(rxdelay, CAPTURE); adss_glb_tdm_ctrl_delay(txdelay, PLAYBACK); res = mbox_dma_init(&mbox_dma[PLAYBACK], &mbox_dma[PLAYBACK], TestTxData, &tdm_priv.pdev->dev, tdm_priv.stereo_tx, PLAYBACK, only_rx_irq ? -1 : mbox_data[tdm_priv.stereo_tx].irq, -1, slots, 8 * 4, 8); res |= mbox_dma_init(&mbox_dma[CAPTURE], &mbox_dma[CAPTURE], TestRxData, &tdm_priv.pdev->dev, tdm_priv.stereo_rx, CAPTURE, mbox_data[tdm_priv.stereo_rx].irq, -1, slots, 8 * 4, 8); if (only_rx_irq) { mbox_dma[CAPTURE].irqcontext_mbox = &mbox_dma[PLAYBACK]; } if (res) { mbox_dma_exit(&mbox_dma[PLAYBACK]); mbox_dma_exit(&mbox_dma[CAPTURE]); } else { unsigned long flags; enum _trace_mode old_trace_mode; mbox_dma_prepare_desc(&mbox_dma[PLAYBACK]); mbox_dma_prepare_reg(&mbox_dma[PLAYBACK]); mbox_dma_prepare_desc(&mbox_dma[CAPTURE]); mbox_dma_prepare_reg(&mbox_dma[CAPTURE]); avm_rte_local_irq_save(flags); old_trace_mode = trace_mode; trace_mode = 0; mbox_dma_start(&mbox_dma[PLAYBACK]); mbox_dma_start(&mbox_dma[CAPTURE]); trace_mode = old_trace_mode; avm_rte_local_irq_restore(flags); } return res; } /** */ static void debugcmd_shutdown_tdm(void) { if (tdm_config.tdm_if_api) { pr_info("tdm-interface locked\n"); return; } mbox_dma_stop(&mbox_dma[PLAYBACK]); mbox_dma_stop(&mbox_dma[CAPTURE]); mbox_dma_exit(&mbox_dma[PLAYBACK]); mbox_dma_exit(&mbox_dma[CAPTURE]); adss_glb_tdm_ctrl_ch_num(0); adss_glb_clk_enable_oe(0, CAPTURE); adss_glb_clk_enable_oe(0, PLAYBACK); adss_glb_rxtx_data_port_en(0); adss_glb_rxtx_framesync_port_en(0); } static const struct _debugcmd_profile debug_cmd[] = { { .cmd = "print", .type = 'p', .argc = 0, .ashex = 0, .help = NULL }, { .cmd = "clk", .type = 'C', .argc = 3, .ashex = 0, .help = " " }, { .cmd = "master", .type = 'M', .argc = 1, .ashex = 0, .help = "<0|1>" }, { .cmd = "ext", .type = 'E', .argc = 1, .ashex = 0, .help = "<0|1>" }, { .cmd = "start", .type = 'S', .argc = 3, .ashex = 0, .help = " " }, { .cmd = "stop", .type = 'H', .argc = 0, .ashex = 0, .help = NULL }, { .cmd = "txval", .type = 'T', .argc = 2, .ashex = 1, .help = "txval " }, { .cmd = "sync", .type = 'X', .argc = 0, .ashex = 1, .help = "[] val != 0: set all slots to: | slot << 4)" }, { .cmd = "resize", .type = 'R', .argc = 2, .ashex = 1, .help = " " }, { .cmd = "irq", .type = 'I', .argc = 0, .ashex = 0, .help = "irq-mode (toggle shared/normal)" }, { .cmd = "wr", .type = 'W', .argc = 0, .ashex = 0, .help = " " }, { .cmd = "packet", .type = 'P', .argc = 1, .ashex = 1, .help = "" }, { .cmd = "reset", .type = 'r', .argc = 0, .ashex = 0, .help = NULL }, { .cmd = NULL, .type = 0, .argc = 0, .ashex = 0, .help = NULL }, /* EOF */ }; /** */ static void cmdlineparse(char *cmdline, void *dummy __attribute__((unused))) { char name[64]; unsigned int arg[3], scanned; const char *p = cmdline; int type, i; memset(&arg, 0x0, sizeof(arg)); type = debugcmd_get_param(debug_cmd, &p, arg, ARRAY_SIZE(arg), &scanned); switch (type) { case 'p': /* print */ snprint_all_register(NULL, 0, 0xff); for (i = 0; i < ARRAY_SIZE(mbox_dma); i++) { snprintf_dump_descriptors(NULL, 0, &mbox_dma[i]); snprintf_dump_slots(NULL, 0, &mbox_dma[i]); } break; case 'C': /* clk */ if (scanned == 3) { adss_set_clk_rate(arg[0], arg[1], arg[2]); } break; case 'M': /* master */ if (scanned) { glb_master = arg[0]; pr_info("set master=%u\n", glb_master); } break; case 'E': /* ext */ if (scanned) { glb_extclk = arg[0]; pr_info("set extclk=%u\n", glb_extclk); } break; case 'X': /* sync */ if (scanned) { glb_syncslots = arg[0]; } else if (glb_syncslots) { glb_syncslots = 0; } else { glb_syncslots = 0x8001; } pr_info("%sset slot-sync-mode (%x)\n", glb_syncslots ? "" : "un", glb_syncslots); break; case 'H': /* stop */ case 'S': /* start */ if (start_tdm) { debugcmd_shutdown_tdm(); start_tdm = 0; } if (type == 'S') { if (debugcmd_startup_tdm(arg[0], arg[1], arg[2]) == 0) { start_tdm = 1; } } break; case 'T': /* txval */ if (scanned) { testval = arg[0]; testval_offset = arg[1]; glb_syncslots = 0; } break; case 'R': /* resize */ if (scanned) { testval_tx_resize = arg[0]; testval_rx_resize = arg[1]; #if defined(DBG_TDM_RESIZE_CHECK) mbox_dma[PLAYBACK].dbg_force_reoffset = arg[0]; mbox_dma[CAPTURE].dbg_force_reoffset = arg[1]; pr_info("set tx_resize=%u rx_resize=%u\n", arg[0], arg[1]); #else if (start_tdm) pr_info("set tx_resize=%u rx_resize=%u\n", arg[0], arg[1]); #endif } break; case 'I': /* irq */ only_rx_irq = !only_rx_irq; pr_err("%s irq-mode\n", only_rx_irq ? "shared" : "normal"); break; case 'W': /* wr */ SKIP_UNTIL_SPACE(p); p = cpy_name(name, sizeof(name), p); scanned = debugcmd_scan_args(p, arg, 2, 1); if (scanned) { write_register(name, arg[0], arg[1]); } break; case 'P': /* packet */ if (scanned) { packet_offset = arg[0]; } pr_err("send packet: offset=%u\n", packet_offset); send_test_packet = 0; break; case 'r': /* tdm-reset */ workarround_reset_tdm_interface(); break; } } /** */ static struct platform_driver qca_pcm_avm_driver = { .remove = qca_pcm_avm_remove, .probe = qca_pcm_avm_probe, .driver = { .name = "qca-pcm-avm", .of_match_table = pcm_avm_id_table, }, }; module_platform_driver(qca_pcm_avm_driver); MODULE_DESCRIPTION(DRV_NAME ": qca avm pcm interface"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:qca_pcm_avm");