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
commit2c3289c7071a4461dc92faac12be7097fbf3457a (patch)
tree4ae6b829fd8688641f26f93e79b1763658ee16fd
parent09714f8ea9072b750c02b0208a10c615901a42e4 (diff)
sound: soc: rda: RDA support
-rw-r--r--sound/soc/rda/Kconfig28
-rw-r--r--sound/soc/rda/Makefile12
-rw-r--r--sound/soc/rda/rda_aif.h234
-rw-r--r--sound/soc/rda/rda_audifc.c347
-rw-r--r--sound/soc/rda/rda_audifc.h86
-rw-r--r--sound/soc/rda/rda_bb_ifc.h120
-rw-r--r--sound/soc/rda/rda_codec.c1061
-rw-r--r--sound/soc/rda/rda_codec.h138
-rw-r--r--sound/soc/rda/rda_codec_adp.h423
-rw-r--r--sound/soc/rda/rda_dai.c471
-rw-r--r--sound/soc/rda/rda_dsp_aud.c334
-rw-r--r--sound/soc/rda/rda_dsp_aud.h44
-rw-r--r--sound/soc/rda/rda_pcm.c378
-rw-r--r--sound/soc/rda/rda_pcm.h40
-rw-r--r--sound/soc/rda/rda_soundcard.c150
-rw-r--r--sound/soc/rda/rda_voice_codec.c211
-rw-r--r--sound/soc/rda/rda_voice_dai.c161
-rw-r--r--sound/soc/rda/rda_voice_pcm.c588
-rw-r--r--sound/soc/rda/rda_voice_pcm.h40
19 files changed, 4866 insertions, 0 deletions
diff --git a/sound/soc/rda/Kconfig b/sound/soc/rda/Kconfig
new file mode 100644
index 000000000000..1fa4fded4bc0
--- /dev/null
+++ b/sound/soc/rda/Kconfig
@@ -0,0 +1,28 @@
+menu "Soc Audio for the RDA chips."
+
+
+config SND_RDA_SOC
+ tristate "SoC Audio for the RDA."
+ depends on ARCH_RDA && !RDA_FPGA
+
+config SND_RDA_SOC_RDASOUNDCARD
+ tristate "rda CHIP sound card -- SoC Audio support for RAD chip."
+ depends on SND_RDA_SOC
+ select SND_RDA_SOC_RDASOUNDCARD_PCM
+ select SND_RDA_SOC_RDASOUNDCARD_DAI
+ select SND_RDA_SOC_RDASOUNDCARD_CODEC
+ help
+ Say Y if you want to build in to kernel, Say M for module.
+
+config SND_RDA_SOC_RDASOUNDCARD_PCM
+ tristate
+
+config SND_RDA_SOC_RDASOUNDCARD_DAI
+ tristate
+
+config SND_RDA_SOC_RDASOUNDCARD_CODEC
+ tristate
+
+endmenu
+
+
diff --git a/sound/soc/rda/Makefile b/sound/soc/rda/Makefile
new file mode 100644
index 000000000000..51cae8c62051
--- /dev/null
+++ b/sound/soc/rda/Makefile
@@ -0,0 +1,12 @@
+# rda Platform Support
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_PCM) += rda_pcm.o
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_PCM) += rda_dsp_aud.o
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_PCM) += rda_voice_pcm.o
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_PCM) += rda_audifc.o
+
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_DAI) += rda_dai.o
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_DAI) += rda_voice_dai.o
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_CODEC) += rda_codec.o
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD_CODEC) += rda_voice_codec.o
+
+obj-$(CONFIG_SND_RDA_SOC_RDASOUNDCARD) += rda_soundcard.o
diff --git a/sound/soc/rda/rda_aif.h b/sound/soc/rda/rda_aif.h
new file mode 100644
index 000000000000..80795b33c92b
--- /dev/null
+++ b/sound/soc/rda/rda_aif.h
@@ -0,0 +1,234 @@
+/*
+ * 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 _RDA_AIF_H_
+#define _RDA_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/rda/rda_audifc.c b/sound/soc/rda/rda_audifc.c
new file mode 100644
index 000000000000..a239ee096296
--- /dev/null
+++ b/sound/soc/rda/rda_audifc.c
@@ -0,0 +1,347 @@
+/*
+ * 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 "rda_bb_ifc.h"
+#include "rda_audifc.h"
+
+static HWP_BB_IFC_T __iomem *hwp_audIfc;
+
+static struct rda_audifc_ch *audifc_ch[RDA_AUDIFC_QTY];
+
+static int irq[2];
+
+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;
+
+ if(!ptr_ch || ptr_ch->inuse == IFC_NOUSE)
+ return IRQ_NONE;
+
+ reg = hwp_audIfc->ch[RDA_AUDIFC_RECORD].status
+ & (BB_IFC_CAUSE_IEF | BB_IFC_CAUSE_IHF | BB_IFC_CAUSE_I4F | BB_IFC_CAUSE_I3_4F);
+
+ hwp_audIfc->ch[RDA_AUDIFC_RECORD].int_clear = reg;
+
+ if(ptr_ch->callback == NULL)
+ return IRQ_NONE;
+
+ 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;
+}
+
+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;
+
+ if(!ptr_ch || ptr_ch->inuse == IFC_NOUSE)
+ 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(ptr_ch->callback == NULL)
+ return IRQ_NONE;
+
+ 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)
+{
+
+ if (hwp_audIfc->ch[ch].status & BB_IFC_ENABLE) {
+ printk(KERN_INFO "%s: audio ifc busy \n", __func__);
+ return RDA_AUDIFC_ERROR;
+ }
+ // Assert on word alignement
+ if (((u32) params->src_addr) % 4 != 0) {
+ 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) {
+ 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;
+ hwp_audIfc->ch[ch].Fifo_Size = params->xfer_size;
+
+ // we need this four ints !!!!
+ 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)
+{
+ return (u32) (hwp_audIfc->ch[ch].cur_ahb_addr);
+}
+
+audifc_addr_t rda_get_audifc_dst_pos(u8 ch)
+{
+ return (u32) (hwp_audIfc->ch[ch].cur_ahb_addr);
+}
+
+void rda_start_audifc(u8 ch)
+{
+ enable_irq(irq[ch]);
+ hwp_audIfc->ch[ch].control = BB_IFC_ENABLE;
+ return;
+}
+
+void rda_stop_audifc(u8 ch)
+{
+ disable_irq_nosync(irq[ch]);
+ hwp_audIfc->ch[ch].control = BB_IFC_DISABLE;
+ return;
+}
+
+void rda_poll_audifc(u8 ch)
+{
+ 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. */
+ if(audifc_ch[dev_id]->inuse == IFC_INUSE)
+ 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[dev_id]->inuse = IFC_INUSE;
+
+ *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;
+ audifc_ch[ch]->inuse = IFC_NOUSE;
+
+ return;
+}
+
+static int rda_audifc_probe(struct platform_device *pdev)
+{
+ struct rda_audifc_device_data *audifc_device;
+ struct resource *mem;
+ //int irq[RDA_AUDIFC_QTY];
+ int ret;
+
+ 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;
+ }
+ disable_irq(irq[RDA_AUDIFC_RECORD]);
+
+ 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;
+ }
+ disable_irq(irq[RDA_AUDIFC_PLAY]);
+ printk(KERN_INFO"audio ifc : initialized .\n");
+
+ return 0;
+}
+
+static int rda_audifc_remove(struct platform_device *pdev)
+{
+ 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]->inuse = IFC_NOUSE;
+ 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]->inuse = IFC_NOUSE;
+ audifc_ch[RDA_AUDIFC_PLAY] = NULL;
+ }
+
+ return 0;
+}
+
+static struct platform_driver rda_audifc_driver = {
+ .probe = rda_audifc_probe,
+ .remove = 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 RDA FPGA PCM");
+MODULE_AUTHOR("Xu Mingliang <mingliangxu@rdamicro.com>");
+MODULE_ALIAS("platform: rda-audifc");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rda/rda_audifc.h b/sound/soc/rda/rda_audifc.h
new file mode 100644
index 000000000000..ee6bc079662f
--- /dev/null
+++ b/sound/soc/rda/rda_audifc.h
@@ -0,0 +1,86 @@
+
+/*
+ * 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 _RDA_AUDIFC_H
+#define _RDA_AUDIFC_H
+
+#include <linux/bitops.h>
+
+// =============================================================================
+// MACROS
+// =============================================================================
+
+#define IFC_INUSE 1
+#define IFC_NOUSE 0
+
+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 /* _RDA_AUDIFC_H */
diff --git a/sound/soc/rda/rda_bb_ifc.h b/sound/soc/rda/rda_bb_ifc.h
new file mode 100644
index 000000000000..76fa81d27652
--- /dev/null
+++ b/sound/soc/rda/rda_bb_ifc.h
@@ -0,0 +1,120 @@
+//==============================================================================
+//
+// 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 _RDA_BB_IFC_H_
+#define _RDA_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;
+
+//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)
+
+//cur_ahb_addr
+#define BB_IFC_CUR_AHB_ADDR(n) (((n)&0x3FFFFFF)<<0)
+
+#endif
diff --git a/sound/soc/rda/rda_codec.c b/sound/soc/rda/rda_codec.c
new file mode 100644
index 000000000000..c335391c93db
--- /dev/null
+++ b/sound/soc/rda/rda_codec.c
@@ -0,0 +1,1061 @@
+/*
+ * ALSA SoC RDA 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:
+ * rda codec
+ *
+ * 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 <mach/iomap.h>
+#include <asm/io.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <plat/reg_spi.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 <plat/rda_debug.h>
+#include <plat/md_sys.h>
+
+#include <linux/gpio.h>
+
+#include "rda_codec.h"
+#include "rda_codec_adp.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+static int gpio_audio_extpa = -1;
+static int gpio_audio_extpa_1 = -1;//add for external PA
+
+//#define DEBUG 1
+
+/* rda_codec_data driver data */
+struct rda_codec_data {
+ void __iomem *io_base;
+ struct msys_device *codec_msys;
+ struct snd_soc_codec* codec;
+ u32 in_sample_rate;
+ u32 in_channel_nb;
+ u32 out_sample_rate;
+ u32 out_channel_nb;
+ SND_ITF_T itf;
+ HAL_AIF_STREAM_T stream;
+ AUD_LEVEL_T cfg;
+ AUD_APP_MODE_T CodecAppMode;
+ u8 codec_is_open;
+
+ // loop mode
+ SND_ITF_T loop_mode_itf;
+};
+
+#ifdef DEBUG
+static void aud_Dump(SND_ITF_T itf, HAL_AIF_STREAM_T *stream, AUD_LEVEL_T *cfg)
+{
+ printk(KERN_INFO "############################### \n");
+ printk(KERN_INFO "itf is [%d] \n", itf);
+ if(stream != NULL) {
+ printk(KERN_INFO "stream->sampleRate is [%d] \n", stream->sampleRate);
+ printk(KERN_INFO "stream->channelNb is [%d] \n", stream->channelNb);
+ }
+ else {
+ printk(KERN_INFO "stream is NULL!!! \n");
+ }
+ if(cfg != NULL) {
+ printk(KERN_INFO "cfg->spkLevel is [%d] \n", cfg->spkLevel);
+ printk(KERN_INFO "cfg->micLevel is [%d] \n", cfg->micLevel);
+ }
+ else {
+ printk(KERN_INFO "cfg is NULL!!! \n");
+ }
+
+ printk(KERN_INFO "############################### \n");
+}
+#endif
+
+static int aud_StreamStart(SND_ITF_T itf, HAL_AIF_STREAM_T *stream, AUD_LEVEL_T* cfg,
+ struct rda_codec_data* codec_data)
+{
+ int ret = 0;
+ u8 __dat[sizeof(SND_ITF_T) + sizeof(HAL_AIF_STREAM_T) + sizeof(AUD_LEVEL_T)] = {0};
+ struct client_cmd codec_cmd;
+
+#ifdef DEBUG
+ aud_Dump(itf, stream, cfg);
+#endif
+
+ memcpy((u8 *)&__dat, &itf,
+ sizeof(SND_ITF_T));
+ memcpy((u8 *)&__dat + sizeof(SND_ITF_T), stream,
+ sizeof(HAL_AIF_STREAM_T));
+ memcpy((u8* )&__dat + sizeof(SND_ITF_T) + sizeof(HAL_AIF_STREAM_T), cfg,
+ sizeof(AUD_LEVEL_T));
+
+ memset(&codec_cmd, 0, sizeof(codec_cmd));
+ codec_cmd.pmsys_dev = codec_data->codec_msys;
+ codec_cmd.mod_id = SYS_AUDIO_MOD;
+ codec_cmd.mesg_id = SYS_AUDIO_CMD_AUD_STREAM_START;
+ codec_cmd.pdata = (void *)&__dat;
+ codec_cmd.data_size = sizeof(__dat);
+ ret = rda_msys_send_cmd(&codec_cmd);
+ if ( ret )
+ printk(KERN_INFO ">>>> [%s], ret [%d] \n", __func__, ret);
+ else
+ rda_dbg_audio(">>>> [%s], ret [%d] \n", __func__, ret);
+
+ return ret;
+}
+
+static int aud_StreamRecord(SND_ITF_T itf, HAL_AIF_STREAM_T *stream, AUD_LEVEL_T* cfg,
+ struct rda_codec_data* codec_data)
+{
+ int ret = 0;
+ u8 __dat[sizeof(SND_ITF_T) + sizeof(HAL_AIF_STREAM_T) + sizeof(AUD_LEVEL_T)] = {0};
+ struct client_cmd codec_cmd;
+
+#ifdef DEBUG
+ aud_Dump(itf, stream, cfg);
+#endif
+
+ memcpy((u8 *)&__dat, &itf, sizeof(SND_ITF_T));
+ memcpy((u8 *)&__dat + sizeof(SND_ITF_T), stream,
+ sizeof(HAL_AIF_STREAM_T));
+ memcpy((u8 *)&__dat + sizeof(SND_ITF_T) + sizeof(HAL_AIF_STREAM_T), cfg,
+ sizeof(AUD_LEVEL_T));
+
+ memset(&codec_cmd, 0, sizeof(codec_cmd));
+ codec_cmd.pmsys_dev = codec_data->codec_msys;
+ codec_cmd.mod_id = SYS_AUDIO_MOD;
+ codec_cmd.mesg_id = SYS_AUDIO_CMD_AUD_STREAM_RECORD;
+ codec_cmd.pdata = (void *)&__dat;
+ codec_cmd.data_size = sizeof(__dat);
+ ret = rda_msys_send_cmd(&codec_cmd);
+ if ( ret )
+ printk(KERN_INFO ">>>> [%s], ret [%d] \n", __func__, ret);
+ else
+ rda_dbg_audio(">>>> [%s], ret [%d] \n", __func__, ret);
+
+
+ return ret;
+}
+
+static int aud_StreamStop(SND_ITF_T itf,
+ struct rda_codec_data* codec_data)
+{
+ int ret = 0;
+ u8 __dat[sizeof(SND_ITF_T)] = {0};
+ struct client_cmd codec_cmd;
+
+ memcpy((u8 *)&__dat, &itf, sizeof(SND_ITF_T));
+
+ memset(&codec_cmd, 0, sizeof(codec_cmd));
+ codec_cmd.pmsys_dev = codec_data->codec_msys;
+ codec_cmd.mod_id = SYS_AUDIO_MOD;
+ codec_cmd.mesg_id = SYS_AUDIO_CMD_AUD_STREAM_STOP;
+ codec_cmd.pdata = (void *)&__dat;
+ codec_cmd.data_size = sizeof(__dat);
+ ret = rda_msys_send_cmd(&codec_cmd);
+ if ( ret )
+ printk(KERN_INFO ">>>> [%s], ret [%d] \n", __func__, ret);
+ else
+ rda_dbg_audio(">>>> [%s], ret [%d] \n", __func__, ret);
+
+ return ret;
+}
+
+static int aud_Setup(SND_ITF_T itf, AUD_LEVEL_T* cfg,
+ struct rda_codec_data* codec_data)
+{
+ int ret = 0;
+ u8 __dat[sizeof(SND_ITF_T) + sizeof(AUD_LEVEL_T)] = {0};
+ struct client_cmd codec_cmd;
+
+#ifdef DEBUG
+ aud_Dump(itf, NULL, cfg);
+#endif
+
+ memcpy((u8 *)&__dat, &itf, sizeof(SND_ITF_T));
+ memcpy((u8 *)&__dat + sizeof(SND_ITF_T), cfg, sizeof(AUD_LEVEL_T));
+
+ memset(&codec_cmd, 0, sizeof(codec_cmd));
+ codec_cmd.pmsys_dev = codec_data->codec_msys;
+ codec_cmd.mod_id = SYS_AUDIO_MOD;
+ codec_cmd.mesg_id = SYS_AUDIO_CMD_AUD_SETUP;
+ codec_cmd.pdata = (void *)&__dat;
+ codec_cmd.data_size = sizeof(__dat);
+ ret = rda_msys_send_cmd(&codec_cmd);
+ if ( ret )
+ printk(KERN_DEBUG ">>>> [%s], ret [%d] \n", __func__, ret);
+ else
+ rda_dbg_audio(">>>> [%s], ret [%d] \n", __func__, ret);
+
+
+ return ret;
+}
+
+static int aud_LoudspeakerWithEarpiece(u8 on,
+ struct rda_codec_data* codec_data)
+{
+ int ret = 0;
+ u32 __dat = 0;
+ struct client_cmd codec_cmd;
+
+ __dat = on;
+
+ memset(&codec_cmd, 0, sizeof(codec_cmd));
+ codec_cmd.pmsys_dev = codec_data->codec_msys;
+ codec_cmd.mod_id = SYS_AUDIO_MOD;
+ codec_cmd.mesg_id = SYS_AUDIO_CMD_AUD_LOUDSPEAKER_WITH_EARPIECE;
+ codec_cmd.pdata = (void *)&__dat;
+ codec_cmd.data_size = sizeof(__dat);
+ ret = rda_msys_send_cmd(&codec_cmd);
+ if ( ret )
+ printk(KERN_INFO ">>>> [%s], ret [%d] \n", __func__, ret);
+ else
+ rda_dbg_audio(">>>> [%s], ret [%d] \n", __func__, ret);
+
+ return ret;
+}
+
+static int aud_ForceReceiverMicSelection(u8 on,
+ struct rda_codec_data* codec_data)
+{
+ int ret = 0;
+ u32 __dat = 0;
+ struct client_cmd codec_cmd;
+
+ __dat = on;
+
+ memset(&codec_cmd, 0, sizeof(codec_cmd));
+ codec_cmd.pmsys_dev = codec_data->codec_msys;
+ codec_cmd.mod_id = SYS_AUDIO_MOD;
+ codec_cmd.mesg_id = SYS_AUDIO_CMD_AUD_FORCE_RECEIVER_MIC_SELECTION;
+ codec_cmd.pdata = (void *)&__dat;
+ codec_cmd.data_size = sizeof(__dat);
+ ret = rda_msys_send_cmd(&codec_cmd);
+ if ( ret )
+ printk(KERN_INFO ">>>> [%s], ret [%d] \n", __func__, ret);
+ else
+ rda_dbg_audio(">>>> [%s], ret [%d] \n", __func__, ret);
+
+ return ret;
+}
+
+static int aud_CodecAppMode(u32 mode,
+ struct rda_codec_data* codec_data)
+{
+ int ret = 0;
+ u32 __dat = 0;
+ struct client_cmd codec_cmd;
+
+ __dat = mode;
+ memset(&codec_cmd, 0, sizeof(codec_cmd));
+ codec_cmd.pmsys_dev = codec_data->codec_msys;
+ codec_cmd.mod_id = SYS_AUDIO_MOD;
+ codec_cmd.mesg_id = SYS_AUDIO_CMD_AUD_CODEC_APP_MODE;
+ codec_cmd.pdata = (void *)&__dat;
+ codec_cmd.data_size = sizeof(__dat);
+ ret = rda_msys_send_cmd(&codec_cmd);
+ if ( ret )
+ printk(KERN_INFO ">>>> [%s], ret [%d] \n", __func__, ret);
+ else
+ rda_dbg_audio(">>>> [%s], ret [%d] \n", __func__, ret);
+
+ return ret;
+
+}
+
+static int aud_TestModeSetup(SND_ITF_T itf, HAL_AIF_STREAM_T *stream, AUD_LEVEL_T* cfg,
+ AUD_TEST_T mode, struct rda_codec_data* codec_data)
+{
+ int ret = 0;
+ u8 __dat[sizeof(SND_ITF_T) + sizeof(HAL_AIF_STREAM_T) + sizeof(AUD_LEVEL_T) + sizeof(AUD_TEST_T)] = {0};
+ struct client_cmd codec_cmd;
+
+#ifdef DEBUG
+ aud_Dump(itf, stream, cfg);
+#endif
+
+ memcpy((u8 *)&__dat, &itf, sizeof(SND_ITF_T));
+ memcpy((u8 *)&__dat + sizeof(SND_ITF_T), stream,
+ sizeof(HAL_AIF_STREAM_T));
+ memcpy((u8 *)&__dat + sizeof(SND_ITF_T) + sizeof(HAL_AIF_STREAM_T), cfg,
+ sizeof(AUD_LEVEL_T));
+ memcpy((u8 *)&__dat + sizeof(SND_ITF_T) + sizeof(HAL_AIF_STREAM_T) + sizeof(AUD_LEVEL_T), &mode, sizeof(AUD_TEST_T));
+
+ memset(&codec_cmd, 0, sizeof(codec_cmd));
+ codec_cmd.pmsys_dev = codec_data->codec_msys;
+ codec_cmd.mod_id = SYS_AUDIO_MOD;
+ codec_cmd.mesg_id = SYS_AUDIO_CMD_AUD_TEST_MODE_SETUP;
+ codec_cmd.pdata = (void *)&__dat;
+ codec_cmd.data_size = sizeof(__dat);
+ ret = rda_msys_send_cmd(&codec_cmd);
+ if ( ret )
+ printk(KERN_INFO ">>>> [%s], ret [%d] \n", __func__, ret);
+ else
+ rda_dbg_audio(">>>> [%s], ret [%d] \n", __func__, ret);
+
+ return ret;
+}
+static int ctrl_ext_get_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+static int rda_codec_get_open_status(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ printk(KERN_INFO"rda codec : codec_data->codec_is_open %d \n", codec_data->codec_is_open);
+
+ ucontrol->value.integer.value[0] = codec_data->codec_is_open;
+
+ return 0;
+}
+static int ctrl_ext_set_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+static int rda_codec_set_playback_volume(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 volume = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ volume = ucontrol->value.integer.value[0];
+
+ codec_data->cfg.spkLevel = volume;
+
+ return 0;
+}
+static int rda_codec_set_capture_volume(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 volume = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ volume = ucontrol->value.integer.value[0];
+
+ codec_data->cfg.micLevel = volume;
+
+ return 0;
+}
+
+static int rda_codec_set_itf(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 itf = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ itf = ucontrol->value.integer.value[0];
+
+ codec_data->itf = itf;
+
+ return 0;
+}
+
+static int rda_codec_codec_app_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ u32 mode = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ mode = ucontrol->value.integer.value[0];
+
+ codec_data->CodecAppMode = mode;
+
+ ret = aud_CodecAppMode(mode, codec_data);
+
+ return (ret!=0?-1:0);
+}
+
+static int rda_codec_start_play(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ codec_data->stream.sampleRate = codec_data->out_sample_rate;
+ codec_data->stream.channelNb = codec_data->out_channel_nb;
+ ret = aud_StreamStart(codec_data->itf, &(codec_data->stream),
+ &(codec_data->cfg), codec_data);
+
+ if(!ret) {
+ codec_data->codec_is_open = TRUE;
+ }
+
+ return (ret!=0?-1:0);
+}
+
+static int rda_codec_stop(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ ret = aud_StreamStop(codec_data->itf, codec_data);
+
+ if(!ret) {
+ codec_data->codec_is_open = FALSE;
+ }
+
+ return (ret!=0?-1:0);
+}
+
+static int rda_codec_start_record(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ codec_data->stream.sampleRate = codec_data->in_sample_rate;
+ codec_data->stream.channelNb = codec_data->in_channel_nb;
+ ret = aud_StreamRecord(codec_data->itf, &(codec_data->stream),
+ &(codec_data->cfg), codec_data);
+
+ if(!ret) {
+ codec_data->codec_is_open = TRUE;
+ }
+
+ return (ret!=0?-1:0);
+}
+
+static int rda_codec_set_in_channel_number(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 val = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ val = ucontrol->value.integer.value[0];
+
+ codec_data->in_channel_nb = val;
+
+ return 0;
+}
+
+static int rda_codec_set_in_sample_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 val = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ val = ucontrol->value.integer.value[0];
+
+ codec_data->in_sample_rate = val;
+
+ return 0;
+}
+
+static int rda_codec_set_out_channel_number(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 val = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ val = ucontrol->value.integer.value[0];
+
+ codec_data->out_channel_nb = val;
+
+ return 0;
+}
+
+static int rda_codec_set_out_sample_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 val = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ val = ucontrol->value.integer.value[0];
+
+ codec_data->out_sample_rate = val;
+
+ return 0;
+}
+
+static int rda_codec_set_spksel(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ AUD_SPK_T spk = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ spk = ucontrol->value.integer.value[0];
+ rda_dbg_audio("SPK = %x\n",spk);
+ if (spk == AUD_SPK_DISABLE) {
+ if (gpio_is_valid(gpio_audio_extpa))
+ gpio_set_value(gpio_audio_extpa,0);//turn off
+ else
+ rda_dbg_audio("disable, gpio extpa is invalid !\n");
+ if (gpio_is_valid(gpio_audio_extpa_1)) {
+ gpio_set_value(gpio_audio_extpa_1,0);
+ rda_dbg_audio("== external PA disable ==\n");
+ } else
+ rda_dbg_audio("disable, gpio extpa 1 is invalid!\n");
+ } else if ((spk == AUD_SPK_LOUD_SPEAKER_EAR_PIECE) || (spk == AUD_SPK_LOUD_SPEAKER)) {
+ if (gpio_is_valid(gpio_audio_extpa))
+ gpio_set_value(gpio_audio_extpa,1);//turn on
+ else
+ rda_dbg_audio("gpio extpa is invalid !\n");
+ if (gpio_is_valid(gpio_audio_extpa_1)) {
+ gpio_set_value(gpio_audio_extpa_1,1);
+ rda_dbg_audio("== external PA enable ==\n");
+ } else
+ rda_dbg_audio("gpio extpa 1 is invalid !\n");
+ }
+
+ if(spk == AUD_SPK_LOUD_SPEAKER_EAR_PIECE) {
+ ret = aud_LoudspeakerWithEarpiece(TRUE, codec_data);
+ } else {
+ ret = aud_LoudspeakerWithEarpiece(FALSE, codec_data);
+ }
+
+ return (ret!=0?-1:0);
+}
+static int rda_codec_force_mainmic(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0, on = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ on = ucontrol->value.integer.value[0];
+
+ ret = aud_ForceReceiverMicSelection(on, codec_data);
+
+ return (ret!=0?-1:0);
+}
+static int rda_codec_mute_mic(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 mute = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ mute = ucontrol->value.integer.value[0];
+
+ codec_data->cfg.micLevel = mute;
+
+ return 0;
+}
+static int rda_codec_set_commit_setup(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ ret = aud_Setup(codec_data->itf, &(codec_data->cfg), codec_data);
+
+ return (ret!=0?-1:0);
+}
+
+static int rda_audio_loop_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ AUD_TEST_T mode = AUD_TEST_NO;
+ SND_ITF_T itf = SND_ITF_LOUD_SPEAKER;
+ HAL_AIF_STREAM_T stream;
+ AUD_LEVEL_T cfg;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ // 1. mode
+ mode = ucontrol->value.integer.value[0];
+ // 2. itf & cfg
+ switch(mode) {
+ case AUD_TEST_HEADSETMIC_IN_HEADSET_OUT:
+ itf = SND_ITF_EAR_PIECE;
+ mode = AUD_TEST_SIDE_TEST;
+ cfg.micLevel = SND_MIC_ENABLE;
+ cfg.spkLevel = SND_SPK_VOL_6;
+ cfg.toneLevel = 0;
+ cfg.sideLevel = 0;
+ break;
+ case AUD_TEST_RECVMIC_IN_EARPIECE_OUT:
+ itf = SND_ITF_EAR_PIECE;
+ mode = AUD_TEST_SIDE_TEST;
+ cfg.micLevel = SND_MIC_ENABLE;
+ cfg.spkLevel = SND_SPK_VOL_6;
+ cfg.toneLevel = 0;
+ cfg.sideLevel = 0;
+ break;
+ case AUD_TEST_MAINMIC_IN_RECEIVER_OUT:
+ itf = SND_ITF_RECEIVER;
+ mode = AUD_TEST_SIDE_TEST;
+ cfg.micLevel = SND_MIC_ENABLE;
+ cfg.spkLevel = SND_SPK_VOL_6;
+ cfg.toneLevel = 0;
+ cfg.sideLevel = 0;
+ break;
+ case AUD_TEST_SIDE_TEST:
+ itf = SND_ITF_LOUD_SPEAKER;
+ mode = AUD_TEST_SIDE_TEST;
+ cfg.micLevel = SND_MIC_ENABLE;
+ cfg.spkLevel = SND_SPK_VOL_1;
+ cfg.toneLevel = 0;
+ cfg.sideLevel = 0;
+ break;
+ case AUD_TEST_NO:
+ default:
+ itf = codec_data->loop_mode_itf;
+ cfg.micLevel = SND_MIC_MUTE;
+ cfg.spkLevel = SND_SPK_MUTE;
+ cfg.toneLevel = 0;
+ cfg.sideLevel = 0;
+ break;
+ }
+
+ codec_data->loop_mode_itf = itf;
+ // 3. stream
+ stream.startAddress = NULL;
+ stream.length = 0;
+ stream.sampleRate = HAL_AIF_FREQ_8000HZ;
+ stream.channelNb = HAL_AIF_MONO;
+ stream.voiceQuality = FALSE;
+ stream.playSyncWithRecord = FALSE;
+ stream.halfHandler = NULL;
+ stream.endHandler = NULL;
+
+ ret = aud_TestModeSetup(itf, &stream, &cfg, mode, codec_data);
+
+ return (ret!=0?-1:0);
+}
+
+/*
+ * rda_codec_data register cache
+ */
+static const u16 rda_codec_reg[] = {
+ 0x0097, 0x0097, 0x00F9, 0x00F9, /* 0 */
+ 0x001A, 0x0004, 0x0007, 0x0001, /* 4 */
+ 0x0020, 0x0000, 0x0000, 0x0000, /* 8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 12 */
+};
+
+static const struct snd_kcontrol_new rda_codec_snd_controls[] = {
+ // volumes ctrls
+ SOC_SINGLE_EXT(MIXER_PLAYBACK_VOLUME, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_set_playback_volume),
+ SOC_SINGLE_EXT(MIXER_CAPTURE_VOLUME, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_set_capture_volume),
+ // devices ctrls
+ SOC_SINGLE_EXT(MIXER_ITF, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_set_itf),
+ SOC_SINGLE_EXT(MIXER_SPK_SEL, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_set_spksel),
+ SOC_SINGLE_EXT(MIXER_FORCE_MAINMIC, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_force_mainmic),
+ SOC_SINGLE_EXT(MIXER_CODEC_APP_MODE, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_codec_app_mode),
+ SOC_SINGLE_EXT(MIXER_START_PLAY, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_start_play),
+ SOC_SINGLE_EXT(MIXER_START_RECORD, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_start_record),
+ SOC_SINGLE_EXT(MIXER_STOP, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_stop),
+ SOC_SINGLE_EXT(MIXER_OUT_SAMPLE_RATE, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_set_out_sample_rate),
+ SOC_SINGLE_EXT(MIXER_OUT_CHANNEL_NB, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_set_out_channel_number),
+ SOC_SINGLE_EXT(MIXER_IN_SAMPLE_RATE, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_set_in_sample_rate),
+ SOC_SINGLE_EXT(MIXER_IN_CHANNEL_NB, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_set_in_channel_number),
+ SOC_SINGLE_EXT(MIXER_MUTE_MIC, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_mute_mic),
+ SOC_SINGLE_EXT(MIXER_COMMIT_SETUP, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_codec_set_commit_setup),
+ // status
+ SOC_SINGLE_EXT(MIXER_CODEC_OPEN_STATUS, 0, 0, 0xFFFF, 0,
+ rda_codec_get_open_status, ctrl_ext_set_reg),
+ // factory mode ctrls
+ SOC_SINGLE_EXT(MIXER_LOOP_MODE, 0, 0, 0xFFFF, 0,
+ ctrl_ext_get_reg, rda_audio_loop_mode),
+};
+
+
+static int rda_codec_dai_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;
+ int ret = 0;
+ struct rda_codec_data *codec_data = snd_soc_codec_get_drvdata(codec);
+
+ u32 sample_rate = params_rate(params);
+
+ codec_data->stream.sampleRate = sample_rate;
+
+ return ret;
+}
+
+static int rda_codec_dai_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ // do this in user space
+ return 0;
+}
+
+static int rda_codec_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int rda_codec_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+
+ return 0;
+}
+
+static void rda_codec_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+}
+
+static int rda_codec_dai_dig_mute(struct snd_soc_dai *dai, int mute)
+{
+ return 0;
+}
+
+static int rda_codec_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ return 0;
+}
+
+static int rda_codec_dai_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ return 0;
+}
+
+static int rda_codec_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define RDA_CODEC_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 rda_codec_dai_ops = {
+ .startup = rda_codec_dai_startup,
+ .hw_params = rda_codec_dai_hw_params,
+ .prepare = rda_codec_dai_pcm_prepare,
+ .trigger = rda_codec_dai_trigger,
+ .shutdown = rda_codec_dai_shutdown,
+ .digital_mute = rda_codec_dai_dig_mute,
+ .set_fmt = rda_codec_dai_set_fmt,
+ .set_sysclk = rda_codec_dai_set_sysclk,
+};
+
+static struct snd_soc_dai_driver rda_codec_dai_driver = {
+ .name = "rda-codec-dai",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = RDA_CODEC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = RDA_CODEC_FORMATS,
+ },
+ .ops = &rda_codec_dai_ops,
+};
+
+static int rda_codec_suspend(struct snd_soc_codec *codec)
+{
+ rda_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int rda_codec_resume(struct snd_soc_codec *codec)
+{
+ snd_soc_cache_sync(codec);
+ rda_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+// just like hw_write in soc-io.c
+static int rda_codec_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ return 0;
+}
+
+// just like hw_write in soc-io.c
+static unsigned int rda_codec_hw_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ return 0;
+}
+
+static int rda_modem_codec_notify(struct notifier_block *nb, unsigned long mesg, void *data)
+{
+ struct msys_device *pmsys_dev = container_of(nb, struct msys_device, notifier);
+ struct rda_codec_data *codec_data = (struct rda_codec_data *)pmsys_dev->private;
+ struct client_mesg *pmesg = (struct client_mesg *)data;
+ struct snd_soc_codec* codec = NULL;
+
+ if(codec_data != NULL)
+ codec = codec_data->codec;
+
+ if (pmesg->mod_id != SYS_GEN_MOD) {
+ return NOTIFY_DONE;
+ }
+
+ if (mesg != SYS_GEN_MESG_RTC_TRIGGER) {
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int rda_codec_probe(struct snd_soc_codec *codec)
+{
+ struct rda_codec_data *codec_data = NULL;
+
+ codec_data = snd_soc_codec_get_drvdata(codec);
+
+ if(codec_data == NULL) {
+ printk(KERN_INFO"NULL codec_data is when probe, error \n");
+ return -1;
+ }
+
+ codec_data->codec = codec;
+
+ snd_soc_add_codec_controls(codec, rda_codec_snd_controls,
+ ARRAY_SIZE(rda_codec_snd_controls));
+
+ return 0;
+}
+
+static int rda_codec_remove(struct snd_soc_codec *codec)
+{
+ struct rda_codec_data* codec_data = NULL;
+
+ codec_data = snd_soc_codec_get_drvdata(codec);
+
+ rda_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_rda_codec_driver = {
+ .reg_cache_size = ARRAY_SIZE(rda_codec_reg),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = rda_codec_reg,
+ .read = rda_codec_hw_read,
+ .write = rda_codec_hw_write,
+ .probe = rda_codec_probe,
+ .remove = rda_codec_remove,
+ .suspend = rda_codec_suspend,
+ .resume = rda_codec_resume,
+ .set_bias_level = rda_codec_set_bias_level,
+ .dapm_widgets = NULL,
+ .num_dapm_widgets = 0,
+ .dapm_routes = NULL,
+ .num_dapm_routes = 0,
+};
+
+static int rda_codec_platform_probe(struct platform_device *pdev)
+{
+ struct rda_codec_data *codec_data = NULL;
+ int ret = 0;
+ struct resource *extpa_res,*extpa1_res;
+
+ codec_data = devm_kzalloc(&pdev->dev, sizeof(struct rda_codec_data), GFP_KERNEL);
+
+ if (codec_data == NULL) {
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, codec_data);
+
+ // ap <---> modem codec
+ codec_data->codec_msys = rda_msys_alloc_device();
+ if (!codec_data->codec_msys) {
+ ret = -ENOMEM;
+ }
+
+ codec_data->codec_msys->module = SYS_AUDIO_MOD;
+ codec_data->codec_msys->name = "rda-codec";
+ codec_data->codec_msys->notifier.notifier_call = rda_modem_codec_notify;
+ codec_data->codec_msys->private = (void *)codec_data;
+
+ rda_msys_register_device(codec_data->codec_msys);
+
+ ret = snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_rda_codec_driver,
+ &rda_codec_dai_driver, 1);
+ extpa_res = platform_get_resource_byname(pdev,IORESOURCE_MEM,"audio-extpa");
+ if (extpa_res->start > 0) {
+ gpio_audio_extpa = extpa_res->start;
+ printk(KERN_INFO"get extpa resource succeed %d\n",gpio_audio_extpa);
+ ret = gpio_request(gpio_audio_extpa,"audio-extpa");
+ if (ret < 0) {
+ printk(KERN_ERR"rda codec : gpio_request fail. ");
+ goto err_request_gpio;
+ }
+ ret = gpio_direction_output(gpio_audio_extpa,1);
+ if (ret < 0) {
+ printk(KERN_ERR"rda codec : gpio_direction_output fail.");
+ goto err_request_gpio;
+ }
+ } else
+ printk(KERN_INFO"FAILED TO GET EXTPA RESOURCE !");
+
+ extpa1_res = platform_get_resource_byname(pdev,IORESOURCE_MEM,"audio-extpa-1");
+ if (extpa1_res->start > 0) {
+ gpio_audio_extpa_1 = extpa1_res->start;
+ printk(KERN_INFO"get extpa_1 resource succeed %d\n",gpio_audio_extpa_1);
+ ret = gpio_request(gpio_audio_extpa_1,"audio-extpa-1");
+ if (ret < 0) {
+ printk(KERN_ERR"rda codec : gpio_request 1 fail. ");
+ goto err_request_gpio1;
+ }
+ ret = gpio_direction_output(gpio_audio_extpa_1,1);
+ if (ret < 0) {
+ printk(KERN_ERR"rda codec : gpio_direction_output 1 fail.");
+ goto err_request_gpio1;
+ }
+ } else
+ printk(KERN_INFO"FAILED TO GET EXTPA_1 RESOURCE !");
+
+ return ret;
+err_request_gpio:
+ gpio_free(gpio_audio_extpa);
+err_request_gpio1:
+ gpio_free(gpio_audio_extpa);
+ gpio_free(gpio_audio_extpa_1);
+
+ return ret;
+}
+
+static int __exit rda_codec_platform_remove(struct platform_device *pdev)
+{
+ struct rda_codec_data *codec_data = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ rda_msys_unregister_device(codec_data->codec_msys);
+ rda_msys_free_device(codec_data->codec_msys);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver rda_codec_driver = {
+ .driver = {
+ .name = "rda-codec",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = rda_codec_platform_probe,
+ .remove = __exit_p(rda_codec_platform_remove),
+};
+
+static int __init rda_codec_modinit(void)
+{
+ return platform_driver_register(&rda_codec_driver);
+}
+
+static void __exit rda_codec_modexit(void)
+{
+ platform_driver_unregister(&rda_codec_driver);
+}
+
+static void __exit rdafpag_pcm_modexit(void)
+{
+ platform_driver_unregister(&rda_codec_driver);
+}
+
+module_init(rda_codec_modinit);
+module_exit(rda_codec_modexit);
+
+MODULE_DESCRIPTION("ASoC RDA codec driver");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rda/rda_codec.h b/sound/soc/rda/rda_codec.h
new file mode 100644
index 000000000000..f1288e44585d
--- /dev/null
+++ b/sound/soc/rda/rda_codec.h
@@ -0,0 +1,138 @@
+/*
+ * ALSA SoC RDA 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 _RDA_CODEC_H
+#define _RDA_CODEC_H
+
+///////////////////////// abb spi /////////////////////////
+///////////////////////////////////////////////////////////
+
+// =============================================================================
+// BOOT_ISPI_DELAY_T
+// -----------------------------------------------------------------------------
+/// Delays
+/// Used to define the configuration delays
+// =============================================================================
+typedef enum {
+ /// Delay of 0 half-period
+ BOOT_ISPI_HALF_CLK_PERIOD_0,
+ /// Delay of 1 half-period
+ BOOT_ISPI_HALF_CLK_PERIOD_1,
+ /// Delay of 2 half-period
+ BOOT_ISPI_HALF_CLK_PERIOD_2,
+ /// Delay of 3 half-period
+ BOOT_ISPI_HALF_CLK_PERIOD_3,
+
+ BOOT_ISPI_HALF_CLK_PERIOD_QTY
+} BOOT_ISPI_DELAY_T;
+
+// =============================================================================
+// BOOT_ISPI_CFG_T
+// -----------------------------------------------------------------------------
+/// Structure for configuration.
+/// A configuration structure allows to open or change the SPI with the desired
+/// parameters.
+// =============================================================================
+typedef struct {
+ /// Polarity of the FM CS.
+ u8 csFmActiveLow;
+
+ /// Polarity of the ABB CS.
+ u8 csAbbActiveLow;
+
+ /// Polarity of the PMU CS.
+ u8 csPmuActiveLow;
+
+ /// If the first edge after the CS activation is a falling edge, set to
+ /// \c 1.\n Otherwise, set to \c 0.
+ u8 clkFallEdge;
+
+ /// The delay between the CS activation and the first clock edge,
+ /// can be 0 to 2 half clocks.
+ BOOT_ISPI_DELAY_T clkDelay;
+
+ /// The delay between the CS activation and the output of the data,
+ /// can be 0 to 2 half clocks.
+ BOOT_ISPI_DELAY_T doDelay;
+
+ /// The delay between the CS activation and the sampling of the input data,
+ /// can be 0 to 3 half clocks.
+ BOOT_ISPI_DELAY_T diDelay;
+
+ /// The delay between the end of transfer and the CS deactivation, can be
+ /// 0 to 3 half clocks.
+ BOOT_ISPI_DELAY_T csDelay;
+
+ /// The time when the CS must remain deactivated before a new transfer,
+ /// can be 0 to 3 half clocks.
+ BOOT_ISPI_DELAY_T csPulse;
+
+ /// Frame size in bits
+ u32 frameSize;
+
+ /// OE ratio - Value from 0 to 31 is the number of data out to transfert
+ /// before the SPI_DO pin switches to input.
+ /// Not needed in the chip, but needed for the FPGA
+ u8 oeRatio;
+
+ /// SPI maximum clock frequency: the SPI clock will be the highest
+ /// possible value inferior to this parameter.
+ u32 spiFreq;
+
+} BOOT_ISPI_CFG_T;
+
+// =============================================================================
+// BOOT_ISPI_CS_T
+// -----------------------------------------------------------------------------
+/// Chip Select
+/// Used to select a Chip Select
+// =============================================================================
+typedef enum {
+ /// Chip Select for the PMU analog module.
+ BOOT_ISPI_CS_PMU = 0,
+ /// Chip Select for the ABB analog module.
+ BOOT_ISPI_CS_ABB,
+ /// Chip Select for the FM analog module.
+ BOOT_ISPI_CS_FM,
+
+ BOOT_ISPI_CS_QTY
+} BOOT_ISPI_CS_T;
+
+// ==============================================================================
+// Codec registers
+// ==============================================================================
+
+// reg 0x12d - s_cnt_constant
+#define RDA_CODEC_REG_SAMPLE_RATE 0x12D
+#define RDA_CODEC_REG_SAMPLE_RATE_SHIFT (6)
+#define RDA_CODEC_REG_SAMPLE_RATE_MASK (0x1f<<(RDA_CODEC_REG_SAMPLE_RATE_SHIFT))
+
+#define RDA_CODEC_REG_DIG_MUTE 0x12A
+#define RDA_CODEC_REG_DIG_MUTE_SHIFT (7)
+#define RDA_CODEC_REG_DIG_MUTE_MASK (0x1<<(RDA_CODEC_REG_DIG_EN_SHIFT))
+
+#define RDA_CODEC_REG_DIG_EN 0x12A
+#define RDA_CODEC_REG_DIG_EN_SHIFT (2)
+#define RDA_CODEC_REG_DIG_EN_MASK (0x1<<(RDA_CODEC_REG_DIG_EN_SHIFT))
+
+#define RDA_CODEC_REG_SELECT_SPK_PA 0x42
+#define RDA_CODEC_REG_SELECT_SPK_PA_SHIFT (2)
+#define RDA_CODEC_REG_SELECT_SPK_PA_MASK (0x1<<(RDA_CODEC_REG_SELECT_SPK_PA_SHIFT))
+
+#define RDA_CODEC_REG_SELECT_HEAD_PA 0x42
+#define RDA_CODEC_REG_SELECT_HEAD_PA_SHIFT (1)
+#define RDA_CODEC_REG_SELECT_HEAD_PA_MASK (0x1<<(RDA_CODEC_REG_SELECT_HEAD_PA_SHIFT))
+
+#define RDA_CODEC_REG_SELECT_RECV_PA 0x42
+#define RDA_CODEC_REG_SELECT_RECV_PA_SHIFT (1)
+#define RDA_CODEC_REG_SELECT_RECV_PA_MASK (0x1<<(RDA_CODEC_REG_SELECT_RECV_PA_SHIFT))
+
+#endif /* _RDA_CODEC_H */
diff --git a/sound/soc/rda/rda_codec_adp.h b/sound/soc/rda/rda_codec_adp.h
new file mode 100644
index 000000000000..abec1fc3f7f6
--- /dev/null
+++ b/sound/soc/rda/rda_codec_adp.h
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+/*
+ * All these are same with modem definitions
+ * and, from definitions of headers in modem's source codes.
+ *
+*/
+
+
+#ifndef RDA8810_MODEM_CODEC_ADP_H
+#define RDA8810_MODEM_CODEC_ADP_H
+// ============================================================================
+// SND_SPK_LEVEL_T
+// -----------------------------------------------------------------------------
+/// This type describes the possible level value for the speaker volume.
+// =============================================================================
+typedef enum
+{
+ SND_SPK_MUTE = 0x00000000,
+ SND_SPK_VOL_1 = 0x00000001,
+ SND_SPK_VOL_2 = 0x00000002,
+ SND_SPK_VOL_3 = 0x00000003,
+ SND_SPK_VOL_4 = 0x00000004,
+ SND_SPK_VOL_5 = 0x00000005,
+ SND_SPK_VOL_6 = 0x00000006,
+ SND_SPK_VOL_7 = 0x00000007,
+ SND_SPK_VOL_QTY = 0x00000008
+} SND_SPK_LEVEL_T;
+
+
+// ============================================================================
+// SND_MIC_LEVEL_T
+// -----------------------------------------------------------------------------
+/// This type describes the possible level value for the mic: mute or enabled.
+// =============================================================================
+typedef enum
+{
+ SND_MIC_MUTE = 0x00000000,
+ SND_MIC_ENABLE = 0x00000001,
+ SND_MIC_VOL_QTY = 0x00000002
+} SND_MIC_LEVEL_T;
+
+
+// ============================================================================
+// SND_SIDE_LEVEL_T
+// -----------------------------------------------------------------------------
+/// This type describes the possible level value for the side tone volume. The value
+/// SND_SIDE_VOL_TEST is used for mic to spk simple loop back test.
+// =============================================================================
+typedef enum
+{
+ SND_SIDE_MUTE = 0x00000000,
+ SND_SIDE_VOL_1 = 0x00000001,
+ SND_SIDE_VOL_2 = 0x00000002,
+ SND_SIDE_VOL_3 = 0x00000003,
+ SND_SIDE_VOL_4 = 0x00000004,
+ SND_SIDE_VOL_5 = 0x00000005,
+ SND_SIDE_VOL_6 = 0x00000006,
+ SND_SIDE_VOL_7 = 0x00000007,
+ SND_SIDE_VOL_TEST = 0x00000008,
+ SND_SIDE_VOL_QTY = 0x00000009
+} SND_SIDE_LEVEL_T;
+
+
+// ============================================================================
+// SND_TONE_ATTENUATION_T
+// -----------------------------------------------------------------------------
+/// Attenuation of the tone. The attenuation can be set to 0 dB, -3 dB, -9 dB and
+/// -15dB.
+// =============================================================================
+typedef enum
+{
+/// No attenuation
+ SND_TONE_0DB = 0x00000000,
+/// -3dB
+ SND_TONE_M3DB = 0x00000001,
+/// -9db
+ SND_TONE_M9DB = 0x00000002,
+/// -15dB
+ SND_TONE_M15DB = 0x00000003,
+ SND_TONE_VOL_QTY = 0x00000004
+} SND_TONE_ATTENUATION_T;
+
+
+// ============================================================================
+// SND_TONE_TYPE_T
+// -----------------------------------------------------------------------------
+/// Tone types. The DTMF Tones are used to inform the user that the number is being
+/// composed. All the standard DTMF are available: 0 to 9, A to D, pound and star.
+/// \n The Comfort Tones are used to inform the user on the current state of the
+/// call: Ringing, Busy, Unavailable... All frequencies needed to do the standard
+/// Comfort Tones are available: 425 Hz, 950 Hz, 1400 Hz and 1800 Hz. \n
+// =============================================================================
+typedef enum
+{
+/// Tone when the '0' key
+ SND_DTMF_0 = 0x00000000,
+/// Tone when the '1' key
+ SND_DTMF_1 = 0x00000001,
+/// Tone when the '2' key
+ SND_DTMF_2 = 0x00000002,
+/// Tone when the '3' key
+ SND_DTMF_3 = 0x00000003,
+/// Tone when the '4' key
+ SND_DTMF_4 = 0x00000004,
+/// Tone when the '5' key
+ SND_DTMF_5 = 0x00000005,
+/// Tone when the '6' key
+ SND_DTMF_6 = 0x00000006,
+/// Tone when the '7' key
+ SND_DTMF_7 = 0x00000007,
+/// Tone when the '8' key
+ SND_DTMF_8 = 0x00000008,
+/// Tone when the '9' key
+ SND_DTMF_9 = 0x00000009,
+ SND_DTMF_A = 0x0000000A,
+ SND_DTMF_B = 0x0000000B,
+ SND_DTMF_C = 0x0000000C,
+ SND_DTMF_D = 0x0000000D,
+/// Tone when the * key
+ SND_DTMF_S = 0x0000000E,
+/// Tone when the # key
+ SND_DTMF_P = 0x0000000F,
+/// Comfort tone at 425 Hz
+ SND_COMFORT_425 = 0x00000010,
+/// Comfort tone at 950 Hz
+ SND_COMFORT_950 = 0x00000011,
+/// Comfort tone at 1400 Hz
+ SND_COMFORT_1400 = 0x00000012,
+/// Confort tone at 1800 Hz
+ SND_COMFORT_1800 = 0x00000013,
+/// No tone is emitted
+ SND_NO_TONE = 0x00000014
+} SND_TONE_TYPE_T;
+
+
+// ============================================================================
+// SND_ITF_T
+// -----------------------------------------------------------------------------
+/// That type provide a way to identify the different audio interface.
+// =============================================================================
+typedef enum
+{
+ SND_ITF_RECEIVER = 0x00000000,
+ SND_ITF_EAR_PIECE = 0x00000001,
+ SND_ITF_LOUD_SPEAKER = 0x00000002,
+ SND_ITF_BLUETOOTH = 0x00000003,
+ SND_ITF_FM = 0x00000004,
+ SND_ITF_TV = 0x00000005,
+/// Number (max) of available interface to the SND driver
+ SND_ITF_QTY = 0x00000006,
+ SND_ITF_NONE = 0x000000FF
+} SND_ITF_T;
+
+
+// ============================================================================
+// SND_EQUALIZER_MODE_T
+// -----------------------------------------------------------------------------
+/// SND equalizer modes enumerator
+// =============================================================================
+typedef enum
+{
+ SND_EQUALIZER_OFF = 0xFFFFFFFF,
+ SND_EQUALIZER_NORMAL = 0x00000000,
+ SND_EQUALIZER_BASS = 0x00000001,
+ SND_EQUALIZER_DANCE = 0x00000002,
+ SND_EQUALIZER_CLASSICAL = 0x00000003,
+ SND_EQUALIZER_TREBLE = 0x00000004,
+ SND_EQUALIZER_PARTY = 0x00000005,
+ SND_EQUALIZER_POP = 0x00000006,
+ SND_EQUALIZER_ROCK = 0x00000007,
+ SND_EQUALIZER_CUSTOM = 0x00000008,
+ SND_EQUALIZER_QTY = 0x00000009
+} SND_EQUALIZER_MODE_T;
+
+//////////////////////////////////////////////////////////////////////////////////
+// =============================================================================
+// HAL_AIF_HANDLER_T
+// -----------------------------------------------------------------------------
+/// Type use to define the user handling function called when an interrupt
+/// related to the IFC occurs. Interrupt can be generated when playing or
+/// recording a buffer, when we reach the middle or the end of the buffer.
+// =============================================================================
+// typedef VOID (*HAL_AIF_HANDLER_T)(VOID);
+typedef void (*HAL_AIF_HANDLER_T)(void);
+
+// =============================================================================
+// HAL_AIF_SR_T
+// -----------------------------------------------------------------------------
+/// Audio stream sample rate.
+// =============================================================================
+typedef enum
+{
+ HAL_AIF_FREQ_8000HZ = 8000,
+ HAL_AIF_FREQ_11025HZ = 11025,
+ HAL_AIF_FREQ_12000HZ = 12000,
+ HAL_AIF_FREQ_16000HZ = 16000,
+ HAL_AIF_FREQ_22050HZ = 22050,
+ HAL_AIF_FREQ_24000HZ = 24000,
+ HAL_AIF_FREQ_32000HZ = 32000,
+ HAL_AIF_FREQ_44100HZ = 44100,
+ HAL_AIF_FREQ_48000HZ = 48000
+} HAL_AIF_FREQ_T;
+
+
+
+// =============================================================================
+// HAL_AIF_CH_NB_T
+// -----------------------------------------------------------------------------
+/// This type defines the number of channels used by a stream
+// ============================================================================
+typedef enum
+{
+ HAL_AIF_MONO = 1,
+ HAL_AIF_STEREO = 2,
+} HAL_AIF_CH_NB_T;
+
+// =============================================================================
+// HAL_AIF_STREAM_T
+// -----------------------------------------------------------------------------
+/// This type describes a stream, played or record.
+// =============================================================================
+typedef struct
+{
+ // UINT32* startAddress;
+ unsigned int* startAddress;
+ /// Stream length in bytes.
+ /// The length of a stream must be a multiple of 16 bytes.
+ /// The maximum size is 32 KB.
+ // UINT16 length;
+ unsigned short length;
+ HAL_AIF_FREQ_T sampleRate;
+ HAL_AIF_CH_NB_T channelNb;
+ /// True if this is a voice stream, coded on 13 bits, mono, at 8kHz.
+ /// Voice quality streams are the only that can be output through
+ /// the receiver interface.
+ // BOOL voiceQuality;
+ unsigned char voiceQuality;
+ /// True if the play stream is started along with the record stream.
+ /// In this case, the play stream will be configured at first,
+ /// but it is not started until the record stream is ready to start.
+ // BOOL playSyncWithRecord;
+ unsigned char playSyncWithRecord;
+ /// Handler called when the middle of the buffer is reached.
+ /// If this field is left blank (NULL), no interrupt will be
+ /// generated.
+ HAL_AIF_HANDLER_T halfHandler;
+ /// Handler called when the end of the buffer is reached.
+ /// If this field is left blank (NULL), no interrupt will be
+ /// generated.
+ HAL_AIF_HANDLER_T endHandler;
+} HAL_AIF_STREAM_T;
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+// =============================================================================
+// AUD_SPK_T
+// -----------------------------------------------------------------------------
+/// Speaker output selection.
+// =============================================================================
+typedef enum
+{
+ /// Output on receiver
+ /// This output can only use voice quality streams (Mono, 8kHz,
+ /// voiceQuality field set to TRUE).
+ AUD_SPK_RECEIVER = 0,
+ /// Output on ear-piece
+ AUD_SPK_EAR_PIECE,
+ /// Output on hand-free loud speaker
+ AUD_SPK_LOUD_SPEAKER,
+ /// Output on both hand-free loud speaker and ear-piece
+ AUD_SPK_LOUD_SPEAKER_EAR_PIECE,
+
+ AUD_SPK_QTY,
+
+ AUD_SPK_DISABLE=255
+} AUD_SPK_T;
+
+
+// =============================================================================
+// AUD_MIC_T
+// -----------------------------------------------------------------------------
+/// Microphone input selection.
+// =============================================================================
+typedef enum
+{
+ /// Input from the regular microphone port
+ AUD_MIC_RECEIVER = 0,
+ /// Input from the ear-piece port,
+ AUD_MIC_EAR_PIECE,
+ /// Input from regular microphone, but for loudspeaker mode.
+ AUD_MIC_LOUD_SPEAKER,
+
+ AUD_MIC_QTY,
+
+ AUD_MIC_DISABLE = 255
+} AUD_MIC_T;
+
+
+// =============================================================================
+// AUD_SPEAKER_TYPE_T
+// -----------------------------------------------------------------------------
+/// Describes how the speaker is plugged on the stereo output:
+/// - is it a stereo speaker ? (speakers ?)
+/// - is it a mono speaker on the left channel ?
+/// - is it a mono speaker on the right channel ?
+/// - is this a mono output ?
+// =============================================================================
+typedef enum
+{
+ AUD_SPEAKER_STEREO,
+ AUD_SPEAKER_MONO_RIGHT,
+ AUD_SPEAKER_MONO_LEFT,
+ /// The output is mono only.
+ AUD_SPEAKER_STEREO_NA,
+
+ AUD_SPEAKER_QTY
+} AUD_SPEAKER_TYPE_T;
+
+
+// =============================================================================
+// AUD_LEVEL_T
+// -----------------------------------------------------------------------------
+/// Level configuration structure.
+///
+/// A level configuration structure allows to start an AUD operation (start
+/// stream, start record, or start tone) with the desired gains on an interface.
+// =============================================================================
+typedef struct
+{
+ /// Speaker level,
+ SND_SPK_LEVEL_T spkLevel;
+
+ /// Microphone level: muted or enabled
+ SND_MIC_LEVEL_T micLevel;
+
+ /// Sidetone
+ SND_SIDE_LEVEL_T sideLevel;
+
+ SND_TONE_ATTENUATION_T toneLevel;
+
+} AUD_LEVEL_T;
+
+// =============================================================================
+// AUD_TEST_T
+// -----------------------------------------------------------------------------
+/// Used to choose the audio mode: normal, test, dai's ...
+// =============================================================================
+typedef enum
+{
+ /// No test mode.
+ AUD_TEST_NO,
+
+ /// For audio test loop; analog to DAI loop + DAI to analog loop.
+ AUD_TEST_LOOP_ACOUSTIC,
+
+ /// For audio test loop; radio loop on DAI interface
+ AUD_TEST_LOOP_RF_DAI,
+
+ /// For audio test loop; radio loop on analog interface
+ AUD_TEST_LOOP_RF_ANALOG,
+
+ /// For board check: mic input is fedback to the speaker output.
+ AUD_TEST_SIDE_TEST,
+
+ /// For board check; audio loop in VOC
+ AUD_TEST_LOOP_VOC,
+
+ /// For board check; regular mic input and earpiece output
+ AUD_TEST_RECVMIC_IN_EARPIECE_OUT,
+
+ /// headset mic in & headset out
+ AUD_TEST_HEADSETMIC_IN_HEADSET_OUT,
+
+ /// main mic in & receiver out
+ AUD_TEST_MAINMIC_IN_RECEIVER_OUT,
+
+ AUD_TEST_MODE_QTY
+} AUD_TEST_T;
+
+typedef enum
+{
+ AUD_APP_CODEC = 0,
+ AUD_APP_FM,
+ AUD_APP_ATV,
+
+ AUD_APP_QTY,
+} AUD_APP_MODE_T;
+
+/* Mixer control names */
+#define MIXER_PLAYBACK_VOLUME "Playback Volume"
+#define MIXER_CAPTURE_VOLUME "Capture Volume"
+#define MIXER_ITF "ITF"
+#define MIXER_SPK_SEL "SpkSel"
+#define MIXER_FORCE_MAINMIC "ForceMainMic"
+#define MIXER_CODEC_APP_MODE "CodecAppMode"
+#define MIXER_START_PLAY "StartPlay"
+#define MIXER_START_RECORD "StartRecord"
+#define MIXER_STOP "Stop"
+#define MIXER_OUT_SAMPLE_RATE "OutSampleRate"
+#define MIXER_IN_SAMPLE_RATE "InSampleRate"
+#define MIXER_OUT_CHANNEL_NB "OutChannelNumber"
+#define MIXER_IN_CHANNEL_NB "InChannelNumber"
+#define MIXER_COMMIT_SETUP "Commit Setup"
+#define MIXER_CODEC_OPEN_STATUS "CodecOpenStatus"
+#define MIXER_MUTE_MIC "MuteMic"
+#define MIXER_LOOP_MODE "Loop Mode"
+
+#endif /* RDA8810_MODEM_CODEC_ADP_H */
diff --git a/sound/soc/rda/rda_dai.c b/sound/soc/rda/rda_dai.c
new file mode 100644
index 000000000000..e98f5d9aac36
--- /dev/null
+++ b/sound/soc/rda/rda_dai.c
@@ -0,0 +1,471 @@
+/*
+ * 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_aif.h"
+
+//#define DEBUG 1
+
+#define AIF_FIX_SAMPLERATE_WHEN_CAPTURE 44100
+
+#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;
+
+static int rda_cpu_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rda_dai *aif_cfg = snd_soc_dai_get_drvdata(dai);
+ int ret = 0;
+
+ mutex_lock(&aif_cfg->mutex);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!aif_cfg->PlayActive) {
+ aif_cfg->PlayActive = true;
+ } else {
+ ret = -EBUSY;
+ }
+ } else {
+ if (!aif_cfg->RecordActive) {
+ aif_cfg->RecordActive = true;
+ } else {
+ ret = -EBUSY;
+ }
+ }
+
+ mutex_unlock(&aif_cfg->mutex);
+
+ return ret;
+}
+
+static void rda_cpu_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rda_dai *aif_cfg = snd_soc_dai_get_drvdata(dai);
+
+
+ mutex_lock(&aif_cfg->mutex);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ aif_cfg->PlayActive = false;
+ } else {
+ aif_cfg->RecordActive = false;
+ }
+
+ if (!aif_cfg->PlayActive && !aif_cfg->RecordActive) {
+ hwp_apAif->ctrl = 0;
+ hwp_apAif->serial_ctrl = AIF_MASTER_MODE_MASTER;
+ hwp_apAif->side_tone = 0;
+ hwp_apAif->Cfg_Aif_Tx_Stb = 0;
+ aif_cfg->OpenStatus = false;
+ }
+ mutex_unlock(&aif_cfg->mutex);
+}
+
+static int rda_cpu_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rda_dai *aif_cfg = snd_soc_dai_get_drvdata(dai);
+
+ u32 channel_num = 0;
+ u32 lrck = 0;
+ u32 bck = 0;
+ u32 bck_lrck_ratio = 0;
+ u32 sample_rate = 0;
+ u32 aif_source_clock = 0;
+
+#ifdef DEBUG
+ printk(KERN_INFO"[%s] \n", __func__);
+#endif
+
+ if (!aif_cfg->OpenStatus) {
+
+ sample_rate = params_rate(params);
+
+#ifdef DEBUG
+ printk(KERN_INFO"sample_rate : [%d] \n", sample_rate);
+#endif
+ // on rda : aif just config the output params
+ // when input sample_rate is 8000, we should still config aif to playback sample_rate
+ // like android-fixed 44100
+#ifdef AIF_FIX_SAMPLERATE_WHEN_CAPTURE
+ if(substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ printk(KERN_INFO"fix capture sample rate : [%d] \n", AIF_FIX_SAMPLERATE_WHEN_CAPTURE);
+ sample_rate = AIF_FIX_SAMPLERATE_WHEN_CAPTURE;
+ }
+#endif
+
+ lrck = sample_rate;
+
+ switch (sample_rate) {
+ case 8000:
+ bck_lrck_ratio = 50;
+ break;
+
+ case 11025:
+ bck_lrck_ratio = 36;
+ break;
+
+ case 12000:
+ bck_lrck_ratio = 38;
+ break;
+
+ case 16000:
+ bck_lrck_ratio = 50;
+ break;
+
+ case 22050:
+ bck_lrck_ratio = 40;
+ break;
+
+ case 24000:
+ bck_lrck_ratio = 38;
+ break;
+
+ case 32000:
+ bck_lrck_ratio = 56;
+ break;
+
+ case 44100:
+ bck_lrck_ratio = 62;
+ break;
+
+ case 48000:
+ bck_lrck_ratio = 36;
+ break;
+
+ default:
+ mutex_unlock(&aif_cfg->mutex);
+ return -EINVAL;
+ break;
+ }
+
+ bck = lrck * bck_lrck_ratio;
+ aif_cfg->AudioBck_div = FAST_CLOCK / bck - 2;
+ aif_cfg->bcklrck_div = bck_lrck_ratio / 2 - 16;
+
+ channel_num = params_channels(params);
+
+ switch (channel_num) {
+ case 1:
+ case 2:
+ aif_cfg->ChannelNb = channel_num;
+ break;
+ default:
+ mutex_unlock(&aif_cfg->mutex);
+ return -EINVAL;
+ }
+
+ if (sample_rate == 8000 || sample_rate == 12000
+ || sample_rate == 16000 || sample_rate == 24000
+ || sample_rate == 32000 || sample_rate == 48000) {
+ aif_source_clock = 24576000;
+ } else if (sample_rate == 11025 || sample_rate == 22050
+ || sample_rate == 44100) {
+ aif_source_clock = 22579200;
+ } else {
+ aif_source_clock = 24576000;
+ }
+
+ aif_cfg->TxStb_div = aif_source_clock / sample_rate - 2;
+ aif_cfg->MasterFlag = true;
+ }
+
+ return 0;
+}
+
+static int rda_cpu_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rda_dai *aif_cfg = snd_soc_dai_get_drvdata(dai);
+
+ u32 serialCfgReg = 0;
+
+ if (!aif_cfg->OpenStatus) {
+
+ hwp_apAif->Cfg_Aif_Tx_Stb =
+ AIF_AIF_TX_STB_EN | AIF_AIF_TX_STB_DIV(aif_cfg->TxStb_div);
+
+ // config - communicate with codec NOT I2S
+ aif_cfg->ControlReg |=
+ AIF_PARALLEL_OUT_SET_PARA | AIF_PARALLEL_IN_SET_PARA |
+ AIF_LOOP_BACK_NORMAL | AIF_TX_STB_MODE;
+
+ // FIXME
+ // we assume :
+ // 1. playback always use channel number 2
+ // 2. this config of aif (not use i2s) just affect playback not capture
+ // when this is called because record, we should know this configure will just used by playback
+ // when capture config channel number 1, we should also configure this to channel number 2,
+ // because playback will happen when capture
+
+ // BUG : playback and capture interface maybe called 1 2 11 2 2 11025
+ // so, aif_cfg->ChannelNb (capture set) maybe used by playback and STREO_DUPLT will be set and sound will be slower.
+ // TODO : different ops when different stream, now we just set all to stereo cause android use 44100 & 2 channels
+ // if (aif_cfg->ChannelNb == 1 && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ // serialCfgReg |=
+ // AIF_MASTER_MODE_MASTER |
+ // AIF_TX_MODE_MONO_STEREO_DUPLI;
+ // } else {
+ serialCfgReg |=
+ AIF_MASTER_MODE_MASTER | AIF_TX_MODE_STEREO_STEREO;
+ //}
+
+ hwp_apAif->serial_ctrl = serialCfgReg;
+
+ aif_cfg->OpenStatus = true;
+ }
+
+ return 0;
+}
+
+static int rda_cpu_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct rda_dai *aif_cfg = snd_soc_dai_get_drvdata(dai);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ hwp_apAif->ctrl =
+ (aif_cfg->ControlReg | AIF_ENABLE_H_ENABLE) &
+ ~AIF_TX_OFF;
+ aif_cfg->PlayStatus = true;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (!aif_cfg->RecordStatus) {
+ hwp_apAif->ctrl = 0;
+ }
+ aif_cfg->PlayStatus = false;
+ break;
+ default:
+ break;
+ }
+
+ } else {
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (!aif_cfg->PlayStatus) {
+ hwp_apAif->ctrl =
+ (aif_cfg->ControlReg | AIF_ENABLE_H_ENABLE)
+ | AIF_TX_OFF_TX_OFF;
+ }
+ aif_cfg->RecordStatus = true;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (!aif_cfg->PlayStatus) {
+ hwp_apAif->ctrl = 0;
+ }
+ aif_cfg->RecordStatus = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rda_cpu_dai_driver_ops = {
+ .startup = rda_cpu_dai_startup,
+ .shutdown = rda_cpu_dai_shutdown,
+ .hw_params = rda_cpu_dai_hw_params,
+ .prepare = rda_cpu_dai_prepare,
+ .trigger = rda_cpu_dai_trigger,
+};
+
+static int rda_cpu_dai_driver_probe(struct snd_soc_dai *dai)
+{
+ struct rda_dai *dmic = snd_soc_dai_get_drvdata(dai);
+
+ 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);
+
+ return 0;
+}
+
+static int rda_cpu_dai_driver_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_cpu_dai_driver = {
+ .name = "rda-cpu-dai-driver",
+ .probe = rda_cpu_dai_driver_probe,
+ .remove = rda_cpu_dai_driver_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_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .sig_bits = 16,
+ },
+ .ops = &rda_cpu_dai_driver_ops,
+};
+
+static struct snd_soc_component_driver rda_component ={
+ .name = "rda-soundcard"
+};
+static int rda_cpu_dai_platform_driver_probe(struct
+ platform_device
+ *pdev)
+{
+ struct rda_dai *AifCfg;
+ struct resource *res;
+ int ret;
+
+ AifCfg =
+ devm_kzalloc(&pdev->dev, sizeof(struct rda_dai), GFP_KERNEL);
+ if (!AifCfg)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, AifCfg);
+ AifCfg->dev = &pdev->dev;
+
+ mutex_init(&AifCfg->mutex);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(AifCfg->dev, "invalid dma 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_component(&pdev->dev,&rda_component, &rda_cpu_dai_driver, 1);
+ if (ret)
+ goto err_put_clk;
+
+ return 0;
+
+err_put_clk:
+ return ret;
+}
+
+static int __exit rda_cpu_dai_platform_driver_remove(struct
+ platform_device
+ *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver rda_cpu_dai_platform_driver = {
+ .driver = {
+ .name = "rda-aif",
+ .owner = THIS_MODULE,
+ },
+ .probe = rda_cpu_dai_platform_driver_probe,
+ .remove = __exit_p(rda_cpu_dai_platform_driver_remove),
+};
+
+static int __init rda_cpu_dai_modinit(void)
+{
+ return platform_driver_register(&rda_cpu_dai_platform_driver);
+}
+
+static void __exit rda_cpu_dai_modexit(void)
+{
+ platform_driver_unregister(&rda_cpu_dai_platform_driver);
+}
+
+module_init(rda_cpu_dai_modinit);
+module_exit(rda_cpu_dai_modexit);
+
+MODULE_DESCRIPTION("ALSA SoC for RDA CPU DAI");
+MODULE_AUTHOR("Xu Mingliang <mingliangxu@rdamicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rda/rda_dsp_aud.c b/sound/soc/rda/rda_dsp_aud.c
new file mode 100644
index 000000000000..e3fcf3b602cd
--- /dev/null
+++ b/sound/soc/rda/rda_dsp_aud.c
@@ -0,0 +1,334 @@
+/*
+ * =====================================================================================
+ *
+ * Filename: rda_dsp_aud.c
+ *
+ * Description: RDA DSP_AUD Receiver driver for linux.
+ *
+ * Version: 1.0
+ * Created: 06/12/2013 04:19:05 PM
+ * Revision: none
+ * Compiler: gcc
+ *
+ * Author: Naiquan Hu,
+ * Organization: RDA Microelectronics Inc.
+ *
+ * Copyright (C) 2013 RDA Microelectronics Inc.
+ * =====================================================================================
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h> // udelay()
+#include <linux/device.h> // device_create()
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+//#include <linux/version.h> /* constant of kernel version */
+#include <asm/uaccess.h> // get_user()
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/audiocontrol.h>
+#include <linux/gpio.h>
+#include <plat/md_sys.h>
+#include <linux/leds.h>
+
+#include "rda_dsp_aud.h"
+#include <rda/tgt_ap_board_config.h>
+
+struct msys_device *bp_gpioc_msys = NULL;
+
+/* rda_gpioc_data driver data */
+struct rda_gpioc_data {
+ struct msys_device *gpioc_msys;
+};
+
+typedef struct
+{
+ u8 id;
+ u8 value;
+ u8 default_value1;
+ u8 default_value2;
+} rda_gpioc_op;
+
+#ifdef _TGT_AP_LED_RED_FLASH
+#define LED_CAM_FLASH "red-flash"
+#elif defined(_TGT_AP_LED_GREEN_FLASH)
+#define LED_CAM_FLASH "green-flash"
+#elif defined(_TGT_AP_LED_BLUE_FLASH)
+#define LED_CAM_FLASH "blue-flash"
+#endif
+
+#ifdef LED_CAM_FLASH
+DEFINE_LED_TRIGGER(rda_sensor_led);
+#endif
+
+static int rda_modem_gpioc_notify(struct notifier_block *nb, unsigned long mesg, void *data)
+{
+ struct msys_device *pmsys_dev = container_of(nb, struct msys_device, notifier);
+ struct client_mesg *pmesg = (struct client_mesg *)data;
+
+
+ if (pmesg->mod_id != SYS_GEN_MOD) {
+ return NOTIFY_DONE;
+ }
+
+ if (mesg != SYS_GEN_MESG_RTC_TRIGGER) {
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+int rda_gpioc_operation(rda_gpioc_op *gpioc_op)
+{
+ int enable;
+ int ret, value;
+ u8 data[sizeof(rda_gpioc_op)] = { 0 };
+ struct client_cmd gpioc_cmd;
+
+ value = sizeof(rda_gpioc_op);
+
+ memcpy(data, gpioc_op, sizeof(rda_gpioc_op));
+ memset(&gpioc_cmd, 0, sizeof(gpioc_cmd));
+ gpioc_cmd.pmsys_dev = bp_gpioc_msys;
+ gpioc_cmd.mod_id = SYS_GPIO_MOD;
+ gpioc_cmd.mesg_id = SYS_GPIO_CMD_OPERATION;
+ gpioc_cmd.pdata = (void *)&data;
+ gpioc_cmd.data_size = sizeof(data);
+ ret = rda_msys_send_cmd(&gpioc_cmd);
+
+ printk( ">>>> [%s], ret [%d] \n", __func__, ret);
+
+ return ret;
+}
+
+static ssize_t rdabp_gpio_open_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+
+ printk(KERN_DEBUG "%s, buf: %s\n", __func__, buf);
+
+ if (sscanf(buf, "%u", &value) != 1)
+ return -EINVAL;
+
+ printk("The SYSTEM value %d\n", value);
+
+ return count;
+}
+
+/*
+ * GPO set.
+ */
+static ssize_t OrangePi_2G_IOT_gpio_set_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int gpionum;
+ rda_gpioc_op gpioc_op;
+
+ if (sscanf(buf, "%u", &gpionum) != 1)
+ return -EINVAL;
+
+ gpioc_op.id = gpionum;
+ gpioc_op.value = 1;
+ gpioc_op.default_value1 = 0;
+ gpioc_op.default_value2 = 0;
+ rda_gpioc_operation(&gpioc_op);
+
+ return 1;
+}
+
+/*
+ * GPO clear.
+ */
+static ssize_t OrangePi_2G_IOT_gpio_clear_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int gpionum;
+ rda_gpioc_op gpioc_op;
+
+ if (sscanf(buf, "%u", &gpionum) != 1)
+ return -EINVAL;
+
+ gpioc_op.id = gpionum;
+ gpioc_op.value = 0;
+ gpioc_op.default_value1 = 0;
+ gpioc_op.default_value2 = 0;
+ rda_gpioc_operation(&gpioc_op);
+
+ return 1;
+}
+static ssize_t rdabp_gpio_close_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value;
+
+ printk(KERN_DEBUG "%s, buf: %s\n", __func__, buf);
+
+ if (sscanf(buf, "%u", &value) != 1)
+ return -EINVAL;
+
+ return count;
+}
+
+
+static ssize_t rdabp_gpio_set_io_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value;
+ rda_gpioc_op gpioc_op;
+
+ if (sscanf(buf, "%u", &value) != 1)
+ return -EINVAL;
+ gpioc_op.id = 8;
+ gpioc_op.value = 1;
+ gpioc_op.default_value1 = 0;
+ gpioc_op.default_value2 = 0;
+ rda_gpioc_operation(&gpioc_op);
+
+ return 1;
+}
+
+
+static ssize_t rdabp_gpio_get_value_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value;
+
+ if (sscanf(buf, "%u", &value) != 1)
+ return -EINVAL;
+
+ return count;
+}
+
+
+static ssize_t rdabp_gpio_set_value_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value;
+
+ if (sscanf(buf, "%u", &value) != 1)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t rdabp_gpio_enable_irq_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value;
+
+ printk(KERN_DEBUG "%s, buf: %s\n", __func__, buf);
+
+ if (sscanf(buf, "%u", &value) != 1)
+ return -EINVAL;
+
+
+ return count;
+}
+
+static DEVICE_ATTR(gpio_open, 0777,
+ NULL, rdabp_gpio_open_store);
+static DEVICE_ATTR(gpio_close,0777,
+ NULL, rdabp_gpio_close_store);
+static DEVICE_ATTR(gpio_set_io, 0777,
+ NULL,rdabp_gpio_set_io_store);
+static DEVICE_ATTR(gpio_get_value, 0777,
+ NULL,rdabp_gpio_get_value_store);
+static DEVICE_ATTR(gpio_set_value, 0777,
+ NULL,rdabp_gpio_set_value_store);
+static DEVICE_ATTR(gpio_enable_irq, 0777,
+ NULL,rdabp_gpio_enable_irq_store);
+static DEVICE_ATTR(gpo_set, 0777,
+ NULL, OrangePi_2G_IOT_gpio_set_store);
+static DEVICE_ATTR(gpo_clear, 0777,
+ NULL, OrangePi_2G_IOT_gpio_clear_store);
+
+
+static int rda_gpioc_platform_probe(struct platform_device *pdev)
+{
+ struct rda_gpioc_data *gpioc_data = NULL;
+ int ret = 0;
+
+ gpioc_data = devm_kzalloc(&pdev->dev, sizeof(struct rda_gpioc_data), GFP_KERNEL);
+
+ if (gpioc_data == NULL) {
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, gpioc_data);
+
+ // ap <---> modem gpioc
+ gpioc_data->gpioc_msys = rda_msys_alloc_device();
+ if (!gpioc_data->gpioc_msys) {
+ ret = -ENOMEM;
+ }
+
+ gpioc_data->gpioc_msys->module = SYS_GPIO_MOD;
+ gpioc_data->gpioc_msys->name = "rda-gpioc";
+ gpioc_data->gpioc_msys->notifier.notifier_call = rda_modem_gpioc_notify;
+
+ rda_msys_register_device(gpioc_data->gpioc_msys);
+ bp_gpioc_msys = gpioc_data->gpioc_msys;
+
+ device_create_file(&pdev->dev, &dev_attr_gpio_open);
+ device_create_file(&pdev->dev, &dev_attr_gpio_close);
+ device_create_file(&pdev->dev, &dev_attr_gpio_set_io);
+ device_create_file(&pdev->dev, &dev_attr_gpio_get_value);
+ device_create_file(&pdev->dev, &dev_attr_gpio_set_value);
+ device_create_file(&pdev->dev, &dev_attr_gpio_enable_irq);
+ device_create_file(&pdev->dev, &dev_attr_gpo_set);
+ device_create_file(&pdev->dev, &dev_attr_gpo_clear);
+
+ #ifdef LED_CAM_FLASH
+ led_trigger_register_simple(LED_CAM_FLASH, &rda_sensor_led);
+ mdelay(5);
+ led_trigger_event(rda_sensor_led, LED_HALF);
+ printk(" rda_gpioc_platform_probe22222222222222 \r\n ");
+ #endif
+
+ return ret;
+}
+
+static int __exit rda_gpioc_platform_remove(struct platform_device *pdev)
+{
+ struct rda_gpioc_data *gpioc_data = platform_get_drvdata(pdev);
+
+ rda_msys_unregister_device(gpioc_data->gpioc_msys);
+ rda_msys_free_device(gpioc_data->gpioc_msys);
+
+ platform_set_drvdata(pdev, NULL);
+
+ #ifdef LED_CAM_FLASH
+ led_trigger_unregister_simple(rda_sensor_led);
+ #endif
+
+ return 0;
+}
+
+static struct platform_driver rda_gpioc_driver = {
+ .driver = {
+ .name = "rda-gpioc",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = rda_gpioc_platform_probe,
+ .remove = __exit_p(rda_gpioc_platform_remove),
+};
+
+static int __init rda_gpioc_modinit(void)
+{
+ return platform_driver_register(&rda_gpioc_driver);
+}
+
+static void __exit rda_gpioc_modexit(void)
+{
+ platform_driver_unregister(&rda_gpioc_driver);
+}
+
+module_init(rda_gpioc_modinit);
+module_exit(rda_gpioc_modexit);
+
+
diff --git a/sound/soc/rda/rda_dsp_aud.h b/sound/soc/rda/rda_dsp_aud.h
new file mode 100644
index 000000000000..2054cfe31039
--- /dev/null
+++ b/sound/soc/rda/rda_dsp_aud.h
@@ -0,0 +1,44 @@
+
+#ifndef __RDA_DSP_AUD_RADIO_H__
+#define __RDA_DSP_AUD_RADIO_H__
+
+
+#include <linux/ioctl.h>
+#include <linux/time.h>
+
+//scan sort algorithm
+enum{
+ DSP_AUD_SCAN_SORT_NON = 0,
+ DSP_AUD_SCAN_SORT_UP,
+ DSP_AUD_SCAN_SORT_DOWN,
+ DSP_AUD_SCAN_SORT_MAX
+};
+
+//scan methods
+enum{
+ DSP_AUD_SCAN_SEL_HW = 0, //select hardware scan, advantage: fast
+ DSP_AUD_SCAN_SEL_SW, //select software scan, advantage: more accurate
+ DSP_AUD_SCAN_SEL_MAX
+};
+
+
+
+#define DSP_AUD_NAME "rdadspAud"
+#define DSP_AUD_DEVICE_NAME "/dev/rdadspAud"
+
+// ********** ***********DSP_AUD IOCTL define start *******************************
+
+#define DSP_AUD_IOC_MAGIC 0xf9 // FIXME: any conflict?
+
+
+//IOCTL and struct for test
+#define GP0_1_LEVEL _IOWR(DSP_AUD_IOC_MAGIC, 11, uint32_t*)
+
+struct dsp_aud_em_parm {
+ uint16_t group_idx;
+ uint16_t item_idx;
+ uint32_t item_value;
+};
+
+
+#endif // __RDA_DSP_AUD_RADIO_H__
diff --git a/sound/soc/rda/rda_pcm.c b/sound/soc/rda/rda_pcm.c
new file mode 100644
index 000000000000..314d00132658
--- /dev/null
+++ b/sound/soc/rda/rda_pcm.c
@@ -0,0 +1,378 @@
+/*
+ * rda_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 <plat/pm_ddr.h>
+#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 <plat/rda_debug.h>
+#include "rda_audifc.h"
+#include "rda_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_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_RESUME ,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = 32,
+ .period_bytes_max = 32 * 1024,//64 * 1024,
+ .periods_min = 4,
+ .periods_max = 4,
+ .buffer_bytes_max = 128 * 1024,//256 * 1024,
+};
+
+struct rda_runtime_data {
+ spinlock_t lock;
+ struct rda_pcm_dma_data *dma_data;
+ int dma_ch;
+};
+
+static void rda_pcm_dma_irq(int ch, int stat, void *data)
+{
+ struct snd_pcm_substream *substream = data;
+
+ 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 rda_runtime_data *prtd = runtime->private_data;
+ int err = 0;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ 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;
+
+ 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_audifc_chan_params 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
+ */
+
+ 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;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (dma_data->set_threshold)
+ dma_data->set_threshold(substream);
+
+ rda_dbg_audio("[start aud ifc] : substream->stream is [%d], %s, cmd is [%d] \n",
+ substream->stream, substream->stream == SNDRV_PCM_STREAM_PLAYBACK?"PLAYBACK":"CAPTURE", cmd);
+ if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pm_ddr_get(PM_DDR_AUDIO_IFC_PLAYBACK);
+ else
+ pm_ddr_get(PM_DDR_AUDIO_IFC_CAPTURE);
+ rda_start_audifc(prtd->dma_ch);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ rda_dbg_audio("[stop aud ifc] : substream->stream is [%d], %s, cmd is [%d] \n",
+ substream->stream, substream->stream == SNDRV_PCM_STREAM_PLAYBACK?"PLAYBACK":"CAPTURE", cmd);
+ rda_stop_audifc(prtd->dma_ch);
+ if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pm_ddr_put(PM_DDR_AUDIO_IFC_PLAYBACK);
+ else
+ pm_ddr_put(PM_DDR_AUDIO_IFC_CAPTURE);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ 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;
+
+ 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;
+ 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) {
+ printk(KERN_INFO "[%s], snd_pcm_hw_constraint_integer < 0\n", __func__);
+ goto out;
+ }
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (prtd == NULL) {
+ printk(KERN_INFO "[%s], kzalloc == NULL\n", __func__);
+ 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;
+
+ 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;
+
+ 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);
+
+ if (!buf->area) {
+ printk(KERN_INFO "rda audio : %s, alloc dma buffer fail. \n", __func__);
+ 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;
+
+ 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;
+
+ 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 int rda_pcm_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev, &rda_soc_platform);
+}
+
+static int __exit rda_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver rda_pcm_driver = {
+ .driver = {
+ .name = "rda-pcm",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = rda_pcm_probe,
+ .remove = __exit_p(rda_pcm_remove),
+};
+
+static int __init rda_pcm_modinit(void)
+{
+ return platform_driver_register(&rda_pcm_driver);
+}
+
+static void __exit rda_pcm_modexit(void)
+{
+ platform_driver_unregister(&rda_pcm_driver);
+}
+
+module_init(rda_pcm_modinit);
+module_exit(rda_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/rda/rda_pcm.h b/sound/soc/rda/rda_pcm.h
new file mode 100644
index 000000000000..1d46ee10f79e
--- /dev/null
+++ b/sound/soc/rda/rda_pcm.h
@@ -0,0 +1,40 @@
+/*
+ * 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/rda/rda_soundcard.c b/sound/soc/rda/rda_soundcard.c
new file mode 100644
index 000000000000..1ce270d55870
--- /dev/null
+++ b/sound/soc/rda/rda_soundcard.c
@@ -0,0 +1,150 @@
+/*
+ * 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_soundcard_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 ">>>> %s \n", __func__);
+
+ /* 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_soundcard_dai_link_ops = {
+ .hw_params = rda_soundcard_hw_params,
+};
+
+static const struct snd_soc_dapm_route rda_soundcard_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_soundcard_dai_link[] = {
+ // audio stream
+ {
+ .name = "rda-soundcard-dai-link-aud", // machine dai link name
+ .stream_name = "rdaaud-stream", // stream name
+
+ // cpu(soc)(platform) name - manage "data" and "audio ifc"
+ .platform_name = "rda-pcm",
+ // cpu dai - manage aif
+ // .cpu_dai_name = "rdaaud-platform-cpu-dai",
+ .cpu_dai_name = "rda-aif",
+
+ // codec name
+ .codec_name = "rda-codec",
+ // codec dai name - in fact, there is no codec dai in rda
+ .codec_dai_name = "rda-codec-dai",
+
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &rda_soundcard_dai_link_ops,
+ },
+ // voice stream
+ {
+ .name = "rda-soundcard-dai-link-voice", // machine dai link name
+ .stream_name = "rdavoice-stream", // stream name
+
+ // cpu(soc)(platform) name - comm with modem
+ .platform_name = "rda-voice-pcm",
+ // cpu dai - fake
+ .cpu_dai_name = "rda-voice-cpu-dai",
+
+ // codec name - fake
+ .codec_name = "rda-voice-codec",
+ // codec dai name - fake
+ .codec_dai_name = "rda-voice-codec-dai",
+
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &rda_soundcard_dai_link_ops,
+ },
+};
+
+/* Audio machine driver */
+static struct snd_soc_card rda_soundcard = {
+ .name = "rda-soundcard", // sound card name
+ .owner = THIS_MODULE,
+ .dai_link = &rda_soundcard_dai_link[0],
+ .num_links = ARRAY_SIZE(rda_soundcard_dai_link),
+
+ .dapm_widgets = NULL,
+ .num_dapm_widgets = 0,
+ .dapm_routes = NULL,
+ .num_dapm_routes = 0,
+};
+
+static struct platform_device *rda_snd_device;
+
+static int __init rda_soundcard_modinit(void)
+{
+ int err = 0;
+
+ rda_snd_device = platform_device_alloc("soc-audio", -1);
+
+ if (!rda_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(rda_snd_device, &rda_soundcard);
+
+ err = platform_device_add(rda_snd_device);
+ if (err)
+ platform_device_put(rda_snd_device);
+
+ return err;
+}
+
+static void __exit rda_soundcard_modexit(void)
+{
+ platform_device_unregister(rda_snd_device);
+}
+
+module_init(rda_soundcard_modinit);
+module_exit(rda_soundcard_modexit);
+
+MODULE_DESCRIPTION("ALSA SoC for RDA SoundCard");
+MODULE_AUTHOR("Xu Mingliang <mingliangxu@rdamicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rda/rda_voice_codec.c b/sound/soc/rda/rda_voice_codec.c
new file mode 100644
index 000000000000..8ab8be16058f
--- /dev/null
+++ b/sound/soc/rda/rda_voice_codec.c
@@ -0,0 +1,211 @@
+/*
+ * ALSA SoC RDA 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:
+ * rda codec
+ *
+ * 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 <mach/iomap.h>
+#include <asm/io.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <plat/reg_spi.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 <plat/rda_debug.h>
+#include <plat/md_sys.h>
+
+static int rda_voice_codec_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static int rda_voice_codec_dai_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static int rda_voice_codec_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static int rda_voice_codec_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static void rda_voice_codec_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+}
+
+static int rda_voice_codec_dai_dig_mute(struct snd_soc_dai *dai, int mute)
+{
+ return 0;
+}
+
+static int rda_voice_codec_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ return 0;
+}
+
+static int rda_voice_codec_dai_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ return 0;
+}
+
+static int rda_voice_codec_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ return 0;
+}
+
+#define RDA_CODEC_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 rda_voice_codec_dai_ops = {
+ .startup = rda_voice_codec_dai_startup,
+ .hw_params = rda_voice_codec_dai_hw_params,
+ .prepare = rda_voice_codec_dai_pcm_prepare,
+ .trigger = rda_voice_codec_dai_trigger,
+ .shutdown = rda_voice_codec_dai_shutdown,
+ .digital_mute = rda_voice_codec_dai_dig_mute,
+ .set_fmt = rda_voice_codec_dai_set_fmt,
+ .set_sysclk = rda_voice_codec_dai_set_sysclk,
+};
+
+static struct snd_soc_dai_driver rda_voice_codec_dai_driver = {
+ .name = "rda-voice-codec-dai",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = RDA_CODEC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = RDA_CODEC_FORMATS,
+ },
+ .ops = &rda_voice_codec_dai_ops,
+};
+
+static int rda_voice_codec_suspend(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static int rda_voice_codec_resume(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static int rda_voice_codec_probe(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static int rda_voice_codec_remove(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_rda_voice_codec_driver = {
+ .reg_cache_size = 0,
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = NULL,
+ .probe = rda_voice_codec_probe,
+ .remove = rda_voice_codec_remove,
+ .suspend = rda_voice_codec_suspend,
+ .resume = rda_voice_codec_resume,
+ .set_bias_level = rda_voice_codec_set_bias_level,
+ .dapm_widgets = NULL,
+ .num_dapm_widgets = 0,
+ .dapm_routes = NULL,
+ .num_dapm_routes = 0,
+};
+
+static int rda_voice_codec_platform_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ ret = snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_rda_voice_codec_driver,
+ &rda_voice_codec_dai_driver, 1);
+ return 0;
+}
+
+static int __exit rda_voice_codec_platform_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver rda_voice_codec_driver = {
+ .driver = {
+ .name = "rda-voice-codec",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = rda_voice_codec_platform_probe,
+ .remove = __exit_p(rda_voice_codec_platform_remove),
+};
+
+static struct platform_device rda_voice_codec = {
+ .name = "rda-voice-codec",
+ .id = -1,
+};
+
+static int __init rda_voice_codec_modinit(void)
+{
+ platform_device_register(&rda_voice_codec);
+ return platform_driver_register(&rda_voice_codec_driver);
+}
+
+static void __exit rda_voice_codec_modexit(void)
+{
+ platform_driver_unregister(&rda_voice_codec_driver);
+}
+
+static void __exit rdafpag_pcm_modexit(void)
+{
+ platform_driver_unregister(&rda_voice_codec_driver);
+}
+
+module_init(rda_voice_codec_modinit);
+module_exit(rda_voice_codec_modexit);
+
+MODULE_DESCRIPTION("ASoC RDA codec driver");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rda/rda_voice_dai.c b/sound/soc/rda/rda_voice_dai.c
new file mode 100644
index 000000000000..710d3d08588a
--- /dev/null
+++ b/sound/soc/rda/rda_voice_dai.c
@@ -0,0 +1,161 @@
+/*
+ * 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>
+
+static int rda_voice_cpu_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static void rda_voice_cpu_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+}
+
+static int rda_voice_cpu_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static int rda_voice_cpu_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static int rda_voice_cpu_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rda_voice_cpu_dai_driver_ops = {
+ .startup = rda_voice_cpu_dai_startup,
+ .shutdown = rda_voice_cpu_dai_shutdown,
+ .hw_params = rda_voice_cpu_dai_hw_params,
+ .prepare = rda_voice_cpu_dai_prepare,
+ .trigger = rda_voice_cpu_dai_trigger,
+};
+
+static int rda_voice_cpu_dai_driver_probe(struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static int rda_voice_cpu_dai_driver_remove(struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static struct snd_soc_dai_driver rda_voice_cpu_dai_driver = {
+ .name = "rda-voice-cpu-dai-driver",
+ .probe = rda_voice_cpu_dai_driver_probe,
+ .remove = rda_voice_cpu_dai_driver_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_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .sig_bits = 16,
+ },
+ .ops = &rda_voice_cpu_dai_driver_ops,
+};
+
+static struct snd_soc_component_driver rda_voice_component = {
+ .name = "rda_voice_component"
+};
+
+static int rda_voice_cpu_dai_platform_driver_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ ret = snd_soc_register_component(&pdev->dev, &rda_voice_component, &rda_voice_cpu_dai_driver, 1);
+ return 0;
+}
+
+static int __exit rda_voice_cpu_dai_platform_driver_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver rda_voice_cpu_dai_platform_driver = {
+ .driver = {
+ .name = "rda-voice-cpu-dai",
+ .owner = THIS_MODULE,
+ },
+ .probe = rda_voice_cpu_dai_platform_driver_probe,
+ .remove = __exit_p(rda_voice_cpu_dai_platform_driver_remove),
+};
+
+static struct platform_device rda_voice_cpu_dai = {
+ .name = "rda-voice-cpu-dai",
+ .id = -1,
+};
+
+static int __init rda_voice_cpu_dai_modinit(void)
+{
+ platform_device_register(&rda_voice_cpu_dai);
+ return platform_driver_register(&rda_voice_cpu_dai_platform_driver);
+}
+
+static void __exit rda_voice_cpu_dai_modexit(void)
+{
+ platform_driver_unregister(&rda_voice_cpu_dai_platform_driver);
+}
+
+module_init(rda_voice_cpu_dai_modinit);
+module_exit(rda_voice_cpu_dai_modexit);
+
+MODULE_DESCRIPTION("ALSA SoC for RDA CPU DAI");
+MODULE_AUTHOR("Xu Mingliang <mingliangxu@rdamicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rda/rda_voice_pcm.c b/sound/soc/rda/rda_voice_pcm.c
new file mode 100644
index 000000000000..15801123137b
--- /dev/null
+++ b/sound/soc/rda/rda_voice_pcm.c
@@ -0,0 +1,588 @@
+/*
+ * rda_voice_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 <plat/md_sys.h>
+#include <plat/pm_ddr.h>
+#include "rda_voice_pcm.h"
+
+// FIXME, same as modem definition (vois_m.h)
+enum {
+ VOIS_STATUS_MID_BUFFER_REACHED,
+ VOIS_STATUS_END_BUFFER_REACHED,
+ VOIS_STATUS_NO_MORE_DATA,
+ VOIS_STATUS_ERR,
+
+ VOIS_STATUS_QTY,
+};
+// FIXME, same as modem definition (Syscmds_audio.c)
+#define PCM_VOICE_PERIOD_COUNT 4
+#define PCM_SHARE_BUFFER_SIZE (640*10)
+#define PCM_VOICE_PERIOD_SIZE (PCM_SHARE_BUFFER_SIZE/2)
+#define PCM_VOICE_PERIOD_TIME_IN_MS (PCM_VOICE_PERIOD_SIZE/2/8000)
+
+#define ADDR_MD2AP(addr) \
+ ((addr&0x0FFFFFFF) | 0x10000000)
+
+static u8 g_current_period = 0;
+static void __iomem * g_modem_share_pcm_buf = NULL;
+static struct rda_voice_pcm_dma_data voice_dma_data;
+static struct msys_device *voice_msys = NULL;
+static struct timer_list period_timer;
+
+static const struct snd_pcm_hardware rda_voice_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_RESUME ,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = PCM_VOICE_PERIOD_SIZE,
+ .period_bytes_max = PCM_VOICE_PERIOD_SIZE,
+ .periods_min = PCM_VOICE_PERIOD_COUNT,
+ .periods_max = PCM_VOICE_PERIOD_COUNT,
+ .buffer_bytes_max = 256 * 1024,
+};
+
+struct rda_runtime_data {
+ spinlock_t lock;
+ struct rda_voice_pcm_dma_data *dma_data;
+ int dma_ch;
+};
+
+static struct work_struct start_work;
+static struct work_struct stop_work;
+static struct workqueue_struct *record_wq;
+static struct mutex record_mutex;
+
+static void period_timer_func(unsigned long from_timer)
+{
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_dma_buffer *buf = NULL;
+ if(voice_msys)
+ substream = voice_msys->private;
+ else {
+ printk(KERN_ERR"%s : voice_msys is null, not process. \n", __func__);
+ return ;
+ }
+
+ if(substream) {
+ buf = &substream->dma_buffer;
+ }
+ else {
+ printk(KERN_ERR"%s: substream is null, not process. \n", __func__);
+ return ;
+ }
+
+ memset(buf->area + g_current_period*PCM_VOICE_PERIOD_SIZE, 0, PCM_VOICE_PERIOD_SIZE);
+ snd_pcm_period_elapsed(substream);
+ ++g_current_period;
+ if(g_current_period >= PCM_VOICE_PERIOD_COUNT)
+ g_current_period = 0;
+ printk(KERN_INFO"mod_timer %d\n",g_current_period);
+ mod_timer(&period_timer, jiffies+msecs_to_jiffies(PCM_VOICE_PERIOD_TIME_IN_MS));
+}
+
+static int vois_RecordStart(u32 *buffer_address)
+{
+ int ret = 0;
+ struct client_cmd cmd;
+
+ printk(KERN_INFO ">>>> [%s]\n", __func__);
+
+ *buffer_address = 0;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.pmsys_dev = voice_msys;
+ cmd.mod_id = SYS_AUDIO_MOD;
+ cmd.mesg_id = SYS_AUDIO_CMD_AUD_VOICE_RECORD_START;
+ cmd.pout_data = buffer_address;
+ cmd.out_size = sizeof(*buffer_address);
+ ret = rda_msys_send_cmd(&cmd);
+ printk(KERN_INFO ">>>> [%s], ret [%d] addr [0x%x]\n",
+ __func__, ret, *buffer_address);
+
+ return ret;
+}
+
+static int vois_RecordStop(void)
+{
+ int ret = 0;
+ struct client_cmd cmd;
+
+ printk(KERN_INFO ">>>> [%s]\n", __func__);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.pmsys_dev = voice_msys;
+ cmd.mod_id = SYS_AUDIO_MOD;
+ cmd.mesg_id = SYS_AUDIO_CMD_AUD_VOICE_RECORD_STOP;
+ cmd.pdata = NULL;
+ cmd.data_size = 0;
+ ret = rda_msys_send_cmd(&cmd);
+ printk(KERN_INFO ">>>> [%s], ret [%d] \n", __func__, ret);
+
+ return ret;
+}
+
+/* this may get called several times by oss emulation */
+static int rda_voice_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rda_runtime_data *prtd = runtime->private_data;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ prtd->dma_data = &voice_dma_data;
+
+ return 0;
+}
+
+static int rda_voice_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;
+
+ prtd->dma_data = NULL;
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+
+ return 0;
+}
+
+static int rda_voice_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static void rda_record_start_work(struct work_struct *work)
+{
+ int ret = 0;
+ u32 ret_addr = 0;
+
+ mutex_lock(&record_mutex);
+ ret = vois_RecordStart(&ret_addr);
+ if (ret) {
+ printk(KERN_INFO "rda voice : failed to start voice record. \n");
+ }
+ g_modem_share_pcm_buf = ioremap(ADDR_MD2AP(ret_addr), PCM_SHARE_BUFFER_SIZE);
+ if(g_modem_share_pcm_buf <= 0) {
+ printk(KERN_INFO "rda voice : remap shared buffer fail. \n");
+ }
+ mutex_unlock(&record_mutex);
+}
+
+static void rda_record_stop_work(struct work_struct *work)
+{
+ unsigned long flags;
+
+ mutex_lock(&record_mutex);
+ vois_RecordStop();
+ if(g_modem_share_pcm_buf)
+ iounmap(g_modem_share_pcm_buf);
+ mutex_unlock(&record_mutex);
+ mod_timer(&period_timer, 0);
+ local_irq_save(flags);
+ g_modem_share_pcm_buf = 0;
+ g_current_period = 0;
+ local_irq_restore(flags);
+
+}
+
+static int rda_voice_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ //u32 ret_addr = 0;
+ int ret = 0;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ {
+ printk(KERN_INFO "rda voice : [start aud pcm] : substream->stream is [%d], cmd is [%d] \n",
+ substream->stream, cmd);
+ if(voice_msys) {
+ voice_msys->private = (void *)substream;
+ // remap it every time in case of modem "malloc" the mem
+ /*ret = vois_RecordStart(&ret_addr);
+ if (ret) {
+ printk(KERN_INFO "rda voice : failed to start voice record. \n");
+ ret = -EINVAL;
+ }
+ g_modem_share_pcm_buf = ioremap(ADDR_MD2AP(ret_addr), PCM_SHARE_BUFFER_SIZE);
+ if(g_modem_share_pcm_buf <= 0) {
+ printk(KERN_INFO "rda voice : remap shared buffer fail. \n");
+ ret = -EINVAL;
+ }*/
+ //queue start work here to avoid dead lock, since md_sys command will sleep
+ queue_work(record_wq, &start_work);
+ }
+ else
+ printk(KERN_INFO "rda voice : [start aud pcm] : BUG : no voice_msys here! \n");
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ {
+ printk(KERN_INFO "rda8810 voice : [stop aud pcm] : substream->stream is [%d], cmd is [%d] \n",
+ substream->stream, cmd);
+ if(voice_msys) {
+ /*vois_RecordStop();
+ if(g_modem_share_pcm_buf)
+ iounmap(g_modem_share_pcm_buf);
+ mod_timer(&period_timer, 0);
+ local_irq_disable();
+ g_modem_share_pcm_buf = 0;
+ voice_msys->private = (void *)NULL;
+ g_current_period = 0;
+ local_irq_enable();*/
+ //queue stop work here same to start
+ queue_work(record_wq,&stop_work);
+ voice_msys->private = (void *)NULL;
+ }
+ else
+ printk(KERN_INFO "rda voice : [stop aud pcm] : BUG : no voice_msys here! \n");
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t rda_voice_pcm_pointer(struct snd_pcm_substream
+ *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t offset;
+ offset = bytes_to_frames(runtime, g_current_period*PCM_VOICE_PERIOD_SIZE);
+
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+
+ return offset;
+}
+
+static int rda_voice_pcm_open(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct rda_runtime_data *prtd;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_soc_set_runtime_hwparams(substream, &rda_voice_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) {
+ printk(KERN_ERR"rda_voice_pcm_open : snd_pcm_hw_constraint_integer < 0\n");
+ goto out;
+ }
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (prtd == NULL) {
+ printk(KERN_ERR"rda_voice_pcm_open : kzalloc == NULL\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ spin_lock_init(&prtd->lock);
+ runtime->private_data = prtd;
+ pm_ddr_get(PM_DDR_AUDIO_IFC_CAPTURE);
+ printk(KERN_INFO"rda_voice_pcm_open get pm ddr\n");
+out:
+ return ret;
+}
+
+static int rda_voice_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ kfree(runtime->private_data);
+ pm_ddr_put(PM_DDR_AUDIO_IFC_CAPTURE);
+ printk(KERN_INFO"rda_voice_pcm_close put pm ddr\n");
+ return 0;
+}
+
+static int rda_voice_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr, runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops rda_voice_pcm_ops = {
+ .open = rda_voice_pcm_open,
+ .close = rda_voice_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = rda_voice_pcm_hw_params,
+ .hw_free = rda_voice_pcm_hw_free,
+ .prepare = rda_voice_pcm_prepare,
+ .trigger = rda_voice_pcm_trigger,
+ .pointer = rda_voice_pcm_pointer,
+ .mmap = rda_voice_pcm_mmap,
+};
+
+
+
+static u64 rda_voice_pcm_dmamask = DMA_BIT_MASK(32);
+
+static int rda_voice_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_voice_pcm_hardware.buffer_bytes_max;
+
+ 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);
+ if (!buf->area)
+ return -ENOMEM;
+ buf->bytes = size;
+ return 0;
+}
+
+static void rda_voice_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream = 0;
+ 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_voice_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;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &rda_voice_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_voice_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ret = rda_voice_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+
+out:
+ /* free preallocated buffers in case of error */
+ if (ret)
+ rda_voice_pcm_free_dma_buffers(pcm);
+
+ return ret;
+}
+
+static struct snd_soc_platform_driver rda_soc_platform = {
+ .ops = &rda_voice_pcm_ops,
+ .pcm_new = rda_voice_pcm_new,
+ .pcm_free = rda_voice_pcm_free_dma_buffers,
+};
+
+static int rda_modem_voice_notify(struct notifier_block *nb, unsigned long mesg, void *data)
+{
+ struct client_mesg *pmesg = (struct client_mesg *)data;
+ struct snd_pcm_runtime *runtime = NULL;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_dma_buffer *buf = NULL;
+ unsigned int off = 0;
+ int status = 0;
+
+ if (pmesg->mod_id != SYS_AUDIO_MOD) {
+ // printk(KERN_ERR"rda_modem_voice_notify : not audio mod mesg \n");
+ return NOTIFY_DONE;
+ }
+
+ if(voice_msys)
+ substream = voice_msys->private;
+ else {
+ // printk(KERN_ERR"rda_modem_voice_notify : BUG : voice_msys is null. \n");
+ return NOTIFY_OK;
+ }
+
+ if(substream) {
+ runtime = substream->runtime;
+ buf = &substream->dma_buffer;
+ }
+ else {
+ printk(KERN_ERR"rda_modem_voice_notify : BUG : substream is null. \n");
+ return NOTIFY_OK;
+ }
+
+ if(mesg == SYS_AUDIO_MESG_VOICE_HANDLER_CALLBACK) {
+ status = *((unsigned int*)&(pmesg->param));
+ // printk(KERN_ERR"rda_modem_voice_notify : mesg [0x%x] \n", status);
+ if(status == VOIS_STATUS_MID_BUFFER_REACHED || status == VOIS_STATUS_END_BUFFER_REACHED) {
+ if(status == VOIS_STATUS_MID_BUFFER_REACHED)
+ off = 0;
+ else
+ off = PCM_VOICE_PERIOD_SIZE;
+
+ // copy buffer, buf->dma_area is virtual address of audio dma buffer we alloc.
+ // a period one time ( ap has 4 periods and md has 2 period )
+ memcpy(buf->area + g_current_period*PCM_VOICE_PERIOD_SIZE,
+ g_modem_share_pcm_buf + off, PCM_VOICE_PERIOD_SIZE);
+ snd_pcm_period_elapsed(substream);
+
+ ++g_current_period;
+
+ if(g_current_period >= PCM_VOICE_PERIOD_COUNT)
+ g_current_period = 0;
+ }
+ else if (status == VOIS_STATUS_NO_MORE_DATA) {
+ // FIXME VOIS_STATUS_NO_MORE_DATA means end recording at modem side, we fill 0 for rest periods
+ // for up layer return from in_read cause it is blocked
+ // way 1
+ //for(i = 0; i < PCM_VOICE_PERIOD_COUNT*5; ++i) {
+ // memset(buf->area + g_current_period*PCM_VOICE_PERIOD_SIZE, 0, PCM_VOICE_PERIOD_SIZE);
+ // snd_pcm_period_elapsed(substream);
+ // ++g_current_period;
+ // if(g_current_period >= PCM_VOICE_PERIOD_COUNT)
+ // g_current_period = 0;
+ //}
+ // way 2
+ mod_timer(&period_timer, jiffies+msecs_to_jiffies(PCM_VOICE_PERIOD_TIME_IN_MS));
+ }
+
+ return NOTIFY_DONE;
+ }
+ else {
+ // printk(KERN_ERR"rda_modem_voice_notify : BUG : mesg != SYS_AUDIO_MESG_VOICE_HANDLER_CALLBACK. \n");
+ }
+
+ return NOTIFY_OK;
+}
+
+
+static int rda_voice_pcm_probe(struct platform_device *pdev)
+{
+ voice_msys = rda_msys_alloc_device();
+ if (!voice_msys) {
+ printk(KERN_ERR"rda_voice_pcm_probe : rda_msys_alloc_device fail. \n");
+ return -ENOMEM;
+ }
+
+ voice_msys->module = SYS_AUDIO_MOD;
+ voice_msys->name = "rda-voice-pcm";
+ voice_msys->notifier.notifier_call = rda_modem_voice_notify;
+ voice_msys->private = (void *)NULL;
+
+ rda_msys_register_device(voice_msys);
+
+ init_timer(&period_timer);
+ period_timer.expires = 0;
+ period_timer.function = period_timer_func;
+ period_timer.data = 0;
+ // add_timer(&period_timer);
+
+ INIT_WORK(&start_work,rda_record_start_work);
+ INIT_WORK(&stop_work,rda_record_stop_work);
+ record_wq = create_singlethread_workqueue("record_wq");
+ mutex_init(&record_mutex);
+
+ return snd_soc_register_platform(&pdev->dev, &rda_soc_platform);
+}
+
+static int __exit rda_voice_pcm_remove(struct platform_device *pdev)
+{
+ if(voice_msys) {
+ rda_msys_unregister_device(voice_msys);
+ rda_msys_free_device(voice_msys);
+ }
+
+ del_timer_sync(&period_timer);
+
+ snd_soc_unregister_platform(&pdev->dev);
+ destroy_workqueue(record_wq);
+ return 0;
+}
+
+static struct platform_driver rda_voice_pcm_driver = {
+ .driver = {
+ .name = "rda-voice-pcm",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = rda_voice_pcm_probe,
+ .remove = __exit_p(rda_voice_pcm_remove),
+};
+
+static struct platform_device rda_voice_pcm = {
+ .name = "rda-voice-pcm",
+ .id = -1,
+};
+
+static int __init rda_voice_pcm_modinit(void)
+{
+ platform_device_register(&rda_voice_pcm);
+ return platform_driver_register(&rda_voice_pcm_driver);
+}
+
+static void __exit rda_voice_pcm_modexit(void)
+{
+ platform_driver_unregister(&rda_voice_pcm_driver);
+}
+
+module_init(rda_voice_pcm_modinit);
+module_exit(rda_voice_pcm_modexit);
+
+MODULE_DESCRIPTION("ALSA SoC for RDA PCM");
+MODULE_AUTHOR("Xu Mingliang <mingliangxu@rdamicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rda/rda_voice_pcm.h b/sound/soc/rda/rda_voice_pcm.h
new file mode 100644
index 000000000000..b6c23eedaa63
--- /dev/null
+++ b/sound/soc/rda/rda_voice_pcm.h
@@ -0,0 +1,40 @@
+/*
+ * 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_VOICE_PCM_H__
+#define __RDA_VOICE_PCM_H__
+
+struct snd_pcm_substream;
+
+struct rda_voice_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