aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Thompson <daniel.thompson@linaro.org>2017-07-30 08:09:38 +0100
committerDaniel Thompson <daniel.thompson@linaro.org>2017-07-31 14:14:34 +0100
commit40600bf3a14bda555279b08f3d4f71d696097dfc (patch)
treeef2a81b3783225f7dbade370907cf432bc9727da
parent2c3289c7071a4461dc92faac12be7097fbf3457a (diff)
sound: soc: rdafpga: RDA support
-rw-r--r--sound/soc/rdafpga/Kconfig28
-rw-r--r--sound/soc/rdafpga/Makefile14
-rw-r--r--sound/soc/rdafpga/aif.h244
-rw-r--r--sound/soc/rdafpga/aif_asm.h211
-rw-r--r--sound/soc/rdafpga/aud_ifc.c369
-rw-r--r--sound/soc/rdafpga/aud_ifc.h99
-rw-r--r--sound/soc/rdafpga/bb_ifc.h209
-rw-r--r--sound/soc/rdafpga/bb_ifc_asm.h202
-rw-r--r--sound/soc/rdafpga/rdafpga_dai.c536
-rw-r--r--sound/soc/rdafpga/rdafpga_pcm.c410
-rw-r--r--sound/soc/rdafpga/rdafpga_pcm.h41
-rw-r--r--sound/soc/rdafpga/rdafpga_soundcard.c129
-rw-r--r--sound/soc/rdafpga/tlv320aic23_spi.c861
-rw-r--r--sound/soc/rdafpga/tlv320aic23_spi.h119
14 files changed, 3472 insertions, 0 deletions
diff --git a/sound/soc/rdafpga/Kconfig b/sound/soc/rdafpga/Kconfig
new file mode 100644
index 000000000000..9e5fa9ee2bb7
--- /dev/null
+++ b/sound/soc/rdafpga/Kconfig
@@ -0,0 +1,28 @@
+menu "Soc Audio for the RDA FPGA chips."
+
+
+config SND_RDA_SOC_FPGA
+ tristate "SoC Audio for the RDA FPGA"
+ depends on ARCH_RDA
+
+config SND_RDA_SOC_RDASOUNDCARD_FPGA
+ tristate "rda FPGA sound card -- SoC Audio support for RAD FPGA boards."
+ depends on SND_RDA_SOC_FPGA
+ select SND_RDA_SOC_RDASOUNDCARD_FPGA_PCM
+ select SND_RDA_SOC_RDASOUNDCARD_FPGA_DAI
+ select SND_SOC_TLV320AIC23
+ help
+ Say Y if you want to build in to kernel, Say M for module.
+
+config SND_RDA_SOC_RDASOUNDCARD_FPGA_PCM
+ tristate
+
+
+config SND_RDA_SOC_RDASOUNDCARD_FPGA_DAI
+ tristate
+
+config SND_SOC_TLV320AIC23
+ tristate
+
+
+endmenu
diff --git a/sound/soc/rdafpga/Makefile b/sound/soc/rdafpga/Makefile
new file mode 100644
index 000000000000..f2406df531a3
--- /dev/null
+++ b/sound/soc/rdafpga/Makefile
@@ -0,0 +1,14 @@
+# rda Platform Support
+
+rda-audio-ifc-objs := aud_ifc.o
+audio-codec-spi-objs := tlv320aic23_spi.o
+snd-rda-rdasoundcard-fpba-objs := rdafpga_soundcard.o
+snd-rda-rdasoundcard-fpba-pcm-objs := rdafpga_pcm.o
+snd-rda-rdasoundcard-fpba-dai-objs := rdafpga_dai.o
+
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_FPGA) += rda-audio-ifc.o
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_FPGA) += audio-codec-spi.o
+
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_FPGA) += snd-rda-rdasoundcard-fpba.o
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_FPGA_PCM) += snd-rda-rdasoundcard-fpba-pcm.o
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_FPGA_DAI) += snd-rda-rdasoundcard-fpba-dai.o
diff --git a/sound/soc/rdafpga/aif.h b/sound/soc/rdafpga/aif.h
new file mode 100644
index 000000000000..283c70ed9a5b
--- /dev/null
+++ b/sound/soc/rdafpga/aif.h
@@ -0,0 +1,244 @@
+/*
+ * aif.h- RDA audio interface
+ *
+ * Copyright (C) 2013 RDA Microelectronics Inc.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ */
+#ifndef _AIF_H_
+#define _AIF_H_
+
+#ifdef CT_ASM
+#error "You are trying to use in an assembly code the normal H description of 'aif'."
+#endif
+
+
+
+// =============================================================================
+// MACROS
+// =============================================================================
+
+// ============================================================================
+// AIF_SAMPLING_RATE_T
+// -----------------------------------------------------------------------------
+///
+// =============================================================================
+typedef enum
+{
+ AIF_8K = 0x00000000,
+ AIF_11K025 = 0x00000001,
+ AIF_12K = 0x00000002,
+ AIF_16K = 0x00000003,
+ AIF_22K05 = 0x00000004,
+ AIF_24K = 0x00000005,
+ AIF_32K = 0x00000006,
+ AIF_44K1 = 0x00000007,
+ AIF_48K = 0x00000008
+} AIF_SAMPLING_RATE_T;
+
+#define AIF_RX_FIFO_SIZE (4)
+#define AIF_TX_FIFO_SIZE (4)
+
+// =============================================================================
+// TYPES
+// =============================================================================
+
+// ============================================================================
+// AIF_T
+// -----------------------------------------------------------------------------
+///
+// =============================================================================
+#define REG_AP_AIF_BASE 0x009E0000
+
+typedef volatile struct
+{
+ REG32 data; //0x00000000
+ REG32 ctrl; //0x00000004
+ REG32 serial_ctrl; //0x00000008
+ REG32 tone; //0x0000000C
+ REG32 side_tone; //0x00000010
+ REG32 Cfg_Clk_AudioBCK; //0x00000014
+ REG32 Cfg_Aif_Tx_Stb; //0x00000018
+} HWP_AIF_T;
+
+//#define hwp_apAif ((HWP_AIF_T*) KSEG1(REG_AP_AIF_BASE))
+
+
+//data
+#define AIF_DATA0(n) (((n)&0xFFFF)<<0)
+#define AIF_DATA1(n) (((n)&0xFFFF)<<16)
+
+//ctrl
+#define AIF_ENABLE (1<<0)
+#define AIF_ENABLE_ENABLE (1<<0)
+#define AIF_ENABLE_DISABLE (0<<0)
+#define AIF_TX_OFF (1<<4)
+#define AIF_TX_OFF_TX_ON (0<<4)
+#define AIF_TX_OFF_TX_OFF (1<<4)
+#define AIF_PARALLEL_OUT_SET (1<<8)
+#define AIF_PARALLEL_OUT_SET_SERL (0<<8)
+#define AIF_PARALLEL_OUT_SET_PARA (1<<8)
+#define AIF_PARALLEL_OUT_CLR (1<<9)
+#define AIF_PARALLEL_OUT_CLR_SERL (0<<9)
+#define AIF_PARALLEL_OUT_CLR_PARA (1<<9)
+#define AIF_PARALLEL_IN_SET (1<<10)
+#define AIF_PARALLEL_IN_SET_SERL (0<<10)
+#define AIF_PARALLEL_IN_SET_PARA (1<<10)
+#define AIF_PARALLEL_IN_CLR (1<<11)
+#define AIF_PARALLEL_IN_CLR_SERL (0<<11)
+#define AIF_PARALLEL_IN_CLR_PARA (1<<11)
+#define AIF_TX_STB_MODE (1<<12)
+#define AIF_PARALLEL_IN2_EN (1<<13)
+#define AIF_PARALLEL_IN2_EN_DISABLE (0<<13)
+#define AIF_PARALLEL_IN2_EN_ENABLE (1<<13)
+#define AIF_OUT_UNDERFLOW (1<<16)
+#define AIF_IN_OVERFLOW (1<<17)
+#define AIF_LOOP_BACK (1<<31)
+#define AIF_LOOP_BACK_NORMAL (0<<31)
+#define AIF_LOOP_BACK_LOOPBACK (1<<31)
+
+//serial_ctrl
+#define AIF_SERIAL_MODE(n) (((n)&3)<<0)
+#define AIF_SERIAL_MODE_I2S_PCM (0<<0)
+#define AIF_SERIAL_MODE_VOICE (1<<0)
+#define AIF_SERIAL_MODE_DAI (2<<0)
+#define AIF_I2S_IN_SEL(n) (((n)&3)<<2)
+#define AIF_I2S_IN_SEL_I2S_IN_0 (0<<2)
+#define AIF_I2S_IN_SEL_I2S_IN_1 (1<<2)
+#define AIF_I2S_IN_SEL_I2S_IN_2 (2<<2)
+#define AIF_MASTER_MODE (1<<4)
+#define AIF_MASTER_MODE_SLAVE (0<<4)
+#define AIF_MASTER_MODE_MASTER (1<<4)
+#define AIF_LSB (1<<5)
+#define AIF_LSB_MSB (0<<5)
+#define AIF_LSB_LSB (1<<5)
+#define AIF_LRCK_POL (1<<6)
+#define AIF_LRCK_POL_LEFT_H_RIGHT_L (0<<6)
+#define AIF_LRCK_POL_LEFT_L_RIGHT_H (1<<6)
+#define AIF_RX_DLY(n) (((n)&3)<<8)
+#define AIF_RX_DLY_ALIGN (0<<8)
+#define AIF_RX_DLY_DLY_1 (1<<8)
+#define AIF_RX_DLY_DLY_2 (2<<8)
+#define AIF_RX_DLY_DLY_3 (3<<8)
+#define AIF_TX_DLY (1<<10)
+#define AIF_TX_DLY_ALIGN (0<<10)
+#define AIF_TX_DLY_DLY_1 (1<<10)
+#define AIF_TX_DLY_S (1<<11)
+#define AIF_TX_DLY_S_NO_DLY (0<<11)
+#define AIF_TX_DLY_S_DLY (1<<11)
+#define AIF_TX_MODE(n) (((n)&3)<<12)
+#define AIF_TX_MODE_STEREO_STEREO (0<<12)
+#define AIF_TX_MODE_MONO_STEREO_CHAN_L (1<<12)
+#define AIF_TX_MODE_MONO_STEREO_DUPLI (2<<12)
+#define AIF_TX_MODE_STEREO_TO_MONO (3<<12)
+#define AIF_RX_MODE (1<<14)
+#define AIF_RX_MODE_STEREO_STEREO (0<<14)
+#define AIF_RX_MODE_STEREO_MONO_FROM_L (1<<14)
+#define AIF_BCK_LRCK(n) (((n)&31)<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_16 (0<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_17 (1<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_18 (2<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_19 (3<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_20 (4<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_21 (5<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_22 (6<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_23 (7<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_24 (8<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_25 (9<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_26 (10<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_27 (11<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_28 (12<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_29 (13<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_30 (14<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_31 (15<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_32 (16<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_33 (17<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_34 (18<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_35 (19<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_36 (20<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_37 (21<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_38 (22<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_39 (23<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_40 (24<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_41 (25<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_42 (26<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_43 (27<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_44 (28<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_45 (29<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_46 (30<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_47 (31<<16)
+#define AIF_OUTPUT_HALF_CYCLE_DLY (1<<25)
+#define AIF_OUTPUT_HALF_CYCLE_DLY_NO_DLY (0<<25)
+#define AIF_OUTPUT_HALF_CYCLE_DLY_DLY (1<<25)
+#define AIF_INPUT_HALF_CYCLE_DLY (1<<26)
+#define AIF_INPUT_HALF_CYCLE_DLY_NO_DLY (0<<26)
+#define AIF_INPUT_HALF_CYCLE_DLY_DLY (1<<26)
+#define AIF_BCKOUT_GATE (1<<28)
+#define AIF_BCKOUT_GATE_NO_GATE (0<<28)
+#define AIF_BCKOUT_GATE_GATED (1<<28)
+
+//tone
+#define AIF_ENABLE_H (1<<0)
+#define AIF_ENABLE_H_DISABLE (0<<0)
+#define AIF_ENABLE_H_ENABLE (1<<0)
+#define AIF_TONE_SELECT (1<<1)
+#define AIF_TONE_SELECT_DTMF (0<<1)
+#define AIF_TONE_SELECT_COMFORT_TONE (1<<1)
+#define AIF_DTMF_FREQ_COL(n) (((n)&3)<<4)
+#define AIF_DTMF_FREQ_COL_1209_HZ (0<<4)
+#define AIF_DTMF_FREQ_COL_1336_HZ (1<<4)
+#define AIF_DTMF_FREQ_COL_1477_HZ (2<<4)
+#define AIF_DTMF_FREQ_COL_1633_HZ (3<<4)
+#define AIF_DTMF_FREQ_ROW(n) (((n)&3)<<6)
+#define AIF_DTMF_FREQ_ROW_697_HZ (0<<6)
+#define AIF_DTMF_FREQ_ROW_770_HZ (1<<6)
+#define AIF_DTMF_FREQ_ROW_852_HZ (2<<6)
+#define AIF_DTMF_FREQ_ROW_941_HZ (3<<6)
+#define AIF_COMFORT_FREQ(n) (((n)&3)<<8)
+#define AIF_COMFORT_FREQ_425_HZ (0<<8)
+#define AIF_COMFORT_FREQ_950_HZ (1<<8)
+#define AIF_COMFORT_FREQ_1400_HZ (2<<8)
+#define AIF_COMFORT_FREQ_1800_HZ (3<<8)
+#define AIF_TONE_GAIN(n) (((n)&3)<<12)
+#define AIF_TONE_GAIN_0_DB (0<<12)
+#define AIF_TONE_GAIN_M3_DB (1<<12)
+#define AIF_TONE_GAIN_M9_DB (2<<12)
+#define AIF_TONE_GAIN_M15_DB (3<<12)
+
+//side_tone
+#define AIF_SIDE_TONE_GAIN(n) (((n)&15)<<0)
+
+//Cfg_Clk_AudioBCK
+#define AIF_AUDIOBCK_DIVIDER(n) (((n)&0x7FF)<<0)
+#define AIF_BCK_POL (1<<16)
+#define AIF_BCK_POL_NORMAL (0<<16)
+#define AIF_BCK_POL_INVERT (1<<16)
+#define AIF_BCK_SEL_PLL (1<<20)
+#define AIF_BCK_PLL_SOURCE (1<<21)
+#define AIF_BCK_PLL_SOURCE_PLL_150M (0<<21)
+#define AIF_BCK_PLL_SOURCE_PLL_CODEC (1<<21)
+
+//Cfg_Aif_Tx_Stb
+#define AIF_AIF_TX_STB_DIV(n) (((n)&0x3FFF)<<0)
+#define AIF_AIF_TX_STB_26M_EN (1<<30)
+#define AIF_AIF_TX_STB_EN (1<<31)
+
+
+
+
+
+#endif
+
diff --git a/sound/soc/rdafpga/aif_asm.h b/sound/soc/rdafpga/aif_asm.h
new file mode 100644
index 000000000000..3076f1ab6095
--- /dev/null
+++ b/sound/soc/rdafpga/aif_asm.h
@@ -0,0 +1,211 @@
+#ifndef _AIF_ASM_H_
+#define _AIF_ASM_H_
+
+//THIS FILE HAS BEEN GENERATED WITH COOLWATCHER. PLEASE EDIT WITH CARE !
+
+#ifndef CT_ASM
+#error "You are trying to use in a normal C code the assembly H description of 'aif'."
+#endif
+
+
+#define AIF_8K (0)
+#define AIF_11K025 (1)
+#define AIF_12K (2)
+#define AIF_16K (3)
+#define AIF_22K05 (4)
+#define AIF_24K (5)
+#define AIF_32K (6)
+#define AIF_44K1 (7)
+#define AIF_48K (8)
+#define AIF_RX_FIFO_SIZE (4)
+#define AIF_TX_FIFO_SIZE (4)
+
+//==============================================================================
+// AIF_Sampling_Rate
+//------------------------------------------------------------------------------
+///
+//==============================================================================
+
+
+
+//==============================================================================
+// aif
+//------------------------------------------------------------------------------
+///
+//==============================================================================
+#define REG_AP_AIF_BASE 0x009E0000
+
+#define REG_AP_AIF_BASE_HI BASE_HI(REG_AP_AIF_BASE)
+#define REG_AP_AIF_BASE_LO BASE_LO(REG_AP_AIF_BASE)
+
+#define REG_AP_AIF_DATA REG_AP_AIF_BASE_LO + 0x00000000
+#define REG_AP_AIF_CTRL REG_AP_AIF_BASE_LO + 0x00000004
+#define REG_AP_AIF_SERIAL_CTRL REG_AP_AIF_BASE_LO + 0x00000008
+#define REG_AP_AIF_TONE REG_AP_AIF_BASE_LO + 0x0000000C
+#define REG_AP_AIF_SIDE_TONE REG_AP_AIF_BASE_LO + 0x00000010
+#define REG_AP_AIF_CFG_CLK_AUDIOBCK REG_AP_AIF_BASE_LO + 0x00000014
+#define REG_AP_AIF_CFG_AIF_TX_STB REG_AP_AIF_BASE_LO + 0x00000018
+
+//data
+#define AIF_DATA0(n) (((n)&0xFFFF)<<0)
+#define AIF_DATA1(n) (((n)&0xFFFF)<<16)
+
+//ctrl
+#define AIF_ENABLE (1<<0)
+#define AIF_ENABLE_ENABLE (1<<0)
+#define AIF_ENABLE_DISABLE (0<<0)
+#define AIF_TX_OFF (1<<4)
+#define AIF_TX_OFF_TX_ON (0<<4)
+#define AIF_TX_OFF_TX_OFF (1<<4)
+#define AIF_PARALLEL_OUT_SET (1<<8)
+#define AIF_PARALLEL_OUT_SET_SERL (0<<8)
+#define AIF_PARALLEL_OUT_SET_PARA (1<<8)
+#define AIF_PARALLEL_OUT_CLR (1<<9)
+#define AIF_PARALLEL_OUT_CLR_SERL (0<<9)
+#define AIF_PARALLEL_OUT_CLR_PARA (1<<9)
+#define AIF_PARALLEL_IN_SET (1<<10)
+#define AIF_PARALLEL_IN_SET_SERL (0<<10)
+#define AIF_PARALLEL_IN_SET_PARA (1<<10)
+#define AIF_PARALLEL_IN_CLR (1<<11)
+#define AIF_PARALLEL_IN_CLR_SERL (0<<11)
+#define AIF_PARALLEL_IN_CLR_PARA (1<<11)
+#define AIF_TX_STB_MODE (1<<12)
+#define AIF_PARALLEL_IN2_EN (1<<13)
+#define AIF_PARALLEL_IN2_EN_DISABLE (0<<13)
+#define AIF_PARALLEL_IN2_EN_ENABLE (1<<13)
+#define AIF_OUT_UNDERFLOW (1<<16)
+#define AIF_IN_OVERFLOW (1<<17)
+#define AIF_LOOP_BACK (1<<31)
+#define AIF_LOOP_BACK_NORMAL (0<<31)
+#define AIF_LOOP_BACK_LOOPBACK (1<<31)
+
+//serial_ctrl
+#define AIF_SERIAL_MODE(n) (((n)&3)<<0)
+#define AIF_SERIAL_MODE_I2S_PCM (0<<0)
+#define AIF_SERIAL_MODE_VOICE (1<<0)
+#define AIF_SERIAL_MODE_DAI (2<<0)
+#define AIF_I2S_IN_SEL(n) (((n)&3)<<2)
+#define AIF_I2S_IN_SEL_I2S_IN_0 (0<<2)
+#define AIF_I2S_IN_SEL_I2S_IN_1 (1<<2)
+#define AIF_I2S_IN_SEL_I2S_IN_2 (2<<2)
+#define AIF_MASTER_MODE (1<<4)
+#define AIF_MASTER_MODE_SLAVE (0<<4)
+#define AIF_MASTER_MODE_MASTER (1<<4)
+#define AIF_LSB (1<<5)
+#define AIF_LSB_MSB (0<<5)
+#define AIF_LSB_LSB (1<<5)
+#define AIF_LRCK_POL (1<<6)
+#define AIF_LRCK_POL_LEFT_H_RIGHT_L (0<<6)
+#define AIF_LRCK_POL_LEFT_L_RIGHT_H (1<<6)
+#define AIF_RX_DLY(n) (((n)&3)<<8)
+#define AIF_RX_DLY_ALIGN (0<<8)
+#define AIF_RX_DLY_DLY_1 (1<<8)
+#define AIF_RX_DLY_DLY_2 (2<<8)
+#define AIF_RX_DLY_DLY_3 (3<<8)
+#define AIF_TX_DLY (1<<10)
+#define AIF_TX_DLY_ALIGN (0<<10)
+#define AIF_TX_DLY_DLY_1 (1<<10)
+#define AIF_TX_DLY_S (1<<11)
+#define AIF_TX_DLY_S_NO_DLY (0<<11)
+#define AIF_TX_DLY_S_DLY (1<<11)
+#define AIF_TX_MODE(n) (((n)&3)<<12)
+#define AIF_TX_MODE_STEREO_STEREO (0<<12)
+#define AIF_TX_MODE_MONO_STEREO_CHAN_L (1<<12)
+#define AIF_TX_MODE_MONO_STEREO_DUPLI (2<<12)
+#define AIF_TX_MODE_STEREO_TO_MONO (3<<12)
+#define AIF_RX_MODE (1<<14)
+#define AIF_RX_MODE_STEREO_STEREO (0<<14)
+#define AIF_RX_MODE_STEREO_MONO_FROM_L (1<<14)
+#define AIF_BCK_LRCK(n) (((n)&31)<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_16 (0<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_17 (1<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_18 (2<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_19 (3<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_20 (4<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_21 (5<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_22 (6<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_23 (7<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_24 (8<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_25 (9<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_26 (10<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_27 (11<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_28 (12<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_29 (13<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_30 (14<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_31 (15<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_32 (16<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_33 (17<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_34 (18<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_35 (19<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_36 (20<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_37 (21<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_38 (22<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_39 (23<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_40 (24<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_41 (25<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_42 (26<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_43 (27<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_44 (28<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_45 (29<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_46 (30<<16)
+#define AIF_BCK_LRCK_BCK_LRCK_47 (31<<16)
+#define AIF_OUTPUT_HALF_CYCLE_DLY (1<<25)
+#define AIF_OUTPUT_HALF_CYCLE_DLY_NO_DLY (0<<25)
+#define AIF_OUTPUT_HALF_CYCLE_DLY_DLY (1<<25)
+#define AIF_INPUT_HALF_CYCLE_DLY (1<<26)
+#define AIF_INPUT_HALF_CYCLE_DLY_NO_DLY (0<<26)
+#define AIF_INPUT_HALF_CYCLE_DLY_DLY (1<<26)
+#define AIF_BCKOUT_GATE (1<<28)
+#define AIF_BCKOUT_GATE_NO_GATE (0<<28)
+#define AIF_BCKOUT_GATE_GATED (1<<28)
+
+//tone
+#define AIF_ENABLE_H (1<<0)
+#define AIF_ENABLE_H_DISABLE (0<<0)
+#define AIF_ENABLE_H_ENABLE (1<<0)
+#define AIF_TONE_SELECT (1<<1)
+#define AIF_TONE_SELECT_DTMF (0<<1)
+#define AIF_TONE_SELECT_COMFORT_TONE (1<<1)
+#define AIF_DTMF_FREQ_COL(n) (((n)&3)<<4)
+#define AIF_DTMF_FREQ_COL_1209_HZ (0<<4)
+#define AIF_DTMF_FREQ_COL_1336_HZ (1<<4)
+#define AIF_DTMF_FREQ_COL_1477_HZ (2<<4)
+#define AIF_DTMF_FREQ_COL_1633_HZ (3<<4)
+#define AIF_DTMF_FREQ_ROW(n) (((n)&3)<<6)
+#define AIF_DTMF_FREQ_ROW_697_HZ (0<<6)
+#define AIF_DTMF_FREQ_ROW_770_HZ (1<<6)
+#define AIF_DTMF_FREQ_ROW_852_HZ (2<<6)
+#define AIF_DTMF_FREQ_ROW_941_HZ (3<<6)
+#define AIF_COMFORT_FREQ(n) (((n)&3)<<8)
+#define AIF_COMFORT_FREQ_425_HZ (0<<8)
+#define AIF_COMFORT_FREQ_950_HZ (1<<8)
+#define AIF_COMFORT_FREQ_1400_HZ (2<<8)
+#define AIF_COMFORT_FREQ_1800_HZ (3<<8)
+#define AIF_TONE_GAIN(n) (((n)&3)<<12)
+#define AIF_TONE_GAIN_0_DB (0<<12)
+#define AIF_TONE_GAIN_M3_DB (1<<12)
+#define AIF_TONE_GAIN_M9_DB (2<<12)
+#define AIF_TONE_GAIN_M15_DB (3<<12)
+
+//side_tone
+#define AIF_SIDE_TONE_GAIN(n) (((n)&15)<<0)
+
+//Cfg_Clk_AudioBCK
+#define AIF_AUDIOBCK_DIVIDER(n) (((n)&0x7FF)<<0)
+#define AIF_BCK_POL (1<<16)
+#define AIF_BCK_POL_NORMAL (0<<16)
+#define AIF_BCK_POL_INVERT (1<<16)
+#define AIF_BCK_SEL_PLL (1<<20)
+#define AIF_BCK_PLL_SOURCE (1<<21)
+#define AIF_BCK_PLL_SOURCE_PLL_150M (0<<21)
+#define AIF_BCK_PLL_SOURCE_PLL_CODEC (1<<21)
+
+//Cfg_Aif_Tx_Stb
+#define AIF_AIF_TX_STB_DIV(n) (((n)&0x3FFF)<<0)
+#define AIF_AIF_TX_STB_26M_EN (1<<30)
+#define AIF_AIF_TX_STB_EN (1<<31)
+
+
+
+
+#endif
diff --git a/sound/soc/rdafpga/aud_ifc.c b/sound/soc/rdafpga/aud_ifc.c
new file mode 100644
index 000000000000..9649297ce1cd
--- /dev/null
+++ b/sound/soc/rdafpga/aud_ifc.c
@@ -0,0 +1,369 @@
+/*
+ * aud_ifc.c -- AUDIO IFC interface for the RDA SoC
+ *
+ * Copyright (C) 2012 RDA Microelectronics (Beijing) Co., Ltd.
+ *
+ * Contact: Xu Mingliang <mingliangxu@rdamicro.com>
+ *
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+
+#include <plat/devices.h>
+
+
+#include "bb_ifc.h"
+#include "aud_ifc.h"
+
+
+static HWP_BB_IFC_T __iomem *hwp_audIfc;
+
+static struct rda_audifc_ch *audifc_ch[RDA_AUDIFC_QTY];
+
+
+static irqreturn_t rda_audifc_record_irq_handler(int irq, void *channel)
+{
+
+ struct rda_audifc_ch *ptr_ch = (struct rda_audifc_ch *)channel;
+ u32 reg;
+
+ printk(KERN_INFO "record\n");
+
+ reg = hwp_audIfc->ch[RDA_AUDIFC_RECORD].status;
+
+ if (reg & BB_IFC_I4F) {
+ ptr_ch->callback(ptr_ch->id, RDA_AUDIFC_QUARTER_IRQ, ptr_ch->data);
+ return IRQ_HANDLED;
+ }else if (reg & BB_IFC_IHF) {
+ ptr_ch->callback(ptr_ch->id, RDA_AUDIFC_HALF_IRQ, ptr_ch->data);
+ return IRQ_HANDLED;
+ }else if (reg & BB_IFC_I3_4F) {
+ ptr_ch->callback(ptr_ch->id, RDA_AUDIFC_THREE_QUARTER_IRQ, ptr_ch->data);
+ return IRQ_HANDLED;
+ }else if (reg & BB_IFC_IEF) {
+ ptr_ch->callback(ptr_ch->id, RDA_AUDIFC_END_IRQ, ptr_ch->data);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static irqreturn_t rda_audifc_play_irq_handler(int irq, void *channel)
+{
+
+
+ struct rda_audifc_ch *ptr_ch = (struct rda_audifc_ch *)channel;
+ u32 reg;
+ //printk(KERN_INFO "play\n");
+
+ if (ptr_ch->callback==NULL)
+ return IRQ_NONE;
+
+ reg = hwp_audIfc->ch[RDA_AUDIFC_PLAY].status&(BB_IFC_CAUSE_IEF|BB_IFC_CAUSE_IHF|BB_IFC_CAUSE_I4F|BB_IFC_CAUSE_I3_4F);
+
+ hwp_audIfc->ch[RDA_AUDIFC_PLAY].int_clear=reg;
+
+ if (reg & BB_IFC_CAUSE_I4F) {
+ ptr_ch->callback(ptr_ch->id, RDA_AUDIFC_QUARTER_IRQ, ptr_ch->data);
+ return IRQ_HANDLED;
+ }else if (reg & BB_IFC_CAUSE_IHF) {
+ ptr_ch->callback(ptr_ch->id, RDA_AUDIFC_HALF_IRQ, ptr_ch->data);
+ return IRQ_HANDLED;
+ }else if (reg & BB_IFC_CAUSE_I3_4F) {
+ ptr_ch->callback(ptr_ch->id, RDA_AUDIFC_THREE_QUARTER_IRQ, ptr_ch->data);
+ return IRQ_HANDLED;
+ }else if (reg & BB_IFC_CAUSE_IEF) {
+ ptr_ch->callback(ptr_ch->id, RDA_AUDIFC_END_IRQ, ptr_ch->data);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+int rda_set_audifc_params(u8 ch, struct rda_audifc_chan_params *params)
+{
+
+ printk(KERN_INFO "rda_set_audifc_params:%d\n",ch);
+ printk(KERN_INFO "params->src_addr:0x%x\n",params->src_addr);
+ printk(KERN_INFO "params->xfer_size:%ld\n",params->xfer_size);
+
+ if (hwp_audIfc->ch[ch].status & BB_IFC_ENABLE){
+ //dev_err(&pdev->dev, "%s: audio ifc busy \n", __func__);
+ printk(KERN_INFO "%s: audio ifc busy \n", __func__);
+ return RDA_AUDIFC_ERROR;
+ }
+
+ // Assert on word alignement
+ if (((u32)params->src_addr)%4 != 0){
+ //dev_err(&pdev->dev, "%s: AUDIO IFC transfer start address not aligned: 0x%x", __func__ , (u32)params->src_addr);
+ printk(KERN_INFO "%s: AUDIO IFC transfer start address not aligned: 0x%x", __func__ , (u32)params->src_addr);
+ return RDA_AUDIFC_ERROR;
+ }
+
+#if 0
+ // Size must be a multiple of 32 bytes
+ if (((u32)params->xfer_size)%32 != 0){
+ //dev_err(&pdev->dev, "%s: AUDIO IFC transfer size not mult. of 32-bytes: 0x%x", __func__ , (u32)params->xfer_size);
+ printk(KERN_INFO "%s: AUDIO IFC transfer size not mult. of 32-bytes: 0x%x", __func__ , (u32)params->xfer_size);
+ return RDA_AUDIFC_ERROR;
+ }
+#endif
+
+ hwp_audIfc->ch[ch].start_addr = (u32)params->src_addr;//(u32)0x100000+16*1024;// (u32)params->src_addr;
+
+ hwp_audIfc->ch[ch].Fifo_Size =params->xfer_size;// 16*1024;//params->xfer_size;
+
+ hwp_audIfc->ch[ch].int_mask = BB_IFC_QUARTER_FIFO|
+ BB_IFC_HALF_FIFO|
+ BB_IFC_THREE_QUARTER_FIFO|
+ BB_IFC_END_FIFO;
+
+ return RDA_AUDIFC_NOERR;
+}
+
+
+
+audifc_addr_t rda_get_audifc_src_pos(u8 ch)
+{
+
+// printk(KERN_INFO "rda_get_audifc_src_pos ch:0x%x\n",(u32)(hwp_audIfc->ch[ch].cur_ahb_addr));
+
+ return (u32)(hwp_audIfc->ch[ch].cur_ahb_addr);
+}
+
+
+
+audifc_addr_t rda_get_audifc_dst_pos(u8 ch)
+{
+// printk(KERN_INFO "rda_get_audifc_src_pos ch:0x%x\n",(u32)(hwp_audIfc->ch[ch].cur_ahb_addr));
+ return (u32)(hwp_audIfc->ch[ch].cur_ahb_addr);
+}
+
+
+
+
+void rda_start_audifc(u8 ch)
+{
+
+ printk(KERN_INFO "rda_start_audifc ch:%d\n",ch);
+
+ hwp_audIfc->ch[ch].control = BB_IFC_ENABLE;
+
+ return;
+}
+
+
+
+void rda_stop_audifc(u8 ch)
+{
+ printk(KERN_INFO "rda_stop_audifc ch:%d\n",ch);
+
+ hwp_audIfc->ch[ch].control = BB_IFC_DISABLE;
+
+ return;
+}
+
+
+
+void rda_poll_audifc(u8 ch)
+{
+//TO BE DONG with TIME CONTROLLING......
+// while (!(hwp_audIfc->ch[ch].status & BB_IFC_FIFO_EMPTY)) {
+ /* Nothing to do */
+// };
+
+ return;
+}
+
+
+
+int rda_request_audifc(int dev_id, const char *dev_name,
+ void (*callback) (int ch, int state, void *data), void *data,
+ int * audifc_ch_out)
+{
+ /* Clear interrupt flag and disble audifc. */
+ rda_stop_audifc(dev_id);
+
+ audifc_ch[dev_id]->id=dev_id;
+ audifc_ch[dev_id]->dev_name = dev_name;
+ audifc_ch[dev_id]->callback = callback;
+ audifc_ch[dev_id]->data = data;
+
+ *audifc_ch_out = dev_id;
+
+ return 0;
+}
+
+
+
+void rda_free_audifc(u8 ch)
+{
+ audifc_ch[ch]->dev_name = NULL;
+ audifc_ch[ch]->callback = NULL;
+ audifc_ch[ch]->data = NULL;
+
+ return;
+}
+
+
+
+static int __devinit rda_audifc_probe(struct platform_device *pdev)
+{
+ struct rda_audifc_device_data *audifc_device;
+ struct resource *mem;
+ int irq[RDA_AUDIFC_QTY];
+ int ret;
+ dev_err(&pdev->dev, "rda_audifc_probe\n");
+
+ audifc_device = pdev->dev.platform_data;
+ if (!audifc_device) {
+ dev_err(&pdev->dev, "%s: rda audifc initialized without platform data\n", __func__);
+ return -EINVAL;
+ }
+
+ audifc_ch[RDA_AUDIFC_RECORD] = &audifc_device->chan[RDA_AUDIFC_RECORD];
+ audifc_ch[RDA_AUDIFC_RECORD]->id = RDA_AUDIFC_RECORD;
+ audifc_ch[RDA_AUDIFC_RECORD]->dev_name = NULL;
+ audifc_ch[RDA_AUDIFC_RECORD]->callback = NULL;
+ audifc_ch[RDA_AUDIFC_RECORD]->data = NULL;
+
+ audifc_ch[RDA_AUDIFC_PLAY] = &audifc_device->chan[RDA_AUDIFC_PLAY];
+ audifc_ch[RDA_AUDIFC_PLAY]->id = RDA_AUDIFC_PLAY;
+ audifc_ch[RDA_AUDIFC_PLAY]->dev_name = NULL;
+ audifc_ch[RDA_AUDIFC_PLAY]->callback = NULL;
+ audifc_ch[RDA_AUDIFC_PLAY]->data = NULL;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "%s: no mem resource\n", __func__);
+ return -EINVAL;
+ }
+
+ irq[RDA_AUDIFC_RECORD] = platform_get_irq(pdev, RDA_AUDIFC_RECORD);
+ if (irq[RDA_AUDIFC_RECORD] < 0) {
+ return irq[RDA_AUDIFC_RECORD];
+ }
+
+ irq[RDA_AUDIFC_PLAY] = platform_get_irq(pdev, RDA_AUDIFC_PLAY);
+ if (irq[RDA_AUDIFC_PLAY] < 0) {
+ return irq[RDA_AUDIFC_PLAY];
+ }
+
+ hwp_audIfc = ioremap(mem->start, resource_size(mem));
+ if (!hwp_audIfc) {
+ dev_err(&pdev->dev, "%s: ioremap fail\n", __func__);
+ return -ENOMEM;
+ }
+ /*
+ * The flag, IRQF_TRIGGER_RISING, is not for device.
+ * It's only for calling mask_irq function to enable interrupt.
+ */
+ ret = request_irq(irq[RDA_AUDIFC_RECORD], rda_audifc_record_irq_handler,
+ 0, "rda-audifc", (void *)audifc_ch[RDA_AUDIFC_RECORD]);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s: request record irq fail\n", __func__);
+ iounmap(hwp_audIfc);
+ return ret;
+ }
+ ret = request_irq(irq[RDA_AUDIFC_PLAY], rda_audifc_play_irq_handler,
+ 0, "rda-audifc", (void *)audifc_ch[RDA_AUDIFC_PLAY]);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s: request play irq fail\n", __func__);
+ iounmap(hwp_audIfc);
+ return ret;
+ }
+ printk(KERN_INFO "rda audifc is initialized!\n");
+
+ return 0;
+}
+
+static int __devexit rda_audifc_remove(struct platform_device *pdev)
+{
+ //struct rda_audifc_device_data *audifc_device = pdev->dev.platform_data;
+ struct resource *io;
+ int irq[2];
+
+ iounmap(hwp_audIfc);
+ hwp_audIfc=NULL;
+
+
+ io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(io->start, resource_size(io));
+
+ //free record irq
+ irq[RDA_AUDIFC_RECORD] = platform_get_irq(pdev, RDA_AUDIFC_RECORD);
+ free_irq(irq[RDA_AUDIFC_RECORD], (void *)audifc_ch[RDA_AUDIFC_RECORD]);
+
+ if(audifc_ch[RDA_AUDIFC_RECORD]!= NULL){
+ audifc_ch[RDA_AUDIFC_RECORD]->id = RDA_AUDIFC_RECORD;
+ audifc_ch[RDA_AUDIFC_RECORD]->dev_name = NULL;
+ audifc_ch[RDA_AUDIFC_RECORD]->callback = NULL;
+ audifc_ch[RDA_AUDIFC_RECORD]->data = NULL;
+ audifc_ch[RDA_AUDIFC_RECORD] = NULL;
+
+ }
+
+ //free record irq
+ irq[RDA_AUDIFC_PLAY] = platform_get_irq(pdev, RDA_AUDIFC_PLAY);
+ free_irq(irq[RDA_AUDIFC_PLAY], (void *)audifc_ch[RDA_AUDIFC_PLAY]);
+
+ if(audifc_ch[RDA_AUDIFC_PLAY]!= NULL){
+
+ audifc_ch[RDA_AUDIFC_PLAY]->id = RDA_AUDIFC_PLAY;
+ audifc_ch[RDA_AUDIFC_PLAY]->dev_name = NULL;
+ audifc_ch[RDA_AUDIFC_PLAY]->callback = NULL;
+ audifc_ch[RDA_AUDIFC_PLAY]->data = NULL;
+ audifc_ch[RDA_AUDIFC_PLAY] = NULL;
+ }
+
+ return 0;
+}
+
+static struct platform_driver rda_audifc_driver = {
+ .probe = rda_audifc_probe,
+ .remove = __devexit_p(rda_audifc_remove),
+ .driver = {
+ .name = "rda-audifc"},
+};
+
+static int __init rda_audifc_init(void)
+{
+ return platform_driver_register(&rda_audifc_driver);
+}
+
+module_init(rda_audifc_init);
+
+static void __exit rda_audifc_exit(void)
+{
+ platform_driver_unregister(&rda_audifc_driver);
+}
+
+module_exit(rda_audifc_exit);
+
+
+MODULE_DESCRIPTION("AUDIO IFC for RDA8810 FPGA PCM");
+MODULE_AUTHOR("Xu Mingliang <mingliangxu@rdamicro.com>");
+MODULE_ALIAS("platform: rda-audifc");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/rdafpga/aud_ifc.h b/sound/soc/rdafpga/aud_ifc.h
new file mode 100644
index 000000000000..b00d6364f3a8
--- /dev/null
+++ b/sound/soc/rdafpga/aud_ifc.h
@@ -0,0 +1,99 @@
+
+/*
+ * aud_ifc.c -- audiodma is the dma only for audio.
+ *
+ * Copyright (C) 2012 RDA Microelectronics (Beijing) Co., Ltd.
+ *
+ * Contact: Xu Mingliang <mingliangxu@rdamicro.com>
+ *
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __ASM_AUDIFC_H
+#define __ASM_AUDIFC_H
+
+
+#include <linux/bitops.h>
+
+
+// =============================================================================
+// MACROS
+// =============================================================================
+
+
+typedef enum {
+ RDA_AUDIFC_QUARTER_IRQ=0,
+ RDA_AUDIFC_HALF_IRQ=1,
+ RDA_AUDIFC_THREE_QUARTER_IRQ=2,
+ RDA_AUDIFC_END_IRQ=3,
+
+ RDA_AUDIFC_QTY_IRQ
+} RDA_AUDIFC_HANDLE_ID_T;
+
+
+
+typedef enum {
+ RDA_AUDIFC_RECORD=0,
+ RDA_AUDIFC_PLAY=1,
+
+ RDA_AUDIFC_QTY
+} RDA_AUDIFC_REQUEST_ID_T;
+
+
+
+
+typedef enum {
+ RDA_AUDIFC_NOERR=0,
+ RDA_AUDIFC_ERROR=1,
+
+} RDA_AUDIFC_ERROR_T;
+
+
+
+typedef unsigned int audifc_addr_t;
+
+struct rda_audifc_chan_params {
+ audifc_addr_t src_addr;
+ audifc_addr_t dst_addr;
+ unsigned long xfer_size;
+ unsigned long audifc_mode;
+};
+
+
+int rda_set_audifc_params(u8 ch, struct rda_audifc_chan_params *params);
+audifc_addr_t rda_get_audifc_src_pos(u8 ch);
+
+audifc_addr_t rda_get_audifc_dst_pos(u8 ch);
+
+void rda_start_audifc(u8 ch);
+
+void rda_stop_audifc(u8 ch);
+
+void rda_poll_audifc(u8 ch);
+;
+int rda_request_audifc(int dev_id, const char *dev_name,
+ void (*callback) (int ch, int state, void *data), void *data,
+ int * audifc_ch_out);
+
+void rda_free_audifc(u8 ch);
+
+#endif /* __ASM_AUDIFC_H */
+
+
+
+
+
diff --git a/sound/soc/rdafpga/bb_ifc.h b/sound/soc/rdafpga/bb_ifc.h
new file mode 100644
index 000000000000..ed75c1a8d3ea
--- /dev/null
+++ b/sound/soc/rdafpga/bb_ifc.h
@@ -0,0 +1,209 @@
+//==============================================================================
+//
+// Copyright (C) 2003-2007, Coolsand Technologies, Inc.
+// All Rights Reserved
+//
+// This source code is the property of Coolsand Technologies and is
+// confidential. Any modification, distribution, reproduction or
+// exploitation of any content of this file is totally forbidden,
+// except with the written permission of Coolsand Technologies.
+//
+//==============================================================================
+//
+// THIS FILE WAS GENERATED FROM ITS CORRESPONDING XML VERSION WITH COOLXML.
+//
+// !!! PLEASE DO NOT EDIT !!!
+//
+// $HeadURL$
+// $Author$
+// $Date$
+// $Revision$
+//
+//==============================================================================
+//
+/// @file
+//
+//==============================================================================
+
+#ifndef _BB_IFC_H_
+#define _BB_IFC_H_
+
+#ifdef CT_ASM
+#error "You are trying to use in an assembly code the normal H description of 'bb_ifc'."
+#endif
+
+
+//#include "globals.h"
+
+// =============================================================================
+// MACROS
+// =============================================================================
+#define BB_IFC_ADDR_LEN (15)
+#define BB_IFC_ADDR_ALIGN (2)
+#define BB_IFC_TC_LEN (8)
+
+// =============================================================================
+// TYPES
+// =============================================================================
+
+// ============================================================================
+// BB_IFC_T
+// -----------------------------------------------------------------------------
+///
+// =============================================================================
+#define REG_AU_IFC_BASE 0x009F0000
+#define REG_BB_IFC_BASE 0x01901000
+
+typedef volatile struct
+{
+ /// The Channel 0 conveys data from the AIF to the memory.
+ /// The Channel 1 conveys data from the memory to the AIF.
+ /// These Channels only exist with Voice Option.
+ struct
+ {
+ REG32 control; //0x00000000
+ REG32 status; //0x00000004
+ REG32 start_addr; //0x00000008
+ REG32 Fifo_Size; //0x0000000C
+ REG32 Reserved_00000010; //0x00000010
+ REG32 int_mask; //0x00000014
+ REG32 int_clear; //0x00000018
+ REG32 cur_ahb_addr; //0x0000001C
+ } ch[2];
+ REG32 ch2_control; //0x00000040
+ REG32 ch2_status; //0x00000044
+ REG32 ch2_start_addr; //0x00000048
+ REG32 ch2_end_addr; //0x0000004C
+ REG32 ch2_tc; //0x00000050
+ REG32 ch2_int_mask; //0x00000054
+ REG32 ch2_int_clear; //0x00000058
+ REG32 ch2_cur_ahb_addr; //0x0000005C
+ REG32 ch3_control; //0x00000060
+ REG32 ch3_status; //0x00000064
+ REG32 ch3_start_addr; //0x00000068
+ REG32 Reserved_0000006C; //0x0000006C
+ REG32 ch3_tc; //0x00000070
+ REG32 ch3_int_mask; //0x00000074
+ REG32 ch3_int_clear; //0x00000078
+ REG32 ch3_cur_ahb_addr; //0x0000007C
+} HWP_BB_IFC_T;
+
+//#define hwp_auIfc ((HWP_BB_IFC_T*) KSEG1(REG_AU_IFC_BASE))
+//#define hwp_bbIfc ((HWP_BB_IFC_T*) KSEG1(REG_BB_IFC_BASE))
+
+
+//control
+#define BB_IFC_ENABLE (1<<0)
+#define BB_IFC_DISABLE (1<<1)
+#define BB_IFC_AUTO_DISABLE (1<<4)
+
+//status
+//#define BB_IFC_ENABLE (1<<0)
+#define BB_IFC_FIFO_EMPTY (1<<4)
+#define BB_IFC_CAUSE_IEF (1<<8)
+#define BB_IFC_CAUSE_IHF (1<<9)
+#define BB_IFC_CAUSE_I4F (1<<10)
+#define BB_IFC_CAUSE_I3_4F (1<<11)
+#define BB_IFC_IEF (1<<16)
+#define BB_IFC_IHF (1<<17)
+#define BB_IFC_I4F (1<<18)
+#define BB_IFC_I3_4F (1<<19)
+
+//start_addr
+#define BB_IFC_START_ADDR(n) (((n)&0xFFFFFF)<<2)
+
+//Fifo_Size
+#define BB_IFC_FIFO_SIZE(n) (((n)&0x7FF)<<4)
+
+//int_mask
+#define BB_IFC_END_FIFO (1<<8)
+#define BB_IFC_HALF_FIFO (1<<9)
+#define BB_IFC_QUARTER_FIFO (1<<10)
+#define BB_IFC_THREE_QUARTER_FIFO (1<<11)
+
+//int_clear
+//#define BB_IFC_END_FIFO (1<<8)
+//#define BB_IFC_HALF_FIFO (1<<9)
+//#define BB_IFC_QUARTER_FIFO (1<<10)
+//#define BB_IFC_THREE_QUARTER_FIFO (1<<11)
+
+//cur_ahb_addr
+#define BB_IFC_CUR_AHB_ADDR(n) (((n)&0x3FFFFFF)<<0)
+
+//ch2_control
+//#define BB_IFC_ENABLE (1<<0)
+//#define BB_IFC_DISABLE (1<<1)
+#define BB_IFC_BURST_SIZE (1<<16)
+#define BB_IFC_FIFO_MODE (1<<17)
+
+//ch2_status
+//#define BB_IFC_ENABLE (1<<0)
+#define BB_IFC_ENABLE_DISABLE (0<<0)
+#define BB_IFC_ENABLE_ENABLE (1<<0)
+//#define BB_IFC_FIFO_EMPTY (1<<4)
+#define BB_IFC_CAUSE_ITC (1<<8)
+//#define BB_IFC_CAUSE_IEF (1<<9)
+#define BB_IFC_CAUSE_IHTC (1<<10)
+#define BB_IFC_ITC (1<<12)
+//#define BB_IFC_IEF (1<<13)
+#define BB_IFC_IHTC (1<<14)
+#define BB_IFC_CUR_TC(n) (((n)&0xFF)<<16)
+
+//ch2_start_addr
+//#define BB_IFC_START_ADDR(n) (((n)&0x1FFF)<<2)
+#define BB_IFC_PAGE_ADDR(n) (((n)&0x7FF)<<15)
+
+//ch2_end_addr
+#define BB_IFC_END_ADDR(n) (((n)&0x1FFF)<<2)
+//#define BB_IFC_PAGE_ADDR(n) (((n)&0x7FF)<<15)
+
+//ch2_tc
+#define BB_IFC_TC(n) (((n)&0xFF)<<0)
+
+//ch2_int_mask
+#define BB_IFC_END_TC (1<<0)
+//#define BB_IFC_END_FIFO (1<<1)
+#define BB_IFC_HALF_TC (1<<2)
+
+//ch2_int_clear
+//#define BB_IFC_END_TC (1<<0)
+//#define BB_IFC_END_FIFO (1<<1)
+//#define BB_IFC_HALF_FIFO (1<<2)
+
+//ch2_cur_ahb_addr
+//#define BB_IFC_CUR_AHB_ADDR(n) (((n)&0x3FFFFFF)<<0)
+
+//ch3_control
+//#define BB_IFC_ENABLE (1<<0)
+//#define BB_IFC_DISABLE (1<<1)
+
+//ch3_status
+//#define BB_IFC_ENABLE (1<<0)
+//#define BB_IFC_ENABLE_DISABLE (0<<0)
+//#define BB_IFC_ENABLE_ENABLE (1<<0)
+//#define BB_IFC_FIFO_EMPTY (1<<4)
+//#define BB_IFC_CAUSE_ITC (1<<8)
+//#define BB_IFC_ITC (1<<12)
+//#define BB_IFC_CUR_TC(n) (((n)&0xFF)<<16)
+
+//ch3_start_addr
+//#define BB_IFC_START_ADDR(n) (((n)&0x1FFF)<<2)
+
+//ch3_tc
+//#define BB_IFC_TC(n) (((n)&0xFF)<<0)
+
+//ch3_int_mask
+//#define BB_IFC_END_TC (1<<0)
+
+//ch3_int_clear
+//#define BB_IFC_END_TC (1<<0)
+
+//ch3_cur_ahb_addr
+//#define BB_IFC_CUR_AHB_ADDR(n) (((n)&0x3FFFFFF)<<0)
+
+
+
+
+
+#endif
+
diff --git a/sound/soc/rdafpga/bb_ifc_asm.h b/sound/soc/rdafpga/bb_ifc_asm.h
new file mode 100644
index 000000000000..3d111950e15a
--- /dev/null
+++ b/sound/soc/rdafpga/bb_ifc_asm.h
@@ -0,0 +1,202 @@
+#ifndef _BB_IFC_ASM_H_
+#define _BB_IFC_ASM_H_
+
+//THIS FILE HAS BEEN GENERATED WITH COOLWATCHER. PLEASE EDIT WITH CARE !
+
+#ifndef CT_ASM
+#error "You are trying to use in a normal C code the assembly H description of 'bb_ifc'."
+#endif
+
+#include "globals_asm.h"
+
+#define BB_IFC_ADDR_LEN (15)
+#define BB_IFC_ADDR_ALIGN (2)
+#define BB_IFC_TC_LEN (8)
+
+//==============================================================================
+// bb_ifc
+//------------------------------------------------------------------------------
+///
+//==============================================================================
+#define REG_AU_IFC_BASE 0x009F0000
+#define REG_BB_IFC_BASE 0x01901000
+
+#define REG_AU_IFC_BASE_HI BASE_HI(REG_AU_IFC_BASE)
+#define REG_AU_IFC_BASE_LO BASE_LO(REG_AU_IFC_BASE)
+
+#define REG_AU_IFC_CH_0_CONTROL REG_AU_IFC_BASE_LO + 0x00000000
+#define REG_AU_IFC_CH_0_STATUS REG_AU_IFC_BASE_LO + 0x00000004
+#define REG_AU_IFC_CH_0_START_ADDR REG_AU_IFC_BASE_LO + 0x00000008
+#define REG_AU_IFC_CH_0_FIFO_SIZE REG_AU_IFC_BASE_LO + 0x0000000C
+#define REG_AU_IFC_CH_0_INT_MASK REG_AU_IFC_BASE_LO + 0x00000014
+#define REG_AU_IFC_CH_0_INT_CLEAR REG_AU_IFC_BASE_LO + 0x00000018
+#define REG_AU_IFC_CH_0_CUR_AHB_ADDR REG_AU_IFC_BASE_LO + 0x0000001C
+#define REG_AU_IFC_CH_1_CONTROL REG_AU_IFC_BASE_LO + 0x00000020
+#define REG_AU_IFC_CH_1_STATUS REG_AU_IFC_BASE_LO + 0x00000024
+#define REG_AU_IFC_CH_1_START_ADDR REG_AU_IFC_BASE_LO + 0x00000028
+#define REG_AU_IFC_CH_1_FIFO_SIZE REG_AU_IFC_BASE_LO + 0x0000002C
+#define REG_AU_IFC_CH_1_INT_MASK REG_AU_IFC_BASE_LO + 0x00000034
+#define REG_AU_IFC_CH_1_INT_CLEAR REG_AU_IFC_BASE_LO + 0x00000038
+#define REG_AU_IFC_CH_1_CUR_AHB_ADDR REG_AU_IFC_BASE_LO + 0x0000003C
+#define REG_AU_IFC_CH2_CONTROL REG_AU_IFC_BASE_LO + 0x00000040
+#define REG_AU_IFC_CH2_STATUS REG_AU_IFC_BASE_LO + 0x00000044
+#define REG_AU_IFC_CH2_START_ADDR REG_AU_IFC_BASE_LO + 0x00000048
+#define REG_AU_IFC_CH2_END_ADDR REG_AU_IFC_BASE_LO + 0x0000004C
+#define REG_AU_IFC_CH2_TC REG_AU_IFC_BASE_LO + 0x00000050
+#define REG_AU_IFC_CH2_INT_MASK REG_AU_IFC_BASE_LO + 0x00000054
+#define REG_AU_IFC_CH2_INT_CLEAR REG_AU_IFC_BASE_LO + 0x00000058
+#define REG_AU_IFC_CH2_CUR_AHB_ADDR REG_AU_IFC_BASE_LO + 0x0000005C
+#define REG_AU_IFC_CH3_CONTROL REG_AU_IFC_BASE_LO + 0x00000060
+#define REG_AU_IFC_CH3_STATUS REG_AU_IFC_BASE_LO + 0x00000064
+#define REG_AU_IFC_CH3_START_ADDR REG_AU_IFC_BASE_LO + 0x00000068
+#define REG_AU_IFC_CH3_TC REG_AU_IFC_BASE_LO + 0x00000070
+#define REG_AU_IFC_CH3_INT_MASK REG_AU_IFC_BASE_LO + 0x00000074
+#define REG_AU_IFC_CH3_INT_CLEAR REG_AU_IFC_BASE_LO + 0x00000078
+#define REG_AU_IFC_CH3_CUR_AHB_ADDR REG_AU_IFC_BASE_LO + 0x0000007C
+
+#define REG_BB_IFC_BASE_HI BASE_HI(REG_BB_IFC_BASE)
+#define REG_BB_IFC_BASE_LO BASE_LO(REG_BB_IFC_BASE)
+
+#define REG_BB_IFC_CH_0_CONTROL REG_BB_IFC_BASE_LO + 0x00000000
+#define REG_BB_IFC_CH_0_STATUS REG_BB_IFC_BASE_LO + 0x00000004
+#define REG_BB_IFC_CH_0_START_ADDR REG_BB_IFC_BASE_LO + 0x00000008
+#define REG_BB_IFC_CH_0_FIFO_SIZE REG_BB_IFC_BASE_LO + 0x0000000C
+#define REG_BB_IFC_CH_0_INT_MASK REG_BB_IFC_BASE_LO + 0x00000014
+#define REG_BB_IFC_CH_0_INT_CLEAR REG_BB_IFC_BASE_LO + 0x00000018
+#define REG_BB_IFC_CH_0_CUR_AHB_ADDR REG_BB_IFC_BASE_LO + 0x0000001C
+#define REG_BB_IFC_CH_1_CONTROL REG_BB_IFC_BASE_LO + 0x00000020
+#define REG_BB_IFC_CH_1_STATUS REG_BB_IFC_BASE_LO + 0x00000024
+#define REG_BB_IFC_CH_1_START_ADDR REG_BB_IFC_BASE_LO + 0x00000028
+#define REG_BB_IFC_CH_1_FIFO_SIZE REG_BB_IFC_BASE_LO + 0x0000002C
+#define REG_BB_IFC_CH_1_INT_MASK REG_BB_IFC_BASE_LO + 0x00000034
+#define REG_BB_IFC_CH_1_INT_CLEAR REG_BB_IFC_BASE_LO + 0x00000038
+#define REG_BB_IFC_CH_1_CUR_AHB_ADDR REG_BB_IFC_BASE_LO + 0x0000003C
+#define REG_BB_IFC_CH2_CONTROL REG_BB_IFC_BASE_LO + 0x00000040
+#define REG_BB_IFC_CH2_STATUS REG_BB_IFC_BASE_LO + 0x00000044
+#define REG_BB_IFC_CH2_START_ADDR REG_BB_IFC_BASE_LO + 0x00000048
+#define REG_BB_IFC_CH2_END_ADDR REG_BB_IFC_BASE_LO + 0x0000004C
+#define REG_BB_IFC_CH2_TC REG_BB_IFC_BASE_LO + 0x00000050
+#define REG_BB_IFC_CH2_INT_MASK REG_BB_IFC_BASE_LO + 0x00000054
+#define REG_BB_IFC_CH2_INT_CLEAR REG_BB_IFC_BASE_LO + 0x00000058
+#define REG_BB_IFC_CH2_CUR_AHB_ADDR REG_BB_IFC_BASE_LO + 0x0000005C
+#define REG_BB_IFC_CH3_CONTROL REG_BB_IFC_BASE_LO + 0x00000060
+#define REG_BB_IFC_CH3_STATUS REG_BB_IFC_BASE_LO + 0x00000064
+#define REG_BB_IFC_CH3_START_ADDR REG_BB_IFC_BASE_LO + 0x00000068
+#define REG_BB_IFC_CH3_TC REG_BB_IFC_BASE_LO + 0x00000070
+#define REG_BB_IFC_CH3_INT_MASK REG_BB_IFC_BASE_LO + 0x00000074
+#define REG_BB_IFC_CH3_INT_CLEAR REG_BB_IFC_BASE_LO + 0x00000078
+#define REG_BB_IFC_CH3_CUR_AHB_ADDR REG_BB_IFC_BASE_LO + 0x0000007C
+
+//control
+#define BB_IFC_ENABLE (1<<0)
+#define BB_IFC_DISABLE (1<<1)
+#define BB_IFC_AUTO_DISABLE (1<<4)
+
+//status
+//#define BB_IFC_ENABLE (1<<0)
+#define BB_IFC_FIFO_EMPTY (1<<4)
+#define BB_IFC_CAUSE_IEF (1<<8)
+#define BB_IFC_CAUSE_IHF (1<<9)
+#define BB_IFC_CAUSE_I4F (1<<10)
+#define BB_IFC_CAUSE_I3_4F (1<<11)
+#define BB_IFC_IEF (1<<16)
+#define BB_IFC_IHF (1<<17)
+#define BB_IFC_I4F (1<<18)
+#define BB_IFC_I3_4F (1<<19)
+
+//start_addr
+#define BB_IFC_START_ADDR(n) (((n)&0xFFFFFF)<<2)
+
+//Fifo_Size
+#define BB_IFC_FIFO_SIZE(n) (((n)&0x7FF)<<4)
+
+//int_mask
+#define BB_IFC_END_FIFO (1<<8)
+#define BB_IFC_HALF_FIFO (1<<9)
+#define BB_IFC_QUARTER_FIFO (1<<10)
+#define BB_IFC_THREE_QUARTER_FIFO (1<<11)
+
+//int_clear
+//#define BB_IFC_END_FIFO (1<<8)
+//#define BB_IFC_HALF_FIFO (1<<9)
+//#define BB_IFC_QUARTER_FIFO (1<<10)
+//#define BB_IFC_THREE_QUARTER_FIFO (1<<11)
+
+//cur_ahb_addr
+#define BB_IFC_CUR_AHB_ADDR(n) (((n)&0x3FFFFFF)<<0)
+
+//ch2_control
+//#define BB_IFC_ENABLE (1<<0)
+//#define BB_IFC_DISABLE (1<<1)
+#define BB_IFC_BURST_SIZE (1<<16)
+#define BB_IFC_FIFO_MODE (1<<17)
+
+//ch2_status
+//#define BB_IFC_ENABLE (1<<0)
+#define BB_IFC_ENABLE_DISABLE (0<<0)
+#define BB_IFC_ENABLE_ENABLE (1<<0)
+//#define BB_IFC_FIFO_EMPTY (1<<4)
+#define BB_IFC_CAUSE_ITC (1<<8)
+//#define BB_IFC_CAUSE_IEF (1<<9)
+#define BB_IFC_CAUSE_IHTC (1<<10)
+#define BB_IFC_ITC (1<<12)
+//#define BB_IFC_IEF (1<<13)
+#define BB_IFC_IHTC (1<<14)
+#define BB_IFC_CUR_TC(n) (((n)&0xFF)<<16)
+
+//ch2_start_addr
+//#define BB_IFC_START_ADDR(n) (((n)&0x1FFF)<<2)
+#define BB_IFC_PAGE_ADDR(n) (((n)&0x7FF)<<15)
+
+//ch2_end_addr
+#define BB_IFC_END_ADDR(n) (((n)&0x1FFF)<<2)
+//#define BB_IFC_PAGE_ADDR(n) (((n)&0x7FF)<<15)
+
+//ch2_tc
+#define BB_IFC_TC(n) (((n)&0xFF)<<0)
+
+//ch2_int_mask
+#define BB_IFC_END_TC (1<<0)
+//#define BB_IFC_END_FIFO (1<<1)
+#define BB_IFC_HALF_TC (1<<2)
+
+//ch2_int_clear
+//#define BB_IFC_END_TC (1<<0)
+//#define BB_IFC_END_FIFO (1<<1)
+//#define BB_IFC_HALF_FIFO (1<<2)
+
+//ch2_cur_ahb_addr
+//#define BB_IFC_CUR_AHB_ADDR(n) (((n)&0x3FFFFFF)<<0)
+
+//ch3_control
+//#define BB_IFC_ENABLE (1<<0)
+//#define BB_IFC_DISABLE (1<<1)
+
+//ch3_status
+//#define BB_IFC_ENABLE (1<<0)
+//#define BB_IFC_ENABLE_DISABLE (0<<0)
+//#define BB_IFC_ENABLE_ENABLE (1<<0)
+//#define BB_IFC_FIFO_EMPTY (1<<4)
+//#define BB_IFC_CAUSE_ITC (1<<8)
+//#define BB_IFC_ITC (1<<12)
+//#define BB_IFC_CUR_TC(n) (((n)&0xFF)<<16)
+
+//ch3_start_addr
+//#define BB_IFC_START_ADDR(n) (((n)&0x1FFF)<<2)
+
+//ch3_tc
+//#define BB_IFC_TC(n) (((n)&0xFF)<<0)
+
+//ch3_int_mask
+//#define BB_IFC_END_TC (1<<0)
+
+//ch3_int_clear
+//#define BB_IFC_END_TC (1<<0)
+
+//ch3_cur_ahb_addr
+//#define BB_IFC_CUR_AHB_ADDR(n) (((n)&0x3FFFFFF)<<0)
+
+
+
+
+#endif
diff --git a/sound/soc/rdafpga/rdafpga_dai.c b/sound/soc/rdafpga/rdafpga_dai.c
new file mode 100644
index 000000000000..72ac1d1e2cdb
--- /dev/null
+++ b/sound/soc/rdafpga/rdafpga_dai.c
@@ -0,0 +1,536 @@
+/*
+ * omap-dmic.c -- OMAP ASoC DMIC DAI driver
+ *
+ * Copyright (C) 2010 - 2011 Texas Instruments
+ *
+ * Author: David Lambert <dlambert@ti.com>
+ * Misael Lopez Cruz <misael.lopez@ti.com>
+ * Liam Girdwood <lrg@ti.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <plat/dma.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+//#include "rda_dai.h"
+#include "aif.h"
+
+
+
+#define FAST_CLOCK 26000000 //50M
+
+#define AIF_SOURCE_CLOCK 48000000 //50M
+
+struct rda_dai {
+ struct device *dev;
+
+ bool PlayActive;
+ bool RecordActive;
+
+ struct mutex mutex;
+
+ u32 TxStb_div;
+ u32 AudioBck_div;
+ u32 bcklrck_div;
+ u32 ChannelNb;
+
+ u32 ControlReg;
+ //u32 SerialMode;
+ bool MasterFlag;
+
+ bool OpenStatus;
+
+ bool PlayStatus;
+ bool RecordStatus;
+
+};
+
+
+
+static HWP_AIF_T __iomem *hwp_apAif;
+/*
+ * Stream DMA parameters
+
+static struct omap_pcm_dma_data rda_dai_dai_dma_params = {
+ .name = "DMIC capture",
+ .data_type = OMAP_DMA_DATA_TYPE_S32,
+ .sync_mode = OMAP_DMA_SYNC_PACKET,
+};
+
+*/
+
+static int rda_dai_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rda_dai *AifCfg = snd_soc_dai_get_drvdata(dai);
+ int ret = 0;
+ printk(KERN_INFO "rda_dai_dai_startup");
+ mutex_lock(&AifCfg->mutex);
+ if(substream->stream==SNDRV_PCM_STREAM_PLAYBACK)
+ {
+ if (!AifCfg->PlayActive)
+ AifCfg->PlayActive = 1;
+ else
+ ret = -EBUSY;
+ }else{
+
+ if (!AifCfg->RecordActive)
+ AifCfg->RecordActive = 1;
+ else
+ ret = -EBUSY;
+ }
+ mutex_unlock(&AifCfg->mutex);
+
+ return ret;
+}
+
+static void rda_dai_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rda_dai *AifCfg = snd_soc_dai_get_drvdata(dai);
+ printk(KERN_INFO "rda_dai_dai_shutdown");
+ mutex_lock(&AifCfg->mutex);
+ if(substream->stream==SNDRV_PCM_STREAM_PLAYBACK)
+ {
+ AifCfg->PlayActive = 0;
+ }else{
+ AifCfg->RecordActive = 0;
+ }
+
+ //close
+ if(AifCfg->PlayActive == 0&&AifCfg->RecordActive == 0){
+
+ hwp_apAif->ctrl=0;
+ hwp_apAif->serial_ctrl=AIF_MASTER_MODE_MASTER;
+ hwp_apAif->side_tone=0;
+ hwp_apAif->Cfg_Aif_Tx_Stb=0;
+ AifCfg->OpenStatus=0;
+ }
+ mutex_unlock(&AifCfg->mutex);
+}
+
+
+
+static int rda_dai_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rda_dai *AifCfg = snd_soc_dai_get_drvdata(dai);
+
+ u32 ChannelNb;
+ u32 Lrck;
+ u32 Bck;
+ u32 BckLrckRatio;
+
+ printk(KERN_INFO "rda_dai_dai_hw_params");
+
+ if(AifCfg->OpenStatus==0){
+ u32 SampleRate=params_rate(params);
+
+ AifCfg->TxStb_div=AIF_SOURCE_CLOCK/SampleRate-2;
+
+ //LRCK frequency
+ Lrck=SampleRate;
+
+
+ switch(SampleRate){
+ case 8000:
+ BckLrckRatio=50;
+ break;
+
+ case 11025:
+ BckLrckRatio=36;
+ break;
+
+ case 12000:
+ BckLrckRatio=38;
+ break;
+
+ case 16000:
+ BckLrckRatio=50;
+ break;
+
+ case 22050:
+ BckLrckRatio=40;
+ break;
+
+ case 24000:
+ BckLrckRatio=38;
+ break;
+
+ case 32000:
+ BckLrckRatio=56;
+ break;
+
+ case 44100:
+ BckLrckRatio=62;
+ break;
+
+ case 48000:
+ BckLrckRatio=36;
+ break;
+
+ default:
+ dev_err(AifCfg->dev,"Improper stream frequency.\n");
+ return -EINVAL;
+
+ break;
+ }
+
+ Bck=Lrck*BckLrckRatio;
+ AifCfg->AudioBck_div=FAST_CLOCK/Bck-2;
+ AifCfg->bcklrck_div=BckLrckRatio/2-16;
+
+
+ ChannelNb = params_channels(params);
+
+ switch (ChannelNb) {
+ case 1:
+ case 2:
+ AifCfg->ChannelNb=ChannelNb;
+ break;
+ default:
+ dev_err(AifCfg->dev, "invalid number of legacy channels\n");
+ return -EINVAL;
+ }
+
+ AifCfg->MasterFlag=true;
+
+
+ /* packet size is threshold * channels */
+ //snd_soc_dai_set_dma_data(dai, substream, &rda_dai_dai_dma_params);
+
+ }
+
+
+ return 0;
+}
+
+static int rda_dai_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rda_dai *AifCfg = snd_soc_dai_get_drvdata(dai);
+ u32 SerialCfgReg=0;
+
+ printk(KERN_INFO "rda_dai_dai_prepare:%p\n",hwp_apAif);
+
+ if(AifCfg->OpenStatus == 0)
+ {
+ hwp_apAif->Cfg_Aif_Tx_Stb=AIF_AIF_TX_STB_EN | AIF_AIF_TX_STB_DIV(AifCfg->TxStb_div);
+
+ // Serial in and serial out
+ AifCfg->ControlReg = AIF_PARALLEL_OUT_CLR_PARA |
+ AIF_PARALLEL_IN_CLR_PARA |
+ AIF_LOOP_BACK_NORMAL;
+
+ //SerialMode
+ SerialCfgReg |=AIF_SERIAL_MODE_I2S_PCM;
+
+ //Master
+ SerialCfgReg |=AifCfg->MasterFlag?AIF_MASTER_MODE_MASTER:AIF_MASTER_MODE_SLAVE;
+
+ //LSB first
+ SerialCfgReg |=AIF_LSB_MSB;
+
+ //LRCK polarity
+ SerialCfgReg |=AIF_LRCK_POL_LEFT_H_RIGHT_L;
+
+ //Rx delay
+ SerialCfgReg |=AIF_RX_DLY_DLY_2;
+
+ //Tx delay
+ SerialCfgReg |=AIF_TX_DLY_DLY_1;
+
+ //Rx Tx Mode
+ if(AifCfg->ChannelNb==1)
+ {
+ //Rx mode
+ SerialCfgReg |=AIF_RX_MODE_STEREO_MONO_FROM_L;
+ //Tx mode
+ SerialCfgReg |=AIF_TX_MODE_MONO_STEREO_DUPLI;
+ }else{
+ //Rx mode
+ SerialCfgReg |=AIF_RX_MODE_STEREO_STEREO;
+ //Tx mode
+ SerialCfgReg |=AIF_TX_MODE_STEREO_STEREO;
+ }
+
+
+ //we get the Bck from the audio clock
+ hwp_apAif->Cfg_Clk_AudioBCK=AIF_AUDIOBCK_DIVIDER(AifCfg->AudioBck_div);
+
+ SerialCfgReg |= AIF_BCK_LRCK(AifCfg->bcklrck_div);
+
+ //BCK polarity
+ SerialCfgReg |=AifCfg->MasterFlag?AIF_BCK_POL_NORMAL:AIF_BCK_POL_INVERT;
+
+ // Output Half Cycle Delay
+ SerialCfgReg |=AifCfg->MasterFlag?AIF_OUTPUT_HALF_CYCLE_DLY_DLY:AIF_OUTPUT_HALF_CYCLE_DLY_NO_DLY;
+
+ // Input Half Cycle Delay
+ SerialCfgReg |=AIF_INPUT_HALF_CYCLE_DLY_NO_DLY;
+
+ //BckOut gating
+ SerialCfgReg |=AIF_BCKOUT_GATE_NO_GATE;
+
+ hwp_apAif->serial_ctrl=SerialCfgReg;
+
+ AifCfg->OpenStatus=1;
+
+ }
+
+ printk(KERN_INFO "rda_dai_dai_prepare end:%p\n",hwp_apAif);
+
+ return 0;
+}
+
+static int rda_dai_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct rda_dai *AifCfg = snd_soc_dai_get_drvdata(dai);
+ printk(KERN_INFO "rda_dai_dai_trigger");
+ if(substream->stream==SNDRV_PCM_STREAM_PLAYBACK)
+ {
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ printk(KERN_INFO "rda_dai_dai_trigger start");
+ hwp_apAif->ctrl=(AifCfg->ControlReg|AIF_ENABLE_H_ENABLE)&~AIF_TX_OFF;
+ AifCfg->PlayStatus=1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ printk(KERN_INFO "rda_dai_dai_trigger try stop");
+ if(AifCfg->RecordStatus==0){
+ printk(KERN_INFO "rda_dai_dai_trigger stop");
+ // To have the clock allowing the disabling.
+ hwp_apAif->ctrl=0;
+ }
+ AifCfg->PlayStatus=0;
+ break;
+ default:
+ break;
+ }
+
+ }else{
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if(AifCfg->PlayStatus==0){
+ hwp_apAif->ctrl=(AifCfg->ControlReg|AIF_ENABLE_H_ENABLE)|AIF_TX_OFF_TX_OFF;
+ }
+ AifCfg->RecordStatus=1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if(AifCfg->PlayStatus==0){
+ // Disable the AIF if not recording.
+ hwp_apAif->ctrl=0;
+ }
+ AifCfg->RecordStatus=0;
+ break;
+ default:
+ break;
+ }
+
+ }
+// printk(KERN_INFO "rda_dai_dai_trigger end");
+
+ return 0;
+}
+
+
+static const struct snd_soc_dai_ops rda_dai_dai_ops = {
+ .startup = rda_dai_dai_startup,
+ .shutdown = rda_dai_dai_shutdown,
+ .hw_params = rda_dai_dai_hw_params,
+ .prepare = rda_dai_dai_prepare,
+ .trigger = rda_dai_dai_trigger,
+// .set_sysclk = rda_dai_set_dai_sysclk,
+};
+
+static int rda_dai_probe(struct snd_soc_dai *dai)
+{
+ struct rda_dai *dmic = snd_soc_dai_get_drvdata(dai);
+ printk(KERN_INFO "rda_dai_probe");
+
+ pm_runtime_enable(dmic->dev);
+
+ /* Disable lines while request is ongoing */
+ pm_runtime_get_sync(dmic->dev);
+ //omap_dmic_write(dmic, rda_dai_CTRL_REG, 0x00);
+ pm_runtime_put_sync(dmic->dev);
+
+ /* Configure DMIC threshold value */
+ //dmic->threshold = rda_dai_THRES_MAX - 3;
+ return 0;
+}
+
+static int rda_dai_remove(struct snd_soc_dai *dai)
+{
+ struct rda_dai *dmic = snd_soc_dai_get_drvdata(dai);
+
+ pm_runtime_disable(dmic->dev);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver rda_dai_dai = {
+ .name = "rda-aif",
+ .probe = rda_dai_probe,
+ .remove = rda_dai_remove,
+ .playback={
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .sig_bits = 16,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .sig_bits = 16,
+ },
+ .ops = &rda_dai_dai_ops,
+};
+
+static __devinit int asoc_dmic_probe(struct platform_device *pdev)
+{
+ struct rda_dai *AifCfg;
+ struct resource *res;
+ int ret;
+
+ printk(KERN_INFO "asoc_dmic_probe");
+
+ AifCfg = devm_kzalloc(&pdev->dev, sizeof(struct rda_dai), GFP_KERNEL);
+ if (!AifCfg)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, AifCfg);
+ AifCfg->dev = &pdev->dev;
+// dmic->sysclk = rda_dai_SYSCLK_SYNC_MUX_CLKS;
+
+
+ mutex_init(&AifCfg->mutex);
+
+// dmic->fclk = clk_get(dmic->dev, "dmic_fck");
+// if (IS_ERR(dmic->fclk)) {
+// dev_err(dmic->dev, "cant get dmic_fck\n");
+// return -ENODEV;
+// }
+
+// res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
+// if (!res) {
+// dev_err(dmic->dev, "invalid dma memory resource\n");
+// ret = -ENODEV;
+// goto err_put_clk;
+// }
+// omap_dmic_dai_dma_params.port_addr = res->start + rda_dai_DATA_REG;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(AifCfg->dev, "invalid dma resource\n");
+ ret = -ENODEV;
+ goto err_put_clk;
+ }
+/* rda_dai_dai_dma_params.dma_req = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
+ if (!res) {
+ dev_err(dmic->dev, "invalid memory resource\n");
+ ret = -ENODEV;
+ goto err_put_clk;
+ }
+*/
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name)) {
+ dev_err(AifCfg->dev, "memory region already claimed\n");
+ ret = -ENODEV;
+ goto err_put_clk;
+ }
+
+ hwp_apAif = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!hwp_apAif) {
+ ret = -ENOMEM;
+ goto err_put_clk;
+ }
+
+ ret = snd_soc_register_dai(&pdev->dev, &rda_dai_dai);
+ if (ret)
+ goto err_put_clk;
+
+ return 0;
+
+err_put_clk:
+ //clk_put(dmic->fclk);
+ return ret;
+}
+
+static int __devexit asoc_dmic_remove(struct platform_device *pdev)
+{
+// struct rda_dai *AifCfg = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_dai(&pdev->dev);
+ //clk_put(dmic->fclk);
+
+ return 0;
+}
+
+static struct platform_driver rda_dai_driver = {
+ .driver = {
+ .name = "rda-aif",
+ .owner = THIS_MODULE,
+ },
+ .probe = asoc_dmic_probe,
+ .remove = __devexit_p(asoc_dmic_remove),
+};
+
+
+
+static int __init rdafpag_dai_modinit(void)
+{
+ return platform_driver_register(&rda_dai_driver);
+}
+
+static void __exit rdafpag_dai_modexit(void)
+{
+ platform_driver_unregister(&rda_dai_driver);
+}
+
+
+module_init(rdafpag_dai_modinit);
+module_exit(rdafpag_dai_modexit);
+
+
+
+MODULE_DESCRIPTION("ALSA SoC for RDA FPGA DAI");
+MODULE_AUTHOR("Xu Mingliang <mingliangxu@rdamicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rdafpga/rdafpga_pcm.c b/sound/soc/rdafpga/rdafpga_pcm.c
new file mode 100644
index 000000000000..bd4361110fed
--- /dev/null
+++ b/sound/soc/rdafpga/rdafpga_pcm.c
@@ -0,0 +1,410 @@
+/*
+ * rdafpga_pcm.c -- ALSA PCM interface for the RDA SoC
+ *
+ * Copyright (C) 2012 RDA Microelectronics (Beijing) Co., Ltd.
+ *
+ * Contact: Xu Mingliang <mingliangxu@rdamicro.com>
+ *
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "aud_ifc.h"
+#include "rdafpga_pcm.h"
+
+struct rda_pcm_dma_data dma_data;
+
+static const struct snd_pcm_hardware rda_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE ,
+ .period_bytes_min = 32,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 4,
+ .periods_max = 4,
+ .buffer_bytes_max = 512 * 1024,
+};
+
+struct rda_runtime_data {
+ spinlock_t lock;
+ struct rda_pcm_dma_data *dma_data;
+ int dma_ch;
+ int period_index;
+};
+
+static void rda_pcm_dma_irq(int ch, int stat, void *data)
+{
+ struct snd_pcm_substream *substream = data;
+
+// printk(KERN_INFO "rda_pcm_dma_irq\n");
+
+ snd_pcm_period_elapsed(substream);
+}
+
+/* this may get called several times by oss emulation */
+static int rda_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ //struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rda_runtime_data *prtd = runtime->private_data;
+
+ int err = 0;
+
+ printk(KERN_INFO "rda_pcm_hw_params\n");
+
+// dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+ /* return if this is a bufferless transfer e.g.
+ * codec <--> BT codec or GSM modem -- lg FIXME */
+// if (!dma_data)
+// return 0;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ //if (prtd->dma_data)
+ // return 0;
+ prtd->dma_data = &dma_data;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_data.dma_req=1;
+ dma_data.name="play";
+
+ } else {
+ dma_data.dma_req=0;
+ dma_data.name="record";
+ }
+
+
+ err = rda_request_audifc(dma_data.dma_req, dma_data.name,
+ rda_pcm_dma_irq, substream, &prtd->dma_ch);
+
+ return err;
+}
+
+static int rda_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rda_runtime_data *prtd = runtime->private_data;
+
+ if (prtd->dma_data == NULL)
+ return 0;
+ printk(KERN_INFO "rda_pcm_hw_free\n");
+
+ rda_free_audifc(prtd->dma_ch);
+ prtd->dma_data = NULL;
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+
+ return 0;
+}
+
+static int rda_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rda_runtime_data *prtd = runtime->private_data;
+ //struct rda_pcm_dma_data *dma_data = prtd->dma_data;
+ struct rda_audifc_chan_params dma_params;
+ int bytes;
+ printk(KERN_INFO "rda_pcm_prepare\n");
+
+ /* return if this is a bufferless transfer e.g.
+ * codec <--> BT codec or GSM modem -- lg FIXME */
+ //if (!prtd->dma_data)
+ // return 0;
+
+ //memset(&dma_params, 0, sizeof(dma_params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_params.src_addr = runtime->dma_addr;
+ dma_params.dst_addr = 0;
+ dma_params.xfer_size = runtime->dma_bytes;
+ dma_params.audifc_mode = 0;
+
+ } else {
+ dma_params.src_addr = runtime->dma_addr;
+ dma_params.dst_addr = 0;
+ dma_params.xfer_size = runtime->dma_bytes;
+ dma_params.audifc_mode = 0;
+ }
+
+ /*
+ * Set DMA transfer frame size equal to ALSA period size and frame
+ * count as no. of ALSA periods. Then with DMA frame interrupt enabled,
+ * we can transfer the whole ALSA buffer with single DMA transfer but
+ * still can get an interrupt at each period bounary
+ */
+ bytes = snd_pcm_lib_period_bytes(substream);
+
+ printk(KERN_INFO "prtd->dma_ch %d\n",prtd->dma_ch);
+
+ rda_set_audifc_params(prtd->dma_ch, &dma_params);
+
+ return 0;
+}
+
+static int rda_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rda_runtime_data *prtd = runtime->private_data;
+ struct rda_pcm_dma_data *dma_data = prtd->dma_data;
+// unsigned long flags;
+ int ret = 0;
+ printk(KERN_INFO "rda_pcm_trigger\n");
+
+ //spin_lock_irqsave(&prtd->lock, flags);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ prtd->period_index = 0;
+ /* Configure McBSP internal buffer usage */
+ if (dma_data->set_threshold)
+ dma_data->set_threshold(substream);
+
+ rda_start_audifc(prtd->dma_ch);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ prtd->period_index = -1;
+ rda_stop_audifc(prtd->dma_ch);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ //spin_unlock_irqrestore(&prtd->lock, flags);
+
+ return ret;
+}
+
+static snd_pcm_uframes_t rda_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rda_runtime_data *prtd = runtime->private_data;
+ audifc_addr_t ptr;
+ snd_pcm_uframes_t offset;
+//printk(KERN_INFO "rda_pcm_pointer\n");
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ptr = rda_get_audifc_dst_pos(prtd->dma_ch);
+ offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+ } else {
+ ptr = rda_get_audifc_src_pos(prtd->dma_ch);
+ offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+ }
+
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+
+ return offset;
+}
+
+static int rda_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rda_runtime_data *prtd;
+ int ret;
+ printk(KERN_INFO "rda_pcm_open\n");
+ snd_soc_set_runtime_hwparams(substream, &rda_pcm_hardware);
+
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (prtd == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ spin_lock_init(&prtd->lock);
+ runtime->private_data = prtd;
+
+out:
+ return ret;
+}
+
+static int rda_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ kfree(runtime->private_data);
+ return 0;
+}
+
+static int rda_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+printk(KERN_INFO "rda_pcm_mmap\n");
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops rda_pcm_ops = {
+ .open = rda_pcm_open,
+ .close = rda_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = rda_pcm_hw_params,
+ .hw_free = rda_pcm_hw_free,
+ .prepare = rda_pcm_prepare,
+ .trigger = rda_pcm_trigger,
+ .pointer = rda_pcm_pointer,
+ .mmap = rda_pcm_mmap,
+};
+
+static u64 rda_pcm_dmamask = DMA_BIT_MASK(32);
+
+static int rda_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
+ int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = rda_pcm_hardware.buffer_bytes_max;
+ printk(KERN_INFO "rda_pcm_preallocate_dma_buffer\n");
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ printk(KERN_INFO "buf->area:%p buf->bytes;%d\n",buf->area,size);
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+ return 0;
+}
+
+static void rda_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+ printk(KERN_INFO "rda_pcm_free_dma_buffers\n");
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_writecombine(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+static int rda_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+
+
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret = 0;
+ printk(KERN_INFO "rda_pcm_new\n");
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &rda_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ ret = rda_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ret = rda_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+
+out:
+ /* free preallocated buffers in case of error */
+ if (ret)
+ rda_pcm_free_dma_buffers(pcm);
+
+ return ret;
+}
+
+static struct snd_soc_platform_driver rda_soc_platform = {
+ .ops = &rda_pcm_ops,
+ .pcm_new = rda_pcm_new,
+ .pcm_free = rda_pcm_free_dma_buffers,
+};
+
+static __devinit int rda_pcm_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev,
+ &rda_soc_platform);
+}
+
+static int __devexit rda_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver rda_pcm_driver = {
+ .driver = {
+ .name = "rdafpga-pcm-audio",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = rda_pcm_probe,
+ .remove = __exit_p(rda_pcm_remove),
+};
+
+static int __init rdafpag_pcm_modinit(void)
+{
+ return platform_driver_register(&rda_pcm_driver);
+}
+
+static void __exit rdafpag_pcm_modexit(void)
+{
+ platform_driver_unregister(&rda_pcm_driver);
+}
+
+
+module_init(rdafpag_pcm_modinit);
+module_exit(rdafpag_pcm_modexit);
+
+
+
+MODULE_DESCRIPTION("ALSA SoC for RDA FPGA PCM");
+MODULE_AUTHOR("Xu Mingliang <mingliangxu@rdamicro.com>");
+MODULE_LICENSE("GPL");
+
+
diff --git a/sound/soc/rdafpga/rdafpga_pcm.h b/sound/soc/rdafpga/rdafpga_pcm.h
new file mode 100644
index 000000000000..e9f02c34b067
--- /dev/null
+++ b/sound/soc/rdafpga/rdafpga_pcm.h
@@ -0,0 +1,41 @@
+/*
+ * rda-pcm.h
+ *
+ * Copyright (C) 2012 RDA Microelectronics (Beijing) Co., Ltd.
+ *
+ * Contact: Xu Mingliang <mingliangxu@rdamicro.com>
+ *
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __RDA_PCM_H__
+#define __RDA_PCM_H__
+
+struct snd_pcm_substream;
+
+struct rda_pcm_dma_data {
+ char *name; /* stream identifier */
+ int dma_req; /* DMA request line */
+ unsigned long port_addr; /* transmit/receive register */
+ void (*set_threshold)(struct snd_pcm_substream *substream);
+ int data_type; /* data type 8,16,32 */
+ int sync_mode; /* DMA sync mode */
+ int packet_size; /* packet size only in PACKET mode */
+};
+
+#endif
+
diff --git a/sound/soc/rdafpga/rdafpga_soundcard.c b/sound/soc/rdafpga/rdafpga_soundcard.c
new file mode 100644
index 000000000000..45c29a32e0ac
--- /dev/null
+++ b/sound/soc/rdafpga/rdafpga_soundcard.c
@@ -0,0 +1,129 @@
+/*
+ * sound/soc/rda/soundcard_rdafpga.c
+ *
+ * Copyright (C) 2012 RDA.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+
+#define CODEC_CLOCK 12000000
+
+
+static int rda_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int err=0;
+
+
+ printk(KERN_INFO "rda_hw_params\n");
+
+ /* Set the codec system clock for DAC and ADC */
+ err =
+ snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
+
+ if (err < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return err;
+ }
+
+ return err;
+}
+
+static struct snd_soc_ops rda_ops = {
+ .hw_params = rda_hw_params,
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Jack", NULL, "LHPOUT"},
+ {"Headphone Jack", NULL, "RHPOUT"},
+
+ {"LLINEIN", NULL, "Line In"},
+ {"RLINEIN", NULL, "Line In"},
+
+ {"MICIN", NULL, "Mic Jack"},
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link rda_dai = {
+ .name = "TLV320AIC23",
+ .stream_name = "AIC23",
+ .cpu_dai_name = "rda-aif",//"rdafpga-dai-i2s",
+ .codec_dai_name = "tlv320aic23-hifi",
+ .platform_name = "rdafpga-pcm-audio",
+ .codec_name ="spi0.0",// "tlv320aic23-spi",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &rda_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_card_rda = {
+ .name = "rdafpga_soundcard",
+ .owner = THIS_MODULE,
+ .dai_link = &rda_dai,
+ .num_links = 1,
+
+ .dapm_widgets = tlv320aic23_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+static struct platform_device *rda_snd_device;
+
+static int __init rdasoundcard_modinit(void)
+{
+ int err;
+
+ printk(KERN_INFO "rdasoundcard_modinit\n");
+
+ rda_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!rda_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(rda_snd_device, &snd_soc_card_rda);
+ err = platform_device_add(rda_snd_device);
+ if (err)
+ platform_device_put(rda_snd_device);
+
+ return err;
+
+}
+
+static void __exit rdasoundcard_modexit(void)
+{
+ //clk_put(tlv320aic23_mclk);
+ platform_device_unregister(rda_snd_device);
+}
+module_init(rdasoundcard_modinit);
+module_exit(rdasoundcard_modexit);
+
+MODULE_DESCRIPTION("ALSA SoC for RDA FPGA tlv320aic23");
+MODULE_AUTHOR("Xu Mingliang <mingliangxu@rdamicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rdafpga/tlv320aic23_spi.c b/sound/soc/rdafpga/tlv320aic23_spi.c
new file mode 100644
index 000000000000..772cc81c81d1
--- /dev/null
+++ b/sound/soc/rdafpga/tlv320aic23_spi.c
@@ -0,0 +1,861 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver
+ *
+ * Author: Arun KS, <arunks@mistralsolutions.com>
+ * Copyright: (C) 2008 Mistral Solutions Pvt Ltd.,
+ *
+ * Based on sound/soc/codecs/wm8731.c by Richard Purdie
+ *
+ * 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.
+ *
+ * Notes:
+ * The AIC23 is a driver for a low power stereo audio
+ * codec tlv320aic23
+ *
+ * The machine layer should disable unsupported inputs/outputs by
+ * snd_soc_dapm_disable_pin(codec, "LHPOUT"), etc.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+
+#include "tlv320aic23_spi.h"
+
+
+
+// =============================================================================
+// MACROS
+// =============================================================================
+/// Address on the I2C bus.
+#define TI_ADDR 0x1a // 0x1a // 0x1b in case of CS=1
+
+/// Ti's register Map
+#define REG_TI_LINE_VOL_L 0x00
+ #define TI_LRS_ON (1<<8)
+ #define TI_LRS_OFF (0<<8)
+ #define TI_LIM_ON (1<<7)
+ #define TI_LIM_OFF (0<<7)
+ #define LIV_VALUE(v) ((v)&0x1f)
+
+#define REG_TI_LINE_VOL_R 0x01
+ #define TI_RLS_ON (1<<8)
+ #define TI_RLS_OFF (0<<8)
+ #define TI_RIM_ON (1<<7)
+ #define TI_RIM_OFF (0<<7)
+ #define RIV_VALUE(v) ((v)&0x1f)
+
+#define REG_TI_HEAD_VOL_L 0x02
+ #define TI_LZC_ON (1<<7)
+ #define TI_LZC_OFF (0<<7)
+ #define TI_LHV_VALUE(v) ((v)&0x7f)
+ #define TI_LHV_MUTE 0x30
+ #define TI_LHV_0dB 0x79
+
+#define REG_TI_HEAD_VOL_R 0x03
+ #define TI_RZC_ON (1<<7)
+ #define TI_RZC_OFF (0<<7)
+ #define TI_RHV_VALUE(v) ((v)&0x7f)
+ #define TI_RHV_MUTE 0x30
+
+#define REG_TI_ANA_AUD_CTRL 0x04
+ #define TI_SIDETONE_0DB 0x120
+ #define TI_SIDETONE_MINUS_6DB 0x20
+ #define TI_SIDETONE_MINUS_9DB 0x60
+ #define TI_SIDETONE_MINUS_12DB 0xa0
+ #define TI_SIDETONE_MINUS_18DB 0xe0
+ #define TI_SIDETONE_OFF (0<<5)
+ #define TI_ANA_DAC_ON (1<<4)
+ #define TI_ANA_DAC_OFF (0<<4)
+ #define TI_ANA_BYP_ON (1<<3)
+ #define TI_ANA_BYP_OFF (0<<3)
+ #define TI_INSEL_MIC (1<<2)
+ #define TI_INSEL_LINE (0<<2)
+ #define TI_MICM_ON (1<<1)
+ #define TI_MICM_OFF (0<<1)
+ #define TI_MICB_ON (1<<0)
+ #define TI_MICB_OFF (0<<0)
+
+#define REG_TI_DIG_AUD_CTRL 0x05
+ #define TI_DACM_ON (1<<3)
+ #define TI_DACM_OFF (0<<3)
+ #define TI_DEEMP_OFF (0<<1)
+ #define TI_DEEMP_32KHZ (1<<1)
+ #define TI_DEEMP_44KHZ (2<<1)
+ #define TI_DEEMP_48KHZ (3<<1)
+ #define TI_ADCHP_ON (0<<0)
+ #define TI_ADCHP_OFF (1<<0)
+
+#define REG_TI_POWER_CTRL 0x06
+ #define TI_POWER_OFF (1<<7)
+ #define TI_POWER_ON (0<<7)
+ #define TI_CLK_OFF (1<<6)
+ #define TI_CLK_ON (0<<6)
+ #define TI_OSC_OFF (1<<5)
+ #define TI_OSC_ON (0<<5)
+ #define TI_OUT_OFF (1<<4)
+ #define TI_OUT_ON (0<<4)
+ #define TI_DAC_OFF (1<<3)
+ #define TI_DAC_ON (0<<3)
+ #define TI_ADC_OFF (1<<2)
+ #define TI_ADC_ON (0<<2)
+ #define TI_MIC_OFF (1<<1)
+ #define TI_MIC_ON (0<<1)
+ #define TI_LINE_OFF (1<<0)
+ #define TI_LINE_ON (0<<0)
+ #define TI_POWER_ALL_OFF 0xff
+
+#define REG_TI_DIG_FORMAT 0x07
+ #define TI_MODE_MASTER (1<<6)
+ #define TI_MODE_SLAVE (0<<6)
+ #define TI_LRSWAP_ON (1<<5)
+ #define TI_LRSWAP_OFF (0<<5)
+ #define TI_LRP_R_LRCIN_LOW (1<<4)
+ #define TI_LRP_R_LRCIN_HIGH (0<<4)
+ #define TI_LRP_MSB_ON_2BLCK (1<<4)
+ #define TI_LRP_MSB_ON_1BLCK (0<<4)
+ #define TI_IWL_16BIT (0<<2)
+ #define TI_IWL_20BIT (1<<2)
+ #define TI_IWL_24BIT (2<<2)
+ #define TI_IWL_32BIT (3<<2)
+ #define TI_FOR_DSP (3<<0)
+ #define TI_FOR_I2S (2<<0)
+ #define TI_FOR_L_ALIGN (1<<0)
+ #define TI_FOR_R_ALIGN (0<<0)
+
+#define REG_TI_SAMPLE_RATE 0x08
+ #define TI_CLKIN_DIV_ON (1<<6)
+ #define TI_CLKIN_DIV_OFF (0<<6)
+ #define TI_CLKOUT_DIV_ON (1<<7)
+ #define TI_CLKOUT_DIV_OFF (0<<7)
+ #define TI_SR_VALUE(v) (((v)&0xf)<<2)
+ #define TI_BOSR_FAST (1<<1)
+ #define TI_BOSR_SLOW (0<<1)
+ #define TI_CLK_MODE_USB (1<<0)
+ #define TI_CLK_MODE_NORM (0<<0)
+
+#define REG_TI_DIG_ACT 0x09
+ #define TI_ACT_ON (1<<0)
+ #define TI_ACT_OFF (0<<0)
+
+#define REG_TI_RESET 0x0f
+ #define TI_RESET 0
+
+#define AIC23_VERSION "0.1"
+
+/*
+ * AIC23 register cache
+ */
+static const u16 tlv320aic23_reg[] = {
+ 0x0097, 0x0097, 0x00F9, 0x00F9, /* 0 */
+ 0x001A, 0x0004, 0x0007, 0x0001, /* 4 */
+ 0x0020, 0x0000, 0x0000, 0x0000, /* 8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 12 */
+};
+
+static const char *rec_src_text[] = { "Line", "Mic" };
+static const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+
+static const struct soc_enum rec_src_enum =
+ SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text);
+
+static const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls =
+SOC_DAPM_ENUM("Input Select", rec_src_enum);
+
+static const struct soc_enum tlv320aic23_rec_src =
+ SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text);
+static const struct soc_enum tlv320aic23_deemph =
+ SOC_ENUM_SINGLE(TLV320AIC23_DIGT, 1, 4, deemph_text);
+
+static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0);
+static const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_vol_tlv, -1800, 300, 0);
+
+static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u16 val, reg;
+
+ val = (ucontrol->value.integer.value[0] & 0x07);
+
+ /* linear conversion to userspace
+ * 000 = -6db
+ * 001 = -9db
+ * 010 = -12db
+ * 011 = -18db (Min)
+ * 100 = 0db (Max)
+ */
+ val = (val >= 4) ? 4 : (3 - val);
+
+ reg = snd_soc_read(codec, TLV320AIC23_ANLG) & (~0x1C0);
+ snd_soc_write(codec, TLV320AIC23_ANLG, reg | (val << 6));
+
+ return 0;
+}
+
+static int snd_soc_tlv320aic23_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u16 val;
+
+ val = snd_soc_read(codec, TLV320AIC23_ANLG) & (0x1C0);
+ val = val >> 6;
+ val = (val >= 4) ? 4 : (3 - val);
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+
+}
+
+static const struct snd_kcontrol_new tlv320aic23_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Playback Volume", TLV320AIC23_LCHNVOL,
+ TLV320AIC23_RCHNVOL, 0, 127, 0, out_gain_tlv),
+ SOC_SINGLE("Digital Playback Switch", TLV320AIC23_DIGT, 3, 1, 1),
+ SOC_DOUBLE_R("Line Input Switch", TLV320AIC23_LINVOL,
+ TLV320AIC23_RINVOL, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("Line Input Volume", TLV320AIC23_LINVOL,
+ TLV320AIC23_RINVOL, 0, 31, 0, input_gain_tlv),
+ SOC_SINGLE("Mic Input Switch", TLV320AIC23_ANLG, 1, 1, 1),
+ SOC_SINGLE("Mic Booster Switch", TLV320AIC23_ANLG, 0, 1, 0),
+ SOC_SINGLE_EXT_TLV("Sidetone Volume", TLV320AIC23_ANLG, 6, 4, 0,
+ snd_soc_tlv320aic23_get_volsw,
+ snd_soc_tlv320aic23_put_volsw, sidetone_vol_tlv),
+ SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph),
+};
+
+/* PGA Mixer controls for Line and Mic switch */
+static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", TLV320AIC23_ANLG, 5, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", TLV320AIC23_ANLG, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", TLV320AIC23_PWR, 3, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", TLV320AIC23_PWR, 2, 1),
+ SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
+ &tlv320aic23_rec_src_mux_controls),
+ SND_SOC_DAPM_MIXER("Output Mixer", TLV320AIC23_PWR, 4, 1,
+ &tlv320aic23_output_mixer_controls[0],
+ ARRAY_SIZE(tlv320aic23_output_mixer_controls)),
+ SND_SOC_DAPM_PGA("Line Input", TLV320AIC23_PWR, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic Input", TLV320AIC23_PWR, 1, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+
+ SND_SOC_DAPM_INPUT("LLINEIN"),
+ SND_SOC_DAPM_INPUT("RLINEIN"),
+
+ SND_SOC_DAPM_INPUT("MICIN"),
+};
+
+static const struct snd_soc_dapm_route tlv320aic23_intercon[] = {
+ /* Output Mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "Playback Switch", "DAC"},
+ {"Output Mixer", "Mic Sidetone Switch", "Mic Input"},
+
+ /* Outputs */
+ {"RHPOUT", NULL, "Output Mixer"},
+ {"LHPOUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+
+ /* Inputs */
+ {"Line Input", "NULL", "LLINEIN"},
+ {"Line Input", "NULL", "RLINEIN"},
+
+ {"Mic Input", "NULL", "MICIN"},
+
+ /* input mux */
+ {"Capture Source", "Line", "Line Input"},
+ {"Capture Source", "Mic", "Mic Input"},
+ {"ADC", NULL, "Capture Source"},
+
+};
+
+/* AIC23 driver data */
+struct aic23 {
+ enum snd_soc_control_type control_type;
+ int mclk;
+ int requested_adc;
+ int requested_dac;
+};
+
+/*
+ * Common Crystals used
+ * 11.2896 Mhz /128 = *88.2k /192 = 58.8k
+ * 12.0000 Mhz /125 = *96k /136 = 88.235K
+ * 12.2880 Mhz /128 = *96k /192 = 64k
+ * 16.9344 Mhz /128 = 132.3k /192 = *88.2k
+ * 18.4320 Mhz /128 = 144k /192 = *96k
+ */
+
+/*
+ * Normal BOSR 0-256/2 = 128, 1-384/2 = 192
+ * USB BOSR 0-250/2 = 125, 1-272/2 = 136
+ */
+static const int bosr_usb_divisor_table[] = {
+ 128, 125, 192, 136
+};
+#define LOWER_GROUP ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<6) | (1<<7))
+#define UPPER_GROUP ((1<<8) | (1<<9) | (1<<10) | (1<<11) | (1<<15))
+static const unsigned short sr_valid_mask[] = {
+ LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 0*/
+ LOWER_GROUP, /* Usb, bosr - 0*/
+ LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 1*/
+ UPPER_GROUP, /* Usb, bosr - 1*/
+};
+/*
+ * Every divisor is a factor of 11*12
+ */
+#define SR_MULT (11*12)
+#define A(x) (SR_MULT/x)
+static const unsigned char sr_adc_mult_table[] = {
+ A(2), A(2), A(12), A(12), 0, 0, A(3), A(1),
+ A(2), A(2), A(11), A(11), 0, 0, 0, A(1)
+};
+static const unsigned char sr_dac_mult_table[] = {
+ A(2), A(12), A(2), A(12), 0, 0, A(3), A(1),
+ A(2), A(11), A(2), A(11), 0, 0, 0, A(1)
+};
+
+static unsigned get_score(int adc, int adc_l, int adc_h, int need_adc,
+ int dac, int dac_l, int dac_h, int need_dac)
+{
+ if ((adc >= adc_l) && (adc <= adc_h) &&
+ (dac >= dac_l) && (dac <= dac_h)) {
+ int diff_adc = need_adc - adc;
+ int diff_dac = need_dac - dac;
+ return abs(diff_adc) + abs(diff_dac);
+ }
+ return UINT_MAX;
+}
+
+static int find_rate(int mclk, u32 need_adc, u32 need_dac)
+{
+ int i, j;
+ int best_i = -1;
+ int best_j = -1;
+ int best_div = 0;
+ unsigned best_score = UINT_MAX;
+ int adc_l, adc_h, dac_l, dac_h;
+
+ need_adc *= SR_MULT;
+ need_dac *= SR_MULT;
+ /*
+ * rates given are +/- 1/32
+ */
+ adc_l = need_adc - (need_adc >> 5);
+ adc_h = need_adc + (need_adc >> 5);
+ dac_l = need_dac - (need_dac >> 5);
+ dac_h = need_dac + (need_dac >> 5);
+ for (i = 0; i < ARRAY_SIZE(bosr_usb_divisor_table); i++) {
+ int base = mclk / bosr_usb_divisor_table[i];
+ int mask = sr_valid_mask[i];
+ for (j = 0; j < ARRAY_SIZE(sr_adc_mult_table);
+ j++, mask >>= 1) {
+ int adc;
+ int dac;
+ int score;
+ if ((mask & 1) == 0)
+ continue;
+ adc = base * sr_adc_mult_table[j];
+ dac = base * sr_dac_mult_table[j];
+ score = get_score(adc, adc_l, adc_h, need_adc,
+ dac, dac_l, dac_h, need_dac);
+ if (best_score > score) {
+ best_score = score;
+ best_i = i;
+ best_j = j;
+ best_div = 0;
+ }
+ score = get_score((adc >> 1), adc_l, adc_h, need_adc,
+ (dac >> 1), dac_l, dac_h, need_dac);
+ /* prefer to have a /2 */
+ if ((score != UINT_MAX) && (best_score >= score)) {
+ best_score = score;
+ best_i = i;
+ best_j = j;
+ best_div = 1;
+ }
+ }
+ }
+ return (best_j << 2) | best_i | (best_div << TLV320AIC23_CLKIN_SHIFT);
+}
+
+#ifdef DEBUG
+static void get_current_sample_rates(struct snd_soc_codec *codec, int mclk,
+ u32 *sample_rate_adc, u32 *sample_rate_dac)
+{
+ int src = snd_soc_read(codec, TLV320AIC23_SRATE);
+ int sr = (src >> 2) & 0x0f;
+ int val = (mclk / bosr_usb_divisor_table[src & 3]);
+ int adc = (val * sr_adc_mult_table[sr]) / SR_MULT;
+ int dac = (val * sr_dac_mult_table[sr]) / SR_MULT;
+ if (src & TLV320AIC23_CLKIN_HALF) {
+ adc >>= 1;
+ dac >>= 1;
+ }
+ *sample_rate_adc = adc;
+ *sample_rate_dac = dac;
+}
+#endif
+
+static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
+ u32 sample_rate_adc, u32 sample_rate_dac)
+{
+ /* Search for the right sample rate */
+ int data = find_rate(mclk, sample_rate_adc, sample_rate_dac);
+ if (data < 0) {
+ printk(KERN_ERR "%s:Invalid rate %u,%u requested\n",
+ __func__, sample_rate_adc, sample_rate_dac);
+ return -EINVAL;
+ }
+ snd_soc_write(codec, TLV320AIC23_SRATE, data);
+#ifdef DEBUG
+ {
+ u32 adc, dac;
+ get_current_sample_rates(codec, mclk, &adc, &dac);
+ printk(KERN_DEBUG "actual samplerate = %u,%u reg=%x\n",
+ adc, dac, data);
+ }
+#endif
+ return 0;
+}
+
+static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ u16 iface_reg;
+ int ret;
+ struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
+ u32 sample_rate_adc = aic23->requested_adc;
+ u32 sample_rate_dac = aic23->requested_dac;
+ u32 sample_rate = params_rate(params);
+
+ printk(KERN_INFO "tlv320aic23_hw_params\n");
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ aic23->requested_dac = sample_rate_dac = sample_rate;
+ if (!sample_rate_adc)
+ sample_rate_adc = sample_rate;
+ } else {
+ aic23->requested_adc = sample_rate_adc = sample_rate;
+ if (!sample_rate_dac)
+ sample_rate_dac = sample_rate;
+ }
+ ret = set_sample_rate_control(codec, aic23->mclk, sample_rate_adc,
+ sample_rate_dac);
+ if (ret < 0)
+ return ret;
+
+ iface_reg = snd_soc_read(codec, TLV320AIC23_DIGT_FMT) & ~(0x03 << 2);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface_reg |= (0x01 << 2);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface_reg |= (0x02 << 2);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface_reg |= (0x03 << 2);
+ break;
+ }
+ snd_soc_write(codec, TLV320AIC23_DIGT_FMT, iface_reg);
+
+ return 0;
+}
+
+static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+
+ printk(KERN_INFO "tlv320aic23_pcm_prepare\n");
+
+ /* set active */
+ snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0001);
+
+ return 0;
+}
+
+static void tlv320aic23_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
+printk(KERN_INFO "tlv320aic23_shutdown\n");
+ /* deactivate */
+ if (!codec->active) {
+ udelay(50);
+ snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0);
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ aic23->requested_dac = 0;
+ else
+ aic23->requested_adc = 0;
+}
+
+static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg;
+
+ printk(KERN_INFO "tlv320aic23_mute:%d\n",mute);
+
+
+
+ reg = snd_soc_read(codec, TLV320AIC23_DIGT);
+ if (mute)
+ reg |= TLV320AIC23_DACM_MUTE;
+
+ else
+ reg &= ~TLV320AIC23_DACM_MUTE;
+
+ snd_soc_write(codec, TLV320AIC23_DIGT, reg);
+
+ return 0;
+}
+
+static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface_reg;
+
+ printk(KERN_INFO "tlv320aic23_set_dai_fmt\n");
+
+ iface_reg = snd_soc_read(codec, TLV320AIC23_DIGT_FMT) & (~0x03);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface_reg |= TLV320AIC23_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ iface_reg &= ~TLV320AIC23_MS_MASTER;
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface_reg |= TLV320AIC23_FOR_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface_reg |= TLV320AIC23_LRP_ON;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface_reg |= TLV320AIC23_FOR_DSP;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg |= TLV320AIC23_FOR_LJUST;
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ snd_soc_write(codec, TLV320AIC23_DIGT_FMT, iface_reg);
+
+ return 0;
+}
+
+static int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct aic23 *aic23 = snd_soc_dai_get_drvdata(codec_dai);
+
+ printk(KERN_INFO "tlv320aic23_set_dai_sysclk\n");
+
+ aic23->mclk = freq;
+ return 0;
+}
+
+static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg = snd_soc_read(codec, TLV320AIC23_PWR) & 0x17f;
+
+
+ printk(KERN_INFO "tlv320aic23_set_bias_level:%d\n",level);
+
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* vref/mid, osc on, dac unmute */
+ reg &= ~(TLV320AIC23_DEVICE_PWR_OFF | TLV320AIC23_OSC_OFF | \
+ TLV320AIC23_DAC_OFF|TLV320AIC23_CLK_OFF);
+ snd_soc_write(codec, TLV320AIC23_PWR, reg);
+
+
+/*ss
+ snd_soc_write(codec,REG_TI_RESET, TI_RESET);
+
+ snd_soc_write(codec,REG_TI_LINE_VOL_L, TI_LRS_ON|TI_LIM_ON);
+
+ snd_soc_write(codec,REG_TI_LINE_VOL_R, TI_LRS_ON|TI_LIM_ON);
+
+
+ // Avoid explosive voice pulse when turning on audio chip
+ snd_soc_write(codec,REG_TI_HEAD_VOL_L, TI_LRS_ON|TI_LHV_VALUE(0x79));
+ snd_soc_write(codec,REG_TI_HEAD_VOL_R, TI_LRS_ON|TI_LHV_VALUE(0x79));
+
+
+ snd_soc_write(codec,REG_TI_ANA_AUD_CTRL, TI_SIDETONE_OFF|TI_ANA_DAC_ON|TI_ANA_BYP_OFF|TI_INSEL_MIC|TI_MICM_ON|TI_MICB_OFF);
+
+ snd_soc_write(codec,REG_TI_DIG_AUD_CTRL, TI_DACM_OFF|TI_DEEMP_OFF|TI_ADCHP_OFF);
+
+ snd_soc_write(codec,REG_TI_DIG_FORMAT, TI_LRSWAP_OFF|TI_IWL_16BIT|TI_MODE_SLAVE|TI_LRP_R_LRCIN_HIGH|TI_FOR_I2S);
+
+ snd_soc_write(codec,REG_TI_SAMPLE_RATE, TI_CLKIN_DIV_OFF|TI_CLKOUT_DIV_OFF|TI_SR_VALUE(3) | TI_BOSR_SLOW|TI_CLK_MODE_USB);
+
+ snd_soc_write(codec,REG_TI_DIG_ACT, TI_ACT_ON);
+
+ snd_soc_write(codec,REG_TI_POWER_CTRL, TI_POWER_ON|TI_CLK_ON|TI_OSC_ON|TI_OUT_ON|TI_DAC_ON|TI_ADC_ON|TI_MIC_ON|TI_LINE_OFF);
+
+ snd_soc_write(codec,REG_TI_ANA_AUD_CTRL, TI_SIDETONE_OFF|TI_ANA_DAC_ON|TI_ANA_BYP_OFF|TI_INSEL_MIC|TI_MICB_OFF|TI_MICM_OFF);
+
+
+
+*/
+
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* everything off except vref/vmid, */
+ snd_soc_write(codec, TLV320AIC23_PWR,
+ reg | TLV320AIC23_CLK_OFF);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* everything off, dac mute, inactive */
+ snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0);
+ snd_soc_write(codec, TLV320AIC23_PWR, 0x1ff);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define AIC23_RATES SNDRV_PCM_RATE_8000_96000
+#define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops tlv320aic23_dai_ops = {
+ .prepare = tlv320aic23_pcm_prepare,
+ .hw_params = tlv320aic23_hw_params,
+ .shutdown = tlv320aic23_shutdown,
+ .digital_mute = tlv320aic23_mute,
+ .set_fmt = tlv320aic23_set_dai_fmt,
+ .set_sysclk = tlv320aic23_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver tlv320aic23_dai = {
+ .name = "tlv320aic23-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC23_RATES,
+ .formats = AIC23_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC23_RATES,
+ .formats = AIC23_FORMATS,},
+ .ops = &tlv320aic23_dai_ops,
+};
+
+static int tlv320aic23_suspend(struct snd_soc_codec *codec)
+{
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int tlv320aic23_resume(struct snd_soc_codec *codec)
+{
+ snd_soc_cache_sync(codec);
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+static int tlv320aic23_probe(struct snd_soc_codec *codec)
+{
+ struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION);
+
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, aic23->control_type);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset codec */
+ snd_soc_write(codec, TLV320AIC23_RESET, 0);
+
+ /* Write the register default value to cache for reserved registers,
+ * so the write to the these registers are suppressed by the cache
+ * restore code when it skips writes of default registers.
+ */
+ snd_soc_cache_write(codec, 0x0A, 0);
+ snd_soc_cache_write(codec, 0x0B, 0);
+ snd_soc_cache_write(codec, 0x0C, 0);
+ snd_soc_cache_write(codec, 0x0D, 0);
+ snd_soc_cache_write(codec, 0x0E, 0);
+
+ /* power on device */
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ snd_soc_write(codec, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K);
+
+ /* Unmute input */
+ snd_soc_update_bits(codec, TLV320AIC23_LINVOL,
+ TLV320AIC23_LIM_MUTED, TLV320AIC23_LRS_ENABLED);
+
+ snd_soc_update_bits(codec, TLV320AIC23_RINVOL,
+ TLV320AIC23_LIM_MUTED, TLV320AIC23_LRS_ENABLED);
+
+ snd_soc_update_bits(codec, TLV320AIC23_ANLG,
+ TLV320AIC23_BYPASS_ON | TLV320AIC23_MICM_MUTED,
+ 0);
+
+ /* Default output volume */
+ snd_soc_write(codec, TLV320AIC23_LCHNVOL,
+ TLV320AIC23_DEFAULT_OUT_VOL & TLV320AIC23_OUT_VOL_MASK);
+ snd_soc_write(codec, TLV320AIC23_RCHNVOL,
+ TLV320AIC23_DEFAULT_OUT_VOL & TLV320AIC23_OUT_VOL_MASK);
+
+ snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x1);
+
+ snd_soc_add_codec_controls(codec, tlv320aic23_snd_controls,
+ ARRAY_SIZE(tlv320aic23_snd_controls));
+
+ return 0;
+}
+
+static int tlv320aic23_remove(struct snd_soc_codec *codec)
+{
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
+ .reg_cache_size = ARRAY_SIZE(tlv320aic23_reg),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = tlv320aic23_reg,
+ .probe = tlv320aic23_probe,
+ .remove = tlv320aic23_remove,
+ .suspend = tlv320aic23_suspend,
+ .resume = tlv320aic23_resume,
+ .set_bias_level = tlv320aic23_set_bias_level,
+ .dapm_widgets = tlv320aic23_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
+ .dapm_routes = tlv320aic23_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon),
+};
+
+
+#if defined(CONFIG_SPI_MASTER)
+static int tlv320aic23_spi_probe(struct spi_device *spi)
+{
+ struct aic23 *aic23;
+ int ret;
+ printk(KERN_INFO "tlv320aic23_spi_probe\n");
+
+ aic23 = devm_kzalloc(&spi->dev, sizeof(struct aic23), GFP_KERNEL);
+ if (aic23 == NULL) {
+ printk(KERN_INFO "tlv320aic23_spi_probe error!");
+ return -ENOMEM;
+ }
+ spi->mode=SPI_MODE_2;
+
+ spi_set_drvdata(spi, aic23);
+ aic23->control_type = SND_SOC_SPI;
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_tlv320aic23, &tlv320aic23_dai, 1);
+ return ret;
+}
+
+static int __exit tlv320aic23_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static const struct spi_device_id tlv320aic23_spi_id[] = {
+ {"tlv320aic23-spi", 0},
+ {}
+};
+
+static struct spi_driver tlv320aic23_spi_driver = {
+ .driver = {
+ .name = "tlv320aic23-spi",
+ .owner = THIS_MODULE,
+ },
+ .probe = tlv320aic23_spi_probe,
+ .remove = __devexit_p(tlv320aic23_spi_remove),
+ .id_table = tlv320aic23_spi_id,
+};
+#endif
+static int __init tlv320aic23_modinit(void)
+{
+ int ret;
+
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&tlv320aic23_spi_driver);
+#endif
+
+ return ret;
+}
+module_init(tlv320aic23_modinit);
+
+static void __exit tlv320aic23_exit(void)
+{
+
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&tlv320aic23_spi_driver);
+#endif
+
+}
+module_exit(tlv320aic23_exit);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rdafpga/tlv320aic23_spi.h b/sound/soc/rdafpga/tlv320aic23_spi.h
new file mode 100644
index 000000000000..e804120bd3da
--- /dev/null
+++ b/sound/soc/rdafpga/tlv320aic23_spi.h
@@ -0,0 +1,119 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver
+ *
+ * Author: Arun KS, <arunks@mistralsolutions.com>
+ * Copyright: (C) 2008 Mistral Solutions Pvt Ltd
+ *
+ * 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.
+ */
+
+#ifndef _TLV320AIC23_H
+#define _TLV320AIC23_H
+
+/* Codec TLV320AIC23 */
+#define TLV320AIC23_LINVOL 0x00
+#define TLV320AIC23_RINVOL 0x01
+#define TLV320AIC23_LCHNVOL 0x02
+#define TLV320AIC23_RCHNVOL 0x03
+#define TLV320AIC23_ANLG 0x04
+#define TLV320AIC23_DIGT 0x05
+#define TLV320AIC23_PWR 0x06
+#define TLV320AIC23_DIGT_FMT 0x07
+#define TLV320AIC23_SRATE 0x08
+#define TLV320AIC23_ACTIVE 0x09
+#define TLV320AIC23_RESET 0x0F
+
+/* Left (right) line input volume control register */
+#define TLV320AIC23_LRS_ENABLED 0x0100
+#define TLV320AIC23_LIM_MUTED 0x0080
+#define TLV320AIC23_LIV_DEFAULT 0x0017
+#define TLV320AIC23_LIV_MAX 0x001f
+#define TLV320AIC23_LIV_MIN 0x0000
+
+/* Left (right) channel headphone volume control register */
+#define TLV320AIC23_LZC_ON 0x0080
+#define TLV320AIC23_LHV_DEFAULT 0x0079
+#define TLV320AIC23_LHV_MAX 0x007f
+#define TLV320AIC23_LHV_MIN 0x0000
+
+/* Analog audio path control register */
+#define TLV320AIC23_STA_REG(x) ((x)<<6)
+#define TLV320AIC23_STE_ENABLED 0x0020
+#define TLV320AIC23_DAC_SELECTED 0x0010
+#define TLV320AIC23_BYPASS_ON 0x0008
+#define TLV320AIC23_INSEL_MIC 0x0004
+#define TLV320AIC23_MICM_MUTED 0x0002
+#define TLV320AIC23_MICB_20DB 0x0001
+
+/* Digital audio path control register */
+#define TLV320AIC23_DACM_MUTE 0x0008
+#define TLV320AIC23_DEEMP_32K 0x0002
+#define TLV320AIC23_DEEMP_44K 0x0004
+#define TLV320AIC23_DEEMP_48K 0x0006
+#define TLV320AIC23_ADCHP_ON 0x0001
+
+/* Power control down register */
+#define TLV320AIC23_DEVICE_PWR_OFF 0x0080
+#define TLV320AIC23_CLK_OFF 0x0040
+#define TLV320AIC23_OSC_OFF 0x0020
+#define TLV320AIC23_OUT_OFF 0x0010
+#define TLV320AIC23_DAC_OFF 0x0008
+#define TLV320AIC23_ADC_OFF 0x0004
+#define TLV320AIC23_MIC_OFF 0x0002
+#define TLV320AIC23_LINE_OFF 0x0001
+
+/* Digital audio interface register */
+#define TLV320AIC23_MS_MASTER 0x0040
+#define TLV320AIC23_LRSWAP_ON 0x0020
+#define TLV320AIC23_LRP_ON 0x0010
+#define TLV320AIC23_IWL_16 0x0000
+#define TLV320AIC23_IWL_20 0x0004
+#define TLV320AIC23_IWL_24 0x0008
+#define TLV320AIC23_IWL_32 0x000C
+#define TLV320AIC23_FOR_I2S 0x0002
+#define TLV320AIC23_FOR_DSP 0x0003
+#define TLV320AIC23_FOR_LJUST 0x0001
+
+/* Sample rate control register */
+#define TLV320AIC23_CLKOUT_HALF 0x0080
+#define TLV320AIC23_CLKIN_HALF 0x0040
+#define TLV320AIC23_BOSR_384fs 0x0002 /* BOSR_272fs in USB mode */
+#define TLV320AIC23_USB_CLK_ON 0x0001
+#define TLV320AIC23_SR_MASK 0xf
+#define TLV320AIC23_CLKOUT_SHIFT 7
+#define TLV320AIC23_CLKIN_SHIFT 6
+#define TLV320AIC23_SR_SHIFT 2
+#define TLV320AIC23_BOSR_SHIFT 1
+
+/* Digital interface register */
+#define TLV320AIC23_ACT_ON 0x0001
+
+/*
+ * AUDIO related MACROS
+ */
+
+#define TLV320AIC23_DEFAULT_OUT_VOL 0x70
+#define TLV320AIC23_DEFAULT_IN_VOLUME 0x10
+
+#define TLV320AIC23_OUT_VOL_MIN TLV320AIC23_LHV_MIN
+#define TLV320AIC23_OUT_VOL_MAX TLV320AIC23_LHV_MAX
+#define TLV320AIC23_OUT_VO_RANGE (TLV320AIC23_OUT_VOL_MAX - \
+ TLV320AIC23_OUT_VOL_MIN)
+#define TLV320AIC23_OUT_VOL_MASK TLV320AIC23_OUT_VOL_MAX
+
+#define TLV320AIC23_IN_VOL_MIN TLV320AIC23_LIV_MIN
+#define TLV320AIC23_IN_VOL_MAX TLV320AIC23_LIV_MAX
+#define TLV320AIC23_IN_VOL_RANGE (TLV320AIC23_IN_VOL_MAX - \
+ TLV320AIC23_IN_VOL_MIN)
+#define TLV320AIC23_IN_VOL_MASK TLV320AIC23_IN_VOL_MAX
+
+#define TLV320AIC23_SIDETONE_MASK 0x1c0
+#define TLV320AIC23_SIDETONE_0 0x100
+#define TLV320AIC23_SIDETONE_6 0x000
+#define TLV320AIC23_SIDETONE_9 0x040
+#define TLV320AIC23_SIDETONE_12 0x080
+#define TLV320AIC23_SIDETONE_18 0x0c0
+
+#endif /* _TLV320AIC23_H */