diff options
Diffstat (limited to 'sound/soc/davinci/davinci-mcasp.c')
| -rw-r--r-- | sound/soc/davinci/davinci-mcasp.c | 973 | 
1 files changed, 973 insertions, 0 deletions
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c new file mode 100644 index 00000000000..eca22d7829d --- /dev/null +++ b/sound/soc/davinci/davinci-mcasp.c @@ -0,0 +1,973 @@ +/* + * ALSA SoC McASP Audio Layer for TI DAVINCI processor + * + * Multi-channel Audio Serial Port Driver + * + * Author: Nirmal Pandey <n-pandey@ti.com>, + *         Suresh Rajashekara <suresh.r@ti.com> + *         Steve Chen <schen@.mvista.com> + * + * Copyright:   (C) 2009 MontaVista Software, Inc., <source@mvista.com> + * Copyright:   (C) 2009  Texas Instruments, India + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/clk.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include "davinci-pcm.h" +#include "davinci-mcasp.h" + +/* + * McASP register definitions + */ +#define DAVINCI_MCASP_PID_REG		0x00 +#define DAVINCI_MCASP_PWREMUMGT_REG	0x04 + +#define DAVINCI_MCASP_PFUNC_REG		0x10 +#define DAVINCI_MCASP_PDIR_REG		0x14 +#define DAVINCI_MCASP_PDOUT_REG		0x18 +#define DAVINCI_MCASP_PDSET_REG		0x1c + +#define DAVINCI_MCASP_PDCLR_REG		0x20 + +#define DAVINCI_MCASP_TLGC_REG		0x30 +#define DAVINCI_MCASP_TLMR_REG		0x34 + +#define DAVINCI_MCASP_GBLCTL_REG	0x44 +#define DAVINCI_MCASP_AMUTE_REG		0x48 +#define DAVINCI_MCASP_LBCTL_REG		0x4c + +#define DAVINCI_MCASP_TXDITCTL_REG	0x50 + +#define DAVINCI_MCASP_GBLCTLR_REG	0x60 +#define DAVINCI_MCASP_RXMASK_REG	0x64 +#define DAVINCI_MCASP_RXFMT_REG		0x68 +#define DAVINCI_MCASP_RXFMCTL_REG	0x6c + +#define DAVINCI_MCASP_ACLKRCTL_REG	0x70 +#define DAVINCI_MCASP_AHCLKRCTL_REG	0x74 +#define DAVINCI_MCASP_RXTDM_REG		0x78 +#define DAVINCI_MCASP_EVTCTLR_REG	0x7c + +#define DAVINCI_MCASP_RXSTAT_REG	0x80 +#define DAVINCI_MCASP_RXTDMSLOT_REG	0x84 +#define DAVINCI_MCASP_RXCLKCHK_REG	0x88 +#define DAVINCI_MCASP_REVTCTL_REG	0x8c + +#define DAVINCI_MCASP_GBLCTLX_REG	0xa0 +#define DAVINCI_MCASP_TXMASK_REG	0xa4 +#define DAVINCI_MCASP_TXFMT_REG		0xa8 +#define DAVINCI_MCASP_TXFMCTL_REG	0xac + +#define DAVINCI_MCASP_ACLKXCTL_REG	0xb0 +#define DAVINCI_MCASP_AHCLKXCTL_REG	0xb4 +#define DAVINCI_MCASP_TXTDM_REG		0xb8 +#define DAVINCI_MCASP_EVTCTLX_REG	0xbc + +#define DAVINCI_MCASP_TXSTAT_REG	0xc0 +#define DAVINCI_MCASP_TXTDMSLOT_REG	0xc4 +#define DAVINCI_MCASP_TXCLKCHK_REG	0xc8 +#define DAVINCI_MCASP_XEVTCTL_REG	0xcc + +/* Left(even TDM Slot) Channel Status Register File */ +#define DAVINCI_MCASP_DITCSRA_REG	0x100 +/* Right(odd TDM slot) Channel Status Register File */ +#define DAVINCI_MCASP_DITCSRB_REG	0x118 +/* Left(even TDM slot) User Data Register File */ +#define DAVINCI_MCASP_DITUDRA_REG	0x130 +/* Right(odd TDM Slot) User Data Register File */ +#define DAVINCI_MCASP_DITUDRB_REG	0x148 + +/* Serializer n Control Register */ +#define DAVINCI_MCASP_XRSRCTL_BASE_REG	0x180 +#define DAVINCI_MCASP_XRSRCTL_REG(n)	(DAVINCI_MCASP_XRSRCTL_BASE_REG + \ +						(n << 2)) + +/* Transmit Buffer for Serializer n */ +#define DAVINCI_MCASP_TXBUF_REG		0x200 +/* Receive Buffer for Serializer n */ +#define DAVINCI_MCASP_RXBUF_REG		0x280 + +/* McASP FIFO Registers */ +#define DAVINCI_MCASP_WFIFOCTL		(0x1010) +#define DAVINCI_MCASP_WFIFOSTS		(0x1014) +#define DAVINCI_MCASP_RFIFOCTL		(0x1018) +#define DAVINCI_MCASP_RFIFOSTS		(0x101C) + +/* + * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management + *     Register Bits + */ +#define MCASP_FREE	BIT(0) +#define MCASP_SOFT	BIT(1) + +/* + * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits + */ +#define AXR(n)		(1<<n) +#define PFUNC_AMUTE	BIT(25) +#define ACLKX		BIT(26) +#define AHCLKX		BIT(27) +#define AFSX		BIT(28) +#define ACLKR		BIT(29) +#define AHCLKR		BIT(30) +#define AFSR		BIT(31) + +/* + * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits + */ +#define AXR(n)		(1<<n) +#define PDIR_AMUTE	BIT(25) +#define ACLKX		BIT(26) +#define AHCLKX		BIT(27) +#define AFSX		BIT(28) +#define ACLKR		BIT(29) +#define AHCLKR		BIT(30) +#define AFSR		BIT(31) + +/* + * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits + */ +#define DITEN	BIT(0)	/* Transmit DIT mode enable/disable */ +#define VA	BIT(2) +#define VB	BIT(3) + +/* + * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits + */ +#define TXROT(val)	(val) +#define TXSEL		BIT(3) +#define TXSSZ(val)	(val<<4) +#define TXPBIT(val)	(val<<8) +#define TXPAD(val)	(val<<13) +#define TXORD		BIT(15) +#define FSXDLY(val)	(val<<16) + +/* + * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits + */ +#define RXROT(val)	(val) +#define RXSEL		BIT(3) +#define RXSSZ(val)	(val<<4) +#define RXPBIT(val)	(val<<8) +#define RXPAD(val)	(val<<13) +#define RXORD		BIT(15) +#define FSRDLY(val)	(val<<16) + +/* + * DAVINCI_MCASP_TXFMCTL_REG -  Transmit Frame Control Register Bits + */ +#define FSXPOL		BIT(0) +#define AFSXE		BIT(1) +#define FSXDUR		BIT(4) +#define FSXMOD(val)	(val<<7) + +/* + * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits + */ +#define FSRPOL		BIT(0) +#define AFSRE		BIT(1) +#define FSRDUR		BIT(4) +#define FSRMOD(val)	(val<<7) + +/* + * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits + */ +#define ACLKXDIV(val)	(val) +#define ACLKXE		BIT(5) +#define TX_ASYNC	BIT(6) +#define ACLKXPOL	BIT(7) + +/* + * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits + */ +#define ACLKRDIV(val)	(val) +#define ACLKRE		BIT(5) +#define RX_ASYNC	BIT(6) +#define ACLKRPOL	BIT(7) + +/* + * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control + *     Register Bits + */ +#define AHCLKXDIV(val)	(val) +#define AHCLKXPOL	BIT(14) +#define AHCLKXE		BIT(15) + +/* + * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control + *     Register Bits + */ +#define AHCLKRDIV(val)	(val) +#define AHCLKRPOL	BIT(14) +#define AHCLKRE		BIT(15) + +/* + * DAVINCI_MCASP_XRSRCTL_BASE_REG -  Serializer Control Register Bits + */ +#define MODE(val)	(val) +#define DISMOD		(val)(val<<2) +#define TXSTATE		BIT(4) +#define RXSTATE		BIT(5) + +/* + * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits + */ +#define LBEN		BIT(0) +#define LBORD		BIT(1) +#define LBGENMODE(val)	(val<<2) + +/* + * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration + */ +#define TXTDMS(n)	(1<<n) + +/* + * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration + */ +#define RXTDMS(n)	(1<<n) + +/* + * DAVINCI_MCASP_GBLCTL_REG -  Global Control Register Bits + */ +#define RXCLKRST	BIT(0)	/* Receiver Clock Divider Reset */ +#define RXHCLKRST	BIT(1)	/* Receiver High Frequency Clock Divider */ +#define RXSERCLR	BIT(2)	/* Receiver Serializer Clear */ +#define RXSMRST		BIT(3)	/* Receiver State Machine Reset */ +#define RXFSRST		BIT(4)	/* Frame Sync Generator Reset */ +#define TXCLKRST	BIT(8)	/* Transmitter Clock Divider Reset */ +#define TXHCLKRST	BIT(9)	/* Transmitter High Frequency Clock Divider*/ +#define TXSERCLR	BIT(10)	/* Transmit Serializer Clear */ +#define TXSMRST		BIT(11)	/* Transmitter State Machine Reset */ +#define TXFSRST		BIT(12)	/* Frame Sync Generator Reset */ + +/* + * DAVINCI_MCASP_AMUTE_REG -  Mute Control Register Bits + */ +#define MUTENA(val)	(val) +#define MUTEINPOL	BIT(2) +#define MUTEINENA	BIT(3) +#define MUTEIN		BIT(4) +#define MUTER		BIT(5) +#define MUTEX		BIT(6) +#define MUTEFSR		BIT(7) +#define MUTEFSX		BIT(8) +#define MUTEBADCLKR	BIT(9) +#define MUTEBADCLKX	BIT(10) +#define MUTERXDMAERR	BIT(11) +#define MUTETXDMAERR	BIT(12) + +/* + * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits + */ +#define RXDATADMADIS	BIT(0) + +/* + * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits + */ +#define TXDATADMADIS	BIT(0) + +/* + * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits + */ +#define FIFO_ENABLE	BIT(16) +#define NUMEVT_MASK	(0xFF << 8) +#define NUMDMA_MASK	(0xFF) + +#define DAVINCI_MCASP_NUM_SERIALIZER	16 + +static inline void mcasp_set_bits(void __iomem *reg, u32 val) +{ +	__raw_writel(__raw_readl(reg) | val, reg); +} + +static inline void mcasp_clr_bits(void __iomem *reg, u32 val) +{ +	__raw_writel((__raw_readl(reg) & ~(val)), reg); +} + +static inline void mcasp_mod_bits(void __iomem *reg, u32 val, u32 mask) +{ +	__raw_writel((__raw_readl(reg) & ~mask) | val, reg); +} + +static inline void mcasp_set_reg(void __iomem *reg, u32 val) +{ +	__raw_writel(val, reg); +} + +static inline u32 mcasp_get_reg(void __iomem *reg) +{ +	return (unsigned int)__raw_readl(reg); +} + +static inline void mcasp_set_ctl_reg(void __iomem *regs, u32 val) +{ +	int i = 0; + +	mcasp_set_bits(regs, val); + +	/* programming GBLCTL needs to read back from GBLCTL and verfiy */ +	/* loop count is to avoid the lock-up */ +	for (i = 0; i < 1000; i++) { +		if ((mcasp_get_reg(regs) & val) == val) +			break; +	} + +	if (i == 1000 && ((mcasp_get_reg(regs) & val) != val)) +		printk(KERN_ERR "GBLCTL write error\n"); +} + +static int davinci_mcasp_startup(struct snd_pcm_substream *substream, +						struct snd_soc_dai *cpu_dai) +{ +	struct davinci_audio_dev *dev = cpu_dai->private_data; +	cpu_dai->dma_data = dev->dma_params[substream->stream]; +	return 0; +} + +static void mcasp_start_rx(struct davinci_audio_dev *dev) +{ +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST); +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST); +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR); +	mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0); + +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); +	mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0); + +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); +} + +static void mcasp_start_tx(struct davinci_audio_dev *dev) +{ +	u8 offset = 0, i; +	u32 cnt; + +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR); +	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0); + +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); +	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); +	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0); +	for (i = 0; i < dev->num_serializer; i++) { +		if (dev->serial_dir[i] == TX_MODE) { +			offset = i; +			break; +		} +	} + +	/* wait for TX ready */ +	cnt = 0; +	while (!(mcasp_get_reg(dev->base + DAVINCI_MCASP_XRSRCTL_REG(offset)) & +		 TXSTATE) && (cnt < 100000)) +		cnt++; + +	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0); +} + +static void davinci_mcasp_start(struct davinci_audio_dev *dev, int stream) +{ +	if (stream == SNDRV_PCM_STREAM_PLAYBACK) +		mcasp_start_tx(dev); +	else +		mcasp_start_rx(dev); + +	/* enable FIFO */ +	if (dev->txnumevt) +		mcasp_set_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE); + +	if (dev->rxnumevt) +		mcasp_set_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE); +} + +static void mcasp_stop_rx(struct davinci_audio_dev *dev) +{ +	mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, 0); +	mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); +} + +static void mcasp_stop_tx(struct davinci_audio_dev *dev) +{ +	mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, 0); +	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); +} + +static void davinci_mcasp_stop(struct davinci_audio_dev *dev, int stream) +{ +	if (stream == SNDRV_PCM_STREAM_PLAYBACK) +		mcasp_stop_tx(dev); +	else +		mcasp_stop_rx(dev); + +	/* disable FIFO */ +	if (dev->txnumevt) +		mcasp_clr_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE); + +	if (dev->rxnumevt) +		mcasp_clr_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE); +} + +static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, +					 unsigned int fmt) +{ +	struct davinci_audio_dev *dev = cpu_dai->private_data; +	void __iomem *base = dev->base; + +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +	case SND_SOC_DAIFMT_CBS_CFS: +		/* codec is clock and frame slave */ +		mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); +		mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + +		mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); +		mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + +		mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x7 << 26)); +		break; +	case SND_SOC_DAIFMT_CBM_CFS: +		/* codec is clock master and frame slave */ +		mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); +		mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + +		mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); +		mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + +		mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x2d << 26)); +		break; +	case SND_SOC_DAIFMT_CBM_CFM: +		/* codec is clock and frame master */ +		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); +		mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + +		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); +		mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + +		mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, (0x3f << 26)); +		break; + +	default: +		return -EINVAL; +	} + +	switch (fmt & SND_SOC_DAIFMT_INV_MASK) { +	case SND_SOC_DAIFMT_IB_NF: +		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); +		mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + +		mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); +		mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); +		break; + +	case SND_SOC_DAIFMT_NB_IF: +		mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); +		mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + +		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); +		mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); +		break; + +	case SND_SOC_DAIFMT_IB_IF: +		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); +		mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + +		mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); +		mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); +		break; + +	case SND_SOC_DAIFMT_NB_NF: +		mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); +		mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + +		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); +		mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); +		break; + +	default: +		return -EINVAL; +	} + +	return 0; +} + +static int davinci_config_channel_size(struct davinci_audio_dev *dev, +				       int channel_size) +{ +	u32 fmt = 0; + +	switch (channel_size) { +	case DAVINCI_AUDIO_WORD_8: +		fmt = 0x03; +		break; + +	case DAVINCI_AUDIO_WORD_12: +		fmt = 0x05; +		break; + +	case DAVINCI_AUDIO_WORD_16: +		fmt = 0x07; +		break; + +	case DAVINCI_AUDIO_WORD_20: +		fmt = 0x09; +		break; + +	case DAVINCI_AUDIO_WORD_24: +		fmt = 0x0B; +		break; + +	case DAVINCI_AUDIO_WORD_28: +		fmt = 0x0D; +		break; + +	case DAVINCI_AUDIO_WORD_32: +		fmt = 0x0F; +		break; + +	default: +		return -EINVAL; +	} + +	mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, +					RXSSZ(fmt), RXSSZ(0x0F)); +	mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, +					TXSSZ(fmt), TXSSZ(0x0F)); +	return 0; +} + +static void davinci_hw_common_param(struct davinci_audio_dev *dev, int stream) +{ +	int i; +	u8 tx_ser = 0; +	u8 rx_ser = 0; + +	/* Default configuration */ +	mcasp_set_bits(dev->base + DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT); + +	/* All PINS as McASP */ +	mcasp_set_reg(dev->base + DAVINCI_MCASP_PFUNC_REG, 0x00000000); + +	if (stream == SNDRV_PCM_STREAM_PLAYBACK) { +		mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); +		mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG, +				TXDATADMADIS); +	} else { +		mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); +		mcasp_clr_bits(dev->base + DAVINCI_MCASP_REVTCTL_REG, +				RXDATADMADIS); +	} + +	for (i = 0; i < dev->num_serializer; i++) { +		mcasp_set_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i), +					dev->serial_dir[i]); +		if (dev->serial_dir[i] == TX_MODE) { +			mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, +					AXR(i)); +			tx_ser++; +		} else if (dev->serial_dir[i] == RX_MODE) { +			mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG, +					AXR(i)); +			rx_ser++; +		} +	} + +	if (dev->txnumevt && stream == SNDRV_PCM_STREAM_PLAYBACK) { +		if (dev->txnumevt * tx_ser > 64) +			dev->txnumevt = 1; + +		mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, tx_ser, +								NUMDMA_MASK); +		mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, +				((dev->txnumevt * tx_ser) << 8), NUMEVT_MASK); +		mcasp_set_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE); +	} + +	if (dev->rxnumevt && stream == SNDRV_PCM_STREAM_CAPTURE) { +		if (dev->rxnumevt * rx_ser > 64) +			dev->rxnumevt = 1; + +		mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, rx_ser, +								NUMDMA_MASK); +		mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, +				((dev->rxnumevt * rx_ser) << 8), NUMEVT_MASK); +		mcasp_set_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE); +	} +} + +static void davinci_hw_param(struct davinci_audio_dev *dev, int stream) +{ +	int i, active_slots; +	u32 mask = 0; + +	active_slots = (dev->tdm_slots > 31) ? 32 : dev->tdm_slots; +	for (i = 0; i < active_slots; i++) +		mask |= (1 << i); + +	mcasp_clr_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); + +	if (stream == SNDRV_PCM_STREAM_PLAYBACK) { +		/* bit stream is MSB first  with no delay */ +		/* DSP_B mode */ +		mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, +				AHCLKXE); +		mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask); +		mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD); + +		if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32)) +			mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, +					FSXMOD(dev->tdm_slots), FSXMOD(0x1FF)); +		else +			printk(KERN_ERR "playback tdm slot %d not supported\n", +				dev->tdm_slots); + +		mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0xFFFFFFFF); +		mcasp_clr_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); +	} else { +		/* bit stream is MSB first with no delay */ +		/* DSP_B mode */ +		mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXORD); +		mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG, +				AHCLKRE); +		mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask); + +		if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32)) +			mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, +					FSRMOD(dev->tdm_slots), FSRMOD(0x1FF)); +		else +			printk(KERN_ERR "capture tdm slot %d not supported\n", +				dev->tdm_slots); + +		mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG, 0xFFFFFFFF); +		mcasp_clr_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); +	} +} + +/* S/PDIF */ +static void davinci_hw_dit_param(struct davinci_audio_dev *dev) +{ +	/* Set the PDIR for Serialiser as output */ +	mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AFSX); + +	/* TXMASK for 24 bits */ +	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0x00FFFFFF); + +	/* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0 +	   and LSB first */ +	mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, +						TXROT(6) | TXSSZ(15)); + +	/* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */ +	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXFMCTL_REG, +						AFSXE | FSXMOD(0x180)); + +	/* Set the TX tdm : for all the slots */ +	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF); + +	/* Set the TX clock controls : div = 1 and internal */ +	mcasp_set_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG, +						ACLKXE | TX_ASYNC); + +	mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS); + +	/* Only 44100 and 48000 are valid, both have the same setting */ +	mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3)); + +	/* Enable the DIT */ +	mcasp_set_bits(dev->base + DAVINCI_MCASP_TXDITCTL_REG, DITEN); +} + +static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, +					struct snd_pcm_hw_params *params, +					struct snd_soc_dai *cpu_dai) +{ +	struct davinci_audio_dev *dev = cpu_dai->private_data; +	struct davinci_pcm_dma_params *dma_params = +					dev->dma_params[substream->stream]; +	int word_length; +	u8 numevt; + +	davinci_hw_common_param(dev, substream->stream); +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +		numevt = dev->txnumevt; +	else +		numevt = dev->rxnumevt; + +	if (!numevt) +		numevt = 1; + +	if (dev->op_mode == DAVINCI_MCASP_DIT_MODE) +		davinci_hw_dit_param(dev); +	else +		davinci_hw_param(dev, substream->stream); + +	switch (params_format(params)) { +	case SNDRV_PCM_FORMAT_S8: +		dma_params->data_type = 1; +		word_length = DAVINCI_AUDIO_WORD_8; +		break; + +	case SNDRV_PCM_FORMAT_S16_LE: +		dma_params->data_type = 2; +		word_length = DAVINCI_AUDIO_WORD_16; +		break; + +	case SNDRV_PCM_FORMAT_S32_LE: +		dma_params->data_type = 4; +		word_length = DAVINCI_AUDIO_WORD_32; +		break; + +	default: +		printk(KERN_WARNING "davinci-mcasp: unsupported PCM format"); +		return -EINVAL; +	} + +	if (dev->version == MCASP_VERSION_2) { +		dma_params->data_type *= numevt; +		dma_params->acnt = 4 * numevt; +	} else +		dma_params->acnt = dma_params->data_type; + +	davinci_config_channel_size(dev, word_length); + +	return 0; +} + +static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, +				     int cmd, struct snd_soc_dai *cpu_dai) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct davinci_audio_dev *dev = rtd->dai->cpu_dai->private_data; +	int ret = 0; + +	switch (cmd) { +	case SNDRV_PCM_TRIGGER_START: +	case SNDRV_PCM_TRIGGER_RESUME: +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +		davinci_mcasp_start(dev, substream->stream); +		break; + +	case SNDRV_PCM_TRIGGER_STOP: +	case SNDRV_PCM_TRIGGER_SUSPEND: +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +		davinci_mcasp_stop(dev, substream->stream); +		break; + +	default: +		ret = -EINVAL; +	} + +	return ret; +} + +static struct snd_soc_dai_ops davinci_mcasp_dai_ops = { +	.startup 	= davinci_mcasp_startup, +	.trigger	= davinci_mcasp_trigger, +	.hw_params	= davinci_mcasp_hw_params, +	.set_fmt	= davinci_mcasp_set_dai_fmt, + +}; + +struct snd_soc_dai davinci_mcasp_dai[] = { +	{ +		.name 		= "davinci-i2s", +		.id 		= 0, +		.playback	= { +			.channels_min	= 2, +			.channels_max 	= 2, +			.rates 		= DAVINCI_MCASP_RATES, +			.formats 	= SNDRV_PCM_FMTBIT_S8 | +						SNDRV_PCM_FMTBIT_S16_LE | +						SNDRV_PCM_FMTBIT_S32_LE, +		}, +		.capture 	= { +			.channels_min 	= 2, +			.channels_max 	= 2, +			.rates 		= DAVINCI_MCASP_RATES, +			.formats	= SNDRV_PCM_FMTBIT_S8 | +						SNDRV_PCM_FMTBIT_S16_LE | +						SNDRV_PCM_FMTBIT_S32_LE, +		}, +		.ops 		= &davinci_mcasp_dai_ops, + +	}, +	{ +		.name 		= "davinci-dit", +		.id 		= 1, +		.playback 	= { +			.channels_min	= 1, +			.channels_max	= 384, +			.rates		= DAVINCI_MCASP_RATES, +			.formats	= SNDRV_PCM_FMTBIT_S16_LE, +		}, +		.ops 		= &davinci_mcasp_dai_ops, +	}, + +}; +EXPORT_SYMBOL_GPL(davinci_mcasp_dai); + +static int davinci_mcasp_probe(struct platform_device *pdev) +{ +	struct davinci_pcm_dma_params *dma_data; +	struct resource *mem, *ioarea, *res; +	struct snd_platform_data *pdata; +	struct davinci_audio_dev *dev; +	int count = 0; +	int ret = 0; + +	dev = kzalloc(sizeof(struct davinci_audio_dev), GFP_KERNEL); +	if (!dev) +		return	-ENOMEM; + +	dma_data = kzalloc(sizeof(struct davinci_pcm_dma_params) * 2, +								GFP_KERNEL); +	if (!dma_data) { +		ret = -ENOMEM; +		goto err_release_dev; +	} + +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!mem) { +		dev_err(&pdev->dev, "no mem resource?\n"); +		ret = -ENODEV; +		goto err_release_data; +	} + +	ioarea = request_mem_region(mem->start, +			(mem->end - mem->start) + 1, pdev->name); +	if (!ioarea) { +		dev_err(&pdev->dev, "Audio region already claimed\n"); +		ret = -EBUSY; +		goto err_release_data; +	} + +	pdata = pdev->dev.platform_data; +	dev->clk = clk_get(&pdev->dev, NULL); +	if (IS_ERR(dev->clk)) { +		ret = -ENODEV; +		goto err_release_region; +	} + +	clk_enable(dev->clk); + +	dev->base = (void __iomem *)IO_ADDRESS(mem->start); +	dev->op_mode = pdata->op_mode; +	dev->tdm_slots = pdata->tdm_slots; +	dev->num_serializer = pdata->num_serializer; +	dev->serial_dir = pdata->serial_dir; +	dev->codec_fmt = pdata->codec_fmt; +	dev->version = pdata->version; +	dev->txnumevt = pdata->txnumevt; +	dev->rxnumevt = pdata->rxnumevt; + +	dma_data[count].name = "I2S PCM Stereo out"; +	dma_data[count].eventq_no = pdata->eventq_no; +	dma_data[count].dma_addr = (dma_addr_t) (pdata->tx_dma_offset + +							io_v2p(dev->base)); +	dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &dma_data[count]; + +	/* first TX, then RX */ +	res = platform_get_resource(pdev, IORESOURCE_DMA, 0); +	if (!res) { +		dev_err(&pdev->dev, "no DMA resource\n"); +		goto err_release_region; +	} + +	dma_data[count].channel = res->start; +	count++; +	dma_data[count].name = "I2S PCM Stereo in"; +	dma_data[count].eventq_no = pdata->eventq_no; +	dma_data[count].dma_addr = (dma_addr_t)(pdata->rx_dma_offset + +							io_v2p(dev->base)); +	dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &dma_data[count]; + +	res = platform_get_resource(pdev, IORESOURCE_DMA, 1); +	if (!res) { +		dev_err(&pdev->dev, "no DMA resource\n"); +		goto err_release_region; +	} + +	dma_data[count].channel = res->start; +	davinci_mcasp_dai[pdata->op_mode].private_data = dev; +	davinci_mcasp_dai[pdata->op_mode].dev = &pdev->dev; +	ret = snd_soc_register_dai(&davinci_mcasp_dai[pdata->op_mode]); + +	if (ret != 0) +		goto err_release_region; +	return 0; + +err_release_region: +	release_mem_region(mem->start, (mem->end - mem->start) + 1); +err_release_data: +	kfree(dma_data); +err_release_dev: +	kfree(dev); + +	return ret; +} + +static int davinci_mcasp_remove(struct platform_device *pdev) +{ +	struct snd_platform_data *pdata = pdev->dev.platform_data; +	struct davinci_pcm_dma_params *dma_data; +	struct davinci_audio_dev *dev; +	struct resource *mem; + +	snd_soc_unregister_dai(&davinci_mcasp_dai[pdata->op_mode]); +	dev = davinci_mcasp_dai[pdata->op_mode].private_data; +	clk_disable(dev->clk); +	clk_put(dev->clk); +	dev->clk = NULL; + +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	release_mem_region(mem->start, (mem->end - mem->start) + 1); + +	dma_data = dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; +	kfree(dma_data); +	kfree(dev); + +	return 0; +} + +static struct platform_driver davinci_mcasp_driver = { +	.probe		= davinci_mcasp_probe, +	.remove		= davinci_mcasp_remove, +	.driver		= { +		.name	= "davinci-mcasp", +		.owner	= THIS_MODULE, +	}, +}; + +static int __init davinci_mcasp_init(void) +{ +	return platform_driver_register(&davinci_mcasp_driver); +} +module_init(davinci_mcasp_init); + +static void __exit davinci_mcasp_exit(void) +{ +	platform_driver_unregister(&davinci_mcasp_driver); +} +module_exit(davinci_mcasp_exit); + +MODULE_AUTHOR("Steve Chen"); +MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface"); +MODULE_LICENSE("GPL"); +  |