📄 omap-mcbsp.c
字号:
/* * File: mcbsp.c * * Defines for Multi-Channel Buffered Serial Port * Multichannel mode not supported. * * Copyright (C) 2002 RidgeRun, Inc. * Author: Samuel Ortiz <samuel.ortiz@nokia.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. * * History * ------- * 2004/08/12 Nishanth Menon Modified to integrate Audio requirements on 1610,1710 * and 2420 platforms. *//************************ INCLUDES *****************************************/#include <linux/module.h>#include <linux/init.h>#include <linux/device.h>#include <linux/wait.h>#include <linux/completion.h>#include <linux/interrupt.h>#include <asm/delay.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/arch/dma.h>#include <asm/arch/mux.h>#include <asm/arch/irqs.h>#include "omap-mcbsp.h"#ifdef DPRINTK#undef DPRINTK#endif#undef DEBUG#define DEBUG#ifdef DEBUG#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)#else#define DPRINTK( x... )#define FN_IN#define FN_OUT(n)#endif#ifdef CONFIG_ARCH_OMAP16XX#include <asm/arch/dsp_common.h>extern int omap_mcbsp_request(unsigned int id);extern void omap_mcbsp_free(unsigned int id);extern void omap_mcbsp_start(unsigned int id);extern void omap_mcbsp_stop(unsigned int id);#endif/************************ MODULE DEFINES **********************************//* Define this to enable IRQ functionality - This implementation Does not support this yet *///#define MCBSP_IRQ_MODE 1/* Define this to make McBSP Master mode - Not supported now. *///#define MCBSP_MASTER/* Define for dumping the registers *///#define MCBSP_DUMP_REGISTERS/* Enable the following for pcrm and muxing logic to be enabled for mcbsp2 24xx */#define MCBSP_PRCM#define MCBSP_PIN_MUXING/************************ MODULE DATA STRUCTURES ***************************//* Information about the mcbsp */struct omap_mcbsp { u32 virt_base; /* virtual base */ u32 phy_base; /* physical base */ u8 free; /* free or not */#ifdef MCBSP_IRQ_MODE omap_mcbsp_word_length rx_word_length; omap_mcbsp_word_length tx_word_length; u8 id; /* IRQ based TX/RX */ int rx_irq; int tx_irq; /* DMA stuff */ u8 dma_rx_sync; short dma_rx_lch; u8 dma_tx_sync; short dma_tx_lch; /* Completion queues */ struct completion tx_irq_completion; struct completion rx_irq_completion; struct completion tx_dma_completion; struct completion rx_dma_completion; spinlock_t lock;#endif /* End of #ifdef MCBSP_IRQ_MODE */};/******************* McBSP definitions (START) */#ifdef CONFIG_ARCH_OMAP16XXstatic struct omap_mcbsp mcbsp_1610[OMAP_MAX_MCBSP_COUNT] = { [0] = {.virt_base = OMAP1610_MCBSP1_BASE, .phy_base = OMAP1610_MCBSP1_BASE_PHY,#ifdef MCBSP_IRQ_MODE .dma_rx_sync = OMAP_DMA_MCBSP1_RX, .dma_tx_sync = OMAP_DMA_MCBSP1_TX, .rx_irq = INT_1610_McBSP1RX, .tx_irq = INT_1610_McBSP1TX#endif }, [1] = {.virt_base = OMAP1610_MCBSP2_BASE, .phy_base = OMAP1610_MCBSP2_BASE_PHY,#ifdef MCBSP_IRQ_MODE .dma_rx_sync = OMAP_DMA_MCBSP2_RX, .dma_tx_sync = OMAP_DMA_MCBSP2_TX, .rx_irq = INT_1610_McBSP2RX, .tx_irq = INT_1610_McBSP2TX#endif }, [2] = {.virt_base = OMAP1610_MCBSP3_BASE, .phy_base = OMAP1610_MCBSP3_BASE_PHY,#ifdef MCBSP_IRQ_MODE .dma_rx_sync = OMAP_DMA_MCBSP3_RX, .dma_tx_sync = OMAP_DMA_MCBSP3_TX, .rx_irq = INT_1610_McBSP3RX, .tx_irq = INT_1610_McBSP3TX#endif },};static struct omap_mcbsp mcbsp_1710[OMAP_MAX_MCBSP_COUNT] = { [0] = {.virt_base = OMAP1710_MCBSP1_BASE, .phy_base = OMAP1710_MCBSP1_BASE_PHY,#ifdef MCBSP_IRQ_MODE .dma_rx_sync = OMAP_DMA_MCBSP1_RX, .dma_tx_sync = OMAP_DMA_MCBSP1_TX, .rx_irq = INT_1710_McBSP1RX, .tx_irq = INT_1710_McBSP1TX#endif }, [1] = {.virt_base = OMAP1710_MCBSP2_BASE, .phy_base = OMAP1710_MCBSP2_BASE_PHY,#ifdef MCBSP_IRQ_MODE .dma_rx_sync = OMAP_DMA_MCBSP2_RX, .dma_tx_sync = OMAP_DMA_MCBSP2_TX, .rx_irq = INT_1710_McBSP2RX, .tx_irq = INT_1710_McBSP2TX#endif }, [2] = {.virt_base = OMAP1710_MCBSP3_BASE, .phy_base = OMAP1710_MCBSP3_BASE_PHY,#ifdef MCBSP_IRQ_MODE .dma_rx_sync = OMAP_DMA_MCBSP3_RX, .dma_tx_sync = OMAP_DMA_MCBSP3_TX, .rx_irq = INT_1710_McBSP3RX, .tx_irq = INT_1710_McBSP3TX#endif },};#endif /* CONFIG_ARCH_OMAP16XX */#ifdef CONFIG_ARCH_OMAP24XXstatic struct omap_mcbsp mcbsp_2420[OMAP_MAX_MCBSP_COUNT] = { [0] = {.virt_base = OMAP2420_MCBSP1_BASE, .phy_base = OMAP2420_MCBSP1_BASE_PHY,#ifdef MCBSP_IRQ_MODE .dma_rx_sync = OMAP_DMA_MCBSP1_RX, .dma_tx_sync = OMAP_DMA_MCBSP1_TX, .rx_irq = INT_2420_McBSP1RX, .tx_irq = INT_2420_McBSP1TX#endif }, [1] = {.virt_base = OMAP2420_MCBSP2_BASE, .phy_base = OMAP2420_MCBSP2_BASE_PHY,#ifdef MCBSP_IRQ_MODE .dma_rx_sync = OMAP_DMA_MCBSP2_RX, .dma_tx_sync = OMAP_DMA_MCBSP2_TX, .rx_irq = INT_2420_McBSP2RX, .tx_irq = INT_2420_McBSP2TX#endif },};#endif /* CONFIG_ARCH_OMAP24XX */static struct omap_mcbsp *mcbsp;/******************* McBSP definitions (END) */#ifdef CONFIG_ARCH_OMAP24XX#ifdef MCBSP_PRCMstatic __inline__ u32 mcbsp_prcm_andout(u32 offset, u32 val){ return writel(readl(PRCM_BASE_ADDRESS + offset) & val, PRCM_BASE_ADDRESS + offset);}static __inline__ u32 omap_mcbsp_prcm_orout(u32 offset, u32 val){ return writel(readl(PRCM_BASE_ADDRESS + offset) | val, PRCM_BASE_ADDRESS + offset);}#endif /* MCBSP_PCRM */#ifdef MCBSP_PIN_MUXING#define CONTROL_REG_BASE_ADDR OMAP24XX_VA_SYSTEM_CONTROL_BASE#define CONTROL_PADCONF_eac_ac_sclk 0x0124#define CONTROL_PADCONF_eac_ac_fs 0x0125#define CONTROL_PADCONF_eac_ac_din 0x0126#define CONTROL_PADCONF_eac_ac_dout 0x0127#define CONTROL_PADCONF_eac_ac_mclk 0x0128#define CONTROL_PADCONF_sys_clkout 0x0137static __inline__ u8 omap_mcbsp_pin_out(u32 offset, u8 val){ return writeb(val, CONTROL_REG_BASE_ADDR + offset);}#endif /* End of pin mux reg read/write */#endif /* End of #ifdef CONFIG_ARCH_OMAP24XX *//************************* MODULE SPECIFIC FUNCTIONS ***********************//* * This function does the required stuff to configure external interfaces to * get mcbsp working */#ifdef CONFIG_ARCH_OMAP24XXstatic void omap_mcbsp_ext_config(unsigned int id){ /* Platform specific inits - unfortunately, can handle only MCBSP2, 24xx */ /* Configure the clocks */#ifdef MCBSP_PRCM omap_mcbsp_prcm_orout(CM_CLKEN_PLL, 0x000F); /* enable APLL and DPLL */ mcbsp_prcm_andout(CM_CLKSEL1_PLL, 0xFFFFFFF7); /* 96 Mhz selected */ /* McBSP2 selected */ /* Done in core.c with ffffs */ omap_mcbsp_prcm_orout(CM_FCLKEN1_CORE, 0x00010000); /* Done in core.c with ffffs */ omap_mcbsp_prcm_orout(CM_ICLKEN1_CORE, 0x00010000); omap_mcbsp_prcm_orout(CM_AUTOIDLE1_CORE, 0x00010000);#endif /* End of #ifdef MCBSP_PRCM */#ifdef MCBSP_PIN_MUXING /* Pin Muxing - TODO * These pins are used exclusively b/w mcbsp2 and eac. since eac is no longer * present, we can have this in mux.h. currently hacking in here. */ /* Enable Mode1 for McBSP2 */ omap_mcbsp_pin_out(CONTROL_PADCONF_eac_ac_sclk, 0x09); omap_mcbsp_pin_out(CONTROL_PADCONF_eac_ac_fs, 0x09); omap_mcbsp_pin_out(CONTROL_PADCONF_eac_ac_din, 0x09); omap_mcbsp_pin_out(CONTROL_PADCONF_eac_ac_dout, 0x09); omap_mcbsp_pin_out(CONTROL_PADCONF_eac_ac_mclk, 0x0B); /* Enable Mode3 for GPIO117 */#endif /* MCBSP_PIN_MUXING */}#endif /* End of #ifdef CONFIG_ARCH_OMAP24XX *//* * omap_mcbsp_config simply write a config to the appropriate McBSP. * NOTE: Not exposing this interface to world until required. If required, this will * act as alternatvie to mcbsp_start. */static inline voidomap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config){ u32 io_base = mcbsp[id].virt_base; /* We write the given config * NOTE: The order is important - we would like to enable the * tx and rx only after configuring everything */ OMAP_MCBSP_WRITE(io_base, RCR2, config->rcr2); OMAP_MCBSP_WRITE(io_base, RCR1, config->rcr1); OMAP_MCBSP_WRITE(io_base, XCR2, config->xcr2); OMAP_MCBSP_WRITE(io_base, XCR1, config->xcr1); OMAP_MCBSP_WRITE(io_base, SRGR2, config->srgr2); OMAP_MCBSP_WRITE(io_base, SRGR1, config->srgr1); OMAP_MCBSP_WRITE(io_base, PCR, config->pcr); OMAP_MCBSP_WRITE(io_base, SPCR2, config->spcr2); OMAP_MCBSP_WRITE(io_base, SPCR1, config->spcr1); return;}/************************* GLOBAL FUNCTIONS ********************************//* * Here we start the McBSP, by enabling the sample * generator, both transmitter and receivers, * and the frame sync. */int omap_mcbsp_begin(unsigned int id){ u32 io_base; const struct omap_mcbsp_reg_cfg initial_config = { .spcr2 = SPCR2_FREE | SPCR2_FRST | SPCR2_GRST | SPCR2_XRST | SPCR2_XINTM(3), .spcr1 = SPCR1_RINTM(3) | SPCR1_RRST, .rcr2 = RCR2_RPHASE | RCR2_RFRLEN2(OMAP_MCBSP_WORD_8) | RCR2_RWDLEN2(OMAP_MCBSP_WORD_16) | RCR2_RDATDLY(1), .rcr1 = RCR1_RFRLEN1(OMAP_MCBSP_WORD_8) | RCR1_RWDLEN1(OMAP_MCBSP_WORD_16), .xcr2 = XCR2_XPHASE | XCR2_XFRLEN2(OMAP_MCBSP_WORD_8) | XCR2_XWDLEN2(OMAP_MCBSP_WORD_16) | XCR2_XDATDLY(1) | XCR2_XFIG, .xcr1 = XCR1_XFRLEN1(OMAP_MCBSP_WORD_8) | XCR1_XWDLEN1(OMAP_MCBSP_WORD_16), .srgr1 = SRGR1_FWID(15), .srgr2 = SRGR2_GSYNC | SRGR2_CLKSP | SRGR2_FSGM | SRGR2_FPER(31), /* platform specific initialization */#if CONFIG_MACH_OMAP_H2 .pcr = PCR_CLKXM | PCR_CLKRM | PCR_FSXP | PCR_FSRP | PCR_CLKXP | PCR_CLKRP,#elif CONFIG_MACH_OMAP_H3 || CONFIG_MACH_OMAP_H4 || CONFIG_ARCH_OMAP24XX#ifdef MCBSP_MASTER .pcr = PCR_FSXM | PCR_FSRM | PCR_CLKXM | PCR_CLKRM | PCR_CLKXP | PCR_CLKRP,#else .pcr = PCR_CLKRM | PCR_SCLKME | PCR_FSXP | PCR_FSRP | PCR_CLKXP | PCR_CLKRP,#endif /* mcbsp Master defs */#endif /* platform specific inits */ }; /* To prevent misuse */ if (!mcbsp[id].free) { printk(KERN_ERR "mcbsp[%d] Already started\n", id); return -EPERM; } io_base = mcbsp[id].virt_base;#ifdef CONFIG_ARCH_OMAP24XX omap_mcbsp_ext_config(id);#endif /* End of #ifdef CONFIG_ARCH_OMAP24XX */#ifdef CONFIG_ARCH_OMAP16XX omap_mcbsp_request(id); omap_dsp_request_idle(); omap_mcbsp_stop(id);#endif /* Disable McBSP */ OMAP_MCBSP_WRITE(io_base, SPCR2, 0x0); OMAP_MCBSP_WRITE(io_base, SPCR1, 0x0); /* configure the registers */ omap_mcbsp_config(id, &initial_config); /* no longer free */ mcbsp[id].free = 0; /* Debug - Dump McBSP Regs */#ifdef MCBSP_DUMP_REGISTERS printk("**** MCBSP%d regs ****\n", id + 1); printk("SPCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].virt_base, SPCR1)); printk("SPCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].virt_base, SPCR2)); printk("RCR1 : 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].virt_base, RCR1)); printk("RCR2 : 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].virt_base, RCR2)); printk("XCR1 : 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].virt_base, XCR1)); printk("XCR2 : 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].virt_base, XCR2)); printk("SRGR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].virt_base, SRGR1)); printk("SRGR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].virt_base, SRGR2)); printk("PCR : 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].virt_base, PCR)); printk("***********************\n");#endif /* End of #ifdef MCBSP_DUMP_REGISTERS */ return 0;}/* * The function to stop the required mcBSP. To be called once all operations are completed. */int omap_mcbsp_end(unsigned int id){ u32 io_base; /* To prevent misuse */ if (mcbsp[id].free) { printk(KERN_ERR "mcbsp[%d] Already stopped \n", id); return -EPERM; } io_base = mcbsp[id].virt_base; // Disable the McBSP OMAP_MCBSP_WRITE(io_base, DXR1, 0x0000); // flush data OMAP_MCBSP_WRITE(io_base, DXR1, 0x0000); // flush data OMAP_MCBSP_WRITE(io_base, SPCR1, 0x0000); // disable SPCR1 OMAP_MCBSP_WRITE(io_base, SPCR2, 0x0000); // disable SPCR2#if (defined(CONFIG_ARCH_OMAP24XX) && defined (MCBSP_PRCM)) mcbsp_prcm_andout(CM_AUTOIDLE1_CORE, ~0x00010000);#endif /* #if CONFIG_ARCH_OMAP24XX */ mcbsp[id].free = 1;#ifdef CONFIG_ARCH_OMAP16XX omap_mcbsp_stop(id); omap_mcbsp_free(id);#endif return (0);}/* * The function to set the clock rate generater for a certain sample rate. * sample rate is provided in hertz */int omap_mcbsp_set_rate(unsigned int id, u32 sample_rate, int bits_per_sample){ /* * CLKG = IP Clk/(CLKGDV) * SampleRateInhz = CLKG/(BitPerSample -1) * Assuming i/p clock of 96Mhz * CLKGDV = Input Clock/(SampleRateInhz * (BitsPerSample -1)) */ int clkgdv = 0; u32 io_base; /* To prevent misuse */ if (mcbsp[id].free) { printk(KERN_ERR "mcbsp[%d] Not started cannot set sample rate\n", id); return -EPERM; } io_base = mcbsp[id].virt_base;#ifdef MCBSP_MASTER clkgdv = DEFAULT_MCBSP_CLOCK / (sample_rate * (bits_per_sample * 2 - 1)); if (clkgdv) OMAP_MCBSP_WRITE(io_base, SRGR1, SRGR1_FWID(bits_per_sample - 1) | SRGR1_CLKGDV(clkgdv)); else return (1); /* Stereo Mode */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -