#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 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 char *print_bitformat(char *erg, int erg_len, unsigned int value, const char *bitformat); static unsigned int print_all_register(char **txt, unsigned int tx_size, unsigned int id_mask); #define MBOX_PHYS_DMA_ADRESS(a) (((dma_addr_t)(a)) & 0xfffffff) /*--- #define DBG_REGISTER ---*/ /*--- #define DBG_TRC_API(args...) printk(KERN_INFO args) ---*/ #define DBG_TRC_API(args...) /*--- #define DBG_TRC(args...) printk(KERN_INFO args) ---*/ #define DBG_TRC(args...) /*--- #define DBG_TRC_DESC(args...) printk(KERN_INFO args) ---*/ #define DBG_TRC_DESC(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 */ BufPtr : 28, /* bit 27-00 */ rsvd2 : 4, /* bit 31-28 */ NextPtr: 28, /* bit 27-00 */ rsvd3 : 4; /* bit 31-28 */ unsigned int vuc_dword[36]; }; struct _mbox_dma { /*--- dma-alloc-stuff ---*/ dma_addr_t phys_desc; dma_addr_t phys_buf[2]; /* Desc array in virtual space */ struct _mbox_desc *virt_desc; unsigned int ndescs; unsigned char *virt_buf[2]; struct device *dev; unsigned long status; uint32_t err_stats; unsigned int irq; 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 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 ---*/ unsigned int (*RxTxData)(void *refhandle, void *p); } 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_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 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); #define printf_add(ptxt, txtlen, args...) if(ptxt){ int local_add_len;\ if((local_add_len = snprintf(ptxt, txtlen, args)) > 0) { \ int tail = min((int)txtlen, local_add_len); \ (ptxt) += tail, (txtlen) -= tail; \ } \ } else printk(KERN_ERR args) #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) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ 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)) { 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)) { 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)) { 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)) { 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 } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ 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, "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)"), 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)SPDIF_OUT_OE(10)I2S3_RXD_OE(9)I2S3_FS_OE(8)I2S0_FS_OE(7)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, NULL), GLB_IO(ADSS_AUDIO_PLL_CONFIG_REG, trace_audio, "POSTPLLDIV(7,9)PLL_PWD(5)REFDIV(0,2)"), GLB_IO(ADSS_AUDIO_PLL_MODULATION_REG, trace_audio, "INT_DIV(1,10)FRAC_DIV(11,29)"), GLB_IO(ADSS_AUDIO_PLL_MOD_STEP_REG, trace_audio, NULL), GLB_IO(ADSS_CURRENT_AUDIO_PLL_MODULATION_REG, trace_audio, NULL), GLB_IO(ADSS_AUDIO_PLL_CONFIG1_REG, trace_audio, NULL), GLB_IO(ADSS_AUDIO_ATB_SETTING_REG, trace_audio, NULL), 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, "ENABLE(0)"), GLB_IO(ADSS_AUDIO_RXM_CMD_RCGR_REG, trace_audio, "ROOT_EN(1)UPDATE(0)"), GLB_IO(ADSS_AUDIO_RXM_CFG_RCGR_REG, trace_audio, "M_RDIV(0,7)SRC_SEL(8,10)"), GLB_IO(ADSS_AUDIO_RXM_MISC_REG, trace_audio, "M_MDIV(4,31)"), GLB_IO(ADSS_AUDIO_RXM_CBCR_REG, trace_audio, "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, "ENABLE(0)"), GLB_IO(ADSS_AUDIO_TXM_CMD_RCGR_REG, trace_audio, "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, "ENABLE(0)"), GLB_IO(ADSS_AUDIO_SAMPLE_CBCR_REG, trace_audio, "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)" ), 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, 0, NULL), MBOX_IO(ADSS_MBOXn_MBOXn_DMA_TX_DESCRIPTOR_BASE_REG, trace_mbox, NULL), MBOX_IO(ADSS_MBOXn_MBOXn_DMA_TX_CONTROL_REG, 0, NULL), 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, "TX_FIFO_OVERFLOW(13)RX_FIFO_UNDERFLOW(12)RX_DMA_COMPLETE(10)TX_DMA_COMPLETE(6)TX_OVERFLOW(5)RX_UNDERFLOW(4)"), MBOX_IO(ADSS_MBOXn_MBOX_INT_ENABLE_REG, trace_mbox, "RX_DMA_COMPLETE(10)TX_DMA_COMPLETE(6)"), MBOX_IO(ADSS_MBOXn_MBOX_FIFO_RESET_REG, 0, NULL), }; #if defined(DBG_REGISTER) #define DBG_TRC_REG(args...) printk(KERN_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) { printk(KERN_ERR"%s *(%s%c+0x%x) = 0x%x\n", regname, basename, id < 0 ? ' ' : id +'0', offset, read_val); } else { printk(KERN_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) { print_bitformat(txt1, sizeof(txt1), read_val, preg ? preg->bitformat : NULL); printk(KERN_ERR"%s:(%s%c+0x%02x)%s\n", regname, basename, id < 0 ? ' ' : id +'0', offset, txt1); } else { print_bitformat(txt1, sizeof(txt1), towrite, preg ? preg->bitformat : NULL); print_bitformat(txt2, sizeof(txt2), read_val, preg ? preg->bitformat : NULL); printk(KERN_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, intf_pcm, intf_mbox, intf_tdm, intf_stereo, }; /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ static const struct of_device_id pcm_avm_id_table[] = { { .compatible = "qca,ipq4019-audio-adss", .data = (void *)intf_audio_adss }, { .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 }, {}, }; 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); msleep(5); 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)) { printk(KERN_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)) { printk(KERN_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_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)) { printk(KERN_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)) { printk(KERN_ERR"%s: error reading critical device node properties\n", np->name); return -EINVAL; } if (of_property_read_u32(np, "dma-rx-channel", &rx_channel)) { printk(KERN_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 void mbox_interrupt_ack(int dma_id, unsigned int status, unsigned int mask) { status &= ~mask; _mbox_write(dma_id, status, ADSS_MBOXn_MBOX_INT_STATUS_REG, NULL); } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ static inline void mbox_ack_desc(struct _mbox_dma *pmbox_dma) { unsigned int i, own_cnt = 0; struct _mbox_desc *desc; desc = pmbox_dma->virt_desc; for (i = 0; i < pmbox_dma->ndescs; i++) { if (desc->OWN == 0) { unsigned int index = MBOX_PHYS_DMA_ADRESS(pmbox_dma->phys_buf[0]) == desc->BufPtr ? 0 : 1; unsigned int resize = 0; if(pmbox_dma->RxTxData && (own_cnt == 0)) { resize = pmbox_dma->RxTxData(pmbox_dma->refhandle, pmbox_dma->virt_buf[index]); } desc->size = pmbox_dma->size - resize; desc->length = desc->size; desc->OWN = 1; desc->ei = 1; own_cnt++; } desc += 1; } if(own_cnt > 1) { pmbox_dma->own_ovr += own_cnt - 1; } else if(own_cnt == 0) { pmbox_dma->own_udr++; } } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ 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; } 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; } if ((status & MBOX_INT_STATUS_RX_UNDERFLOW) || (status & MBOX_INT_STATUS_RX_FIFO_UNDERFLOW)) { mbox_interrupt_ack(dma_id, status, status & (MBOX_INT_STATUS_RX_UNDERFLOW | 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)) { mbox_interrupt_ack(dma_id, status, status & (MBOX_INT_STATUS_TX_OVERFLOW | MBOX_INT_STATUS_TX_FIFO_OVERFLOW)); pmbox_dma->err_cnt++; ret = IRQ_HANDLED; } mbox_interrupt_ack(dma_id, status, status); if(pmbox_dma->irqcontext_mbox) { return mbox_dma_irq(irq, (void *)pmbox_dma->irqcontext_mbox); } 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); msleep(2); 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, unsigned 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 i; unsigned int buf_size; int rc = 0; if(pmbox_dma->irq_active > irq_uninitialized) { return -1; } memset(pmbox_dma, 0, sizeof(struct _mbox_dma)); buf_size = slots * sizeof(short) * fs_per_dma; DBG_TRC("%s: dma_id=%d, dir=%d(%s), irq=%d cpu=%d buf_size=%u\n", __func__, dma_id, dir, dir == PLAYBACK ? "tx" : dir == CAPTURE ? "rx" : "?", irq, cpu, buf_size ); pmbox_dma->slots = slots; pmbox_dma->irq = irq; pmbox_dma->dma_id = dma_id; pmbox_dma->dir = dir; pmbox_dma->dev = dev; pmbox_dma->ndescs = n_desc; pmbox_dma->size = buf_size; pmbox_dma->refhandle = refhandle; pmbox_dma->RxTxData = RxTxData; pmbox_dma->virt_desc = dma_alloc_coherent(dev, pmbox_dma->ndescs * sizeof(struct _mbox_desc), &pmbox_dma->phys_desc, GFP_KERNEL); if (!pmbox_dma->virt_desc) { printk(KERN_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] = dma_alloc_coherent(dev, pmbox_dma->size, &pmbox_dma->phys_buf[i], GFP_KERNEL); if (!pmbox_dma->virt_buf[0]) { printk(KERN_ERR"%s: no dma-memory\n", __func__); mbox_dma_exit(pmbox_dma); return -ENOMEM; } } mbox_dma_policy_reset(dma_id); if(irq >= 0) { if(cpu < 0) cpu = raw_smp_processor_id(); rc = request_irq_on(cpu, irq, mbox_dma_irq, 0, dir == CAPTURE ? "ipq40xx-mbox-rx" : "ipq40xx-mbox-tx", (void *)pmbox_dma); } if(rc == 0) { pmbox_dma->irq_active = irq_initialized; } else { printk(KERN_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->virt_desc) { dma_free_coherent(pmbox_dma->dev, pmbox_dma->ndescs * sizeof(struct _mbox_desc), pmbox_dma->virt_desc, pmbox_dma->phys_desc); pmbox_dma->virt_desc = NULL; } for(i = 0; i < ARRAY_SIZE(pmbox_dma->virt_buf); i++) { if (pmbox_dma->virt_buf[i]) { dma_free_coherent(pmbox_dma->dev, pmbox_dma->size, pmbox_dma->virt_buf[i], pmbox_dma->phys_buf[i]); pmbox_dma->virt_desc = NULL; } } if(pmbox_dma->irq_active) { if(pmbox_dma->irq >= 0) { free_irq(pmbox_dma->irq, pmbox_dma); } pmbox_dma->irq_active = irq_uninitialized; } } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ 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. */ msleep(10); adss_stereo_config_enable(pmbox_dma, DISABLE); } /**--------------------------------------------------------------------------------**\ * 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 ---*/ bmisc_cfg = (0 << 1); /*--- B_MDIV ---*/ mmisc_cfg = (0 << 4); /*--- M_MDIV ---*/ xm_cfg = (2 << 8) | 0; /*--- M_SRC_SEL | M_RDIV ---*/ } 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); msleep(1); 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); msleep(1); 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 mbox_dma_prepare_desc(struct _mbox_dma *pmbox_dma) { struct _mbox_desc *virt_desc, *phys_desc; unsigned int i; 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->rsvd2 = virt_desc->rsvd3 = virt_desc->EOM = 0; virt_desc->BufPtr = MBOX_PHYS_DMA_ADRESS(pmbox_dma->phys_buf[i & 1]); virt_desc->NextPtr = MBOX_PHYS_DMA_ADRESS(&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_ADRESS(virt_desc->BufPtr), virt_desc->size, virt_desc->length, virt_desc->NextPtr); virt_desc += 1; } pmbox_dma->irq_cnt = 0; pmbox_dma->err_cnt = 0; pmbox_dma->own_udr = 0; pmbox_dma->own_ovr = 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_config_reset(pmbox_dma, ENABLE); msleep(5); mbox_write(pmbox_dma->dma_id, MBOX_FIFO_RESET_TX_INIT | MBOX_FIFO_RESET_TX_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); val &= ~ADSS_MBOX_DMA_POLICY_TX_FIFO_THRESHOLD(0xF); val &= ~0xF; /*---- ? ----*/ val |= ADSS_MBOX_DMA_POLICY_TX_FIFO_THRESHOLD(6); val |= 0xA; /*---- ? ----*/ if(pmbox_dma->dir == PLAYBACK) { /* Request the DMA channel to the controller */ val |= MBOX_DMA_POLICY_RX_INT_TYPE; mbox_write(pmbox_dma->dma_id, val, ADSS_MBOXn_MBOX_DMA_POLICY_REG); val = mbox_read(pmbox_dma->dma_id, ADSS_MBOXn_MBOX_DMA_POLICY_REG); mbox_write(pmbox_dma->dma_id, val | ADSS_MBOX_DMA_POLICY_SRAM_AC((unsigned long)pmbox_dma->phys_desc), ADSS_MBOXn_MBOX_DMA_POLICY_REG); mbox_write(pmbox_dma->dma_id, MBOX_PHYS_DMA_ADRESS(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 |= MBOX_DMA_POLICY_TX_INT_TYPE; mbox_write(pmbox_dma->dma_id, val, ADSS_MBOXn_MBOX_DMA_POLICY_REG); val = mbox_read(pmbox_dma->dma_id, ADSS_MBOXn_MBOX_DMA_POLICY_REG); mbox_write(pmbox_dma->dma_id, val | ADSS_MBOX_DMA_POLICY_SRAM_AC((unsigned long)pmbox_dma->phys_desc), ADSS_MBOXn_MBOX_DMA_POLICY_REG); mbox_write(pmbox_dma->dma_id, MBOX_PHYS_DMA_ADRESS(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_bclk) || IS_ERR(tdm_priv.audio_tx_bclk) || IS_ERR(tdm_priv.audio_tx_bclk)) { pr_err("%s error on get clock\n", __func__); } else { tdm_priv.pdev = pdev; printk(KERN_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); printk(KERN_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) { printk(KERN_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: ret = intf_audio_adss_probe(pdev); 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; } 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) { printk(KERN_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: 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); printk(KERN_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; } return ret; /*--- ret = clk_prepare_enable(pcm_clk); ---*/ } /**--------------------------------------------------------------------------------**\ 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 &= ~(1); cfg |= GLB_AUDIO_MODE_XMIT_TDM; cfg &= ~(4); 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); } /**--------------------------------------------------------------------------------**\ 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) { uint32_t cfg; DBG_TRC("%s(%u)\n", __func__, enable); cfg = adss_audio_read(ADSS_GLB_AUDIO_MODE_REG); cfg &= ~(GLB_AUDIO_MODE_I2S0_TXD_OE | GLB_AUDIO_MODE_I2S3_RXD_OE); if(enable) { cfg |= (GLB_AUDIO_MODE_I2S0_TXD_OE | GLB_AUDIO_MODE_I2S3_RXD_OE); } adss_audio_write(cfg, ADSS_GLB_AUDIO_MODE_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) { uint32_t cfg; DBG_TRC("%s(%u)\n", __func__, enable); cfg = adss_audio_read(ADSS_GLB_AUDIO_MODE_REG); cfg &= ~(GLB_AUDIO_MODE_I2S0_FS_OE | GLB_AUDIO_MODE_I2S3_FS_OE); if(enable) { cfg |= (GLB_AUDIO_MODE_I2S0_FS_OE | GLB_AUDIO_MODE_I2S3_FS_OE); } adss_audio_write(cfg, ADSS_GLB_AUDIO_MODE_REG); } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ static void adss_glb_clk_enable_oe(int enable) { 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); mask = (GLB_CLK_I2S_CTRL_TX_BCLK_OE | GLB_CLK_I2S_CTRL_TX_MCLK_OE) | (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); msleep(5); } 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(GLB_I2S_RESET_VAL_4019, ADSS_GLB_I2S_RST_REG); msleep(5); adss_audio_write(0x0, ADSS_GLB_I2S_RST_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(uint32_t enable, unsigned int master, unsigned int ext_clk, unsigned int bclk, unsigned int mclk) { uint32_t cfg; unsigned int reset_mask, cfg_mode; DBG_TRC("%s(ena=%u, master=%u ext_clk=%u bclk=%u mclk=%u)", __func__, enable, master, ext_clk, bclk, mclk); adss_glb_i2s_reset(); 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); msleep(5); /*--- 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); msleep(5); } if(adss_audio_read(ADSS_AUDIO_PLL_MODULATION_REG) != 0x04dd3062) { adss_audio_write(0x04dd3062, ADSS_AUDIO_PLL_MODULATION_REG); msleep(5); } } msleep(10); } else { adss_config_clksel(2); } 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 unsigned int tdm_if_api = 0; /**--------------------------------------------------------------------------------**\ * TDM-IF-API * Support nur Master-Mode \**--------------------------------------------------------------------------------**/ int tdm_if_config_init(unsigned int slots, unsigned int rxdelay, unsigned int txdelay) { 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; } tdm_if_api = 1; DBG_TRC_API("%s(slots=%u, rxdelay=%u txdelay=%u)\n", __func__, slots, rxdelay, txdelay); adss_config_clk(ENABLE, 0, 1, 0, 0); adss_glb_tdm_ctrl_ch_num(slots - 1); adss_glb_clk_enable_oe(0); adss_glb_rxtx_data_port_en(1); adss_glb_rxtx_framesync_port_en(0); adss_glb_tdm_mode(); 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); return 0; } EXPORT_SYMBOL(tdm_if_config_init); /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ void tdm_if_config_exit(void) { DBG_TRC_API("%s()\n", __func__); tdm_if_api = 0; adss_glb_tdm_ctrl_ch_num(0); adss_glb_clk_enable_oe(0); adss_glb_clk_enable_oe(0); adss_glb_rxtx_data_port_en(0); adss_glb_rxtx_framesync_port_en(0); } EXPORT_SYMBOL(tdm_if_config_exit); /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ int tdm_if_dma_init(void *refhandle, unsigned int (*TxData)(void *refhandle, void *buf), unsigned 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; } EXPORT_SYMBOL(tdm_if_dma_init); /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ void tdm_if_dma_start(void) { unsigned long flags; DBG_TRC_API("%s()\n", __func__); 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]); local_irq_save(flags); /*--- sehr zeitnah starten, damit dma-buffer zur gleichen Zeit fertig! ---*/ mbox_dma_start(&mbox_dma[PLAYBACK]); mbox_dma_start(&mbox_dma[CAPTURE]); local_irq_restore(flags); } EXPORT_SYMBOL(tdm_if_dma_start); /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ void tdm_if_dma_stop(void) { DBG_TRC_API("%s()\n", __func__); mbox_dma_stop(&mbox_dma[PLAYBACK]); mbox_dma_stop(&mbox_dma[CAPTURE]); } EXPORT_SYMBOL(tdm_if_dma_stop); /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ void tdm_if_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]); } EXPORT_SYMBOL(tdm_if_dma_exit); /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ void tdm_if_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; } EXPORT_SYMBOL(tdm_if_dma_irqcnt); /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ void tdm_if_dma_irqcnt_reset(void) { mbox_dma[PLAYBACK].irq_cnt = 0; mbox_dma[CAPTURE].irq_cnt = 0; } EXPORT_SYMBOL(tdm_if_dma_irqcnt_reset); /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ int tdm_if_dma_get_dma_buffer(unsigned char **rx1, unsigned char **rx2, unsigned char **tx1, unsigned char **tx2, unsigned int *slotoffset){ struct _mbox_dma *pmbox_dma; pmbox_dma = &mbox_dma[CAPTURE]; if(pmbox_dma->irq_active == irq_uninitialized) { return 1; } *rx1 = pmbox_dma->virt_buf[0]; *rx2 = pmbox_dma->virt_buf[1]; pmbox_dma = &mbox_dma[PLAYBACK]; if(pmbox_dma->irq_active == irq_uninitialized) { return 1; } *tx1 = pmbox_dma->virt_buf[0]; *tx2 = pmbox_dma->virt_buf[1]; *slotoffset = pmbox_dma->slots; DBG_TRC_API("%s(rx %p/%p tx %p/%p slots=%u)\n", __func__, *rx1, *rx2, *tx1, *tx2, *slotoffset); return 0; } EXPORT_SYMBOL(tdm_if_dma_get_dma_buffer); /**--------------------------------------------------------------------------------**\ * txt: NULL -> gleich printk \**--------------------------------------------------------------------------------**/ void tdm_if_print_status(const char *prefix, char *txt, unsigned int txt_size) { struct _mbox_dma *pmbox_dma; pmbox_dma = &mbox_dma[CAPTURE]; printf_add(txt, txt_size, "%s\n", prefix); printf_add(txt, txt_size, "Rx(%s):Irqs=%lu ErrCnt=%u OWN-UDR=%u OWN-OVR=%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 = &mbox_dma[PLAYBACK]; printf_add(txt, txt_size, "Tx(%s):Irqs=%lu ErrCnt=%u OWN-UDR=%u OWN-OVR=%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); print_all_register(txt ? &txt : NULL, txt_size, (1 << 0) | (1 << 3)); } EXPORT_SYMBOL(tdm_if_print_status); #define SKIP_UNTIL_CHAR(a, character) while(*(a) && *(a) != character) (a)++ #define SKIP_SPACE(a) while(*(a) && (*(a) == ' ' || *(a) == '\t')) (a)++ /**--------------------------------------------------------------------------------**\ * format ([~],)... \**--------------------------------------------------------------------------------**/ static const char *extract_param(const char *bitformat, char *name, int name_len, unsigned int *start_bit, unsigned int *end_bit, unsigned int *neg) { const char *p, *end_seperate; int len; if(bitformat == NULL) { return NULL; } SKIP_SPACE(bitformat); p = bitformat; end_seperate = p; SKIP_UNTIL_CHAR(end_seperate, ')'); if(*end_seperate != ')') { return NULL; } SKIP_UNTIL_CHAR(p, '('); if(p > end_seperate || *p != '(') { return NULL; } len = min(p - bitformat, name_len - 1); if(len) { memcpy(name, bitformat, len); name[len] = 0; } p++; SKIP_SPACE(p); if(*p == '~') { *neg = 1; p++; } else { *neg = 0; } sscanf(p, "%u", start_bit); SKIP_UNTIL_CHAR(p, ','); if(p > end_seperate) { *end_bit = *start_bit; } else { p++; SKIP_SPACE(p); sscanf(p, "%u", end_bit); if(*end_bit < *start_bit) { unsigned int tmp; tmp = *end_bit; *end_bit = *start_bit; *start_bit = tmp; } } return end_seperate + 1; } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ static char *print_bitformat(char *erg, int erg_len, unsigned int value, const char *bitformat) { unsigned int start_bit, end_bit, neg; char *start = erg; char name[64]; printf_add(erg, erg_len, "0x%08x", value); while((bitformat = extract_param(bitformat, name, sizeof(name), &start_bit, &end_bit, &neg))) { /*--- printk(KERN_INFO"%s %u %u %u\n", name, start_bit, end_bit, neg); ---*/ if(end_bit == start_bit) { if((value & (1 << start_bit)) == ((!neg) << start_bit)) { printf_add(erg, erg_len, " %s", name); } } else { printf_add(erg, erg_len, " %s=0x%x", name, (value >> start_bit) & ((1 << (end_bit - start_bit + 1)) - 1)); } } return start; } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ static unsigned int print_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(preg->read) { unsigned int val = preg->read(preg->offset, NULL); print_bitformat(txt, sizeof(txt), val, preg->bitformat); printf_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; } 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(preg->stereo_read) { unsigned int val = preg->stereo_read(id, preg->offset, NULL); print_bitformat(txt, sizeof(txt), val, preg->bitformat); printf_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; } printf_add(ptxt, txt_size, "MBOX[%u]\n", 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(preg->_mbox_read) { unsigned int val = preg->_mbox_read(id, preg->offset, NULL); print_bitformat(txt, sizeof(txt), val, preg->bitformat); printf_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; /*--- printk("%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(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 int generic_scan_args(const char *buf, unsigned int args[], int argc, int as_hex) { int i, scanned = 0; if(buf == NULL) { return 0; } for(i = 0; i < argc; i++) { if(*buf) { const char *format; SKIP_SPACE(buf); if(as_hex) { format = "%x"; } else{ format = "%u"; } sscanf(buf, format, &args[i]); scanned++; SKIP_UNTIL_SPACE(buf); } } return scanned; } unsigned short testpacket[] = { 0xffff, 0x0005, 0x0aff, 0x0002, 0x0214, 0xf600, 0x010e, 0xffff }; unsigned int send_test_packet = 0xFF; unsigned int packet_offset = 0x1; /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ unsigned short send_testpacket(void) { if(send_test_packet < ARRAY_SIZE(testpacket)) { return testpacket[send_test_packet++]; } return 0; } unsigned int glb_mclk, glb_bclk, glb_master = 0, glb_extclk = 1; unsigned int testval = 0x80A0; unsigned int testval_offset = 4; unsigned int testval_rx_resize = 0; unsigned int testval_tx_resize = 0; unsigned int only_rx_irq = 0; static unsigned int start_tdm; /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ unsigned int TestTxData(void *refhandle, void *buf) { unsigned int i; static unsigned 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 / 2; i++) { *p = 0; if((i % pmbox_dma->slots) == testval_offset) { *p = testval; } 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; printk("tx:%p (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, 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; } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ unsigned int TestRxData(void *refhandle, void *buf) { unsigned int i; unsigned char *p = buf; static unsigned start_j; 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; printk("rx:%p (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, 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 startup_tdm(unsigned int bclk, unsigned int mclk, unsigned int slots) { int res = 0; if(tdm_if_api) { printk(KERN_INFO"tdm-interface locked\n"); return 1; } if(slots == 0) { slots = 7; } if(mclk == 0) { bclk = 1024 * 1000; mclk = bclk * 4; } else { mclk *= 1000; bclk *= 1000; } /*--- if(!glb_master) { ---*/ /*--- bclk = mclk; ---*/ /*--- } ---*/ if((glb_bclk == bclk) && (glb_mclk == mclk)) { /*--- bclk = 0; ---*/ } else { glb_bclk = bclk, glb_mclk = mclk; } adss_config_clk(ENABLE, glb_master, glb_extclk, bclk, mclk); adss_glb_tdm_ctrl_ch_num(slots); adss_glb_clk_enable_oe(0); adss_glb_rxtx_data_port_en(1); adss_glb_rxtx_framesync_port_en(0); adss_glb_tdm_mode(); adss_glb_tdm_ctrl_sync_num(0, CAPTURE); adss_glb_tdm_ctrl_sync_num(0, PLAYBACK); adss_glb_tdm_ctrl_delay(1, CAPTURE); adss_glb_tdm_ctrl_delay(1, 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 + 1), 8 * 8, 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 + 1), 8 * 8, 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]); 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; local_irq_restore(flags); } return res; } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ static void shutdown_tdm(void){ if(tdm_if_api) { printk(KERN_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); adss_glb_clk_enable_oe(0); adss_glb_rxtx_data_port_en(0); adss_glb_rxtx_framesync_port_en(0); } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ static void cmdlineparse(char *cmdline, void *dummy __attribute__((unused))) { char name[64]; unsigned int arg[3], scanned; const char *p = cmdline; memset(&arg, 0x0, sizeof(arg)); if((p = strstr(cmdline, "print"))) { print_all_register(NULL, 0, 0xff); } else if((p = strstr(cmdline, "clk"))) { p += sizeof("clk") -1; scanned = generic_scan_args(p, arg, 3, 0); if(scanned == 3) { adss_set_clk_rate(arg[0], arg[1], arg[2]); } } else if((p = strstr(cmdline, "master"))) { p += sizeof("master") -1; scanned = generic_scan_args(p, arg, 1, 0); if(scanned) { glb_master = arg[0]; printk("set master=%u\n", glb_master); } } else if((p = strstr(cmdline, "ext"))) { p += sizeof("ext") -1; scanned = generic_scan_args(p, arg, 1, 0); if(scanned) { glb_extclk = arg[0]; printk("set extclk=%u\n", glb_extclk); } } else if((p = strstr(cmdline, "start"))) { p += sizeof("start") -1; if(start_tdm == 0) { scanned = generic_scan_args(p, arg, 3, 0); if(startup_tdm(arg[0], arg[1], arg[2]) == 0) { start_tdm = 1; } } else { start_tdm = 0; shutdown_tdm(); } } else if((p = strstr(cmdline, "stop"))) { if(start_tdm) { start_tdm = 0; shutdown_tdm(); } } else if((p = strstr(cmdline, "txval"))) { p += sizeof("txval") -1; if(generic_scan_args(p, arg, 2, 1)) { testval = arg[0]; testval_offset = arg[1]; } } else if((p = strstr(cmdline, "resize"))) { p += sizeof("resize") -1; if(generic_scan_args(p, arg, 2, 1)) { testval_tx_resize = arg[0]; testval_rx_resize = arg[1]; printk("set tx_resize=%u rx_resize=%u\n", arg[0], arg[1]); } } else if((p = strstr(cmdline, "irq"))) { only_rx_irq = !only_rx_irq; printk(KERN_ERR"%s irq-mode\n", only_rx_irq ? "shared" : "normal"); } else if((p = strstr(cmdline, "wr"))) { SKIP_UNTIL_SPACE(p); p = cpy_name(name, sizeof(name), p); scanned = generic_scan_args(p, arg, 2, 1); if(scanned) { write_register(name, arg[0], arg[1]); } } else if((p = strstr(cmdline, "packet"))) { p += sizeof("packet") -1; if(generic_scan_args(p, arg, 1, 1)) { packet_offset = arg[0]; } printk(KERN_ERR"send packet: offset=%u\n", packet_offset); send_test_packet = 0; } else { printk(KERN_ERR"\tstart \n" "\tstop\n" "\text <0|1>\n" "\tmaster <0|1>\n" "\tclk \n" "\tresize \n" "\tirq-mode (toggle shared/normal)\n" "\tpcm_wr \n" "\tpacket \n" ); } } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ 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");