summaryrefslogtreecommitdiff
path: root/ANDROID_3.4.5/sound/soc
diff options
context:
space:
mode:
authorKevin2014-11-15 10:00:36 +0800
committerKevin2014-11-15 10:00:36 +0800
commit9d40ac5867b9aefe0722bc1f110b965ff294d30d (patch)
treede942df665fac4bac0d9cb7ae86910fe937b0c1a /ANDROID_3.4.5/sound/soc
parent392e8802486cb573b916e746010e141a75f507e6 (diff)
downloadFOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.tar.gz
FOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.tar.bz2
FOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.zip
add via modify part source code for wm8880 4.4 kitkat
Diffstat (limited to 'ANDROID_3.4.5/sound/soc')
-rw-r--r--ANDROID_3.4.5/sound/soc/Kconfig1
-rw-r--r--ANDROID_3.4.5/sound/soc/Makefile1
-rw-r--r--ANDROID_3.4.5/sound/soc/codecs/Kconfig16
-rw-r--r--ANDROID_3.4.5/sound/soc/codecs/Makefile12
-rwxr-xr-xANDROID_3.4.5/sound/soc/codecs/vt1603.c1249
-rwxr-xr-xANDROID_3.4.5/sound/soc/codecs/vt1603.h119
-rw-r--r--ANDROID_3.4.5/sound/soc/codecs/wm8994.c114
-rwxr-xr-xANDROID_3.4.5/sound/soc/codecs/wmt_fm34.c830
-rwxr-xr-xANDROID_3.4.5/sound/soc/codecs/wmt_hwdac.c195
-rwxr-xr-xANDROID_3.4.5/sound/soc/codecs/wmt_vt1602.c981
-rwxr-xr-xANDROID_3.4.5/sound/soc/codecs/wmt_vt1602.h67
-rw-r--r--ANDROID_3.4.5/sound/soc/soc-core.c251
-rw-r--r--ANDROID_3.4.5/sound/soc/soc-dapm.c12
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/Kconfig65
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/Makefile12
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c1105
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt-pcm-controller.c419
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt-pcm-controller.h122
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt-pcm-dma.c597
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt-pcm-dma.h35
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt-pcm.c767
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt-pcm.h46
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt-soc.c321
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt-soc.h53
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt_hwdep.c259
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt_hwdep.h55
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt_swmixer.c101
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt_swmixer.h33
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt_wm8994.c304
29 files changed, 8121 insertions, 21 deletions
diff --git a/ANDROID_3.4.5/sound/soc/Kconfig b/ANDROID_3.4.5/sound/soc/Kconfig
index 91c98559..5727227d 100644
--- a/ANDROID_3.4.5/sound/soc/Kconfig
+++ b/ANDROID_3.4.5/sound/soc/Kconfig
@@ -48,6 +48,7 @@ source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
+source "sound/soc/wmt/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
diff --git a/ANDROID_3.4.5/sound/soc/Makefile b/ANDROID_3.4.5/sound/soc/Makefile
index 2feaf376..4dca9714 100644
--- a/ANDROID_3.4.5/sound/soc/Makefile
+++ b/ANDROID_3.4.5/sound/soc/Makefile
@@ -5,6 +5,7 @@ snd-soc-dmaengine-pcm-objs := soc-dmaengine-pcm.o
obj-$(CONFIG_SND_SOC_DMAENGINE_PCM) += snd-soc-dmaengine-pcm.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
+obj-$(CONFIG_SND_SOC) += wmt/
obj-$(CONFIG_SND_SOC) += codecs/
obj-$(CONFIG_SND_SOC) += atmel/
obj-$(CONFIG_SND_SOC) += au1x/
diff --git a/ANDROID_3.4.5/sound/soc/codecs/Kconfig b/ANDROID_3.4.5/sound/soc/codecs/Kconfig
index 59d8efaa..7077ac69 100644
--- a/ANDROID_3.4.5/sound/soc/codecs/Kconfig
+++ b/ANDROID_3.4.5/sound/soc/codecs/Kconfig
@@ -335,6 +335,18 @@ config SND_SOC_WM8741
config SND_SOC_WM8750
tristate
+config SND_SOC_VT1602
+ tristate
+ depends on SND_SOC
+
+config SND_SOC_VT1603
+ tristate
+ depends on SND_SOC
+
+config SND_SOC_HWDAC
+ tristate
+ depends on SND_SOC
+
config SND_SOC_WM8753
tristate
@@ -437,3 +449,7 @@ config SND_SOC_MAX9877
config SND_SOC_TPA6130A2
tristate
+
+# echo cancellation
+config SND_SOC_WMT_FM34
+ tristate
diff --git a/ANDROID_3.4.5/sound/soc/codecs/Makefile b/ANDROID_3.4.5/sound/soc/codecs/Makefile
index 6662eb0c..83175a64 100644
--- a/ANDROID_3.4.5/sound/soc/codecs/Makefile
+++ b/ANDROID_3.4.5/sound/soc/codecs/Makefile
@@ -66,6 +66,9 @@ snd-soc-wm8731-objs := wm8731.o
snd-soc-wm8737-objs := wm8737.o
snd-soc-wm8741-objs := wm8741.o
snd-soc-wm8750-objs := wm8750.o
+snd-soc-vt1602-objs := wmt_vt1602.o
+snd-soc-vt1603-objs := vt1603.o
+snd-soc-hwdac-objs := wmt_hwdac.o
snd-soc-wm8753-objs := wm8753.o
snd-soc-wm8770-objs := wm8770.o
snd-soc-wm8776-objs := wm8776.o
@@ -102,6 +105,9 @@ snd-soc-wm-hubs-objs := wm_hubs.o
snd-soc-max9877-objs := max9877.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
+# echo cancellation
+snd-soc-wmt-fm34-objs := wmt_fm34.o
+
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
@@ -170,6 +176,9 @@ obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
obj-$(CONFIG_SND_SOC_WM8737) += snd-soc-wm8737.o
obj-$(CONFIG_SND_SOC_WM8741) += snd-soc-wm8741.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
+obj-$(CONFIG_SND_SOC_VT1602) += snd-soc-vt1602.o
+obj-$(CONFIG_SND_SOC_VT1603) += snd-soc-vt1603.o
+obj-$(CONFIG_SND_SOC_HWDAC) += snd-soc-hwdac.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o
obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
@@ -205,3 +214,6 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
+
+# echo cancellation
+obj-$(CONFIG_SND_SOC_WMT_FM34) += snd-soc-wmt-fm34.o
diff --git a/ANDROID_3.4.5/sound/soc/codecs/vt1603.c b/ANDROID_3.4.5/sound/soc/codecs/vt1603.c
new file mode 100755
index 00000000..eaa3d28e
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/codecs/vt1603.c
@@ -0,0 +1,1249 @@
+/*++
+ * linux/sound/soc/codecs/vt1603.c
+ * WonderMedia audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <linux/workqueue.h>
+#include <linux/switch.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+
+#include "vt1603.h"
+#include <linux/mfd/vt1603/core.h>
+
+#define VT1603_TIMER_INTERVAL 100 // ms
+
+static struct snd_soc_codec *gvt1603_codec = NULL; //
+
+struct vt1603_priv {
+ unsigned int sysclk;
+ struct switch_dev hp_switch; // state of headphone insert or not
+ struct work_struct work;
+ struct snd_soc_codec *codec;
+ struct timer_list timer; // timer for detecting headphone
+ struct proc_dir_entry *proc;
+ struct notifier_block reboot_notifier;
+};
+
+struct vt1603_boardinfo {
+ int hp_level; // 0: headset level low; 1: high
+ int ignore_hp_event;
+ int rec_src; // 0: AMIC-IN; 1: DMIC-IN
+
+ int hpd_rm; //add 2013-11-26
+ int hp_debounce; //add 2014-1-14
+ int timer_del; //2014-1-20 tvbox
+
+ int out_on; //2014-4-29 av out on for tvbox noisy. hw not good!
+};
+static struct vt1603_boardinfo vt1603_boardinfo;
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+static const u16 vt1603_regs[] = {
+ 0x0020, 0x00D7, 0x00D7, 0x0004, /* 0-3 */
+ 0x0000, 0x0040, 0x0000, 0x00c0, /* 4-7 */
+ 0x00c0, 0x0010, 0x0001, 0x0041, /* 8-11 */
+ 0x0000, 0x0000, 0x000b, 0x0093, /* 12-15 */
+ 0x0004, 0x0016, 0x0026, 0x0060, /* 16-19 */
+ 0x001a, 0x0000, 0x0002, 0x0000, /* 20-23 */
+ 0x0000, 0x000a, 0x0000, 0x0010, /* 24-27 */
+ 0x0000, 0x00e2, 0x0000, 0x0000, /* 28-31 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 32-35 */
+ 0x0000, 0x000c, 0x0054, 0x0000, /* 36-39 */
+ 0x0001, 0x000c, 0x000c, 0x000c, /* 40-43 */
+ 0x000c, 0x000c, 0x00c0, 0x00d5, /* 44-47 */
+ 0x00c5, 0x0012, 0x0085, 0x002b, /* 48-51 */
+ 0x00cd, 0x00cd, 0x008e, 0x008d, /* 52-55 */
+ 0x00e0, 0x00a6, 0x00a5, 0x0050, /* 56-59 */
+ 0x00e9, 0x00cf, 0x0040, 0x0000, /* 60-63 */
+ 0x0000, 0x0008, 0x0004, 0x0000, /* 64-67 */
+ 0x0040, 0x0000, 0x0040, 0x0000, /* 68-71 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 72-75 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 76-79 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 80-83 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 84-87 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 88-91 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 92-95 */
+ 0x00c4, 0x0079, 0x000c, 0x0024, /* 96-99 */
+ 0x0016, 0x0016, 0x0060, 0x0002, /* 100-103 */
+ 0x005b, 0x0003, 0x0039, 0x0039, /* 104-107 */
+ 0x00fe, 0x0012, 0x0034, 0x0000, /* 108-111 */
+ 0x0004, 0x00f0, 0x0000, 0x0000, /* 112-115 */
+ 0x0000, 0x0000, 0x0003, 0x0000, /* 116-119 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 120-123 */
+ 0x00f2, 0x0020, 0x0023, 0x006e, /* 124-127 */
+ 0x0019, 0x0040, 0x0000, 0x0004, /* 128-131 */
+ 0x000a, 0x0075, 0x0035, 0x00b0, /* 132-135 */
+ 0x0040, 0x0000, 0x0000, 0x0000, /* 136-139 */
+ 0x0088, 0x0013, 0x000c, 0x0003, /* 140-143 */
+ 0x0000, 0x0000, 0x0008, 0x0000, /* 144-147 */
+ 0x0003, 0x0020, 0x0030, 0x0018, /* 148-151 */
+ 0x001b,
+};
+
+/*----------------------------------------------------------------------*/
+
+
+/*---------------------Control Interface Functions----------------------*/
+
+static int vt1603_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ int ret = vt1603_reg_write(codec->control_data, reg, value);
+ if (ret == 0)
+ ((u16 *)codec->reg_cache)[reg] = value;
+ return ret;
+}
+
+static unsigned int vt1603_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ return ((u16 *)codec->reg_cache)[reg];
+}
+
+static int vt1603_hw_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ return vt1603_reg_write(codec->control_data, reg, value);
+}
+
+static unsigned int vt1603_hw_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 val;
+ int ret = vt1603_reg_read(codec->control_data, reg, &val);
+ if (ret == 0)
+ return val;
+ else
+ return ((u16 *)codec->reg_cache)[reg];
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*----------------------IRQ handle Functions----------------------------*/
+
+static void vt1603_set_switch_state(struct snd_soc_codec *codec)
+{
+ /* Just report hp state, don't switch hw device, app will handle it. */
+
+ struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = vt1603_hw_read(codec, VT1603_R21);
+
+ if (vt1603_boardinfo.hp_level) {
+ if (val & BIT1) {
+ val &= ~BIT1;
+ snd_soc_write(codec, VT1603_R21, val);
+ if (!vt1603_boardinfo.ignore_hp_event)
+ switch_set_state(&vt1603->hp_switch, 0);
+ pr_info("<<<vt1603: hp out!\n");
+ }
+ else {
+ val |= BIT1;
+ snd_soc_write(codec, VT1603_R21, val);
+ if (!vt1603_boardinfo.ignore_hp_event)
+ switch_set_state(&vt1603->hp_switch, 1);
+ pr_info("<<<vt1603: hp in!\n");
+ }
+ }
+ else {
+ if (val & BIT1) {
+ val &= ~BIT1;
+ snd_soc_write(codec, VT1603_R21, val);
+ if (!vt1603_boardinfo.ignore_hp_event)
+ switch_set_state(&vt1603->hp_switch, 1);
+ pr_info("<<<vt1603: hp in!\n");
+ }
+ else {
+ val |= BIT1;
+ snd_soc_write(codec, VT1603_R21, val);
+ if (!vt1603_boardinfo.ignore_hp_event)
+ switch_set_state(&vt1603->hp_switch, 0);
+ pr_info("<<<vt1603: hp out!\n");
+ }
+ }
+}
+
+static void vt1603_codec_irq_handle(struct work_struct *work)
+{
+ struct vt1603_priv *priv = container_of(work, struct vt1603_priv, work);
+ struct snd_soc_codec *codec = priv->codec;
+ unsigned int val;
+ static unsigned int count = 0;
+ int hp_state = 0; //switch_get_state(&priv->hp_switch);
+ if (!vt1603_boardinfo.hpd_rm)
+ hp_state = switch_get_state(&priv->hp_switch);
+
+ unsigned int debounce = (hp_state == 0) ? 3 : 1; // sw-debounce for headset plugin
+
+ if (vt1603_boardinfo.hp_debounce > 0)
+ debounce = (hp_state == 0) ? vt1603_boardinfo.hp_debounce : 1;
+
+
+
+ val = vt1603_hw_read(codec, VT1603_R51);
+ if (val & BIT1) {
+ val = vt1603_hw_read(codec, VT1603_R1b);
+ val |= BIT1;
+ snd_soc_write(codec, VT1603_R1b, val);
+ val &= ~BIT1;
+ snd_soc_write(codec, VT1603_R1b, val);
+ count++;
+ }
+ else {
+ count = 0;
+ }
+
+ if (count == debounce) {
+ pr_info("vt1603: hpdetect\n");
+ vt1603_set_switch_state(codec);
+ // clear headphone irq mask
+ val = vt1603_hw_read(codec,VT1603_R1b);
+ val |= BIT1;
+ snd_soc_write(codec, VT1603_R1b, val);
+ val &= ~BIT1;
+ snd_soc_write(codec, VT1603_R1b, val);
+ count = 0;
+ }
+
+ val = vt1603_hw_read(codec, VT1603_R52);
+ if (val & BIT4) {
+ pr_info("vt1603: left cld oc\n");
+ val = vt1603_hw_read(codec,VT1603_R1c);
+ val |= BIT4;
+ snd_soc_write(codec, VT1603_R1c, val);
+ val &= ~BIT4;
+ snd_soc_write(codec, VT1603_R1c, val);
+ }
+ if (val & BIT3) {
+ pr_info("vt1603: right cld oc\n");
+ val = vt1603_hw_read(codec,VT1603_R1c);
+ val |= BIT3;
+ snd_soc_write(codec, VT1603_R1c, val);
+ val &= ~BIT3;
+ snd_soc_write(codec, VT1603_R1c, val);
+ }
+}
+
+static void vt1603_timer_handler(unsigned long data)
+{
+ struct vt1603_priv *vt1603 = (struct vt1603_priv *)data;
+ schedule_work(&vt1603->work);
+ mod_timer(&vt1603->timer, jiffies + msecs_to_jiffies(VT1603_TIMER_INTERVAL));
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*--------------------Mixer controls & dapm-----------------------------*/
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -9550, 50, 1);
+static const DECLARE_TLV_DB_SCALE(digital_clv, -4350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_boost_clv, 0, 1000, 1);
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_pga_clv, -1650, 150, 1);
+static const DECLARE_TLV_DB_SCALE(analog_tlv, -2100, 300, 1);
+static const DECLARE_TLV_DB_SCALE(hp_mixer_clv, -5700, 100, 1);
+static const DECLARE_TLV_DB_SCALE(cld_mixer_clv, -300, 300, 1);
+
+static const char *adc_cutoff_freq_txt[] = {
+ "Hi-fi mode",
+ "Voice mode 1",
+ "Voice mode 2",
+ "Voice mode 3",
+};
+
+static const struct soc_enum adc_cutoff_freq =
+SOC_ENUM_SINGLE(VT1603_R00, 3, 4, adc_cutoff_freq_txt);
+
+static const char *lr_txt[] = {
+ "Left", "Right"
+};
+
+static const char *clamper_limit_voltage_txt[] = {
+ "Vih=2.40V, Vil=0.90V, P=0.63W",
+ "Vih=2.45V, Vil=0.85V, P=0.72W",
+ "Vih=2.50V, ViL=0.80V, P=0.81W",
+ "Vih=2.55V, Vil=0.75V, P=0.91W",
+ "Vih=2.60V, Vil=0.70V, P=1.02W",
+ "Vih=2.65V, Vil=0.65V, P=1.12W",
+ "Vih=2.70V, Vil=0.60V, P=1.24W",
+ "Vih=2.75V, Vil=0.55V, P=1.36W",
+ "Vih=2.80V, Vil=0.50V, P=1.49W",
+ "Vih=2.85V, Vil=0.45V, P=1.62W",
+ "Vih=2.90V, Vil=0.40V, P=1.76W",
+ "Vih=2.95V, Vil=0.35V, P=1.90W",
+ "Vih=3.00V, Vil=0.30V, P=2.05W",
+ "Vih=3.05V, Vil=0.25V, P=2.20W",
+ "Vih=3.10V, Vil=0.20V, P=2.37W",
+ "Vih=3.15V, Vil=0.15V, P=2.53W",
+};
+
+static const char *clamper_work_voltage_txt[] = {
+ "Voh=3.10V, Vol=1.90V, P=0.36W",
+ "Voh=3.25V, Vol=1.75V, P=0.56W",
+ "Voh=3.40V, Vol=1.60V, P=0.81W",
+ "Voh=3.55V, Vol=1.45V, P=1.10W",
+ "Voh=3.70V, Vol=1.30V, P=1.44W",
+ "Voh=3.85V, Vol=1.15V, P=1.82W",
+ "Voh=4.00V, Vol=1.00V, P=2.25W",
+ "Voh=4.15V, Vol=0.85V, P=2.72W",
+};
+
+static const struct soc_enum dacl_src =
+SOC_ENUM_SINGLE(VT1603_R0b, 7, 2, lr_txt);
+
+static const struct soc_enum dacr_src =
+SOC_ENUM_SINGLE(VT1603_R0b, 6, 2, lr_txt);
+
+static const struct soc_enum adcl_src =
+SOC_ENUM_SINGLE(VT1603_R03, 3, 2, lr_txt);
+
+static const struct soc_enum adcr_src =
+SOC_ENUM_SINGLE(VT1603_R03, 2, 2, lr_txt);
+
+static const struct soc_enum clamper_limit_voltage =
+SOC_ENUM_SINGLE(VT1603_R87, 4, 16, clamper_limit_voltage_txt);
+
+static const struct soc_enum clamper_work_voltage =
+SOC_ENUM_SINGLE(VT1603_R87, 0, 8, clamper_work_voltage_txt);
+
+static const struct snd_kcontrol_new vt1603_snd_controls[] = {
+/* Volume */
+SOC_DOUBLE_R_TLV("Digital Capture Volume", VT1603_R01, VT1603_R02, 0, 0x7f, 0, digital_clv),
+SOC_DOUBLE_R_TLV("Digital Playback Volume", VT1603_R07, VT1603_R08, 0, 0xc0, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("Analog Playback Volume", VT1603_R72, VT1603_R73, 0, 0x07, 1, analog_tlv),
+SOC_DOUBLE_R_TLV("Input Boost Volume", VT1603_R64, VT1603_R65, 6, 4, 0, adc_boost_clv),
+SOC_DOUBLE_R_TLV("Input PGA Volume", VT1603_R64, VT1603_R65, 1, 0x1f, 0, adc_pga_clv),
+SOC_DOUBLE_TLV("Output Boost Volume", VT1603_R06, 0, 3, 7, 0, dac_boost_tlv),
+SOC_DOUBLE_TLV("Output CLD Volume", VT1603_R91, 5, 2, 7, 0, cld_mixer_clv),
+SOC_DOUBLE_R_TLV("Output HP Volume", VT1603_R6a, VT1603_R6b, 0, 0x3f, 0, hp_mixer_clv),
+
+/* Switch */
+SOC_SINGLE("EQ Switch", VT1603_R28, 0, 1, 0),
+
+SOC_SINGLE("DAC Mono Mix Switch", VT1603_R0d, 4, 1, 0),
+SOC_SINGLE("Left DAC Switch", VT1603_R62, 5, 1, 0),
+SOC_SINGLE("Right DAC Switch", VT1603_R62, 4, 1, 0),
+
+SOC_ENUM("Left DAC Source", dacl_src),
+SOC_ENUM("Right DAC Source", dacr_src),
+
+SOC_ENUM("Digital Capture Voice Mode", adc_cutoff_freq),
+SOC_ENUM("Left ADC Source", adcl_src),
+SOC_ENUM("Right ADC Source", adcr_src),
+
+SOC_DOUBLE("Analog Playback Switch", VT1603_R71, 7, 6, 1, 1),
+SOC_SINGLE("Digital Playback Switch", VT1603_R0b, 0, 1, 1),
+SOC_DOUBLE("Digital Capture Switch", VT1603_R63, 7, 6, 1, 0),
+
+SOC_SINGLE("Headset Switch", VT1603_R68, 4, 1, 1),
+SOC_SINGLE("Headfree Switch", VT1603_R25, 1, 1, 0),
+
+SOC_SINGLE("Left CLD Switch", VT1603_R82, 3, 1, 1),
+SOC_SINGLE("Right CLD Switch", VT1603_R82, 4, 1, 1),
+
+SOC_ENUM("Limit Voltage of Power Clamper", clamper_limit_voltage),
+SOC_ENUM("Voltage of Clamper Work", clamper_work_voltage),
+};
+
+/* DAPM Controls */
+
+static const struct snd_kcontrol_new linmix_controls[] = {
+SOC_DAPM_SINGLE("Left Mic Switch", VT1603_R8e, 5, 1, 0),
+SOC_DAPM_SINGLE("Left Linein Switch", VT1603_R8e, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new rinmix_controls[] = {
+SOC_DAPM_SINGLE("Right Mic Switch", VT1603_R8e, 4, 1, 0),
+SOC_DAPM_SINGLE("Right Linein Switch", VT1603_R8e, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new loutmix_controls[] = {
+SOC_DAPM_SINGLE("Left Input Mixer Switch", VT1603_R71, 3, 1, 0),
+SOC_DAPM_SINGLE("DACL Switch", VT1603_R71, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new routmix_controls[] = {
+SOC_DAPM_SINGLE("Right Input Mixer Switch", VT1603_R71, 2, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", VT1603_R71, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new lcld_controls[] = {
+SOC_DAPM_SINGLE("Left Output Mixer Switch", VT1603_R90, 5, 1, 0),
+SOC_DAPM_SINGLE("DACL Switch", VT1603_R90, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new rcld_controls[] = {
+SOC_DAPM_SINGLE("Right Output Mixer Switch", VT1603_R90, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", VT1603_R90, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new lhp_controls[] = {
+SOC_DAPM_SINGLE("Left Output Mixer Switch", VT1603_R69, 5, 1, 0),
+SOC_DAPM_SINGLE("DACL Switch", VT1603_R69, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new rhp_controls[] = {
+SOC_DAPM_SINGLE("Right Output Mixer Switch", VT1603_R69, 2, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", VT1603_R69, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget vt1603_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("Mic"),
+SND_SOC_DAPM_INPUT("Linein"),
+SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+SND_SOC_DAPM_PGA("Left Input Boost", VT1603_R67, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Input Boost", VT1603_R67, 6, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Left Input PGA", VT1603_R67, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Input PGA", VT1603_R67, 4, 0, NULL, 0),
+
+
+SND_SOC_DAPM_MIXER("Left Input Mixer", VT1603_R66, 6, 1,
+ linmix_controls, ARRAY_SIZE(linmix_controls)),
+SND_SOC_DAPM_MIXER("Right Input Mixer", VT1603_R66, 5, 1,
+ rinmix_controls, ARRAY_SIZE(rinmix_controls)),
+
+SND_SOC_DAPM_ADC("ADCL", "Left Capture", VT1603_R01, 7, 1),
+SND_SOC_DAPM_ADC("ADCR", "Right Capture", VT1603_R02, 7, 1),
+
+//SND_SOC_DAPM_DAC("DACL", "Left Playback", VT1603_R62, 5, 0),
+//SND_SOC_DAPM_DAC("DACR", "Right Playback", VT1603_R62, 4, 0),
+
+SND_SOC_DAPM_INPUT("DACL"),
+SND_SOC_DAPM_INPUT("DACR"),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", VT1603_R72, 6, 0,
+ loutmix_controls, ARRAY_SIZE(loutmix_controls)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", VT1603_R73, 6, 0,
+ routmix_controls, ARRAY_SIZE(routmix_controls)),
+
+SND_SOC_DAPM_MIXER("Left CLD Mixer", VT1603_R91, 1, 0,
+ lcld_controls, ARRAY_SIZE(lcld_controls)),
+SND_SOC_DAPM_MIXER("Right CLD Mixer", VT1603_R91, 0, 0,
+ rcld_controls, ARRAY_SIZE(rcld_controls)),
+
+SND_SOC_DAPM_PGA("Left CLD PGA", VT1603_R82, 3, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Right CLD PGA", VT1603_R82, 4, 1, NULL, 0),
+
+SND_SOC_DAPM_MIXER("Left HP Mixer", VT1603_R68, 1, 1,
+ lhp_controls, ARRAY_SIZE(lhp_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", VT1603_R68, 0, 1,
+ rhp_controls, ARRAY_SIZE(rhp_controls)),
+
+SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+SND_SOC_DAPM_OUTPUT("Left HP"),
+SND_SOC_DAPM_OUTPUT("Right HP"),
+SND_SOC_DAPM_OUTPUT("Left SPK"),
+SND_SOC_DAPM_OUTPUT("Right SPK"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Make DACs turn on when playing even if not mixed into any outputs */
+ //{"Internal DAC Sink", NULL, "DACL"},
+ //{"Internal DAC Sink", NULL, "DACR"},
+
+ /* Make ADCs turn on when recording even if not mixed from any inputs */
+ //{"ADCL", NULL, "Internal ADC Source"},
+ //{"ADCR", NULL, "Internal ADC Source"},
+
+
+ /* Capture route */
+
+ { "Left Input Mixer", "Left Linein Switch", "Linein" },
+ { "Left Input Mixer", "Left Mic Switch", "Mic" },
+ { "Right Input Mixer", "Right Linein Switch", "Linein" },
+ { "Right Input Mixer", "Right Mic Switch", "Mic" },
+
+ { "Left Input Boost", NULL, "Left Input Mixer"},
+ { "Right Input Boost", NULL, "Right Input Mixer"},
+
+ { "Left Input PGA", NULL, "Left Input Boost"},
+ { "Right Input PGA", NULL, "Right Input Boost"},
+
+ { "ADCL", NULL, "Left Input PGA" },
+ { "ADCR", NULL, "Right Input PGA" },
+
+ //{ "Left Input Mixer", NULL, "Left Input PGA" },
+ //{ "Right Input Mixer", NULL, "Right Input PGA" },
+
+ /* Playback route */
+
+ //{ "Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer" },
+ { "Left Output Mixer", "Left Input Mixer Switch", "Left Input PGA"},
+ { "Left Output Mixer", "DACL Switch", "DACL" },
+ //{ "Right Output Mixer", "Right Input Mixer Switch", "Right Input Mixer" },
+ { "Right Output Mixer", "Right Input Mixer Switch", "Right Input PGA"},
+ { "Right Output Mixer", "DACR Switch", "DACR" },
+
+ { "Left CLD Mixer", "Left Output Mixer Switch", "Left Output Mixer" },
+ { "Left CLD Mixer", "DACL Switch", "DACL" },
+ { "Right CLD Mixer", "Right Output Mixer Switch", "Right Output Mixer" },
+ { "Right CLD Mixer", "DACR Switch", "DACR" },
+
+ { "Left HP Mixer", "Left Output Mixer Switch", "Left Output Mixer" },
+ { "Left HP Mixer", "DACL Switch", "DACL" },
+ { "Right HP Mixer", "Right Output Mixer Switch", "Right Output Mixer" },
+ { "Right HP Mixer", "DACR Switch", "DACR" },
+
+ { "Left CLD PGA", NULL, "Left CLD Mixer" },
+ { "Right CLD PGA", NULL, "Right CLD Mixer" },
+
+ { "Left HP", NULL, "Left HP Mixer" },
+ { "Right HP", NULL, "Right HP Mixer" },
+
+ { "Left SPK", NULL, "Left CLD PGA" },
+ { "Right SPK", NULL, "Right CLD PGA" },
+};
+
+/*----------------------------------------------------------------------*/
+
+
+/*----------------------codec dai functions-----------------------------*/
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr;
+ u8 bclk_div;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 8k */
+ {12288000, 8000, 1536, 0x4, 0x0},
+ /* 11.025k */
+ {11289600, 11025, 1024, 0x8, 0x0},
+ /* 16k */
+ {12288000, 16000, 768, 0x5, 0x0},
+ /* 22.05k */
+ {11289600, 22050, 512, 0x9, 0x0},
+ /* 32k */
+ {12288000, 32000, 384, 0x7, 0x0},
+ /* 44.1k */
+ {11289600, 44100, 256, 0x6, 0x07},
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x07},
+ /* 96k */
+ {12288000, 96000, 128, 0x1, 0x04},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int vt1603_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec);
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ vt1603->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int vt1603_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = snd_soc_read(codec, VT1603_R19);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface |= BIT5 ;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, VT1603_R19, iface);
+ return 0;
+}
+
+static int vt1603_pcm_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;
+ struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec);
+ u16 val;
+ u16 iface = snd_soc_read(codec, VT1603_R19) & 0x73;
+ int coeff = get_coeff(vt1603->sysclk, params_rate(params));
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x08;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x0c;
+ break;
+ }
+
+ /* set iface & srate */
+ snd_soc_write(codec, VT1603_R19, iface);
+
+ if (coeff >= 0) {
+ val = snd_soc_read(codec, VT1603_R42);
+ val &= 0xf0;
+ val |= coeff_div[coeff].bclk_div;
+ snd_soc_write(codec, VT1603_R42, val);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ val = snd_soc_read(codec, VT1603_R05);
+ val &= 0xf0;
+ val |= coeff_div[coeff].sr;
+ snd_soc_write(codec, VT1603_R05, val);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ val = snd_soc_read(codec, VT1603_R03);
+ val &= 0x0f;
+ val |= ((coeff_div[coeff].sr<<4) & 0xf0);
+ snd_soc_write(codec, VT1603_R03, val);
+ }
+ }
+
+ return 0;
+}
+
+static int vt1603_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 val;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ val = snd_soc_read(codec, VT1603_R96);
+ val |= BIT4+BIT5;
+ snd_soc_write(codec, VT1603_R96, val);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static struct snd_soc_dai_ops vt1603_dai_ops = {
+ .hw_params = vt1603_pcm_hw_params,
+ .set_fmt = vt1603_set_dai_fmt,
+ .set_sysclk = vt1603_set_dai_sysclk,
+};
+
+struct snd_soc_dai_driver vt1603_dai = {
+ .name = "VT1603",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &vt1603_dai_ops,
+};
+
+/*----------------------------------------------------------------------*/
+
+
+/*----------------------------------------------------------------------*/
+
+static void vt1603_get_boardinfo(void)
+{
+ int ret = 0;
+ unsigned char buf[64];
+ int varlen = sizeof(buf);
+
+ memset(buf, 0x0, sizeof(buf));
+ ret = wmt_getsyspara("wmt.audio.i2s", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "vt1603:%d:%d:%d",
+ &vt1603_boardinfo.hp_level,
+ &vt1603_boardinfo.ignore_hp_event,
+ &vt1603_boardinfo.rec_src);
+ }
+ else {
+ vt1603_boardinfo.hp_level = 0;
+ vt1603_boardinfo.ignore_hp_event = 0;
+ vt1603_boardinfo.rec_src = 0;
+ }
+
+ memset(buf, 0x0, sizeof(buf));
+ vt1603_boardinfo.hpd_rm = 0;
+ ret = wmt_getsyspara("wmt.vt1603.hpd.rm", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "%d",
+ &vt1603_boardinfo.hpd_rm);
+ }
+ else {
+ vt1603_boardinfo.hpd_rm = 0;
+
+ }
+ printk("<<<<%s hpd_rm %d\n", __func__, vt1603_boardinfo.hpd_rm);
+
+ memset(buf, 0x0, sizeof(buf));
+ vt1603_boardinfo.hp_debounce = 0;
+ ret = wmt_getsyspara("wmt.vt1603.hp.debounce", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "%d",
+ &vt1603_boardinfo.hp_debounce);
+ }
+ else {
+ vt1603_boardinfo.hp_debounce = 0;
+
+ }
+ printk("<<<<%s hp_debounce %d\n", __func__, vt1603_boardinfo.hp_debounce);
+
+
+ memset(buf, 0x0, sizeof(buf));
+ vt1603_boardinfo.timer_del = 0;
+ ret = wmt_getsyspara("wmt.vt1603.timer.del", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "%d",
+ &vt1603_boardinfo.timer_del);
+ }
+ else {
+ vt1603_boardinfo.timer_del = 0;
+
+ }
+ printk("<<<<%s timer_del %d\n", __func__, vt1603_boardinfo.timer_del);
+
+ memset(buf, 0x0, sizeof(buf));
+ vt1603_boardinfo.out_on = 0;
+ ret = wmt_getsyspara("wmt.vt1603.out_on", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "%d",
+ &vt1603_boardinfo.out_on);
+ }
+ else {
+ vt1603_boardinfo.out_on = 0;
+
+ }
+ printk("<<<<%s out_on %d\n", __func__, vt1603_boardinfo.out_on);
+}
+
+static int vt1603_readproc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct vt1603_priv *vt1603 = data;
+ pr_info("\nvt1603 regs dump:\n");
+ vt1603_regs_dump(vt1603->codec->control_data);
+ return 0;
+}
+
+static void vt1603_codec_reset(struct snd_soc_codec *codec)
+{
+ unsigned int val;
+
+ vt1603_hw_write(codec, 0xc2, 0x01);
+
+ // reset all registers to default settings
+ vt1603_hw_write(codec, VT1603_REG_RESET, 0xff);
+ vt1603_hw_write(codec, VT1603_REG_RESET, 0x00);
+
+ val = vt1603_hw_read(codec, VT1603_R60);
+ val &= ~(BIT6 + BIT7);
+ vt1603_hw_write(codec, VT1603_R60, val);
+ val |= BIT6 + BIT7;
+ vt1603_hw_write(codec, VT1603_R60, val);
+}
+
+static void vt1603_codec_init(struct snd_soc_codec *codec)
+{
+ unsigned int val;
+
+ // vt1603 touch uses irq low active???
+ if (vt1603_boardinfo.hp_level) {
+ //hp high
+ snd_soc_write(codec, VT1603_R21, 0xfd);
+ }
+ else {
+ //hp low
+ snd_soc_write(codec, VT1603_R21, 0xff);
+ }
+
+ // vt1603 codec irq settings
+ snd_soc_write(codec, VT1603_R1b, 0xff);
+ snd_soc_write(codec, VT1603_R1b, 0xfd);
+ snd_soc_write(codec, VT1603_R1c, 0xff);
+ snd_soc_write(codec, VT1603_R1d, 0xff);
+ snd_soc_write(codec, VT1603_R24, 0x04);
+
+ snd_soc_write(codec, VT1603_R95, 0x00);
+ snd_soc_write(codec, VT1603_R60, 0xc7);
+ snd_soc_write(codec, VT1603_R93, 0x02);
+ snd_soc_write(codec, VT1603_R7b, 0x10);
+ snd_soc_write(codec, VT1603_R92, 0x2c);
+
+ // r4 is reserved, I don't know more about it.
+ snd_soc_write(codec, VT1603_R04, 0x00);
+ // r9[5] is reserved, I don't know more about it.
+ val = snd_soc_read(codec, VT1603_R09);
+ val |= BIT5;
+ snd_soc_write(codec, VT1603_R09, val);
+ // r79 is reserved, I don't know more about it.
+ val = snd_soc_read(codec, VT1603_R79);
+ val |= BIT2;
+ val &= ~(BIT0+BIT1);
+ snd_soc_write(codec, VT1603_R79, val);
+ // r7a is reserved, I don't know more about it.
+ val = snd_soc_read(codec, VT1603_R7a);
+ val |= BIT3+BIT4+BIT7;
+ snd_soc_write(codec, VT1603_R7a, val);
+ // r87 is reserved, I don't know more about it.
+ snd_soc_write(codec, VT1603_R87, 0x90);
+ // r88 is reserved, I don't know more about it.
+ snd_soc_write(codec, VT1603_R88, 0x28);
+ // r8a is reserved, I don't know more about it.
+ val = snd_soc_read(codec, VT1603_R8a);
+ val |= BIT1;
+ snd_soc_write(codec, VT1603_R8a, val);
+ // enable CLK_SYS, CLK_DSP, CLK_AIF
+ val = snd_soc_read(codec, VT1603_R40);
+ val |= BIT4+BIT5+BIT6;
+ snd_soc_write(codec, VT1603_R40, val);
+ // set DAC sample rate divier=CLK_SYS/1, DAC clock divider=DAC_CLK_512X
+ val = snd_soc_read(codec, VT1603_R41);
+ val &= ~(BIT3+BIT2+BIT1+BIT0);
+ val |= BIT1;//add for mclk 24.576M, 10Khz 17kHz sine distortion 2014-6-12
+ snd_soc_write(codec, VT1603_R41, val);
+ // set BCLK rate=CLK_SYS/8
+ val = snd_soc_read(codec, VT1603_R42);
+ val |= BIT0+BIT1+BIT2;
+ val &= ~BIT3;
+ snd_soc_write(codec, VT1603_R42, val);
+ // enable VREF_SC_DA buffer
+ val = snd_soc_read(codec, VT1603_R61);
+ val |= BIT7;
+ snd_soc_write(codec, VT1603_R61, val);
+ // ADC VREF_SC offset voltage
+ snd_soc_write(codec, VT1603_R93, 0x20);
+ // set mic bias voltage = 90% * AVDD
+ val = snd_soc_read(codec, VT1603_R92);
+ val |= BIT2;
+ snd_soc_write(codec, VT1603_R92, val);
+ // enable bypass for AOW0 parameterized HPF
+ val = snd_soc_read(codec, VT1603_R0a);
+ val |= BIT6;
+ snd_soc_write(codec, VT1603_R0a, val);
+ // CPVEE=2.2uF, the ripple frequency ON CPVEE above 22kHz, that will reduce HP noise
+ val = snd_soc_read(codec, VT1603_R7a);
+ val |= BIT6;
+ val &= ~BIT5;
+ snd_soc_write(codec, VT1603_R7a, val);
+
+ // set DAC soft mute mode
+ val = snd_soc_read(codec, VT1603_R0b);
+ val |= BIT1+BIT2;
+ snd_soc_write(codec, VT1603_R0b, val);
+ // DAC High Voltage Switch Control Signal enable???
+ val = snd_soc_read(codec, VT1603_R62);
+ val |= BIT7+BIT6;
+ val &= ~BIT3;
+ snd_soc_write(codec, VT1603_R62, val);
+ // enable microphone bias
+ val = snd_soc_read(codec, VT1603_R60);
+ val |= 0x0f;
+ snd_soc_write(codec, VT1603_R60, val);
+ // capture pga settings??
+ val = snd_soc_read(codec, VT1603_R8e);
+ val |= 0x0f;
+ snd_soc_write(codec, VT1603_R8e, val);
+ // capture pga settings??
+ val = snd_soc_read(codec, VT1603_R66);
+ val |= BIT1+BIT2+BIT3+BIT4; //
+ snd_soc_write(codec, VT1603_R66, val);
+ // PGA Zero Cross Detector Enable, Change gain on zero cross only
+ val = snd_soc_read(codec, VT1603_R64);
+ val |= BIT0;
+ snd_soc_write(codec, VT1603_R64, val);
+ val = snd_soc_read(codec, VT1603_R65);
+ val |= BIT0;
+ snd_soc_write(codec, VT1603_R65, val);
+ // enable hp output mode
+ val = snd_soc_read(codec, VT1603_R68);
+ val |= BIT2;
+ snd_soc_write(codec, VT1603_R68, val);
+ // enable class-d, unmute channels
+ snd_soc_write(codec, VT1603_R7c, 0xe0);
+
+ // set DAC gain update
+ val = snd_soc_read(codec, VT1603_R05);
+ val |= BIT6+BIT7;
+ snd_soc_write(codec, VT1603_R05, val);
+ // set class-d AC boost gain=1.6
+ snd_soc_write(codec, VT1603_R97, 0x1c);
+ val = snd_soc_read(codec, VT1603_R97);
+ val = (val & (~0x07)) + 0x04;
+ snd_soc_write(codec, VT1603_R97, val);
+ // set ADC gain update, enable ADCDAT output
+ val = snd_soc_read(codec, VT1603_R00);
+ val |= BIT5+BIT6+BIT7;
+ snd_soc_write(codec, VT1603_R00, val);
+
+ if (vt1603_boardinfo.out_on)
+ {
+ val = snd_soc_read(codec, VT1603_R68);
+ val &= ~(1<<4);
+ snd_soc_write(codec, VT1603_R68, val);
+
+ val = snd_soc_read(codec, VT1603_R69);
+ val |= BIT2 +BIT5;
+ snd_soc_write(codec, VT1603_R69, val);
+
+
+ val = snd_soc_read(codec, VT1603_R25);
+ val |= BIT1;
+ snd_soc_write(codec, VT1603_R25, val);
+
+ val = snd_soc_read(codec, VT1603_R90);
+ val |= BIT5 +BIT3;
+ snd_soc_write(codec, VT1603_R90, val);
+
+ }
+ // DA DRC settings
+ //snd_soc_write(codec, VT1603_R0e, 0x0b);
+ //snd_soc_write(codec, VT1603_R0f, 0x94);
+ //snd_soc_write(codec, VT1603_R10, 0x02);
+ //snd_soc_write(codec, VT1603_R11, 0x00);
+ //snd_soc_write(codec, VT1603_R12, 0x60);
+}
+
+static int vt1603_reboot_notify(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ unsigned int val;
+ struct vt1603_priv *priv = container_of(this, struct vt1603_priv, reboot_notifier);
+ struct snd_soc_codec *codec = priv->codec;
+
+ printk("vt1603_reboot_notify [%lu]\n", code);
+
+ // power down headphone
+ val = snd_soc_read(codec, VT1603_R68);
+ val |= BIT4;
+ snd_soc_write(codec, VT1603_R68, val);
+
+ // power down class-d
+ val = snd_soc_read(codec, VT1603_R25);
+ val &= ~BIT1;
+ snd_soc_write(codec, VT1603_R25, val);
+
+ return NOTIFY_DONE;
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*---------------------codec driver functions---------------------------*/
+
+static int vt1603_codec_suspend(struct snd_soc_codec *codec)
+{
+ struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec);
+ if (!vt1603_boardinfo.timer_del) { //add 2014-1-22 tvbox
+ del_timer_sync(&vt1603->timer);
+ }
+ return 0;
+}
+
+static int vt1603_codec_resume(struct snd_soc_codec *codec)
+{
+ struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec);
+ int reg;
+ u16 *cache = codec->reg_cache;
+ unsigned int val;
+ int state = 0;//switch_get_state(&vt1603->hp_switch);
+ if (!vt1603_boardinfo.hpd_rm)
+ state = switch_get_state(&vt1603->hp_switch);
+ // reset codec
+ vt1603_codec_reset(codec);
+
+ // restore codec registers
+ for (reg = 0; reg < ARRAY_SIZE(vt1603_regs); reg++) {
+ if (reg == VT1603_REG_RESET)
+ continue;
+ snd_soc_write(codec, reg, cache[reg]);
+ }
+
+ // restore headphone irq status
+ val = vt1603_hw_read(codec, VT1603_R21);
+ if ((vt1603_boardinfo.hp_level && state) ||
+ (!vt1603_boardinfo.hp_level && !state)) {
+ val |= BIT1;
+ snd_soc_write(codec, VT1603_R21, val);
+ }
+ else {
+ val &= ~BIT1;
+ snd_soc_write(codec, VT1603_R21, val);
+ }
+
+ // open headphone detect
+ val = vt1603_hw_read(codec,VT1603_R1b);
+ val |= BIT1;
+ snd_soc_write(codec, VT1603_R1b, val);
+ val &= ~BIT1;
+ snd_soc_write(codec, VT1603_R1b, val);
+
+ // clear touch interrupt status??? why here???
+ snd_soc_write(codec, 0xca, 0x0f);
+
+ if (!vt1603_boardinfo.timer_del) { //add 2014-1-22 tvbox
+ mod_timer(&vt1603->timer, jiffies + msecs_to_jiffies(50));
+ }
+
+ return 0;
+}
+
+static int vt1603_codec_probe(struct snd_soc_codec *codec)
+{
+ struct vt1603_priv *vt1603;
+ int ret;
+
+ vt1603 = kzalloc(sizeof(struct vt1603_priv), GFP_KERNEL);
+ if (vt1603 == NULL)
+ return -ENOMEM;
+
+ gvt1603_codec = codec;//add 2013-9-2
+ snd_soc_codec_set_drvdata(codec, vt1603);
+
+ codec->control_data = dev_get_platdata(codec->dev);
+
+ vt1603->codec = codec;
+// need close to use wmt-switch.c 2013-11-27
+ if (!vt1603_boardinfo.hpd_rm) {
+ vt1603->hp_switch.name = "h2w";
+ switch_dev_register(&vt1603->hp_switch);
+ }
+//need close
+ vt1603_codec_reset(codec);
+ vt1603_codec_init(codec);
+ vt1603_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+
+ INIT_WORK(&vt1603->work, vt1603_codec_irq_handle);
+ if (!vt1603_boardinfo.timer_del) { //add 2014-1-20 tvbox
+ init_timer(&vt1603->timer);
+ vt1603->timer.data = (unsigned long)vt1603;
+ vt1603->timer.function = vt1603_timer_handler;
+ mod_timer(&vt1603->timer, jiffies + msecs_to_jiffies(2000));
+ }
+
+ vt1603->proc = create_proc_read_entry("vt1603_dumpregs", 0666, NULL,
+ vt1603_readproc, vt1603);
+
+ // register reboot notifier event
+ vt1603->reboot_notifier.notifier_call = vt1603_reboot_notify;
+ ret = register_reboot_notifier(&vt1603->reboot_notifier);
+ if (ret != 0)
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
+
+ return 0;
+}
+
+static int vt1603_codec_remove(struct snd_soc_codec *codec)
+{
+ struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec);
+ remove_proc_entry("vt1603_dumpregs", NULL);
+ if (!vt1603_boardinfo.hpd_rm) {
+ switch_dev_unregister(&vt1603->hp_switch);
+ }
+ del_timer_sync(&vt1603->timer);
+ vt1603_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ kfree(vt1603);
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_vt1603 = {
+ .probe = vt1603_codec_probe,
+ .remove = vt1603_codec_remove,
+ .suspend = vt1603_codec_suspend,
+ .resume = vt1603_codec_resume,
+ .read = vt1603_read,
+ .write = vt1603_write,
+ .set_bias_level = vt1603_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(vt1603_regs),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = vt1603_regs,
+
+ .controls = vt1603_snd_controls,
+ .num_controls = ARRAY_SIZE(vt1603_snd_controls),
+ .dapm_widgets = vt1603_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(vt1603_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+static int __devinit vt1603_probe(struct platform_device *pdev)
+{
+ pr_info("<<<<%s \n", __FUNCTION__);
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_vt1603,
+ &vt1603_dai, 1);
+}
+
+static int __devexit vt1603_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver vt1603_codec_driver = {
+ .driver = {
+ .name = "vt1603-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = vt1603_probe,
+ .remove = __devexit_p(vt1603_remove),
+};
+
+static __init int vt1603_init(void)
+{
+ vt1603_get_boardinfo();
+ return platform_driver_register(&vt1603_codec_driver);
+}
+module_init(vt1603_init);
+
+static __exit void vt1603_exit(void)
+{
+ platform_driver_unregister(&vt1603_codec_driver);
+}
+module_exit(vt1603_exit);
+
+int vt1603_hwdep_ioctl(u8 rw_flag, u16 offset, u16 value)
+{
+ struct snd_soc_codec *codec = gvt1603_codec;
+ if (!codec){
+ printk("%s NULL!\n", __FUNCTION__);
+ return -1;
+ }
+ //info("rw_flag=%d, offset=0x%x, value=0x%x", rw_flag, offset, value);
+
+ if (rw_flag) {
+ if ((offset >= VT1603_R00) && (offset <= VT1603_R97)) {
+ snd_soc_write(codec, offset, value);
+ }
+ else {
+ printk("write to offset 0x%x is not allowed", offset);
+ }
+ return 0;
+ }
+ else {
+ return snd_soc_read(codec, offset);
+ }
+}
+EXPORT_SYMBOL(vt1603_hwdep_ioctl);
+
+MODULE_DESCRIPTION("WMT [ALSA SoC/codec] driver");
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:vt1603-codec");
diff --git a/ANDROID_3.4.5/sound/soc/codecs/vt1603.h b/ANDROID_3.4.5/sound/soc/codecs/vt1603.h
new file mode 100755
index 00000000..ef130f10
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/codecs/vt1603.h
@@ -0,0 +1,119 @@
+/*++
+ * linux/sound/soc/codecs/vt1603.h
+ * WonderMedia audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#ifndef _VT1603_H
+#define _VT1603_H
+
+#define BIT0 0x00000001
+#define BIT1 0x00000002
+#define BIT2 0x00000004
+#define BIT3 0x00000008
+#define BIT4 0x00000010
+#define BIT5 0x00000020
+#define BIT6 0x00000040
+#define BIT7 0x00000080
+#define BIT8 0x00000100
+
+#define VT1603_REG_RESET 0x15
+
+#define VT1603_R00 0x00
+#define VT1603_R01 0x01
+#define VT1603_R02 0x02
+#define VT1603_R03 0x03
+#define VT1603_R04 0x04
+#define VT1603_R05 0x05
+#define VT1603_R06 0x06
+#define VT1603_R07 0x07
+#define VT1603_R08 0x08
+#define VT1603_R09 0x09
+#define VT1603_R0a 0x0a
+#define VT1603_R0b 0x0b
+#define VT1603_R0c 0x0c
+#define VT1603_R0d 0x0d
+#define VT1603_R0e 0x0e
+#define VT1603_R0f 0x0f
+#define VT1603_R10 0x10
+#define VT1603_R11 0x11
+#define VT1603_R12 0x12
+#define VT1603_R13 0x13
+#define VT1603_R15 0x15
+#define VT1603_R19 0x19
+#define VT1603_R1b 0x1b
+#define VT1603_R1c 0x1c
+#define VT1603_R1d 0x1d
+#define VT1603_R20 0x20
+#define VT1603_R21 0x21
+#define VT1603_R23 0x23
+#define VT1603_R24 0x24
+#define VT1603_R25 0x25
+#define VT1603_R28 0x28
+#define VT1603_R29 0x29
+#define VT1603_R2a 0x2a
+#define VT1603_R2b 0x2b
+#define VT1603_R2c 0x2c
+#define VT1603_R2d 0x2d
+#define VT1603_R40 0x40
+#define VT1603_R41 0x41
+#define VT1603_R42 0x42
+#define VT1603_R47 0x47
+#define VT1603_R51 0x51
+#define VT1603_R52 0x52
+#define VT1603_R53 0x53
+#define VT1603_R5f 0x5f
+#define VT1603_R60 0x60
+#define VT1603_R61 0x61
+#define VT1603_R62 0x62
+#define VT1603_R63 0x63
+#define VT1603_R64 0x64
+#define VT1603_R65 0x65
+#define VT1603_R66 0x66
+#define VT1603_R67 0x67
+#define VT1603_R68 0x68
+#define VT1603_R69 0x69
+#define VT1603_R6a 0x6a
+#define VT1603_R6b 0x6b
+#define VT1603_R6d 0x6d
+#define VT1603_R6e 0x6e
+#define VT1603_R70 0x70
+#define VT1603_R71 0x71
+#define VT1603_R72 0x72
+#define VT1603_R73 0x73
+#define VT1603_R77 0x77
+#define VT1603_R79 0x79
+#define VT1603_R7a 0x7a
+#define VT1603_R7b 0x7b
+#define VT1603_R7c 0x7c
+#define VT1603_R82 0x82
+#define VT1603_R87 0x87
+#define VT1603_R88 0x88
+#define VT1603_R8a 0x8a
+#define VT1603_R8e 0x8e
+#define VT1603_R90 0x90
+#define VT1603_R91 0x91
+#define VT1603_R92 0x92
+#define VT1603_R93 0x93
+#define VT1603_R95 0x95
+#define VT1603_R96 0x96
+#define VT1603_R97 0x97
+
+#endif
diff --git a/ANDROID_3.4.5/sound/soc/codecs/wm8994.c b/ANDROID_3.4.5/sound/soc/codecs/wm8994.c
index f351b933..ab30c796 100644
--- a/ANDROID_3.4.5/sound/soc/codecs/wm8994.c
+++ b/ANDROID_3.4.5/sound/soc/codecs/wm8994.c
@@ -30,6 +30,8 @@
#include <sound/tlv.h>
#include <trace/events/asoc.h>
+#include <linux/proc_fs.h>
+
#include <linux/mfd/wm8994/core.h>
#include <linux/mfd/wm8994/registers.h>
#include <linux/mfd/wm8994/pdata.h>
@@ -46,6 +48,9 @@
#define WM8994_NUM_DRC 3
#define WM8994_NUM_EQ 3
+static int aif1clk_on = 1;
+static struct proc_dir_entry *wm8994_proc;
+
static struct {
unsigned int reg;
unsigned int mask;
@@ -493,6 +498,10 @@ static const char *aif_chan_src_text[] = {
"Left", "Right"
};
+static const char *aif_mono_text[] = {
+ "Stereo", "Mono"
+};
+
static const struct soc_enum aif1adcl_src =
SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_1, 15, 2, aif_chan_src_text);
@@ -517,6 +526,9 @@ static const struct soc_enum aif2dacl_src =
static const struct soc_enum aif2dacr_src =
SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 14, 2, aif_chan_src_text);
+static const struct soc_enum aif2dac_mono =
+ SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 8, 2, aif_mono_text);
+
static const char *osr_text[] = {
"Low Power", "High Performance",
};
@@ -527,6 +539,16 @@ static const struct soc_enum dac_osr =
static const struct soc_enum adc_osr =
SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 1, 2, osr_text);
+static const char *compand_mode_text[] = {
+ "u-law", "a-law",
+};
+
+static const struct soc_enum aif2dac_compand_mode =
+ SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 3, 2, compand_mode_text);
+
+static const struct soc_enum aif2adc_compand_mode =
+ SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 1, 2, compand_mode_text);
+
static const struct snd_kcontrol_new wm8994_snd_controls[] = {
SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
WM8994_AIF1_ADC1_RIGHT_VOLUME,
@@ -548,6 +570,8 @@ SOC_ENUM("AIF1DACR Source", aif1dacr_src),
SOC_ENUM("AIF2DACL Source", aif2dacl_src),
SOC_ENUM("AIF2DACR Source", aif2dacr_src),
+SOC_ENUM("AIF2DAC Mono", aif2dac_mono),
+
SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8994_AIF1_DAC1_LEFT_VOLUME,
WM8994_AIF1_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8994_AIF1_DAC2_LEFT_VOLUME,
@@ -629,6 +653,15 @@ SOC_SINGLE_TLV("AIF2DAC 3D Stereo Volume", WM8994_AIF2_DAC_FILTERS_2,
10, 15, 0, wm8994_3d_tlv),
SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF2_DAC_FILTERS_2,
8, 1, 0),
+
+SOC_SINGLE("MICBIAS2 Switch", WM8994_POWER_MANAGEMENT_1, 5, 1, 0),
+SOC_SINGLE("MICBIAS1 Switch", WM8994_POWER_MANAGEMENT_1, 4, 1, 0),
+
+SOC_SINGLE("AIF2DAC Compand Switch", WM8994_AIF2_CONTROL_2, 4, 1, 0),
+SOC_SINGLE("AIF2ADC Compand Switch", WM8994_AIF2_CONTROL_2, 2, 1, 0),
+
+SOC_ENUM("AIF2DAC Compand Mode", aif2dac_compand_mode),
+SOC_ENUM("AIF2ADC Compand Mode", aif2adc_compand_mode),
};
static const struct snd_kcontrol_new wm8994_eq_controls[] = {
@@ -1161,9 +1194,9 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
WM8994_AIF2ADCL_ENA |
WM8994_AIF2ADCR_ENA, adc);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ /*snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
WM8994_AIF2DACL_ENA |
- WM8994_AIF2DACR_ENA, dac);
+ WM8994_AIF2DACR_ENA, dac);*/
snd_soc_update_bits(codec, WM8994_CLOCKING_1,
WM8994_AIF2DSPCLK_ENA |
WM8994_SYSDSPCLK_ENA,
@@ -1174,11 +1207,11 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
WM8994_AIF2ADCR_ENA,
WM8994_AIF2ADCL_ENA |
WM8994_AIF2ADCR_ENA);
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ /*snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
WM8994_AIF2DACL_ENA |
WM8994_AIF2DACR_ENA,
WM8994_AIF2DACL_ENA |
- WM8994_AIF2DACR_ENA);
+ WM8994_AIF2DACR_ENA);*/
break;
case SND_SOC_DAPM_POST_PMU:
@@ -1190,9 +1223,9 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_PRE_PMD:
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ /*snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
WM8994_AIF2DACL_ENA |
- WM8994_AIF2DACR_ENA, 0);
+ WM8994_AIF2DACR_ENA, 0);*/
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
WM8994_AIF2ADCL_ENA |
WM8994_AIF2ADCR_ENA, 0);
@@ -1289,10 +1322,14 @@ static int late_disable_ev(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMD:
if (wm8994->aif1clk_disable) {
- aif1clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMD);
- snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
+
+ aif1clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMD);
+ if (!aif1clk_on){
+ snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
WM8994_AIF1CLK_ENA_MASK, 0);
- aif1clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMD);
+ }
+ aif1clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMD);
+
wm8994->aif1clk_disable = 0;
}
if (wm8994->aif2clk_disable) {
@@ -1917,6 +1954,10 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" },
{ "AIF2ADC Mux", "AIF3DACDAT", "AIF3ADCDAT" },
+ { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADCDAT" },
+ { "AIF3ADC Mux", "AIF2ADCDAT", "AIF2ADCDAT" },
+ { "AIF3ADC Mux", "AIF2DACDAT", "AIF2ADCDAT" },
+
/* DAC1 inputs */
{ "DAC1L Mixer", "AIF2 Switch", "AIF2DACL" },
{ "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" },
@@ -2695,7 +2736,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- bclk_rate = params_rate(params) * 2;
+ bclk_rate = params_rate(params) * 4;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
bclk_rate *= 16;
@@ -2780,7 +2821,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream,
lrclk, bclk_rate / lrclk);
snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
- snd_soc_update_bits(codec, aif2_reg, WM8994_AIF1_MONO, aif2);
+ //snd_soc_update_bits(codec, aif2_reg, WM8994_AIF1_MONO, aif2);
snd_soc_update_bits(codec, bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk);
snd_soc_update_bits(codec, lrclk_reg, WM8994_AIF1DAC_RATE_MASK,
lrclk);
@@ -3053,6 +3094,11 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec)
switch (control->type) {
case WM8994:
snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, 0);
+ if (aif1clk_on){
+ snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
+ WM8994_AIF1CLK_ENA_MASK, 0);
+ //printk("<<<<%s\n", __FUNCTION__);
+ } //add 2013-7-11
break;
case WM1811:
snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
@@ -3779,6 +3825,43 @@ static irqreturn_t wm8994_temp_shut(int irq, void *data)
return IRQ_HANDLED;
}
+static int wm8994_readproc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+#define WM8994_CACHE_SIZE 1570
+
+ int ret;
+ unsigned short reg;
+ struct wm8994_priv *wm8994 = data;
+
+ printk(KERN_INFO "\nwm8994 regs dump:\n");
+ printk(KERN_INFO "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+ for (reg = 0; reg < WM8994_CACHE_SIZE; reg++) {
+ ret = wm8994_reg_read(wm8994->wm8994, reg);
+ if (ret < 0) {
+ printk(KERN_INFO "\nwm8994_reg_read reg[%04x] error\n", reg);
+ goto out;
+ }
+ if (reg%4 == 0) printk("\n");
+ printk("reg[%04x]=%04x ", reg, ret);
+ }
+
+ printk("\n\nGPIO Pin settings:\n");
+ for (reg = WM8994_GPIO_1; reg < WM8994_GPIO_1+11; reg++) {
+ ret = wm8994_reg_read(wm8994->wm8994, reg);
+ if (ret < 0) {
+ printk(KERN_INFO "\nwm8994_reg_read reg[%04x] error\n", reg);
+ goto out;
+ }
+ if (reg%4 == 0) printk("\n");
+ printk("reg[%04x]=%04x ", reg, ret);
+ }
+
+out:
+ printk(KERN_INFO "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n");
+ return 0;
+}
+
static int wm8994_codec_probe(struct snd_soc_codec *codec)
{
struct wm8994 *control = dev_get_drvdata(codec->dev->parent);
@@ -4145,6 +4228,15 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break;
}
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+ WM8994_AIF2DACL_ENA |
+ WM8994_AIF2DACR_ENA,
+ WM8994_AIF2DACL_ENA |
+ WM8994_AIF2DACR_ENA);
+
+ wm8994_proc = create_proc_read_entry("wm8994_dumpregs", 0666, NULL,
+ wm8994_readproc, wm8994);
+
return 0;
err_irq:
diff --git a/ANDROID_3.4.5/sound/soc/codecs/wmt_fm34.c b/ANDROID_3.4.5/sound/soc/codecs/wmt_fm34.c
new file mode 100755
index 00000000..cee4dce9
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/codecs/wmt_fm34.c
@@ -0,0 +1,830 @@
+/*++
+ * linux/sound/soc/codecs/wmt_fm34.c
+ * WonderMedia echo cancellation driver
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/kdev_t.h>
+#include <asm/page.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+
+#define FM34_DEVID 0x501a
+#define FM34_I2C_BUS_NR 4
+
+#define FM34_CTRL_NAME "wmt_echo"
+static int fm34_ctrl_major = 0;
+static struct cdev fm34_ctrl_dev;
+static struct class *fm34_ctrl_class;
+
+// FM34 control interface commands
+#define FM34_CMD_ECHO _IOWR('E', 0x01, int)
+#define FM34_CMD_BYPASS _IOWR('E', 0x02, int)
+#define FM34_CMD_INCALL_MAIN_MIC _IOWR('E', 0x04, int)
+#define FM34_CMD_INCALL_HEADSET_MIC _IOWR('E', 0x08, int)
+#define FM34_CMD_CAPTURE_MAIN_MIC _IOWR('E', 0x10, int)
+#define FM34_CMD_CAPTURE_HEADSET_MIC _IOWR('E', 0x20, int)
+
+enum FM34_MODE_TYPE {
+ FM34_MODE_ECHO,
+ FM34_MODE_BYPASS,
+ FM34_MODE_MAX,
+};
+
+enum FM34_MIC_SOURCE {
+ FM34_INCALL_MIC_MAIN,
+ FM34_INCALL_MIC_HEADSET,
+ FM34_CAPTURE_MIC_MAIN,
+ FM34_CAPTURE_MIC_HEADSET,
+ FM34_MIC_MAX,
+};
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+struct fm34_ctrl_gpo {
+ int enable;
+ int gpio;
+ int active;
+};
+
+static struct fm34_ctrl_gpo fm34_reset;
+static struct fm34_ctrl_gpo fm34_powerdown;
+static struct fm34_ctrl_gpo fm34_bypass;
+
+struct fm34 {
+ int (*read_dev)(struct fm34 *fm34,
+ const u8 wdata[], int wsize,
+ u8 rdata[], int rsize);
+ int (*write_dev)(struct fm34 *fm34, const u8 wdata[], int wsize);
+ void *control_data;
+ struct device *dev;
+ struct delayed_work delaywork;
+};
+
+static struct fm34 *fm34_context;
+static enum FM34_MODE_TYPE fm_mode = FM34_MODE_MAX;
+static enum FM34_MIC_SOURCE fm_mic = FM34_MIC_MAX;
+
+static const u8 wmt_fm34_incall_main_mic[] = {
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x2C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x12, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF2, 0x00, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x05,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x20, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x22, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x01, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x02, 0x01, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x09, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x03, 0x1E,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0xF0, 0xF0,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1B, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x06, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x0A, 0x0A,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x0F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x22, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x12, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x77, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7B, 0x00, 0x08,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x86, 0x48, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8C, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x60, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9D, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9E, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCE, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x0A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x05, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x05, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD5, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x85, 0x00, 0x05,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8E, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x90, 0xA9, 0x87,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x76, 0x54,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x44, 0x55,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x94, 0x55, 0x66,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x95, 0x66, 0x77,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x96, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x97, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x98, 0x00, 0x20,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xA5, 0x00, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xA8, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xA9, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAA, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAB, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAC, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAD, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAE, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAF, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB0, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB1, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB3, 0x00, 0x20,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB4, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x48, 0x90,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x01, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x00, 0x30,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x37, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+};
+
+static const u8 wmt_fm34_incall_headset_mic[] = {
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x2C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x12, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF2, 0x00, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x05,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x20, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x22, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x01, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x02, 0x01, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x09, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x03, 0x1E,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0xF0, 0xF0,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x06, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x0A, 0x0A,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x0F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x22, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x12, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x77, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7B, 0x00, 0x08,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x86, 0x48, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8C, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x60, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9D, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9E, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCE, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x06, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x05, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x05, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD5, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x85, 0x00, 0x05,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8E, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x90, 0xA9, 0x87,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x76, 0x54,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x44, 0x55,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x94, 0x55, 0x66,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x95, 0x66, 0x77,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x96, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x97, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x98, 0x00, 0x20,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xA5, 0x00, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xA8, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xA9, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAA, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAB, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAC, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAD, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAE, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xAF, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB0, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB1, 0x7F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB3, 0x00, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB4, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x48, 0x90,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x01, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x00, 0x30,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x37, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+};
+
+static const u8 wmt_fm34_capture_main_mic[] = {
+ 0xFC, 0xF3, 0x3B, 0x22, 0x9F, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x2C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF2, 0x00, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x67, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x23, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x01, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x02, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x29, 0x88,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x20, 0x0E,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x0C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1B, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x0B, 0x05,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x0C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x21, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7B, 0x00, 0x08,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x05, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x03, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x86, 0x48, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8C, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x90, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCE, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x12, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x07, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x05, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD5, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x0A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x28, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF1, 0xD0, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+};
+
+static const u8 wmt_fm34_capture_headset_mic[] = {
+ 0xFC, 0xF3, 0x3B, 0x22, 0x9F, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x2C,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF2, 0x00, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x67, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x23, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x01, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x02, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x29, 0x88,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x20, 0x0E,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x0C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x0B, 0x05,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x0C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x21, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7B, 0x00, 0x08,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x05, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x03, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x86, 0x48, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8C, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x90, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCE, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x12, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x07, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x05, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD5, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x0A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x28, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF1, 0xD0, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+};
+
+static int fm34_check_functionality(struct fm34 *fm34)
+{
+ u8 rdata1, rdata2;
+ int devid = 0;
+ const u8 wdata1[] = {
+ 0xFC, 0xF3, 0x27, 0x37, 0xFF
+ };
+ const u8 wdata2[] = {
+ 0xFC, 0xF3, 0x60, 0x25
+ };
+ const u8 wdata3[] = {
+ 0xFC, 0xF3, 0x60, 0x26
+ };
+
+ if (fm34->write_dev(fm34, wdata1, sizeof(wdata1)))
+ return 0;
+
+ if (fm34->read_dev(fm34, wdata2, sizeof(wdata2), &rdata1, 1) ||
+ fm34->read_dev(fm34, wdata3, sizeof(wdata3), &rdata2, 1))
+ return 0;
+
+ devid = (rdata2 << 8) | rdata1;
+ printk("-->fm34_check_functionality: %x\n", devid);
+ if (devid == FM34_DEVID)
+ return 1;
+
+ return 0;
+}
+
+static int fm34_i2c_write(struct fm34 *fm34, const u8 wdata[], int wsize)
+{
+ int ret;
+ struct i2c_client *i2c = fm34->control_data;
+ struct i2c_msg xfer[1];
+
+ /*
+ * [MSG1]: fill the register address data
+ * fill the data Tx buffer
+ */
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0 ;
+ xfer[0].flags &= ~(I2C_M_RD);
+ xfer[0].buf = (__u8 *)wdata;
+ xfer[0].len = wsize;
+
+ /* i2c_transfer returns number of messages transferred */
+ ret = i2c_transfer(i2c->adapter, xfer, ARRAY_SIZE(xfer));
+ if (ret != ARRAY_SIZE(xfer)) {
+ pr_err("fm34_i2c_write err[%d]\n", ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+ } else
+ return 0;
+}
+
+static int fm34_i2c_read(struct fm34 *fm34,
+ const u8 wdata[], int wsize,
+ u8 rdata[], int rsize)
+{
+ int ret;
+ struct i2c_client *i2c = fm34->control_data;
+ struct i2c_msg xfer[2];
+
+ /* [MSG1] fill the register address data */
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 2; /* Read the register val */
+ xfer[0].buf = (__u8 *)wdata;
+ xfer[0].len = wsize;
+
+ /* [MSG2] fill the data rx buffer */
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD; /* Read the register val */
+ xfer[1].len = rsize; /* only n bytes */
+ xfer[1].buf = rdata;
+
+ /* i2c_transfer returns number of messages transferred */
+ ret = i2c_transfer(i2c->adapter, xfer, ARRAY_SIZE(xfer));
+ if (ret != ARRAY_SIZE(xfer)) {
+ pr_err("vt1603_i2c_read err[%d]\n", ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+ } else {
+ return 0;
+ }
+}
+
+static void fm34_timing_init(void)
+{
+ // pwdn high to power up fm34 module
+ if (fm34_powerdown.enable) {
+ gpio_set_value_cansleep(fm34_powerdown.gpio, !!fm34_powerdown.active);
+ msleep(100);
+ }
+
+ // reset fm34 module
+ if (fm34_reset.enable) {
+ gpio_set_value_cansleep(fm34_reset.gpio, !!fm34_reset.active);
+ msleep(10);
+ gpio_set_value_cansleep(fm34_reset.gpio, !fm34_reset.active);
+ msleep(10);
+ gpio_set_value_cansleep(fm34_reset.gpio, !!fm34_reset.active);
+ msleep(100);
+ }
+
+ // disable fm34 bypass
+ if (fm34_bypass.enable) {
+ gpio_set_value_cansleep(fm34_bypass.gpio, !!fm34_bypass.active);
+ }
+}
+
+static void fm34_do_work(struct work_struct *work)
+{
+ struct fm34 *fm34 = container_of(work, struct fm34, delaywork.work);
+
+ fm34_timing_init();
+
+ if (fm34_check_functionality(fm34) == 0) {
+ pr_err("fm34_check_functionality: wrong device id\n");
+ }
+
+ fm34->write_dev(fm34, wmt_fm34_incall_main_mic, sizeof(wmt_fm34_incall_main_mic));
+ //fm_mode = FM34_MODE_ECHO;
+ fm_mic = FM34_INCALL_MIC_MAIN;
+}
+
+static int __devinit fm34_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct fm34 *fm34;
+ int ret;
+
+ if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C) == 0) {
+ pr_err("i2c_check_functionality: can't talk I2C?\n");
+ return -EIO;
+ }
+
+ fm34 = kzalloc(sizeof(struct fm34), GFP_KERNEL);
+ if (fm34 == NULL)
+ return -ENOMEM;
+
+ fm34->dev = &i2c->dev;
+ fm34->control_data = i2c;
+ fm34->read_dev = fm34_i2c_read;
+ fm34->write_dev = fm34_i2c_write;
+
+ i2c_set_clientdata(i2c, fm34);
+ dev_set_drvdata(fm34->dev, fm34);
+
+ INIT_DELAYED_WORK(&fm34->delaywork, fm34_do_work);
+
+ if (fm34_powerdown.enable && gpio_is_valid(fm34_powerdown.gpio)) {
+ ret = gpio_request(fm34_powerdown.gpio, "FM34-powerdown");
+ if (ret < 0){
+ dev_warn(fm34->dev, "cann't register fm34_powerdown.gpio-%d\n", fm34_powerdown.gpio);
+ fm34_powerdown.enable = 0;
+ }
+ else
+ gpio_direction_output(fm34_powerdown.gpio, 0);
+ }
+
+ if (fm34_reset.enable && gpio_is_valid(fm34_reset.gpio)) {
+ ret = gpio_request(fm34_reset.gpio, "FM34-reset");
+ if (ret < 0) {
+ dev_warn(fm34->dev, "cann't register fm34_reset.gpio-%d\n", fm34_reset.gpio);
+ fm34_reset.enable = 0;
+ }
+ else
+ gpio_direction_output(fm34_reset.gpio, 0);
+ }
+
+ if (fm34_bypass.enable && gpio_is_valid(fm34_bypass.gpio)) {
+ ret = gpio_request(fm34_bypass.gpio, "FM34-bypass");
+ if (ret < 0) {
+ dev_warn(fm34->dev, "cann't register fm34_bypass.gpio-%d\n", fm34_bypass.gpio);
+ fm34_bypass.enable = 0;
+ }
+ else
+ gpio_direction_output(fm34_bypass.gpio, 0);
+ }
+
+ fm34_timing_init();
+
+ if (fm34_check_functionality(fm34) == 0) {
+ pr_err("fm34_check_functionality: wrong device id\n");
+ return -EIO;
+ }
+
+ fm34_context = fm34;
+
+ /* write the firmware to device */
+ fm34->write_dev(fm34, wmt_fm34_incall_main_mic, sizeof(wmt_fm34_incall_main_mic));
+ fm_mode = FM34_MODE_ECHO; // default is echo mode
+ fm_mic = FM34_INCALL_MIC_MAIN;
+ return 0;
+}
+
+static int __devexit fm34_i2c_remove(struct i2c_client *i2c)
+{
+ struct fm34 *fm34 = i2c_get_clientdata(i2c);
+
+ if (fm34_powerdown.enable) {
+ gpio_free(fm34_powerdown.gpio);
+ }
+ if (fm34_reset.enable) {
+ gpio_free(fm34_reset.gpio);
+ }
+ if (fm34_bypass.enable) {
+ gpio_free(fm34_bypass.gpio);
+ }
+
+ kfree(fm34);
+ return 0;
+}
+
+static int fm34_suspend(struct i2c_client *i2c, pm_message_t mesg)
+{
+ struct fm34 *fm34 = i2c_get_clientdata(i2c);
+ cancel_delayed_work_sync(&fm34->delaywork);
+ return 0;
+}
+static int fm34_resume(struct i2c_client *i2c)
+{
+ struct fm34 *fm34 = i2c_get_clientdata(i2c);
+ schedule_delayed_work(&fm34->delaywork, msecs_to_jiffies(2000));
+ return 0;
+}
+
+static const struct i2c_device_id fm34_i2c_id[] = {
+ { "wmt-fm34", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, fm34_i2c_id);
+
+static struct i2c_driver fm34_i2c_driver = {
+ .driver = {
+ .name = "wmt-fm34",
+ .owner = THIS_MODULE,
+ },
+ .probe = fm34_i2c_probe,
+ .remove = __devexit_p(fm34_i2c_remove),
+ .suspend = fm34_suspend,
+ .resume = fm34_resume,
+ .id_table = fm34_i2c_id,
+};
+
+static struct i2c_board_info fm34_i2c_board_info = {
+ I2C_BOARD_INFO("wmt-fm34", 0xc0>>1),
+};
+
+static int fm34_ctrl_open (struct inode *inode, struct file *filp)
+{
+ printk("%s %d\n", __func__, __LINE__);
+ return 0;
+}
+
+static int fm34_ctrl_release(struct inode *inode, struct file *filp)
+{
+ printk("%s %d\n", __func__, __LINE__);
+ return 0;
+}
+
+static ssize_t fm34_ctrl_read(struct file *filp, char __user *buf, size_t size, loff_t *offp)
+{
+ printk("%s %d\n", __func__, __LINE__);
+ return size;
+}
+
+static long fm34_ctrl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ const u8 echo[] = { 0xFC, 0xF3, 0x3B, 0x23, 0x00, 0x00, 0x00 };
+ const u8 bypass[] = { 0xFC, 0xF3, 0x3B, 0x23, 0x00, 0x00, 0x01 };
+ //const u8 main_mic[] = { 0xFC, 0xF3, 0x3B, 0x23, 0x0a, 0x1a, 0x00 };
+ //const u8 headset_mic[] = { 0xFC, 0xF3, 0x3B, 0x23, 0x0a, 0x1b, 0x00 };
+
+ switch (cmd) {
+ case FM34_CMD_ECHO:
+ if (fm_mode != FM34_MODE_ECHO) {
+ printk("FM34 cmd: echo\n");
+ if (fm34_context->write_dev(fm34_context, echo, sizeof(echo)))
+ return -EIO;
+ fm_mode = FM34_MODE_ECHO;
+ }
+ break;
+
+ case FM34_CMD_BYPASS:
+ if (fm_mode != FM34_MODE_BYPASS) {
+ printk("FM34 cmd: bypass\n");
+ if (fm34_context->write_dev(fm34_context, bypass, sizeof(bypass)))
+ return -EIO;
+ fm_mode = FM34_MODE_BYPASS;
+ }
+ break;
+
+ case FM34_CMD_INCALL_MAIN_MIC:
+ if (fm_mic != FM34_INCALL_MIC_MAIN) {
+ printk("FM34 cmd: incall main-mic\n");
+ if (fm34_reset.enable) {
+ gpio_set_value_cansleep(fm34_reset.gpio, !fm34_reset.active);
+ msleep(20);
+ gpio_set_value_cansleep(fm34_reset.gpio, !!fm34_reset.active);
+ msleep(20);
+ }
+ if (fm34_context->write_dev(fm34_context, wmt_fm34_incall_main_mic, sizeof(wmt_fm34_incall_main_mic)))
+ return -EIO;
+ fm_mic = FM34_INCALL_MIC_MAIN;
+ }
+ break;
+
+ case FM34_CMD_INCALL_HEADSET_MIC:
+ if (fm_mic != FM34_INCALL_MIC_HEADSET) {
+ printk("FM34 cmd: incall headset-mic\n");
+ if (fm34_reset.enable) {
+ gpio_set_value_cansleep(fm34_reset.gpio, !fm34_reset.active);
+ msleep(20);
+ gpio_set_value_cansleep(fm34_reset.gpio, !!fm34_reset.active);
+ msleep(20);
+ }
+ if (fm34_context->write_dev(fm34_context, wmt_fm34_incall_headset_mic, sizeof(wmt_fm34_incall_headset_mic)))
+ return -EIO;
+ fm_mic = FM34_INCALL_MIC_HEADSET;
+ }
+ break;
+
+ case FM34_CMD_CAPTURE_MAIN_MIC:
+ if (fm_mic != FM34_CMD_CAPTURE_MAIN_MIC) {
+ printk("FM34 cmd: capture main-mic\n");
+ if (fm34_reset.enable) {
+ gpio_set_value_cansleep(fm34_reset.gpio, !fm34_reset.active);
+ msleep(20);
+ gpio_set_value_cansleep(fm34_reset.gpio, !!fm34_reset.active);
+ msleep(20);
+ }
+ if (fm34_context->write_dev(fm34_context, wmt_fm34_capture_main_mic, sizeof(wmt_fm34_capture_main_mic)))
+ return -EIO;
+ fm_mic = FM34_CMD_CAPTURE_MAIN_MIC;
+ }
+ break;
+
+ case FM34_CMD_CAPTURE_HEADSET_MIC:
+ if (fm_mic != FM34_CMD_CAPTURE_HEADSET_MIC) {
+ printk("FM34 cmd: capture headset-mic\n");
+ if (fm34_reset.enable) {
+ gpio_set_value_cansleep(fm34_reset.gpio, !fm34_reset.active);
+ msleep(20);
+ gpio_set_value_cansleep(fm34_reset.gpio, !!fm34_reset.active);
+ msleep(20);
+ }
+ if (fm34_context->write_dev(fm34_context, wmt_fm34_capture_headset_mic, sizeof(wmt_fm34_capture_headset_mic)))
+ return -EIO;
+ fm_mic = FM34_CMD_CAPTURE_HEADSET_MIC;
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "FM34: Not support cmd[%d]\n", cmd);
+ break;
+ }
+
+ return 0;
+}
+
+static struct file_operations fm34_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .open = fm34_ctrl_open,
+ .release = fm34_ctrl_release,
+ .read = fm34_ctrl_read,
+ .unlocked_ioctl = fm34_ctrl_ioctl,
+};
+
+static int fm34_ctrl_setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
+{
+ int ret, devno = MKDEV(fm34_ctrl_major, minor);
+ cdev_init(dev, fops);
+ dev->owner = THIS_MODULE;
+ dev->ops = fops;
+ ret = cdev_add(dev, devno, 1);
+ if (ret)
+ printk(KERN_ERR "Error %d adding wmt_echo %d", ret, minor);
+ return ret;
+}
+
+static int __init fm34_init(void)
+{
+ int ret = -1;
+ int len = 64;
+ char buf[64];
+ dev_t dev;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+
+ ret = wmt_getsyspara("wmt.echo.cancellation", buf, &len);
+ if (ret == 0) {
+ sscanf(buf, "fm34:[%d:%d:%d]:[%d:%d:%d]:[%d:%d:%d]",
+ &fm34_powerdown.enable, &fm34_powerdown.gpio, &fm34_powerdown.active,
+ &fm34_reset.enable, &fm34_reset.gpio, &fm34_reset.active,
+ &fm34_bypass.enable, &fm34_bypass.gpio, &fm34_bypass.active);
+ }
+ else {
+ fm34_reset.enable = 0;
+ fm34_powerdown.enable = 0;
+ fm34_bypass.enable = 0;
+ }
+
+ ret = alloc_chrdev_region(&dev, 0, 1, FM34_CTRL_NAME);
+ if (ret == 0)
+ fm34_ctrl_major = MAJOR(dev);
+ else
+ printk(KERN_ERR "fm34: alloc_chrdev_region failed!\n");
+
+ ret = fm34_ctrl_setup_cdev(&fm34_ctrl_dev, 0, &fm34_ctrl_ops);
+ if (ret == 0) {
+ fm34_ctrl_class = class_create(THIS_MODULE, FM34_CTRL_NAME);
+ if (IS_ERR(fm34_ctrl_class)) {
+ printk("fm34: class_create failed!\n");
+ }
+ else
+ device_create(fm34_ctrl_class, NULL, MKDEV(fm34_ctrl_major, 0), NULL, FM34_CTRL_NAME);
+ }
+
+ adapter = i2c_get_adapter(FM34_I2C_BUS_NR);
+ client = i2c_new_device(adapter, &fm34_i2c_board_info);
+ i2c_put_adapter(adapter);
+
+ ret = i2c_add_driver(&fm34_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register FM34 I2C driver: %d\n", ret);
+ return ret;
+}
+module_init(fm34_init);
+
+static void __exit fm34_exit(void)
+{
+ cdev_del(&fm34_ctrl_dev);
+ device_destroy(fm34_ctrl_class, MKDEV(fm34_ctrl_major, 0));
+ class_destroy(fm34_ctrl_class);
+ unregister_chrdev_region(MKDEV(fm34_ctrl_major, 0), 1);
+
+ i2c_del_driver(&fm34_i2c_driver);
+}
+module_exit(fm34_exit);
+
+MODULE_DESCRIPTION("WMT [ALSA SoC/echocancellation] driver");
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/ANDROID_3.4.5/sound/soc/codecs/wmt_hwdac.c b/ANDROID_3.4.5/sound/soc/codecs/wmt_hwdac.c
new file mode 100755
index 00000000..ae31bb5e
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/codecs/wmt_hwdac.c
@@ -0,0 +1,195 @@
+/*++
+ * linux/sound/soc/codecs/wmt_hwdac.c
+ * WonderMedia I2S audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+
+/*
+ * Debug
+ */
+#define AUDIO_NAME "HW_DAC"
+//#define WMT_HWDAC_DEBUG 1
+//#define WMT_HWDAC_DEBUG_DETAIL 1
+
+#ifdef WMT_HWDAC_DEBUG
+#define DPRINTK(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#else
+#define DPRINTK(format, arg...) do {} while (0)
+#endif
+
+#ifdef WMT_HWDAC_DEBUG_DETAIL
+#define DBG_DETAIL(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": [%s]" format "\n" , __FUNCTION__, ## arg)
+#else
+#define DBG_DETAIL(format, arg...) do {} while (0)
+#endif
+
+#define err(format, arg...) \
+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) \
+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+
+static int hwdac_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ DBG_DETAIL();
+
+ return 0;
+}
+
+
+#define HWDAC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+#define HWDAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_FLOAT)
+
+static struct snd_soc_dai_ops hwdac_dai_ops = {
+ .hw_params = hwdac_pcm_hw_params,
+};
+
+struct snd_soc_dai_driver hwdac_dai = {
+ .name = "HWDAC",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = HWDAC_RATES,
+ .formats = HWDAC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = HWDAC_RATES,
+ .formats = HWDAC_FORMATS,
+ },
+ .ops = &hwdac_dai_ops,
+};
+
+static int hwdac_probe(struct snd_soc_codec *codec)
+{
+ DBG_DETAIL();
+
+ return 0;
+}
+
+/* remove everything here */
+static int hwdac_remove(struct snd_soc_codec *codec)
+{
+ DBG_DETAIL();
+
+ return 0;
+}
+
+struct snd_soc_codec_driver soc_codec_dev_hwdac = {
+ .probe = hwdac_probe,
+ .remove = hwdac_remove,
+};
+
+static int __devinit hwdac_platform_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ DBG_DETAIL();
+
+ ret = snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_hwdac, &hwdac_dai, 1);
+
+ if (ret)
+ info("snd_soc_register_codec fail");
+
+ return ret;
+}
+
+static int __devexit hwdac_platform_remove(struct platform_device *pdev)
+{
+ DBG_DETAIL();
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver wmt_hwdac_driver = {
+ .driver = {
+ .name = "wmt-i2s-hwdac",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = hwdac_platform_probe,
+ .remove = __devexit_p(hwdac_platform_remove),
+};
+
+static int __init wmt_hwdac_platform_init(void)
+{
+ char buf[80];
+ int varlen = 80;
+ char codec_name[5];
+ int ret = 0;
+
+ DBG_DETAIL();
+
+ /*ret = wmt_getsyspara("wmt.audio.i2s", buf, &varlen);
+
+ if (ret == 0) {
+ sscanf(buf, "%5s", codec_name);
+
+ if (strcmp(codec_name, "hwdac")) {
+ info("hwdac string not found");
+ return -EINVAL;
+ }
+ }*/
+
+ return platform_driver_register(&wmt_hwdac_driver);
+}
+module_init(wmt_hwdac_platform_init);
+
+static void __exit wmt_hwdac_platform_exit(void)
+{
+ DBG_DETAIL();
+
+ platform_driver_unregister(&wmt_hwdac_driver);
+}
+module_exit(wmt_hwdac_platform_exit);
+
+MODULE_DESCRIPTION("WMT [ALSA SoC] driver");
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/ANDROID_3.4.5/sound/soc/codecs/wmt_vt1602.c b/ANDROID_3.4.5/sound/soc/codecs/wmt_vt1602.c
new file mode 100755
index 00000000..fd625c95
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/codecs/wmt_vt1602.c
@@ -0,0 +1,981 @@
+/*++
+ * linux/sound/soc/codecs/wmt_vt1602.c
+ * WonderMedia I2S audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wmt_vt1602.h"
+
+#define VT1602_VERSION "0.20"
+#define I2C_DRIVERID_VT1602 90
+
+/*
+ * Debug
+ */
+#define AUDIO_NAME "VT1602"
+//#define WMT_VT1602_DEBUG 1
+//#define WMT_VT1602_DEBUG_DETAIL 1
+
+#ifdef WMT_VT1602_DEBUG
+#define DPRINTK(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#else
+#define DPRINTK(format, arg...) do {} while (0)
+#endif
+
+#ifdef WMT_VT1602_DEBUG_DETAIL
+#define DBG_DETAIL(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": [%s]" format "\n" , __FUNCTION__, ## arg)
+#else
+#define DBG_DETAIL(format, arg...) do {} while (0)
+#endif
+
+#define err(format, arg...) \
+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) \
+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
+
+/* codec private data */
+struct vt1602_priv {
+ unsigned int sysclk;
+ enum snd_soc_control_type control_type;
+ struct snd_pcm_substream *master_substream;
+ struct snd_pcm_substream *slave_substream;
+};
+
+/*
+ * vt1602 register cache
+ * We can't read the VT1602 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const u16 vt1602_reg[] = {
+ 0x0050, 0x0050, 0x007f, 0x007f, /* 0 */
+ 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */
+ 0x0000, 0x0000, 0x00fc, 0x00fc, /* 8 */
+ 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */
+ 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */
+ 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */
+ 0x0000, 0x0000, 0x0100, 0x0000, /* 32 */
+ 0x0000, 0x0100, 0x0050, 0x0050, /* 36 */
+ 0x0079, 0x0079, 0x0079, /* 40 */
+};
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+#define vt1602_reset(c) snd_soc_write(c, VT1602_RESET, 0)
+
+/*
+ * VT1602 Controls
+ */
+static const char *vt1602_bass[] = {"Linear Control", "Adaptive Boost"};
+static const char *vt1602_bass_filter[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
+static const char *vt1602_treble[] = {"8kHz", "4kHz"};
+static const char *vt1602_3d_lc[] = {"200Hz", "500Hz"};
+static const char *vt1602_3d_uc[] = {"2.2kHz", "1.5kHz"};
+static const char *vt1602_3d_func[] = {"Capture", "Playback"};
+static const char *vt1602_alc_func[] = {"Off", "Right", "Left", "Stereo"};
+static const char *vt1602_ng_type[] = {"Constant PGA Gain",
+ "Mute ADC Output"};
+static const char *vt1602_line_mux[] = {"Line 1", "Line 2", "Line 3", "PGA",
+ "Differential"};
+static const char *vt1602_pga_sel[] = {"Line 1", "Line 2", "Line 3",
+ "Differential"};
+static const char *vt1602_out3[] = {"VREF", "ROUT1 + Vol", "MonoOut",
+ "ROUT1"};
+static const char *vt1602_diff_sel[] = {"Line 1", "Line 2"};
+static const char *vt1602_adcpol[] = {"Normal", "L Invert", "R Invert",
+ "L + R Invert"};
+static const char *vt1602_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const char *vt1602_mono_mux[] = {"Stereo", "Mono (Left)",
+ "Mono (Right)", "Digital Mono"};
+
+static const struct soc_enum vt1602_enum[] = {
+SOC_ENUM_SINGLE(VT1602_BASS, 7, 2, vt1602_bass),
+SOC_ENUM_SINGLE(VT1602_BASS, 6, 2, vt1602_bass_filter),
+SOC_ENUM_SINGLE(VT1602_TREBLE, 6, 2, vt1602_treble),
+SOC_ENUM_SINGLE(VT1602_3D, 5, 2, vt1602_3d_lc),
+SOC_ENUM_SINGLE(VT1602_3D, 6, 2, vt1602_3d_uc),
+SOC_ENUM_SINGLE(VT1602_3D, 7, 2, vt1602_3d_func),
+SOC_ENUM_SINGLE(VT1602_ALC1, 7, 4, vt1602_alc_func),
+SOC_ENUM_SINGLE(VT1602_NGATE, 1, 2, vt1602_ng_type),
+SOC_ENUM_SINGLE(VT1602_LOUTM1, 0, 5, vt1602_line_mux),
+SOC_ENUM_SINGLE(VT1602_ROUTM1, 0, 5, vt1602_line_mux),
+SOC_ENUM_SINGLE(VT1602_LADCIN, 6, 4, vt1602_pga_sel), /* 10 */
+SOC_ENUM_SINGLE(VT1602_RADCIN, 6, 4, vt1602_pga_sel),
+SOC_ENUM_SINGLE(VT1602_ADCTL2, 7, 4, vt1602_out3),
+SOC_ENUM_SINGLE(VT1602_ADCIN, 8, 2, vt1602_diff_sel),
+SOC_ENUM_SINGLE(VT1602_ADCDAC, 5, 4, vt1602_adcpol),
+SOC_ENUM_SINGLE(VT1602_ADCDAC, 1, 4, vt1602_deemph),
+SOC_ENUM_SINGLE(VT1602_ADCIN, 6, 4, vt1602_mono_mux), /* 16 */
+
+};
+
+static const struct snd_kcontrol_new vt1602_snd_controls[] = {
+
+SOC_DOUBLE_R("Capture Volume", VT1602_LINVOL, VT1602_RINVOL, 0, 63, 0),
+SOC_DOUBLE_R("Capture ZC Switch", VT1602_LINVOL, VT1602_RINVOL, 6, 1, 0),
+SOC_DOUBLE_R("Capture Switch", VT1602_LINVOL, VT1602_RINVOL, 7, 1, 1),
+
+SOC_DOUBLE_R("Headphone Playback ZC Switch", VT1602_LOUT1V,
+ VT1602_ROUT1V, 7, 1, 0),
+SOC_DOUBLE_R("Speaker Playback ZC Switch", VT1602_LOUT2V,
+ VT1602_ROUT2V, 7, 1, 0),
+
+SOC_ENUM("Playback De-emphasis", vt1602_enum[15]),
+
+SOC_ENUM("Capture Polarity", vt1602_enum[14]),
+SOC_SINGLE("Playback 6dB Attenuate", VT1602_ADCDAC, 7, 1, 0),
+SOC_SINGLE("Capture 6dB Attenuate", VT1602_ADCDAC, 8, 1, 0),
+
+SOC_DOUBLE_R("Master Volume", VT1602_LOUT1V, VT1602_ROUT1V, 0, 127, 0),
+//SOC_DOUBLE_R("PCM Volume", VT1602_LOUT1V, VT1602_ROUT1V, 0, 127, 0),
+
+/*
+SOC_ENUM("Bass Boost", vt1602_enum[0]),
+SOC_ENUM("Bass Filter", vt1602_enum[1]),
+SOC_SINGLE("Bass Volume", VT1602_BASS, 0, 15, 1),
+
+SOC_SINGLE("Treble Volume", VT1602_TREBLE, 0, 15, 0),
+SOC_ENUM("Treble Cut-off", vt1602_enum[2]),
+
+SOC_SINGLE("3D Switch", VT1602_3D, 0, 1, 0),
+SOC_SINGLE("3D Volume", VT1602_3D, 1, 15, 0),
+SOC_ENUM("3D Lower Cut-off", vt1602_enum[3]),
+SOC_ENUM("3D Upper Cut-off", vt1602_enum[4]),
+SOC_ENUM("3D Mode", vt1602_enum[5]),
+
+SOC_SINGLE("ALC Capture Target Volume", VT1602_ALC1, 0, 7, 0),
+SOC_SINGLE("ALC Capture Max Volume", VT1602_ALC1, 4, 7, 0),
+SOC_ENUM("ALC Capture Function", vt1602_enum[6]),
+SOC_SINGLE("ALC Capture ZC Switch", VT1602_ALC2, 7, 1, 0),
+SOC_SINGLE("ALC Capture Hold Time", VT1602_ALC2, 0, 15, 0),
+SOC_SINGLE("ALC Capture Decay Time", VT1602_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack Time", VT1602_ALC3, 0, 15, 0),
+SOC_SINGLE("ALC Capture NG Threshold", VT1602_NGATE, 3, 31, 0),
+SOC_ENUM("ALC Capture NG Type", vt1602_enum[4]),
+SOC_SINGLE("ALC Capture NG Switch", VT1602_NGATE, 0, 1, 0),
+*/
+
+SOC_SINGLE("Left ADC Capture Volume", VT1602_LADC, 0, 255, 0),
+SOC_SINGLE("Right ADC Capture Volume", VT1602_RADC, 0, 255, 0),
+
+SOC_SINGLE("ZC Timeout Switch", VT1602_ADCTL1, 0, 1, 0),
+SOC_SINGLE("Playback Invert Switch", VT1602_ADCTL1, 1, 1, 0),
+
+SOC_SINGLE("Right Speaker Playback Invert Switch", VT1602_ADCTL2, 4, 1, 0),
+
+/* Unimplemented */
+/* ADCDAC Bit 0 - ADCHPD */
+/* ADCDAC Bit 4 - HPOR */
+/* ADCTL1 Bit 2,3 - DATSEL */
+/* ADCTL1 Bit 4,5 - DMONOMIX */
+/* ADCTL1 Bit 6,7 - VSEL */
+/* ADCTL2 Bit 2 - LRCM */
+/* ADCTL2 Bit 3 - TRI */
+/* ADCTL3 Bit 5 - HPFLREN */
+/* ADCTL3 Bit 6 - VROI */
+/* ADCTL3 Bit 7,8 - ADCLRM */
+/* ADCIN Bit 4 - LDCM */
+/* ADCIN Bit 5 - RDCM */
+
+SOC_DOUBLE_R("Mic Boost", VT1602_LADCIN, VT1602_RADCIN, 4, 3, 0),
+
+SOC_DOUBLE_R("Bypass Left Playback Volume", VT1602_LOUTM1,
+ VT1602_LOUTM2, 4, 7, 1),
+SOC_DOUBLE_R("Bypass Right Playback Volume", VT1602_ROUTM1,
+ VT1602_ROUTM2, 4, 7, 1),
+SOC_DOUBLE_R("Bypass Mono Playback Volume", VT1602_MOUTM1,
+ VT1602_MOUTM2, 4, 7, 1),
+
+SOC_SINGLE("Mono Playback ZC Switch", VT1602_MOUTV, 7, 1, 0),
+
+SOC_DOUBLE_R("Headphone Playback Volume", VT1602_LOUT1V, VT1602_ROUT1V,
+ 0, 127, 0),
+SOC_DOUBLE_R("Speaker Playback Volume", VT1602_LOUT2V, VT1602_ROUT2V,
+ 0, 127, 0),
+
+SOC_SINGLE("Mono Playback Volume", VT1602_MOUTV, 0, 127, 0),
+
+};
+
+/*
+ * DAPM Controls
+ */
+
+/* Left Mixer */
+static const struct snd_kcontrol_new vt1602_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("Playback Switch", VT1602_LOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", VT1602_LOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", VT1602_LOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", VT1602_LOUTM2, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new vt1602_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", VT1602_ROUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", VT1602_ROUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", VT1602_ROUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", VT1602_ROUTM2, 7, 1, 0),
+};
+
+/* Mono Mixer */
+static const struct snd_kcontrol_new vt1602_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", VT1602_MOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", VT1602_MOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", VT1602_MOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", VT1602_MOUTM2, 7, 1, 0),
+};
+
+/* Left Line Mux */
+static const struct snd_kcontrol_new vt1602_left_line_controls =
+SOC_DAPM_ENUM("Route", vt1602_enum[8]);
+
+/* Right Line Mux */
+static const struct snd_kcontrol_new vt1602_right_line_controls =
+SOC_DAPM_ENUM("Route", vt1602_enum[9]);
+
+/* Left PGA Mux */
+static const struct snd_kcontrol_new vt1602_left_pga_controls =
+SOC_DAPM_ENUM("Route", vt1602_enum[10]);
+
+/* Right PGA Mux */
+static const struct snd_kcontrol_new vt1602_right_pga_controls =
+SOC_DAPM_ENUM("Route", vt1602_enum[11]);
+
+/* Out 3 Mux */
+static const struct snd_kcontrol_new vt1602_out3_controls =
+SOC_DAPM_ENUM("Route", vt1602_enum[12]);
+
+/* Differential Mux */
+static const struct snd_kcontrol_new vt1602_diffmux_controls =
+SOC_DAPM_ENUM("Route", vt1602_enum[13]);
+
+/* Mono ADC Mux */
+static const struct snd_kcontrol_new vt1602_monomux_controls =
+SOC_DAPM_ENUM("Route", vt1602_enum[16]);
+
+static const struct snd_soc_dapm_widget vt1602_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &vt1602_left_mixer_controls[0],
+ ARRAY_SIZE(vt1602_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &vt1602_right_mixer_controls[0],
+ ARRAY_SIZE(vt1602_right_mixer_controls)),
+ /*
+ SND_SOC_DAPM_MIXER("Mono Mixer", VT1602_PWR2, 2, 0,
+ &vt1602_mono_mixer_controls[0],
+ ARRAY_SIZE(vt1602_mono_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right Out 2", VT1602_PWR2, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", VT1602_PWR2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", VT1602_PWR2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", VT1602_PWR2, 6, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", VT1602_PWR2, 7, 0),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", VT1602_PWR2, 8, 0),
+ */
+ /*
+ SND_SOC_DAPM_MICBIAS("Mic Bias", VT1602_PWR1, 1, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", VT1602_PWR1, 2, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", VT1602_PWR1, 3, 0),
+
+ SND_SOC_DAPM_MUX("Left PGA Mux", VT1602_PWR1, 5, 0,
+ &vt1602_left_pga_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", VT1602_PWR1, 4, 0,
+ &vt1602_right_pga_controls),
+ */
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+ &vt1602_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+ &vt1602_right_line_controls),
+
+ SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &vt1602_out3_controls),
+ /*
+ SND_SOC_DAPM_PGA("Out 3", VT1602_PWR2, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out 1", VT1602_PWR2, 2, 0, NULL, 0),
+ */
+
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &vt1602_diffmux_controls),
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+ &vt1602_monomux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+ &vt1602_monomux_controls),
+
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("MONO1"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("VREF"),
+
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("LINPUT2"),
+ SND_SOC_DAPM_INPUT("LINPUT3"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT2"),
+ SND_SOC_DAPM_INPUT("RINPUT3"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* left mixer */
+ {"Left Mixer", "Playback Switch", "Left DAC"},
+ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Left Mixer", "Right Playback Switch", "Right DAC"},
+ {"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* right mixer */
+ {"Right Mixer", "Left Playback Switch", "Left DAC"},
+ {"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Right Mixer", "Playback Switch", "Right DAC"},
+ {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* left out 1 */
+ {"Left Out 1", NULL, "Left Mixer"},
+ {"LOUT1", NULL, "Left Out 1"},
+
+ /* left out 2 */
+ {"Left Out 2", NULL, "Left Mixer"},
+ {"LOUT2", NULL, "Left Out 2"},
+
+ /* right out 1 */
+ {"Right Out 1", NULL, "Right Mixer"},
+ {"ROUT1", NULL, "Right Out 1"},
+
+ /* right out 2 */
+ {"Right Out 2", NULL, "Right Mixer"},
+ {"ROUT2", NULL, "Right Out 2"},
+
+ /* mono mixer */
+ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
+ {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
+ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
+ {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+ /* mono out */
+ {"Mono Out 1", NULL, "Mono Mixer"},
+ {"MONO1", NULL, "Mono Out 1"},
+
+ /* out 3 */
+ {"Out3 Mux", "VREF", "VREF"},
+ {"Out3 Mux", "ROUT1 + Vol", "ROUT1"},
+ {"Out3 Mux", "ROUT1", "Right Mixer"},
+ {"Out3 Mux", "MonoOut", "MONO1"},
+ {"Out 3", NULL, "Out3 Mux"},
+ {"OUT3", NULL, "Out 3"},
+
+ /* Left Line Mux */
+ {"Left Line Mux", "Line 1", "LINPUT1"},
+ {"Left Line Mux", "Line 2", "LINPUT2"},
+ {"Left Line Mux", "Line 3", "LINPUT3"},
+ {"Left Line Mux", "PGA", "Left PGA Mux"},
+ {"Left Line Mux", "Differential", "Differential Mux"},
+
+ /* Right Line Mux */
+ {"Right Line Mux", "Line 1", "RINPUT1"},
+ {"Right Line Mux", "Line 2", "RINPUT2"},
+ {"Right Line Mux", "Line 3", "RINPUT3"},
+ {"Right Line Mux", "PGA", "Right PGA Mux"},
+ {"Right Line Mux", "Differential", "Differential Mux"},
+
+ /* Left PGA Mux */
+ {"Left PGA Mux", "Line 1", "LINPUT1"},
+ {"Left PGA Mux", "Line 2", "LINPUT2"},
+ {"Left PGA Mux", "Line 3", "LINPUT3"},
+ {"Left PGA Mux", "Differential", "Differential Mux"},
+
+ /* Right PGA Mux */
+ {"Right PGA Mux", "Line 1", "RINPUT1"},
+ {"Right PGA Mux", "Line 2", "RINPUT2"},
+ {"Right PGA Mux", "Line 3", "RINPUT3"},
+ {"Right PGA Mux", "Differential", "Differential Mux"},
+
+ /* Differential Mux */
+ {"Differential Mux", "Line 1", "LINPUT1"},
+ {"Differential Mux", "Line 1", "RINPUT1"},
+ {"Differential Mux", "Line 2", "LINPUT2"},
+ {"Differential Mux", "Line 2", "RINPUT2"},
+
+ /* Left ADC Mux */
+ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
+ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
+ {"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
+
+ /* Right ADC Mux */
+ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
+ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
+ {"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
+
+ /* ADC */
+ {"Left ADC", NULL, "Left ADC Mux"},
+ {"Right ADC", NULL, "Right ADC Mux"},
+};
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:5;
+ u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 8k */
+ {12288000, 8000, 1536, 0x6, 0x0},
+
+ /* 11.025k */
+ {11289600, 11025, 1024, 0x18, 0x0},
+
+ /* 16k */
+ {12288000, 16000, 768, 0xa, 0x0},
+
+ /* 22.05k */
+ {11289600, 22050, 512, 0x1a, 0x0},
+
+ /* 32k */
+ {12288000, 32000, 384, 0xc, 0x0},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x10, 0x0},
+
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x0},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0x1e, 0x0},
+
+ /* 96k */
+ {12288000, 96000, 128, 0xe, 0x0},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ DBG_DETAIL("mclk=%d, rate=%d", mclk, rate);
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+
+ printk(KERN_ERR "vt1602: could not get coeff for mclk %d @ rate %d\n",
+ mclk, rate);
+ return -EINVAL;
+}
+
+static int vt1602_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ u16 iface = snd_soc_read(codec, VT1602_IFACE) & 0x1f3;
+ //int coeff = get_coeff(vt1602->sysclk, params_rate(params));
+
+ DBG_DETAIL();
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x0004;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x0008;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x000c;
+ break;
+ }
+
+ /* set iface & srate */
+ snd_soc_write(codec, VT1602_IFACE, iface);
+
+ return 0;
+}
+
+static int vt1602_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, VT1602_ADCDAC) & 0xfff7;
+
+ DBG_DETAIL();
+
+ if (mute)
+ snd_soc_write(codec, VT1602_ADCDAC, mute_reg | 0x8);
+ else
+ snd_soc_write(codec, VT1602_ADCDAC, mute_reg);
+ return 0;
+}
+
+static int vt1602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct vt1602_priv *vt1602 = snd_soc_codec_get_drvdata(codec);
+
+ DBG_DETAIL();
+
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ vt1602->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int vt1602_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ DBG_DETAIL();
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface = 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, VT1602_IFACE, iface);
+ return 0;
+}
+
+static int vt1602_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level )
+{
+ DBG_DETAIL();
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* set vmid to 50k and unmute dac */
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* set vmid to 5k for quick power up */
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* mute dac and set vmid to 500k, enable VREF */
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static void vt1602_work(struct work_struct *work)
+{
+ struct snd_soc_codec *codec =
+ container_of(work, struct snd_soc_codec, dapm.delayed_work.work);
+
+ DBG_DETAIL();
+
+ vt1602_set_bias_level(codec, codec->dapm.bias_level);
+}
+
+#define VT1602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+#define VT1602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_FLOAT)
+
+static const struct snd_soc_dai_ops vt1602_dai_ops = {
+ .hw_params = vt1602_pcm_hw_params,
+ .digital_mute = vt1602_mute,
+ .set_sysclk = vt1602_set_dai_sysclk,
+ .set_fmt = vt1602_set_dai_fmt,
+};
+
+struct snd_soc_dai_driver vt1602_dai = {
+ .name = "VT1602",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = VT1602_RATES,
+ .formats = VT1602_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = VT1602_RATES,
+ .formats = VT1602_FORMATS,},
+ .ops = &vt1602_dai_ops,
+};
+
+static int vt1602_suspend(struct snd_soc_codec *codec)
+{
+ DBG_DETAIL();
+
+ vt1602_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int vt1602_resume(struct snd_soc_codec *codec)
+{
+ DBG_DETAIL();
+
+ snd_soc_cache_sync(codec);
+ vt1602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* charge vt1602 caps */
+ if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
+ vt1602_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ codec->dapm.bias_level = SND_SOC_BIAS_ON;
+ schedule_delayed_work(&codec->dapm.delayed_work,
+ msecs_to_jiffies(1000));
+ }
+
+ return 0;
+}
+
+static int wmt_vt1602_init(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret, reg;
+
+ DBG_DETAIL();
+
+ /* charge output caps */
+ vt1602_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
+ schedule_delayed_work(&codec->dapm.delayed_work, msecs_to_jiffies(1000));
+
+ /* set the update bits */
+ reg = snd_soc_read(codec, VT1602_LDAC);
+ snd_soc_write(codec, VT1602_LDAC, reg | 0x0100);
+ reg = snd_soc_read(codec, VT1602_RDAC);
+ snd_soc_write(codec, VT1602_RDAC, reg | 0x0100);
+ reg = snd_soc_read(codec, VT1602_LOUT1V);
+ snd_soc_write(codec, VT1602_LOUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, VT1602_ROUT1V);
+ snd_soc_write(codec, VT1602_ROUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, VT1602_LOUT2V);
+ snd_soc_write(codec, VT1602_LOUT2V, reg | 0x0100);
+ reg = snd_soc_read(codec, VT1602_ROUT2V);
+ snd_soc_write(codec, VT1602_ROUT2V, reg | 0x0100);
+ reg = snd_soc_read(codec, VT1602_LINVOL);
+ snd_soc_write(codec, VT1602_LINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, VT1602_RINVOL);
+ snd_soc_write(codec, VT1602_RINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, VT1602_LOUTM1);
+ snd_soc_write(codec, VT1602_LOUTM1, reg);
+ reg = snd_soc_read(codec, VT1602_LOUTM2);
+ snd_soc_write(codec, VT1602_LOUTM2, reg);
+ reg = snd_soc_read(codec, VT1602_ROUTM1);
+ snd_soc_write(codec, VT1602_ROUTM1, reg);
+ reg = snd_soc_read(codec, VT1602_ROUTM2);
+ snd_soc_write(codec, VT1602_ROUTM2, reg);
+ snd_soc_write(codec, VT1602_PWR1, 0x1FE);
+ snd_soc_write(codec, VT1602_PWR2, 0x1E0);
+ snd_soc_write(codec, VT1602_LADCIN, 0x020);
+ snd_soc_write(codec, VT1602_RADCIN, 0x020);
+ snd_soc_write(codec, VT1602_ADCTL3, 0x040);
+
+ ret = snd_soc_add_codec_controls(codec, vt1602_snd_controls,
+ ARRAY_SIZE(vt1602_snd_controls));
+ if (ret) {
+ info("snd_soc_add_codec_controls fail");
+ return ret;
+ }
+
+ ret = snd_soc_dapm_new_controls(dapm, vt1602_dapm_widgets,
+ ARRAY_SIZE(vt1602_dapm_widgets));
+ if (ret) {
+ info("snd_soc_dapm_new_controls fail");
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, audio_map,
+ ARRAY_SIZE(audio_map));
+ if (ret) {
+ info("snd_soc_dapm_add_routes fail");
+ return 0;
+ }
+
+ return ret;
+}
+
+static int vt1602_probe(struct snd_soc_codec *codec)
+{
+ struct vt1602_priv *vt1602 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ pr_info("VT1602 Audio Codec %s\n", VT1602_VERSION);
+
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, vt1602->control_type);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ ret = vt1602_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ INIT_DELAYED_WORK(&codec->dapm.delayed_work, vt1602_work);
+
+ ret = wmt_vt1602_init(codec);
+ if (ret)
+ info("vt1602_probe fail");
+ return ret;
+}
+
+/*
+ * This function forces any delayed work to be queued and run.
+ */
+static int run_delayed_work(struct delayed_work *dwork)
+{
+ int ret;
+
+ DBG_DETAIL();
+
+ /* cancel any work waiting to be queued. */
+ ret = cancel_delayed_work(dwork);
+
+ /* if there was any work waiting then we run it now and
+ * wait for it's completion */
+ if (ret) {
+ schedule_delayed_work(dwork, 0);
+ flush_scheduled_work();
+ }
+ return ret;
+}
+
+/* remove everything here */
+static int vt1602_remove(struct snd_soc_codec *codec)
+{
+ DBG_DETAIL();
+
+ vt1602_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ run_delayed_work(&codec->dapm.delayed_work);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_vt1602 = {
+ .probe = vt1602_probe,
+ .remove = vt1602_remove,
+ .suspend = vt1602_suspend,
+ .resume = vt1602_resume,
+ .set_bias_level = vt1602_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(vt1602_reg),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = vt1602_reg,
+
+ /*.controls = vt1602_snd_controls,
+ .num_controls = ARRAY_SIZE(vt1602_snd_controls),
+ .dapm_widgets = vt1602_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(vt1602_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),*/
+};
+
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+static int __devinit vt1602_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct vt1602_priv *vt1602;
+ int ret;
+
+ DBG_DETAIL();
+
+ vt1602 = devm_kzalloc(&i2c->dev, sizeof(struct vt1602_priv), GFP_KERNEL);
+ if (vt1602 == NULL) {
+ info("vt1602_i2c_probe kzalloc fail");
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(i2c, vt1602);
+ vt1602->control_type = SND_SOC_I2C;
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_vt1602, &vt1602_dai, 1);
+
+ return ret;
+}
+
+static int __devexit vt1602_i2c_remove(struct i2c_client *client)
+{
+ DBG_DETAIL();
+
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id vt1602_i2c_id[] = {
+ { "vt1602", 1},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, vt1602_i2c_id);
+
+/* corgi i2c codec control layer */
+static struct i2c_driver vt1602_i2c_driver = {
+ .driver = {
+ .name = "vt1602",
+ .owner = THIS_MODULE,
+ },
+ .probe = vt1602_i2c_probe,
+ .remove = __devexit_p(vt1602_i2c_remove),
+ .id_table = vt1602_i2c_id,
+};
+#endif
+
+static struct i2c_board_info __initdata wmt_vt1602_board_info[] = {
+ {
+ I2C_BOARD_INFO("vt1602", 0x1a),
+ },
+};
+
+static int __init vt1602_modinit(void)
+{
+ char buf[80];
+ int varlen = 80;
+ int ret = 0;
+ char codec_name[6];
+ struct i2c_adapter *adapter = NULL;
+
+ DBG_DETAIL();
+
+ ret = wmt_getsyspara("wmt.audio.i2s", buf, &varlen);
+
+ if (ret == 0) {
+ sscanf(buf, "%6s", codec_name);
+
+ if (strcmp(codec_name, "vt1602")) {
+ info("vt1602 string not found");
+ return -EINVAL;
+ }
+ }
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ /*i2c_register_board_info(1, wmt_vt1602_board_info,
+ ARRAY_SIZE(wmt_vt1602_board_info));*/
+
+ adapter = i2c_get_adapter(1);
+ if (adapter == NULL) {
+ err("can not get i2c adapter, client address error");
+ return -ENODEV;
+ }
+
+ if (i2c_new_device(adapter, wmt_vt1602_board_info) == NULL) {
+ err("allocate i2c client failed");
+ return -ENOMEM;
+ }
+
+ i2c_put_adapter(adapter);
+
+ ret = i2c_add_driver(&vt1602_i2c_driver);
+ if (ret) {
+ info("i2c_add_driver fail");
+ }
+#endif
+
+ return ret;
+}
+module_init(vt1602_modinit);
+
+static void __exit vt1602_exit(void)
+{
+ DBG_DETAIL();
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&vt1602_i2c_driver);
+#endif
+}
+module_exit(vt1602_exit);
+
+MODULE_DESCRIPTION("WMT [ALSA SoC] driver");
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/ANDROID_3.4.5/sound/soc/codecs/wmt_vt1602.h b/ANDROID_3.4.5/sound/soc/codecs/wmt_vt1602.h
new file mode 100755
index 00000000..718c822a
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/codecs/wmt_vt1602.h
@@ -0,0 +1,67 @@
+/*++
+ * linux/sound/soc/codecs/wmt_vt1602.h
+ * WonderMedia I2S audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+
+#ifndef _VT1602_H
+#define _VT1602_H
+
+/* VT1602 register space */
+
+#define VT1602_LINVOL 0x00
+#define VT1602_RINVOL 0x01
+#define VT1602_LOUT1V 0x02
+#define VT1602_ROUT1V 0x03
+#define VT1602_ADCDAC 0x05
+#define VT1602_IFACE 0x07
+#define VT1602_SRATE 0x08
+#define VT1602_LDAC 0x0a
+#define VT1602_RDAC 0x0b
+#define VT1602_BASS 0x0c
+#define VT1602_TREBLE 0x0d
+#define VT1602_RESET 0x0f
+#define VT1602_3D 0x10
+#define VT1602_ALC1 0x11
+#define VT1602_ALC2 0x12
+#define VT1602_ALC3 0x13
+#define VT1602_NGATE 0x14
+#define VT1602_LADC 0x15
+#define VT1602_RADC 0x16
+#define VT1602_ADCTL1 0x17
+#define VT1602_ADCTL2 0x18
+#define VT1602_PWR1 0x19
+#define VT1602_PWR2 0x1a
+#define VT1602_ADCTL3 0x1b
+#define VT1602_ADCIN 0x1f
+#define VT1602_LADCIN 0x20
+#define VT1602_RADCIN 0x21
+#define VT1602_LOUTM1 0x22
+#define VT1602_LOUTM2 0x23
+#define VT1602_ROUTM1 0x24
+#define VT1602_ROUTM2 0x25
+#define VT1602_MOUTM1 0x26
+#define VT1602_MOUTM2 0x27
+#define VT1602_LOUT2V 0x28
+#define VT1602_ROUT2V 0x29
+#define VT1602_MOUTV 0x2a
+
+#endif
diff --git a/ANDROID_3.4.5/sound/soc/soc-core.c b/ANDROID_3.4.5/sound/soc/soc-core.c
index 18711a50..7e286549 100644
--- a/ANDROID_3.4.5/sound/soc/soc-core.c
+++ b/ANDROID_3.4.5/sound/soc/soc-core.c
@@ -639,7 +639,8 @@ static void soc_resume_deferred(struct work_struct *work)
container_of(work, struct snd_soc_card, deferred_resume_work);
struct snd_soc_codec *codec;
int i;
-
+
+ int dbg_resume = 0;
/* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
* so userspace apps are blocked from touching us
*/
@@ -674,12 +675,23 @@ static void soc_resume_deferred(struct work_struct *work)
case SND_SOC_BIAS_OFF:
codec->driver->resume(codec);
codec->suspended = 0;
+ dbg_resume = 1;
break;
default:
+
dev_dbg(codec->dev, "CODEC was on over suspend\n");
break;
}
}
+ #if 1
+ if (!dbg_resume && codec->driver->resume) //add for temp std 2014-3-24
+ {
+ dbg_resume = 1;
+ codec->driver->resume(codec);
+ codec->suspended = 0;
+
+ }
+ #endif
}
for (i = 0; i < card->num_rtd; i++) {
@@ -1718,24 +1730,247 @@ int snd_soc_poweroff(struct device *dev)
EXPORT_SYMBOL_GPL(snd_soc_poweroff);
const struct dev_pm_ops snd_soc_pm_ops = {
- .suspend = snd_soc_suspend,
- .resume = snd_soc_resume,
- .freeze = snd_soc_suspend,
- .thaw = snd_soc_resume,
- .poweroff = snd_soc_poweroff,
- .restore = snd_soc_resume,
+ .suspend = snd_soc_suspend, //PMSG_SUSPEND
+ .resume = snd_soc_resume, //
+ .freeze = snd_soc_suspend, //PMSG_FREEZE
+ .thaw = snd_soc_resume, //
+ .poweroff = snd_soc_poweroff, //PMSG_HIBERNATE
+ .restore = snd_soc_resume, //
};
EXPORT_SYMBOL_GPL(snd_soc_pm_ops);
+/*****add for std 2014-3-24*************/
+
+/* powers down audio subsystem for suspend */
+static int soc_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct snd_soc_card *card = platform_get_drvdata(dev);
+ struct snd_soc_codec *codec;
+ int i;
+ printk("<<<<<<<<%s, pm state: 0x%x\n", __func__, state.event);
+
+ /* If the initialization of this soc device failed, there is no codec
+ * associated with it. Just bail out in this case.
+ */
+ if (list_empty(&card->codec_dev_list))
+ return 0;
+
+#if 0
+ mutex_lock(&std_pm_mutex);
+ if (std_dev_suspend)
+ {
+ mutex_unlock(&std_pm_mutex);
+ return 0;
+ }
+ std_pdev_suspend = 1;
+ mutex_unlock(&std_pm_mutex);
+
+ if (std_pdev_suspend)
+ return 0;
+
+ std_pdev_suspend = 1;
+#endif
+ /* Due to the resume being scheduled into a workqueue we could
+ * suspend before that's finished - wait for it to complete.
+ */
+
+ if (state.event==PM_EVENT_SUSPEND/*0x2*/ || state.event==PM_EVENT_FREEZE/*0x1*/){ //move from device_driver pm
+ snd_power_lock(card->snd_card);
+ snd_power_wait(card->snd_card, SNDRV_CTL_POWER_D0);
+ snd_power_unlock(card->snd_card);
+
+ /* we're going to block userspace touching us until resume completes */
+ snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot);
+
+ /* mute any active DACs */
+ for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_dai *dai = card->rtd[i].codec_dai;
+ struct snd_soc_dai_driver *drv = dai->driver;
+
+ if (card->rtd[i].dai_link->ignore_suspend)
+ continue;
+
+ if (drv->ops->digital_mute && dai->playback_active)
+ drv->ops->digital_mute(dai, 1);
+ }
+
+ /* suspend all pcms */
+ for (i = 0; i < card->num_rtd; i++) {
+ if (card->rtd[i].dai_link->ignore_suspend)
+ continue;
+
+ snd_pcm_suspend_all(card->rtd[i].pcm);
+ }
+
+ if (card->suspend_pre)
+ card->suspend_pre(card);
+
+ for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+ struct snd_soc_platform *platform = card->rtd[i].platform;
+
+ if (card->rtd[i].dai_link->ignore_suspend)
+ continue;
+
+ if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
+ cpu_dai->driver->suspend(cpu_dai);
+ if (platform->driver->suspend && !platform->suspended) {
+ platform->driver->suspend(cpu_dai);
+ platform->suspended = 1;
+ }
+ }
+
+ /* close any waiting streams and save state */
+ for (i = 0; i < card->num_rtd; i++) {
+ flush_delayed_work_sync(&card->rtd[i].delayed_work);
+ card->rtd[i].codec->dapm.suspend_bias_level = card->rtd[i].codec->dapm.bias_level;
+ }
+
+ for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
+
+ if (card->rtd[i].dai_link->ignore_suspend)
+ continue;
+
+ snd_soc_dapm_stream_event(&card->rtd[i],
+ SNDRV_PCM_STREAM_PLAYBACK,
+ codec_dai,
+ SND_SOC_DAPM_STREAM_SUSPEND);
+
+ snd_soc_dapm_stream_event(&card->rtd[i],
+ SNDRV_PCM_STREAM_CAPTURE,
+ codec_dai,
+ SND_SOC_DAPM_STREAM_SUSPEND);
+ }
+
+ /* suspend all CODECs */
+ list_for_each_entry(codec, &card->codec_dev_list, card_list) {
+ /* If there are paths active then the CODEC will be held with
+ * bias _ON and should not be suspended. */
+ if (!codec->suspended && codec->driver->suspend) {
+ switch (codec->dapm.bias_level) {
+ case SND_SOC_BIAS_STANDBY:
+ /*
+ * If the CODEC is capable of idle
+ * bias off then being in STANDBY
+ * means it's doing something,
+ * otherwise fall through.
+ */
+ if (codec->dapm.idle_bias_off) {
+ dev_dbg(codec->dev,
+ "idle_bias_off CODEC on over suspend\n");
+ break;
+ }
+ case SND_SOC_BIAS_OFF:
+ codec->driver->suspend(codec);
+ codec->suspended = 1;
+ codec->cache_sync = 1;
+ break;
+ default:
+ dev_dbg(codec->dev, "CODEC is on over suspend\n");
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+
+ if (card->rtd[i].dai_link->ignore_suspend)
+ continue;
+
+ if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control)
+ cpu_dai->driver->suspend(cpu_dai);
+ }
+
+ if (card->suspend_post)
+ card->suspend_post(card);
+
+ return 0;
+ }
+
+ if (state.event == PM_EVENT_HIBERNATE) { // 0x4
+
+ /* Flush out pmdown_time work - we actually do want to run it
+ * now, we're shutting down so no imminent restart. */
+ for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+ flush_delayed_work_sync(&rtd->delayed_work);
+ }
+
+ snd_soc_dapm_shutdown(card);
+
+ return 0;
+
+ }
+
+ return 0;
+}
+
+/* powers up audio subsystem after a suspend */
+int soc_resume(struct platform_device *dev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(dev);
+ int i, ac97_control = 0;
+
+ printk("<<<<<%s\n", __func__);
+
+#if 0
+ if (std_pdev_suspend)
+ std_pdev_suspend = 0;
+ else
+ return 0;
+#endif
+ /* If the initialization of this soc device failed, there is no codec
+ * associated with it. Just bail out in this case.
+ */
+ if (list_empty(&card->codec_dev_list))
+ return 0;
+#if 0
+ mutex_lock(&std_pm_mutex);
+ if (std_dev_suspend)
+ {
+ mutex_unlock(&std_pm_mutex);
+ return 0;
+ }
+ std_pdev_suspend = 0;
+ mutex_unlock(&std_pm_mutex);
+#endif
+
+ /* AC97 devices might have other drivers hanging off them so
+ * need to resume immediately. Other drivers don't have that
+ * problem and may take a substantial amount of time to resume
+ * due to I/O costs and anti-pop so handle them out of line.
+ */
+ for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+ ac97_control |= cpu_dai->driver->ac97_control;
+ }
+ if (ac97_control) {
+ dev_dbg(dev, "Resuming AC97 immediately\n");
+ soc_resume_deferred(&card->deferred_resume_work);
+ } else {
+ dev_dbg(dev, "Scheduling resume work\n");
+ if (!schedule_work(&card->deferred_resume_work))
+ dev_err(dev, "resume work item may be lost\n");
+ }
+
+ return 0;
+}
+
+/*********************************************/
+
/* ASoC platform driver */
static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
.owner = THIS_MODULE,
- .pm = &snd_soc_pm_ops,
+ //.pm = &snd_soc_pm_ops,
},
.probe = soc_probe,
.remove = soc_remove,
+ .suspend = soc_suspend,
+ .resume = soc_resume,
};
/**
diff --git a/ANDROID_3.4.5/sound/soc/soc-dapm.c b/ANDROID_3.4.5/sound/soc/soc-dapm.c
index 64bcd4e2..6201fc54 100644
--- a/ANDROID_3.4.5/sound/soc/soc-dapm.c
+++ b/ANDROID_3.4.5/sound/soc/soc-dapm.c
@@ -1442,7 +1442,15 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
}
list_for_each_entry(w, &card->widgets, list) {
- list_del_init(&w->dirty);
+ switch (w->id) {
+ case snd_soc_dapm_pre:
+ case snd_soc_dapm_post:
+ /* These widgets always need to be powered */
+ break;
+ default:
+ list_del_init(&w->dirty);
+ break;
+ }
if (w->power) {
d = w->dapm;
@@ -3266,7 +3274,7 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card)
{
struct snd_soc_codec *codec;
- list_for_each_entry(codec, &card->codec_dev_list, list) {
+ list_for_each_entry(codec, &card->codec_dev_list, card_list) {
soc_dapm_shutdown_codec(&codec->dapm);
if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
snd_soc_dapm_set_bias_level(&codec->dapm,
diff --git a/ANDROID_3.4.5/sound/soc/wmt/Kconfig b/ANDROID_3.4.5/sound/soc/wmt/Kconfig
new file mode 100755
index 00000000..344ec73d
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/Kconfig
@@ -0,0 +1,65 @@
+config SND_WMT_SOC
+ bool "SoC Audio for the WMT chip"
+ depends on ARCH_WMT && SND && SND_SOC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the WMT AC97, I2S interface. You will also need
+ to select the audio interfaces to support below.
+
+#
+# WMT ALSA I2S driver
+#
+config SND_WMT_SOC_I2S
+ bool "SoC I2S Audio support for WMT"
+ depends on SND_WMT_SOC
+ ---help---
+ Say Y or M if you want to add support for codecs attached to
+ the WMT I2S interface.
+
+config SND_WMT_SOC_PDM
+ bool "SoC PDM Audio Interface support for WMT"
+ depends on SND_WMT_SOC
+ ---help---
+ Say Y or M if you want to add support for codecs attached to
+ the WMT PDM interface.
+
+config I2S_HW_DAC
+ bool "HW_DAC"
+ depends on SND_WMT_SOC_I2S
+ select SND_SOC_HWDAC
+ ---help---
+ Say Y here if you want to use HW DAC.
+
+config I2S_CODEC_VT1602
+ bool "VT1602"
+ depends on SND_WMT_SOC_I2S
+ select SND_SOC_VT1602
+ ---help---
+ Say Y here if you want to use VT1602 as the I2S Codec.
+
+config I2S_CODEC_WM8900
+ bool "WM8900"
+ depends on SND_WMT_SOC_I2S
+ select SND_SOC_WM8900
+ ---help---
+ Say Y here if you want to use WM8900 as the I2S Codec.
+
+config I2S_CODEC_WM8994
+ bool "WM8994"
+ depends on SND_WMT_SOC_I2S
+ select SND_SOC_WM8994
+ ---help---
+ Say Y here if you want to use WM8994 as the I2S Codec.
+
+config I2S_CODEC_VT1603
+ bool "VT1603"
+ depends on SND_WMT_SOC_I2S
+ select SND_SOC_VT1603
+ ---help---
+ Say Y here if you want to use VT1603 as the I2S Codec.
+
+config ECHO_CANCELLATION_FM34
+ tristate "FM34"
+ select SND_SOC_WMT_FM34
+ ---help---
+ Say Y here if you want to use fm34 to echo cancallation.
diff --git a/ANDROID_3.4.5/sound/soc/wmt/Makefile b/ANDROID_3.4.5/sound/soc/wmt/Makefile
new file mode 100755
index 00000000..bf4a07ef
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/Makefile
@@ -0,0 +1,12 @@
+#
+## Makefile for ALSA WMT
+#
+#
+# WMT SOC Support
+snd-soc-wmt-i2s-objs := wmt-i2s.o wmt_swmixer.o
+snd-soc-wmt-objs := wmt-soc.o wmt-pcm.o wmt_wm8994.o wmt_hwdep.o
+
+snd-soc-wmt-pdm-objs := wmt-pcm-controller.o wmt-pcm-dma.o
+obj-$(CONFIG_SND_WMT_SOC_I2S) += snd-soc-wmt-i2s.o
+obj-$(CONFIG_SND_WMT_SOC) += snd-soc-wmt.o
+obj-$(CONFIG_SND_WMT_SOC_PDM) += snd-soc-wmt-pdm.o
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c b/ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c
new file mode 100755
index 00000000..643d25e2
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c
@@ -0,0 +1,1105 @@
+/*++
+ * linux/sound/soc/wmt/wmt-i2s.c
+ * WonderMedia I2S audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+#include <linux/suspend.h>
+
+#include <mach/hardware.h>
+#include <asm/dma.h>
+#include "wmt-soc.h"
+#include "wmt-pcm.h"
+
+#define NULL_DMA ((dmach_t)(-1))
+#define AUD_SPDIF_ENABLE 1
+
+static int wmt_i2s_output = 0; //hdmi enable or not 2013-9-3 //maybe can remove it 2013-10-24
+static int wmt_codec_wm8994 = 0; //env set 2013-9-3
+
+static int gpio_pa = -1;
+static int gpio_active = 0;
+extern int wmt_gpio_setpull(unsigned int gpio, enum wmt_gpio_pulltype pull);
+/*
+ * Debug
+ */
+#define AUDIO_NAME "WMT_I2S"
+//#define WMT_I2S_DEBUG 1
+//#define WMT_I2S_DEBUG_DETAIL 1
+
+#ifdef WMT_I2S_DEBUG
+#define DPRINTK(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#else
+#define DPRINTK(format, arg...) do {} while (0)
+#endif
+
+#ifdef WMT_I2S_DEBUG_DETAIL
+#define DBG_DETAIL(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": [%s]" format "\n" , __FUNCTION__, ## arg)
+#else
+#define DBG_DETAIL(format, arg...) do {} while (0)
+#endif
+
+#define err(format, arg...) \
+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) \
+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
+
+struct wmt_i2s_asoc_data {
+ int stream_id;
+ unsigned int bus_id;
+ struct i2s_s i2s;
+ /*
+ * Flags indicating is the bus already activated and configured by
+ * another substream
+ */
+ unsigned char HDMI_and_DAC0; //new env set to determine
+ unsigned char HDMI_aud_enable; //same with global wmt_i2s_output
+
+ int active;
+ int configured;
+ unsigned char CH_SEL_NUM;
+ unsigned char HDMI_SPDIF_STATE;
+ /*struct timer_list delay_timer;*/
+ struct audio_stream_a s[2];
+};
+static struct timer_list pa_timer;
+
+static int audio_interface_mode = 0;//add 2014-6-24 i2s left right
+
+static int wmt_pdm_module_enable = 0;
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+#define to_i2s_data(priv) container_of((priv), struct wmt_i2s_asoc_data, bus_id)
+#define SNDRV_PCM_STREAM_ALL 2
+
+static void i2s_init(int mode);
+static void i2s_exit(void);
+
+#ifdef CONFIG_FB_WMT
+extern int vpp_set_audio(int format, int sample_rate, int channel);
+#endif
+
+
+static struct wmt_i2s_asoc_data i2s_data[NUM_LINKS] = {
+ {
+ .bus_id = 0, //?? 2 SNDRV_PCM_STREAM_ALL
+ .i2s = {
+ /* interrupt counters */
+ {0, 0, 0, 0, 0, 0, 0, 0},
+ /* irq number*/
+ 0,
+ /* reference counter */
+ 0,
+ /* channels */
+ 0,
+ /* format */
+ 0,
+ /* fragment size */
+ 0,
+ /* sample rate */
+ 0,
+ i2s_init,
+ i2s_exit,
+ },
+ .s = {
+ {
+ .id = "WMT I2S out",
+ .stream_id = SNDRV_PCM_STREAM_PLAYBACK,
+ .dmach = NULL_DMA,
+ .dma_dev = AHB1_AUD_DMA_REQ_1,
+ /*.dma_cfg = dma_device_cfg_table[I2S_TX_DMA_REQ],*/
+ },
+ {
+ .id = "WMT I2S in",
+ .stream_id = SNDRV_PCM_STREAM_CAPTURE,
+ .dmach = NULL_DMA,
+ .dma_dev = AHB1_AUD_DMA_REQ_0,
+ /*.dma_cfg = dma_device_cfg_table[I2S_RX_DMA_REQ],*/
+ },
+ },
+ .HDMI_aud_enable = 0,
+ .HDMI_and_DAC0 = 0,
+ .CH_SEL_NUM = 2,
+ .HDMI_SPDIF_STATE = 6,//hdmi spdif play at sametime
+ },
+};
+
+
+/* for HDMI Audio status */
+int hd_audio_flag = 0;
+
+
+
+#ifdef CONFIG_WMT_I2S_INT
+/* wmt_i2s_interrupt()
+ *
+ * It's only interrupt counter now, might be useful to
+ * debug or benchmark.
+ */
+static irqreturn_t
+wmt_i2s_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ DBG_DETAIL();
+
+ return IRQ_HANDLED;
+}
+#endif
+
+#if 0
+static void delay_timer_handler(unsigned long data)
+{
+ DBG_DETAIL();
+}
+#endif
+
+static void pa_timer_handler(unsigned long data)
+{
+ printk("%s\n", __func__);
+ if (gpio_pa >= 0)
+ {
+
+ gpio_direction_output(gpio_pa, gpio_active);
+ }
+}
+
+void wmt_i2s_ch_config(void)
+{
+ /* CH_SEL_NUM = 0, mesns output L to SPDIF&DAC's L and R
+ 1, mesns output R to SPDIF&DAC's L and R
+ 2, means normal stereo output to SPDIF&DAC */
+ switch (i2s_data->CH_SEL_NUM) {
+ case 0:
+ ASMPFCHCFG0_VAL = 0x10100000;
+ ASMPF2HDACHCFG_VAL = 0x76543200;
+ break;
+ case 1:
+ ASMPFCHCFG0_VAL = 0x10101111;
+ ASMPF2HDACHCFG_VAL = 0x76543211;
+ break;
+ case 2:
+ ASMPFCHCFG0_VAL = 0x10101010;
+ ASMPF2HDACHCFG_VAL = 0x76543210;
+ break;
+ }
+
+ if (i2s_data->i2s.channels == 0x01) {
+ ASMPFCHCFG0_VAL = 0x10100000;
+ ASMPF2HDACHCFG_VAL = 0x76543200;
+ }
+
+ if (i2s_data->HDMI_aud_enable) {
+ /* disable DAC#0, if HDMI Audio is enabled */
+ ASMPFCHCFG0_VAL &= 0xFFFF00FF;
+ ASMPFCHCFG0_VAL |= 0x00009800;
+ }
+
+ /* HDMI_SPDIF_STATE = 3, means disable HDMI&SPDIF
+ 4, means enable HDMI only
+ 5, means enable SPDIF only
+ 6, means enable HDMI&SPDIF as normal */
+ switch (i2s_data->HDMI_SPDIF_STATE) {
+ case 3:
+ ASMPFCHCFG0_VAL &= 0xFFFFFF00;
+ ASMPFCHCFG0_VAL |= 0x00000098;
+ ASMPF2HDACHCFG_VAL &= 0xFFFFFF00;
+ ASMPF2HDACHCFG_VAL |= 0x00000076;
+ break;
+ case 4:
+ ASMPFCHCFG0_VAL &= 0xFFFFFF00;
+ ASMPFCHCFG0_VAL |= 0x00000098;
+ break;
+ case 5:
+ ASMPF2HDACHCFG_VAL &= 0xFFFFFF00;
+ ASMPF2HDACHCFG_VAL |= 0x00000076;
+ break;
+ }
+}
+
+
+void wmt_i2s_dac0_ctrl(int HDMI_audio_enable)
+{
+ /* check if output to HDMI audio and DAC0 at same time */
+ if (i2s_data->HDMI_and_DAC0)
+ return;
+
+ if (HDMI_audio_enable) {
+ i2s_data->HDMI_aud_enable = 1;
+ info("HDMI Audio is enabled, disable dac0 of I2S");
+ }
+ else {
+ i2s_data->HDMI_aud_enable = 0;
+ info("HDMI Audio is disabled, enable dac0 of I2S");
+ }
+
+ wmt_i2s_ch_config();
+
+ info("CHCFG0=0x%x, HDACHCFG=0x%x", ASMPFCHCFG0_VAL, ASMPF2HDACHCFG_VAL);
+}
+EXPORT_SYMBOL(wmt_i2s_dac0_ctrl);
+void wmt_i2s_ch_sel(int ch_sel_num)
+{
+ if (ch_sel_num < 3)
+ i2s_data->CH_SEL_NUM = ch_sel_num;
+ else
+ i2s_data->HDMI_SPDIF_STATE = ch_sel_num;
+
+ wmt_i2s_ch_config();
+
+
+ info("SEL: CHCFG0=0x%x, HDACHCFG=0x%x", ASMPFCHCFG0_VAL, ASMPF2HDACHCFG_VAL);
+}
+EXPORT_SYMBOL(wmt_i2s_ch_sel);
+
+static void i2s_disable(void)
+{
+ DBG_DETAIL();
+
+ DACCFG_VAL &= ~DACITF_ENABLE;
+ ASMPFCFG_VAL &=~ASMPF_ENABLE;
+ HDACKGEN_VAL &= ~HDACKGEN_ENABLE;
+
+#ifdef AUD_SPDIF_ENABLE
+ DGOCFG_VAL &= ~DGOITF_ENABLE;
+#endif
+
+ AADCF0CFG_VAL &= ~(AADCITF_ENABLE | AADCF_ENABLE);
+}
+
+static void i2s_enable(void)
+{
+ DBG_DETAIL();
+
+ AADCF0CFG_VAL |= (AADCITF_ENABLE | AADCF_ENABLE);
+ ASMPFCFG_VAL |= (ASMPF_ENABLE);
+
+#ifdef AUD_SPDIF_ENABLE
+ DGOCFG_VAL |= DGOITF_ENABLE;
+#endif
+
+ DACCFG_VAL |= DACITF_ENABLE;
+
+ if (hd_audio_flag)
+ HDACKGEN_VAL |= HDACKGEN_ENABLE;
+}
+
+static void aud_audprf_setting(unsigned char smp_rate_index)
+{
+ unsigned long cfg_tbl[] = {0x00002001, 0x00002001, 0x00002001,
+ 0x00002001, 0x00002001, 0x00002001, 0x00002001, 0x00002001,
+ 0x00002001, 0x00002001, 0x00002001, 0x00002001,
+ 0x00000001, 0x00000001, 0x00000001
+ };
+ unsigned int dgo_tbl[] = {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00};
+ unsigned int sysclk_tbl[] = {4096, 5632, 6144, 8192, 11264, 12288, 16384,
+ 22579, 24576, 32768, 45056, 49152, 16384, 22528, 24576};
+#ifdef AUD_SPDIF_ENABLE
+ unsigned int dgocs_tbl[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x02, 0x00, 0x08, 0x0A, 0x00, 0x0C, 0x0E};
+#endif
+ //unsigned int clock = 0;
+ unsigned long aud_reg_val;
+
+ DBG_DETAIL();
+
+ ADCCFG_VAL = cfg_tbl[smp_rate_index];
+
+ aud_reg_val = dgo_tbl[smp_rate_index];
+ DGOCFG_VAL = aud_reg_val;
+ HDACKGEN_VAL = aud_reg_val;
+
+#ifdef AUD_SPDIF_ENABLE
+ /* set ADGO channel status */
+ aud_reg_val = dgocs_tbl[smp_rate_index] << 24;
+ DGOCS0A_VAL = aud_reg_val;
+ DGOCS1A_VAL = aud_reg_val;
+ //info("%s : DGOCS01A_VAL=0x%x", __func__, (unsigned int)aud_reg_val);
+#endif
+
+ DACCFG_VAL = cfg_tbl[smp_rate_index];
+
+ auto_pll_divisor(DEV_I2S, CLK_ENABLE , 0, 0);
+ auto_pll_divisor(DEV_I2S, SET_PLLDIV, 1, sysclk_tbl[smp_rate_index]);
+ /*clock = auto_pll_divisor(DEV_I2S, GET_FREQ , 0, 0);
+ info("%s : clock=%d", __func__, clock);*/
+}
+
+static unsigned char aud_smp_rate_convert(unsigned int smp_rate)
+{
+ unsigned char i = 0;
+ unsigned int smp_rate_tbl[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000,
+ 44100, 48000, 64000, 88200, 96000, 128000, 176000, 192000};
+
+ DBG_DETAIL();
+
+ /* boundary checking */
+ if (smp_rate < 8000) {
+ i = 0x00;
+ }
+ else if (smp_rate > 192000) {
+ i = 0x0E;
+ }
+ else {
+ for (i = 0; i < 0x1F; i++) {
+ if (smp_rate == smp_rate_tbl[i]) {
+ break;
+ }
+ else if (smp_rate < smp_rate_tbl[i + 1]) {
+ if (smp_rate < (smp_rate_tbl[i] + ((smp_rate_tbl[i + 1] - smp_rate_tbl[i]) / 2))) {
+ break;
+ }
+ else {
+ i++;
+ break;
+ }
+ }
+ }
+ }
+
+ return i;
+
+}
+
+static void i2s_sample_rate(unsigned int rate)
+{
+ unsigned char rate_index;
+
+ DBG_DETAIL("rate=%d", rate);
+
+ if (rate == i2s_data->i2s.rate)
+ return;
+
+ i2s_data->i2s.rate = rate;
+
+ rate_index = aud_smp_rate_convert(rate);
+
+ aud_audprf_setting(rate_index);
+
+#ifdef CONFIG_FB_WMT
+ /* pass information of audio to HDMI Audio */
+ hd_audio_flag = vpp_set_audio(16, rate, 2);
+#endif
+}
+
+static int i2sdacdat_gpio = -1;//means don't touch GP10 bit0-3 i2sDacDat0-3
+
+void wmt_set_i2s_share_pin(void)
+{
+ int pwren_num,active_level,gpio_int_num;
+ char buf[256];
+ int varlen = 256;
+ static int gpio26_mux = -1;
+ unsigned int gpval = 0x0;
+
+ if(gpio26_mux == -1){
+ if(wmt_getsyspara("wmt.bt.mtk6622",buf,&varlen) == 0)
+ {
+ sscanf(buf,"%d:%d:%d",&pwren_num,&active_level,&gpio_int_num);
+ printk("use customized value:p%d,a%d,i%d\n",pwren_num,active_level,gpio_int_num);
+ if(pwren_num == 62){
+ gpio26_mux = 0x01;
+ }else{
+ gpio26_mux = 0x00;
+ }
+
+ }else{
+ gpio26_mux = 0x00;
+ }
+ }
+ printk("%s gpio26_mux:%d\n",__func__,gpio26_mux);
+ if(!gpio26_mux){
+ /* disable GPIO and enable Pull Down mode */
+ gpval &= ~0xff;
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL &= ~0xFF; // 0xFF bit1
+ }else{
+ gpval &= ~0xF7;
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL &= ~0xF7;
+ }
+
+ // configure I2SDACDAT1/2/3 as GPIO function but mmax-wm8994 that configured as I2SADCLRC/I2SADCBLCK
+ if (!wmt_codec_wm8994)
+ {
+ gpval |= (BIT1 | BIT2 | BIT3);
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL |= (BIT1 | BIT2 | BIT3);
+
+ }
+
+ //add for mainly i2sdacdat1 work as gpio to control led 2014-8-15 so here just keep it specific func
+ //tp driver will use it as gpio
+ switch (i2sdacdat_gpio)
+ {
+ case 1:
+ gpval |= BIT1;
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL |= BIT1; // i2sdacdat1 work as gpio
+ break;
+ case 2:
+ gpval |= BIT2;
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL |= BIT2;
+ break;
+ case 3:
+ gpval |= BIT3;
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL |= BIT3;
+ break;
+
+ }
+ GPIO_CTRL_GP10_I2S_BYTE_VAL = gpval;
+
+ GPIO_CTRL_GP11_I2S_BYTE_VAL &= ~(BIT0 | BIT1 | BIT2);
+
+ if(!gpio26_mux){
+ PULL_EN_GP10_I2S_BYTE_VAL |= 0xFF;
+ }else{
+ PULL_EN_GP10_I2S_BYTE_VAL |= 0xF7;
+ }
+ PULL_EN_GP11_I2S_BYTE_VAL |= (BIT0 | BIT1 | BIT2);
+
+ if(!gpio26_mux){
+ /* set to 2ch input, 2ch output */
+ PIN_SHARING_SEL_4BYTE_VAL &= ~(BIT15 | BIT17 | BIT19 | BIT20);
+ PIN_SHARING_SEL_4BYTE_VAL |= (BIT1 | BIT16 | BIT18);
+ }else{
+ PIN_SHARING_SEL_4BYTE_VAL &= ~(BIT15 | BIT17 | BIT20);
+ PIN_SHARING_SEL_4BYTE_VAL |= (BIT1 | BIT16);
+ }
+}
+EXPORT_SYMBOL(wmt_set_i2s_share_pin);
+
+static void i2s_init(int mode)
+{
+#ifdef DEBUG
+ int ret;
+#endif
+ int temp ;
+ //unsigned int clock = 0;
+
+ DPRINTK("i2s_ref = %d ", i2s_data->i2s.ref);
+
+ if (++i2s_data->i2s.ref > 1)
+ return;
+
+ DBG_DETAIL();
+
+ if (!mode) {
+ /* set to 24.576MHz */
+ auto_pll_divisor(DEV_I2S, CLK_ENABLE , 0, 0);
+ auto_pll_divisor(DEV_I2S, SET_PLLDIV, 1, 24576);
+ /*clock = auto_pll_divisor(DEV_I2S, GET_FREQ , 0, 0);
+ info("%s : clock=%d \n" , __func__, clock);*/
+
+ /* Enable BIT4:ARFP clock, BIT3:ARF clock */
+ PMCEU_VAL |= (BIT4 | BIT3);
+
+ /* Enable BIT2:AUD clock */
+ PMCE3_VAL |= BIT2;
+
+ wmt_set_i2s_share_pin();
+ }
+
+ /* connect DZDRQ8 to ADC0 FIFO, DZDRQ9 to DAC FIFO and DZDRQA to ADC1 FIFO */
+ DZDRQ8_CFG_VAL = 0x0;
+ DZDRQ9_CFG_VAL = 0x1;
+ DZDRQA_CFG_VAL = 0x2;
+
+ DACCFG_VAL = 0x0;
+ ADCCFG_VAL = 0x0;
+
+ /* little endian, signed format, enable sample FIFO, 16bit sample, 2 channel */
+ ASMPFCFG_VAL = 0x52;
+
+ /* assign ch0 to DAC#0_L, DAC#1_L, DAC#2_L, SPDIF_L,
+ ch1 to DAC#0_R, DAC#1_R, DAC#2_R, SPDIF_R */
+ ASMPFCHCFG0_VAL = 0x10101010;
+
+ /* assign ch0 to DAC#3_L, ch1 to DAC#3_R */
+ ASMPFCHCFG1_VAL = 0x10;
+
+ /* select ADCDAT0, 16 bits mode, enable AADCFIFO and AADCITF */
+ AADCF0CFG_VAL = (AADCITF_ENABLE | AADCF16_ENABLE | AADCF_ENABLE);
+
+ /* the sequence must be ARF_ADCCFG first then ARF_DGOCFG, finally ARF_DACCFG while slave mode,
+ otherwise will generate noise when record function is active */
+
+ /* ADC slave mode, 48K sample rate, I2S mode */
+ ADCCFG_VAL = 0x2001;
+
+ i2s_data->i2s.rate = 48000;
+ i2s_data->i2s.channels = 2;
+ i2s_data->i2s.format = SNDRV_PCM_FORMAT_S16_LE;
+
+#ifdef AUD_SPDIF_ENABLE
+ /* B1: 0 -> PCM, 1 -> Bitstream
+ B24 ~ B27: sample rate */
+ /* set ADGO channel status for 48K sample rate */
+ DGOCS0A_VAL = 0x02 << 24;
+ DGOCS1A_VAL = 0x02 << 24;
+
+ /* enable ADGOITF and ADGOCKGEN for 48K sample rate */
+ DGOCFG_VAL = 0x82;
+#endif
+
+ /* enable ADACITF and ADACCKGEN for 44.1K sample rate, I2S mode */
+ DACCFG_VAL = 0x402001;
+ if (audio_interface_mode == 1)
+ {
+ printk("<<<<<%s left justified!\n", __func__);
+ //left justified bit20: 0->i2s, 1->l/r clock polarity
+ DACCFG_VAL |= 0x1 << 20;
+ //bit7:0 padding added to dac serial data output
+ //0 = LJ
+ //1 = I2S
+ //others = RJ (depends on Bit 11:8)
+
+ DACCFG_VAL &= ~(0x1<<0);
+
+ }
+
+ /* enable HD Audio clock for 48K sample rate or not*/
+ HDACKGEN_VAL = 0x12;
+
+#ifdef CONFIG_FB_WMT
+ /* pass information of audio to HDMI Audio */
+ hd_audio_flag = vpp_set_audio(16, i2s_data->i2s.rate, i2s_data->i2s.channels);
+ ASMPF2HDACHCFG_VAL = 0x76543210;
+#endif
+
+ if (!hd_audio_flag)
+ HDACKGEN_VAL = 0;
+
+ /* audio peri reset */
+ temp = AUDPRFRST_VAL;
+
+#ifdef AUD_SPDIF_ENABLE
+ temp |= (ASMPF_RESET | DACITF_RESET | ADCITF_RESET | DGOITF_RESET);
+#else
+ temp |= (ASMPF_RESET | DACITF_RESET | ADCITF_RESET);
+#endif
+
+ AUDPRFRST_VAL = temp;
+
+ /*
+ request irq
+ */
+#ifdef CONFIG_WMT_I2S_INT
+ ret = request_irq(i2s_data->i2s.irq,
+ wmt_i2s_interrupt,
+ SA_INTERRUPT,
+ "wmt_alsa_vt1602",
+ NULL);
+ if (ret)
+ printk("%s : unable to request IRQ \n" , __func__);
+#endif
+}
+
+static void i2s_exit(void)
+{
+ DBG_DETAIL();
+
+ if (--i2s_data->i2s.ref)
+ return;
+
+ DPRINTK("Do i2s_exit ");
+
+#ifdef CONFIG_WMT_I2S_INT
+ free_irq(i2s_data->i2s.irq, NULL);
+#endif
+
+ /* Reset counter.*/
+ memset(&i2s_data->i2s.ints, 0, sizeof(struct i2s_ints_s));
+ return;
+}
+
+static int wmt_i2s_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int stream_id = substream->pstr->stream;
+ struct audio_stream_a *s = &i2s_data->s[0];
+
+ DBG_DETAIL();
+
+ s[stream_id].stream = substream;
+
+ runtime->private_data = s;
+
+ return 0;
+}
+
+static void wmt_i2s_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ DBG_DETAIL();
+}
+
+static int wmt_i2s_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int err = 0;
+ int stream_id = substream->pstr->stream;
+
+ DBG_DETAIL();
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ ASMPFCFG_VAL |= ASMPF_ENABLE;
+ }
+ else if (stream_id == SNDRV_PCM_STREAM_CAPTURE) {
+ AADCF0CFG_VAL |= AADCF_ENABLE;
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ ASMPFCFG_VAL &= ~ASMPF_ENABLE;
+ }
+ else if (stream_id == SNDRV_PCM_STREAM_CAPTURE) {
+ AADCF0CFG_VAL &= ~AADCF_ENABLE;
+ }
+
+ /*
+ mod_timer(&i2s_data->delay_timer, jiffies + HZ / 100);
+ */
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static int wmt_i2s_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ DBG_DETAIL();
+ return 0;
+}
+
+static int wmt_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int channel, byte;
+ int stream_id = substream->pstr->stream;
+
+#ifdef CONFIG_SND_OSSEMUL
+ //info("oss.rate=%d, oss.channels=%d", runtime->oss.rate, runtime->oss.channels);
+#else
+ DBG_DETAIL();
+#endif
+
+ byte = (runtime->sample_bits)/8;
+ channel = runtime->channels;
+
+ DPRINTK("snd_wmt_alsa_prepare byte = %d, channels = %d", byte, runtime->channels);
+
+
+ if ((runtime->rate != i2s_data->i2s.rate) || (runtime->format != i2s_data->i2s.format) ||
+ (runtime->channels != i2s_data->i2s.channels) || (stream_id != i2s_data->stream_id)) {
+ info("*** stream_id=%d, rate=%d, format=0x%x channels=%d ***",
+ stream_id, runtime->rate, runtime->format, runtime->channels);
+ i2s_data->i2s.format = runtime->format;
+ i2s_data->i2s.channels = runtime->channels;
+ i2s_data->stream_id = stream_id;
+ }
+ else {
+ return 0;
+ }
+
+ i2s_disable();
+
+ /* format setting */
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* little or big endian check */
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_BE:
+ case SNDRV_PCM_FORMAT_U16_BE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ case SNDRV_PCM_FORMAT_U24_BE:
+ case SNDRV_PCM_FORMAT_S32_BE:
+ case SNDRV_PCM_FORMAT_U32_BE:
+ ASMPFCFG_VAL |= ASMPF_EXCH_ENDIAN;
+ break;
+ default:
+ ASMPFCFG_VAL &= ~ASMPF_EXCH_ENDIAN;
+ break;
+ }
+
+ /* unsigned or signed check */
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_U16_BE:
+ case SNDRV_PCM_FORMAT_U24_LE:
+ case SNDRV_PCM_FORMAT_U24_BE:
+ case SNDRV_PCM_FORMAT_U32_LE:
+ case SNDRV_PCM_FORMAT_U32_BE:
+ ASMPFCFG_VAL |= ASMPF_EXCH_FMT;
+ break;
+ default:
+ ASMPFCFG_VAL &= ~ASMPF_EXCH_FMT;
+ break;
+ }
+
+ /* sample quantization check */
+ ASMPFCFG_VAL &= ~(BIT4 | BIT5);
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S8:
+ case SNDRV_PCM_FORMAT_U8:
+ ASMPFCFG_VAL |= ASMPF_8BIT_SMP;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S16_BE:
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_U16_BE:
+ ASMPFCFG_VAL |= ASMPF_16BIT_SMP;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S32_BE:
+ case SNDRV_PCM_FORMAT_U32_LE:
+ case SNDRV_PCM_FORMAT_U32_BE:
+ ASMPFCFG_VAL |= ASMPF_32BIT_SMP;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ case SNDRV_PCM_FORMAT_U24_LE:
+ case SNDRV_PCM_FORMAT_U24_BE:
+ info("*** Not Supported: fmt=24Bit ***");
+ default:
+ break;
+ }
+
+ /* channel number check */
+ ASMPFCFG_VAL &= ~(BIT0 | BIT1 | BIT2 | BIT3);
+ ASMPFCFG_VAL |= runtime->channels;
+
+ wmt_i2s_ch_config();
+ info("Prepare: CHCFG0=0x%x, HDACHCFG=0x%x", ASMPFCHCFG0_VAL, ASMPF2HDACHCFG_VAL);
+ }
+
+ /* sample rate setting */
+#ifdef CONFIG_SND_OSSEMUL
+ if (runtime->oss.rate) {
+ i2s_sample_rate(runtime->oss.rate);
+ }
+ else {
+ i2s_sample_rate(runtime->rate);
+ }
+#else
+ i2s_sample_rate(runtime->rate);
+#endif
+
+ i2s_enable();
+
+ /*
+ printk("avail_max=%d, rate=%d, channels=%d, period_size=%d, periods=%d, buffer_size=%d, tick_time=%d, \
+ min_align=%d, byte_align=%d, frame_bits=%d, sample_bits=%d, sleep_min=%d, xfer_align=%d, boundary=%d\n",
+ runtime->avail_max, runtime->rate, runtime->channels, runtime->period_size, runtime->periods,
+ runtime->buffer_size, runtime->tick_time, runtime->min_align, runtime->byte_align,
+ runtime->frame_bits, runtime->sample_bits,
+ runtime->sleep_min, runtime->xfer_align, runtime->boundary);
+ */
+ return 0;
+}
+
+/*
+ * This must be called before _set_clkdiv and _set_sysclk since McBSP register
+ * cache is initialized here
+ */
+static int wmt_i2s_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ DBG_DETAIL();
+ return 0; //add rambo 2013-3-13
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ /* Unsupported data format */
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set bit clock (CLKX/CLKR) and FS polarities */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /*
+ * Normal BCLK + FS.
+ * FS active low. TX data driven on falling edge of bit clock
+ * and RX data sampled on rising edge of bit clock.
+ */
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wmt_i2s_suspend(struct snd_soc_dai *cpu_dai)
+{
+ printk("%s!\n", __func__);
+
+ DBG_DETAIL();
+
+ i2s_data->i2s.ref = 0;
+#if 1
+ if (gpio_pa >= 0)
+ {
+ gpio_direction_output(gpio_pa, !gpio_active);
+ }
+#endif
+ return 0;
+}
+
+static int wmt_i2s_resume(struct snd_soc_dai *cpu_dai)
+{
+ printk("%s!\n", __func__);
+
+ DBG_DETAIL();
+
+ i2s_init(1);
+
+ wmt_i2s_ch_config();
+#if 1
+ if (gpio_pa >= 0)
+ {
+ gpio_direction_output(gpio_pa, gpio_active);
+ mdelay(50);
+ }
+#endif
+ info("Resume: CHCFG0=0x%x, HDACHCFG=0x%x", ASMPFCHCFG0_VAL, ASMPF2HDACHCFG_VAL);
+ return 0;
+}
+#else
+#define wmt_i2s_suspend NULL
+#define wmt_i2s_resume NULL
+#endif
+
+static struct snd_soc_dai_ops wmt_i2s_dai_ops = {
+ .startup = wmt_i2s_dai_startup,
+ .prepare = wmt_i2s_prepare,
+ .shutdown = wmt_i2s_dai_shutdown,
+ .trigger = wmt_i2s_dai_trigger,
+ .hw_params = wmt_i2s_dai_hw_params,
+ .set_fmt = wmt_i2s_dai_set_dai_fmt,
+};
+
+struct snd_soc_dai_driver wmt_i2s_dai = {
+ .suspend = wmt_i2s_suspend,
+ .resume = wmt_i2s_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_FLOAT,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &wmt_i2s_dai_ops,
+};
+
+static int wmt_i2s_probe(struct platform_device *pdev)
+{
+ int ret;
+ char buf[64];
+ int varlen = 64;
+
+ DBG_DETAIL();
+
+ ret = wmt_getsyspara("wmt.audio.pcm", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "%d", &wmt_pdm_module_enable);
+ }
+
+ ret = wmt_getsyspara("wmt.audio.i2s", buf, &varlen);
+ if (ret == 0) {
+ if (!strncmp(buf, "wm8994", strlen("wm8994")))
+ wmt_codec_wm8994 = 1;
+ }
+ memset(buf, 0, sizeof(buf));
+ ret = wmt_getsyspara("wmt.audio.codechdmi", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "%d", &i2s_data[0].HDMI_and_DAC0);
+ printk("<<<%s codec and hdmi:%d\n", __FUNCTION__, i2s_data[0].HDMI_and_DAC0);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ //0:i2sdacdat0(usually use i2s function!!) 1:i2sdacdat1 2:i2sdacdat2 3:i2sdacdat3
+ ret = wmt_getsyspara("wmt.audio.dacdat.gpio", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "%d", &i2sdacdat_gpio);
+ printk("<<<%s i2sdacdat_gpio:%d\n", __FUNCTION__, i2sdacdat_gpio);
+ }
+
+
+ memset(buf, 0, sizeof(buf));
+ ret = wmt_getsyspara("wmt.audio.interface.mode", buf, &varlen);
+ if (ret == 0) { // 0:i2s 1:left justified 2:right justified
+ sscanf(buf, "%d", &audio_interface_mode);
+ printk("<<<%s audio_interface_mode:%d\n", __FUNCTION__, audio_interface_mode);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ ret = wmt_getsyspara("wmt.audio.pa", buf, &varlen);
+ if (ret == 0) { // gpio nr:active---> 1:0
+ sscanf(buf, "%d:%d", &gpio_pa, &gpio_active);
+
+ }
+ printk("%s audio pa:%d:%d\n", __FUNCTION__, gpio_pa, gpio_active);
+ if (gpio_pa >= 0)
+ {
+ ret = gpio_request(gpio_pa, "audio pa");
+ if (ret)
+ {
+ printk("%s gpio %d request error!\n", __func__, gpio_pa);
+ return ret;
+ }
+ #if 0
+ if (gpio_active)
+ wmt_gpio_setpull(gpio_pa, WMT_GPIO_PULL_DOWN);
+ else
+ wmt_gpio_setpull(gpio_pa, WMT_GPIO_PULL_UP);
+ msleep(10);
+ gpio_direction_output(gpio_pa, !gpio_active); //???pop???
+ printk("%s shutdown pa!\n", __func__);
+ msleep(100);
+ #endif
+
+
+ init_timer(&pa_timer);
+ pa_timer.function = pa_timer_handler;
+ }
+ i2s_data->s[0].dma_cfg = dma_device_cfg_table[AHB1_AUD_DMA_REQ_1];
+ i2s_data->s[1].dma_cfg = dma_device_cfg_table[AHB1_AUD_DMA_REQ_0];
+ /*init i2s controller*/
+ i2s_data->i2s.init(0);
+
+ spin_lock_init(&i2s_data->s[0].dma_lock);
+ spin_lock_init(&i2s_data->s[1].dma_lock);
+ /*init_timer(&i2s_data->delay_timer);
+ i2s_data->delay_timer.function = delay_timer_handler;*/
+
+ /* register with the ASoC layers */
+ ret = snd_soc_register_dai(&pdev->dev, &wmt_i2s_dai);
+ if (ret) {
+ pr_err("Failed to register DAI: %d\n", ret);
+ return ret;
+ }
+
+ if (gpio_pa >= 0)
+ {
+ mod_timer(&pa_timer, jiffies+msecs_to_jiffies(10000));
+ }
+ return 0;
+}
+
+static int __devexit wmt_i2s_remove(struct platform_device *pdev)
+{
+ DBG_DETAIL();
+
+ snd_soc_unregister_dai(&pdev->dev);
+
+ return 0;
+}
+
+static void wmt_i2s_plat_shutdown(struct platform_device *pdev)
+{
+ printk("%s!\n", __func__);
+ if (gpio_pa >= 0)
+ {
+ gpio_direction_output(gpio_pa, !gpio_active);
+ }
+}
+
+static struct platform_driver wmt_i2s_driver = {
+ .probe = wmt_i2s_probe,
+ .remove = __devexit_p(wmt_i2s_remove),
+ //.suspend = wmt_i2s_plat_suspend,
+ //.resume = wmt_i2s_plat_resume,
+ .shutdown = wmt_i2s_plat_shutdown,
+ .driver = {
+ .name = "wmt-i2s",
+ .owner = THIS_MODULE,
+ },
+};
+
+
+static int __init wmt_i2s_init(void)
+{
+ DBG_DETAIL();
+
+ return platform_driver_register(&wmt_i2s_driver);
+}
+
+static void __exit wmt_i2s_exit(void)
+{
+ DBG_DETAIL();
+
+ platform_driver_unregister(&wmt_i2s_driver);
+}
+
+module_init(wmt_i2s_init);
+module_exit(wmt_i2s_exit);
+
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_DESCRIPTION("WMT [ALSA SoC] driver");
+MODULE_LICENSE("GPL");
+
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-controller.c b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-controller.c
new file mode 100755
index 00000000..dccb82c6
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-controller.c
@@ -0,0 +1,419 @@
+/*++
+ * linux/sound/soc/wmt/wmt-pdm-if.c
+ * WonderMedia I2S audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <mach/hardware.h>
+#include <asm/dma.h>
+#include "wmt-soc.h"
+#include "wmt-pcm-controller.h"
+
+#define PCM_IS_MASTER_MODE
+#define NULL_DMA ((dmach_t)(-1))
+
+static int wmt_pcm_module_enable = 0;
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+static struct audio_stream_a wmt_pcm_controller_data[] = {
+ {
+ .id = "WMT PCM out",
+ .stream_id = SNDRV_PCM_STREAM_PLAYBACK,
+ .dmach = NULL_DMA,
+ .dma_dev = PCM_TX_DMA_REQ,
+ /*.dma_cfg = dma_device_cfg_table[I2S_TX_DMA_REQ],*/
+ },
+ {
+ .id = "WMT PCM in",
+ .stream_id = SNDRV_PCM_STREAM_CAPTURE,
+ .dmach = NULL_DMA,
+ .dma_dev = PCM_RX_DMA_REQ,
+ /*.dma_cfg = dma_device_cfg_table[I2S_RX_DMA_REQ],*/
+ },
+};
+
+struct wmt_pcm_controller
+{
+ int irq_no;
+ int pcm_clk_src;
+ int pcm_enable;
+};
+
+static struct wmt_pcm_controller wmt_pcm_controller;
+
+static irqreturn_t
+wmt_pcm_controller_irq_handler(int irq, void *dev_id)
+{
+ if (PCMSR_VAL & PCMSR_TXUND) {
+ printk("-->PCMSR_TXUND\n");
+ }
+ else if (PCMSR_VAL & PCMSR_RXOVR) {
+ printk("-->PCMSR_RXOVR\n");
+ }
+
+ PCMSR_VAL = 0x7F; //write clear all intr
+ return IRQ_HANDLED;
+}
+
+static void wmt_pcm_controller_enable(void)
+{
+ PCMCR_VAL |= (PCMCR_PCM_ENABLE | PCMCR_DMA_EN);
+}
+
+static void wmt_pcm_controller_disable(void)
+{
+ PCMCR_VAL &= ~(PCMCR_PCM_ENABLE | PCMCR_DMA_EN);
+}
+
+static int wmt_pcm_controller_init(void)
+{
+ wmt_pcm_controller.irq_no = IRQ_PCM;
+
+ // Before control pcm-module, enable pcm clock first
+ CLOCKEN(27);
+
+ // set pcm_clk_source = 62464khz
+ //auto_pll_divisor(DEV_PCM0, CLK_ENABLE, 0, 0);
+ //auto_pll_divisor(DEV_PCM0, SET_PLLDIV, 1, 83333);
+
+ wmt_pcm_controller.pcm_clk_src = auto_pll_divisor(DEV_PCM0, GET_FREQ, 0, 0);
+ wmt_pcm_controller.pcm_clk_src /= 1000;
+ wmt_pcm_controller.pcm_enable = 0;
+ printk("wmt_pcm_controller_init: pcm_clk_src=%d \n\r", wmt_pcm_controller.pcm_clk_src);
+
+ // Note: you should config PIN_SHARING_SEL_4BYTE_VAL if using pcm function!!! Loon mark at 2013/4/10
+ if (wmt_pcm_module_enable) {
+ printk("begin to configure pcm pin\n");
+ /* disable GPIO and Pull Down mode */
+ /* Bit1:I2SDACDAT1=PCMSYNC, Bit2:I2SDACDAT2=PCMCLK, Bit3:I2SDACDAT3=PCMIN, Bit4:I2SADCMCLK=PCMOUT */
+ GPIO_CTRL_GP10_I2S_BYTE_VAL &= ~(BIT1 | BIT2 | BIT3 | BIT4);
+ GPIO_CTRL_GP11_I2S_BYTE_VAL &= ~(BIT1);
+ /*disable pull enable*/
+ PULL_EN_GP10_I2S_BYTE_VAL &= ~(BIT1 | BIT2 | BIT3 | BIT4);
+ PULL_EN_GP11_I2S_BYTE_VAL &= ~(BIT1);
+
+ /* set to pcm mode */
+ // select PCMMCLK[bit0], PCMSYNC[bit17:16], PCMCLK[19:18], PCMIN[20], PCMOUT[22:21]
+ //GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~(BIT21);
+ GPIO_PIN_SHARING_SEL_4BYTE_VAL |= (BIT15 | BIT16 | BIT17 | BIT18 | BIT19 |BIT20);
+ }
+
+ // set pcm control register
+ PCMCR_VAL |= (PCMCR_TXFF_RST | PCMCR_RXFF_RST);
+ //PCMCR_VAL |= 0x00880000; // TX/RX Fifo Threshold A
+ PCMCR_VAL &= ~(PCMCR_BCLK_SEL);
+ PCMCR_VAL &= ~PCMCR_SLAVE; // master mode
+ PCMCR_VAL |= PCMCR_SYNC_MODE;//short frame sync
+
+ // set pcm format register
+ PCMDFCR_VAL = 0;
+ PCMDFCR_VAL |= (PCMDFCR_WR_AL | PCMDFCR_TX_AL | PCMDFCR_RX_AL | PCMDFCR_RD_AL);
+ PCMDFCR_VAL |= (PCMDFCR_TX_SZ_14 | PCMDFCR_RX_SZ_14);
+ //PCMDFCR_VAL |= (PCMDFCR_TX_SZ_08 | PCMDFCR_RX_SZ_08);
+
+ //
+ // request irq
+ //
+ /*if (request_irq(wmt_pcm_controller.irq_no, &wmt_pcm_controller_irq_handler, IRQF_DISABLED, "wmt_pcm_controller", NULL)){
+ printk(KERN_ERR "PCM_IRQ Request Failed!\n");
+ }
+ PCMCR_VAL |= (PCMCR_IRQ_EN | PCMCR_TXUND_EN | PCMCR_RXOVR_EN);
+ */
+ printk("fun:%s,line:%d\n",__func__,__LINE__);
+ return 0 ;
+}
+
+
+static int wmt_pcm_controller_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int stream_id = substream->pstr->stream;
+ struct audio_stream_a *s = &wmt_pcm_controller_data[0];
+ //dump_stack();
+ s[stream_id].stream = substream;
+ runtime->private_data = s;
+
+ return 0;
+}
+
+static void wmt_pcm_controller_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ //dump_stack();
+}
+
+static int wmt_pcm_controller_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int err = 0;
+ int stream_id = substream->pstr->stream;
+ //dump_stack();
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ PCMCR_VAL |= PCMCR_TXFF_RST; // reset txfifo
+ PCMDFCR_VAL |= PCMDFCR_TXFM_EN;
+ }
+ else if (stream_id == SNDRV_PCM_STREAM_CAPTURE) {
+ PCMCR_VAL |= PCMCR_RXFF_RST; // reset rxfifo
+ PCMDFCR_VAL |= PCMDFCR_RXFM_EN;
+ }
+ //wmt_pcm_controller_enable();
+ wmt_pcm_controller.pcm_enable++;
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ PCMDFCR_VAL &= ~PCMDFCR_TXFM_EN;
+ }
+ else if (stream_id == SNDRV_PCM_STREAM_CAPTURE) {
+ PCMDFCR_VAL &= ~PCMDFCR_RXFM_EN;
+ }
+ //wmt_pcm_controller_disable();
+ wmt_pcm_controller.pcm_enable--;
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (wmt_pcm_controller.pcm_enable)
+ wmt_pcm_controller_enable();
+ else
+ wmt_pcm_controller_disable();
+
+ return err;
+}
+
+static int wmt_pcm_controller_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ //dump_stack();
+ return 0;
+}
+
+static int wmt_pcm_controller_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int channel, byte;
+ int stream_id = substream->pstr->stream;
+
+ byte = (runtime->sample_bits)/8;
+ channel = runtime->channels;
+
+ printk(KERN_INFO "wmt_pcm_controller_dai_prepare byte = %d, channels = %d\n", byte, runtime->channels);
+
+ /* format setting */
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* little or big endian check */
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ default:
+ break;
+ }
+
+ /* channel number check */
+ switch (runtime->channels) {
+ case 1:
+ break;
+ case 2:
+ break;
+ default:
+ break;
+ }
+ }
+ else if (stream_id == SNDRV_PCM_STREAM_CAPTURE) {
+ /* little or big endian check */
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ default:
+ break;
+ }
+
+ /* channel number check */
+ switch (runtime->channels) {
+ case 1:
+ break;
+ case 2:
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (runtime->rate) {
+ case 16000:
+ PCMDIVR_VAL &= ~PCMCLK_DIV_MASK;
+ PCMDIVR_VAL |= (wmt_pcm_controller.pcm_clk_src / PCMCLK_256K);
+ break;
+ case 8000:
+ PCMDIVR_VAL &= ~PCMCLK_DIV_MASK;
+ PCMDIVR_VAL |= (wmt_pcm_controller.pcm_clk_src / PCMCLK_128K);
+ break;
+ default :
+ printk(KERN_ERR "not supported fs: %d \n\r", runtime->rate);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * This must be called before _set_clkdiv and _set_sysclk since McBSP register
+ * cache is initialized here
+ */
+static int wmt_pcm_controller_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wmt_pcm_controller_suspend(struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+
+static int wmt_pcm_controller_resume(struct snd_soc_dai *cpu_dai)
+{
+ int ret = wmt_pcm_controller_init();
+ if (ret) {
+ pr_err("Failed to init pcm module: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+#else
+#define wmt_pcm_controller_suspend NULL
+#define wmt_pcm_controller_resume NULL
+#endif
+
+static struct snd_soc_dai_ops wmt_pcm_controller_dai_ops = {
+ .startup = wmt_pcm_controller_dai_startup,
+ .prepare = wmt_pcm_controller_dai_prepare,
+ .shutdown = wmt_pcm_controller_dai_shutdown,
+ .trigger = wmt_pcm_controller_dai_trigger,
+ .hw_params = wmt_pcm_controller_dai_hw_params,
+ .set_fmt = wmt_pcm_controller_dai_set_dai_fmt,
+};
+
+struct snd_soc_dai_driver wmt_pcm_controller_dai = {
+ .suspend = wmt_pcm_controller_suspend,
+ .resume = wmt_pcm_controller_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &wmt_pcm_controller_dai_ops,
+};
+
+static int wmt_pcm_controller_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ char buf[64];
+ int varlen = 64;
+
+ ret = wmt_getsyspara("wmt.audio.pcm", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "%d", &wmt_pcm_module_enable);
+ }
+
+ ret = wmt_pcm_controller_init();
+ if (ret) {
+ pr_err("Failed to init pcm module: %d\n", ret);
+ return ret;
+ }
+
+ /* register with the ASoC layers */
+ ret = snd_soc_register_dai(&pdev->dev, &wmt_pcm_controller_dai);
+ if (ret) {
+ pr_err("Failed to register DAI: %d\n", ret);
+ return ret;
+ }
+
+ wmt_pcm_controller_data[0].dma_cfg = dma_device_cfg_table[PCM_TX_DMA_REQ];
+ wmt_pcm_controller_data[1].dma_cfg = dma_device_cfg_table[PCM_RX_DMA_REQ];
+
+ spin_lock_init(&wmt_pcm_controller_data[0].dma_lock);
+ spin_lock_init(&wmt_pcm_controller_data[1].dma_lock);
+
+ return 0;
+}
+
+static int __devexit wmt_pcm_controller_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wmt_pcm_controller_driver = {
+ .probe = wmt_pcm_controller_probe,
+ .remove = __devexit_p(wmt_pcm_controller_remove),
+ .driver = {
+ .name = "wmt-pcm-controller",
+ .owner = THIS_MODULE,
+ },
+};
+
+
+static int __init wmt_pcm_controller_module_init(void)
+{
+ return platform_driver_register(&wmt_pcm_controller_driver);
+}
+
+static void __exit wmt_pcm_controller_module_exit(void)
+{
+ platform_driver_unregister(&wmt_pcm_controller_driver);
+}
+
+module_init(wmt_pcm_controller_module_init);
+module_exit(wmt_pcm_controller_module_exit);
+
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_DESCRIPTION("WMT [ALSA SoC] driver");
+MODULE_LICENSE("GPL");
+
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-controller.h b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-controller.h
new file mode 100755
index 00000000..58e94f23
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-controller.h
@@ -0,0 +1,122 @@
+/*++
+linux/include/asm-arm/arch-wmt/wmt_pcm.h
+
+Copyright (c) 2008 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+
+WonderMedia Technologies, Inc.
+10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
+--*/
+
+/* Be sure that virtual mapping is defined right */
+
+#ifndef __WMT_PCM_CONTROLLER_H
+#define __WMT_PCM_CONTROLLER_H
+
+/*
+ * Address
+ */
+#define PCM_CR_ADDR (0x0000+PCM_BASE_ADDR)
+#define PCM_SR_ADDR (0x0004+PCM_BASE_ADDR)
+/* Reserved 0x0008 ~ 0x000F */
+#define PCM_DFCR_ADDR (0x0008+PCM_BASE_ADDR)
+#define PCM_DIVR_ADDR (0x000C+PCM_BASE_ADDR)
+/* Reserved 0x0020 ~ 0x007F */
+#define PCM_TFIFO_ADDR (0x0010+PCM_BASE_ADDR)
+#define PCM_TFIFO_1_ADDR (0x0014+PCM_BASE_ADDR)
+
+#define PCM_RFIFO_ADDR (0x0030+PCM_BASE_ADDR)
+#define PCM_RFIFO_1_ADDR (0x0034+PCM_BASE_ADDR)
+/* Reserved 0x0100 ~ 0xFFFF */
+
+/*
+ * Value
+ */
+#define PCMCR_VAL (REG32_VAL(PCM_CR_ADDR))
+#define PCMSR_VAL (REG32_VAL(PCM_SR_ADDR))
+#define PCMDFCR_VAL (REG32_VAL(PCM_DFCR_ADDR))
+#define PCMDIVR_VAL (REG32_VAL(PCM_DIVR_ADDR))
+#define GPIO_PIN_SHARING_SEL_4BYTE_VAL (REG32_VAL(SHARE_PIN_SELEC))
+
+#define PCMCLK_DIV_MASK 0x7FF
+#define PLL_B_DIVF_MASK 0xFF0000
+#define PLL_B_DIVR_MASK 0x1F00
+#define PLL_B_DIVQ_MASK 0x07
+#define OSC_25M 25000
+#define PCMCLK_128K 128
+#define PCMCLK_256K 256
+
+
+//=============================================================================
+//
+// PCMCR PCM Control Register
+//
+//=============================================================================
+#define PCMCR_PCM_ENABLE (BIT0) /* PCM Interface Enable */
+#define PCMCR_SLAVE (BIT1) /* Master/Slave Mode */
+
+#define PCMCR_BCLK_SEL (BIT3) /* PCM_CLK Select */
+#define PCMCR_SYNC_MODE (BIT4) /* Frame Sync Mode: 0-long, 1-Short */
+#define PCMCR_DMA_EN (BIT5) /* DMA Enable */
+#define PCMCR_SYNC_OFF (BIT6) /* Sync Disable */
+#define PCMCR_MUTE (BIT7) /* Mute Enable */
+#define PCMCR_IRQ_EN (BIT8) /* Interrupt Enable */
+#define PCMCR_DMA_IRQ_SEL (BIT9) /* Threshold DMA/IRQ Select */
+#define PCMCR_RXFE_EN (BIT10) /* RX FIFO Empty Interrupt Enable */
+#define PCMCR_RXFF_EN (BIT11) /* RX FIFO Full Interrupt Enable */
+#define PCMCR_RXOVR_EN (BIT12) /* RX FIFO Overrun Interrupt Enable */
+#define PCMCR_TXFE_EN (BIT13) /* TX FIFO Empty Interrupt Enable */
+#define PCMCR_TXUND_EN (BIT14) /* TX FIFO Underrun Interrupt Enable */
+
+#define PCMCR_TXFF_RST (BIT24) /* TX FIFO Reset */
+#define PCMCR_RXFF_RST (BIT25) /* RX FIFO Reset */
+
+
+//=============================================================================
+//
+// PCMSR PCM Status Register
+//
+//=============================================================================
+#define PCMSR_RXFE (BIT0) /* RX FIFO Empty Status */
+#define PCMSR_RXFF (BIT1) /* RX FIFO Full Status */
+#define PCMSR_RXOVR (BIT2) /* RX FIFO Overrun Status */
+#define PCMSR_TXFE (BIT3) /* TX FIFO Empty Status */
+#define PCMSR_TXUND (BIT4) /* TX FIFO Underrun Status */
+#define PCMSR_TXFAE (BIT5) /* TX FIFO Almost Empty Status */
+#define PCMSR_RXFAF (BIT6) /* RX FIFO Almost Full Status */
+
+
+//=============================================================================
+//
+// PCMDFCR PCM Data Format Control Register
+//
+//=============================================================================
+#define PCMDFCR_TXFM_EN (BIT0) /* TX Data Format Enable */
+#define PCMDFCR_TX_SZ_13 0x00 /* TX Input Data Size 13 bits wide */
+#define PCMDFCR_TX_SZ_14 0x02 /* TX Input Data Size 14 bits wide */
+#define PCMDFCR_TX_SZ_08 0x04 /* TX Input Data Size 8 bits wide */
+#define PCMDFCR_WR_AL (BIT3) /* TX Write Data Alignment Setup */
+#define PCMDFCR_TX_AL (BIT4) /* PCM_OUT Alignment Control */
+#define PCMDFCR_TX_PAD (BIT5) /* PCM_OUT Padding Control */
+#define PCMDFCR_TX_MSB (BIT6) /* PCM_OUT First Bit Select */
+
+#define PCMDFCR_RXFM_EN (BIT8) /* RX Data Format Enable */
+#define PCMDFCR_RX_SZ_13 0x0000 /* RCM_IN Data Size 13 bits wide */
+#define PCMDFCR_RX_SZ_14 0x0200 /* RCM_IN Data Size 14 bits wide */
+#define PCMDFCR_RX_SZ_08 0x0400 /* RCM_IN Data Size 8 bits wide */
+#define PCMDFCR_RX_AL (BIT11) /* RCM_IN Data Alignment Setup */
+#define PCMDFCR_RD_AL (BIT12) /* PX FIFO Alignment Control */
+#define PCMDFCR_RX_PAD (BIT13) /* PX FIFO Padding Control */
+#define PCMDFCR_RX_MSB (BIT14) /* RX FIFO First Bit Select */
+
+
+#endif /* __WMT_PCM_CONTROLLER_H */
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-dma.c b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-dma.c
new file mode 100755
index 00000000..27a66bea
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-dma.c
@@ -0,0 +1,597 @@
+/*++
+ * linux/sound/soc/wmt/wmt-pcm.c
+ * WonderMedia audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/delay.h>
+
+#include <asm/dma.h>
+#include "wmt-pcm-dma.h"
+#include "wmt-soc.h"
+
+#define NULL_DMA ((dmach_t)(-1))
+
+/*
+ * Debug
+ */
+#define AUDIO_NAME "WMT_PCM_DMA"
+//#define WMT_PCM_DEBUG 1
+//#define WMT_PCM_DEBUG_DETAIL 1
+
+#ifdef WMT_PCM_DEBUG
+#define DPRINTK(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#else
+#define DPRINTK(format, arg...) do {} while (0)
+#endif
+
+#ifdef WMT_PCM_DEBUG_DETAIL
+#define DBG_DETAIL(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": [%s]" format "\n" , __FUNCTION__, ## arg)
+#else
+#define DBG_DETAIL(format, arg...) do {} while (0)
+#endif
+
+#define err(format, arg...) \
+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) \
+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
+
+static const struct snd_pcm_hardware wmt_pcm_dma_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .period_bytes_min = 32,
+ .period_bytes_max = 4 * 1024,
+ .periods_min = 1,
+ .periods_max = 32,
+ .buffer_bytes_max = 16 * 1024,
+};
+
+static int audio_dma_free(struct audio_stream_a *s);
+
+/*
+ * Main dma routine, requests dma according where you are in main alsa buffer
+ */
+static void audio_process_dma(struct audio_stream_a *s)
+{
+ struct snd_pcm_substream *substream = s->stream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size;
+ unsigned int offset;
+ dma_addr_t dma_base;
+ int ret = 0;
+
+ DPRINTK("s: %d, dmach: %d. active: %d", (int)s, s->dmach, s->active);
+
+ if (s->active) {
+ substream = s->stream;
+ runtime = substream->runtime;
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+
+ if (dma_size > MAX_DMA_SIZE)
+ dma_size = CUT_DMA_SIZE;
+ offset = dma_size * s->period;
+
+ dma_base = __virt_to_phys((dma_addr_t)runtime->dma_area);
+
+ if ((runtime->channels == 2 || runtime->channels == 1) &&
+ (runtime->format == SNDRV_PCM_FORMAT_S16_LE)) {
+ ret = wmt_start_dma(s->dmach, runtime->dma_addr + offset, 0, dma_size);
+ }
+
+ if (ret) {
+ printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i) \n", ret);
+ return;
+ }
+
+ s->period++;
+ s->period %= runtime->periods;
+ s->periods++;
+ s->offset = offset;
+ }
+}
+
+/*
+ * This is called when dma IRQ occurs at the end of each transmited block
+ */
+static void audio_dma_callback(void *data)
+{
+ struct audio_stream_a *s = data;
+
+ //DBG_DETAIL();
+
+ /*
+ * If we are getting a callback for an active stream then we inform
+ * the PCM middle layer we've finished a period
+ */
+ if (s->active)
+ snd_pcm_period_elapsed(s->stream);
+
+ spin_lock(&s->dma_lock);
+ if (s->periods > 0)
+ s->periods--;
+
+ audio_process_dma(s);
+ spin_unlock(&s->dma_lock);
+}
+
+static int audio_dma_request(struct audio_stream_a *s, void (*callback) (void *))
+{
+ int err;
+ err = 0;
+
+ DBG_DETAIL();
+
+ //DPRINTK("s pointer: %d, dmach: %d, id: %s, dma_dev: %d", (int)s, s->dmach, s->id, s->dma_dev);
+ err = wmt_request_dma(&s->dmach, s->id, s->dma_dev, callback, s);
+ if (err < 0)
+ printk(KERN_ERR "Unable to grab audio dma 0x%x\n", s->dmach);
+
+ return err;
+}
+
+static void audio_setup_dma(struct audio_stream_a *s, int stream_id)
+{
+ struct snd_pcm_runtime *runtime = s->stream->runtime;
+
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* From memory to device */
+ switch (runtime->channels * runtime->format) {
+ case 1:
+ s->dma_cfg.DefaultCCR = PCM_TX_DMA_8BITS_CFG; /* setup 1 bytes*/
+ break ;
+ case 2:
+ s->dma_cfg.DefaultCCR = PCM_TX_DMA_16BITS_CFG; /* setup 2 bytes*/
+ break ;
+ case 4:
+ s->dma_cfg.DefaultCCR = PCM_TX_DMA_32BITS_CFG; /* setup 4 byte*/
+ break ;
+ }
+ }
+ else {
+ /* From device to memory */
+ switch (runtime->channels * runtime->format) {
+ case 1:
+ s->dma_cfg.DefaultCCR = PCM_RX_DMA_8BITS_CFG ; /* setup 1 bytes*/
+ break ;
+ case 2:
+ s->dma_cfg.DefaultCCR = PCM_RX_DMA_16BITS_CFG ; /* setup 2 bytes*/
+ break ;
+ case 4:
+ s->dma_cfg.DefaultCCR = PCM_RX_DMA_32BITS_CFG ; /* setup 4 byte*/
+ break ;
+ }
+ }
+
+ s->dma_cfg.ChunkSize = 1;
+
+ wmt_setup_dma(s->dmach, s->dma_cfg) ;
+}
+
+static int audio_dma_free(struct audio_stream_a *s)
+{
+ int err = 0;
+ DBG_DETAIL();
+ wmt_free_dma(s->dmach);
+ s->dmach = NULL_DMA;
+ return err;
+}
+
+/*
+ * this stops the dma and clears the dma ptrs
+ */
+static void audio_stop_dma(struct audio_stream_a *s)
+{
+ //dump_stack();
+ unsigned long flags;
+ DBG_DETAIL();
+ local_irq_save(flags);
+ s->active = 0;
+ s->period = 0;
+ s->periods = 0;
+ s->offset = 0;
+ wmt_stop_dma(s->dmach);
+ wmt_clear_dma(s->dmach);
+ local_irq_restore(flags);
+}
+
+/* this may get called several times by oss emulation */
+static int wmt_pcm_dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ //dump_stack();
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err = 0;
+ DBG_DETAIL();
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+ return err;
+}
+
+static int wmt_pcm_dma_hw_free(struct snd_pcm_substream *substream)
+{
+ DBG_DETAIL();
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+}
+
+static int wmt_pcm_dma_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int stream_id = substream->pstr->stream;
+ struct audio_stream_a *prtd = runtime->private_data;
+ struct audio_stream_a *s = &prtd[stream_id];
+ //dump_stack();
+ DBG_DETAIL();
+
+ s->period = 0;
+ s->periods = 0;
+ s->offset = 0;
+ audio_setup_dma(s, stream_id);
+
+ return 0;
+}
+
+static int wmt_pcm_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int stream_id = substream->pstr->stream;
+ struct audio_stream_a *prtd = runtime->private_data;
+ struct audio_stream_a *s = &prtd[stream_id];
+ int ret = 0;
+
+ DPRINTK("Enter, cmd=%d", cmd);
+ //dump_stack();
+ spin_lock(&s->dma_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ s->active = 1;
+ audio_process_dma(s);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ s->active = 0;
+ audio_stop_dma(s);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ spin_unlock(&s->dma_lock);
+
+ return ret;
+}
+
+static snd_pcm_uframes_t wmt_pcm_dma_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_stream_a *prtd = runtime->private_data;
+ int stream_id = substream->pstr->stream;
+ struct audio_stream_a *s = &prtd[stream_id];
+ dma_addr_t ptr;
+ snd_pcm_uframes_t offset = 0;
+ //dump_stack();
+ ptr = wmt_get_dma_pos(s->dmach);
+
+ if ((runtime->channels == 1 || runtime->channels == 2) && (runtime->format == SNDRV_PCM_FORMAT_S16_LE)) {
+ offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+ }
+
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+
+ spin_lock(&s->dma_lock);
+
+ if (s->periods > 0 && s->periods < 2) {
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (snd_pcm_playback_hw_avail(runtime) >= 2 * runtime->period_size)
+ audio_process_dma(s);
+ }
+ else {
+ if (snd_pcm_capture_hw_avail(runtime) >= 2* runtime->period_size)
+ audio_process_dma(s);
+ }
+
+ }
+ spin_unlock(&s->dma_lock);
+
+ return offset;
+}
+
+static int wmt_pcm_dma_open(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_stream_a *s = runtime->private_data;
+ int ret;
+
+ DBG_DETAIL();
+
+ if (!cpu_dai->active) {
+ audio_dma_request(&s[0], audio_dma_callback);
+ audio_dma_request(&s[1], audio_dma_callback);
+ }
+ //dump_stack();
+ snd_soc_set_runtime_hwparams(substream, &wmt_pcm_dma_hardware);
+
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+out:
+ return ret;
+}
+
+static int wmt_pcm_dma_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_stream_a *s = runtime->private_data;
+
+ DBG_DETAIL();
+ //dump_stack();
+ if (!cpu_dai->active) {
+ audio_dma_free(&s[0]);
+ audio_dma_free(&s[1]);
+ }
+
+ return 0;
+}
+
+static int wmt_pcm_dma_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ DBG_DETAIL();
+ //dump_stack();
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops wmt_pcm_dma_ops = {
+ .open = wmt_pcm_dma_open,
+ .close = wmt_pcm_dma_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = wmt_pcm_dma_hw_params,
+ .hw_free = wmt_pcm_dma_hw_free,
+ .prepare = wmt_pcm_dma_prepare,
+ .trigger = wmt_pcm_dma_trigger,
+ .pointer = wmt_pcm_dma_pointer,
+ .mmap = wmt_pcm_dma_mmap,
+};
+
+static u64 wmt_pcm_dma_dmamask = DMA_BIT_MASK(32);
+
+static int wmt_pcm_dma_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 = wmt_pcm_dma_hardware.buffer_bytes_max;
+
+ DBG_DETAIL();
+
+ 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);
+
+ DPRINTK("buf_area = %x, buf_addr = %x", (unsigned int)buf->area, buf->addr);
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+
+ return 0;
+}
+
+static void wmt_pcm_dma_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ DBG_DETAIL();
+
+ 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 wmt_pcm_dma_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret = 0;
+
+ DBG_DETAIL();
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &wmt_pcm_dma_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 = wmt_pcm_dma_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ret = wmt_pcm_dma_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+
+out:
+ /* free preallocated buffers in case of error */
+ if (ret)
+ wmt_pcm_dma_free_dma_buffers(pcm);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int wmt_pcm_dma_suspend(struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct audio_stream_a *prtd;
+ struct audio_stream_a *s;
+
+ DBG_DETAIL();
+
+ if (!runtime)
+ return 0;
+
+ prtd = runtime->private_data;
+ s = &prtd[SNDRV_PCM_STREAM_PLAYBACK];
+
+ if (s->active) {
+ udelay(5);
+ wmt_stop_dma(s->dmach);
+ /*
+ wmt_clear_dma(s->dmach);
+ audio_stop_dma(s);
+ */
+ }
+
+ s = &prtd[SNDRV_PCM_STREAM_CAPTURE];
+
+ if (s->active) {
+ udelay(5);
+ wmt_stop_dma(s->dmach);
+ /*
+ wmt_clear_dma(s->dmach);
+ audio_stop_dma(s);
+ */
+ }
+
+ return 0;
+}
+
+static int wmt_pcm_dma_resume(struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct audio_stream_a *prtd;
+ struct audio_stream_a *s;
+
+ DBG_DETAIL();
+
+ if (!runtime)
+ return 0;
+
+ prtd = runtime->private_data;
+ s = &prtd[SNDRV_PCM_STREAM_PLAYBACK];
+ audio_setup_dma(s, SNDRV_PCM_STREAM_PLAYBACK);
+
+ if (s->active) {
+ wmt_resume_dma(s->dmach) ;
+ }
+
+ s = &prtd[SNDRV_PCM_STREAM_CAPTURE];
+ audio_setup_dma(s, SNDRV_PCM_STREAM_CAPTURE);
+
+ if (s->active) {
+ wmt_resume_dma(s->dmach) ;
+ }
+
+ return 0;
+}
+#else
+#define wmt_pcm_dma_suspend NULL
+#define wmt_pcm_dma_resume NULL
+#endif
+
+static struct snd_soc_platform_driver wmt_soc_platform = {
+ .ops = &wmt_pcm_dma_ops,
+ .pcm_new = wmt_pcm_dma_new,
+ .pcm_free = wmt_pcm_dma_free_dma_buffers,
+ .suspend = wmt_pcm_dma_suspend,
+ .resume = wmt_pcm_dma_resume,
+};
+
+static int __devinit wmt_pcm_dma_platform_probe(struct platform_device *pdev)
+{
+ DBG_DETAIL();
+ return snd_soc_register_platform(&pdev->dev, &wmt_soc_platform);
+}
+
+static int __devexit wmt_pcm_dma_platform_remove(struct platform_device *pdev)
+{
+ DBG_DETAIL();
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wmt_pcm_dma_driver = {
+ .driver = {
+ .name = "wmt-pcm-dma",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = wmt_pcm_dma_platform_probe,
+ .remove = __devexit_p(wmt_pcm_dma_platform_remove),
+};
+
+module_platform_driver(wmt_pcm_dma_driver);
+
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_DESCRIPTION("WMT [ALSA SoC/pcm dma] driver");
+MODULE_LICENSE("GPL");
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-dma.h b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-dma.h
new file mode 100755
index 00000000..88f246b2
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm-dma.h
@@ -0,0 +1,35 @@
+/*++
+ * linux/sound/soc/wmt/wmt-pcm.h
+ * WonderMedia audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+
+#ifndef __WMT_PCM_DMA_H__
+#define __WMT_PCM_DMA_H__
+
+struct wmt_pcm_dma_data {
+ char *name; /* stream identifier */
+ int dma_req; /* DMA request line */
+ unsigned long port_addr; /* transmit/receive register */
+ struct dma_device_cfg_s *dma_cfg;
+};
+
+#endif /* __WMT_PDM_PCM_H__ */
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm.c b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm.c
new file mode 100755
index 00000000..848ea5a1
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm.c
@@ -0,0 +1,767 @@
+/*++
+ * linux/sound/soc/wmt/wmt-pcm.c
+ * WonderMedia audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/delay.h>
+
+#include <asm/dma.h>
+#include "wmt-pcm.h"
+#include "wmt-soc.h"
+
+#define NULL_DMA ((dmach_t)(-1))
+
+#define AUDIO_NAME "WMT_PCM"
+//#define WMT_PCM_DEBUG 1
+//#define WMT_PCM_DEBUG_DETAIL 1
+
+#ifdef WMT_PCM_DEBUG
+#define DPRINTK(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#else
+#define DPRINTK(format, arg...) do {} while (0)
+#endif
+
+#ifdef WMT_PCM_DEBUG_DETAIL
+#define DBG_DETAIL(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": [%s]" format "\n" , __FUNCTION__, ## arg)
+#else
+#define DBG_DETAIL(format, arg...) do {} while (0)
+#endif
+
+#define err(format, arg...) \
+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) \
+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
+
+static const struct snd_pcm_hardware wmt_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .period_bytes_min = 4*1024,//32,
+ .period_bytes_max = 8*1024,//4 * 1024,
+ .periods_min = 1,//2,
+ .periods_max = 16,//128,
+ .buffer_bytes_max = 64 * 1024,
+ .fifo_size = 32,//
+};
+
+struct wmt_runtime_data {
+ spinlock_t lock;
+ struct wmt_pcm_dma_data *dma_data;
+ int dma_ch;
+ int period_index;
+};
+
+struct snd_wfd_buffer {
+ struct device *dev;
+ unsigned char *area; /* virtual pointer */
+ dma_addr_t addr; /* physical address */
+ size_t bytes; /* buffer size in bytes */
+ size_t valuable_sz; /* valuable size in wfd buffer */
+ unsigned char *wr_ptr;
+ unsigned char *rd_ptr;
+ int enable;
+};
+static struct snd_wfd_buffer wfd_audbuf;
+
+static int audio_dma_free(struct audio_stream_a *s);
+
+dmach_t pcm_out_dmach = 0xFF;
+struct dma_device_cfg_s *pcm_out_dma_cfg = NULL;
+
+void wmt_pcm_wfd_start(void)
+{
+ /* allocate buffer for WFD support */
+ wfd_audbuf.area = dma_alloc_writecombine(wfd_audbuf.dev, wfd_audbuf.bytes,
+ &wfd_audbuf.addr, GFP_KERNEL);
+
+ if (!wfd_audbuf.area) {
+ err("WFD_Aud allocate buffer fail");
+ }
+ else {
+ info("*WFD_Aud enable");
+ wfd_audbuf.enable = 1;
+ }
+
+ memset(wfd_audbuf.area, 0x0, wfd_audbuf.bytes);
+ //info("&wfd_audbuf.addr=0x%x wfd_audbuf.addr=0x%x", &wfd_audbuf.addr, (unsigned int)wfd_audbuf.addr);
+}
+EXPORT_SYMBOL(wmt_pcm_wfd_start);
+
+unsigned int wmt_pcm_wfd_get_buf(void)
+{
+ wfd_audbuf.valuable_sz = 0;
+ wfd_audbuf.wr_ptr = wfd_audbuf.rd_ptr = wfd_audbuf.area;
+ return (unsigned int)&wfd_audbuf.addr;
+}
+EXPORT_SYMBOL(wmt_pcm_wfd_get_buf);
+
+void wmt_pcm_wfd_stop(void)
+{
+ wfd_audbuf.wr_ptr = wfd_audbuf.area;
+ wfd_audbuf.enable = 0;
+
+ dma_free_writecombine(wfd_audbuf.dev, wfd_audbuf.bytes,
+ wfd_audbuf.area, wfd_audbuf.addr);
+ info("*WFD_Aud disable");
+ return;
+}
+EXPORT_SYMBOL(wmt_pcm_wfd_stop);
+
+int wmt_pcm_wfd_get_strm(WFDStrmInfo_t *info)
+{
+ if ((info->req_sz > wfd_audbuf.bytes) || (!wfd_audbuf.valuable_sz)) {
+ //info("WFD read size=%d, Too Large!", info->req_sz);
+ info->avail_sz = 0;
+ return (int)info;
+ }
+ else if (wfd_audbuf.valuable_sz > info->req_sz) {
+ info->avail_sz = info->req_sz;
+ wfd_audbuf.valuable_sz -= info->req_sz;
+ }
+ else {
+ info->avail_sz = wfd_audbuf.valuable_sz;
+ wfd_audbuf.valuable_sz = 0;
+ }
+
+ info->buf_offset = wfd_audbuf.rd_ptr - wfd_audbuf.area;
+ wfd_audbuf.rd_ptr += info->avail_sz;
+
+ if (wfd_audbuf.rd_ptr >= wfd_audbuf.area + wfd_audbuf.bytes)
+ wfd_audbuf.rd_ptr = wfd_audbuf.rd_ptr - wfd_audbuf.bytes;
+
+ return (int)info;
+}
+EXPORT_SYMBOL(wmt_pcm_wfd_get_strm);
+
+void wmt_pcm_wfd_update(char *src_buf, unsigned int chunksize)
+{
+ //info("wmt_pcm_wfd_update, 0x%x", src_buf[1024]);
+ memcpy(wfd_audbuf.wr_ptr, src_buf, chunksize);
+ wfd_audbuf.wr_ptr += chunksize;
+
+ if (wfd_audbuf.wr_ptr == wfd_audbuf.area + wfd_audbuf.bytes)
+ wfd_audbuf.wr_ptr = wfd_audbuf.area;
+
+ wfd_audbuf.valuable_sz += chunksize;
+ if (wfd_audbuf.valuable_sz >= wfd_audbuf.bytes) {
+ wfd_audbuf.valuable_sz = wfd_audbuf.bytes;
+ wfd_audbuf.rd_ptr = wfd_audbuf.wr_ptr;
+ }
+}
+
+/*
+ * Main dma routine, requests dma according where you are in main alsa buffer
+ */
+static void audio_process_dma(struct audio_stream_a *s)
+{
+ struct snd_pcm_substream *substream = s->stream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size;
+ unsigned int offset;
+ dma_addr_t dma_base;
+ int ret = 0;
+
+ //DBG_DETAIL();
+ DPRINTK("s: %d, dmach: %d. active: %d", (int)s, s->dmach, s->active);
+
+ if (s->active) {
+ substream = s->stream;
+ runtime = substream->runtime;
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ /*DPRINTK("frame_bits=%d, period_size=%d, dma_size 1=%d",
+ runtime->frame_bits, (int)runtime->period_size, dma_size);*/
+ if (dma_size > MAX_DMA_SIZE)
+ dma_size = CUT_DMA_SIZE;
+ offset = dma_size * s->period;
+
+ /*DPRINTK("offset: 0x%x, ->dma_area: 0x%x, ->dma_addr: 0x%x, final addr: 0x%x",
+ offset, (unsigned int)runtime->dma_area, runtime->dma_addr, runtime->dma_addr+offset);*/
+
+ dma_base = __virt_to_phys((dma_addr_t)runtime->dma_area);
+
+ //DPRINTK("dma address: 0x%x", dma_base+offset);
+ /*DPRINTK("hw_ptr_interrupt: 0x%x, state: %d, hwptr: %u, applptr: %u, avail_min: %u",
+ (unsigned int)runtime->hw_ptr_interrupt, runtime->status->state, (unsigned int)runtime->status->hw_ptr,
+ (unsigned int)runtime->control->appl_ptr, (unsigned int)runtime->control->avail_min);*/
+ //DPRINTK("dmach: %u, dma_addr: %x, dma_size: %u", s->dmach, dma_base+offset, dma_size);
+
+ if ((runtime->channels == 2 || runtime->channels == 1) &&
+ (runtime->format == SNDRV_PCM_FORMAT_S16_LE)) {
+ ret = wmt_start_dma(s->dmach, runtime->dma_addr + offset, 0, dma_size);
+ }
+
+ if (ret) {
+ printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i) \n", ret);
+ return;
+ }
+
+ s->period++;
+ s->period %= runtime->periods;
+ s->periods++;
+ s->offset = offset;
+ }
+}
+
+/*
+ * This is called when dma IRQ occurs at the end of each transmited block
+ */
+static void audio_dma_callback(void *data)
+{
+ struct audio_stream_a *s = data;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size;
+ unsigned int offset;
+ int stream_id;
+
+ //DBG_DETAIL();
+
+ substream = s->stream;
+ runtime = substream->runtime;
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ stream_id = substream->pstr->stream;
+
+ if (s->period > 0)
+ offset = dma_size * (s->period - 1);
+ else
+ offset = dma_size * (s->periods - 1);
+
+ if ((stream_id == SNDRV_PCM_STREAM_PLAYBACK) && (wfd_audbuf.enable)) {
+ wmt_pcm_wfd_update(runtime->dma_area + offset, dma_size);
+ }
+
+ /*
+ * If we are getting a callback for an active stream then we inform
+ * the PCM middle layer we've finished a period
+ */
+ if (s->active)
+ snd_pcm_period_elapsed(s->stream);
+
+ spin_lock(&s->dma_lock);
+ if (s->periods > 0)
+ s->periods--;
+
+ audio_process_dma(s);
+ spin_unlock(&s->dma_lock);
+}
+
+static int audio_dma_request(struct audio_stream_a *s, void (*callback) (void *))
+{
+ int err;
+ err = 0;
+
+ DBG_DETAIL();
+
+ //DPRINTK("s pointer: %d, dmach: %d, id: %s, dma_dev: %d", (int)s, s->dmach, s->id, s->dma_dev);
+ err = wmt_request_dma(&s->dmach, s->id, s->dma_dev, callback, s);
+ if (err < 0)
+ printk(KERN_ERR "Unable to grab audio dma 0x%x\n", s->dmach);
+
+ if (!strcmp(s->id, "WMT I2S out")) {
+ pcm_out_dmach = s->dmach;
+ }
+ return err;
+}
+
+static void audio_setup_dma(struct audio_stream_a *s, int stream_id)
+{
+ struct snd_pcm_runtime *runtime = s->stream->runtime;
+
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* From memory to device */
+ switch (runtime->channels * runtime->format) {
+ #if 0 //comment 2014-1-3 tinyplay mono pattern abnormal
+ case 1:
+ s->dma_cfg.DefaultCCR = I2S_TX_DMA_8BITS_CFG; /* setup 1 bytes*/
+ break ;
+ case 2:
+ s->dma_cfg.DefaultCCR = I2S_TX_DMA_16BITS_CFG; /* setup 2 bytes*/
+ break ;
+ case 4:
+ #endif
+ default :
+ s->dma_cfg.DefaultCCR = I2S_TX_DMA_32BITS_CFG; /* setup 4 byte*/
+ break ;
+ }
+ }
+ else {
+ /* From device to memory */
+ switch (runtime->channels * runtime->format) {
+ case 1:
+ s->dma_cfg.DefaultCCR = I2S_RX_DMA_8BITS_CFG ; /* setup 1 bytes*/
+ break ;
+ case 2:
+ s->dma_cfg.DefaultCCR = I2S_RX_DMA_16BITS_CFG ; /* setup 2 bytes*/
+ break ;
+ case 4:
+ s->dma_cfg.DefaultCCR = I2S_RX_DMA_32BITS_CFG ; /* setup 4 byte*/
+ break ;
+ }
+ }
+
+ s->dma_cfg.ChunkSize = 1;
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ pcm_out_dma_cfg = &s->dma_cfg;
+ }
+
+ //DPRINTK("s pointer: %d. audio dma %d cfg.DefaultCCR 0x%x ", (int)s, s->dmach, (unsigned int)s->dma_cfg.DefaultCCR);
+ //DPRINTK("cfg.ChunkSize 0x%x ", s->dma_cfg.ChunkSize);
+ wmt_setup_dma(s->dmach, s->dma_cfg) ;
+}
+
+static int audio_dma_free(struct audio_stream_a *s)
+{
+ int err = 0;
+ DBG_DETAIL();
+ wmt_free_dma(s->dmach);
+ s->dmach = NULL_DMA;
+ pcm_out_dma_cfg = NULL;
+ return err;
+}
+
+/*
+ * this stops the dma and clears the dma ptrs
+ */
+static void audio_stop_dma(struct audio_stream_a *s)
+{
+ unsigned long flags;
+ DBG_DETAIL();
+ local_irq_save(flags);
+ s->active = 0;
+ s->period = 0;
+ s->periods = 0;
+ s->offset = 0;
+ s->last_offset = 0;
+ wmt_stop_dma(s->dmach);
+ wmt_clear_dma(s->dmach);
+ local_irq_restore(flags);
+}
+
+/* this may get called several times by oss emulation */
+static int wmt_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err = 0;
+ DBG_DETAIL();
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+ return err;
+}
+
+static int wmt_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ DBG_DETAIL();
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+}
+
+static int wmt_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int stream_id = substream->pstr->stream;
+ struct audio_stream_a *prtd = runtime->private_data;
+ struct audio_stream_a *s = &prtd[stream_id];
+
+ DBG_DETAIL();
+
+ s->period = 0;
+ s->periods = 0;
+ s->offset = 0;
+ s->last_offset = 0;
+ audio_setup_dma(s, stream_id);
+
+ return 0;
+}
+
+static int wmt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int stream_id = substream->pstr->stream;
+ struct audio_stream_a *prtd = runtime->private_data;
+ struct audio_stream_a *s = &prtd[stream_id];
+ int ret = 0;
+
+ DPRINTK("wmt_pcm_trigger Enter, cmd=%d", cmd);
+
+ spin_lock(&s->dma_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ s->active = 1;
+ audio_process_dma(s);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ s->active = 0;
+ audio_stop_dma(s);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ spin_unlock(&s->dma_lock);
+
+ return ret;
+}
+
+static snd_pcm_uframes_t wmt_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_stream_a *prtd = runtime->private_data;
+ int stream_id = substream->pstr->stream;
+ struct audio_stream_a *s = &prtd[stream_id];
+ dma_addr_t ptr;
+ snd_pcm_uframes_t offset = 0;
+
+ ptr = wmt_get_dma_pos(s->dmach);
+
+ if ((runtime->channels == 1 || runtime->channels == 2) && (runtime->format == SNDRV_PCM_FORMAT_S16_LE)) {
+ offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+ }
+
+ if ((offset < s->last_offset) && ((s->last_offset - offset) < runtime->period_size) &&
+ (s->last_offset != runtime->buffer_size)) {
+ snd_pcm_uframes_t old_offset = offset;
+ if (s->last_offset < runtime->period_size)
+ offset = runtime->period_size;
+ else {
+ offset = runtime->period_size *
+ ((s->last_offset / runtime->period_size) + 1);
+ }
+ printk("last_offset %d, old offset %d, new offset %d\n", (int)s->last_offset, (int)old_offset, (int)offset);
+ }
+ s->last_offset = offset;
+
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+
+ spin_lock(&s->dma_lock);
+
+ if (s->periods > 0 && s->periods < 3) {
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (snd_pcm_playback_hw_avail(runtime) >= 3 * runtime->period_size)
+ audio_process_dma(s);
+ }
+ else {
+ if (snd_pcm_capture_hw_avail(runtime) >= 3* runtime->period_size)
+ audio_process_dma(s);
+ }
+
+ }
+ spin_unlock(&s->dma_lock);
+
+ //DPRINTK("offset = %x", (unsigned int)offset);
+ return offset;
+}
+
+static int wmt_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_stream_a *s = runtime->private_data;
+ int ret;
+
+ DBG_DETAIL();
+
+ if (!cpu_dai->active) {
+ audio_dma_request(&s[0], audio_dma_callback);
+ audio_dma_request(&s[1], audio_dma_callback);
+ }
+
+ snd_soc_set_runtime_hwparams(substream, &wmt_pcm_hardware);
+
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+out:
+ return ret;
+}
+
+static int wmt_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_stream_a *s = runtime->private_data;
+
+ DBG_DETAIL();
+
+ if (!cpu_dai->active) {
+ audio_dma_free(&s[0]);
+ audio_dma_free(&s[1]);
+ }
+
+ return 0;
+}
+
+static int wmt_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ DBG_DETAIL();
+
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops wmt_pcm_ops = {
+ .open = wmt_pcm_open,
+ .close = wmt_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = wmt_pcm_hw_params,
+ .hw_free = wmt_pcm_hw_free,
+ .prepare = wmt_pcm_prepare,
+ .trigger = wmt_pcm_trigger,
+ .pointer = wmt_pcm_pointer,
+ .mmap = wmt_pcm_mmap,
+};
+
+static u64 wmt_pcm_dmamask = DMA_BIT_MASK(32);
+
+static int wmt_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 = wmt_pcm_hardware.buffer_bytes_max;
+
+ DBG_DETAIL();
+
+ 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 (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ info("stream = %d, buf_addr = %x", stream, buf->addr);
+
+ wfd_audbuf.bytes = size;
+ wfd_audbuf.dev = pcm->card->dev;
+ wfd_audbuf.enable = 0;
+ }
+
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+
+ return 0;
+}
+
+static void wmt_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ DBG_DETAIL();
+
+ 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;
+ }
+}
+
+int wmt_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;
+
+ DBG_DETAIL();
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &wmt_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 = wmt_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ret = wmt_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+
+out:
+ /* free preallocated buffers in case of error */
+ if (ret)
+ wmt_pcm_free_dma_buffers(pcm);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int wmt_pcm_suspend(struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct audio_stream_a *prtd;
+ struct audio_stream_a *s;
+
+ DBG_DETAIL();
+
+ if (!runtime)
+ return 0;
+
+ prtd = runtime->private_data;
+ s = &prtd[SNDRV_PCM_STREAM_PLAYBACK];
+
+ if (s->active) {
+ udelay(5);
+ wmt_stop_dma(s->dmach);
+ /*
+ wmt_clear_dma(s->dmach);
+ audio_stop_dma(s);
+ */
+ }
+
+ s = &prtd[SNDRV_PCM_STREAM_CAPTURE];
+
+ if (s->active) {
+ udelay(5);
+ wmt_stop_dma(s->dmach);
+ /*
+ wmt_clear_dma(s->dmach);
+ audio_stop_dma(s);
+ */
+ }
+
+ return 0;
+}
+
+static int wmt_pcm_resume(struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct audio_stream_a *prtd;
+ struct audio_stream_a *s;
+
+ DBG_DETAIL();
+
+ if (!runtime) {
+ if ((pcm_out_dmach != 0xFF) && (pcm_out_dma_cfg != NULL)) {
+ wmt_setup_dma(pcm_out_dmach, *pcm_out_dma_cfg);
+ }
+ return 0;
+ }
+
+ prtd = runtime->private_data;
+ s = &prtd[SNDRV_PCM_STREAM_PLAYBACK];
+ audio_setup_dma(s, SNDRV_PCM_STREAM_PLAYBACK);
+
+ if (s->active) {
+ wmt_resume_dma(s->dmach) ;
+ }
+
+ s = &prtd[SNDRV_PCM_STREAM_CAPTURE];
+ audio_setup_dma(s, SNDRV_PCM_STREAM_CAPTURE);
+
+ if (s->active) {
+ wmt_resume_dma(s->dmach) ;
+ }
+
+ return 0;
+}
+#else
+#define wmt_pcm_suspend NULL
+#define wmt_pcm_resume NULL
+#endif
+
+static struct snd_soc_platform_driver wmt_soc_platform = {
+ .ops = &wmt_pcm_ops,
+ .pcm_new = wmt_pcm_new,
+ .pcm_free = wmt_pcm_free_dma_buffers,
+ .suspend = wmt_pcm_suspend,
+ .resume = wmt_pcm_resume,
+};
+
+static int __devinit wmt_pcm_platform_probe(struct platform_device *pdev)
+{
+ DBG_DETAIL();
+
+ return snd_soc_register_platform(&pdev->dev, &wmt_soc_platform);
+}
+
+static int __devexit wmt_pcm_platform_remove(struct platform_device *pdev)
+{
+ DBG_DETAIL();
+
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wmt_pcm_driver = {
+ .driver = {
+ .name = "wmt-audio-pcm",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = wmt_pcm_platform_probe,
+ .remove = __devexit_p(wmt_pcm_platform_remove),
+};
+
+module_platform_driver(wmt_pcm_driver);
+
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_DESCRIPTION("WMT [ALSA SoC/pcm] driver");
+MODULE_LICENSE("GPL");
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm.h b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm.h
new file mode 100755
index 00000000..90ad7747
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt-pcm.h
@@ -0,0 +1,46 @@
+/*++
+ * linux/sound/soc/wmt/wmt-pcm.h
+ * WonderMedia audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+
+#ifndef __WMT_PCM_H__
+#define __WMT_PCM_H__
+
+struct wmt_pcm_dma_data {
+ char *name; /* stream identifier */
+ int dma_req; /* DMA request line */
+ unsigned long port_addr; /* transmit/receive register */
+ struct dma_device_cfg_s *dma_cfg;
+};
+
+typedef struct WFDStrmInfo {
+ unsigned int req_sz;
+ unsigned int avail_sz;
+ unsigned int buf_offset;
+} WFDStrmInfo_t;
+
+extern void wmt_pcm_wfd_start(void);
+extern unsigned int wmt_pcm_wfd_get_buf(void);
+extern void wmt_pcm_wfd_stop(void);
+extern int wmt_pcm_wfd_get_strm(WFDStrmInfo_t *info);
+
+#endif
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt-soc.c b/ANDROID_3.4.5/sound/soc/wmt/wmt-soc.c
new file mode 100755
index 00000000..f62e993d
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt-soc.c
@@ -0,0 +1,321 @@
+/*++
+ * linux/sound/soc/wmt/wmt-soc.c
+ * WonderMedia audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/hwdep.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "wmt-soc.h"
+#include "wmt-pcm.h"
+#include "wmt_hwdep.h"
+#include "../codecs/wmt_vt1602.h"
+#include "../codecs/vt1603.h"
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+extern void wmt_set_i2s_share_pin();
+char wmt_codec_name[80];
+char wmt_dai_name[80];
+char wmt_rate[10];
+
+#define AUDIO_NAME "WMT_SOC"
+//#define WMT_SOC_DEBUG 1
+//#define WMT_SOC_DEBUG_DETAIL 1
+
+#ifdef WMT_SOC_DEBUG
+#define DPRINTK(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#else
+#define DPRINTK(format, arg...) do {} while (0)
+#endif
+
+#ifdef WMT_SOC_DEBUG_DETAIL
+#define DBG_DETAIL(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": [%s]" format "\n" , __FUNCTION__, ## arg)
+#else
+#define DBG_DETAIL(format, arg...) do {} while (0)
+#endif
+
+#define err(format, arg...) \
+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) \
+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
+
+#define WMT_I2S_RATES SNDRV_PCM_RATE_8000_96000
+
+static struct snd_soc_card snd_soc_machine_wmt;
+
+static int wmt_soc_primary_startup(struct snd_pcm_substream *substream)
+{
+ DBG_DETAIL();
+ return 0;
+}
+
+static void wmt_soc_primary_shutdown(struct snd_pcm_substream *substream)
+{
+ DBG_DETAIL();
+}
+
+static int wmt_soc_primary_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;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+
+ DBG_DETAIL();
+
+ if (strcmp(wmt_codec_name, "hwdac")) {
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_CBS_CFS |
+ SND_SOC_DAIFMT_I2S |SND_SOC_DAIFMT_NB_NF);
+ if (ret < 0)
+ return ret;
+ }
+
+
+ /* Set cpu DAI configuration for I2S */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+ |SND_SOC_DAIFMT_MASTER_MASK | SND_SOC_DAIFMT_NB_NF);
+ if (ret < 0)
+ return ret;
+
+ if ((!strcmp(wmt_codec_name, "vt1602")) || (!strcmp(wmt_codec_name, "vt1603"))) {
+ /* Set the codec system clock for DAC and ADC */
+ if (!(params_rate(params) % 11025)) {
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 11289600,
+ SND_SOC_CLOCK_IN);
+ }
+ else {
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 12288000,
+ SND_SOC_CLOCK_IN);
+ }
+ }
+
+ return ret;
+}
+
+static int wmt_soc_second_startup(struct snd_pcm_substream *substream)
+{
+ //dump_stack();
+ DBG_DETAIL();
+ return 0;
+}
+
+static void wmt_soc_second_shutdown(struct snd_pcm_substream *substream)
+{
+ //dump_stack();
+ DBG_DETAIL();
+}
+
+static int wmt_soc_second_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ //dump_stack();
+ DBG_DETAIL();
+ return 0;
+}
+
+static int wmt_soc_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ DBG_DETAIL();
+ wmt_soc_hwdep_new(rtd->codec);
+ return 0;
+}
+
+static int wmt_suspend_pre(struct snd_soc_card *card)
+{
+ snd_soc_dapm_disable_pin(&card->rtd->codec->dapm, "Left HP");
+ snd_soc_dapm_disable_pin(&card->rtd->codec->dapm, "Right HP");
+ snd_soc_dapm_disable_pin(&card->rtd->codec->dapm, "Left SPK");
+ snd_soc_dapm_disable_pin(&card->rtd->codec->dapm, "Right SPK");
+}
+
+static int wmt_suspend_post(struct snd_soc_card *card)
+{
+ DBG_DETAIL();
+
+ /* Disable BIT15:I2S clock, BIT4:ARFP clock, BIT3:ARF clock */
+ PMCEU_VAL &= ~(BIT15 | BIT4 | BIT3);
+
+ snd_soc_dapm_enable_pin(&card->rtd->codec->dapm, "Left HP");
+ snd_soc_dapm_enable_pin(&card->rtd->codec->dapm, "Right HP");
+ snd_soc_dapm_enable_pin(&card->rtd->codec->dapm, "Left SPK");
+ snd_soc_dapm_enable_pin(&card->rtd->codec->dapm, "Right SPK");
+
+ return 0;
+}
+
+static int wmt_resume_pre(struct snd_soc_card *card)
+{
+ /* Enable MCLK before VT1602 codec enable, otherwise the codec will be disabled. */
+
+ /* set to 24.576MHz */
+ auto_pll_divisor(DEV_I2S, CLK_ENABLE , 0, 0);
+ auto_pll_divisor(DEV_I2S, SET_PLLDIV, 1, 24576);
+ /* Enable BIT4:ARFP clock, BIT3:ARF clock */
+ PMCEU_VAL |= (BIT4 | BIT3);
+ /* Enable BIT2:AUD clock */
+ PMCE3_VAL |= BIT2;
+
+ wmt_set_i2s_share_pin();
+ return 0;
+}
+
+static struct snd_soc_ops wmt_soc_primary_ops = {
+ .startup = wmt_soc_primary_startup,
+ .hw_params = wmt_soc_primary_hw_params,
+ .shutdown = wmt_soc_primary_shutdown,
+};
+
+static struct snd_soc_ops wmt_soc_second_ops = {
+ .startup = wmt_soc_second_startup,
+ .hw_params = wmt_soc_second_hw_params,
+ .shutdown = wmt_soc_second_shutdown,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link wmt_dai[] = {
+ {
+ .name = "HiFi",
+ .stream_name = "HiFi",
+ .platform_name = "wmt-audio-pcm.0",
+ .init = wmt_soc_dai_init,
+ .ops = &wmt_soc_primary_ops,
+ },
+ {
+ .name = "Voice",
+ .stream_name = "Voice",
+ .platform_name = "wmt-pcm-dma.0",
+ .cpu_dai_name = "wmt-pcm-controller.0",
+ .codec_dai_name = "HWDAC",
+ .codec_name = "wmt-i2s-hwdac.0",
+ .ops = &wmt_soc_second_ops,
+ },
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_machine_wmt = {
+ .name = "WMT_VT1609",
+ .dai_link = wmt_dai,
+ .num_links = ARRAY_SIZE(wmt_dai),
+ .suspend_pre = wmt_suspend_pre,
+ .suspend_post = wmt_suspend_post,
+ .resume_pre = wmt_resume_pre,
+};
+
+static struct platform_device *wmt_snd_device;
+
+static int __init wmt_soc_init(void)
+{
+ int ret, i;
+ char buf[64];
+ int len = ARRAY_SIZE(buf);
+
+ if (wmt_getsyspara("wmt.audio.i2s", buf, &len) != 0) {
+ strcpy(wmt_dai_name, "null");
+ strcpy(wmt_codec_name, "null");
+ }
+ else {
+ strcpy(wmt_dai_name, "i2s");
+ }
+
+ if (strcmp(wmt_dai_name, "null")) {
+ for (i = 0; i < 80; ++i) {
+ if (buf[i] == ':')
+ break;
+ else
+ wmt_codec_name[i] = buf[i];
+ }
+ }
+ else {
+ return -EINVAL;
+ }
+
+ // is wm8994, return and load wmt_wm8994 module
+ if (strcmp(wmt_codec_name, "wm8994") == 0)
+ return -ENODEV;
+
+ info("dai_name=%s, codec_name=%s", wmt_dai_name, wmt_codec_name);
+
+
+ wmt_i2s_dai.playback.rates = (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000);
+ wmt_i2s_dai.capture.rates = (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000);
+ wmt_dai[0].cpu_dai_name = "wmt-i2s.0";
+
+ if (!strcmp(wmt_codec_name, "vt1602")) {
+ wmt_dai[0].codec_dai_name = "VT1602";
+ wmt_dai[0].codec_name = "vt1602.1-001a";
+ }
+ else if (!strcmp(wmt_codec_name, "hwdac")) {
+ wmt_dai[0].codec_dai_name = "HWDAC";
+ wmt_dai[0].codec_name = "wmt-i2s-hwdac.0";
+ }
+ else if (!strcmp(wmt_codec_name, "vt1603")) {
+ wmt_dai[0].codec_dai_name = "VT1603";
+ wmt_dai[0].codec_name = "vt1603-codec";
+ }
+
+ /* Doing register process after plug-in */
+ wmt_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!wmt_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(wmt_snd_device, &snd_soc_machine_wmt);
+
+ ret = platform_device_add(wmt_snd_device);
+ if (ret)
+ goto err1;
+
+ return 0;
+
+err1:
+ platform_device_put(wmt_snd_device);
+ return ret;
+
+}
+
+static void __exit wmt_soc_exit(void)
+{
+ DBG_DETAIL();
+
+ platform_device_unregister(wmt_snd_device);
+}
+
+module_init(wmt_soc_init);
+module_exit(wmt_soc_exit);
+
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_DESCRIPTION("WMT [ALSA SoC/Machine] driver");
+MODULE_LICENSE("GPL");
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt-soc.h b/ANDROID_3.4.5/sound/soc/wmt/wmt-soc.h
new file mode 100755
index 00000000..f49a2764
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt-soc.h
@@ -0,0 +1,53 @@
+/*++
+ * linux/sound/soc/wmt/wmt-soc.h
+ * WonderMedia audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+
+#ifndef __WMT_ASOC_H__
+#define __WMT_ASOC_H__
+#include <mach/dma.h>
+
+struct audio_stream_a {
+ char *id; /* identification string */
+ int stream_id; /* numeric identification */
+ dmach_t dmach; /* DMA channel number */
+ struct dma_device_cfg_s dma_cfg; /* DMA device config */
+ int dma_dev; /* dma number of that device */
+ int dma_q_head; /* DMA Channel Q Head */
+ int dma_q_tail; /* DMA Channel Q Tail */
+ char dma_q_count; /* DMA Channel Q Count */
+ int active:1; /* we are using this stream for transfer now */
+ int period; /* current transfer period */
+ int periods; /* current count of periods registerd in the DMA engine */
+ spinlock_t dma_lock; /* for locking in DMA operations */
+ struct snd_pcm_substream *stream; /* the pcm stream */
+ unsigned linked:1; /* dma channels linked */
+ int offset; /* store start position of the last period in the alsa buffer */
+ snd_pcm_uframes_t last_offset;
+};
+
+#define NUM_LINKS 1
+
+extern struct snd_soc_dai_driver wmt_i2s_dai;
+
+
+#endif
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt_hwdep.c b/ANDROID_3.4.5/sound/soc/wmt/wmt_hwdep.c
new file mode 100755
index 00000000..8a6d374e
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt_hwdep.c
@@ -0,0 +1,259 @@
+/*++
+ * linux/sound/soc/wmt/wmt_hwdep.c
+ * WonderMedia I2S audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include <asm/mach-types.h>
+#include <mach/gpio.h>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc-dapm.h>
+#include <sound/hwdep.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "wmt_hwdep.h"
+#include "wmt-pcm.h"
+
+int WFD_flag = 0;
+static int gmode = 2;
+static char gstring[3] = "LR";
+
+static char gSpdHdm[6] = "BOTH";
+static int gSpHd = 6;
+
+extern void wmt_i2s_dac0_ctrl(int HDMI_audio_enable);
+
+static int wmt_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+ if ((file->f_flags & O_RDWR) && (WFD_flag)) {
+ return -EBUSY;
+ }
+ else if (file->f_flags & O_SYNC) {
+ WFD_flag = 1;
+ }
+ return 0;
+}
+
+static int wmt_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+ WFD_flag = 0;
+ return 0;
+}
+
+static int wmt_hwdep_mmap(struct snd_hwdep *hw, struct file *file, struct vm_area_struct *vma)
+{
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ if (remap_pfn_range(vma, vma->vm_start, (vma->vm_pgoff),
+ vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
+ printk("*E* remap page range failed: vm_pgoff=0x%x ", (unsigned int)vma->vm_pgoff);
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static int wmt_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int *value;
+ WFDStrmInfo_t *info;
+ struct wmt_soc_vt1603_info vt1603_info;
+ int ret = 0;
+
+ switch (cmd) {
+ case WMT_SOC_IOCTL_HDMI:
+ value = (int __user *)arg;
+
+ if (*value > 1) {
+ printk("Not supported status for HDMI Audio %d", *value);
+ return 0;
+ }
+ wmt_i2s_dac0_ctrl(*value);
+ return 0;
+
+ case WMT_SOC_IOCTL_WFD_START:
+ wmt_pcm_wfd_start();
+ return copy_to_user( (void *)arg, (const void __user *) wmt_pcm_wfd_get_buf(), sizeof(unsigned int));
+
+ case WMT_SOC_IOCTL_GET_STRM:
+ info = (WFDStrmInfo_t *)wmt_pcm_wfd_get_strm((WFDStrmInfo_t *)arg);
+ return __put_user((int)info, (unsigned int __user *) arg);
+
+ case WMT_SOC_IOCTL_WFD_STOP:
+ wmt_pcm_wfd_stop();
+ return 0;
+
+ case WMT_SOC_IOCTL_VT1603_RD:
+ ret = copy_from_user(&vt1603_info, (void __user *)arg, sizeof(vt1603_info));
+
+ if (ret == 0) {
+ vt1603_info.reg_value = vt1603_hwdep_ioctl(0, vt1603_info.reg_offset, vt1603_info.reg_value);
+ printk("<<<%s read reg 0x%x val 0x%x\n", __FUNCTION__, vt1603_info.reg_offset, vt1603_info.reg_value);
+ ret = copy_to_user((void __user *)arg, &vt1603_info, sizeof(vt1603_info));
+ }
+ return ret;
+ case WMT_SOC_IOCTL_VT1603_WR:
+ ret = copy_from_user(&vt1603_info, (void __user *)arg, sizeof(vt1603_info));
+ printk("<<<%s write reg 0x%x val 0x%x\n", __FUNCTION__, vt1603_info.reg_offset, vt1603_info.reg_value);
+ if (ret == 0)
+ vt1603_hwdep_ioctl(1, vt1603_info.reg_offset, vt1603_info.reg_value);
+ return ret;
+
+ case WMT_SOC_IOCTL_CH_SEL:
+ value = (int __user *)arg;
+
+ if (*value > 2) {
+ printk("Not supported for CH select %d", *value);
+ return 0;
+ }
+ wmt_i2s_ch_sel(*value);
+ return 0;
+
+ default:
+ break;
+ }
+
+ printk("Not supported ioctl for WMT-HWDEP");
+ return -ENOIOCTLCMD;
+}
+
+static long wmt_hwdep_write(struct snd_hwdep *hw, const char __user *buf,
+ long count, loff_t *offset)
+{
+ char string[3];
+ //int mode;
+ memset(string, 0, sizeof(string));
+ copy_from_user(&string, buf, sizeof(string));
+ printk("<<<%s %s\n", __FUNCTION__, string);
+ if (!memcmp(string, "LL", 2)) {
+ gmode = 0;
+ }
+ else if (!memcmp(string, "RR", 2)) {
+ gmode = 1;
+ }
+ else if (!memcmp(string, "LR", 2)) {
+ gmode = 2;
+ }
+ else {
+ printk("Not supported for CH select");
+ return count;
+ }
+
+ memset(gstring, 0, sizeof(gstring));
+ strncpy(gstring, string, sizeof(string));
+ wmt_i2s_ch_sel(gmode);
+ return count;
+}
+
+static long wmt_hwdep_read(struct snd_hwdep *hw, char __user *buf,
+ long count, loff_t *offset)
+{
+ int len = 0;
+ printk("%s string %s --> mode %d\n", __FUNCTION__, gstring, gmode);
+ len = copy_to_user(buf, gstring, sizeof(gstring));
+
+ return sizeof(gstring);
+}
+
+static long wmt_hwdep_write_1(struct snd_hwdep *hw, const char __user *buf,
+ long count, loff_t *offset)
+{
+ char string[5];
+ //int mode;
+
+ copy_from_user(&string, buf, sizeof(string));
+ printk("<<<%s %s\n", __FUNCTION__, string);
+ if (!memcmp(string, "NONE", 4)) {
+ gSpHd = 3;
+ }
+ else if (!memcmp(string, "HDMI", 4)) {
+ gSpHd = 4;
+ }
+ else if (!memcmp(string, "SPDIF", 5)) {
+ gSpHd = 5;
+ }
+ else if (!memcmp(string, "BOTH", 4)) {
+ gSpHd = 6;
+ }
+ else {
+ printk("Not supported for SPDIF/HDMI switch");
+ return count;
+ }
+
+ memset(gSpdHdm, 0, sizeof(gSpdHdm));
+ strncpy(gSpdHdm, string, sizeof(string));
+
+ wmt_i2s_ch_sel(gSpHd);
+ return count;
+}
+
+
+static long wmt_hwdep_read_1(struct snd_hwdep *hw, char __user *buf,
+ long count, loff_t *offset)
+{
+ int len = 0;
+ printk("%s string %s --> mode %d\n", __FUNCTION__, gSpdHdm, gSpHd);
+ len = copy_to_user(buf, gSpdHdm, sizeof(gSpdHdm));
+
+ return sizeof(gstring);
+}
+
+void wmt_soc_hwdep_new(struct snd_soc_codec *codec)
+{
+ struct snd_hwdep *hwdep;
+ struct snd_hwdep *hwdep_1;
+ if (snd_hwdep_new(codec->card->snd_card, "WMT-HWDEP", 0, &hwdep) < 0) {
+ printk("create WMT-HWDEP_0 fail");
+ return;
+ }
+
+ sprintf(hwdep->name, "WMT-HWDEP %d", 0);
+
+ hwdep->iface = SNDRV_HWDEP_IFACE_WMT;
+ hwdep->ops.open = wmt_hwdep_open;
+ hwdep->ops.ioctl = wmt_hwdep_ioctl;
+ hwdep->ops.release = wmt_hwdep_release;
+ hwdep->ops.mmap = wmt_hwdep_mmap;
+ hwdep->ops.write = wmt_hwdep_write;
+ hwdep->ops.read = wmt_hwdep_read;
+
+ if (snd_hwdep_new(codec->card->snd_card, "WMT-HWDEP", 1, &hwdep_1) < 0) {
+ printk("create WMT-HWDEP_1 fail");
+ return;
+ }
+
+ sprintf(hwdep_1->name, "WMT-HWDEP %d", 1);
+ printk("create %s success", hwdep_1->name);
+
+ hwdep_1->iface = SNDRV_HWDEP_IFACE_WMT;
+ hwdep_1->ops.write = wmt_hwdep_write_1;
+ hwdep_1->ops.read = wmt_hwdep_read_1;
+}
+
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt_hwdep.h b/ANDROID_3.4.5/sound/soc/wmt/wmt_hwdep.h
new file mode 100755
index 00000000..c95d5e9e
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt_hwdep.h
@@ -0,0 +1,55 @@
+/*++
+ * linux/sound/soc/wmt/wmt_hwdep.h
+ * WonderMedia I2S audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+/*
+ * ioctls for Hardware Dependant Interface
+ */
+#ifndef __WMT_HWDEP_H__
+#define __WMT_HWDEP_H__
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/hwdep.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+struct wmt_soc_vt1603_info {
+ u16 reg_offset;
+ u16 reg_value;
+};//add 2013-9-2 for vt1603 eq apk
+
+#define WMT_SOC_IOCTL_HDMI _IOWR('H', 0x10, int)
+#define WMT_SOC_IOCTL_WFD_START _IOWR('H', 0x20, int)
+#define WMT_SOC_IOCTL_GET_STRM _IOWR('H', 0x30, int)
+#define WMT_SOC_IOCTL_WFD_STOP _IOWR('H', 0x40, int)
+#define WMT_SOC_IOCTL_VT1603_RD _IOWR('H', 0x50, int)
+#define WMT_SOC_IOCTL_VT1603_WR _IOWR('H', 0x60, int)
+#define WMT_SOC_IOCTL_CH_SEL _IOWR('H', 0x70, int)
+void wmt_soc_hwdep_new(struct snd_soc_codec *codec);
+
+extern int vt1603_hwdep_ioctl(u8 rw_flag, u16 offset, u16 value);
+extern void wmt_i2s_ch_sel(int ch_sel_num);
+#endif
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt_swmixer.c b/ANDROID_3.4.5/sound/soc/wmt/wmt_swmixer.c
new file mode 100755
index 00000000..4d92a81f
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt_swmixer.c
@@ -0,0 +1,101 @@
+/*++
+ * linux/sound/soc/wmt/wmt_swmixer.c
+ * WonderMedia I2S audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+
+#include "wmt_swmixer.h"
+#include <sound/asound.h>
+
+
+
+void wmt_sw_u2s(int fmt, char *buffer, unsigned int chunksize)
+{
+ unsigned int index;
+ if (fmt == SNDRV_PCM_FORMAT_U8) {
+ for (index = 0; index < chunksize; ++index)
+ *(buffer + index) ^= 0x80;
+ }
+
+}
+
+
+void wmt_pcm_fmt_trans(int fmt, int channel, char *src_buf, char *dst_buf, unsigned int chunksize)
+{
+ unsigned int index = 0;
+ float_data_t f_data;
+ unsigned short data;
+
+ /* always convert to 2ch, s16le (4 bytes) */
+ if ((fmt == SNDRV_PCM_FORMAT_S16_LE) && (channel == 1)) {
+ /* transfer from 1ch s16le(2 bytes) to 2ch s16le(4 bytes) */
+ for (index = 0; index < (chunksize / 2); ++index) {
+ *((unsigned int *)dst_buf + index) = (*((unsigned short *)src_buf + index) << 16 |
+ *((unsigned short *)src_buf + index));
+ }
+ }
+ else if ((fmt == SNDRV_PCM_FORMAT_U8) && (channel == 1)) {
+ /* transfer from 1ch U8(1 bytes) to 2ch s16le(4 bytes) */
+ for (index = 0; index < chunksize; ++index) {
+ /* padding zero to byte 0 & byte 2 */
+ *((unsigned int *)dst_buf + index) = (*((unsigned char *)src_buf + index) << 24 |
+ *((unsigned char *)src_buf + index) << 8);
+ }
+ }
+ else if ((fmt == SNDRV_PCM_FORMAT_U8) && (channel == 2)) {
+ /* transfer from 2ch U8(2 bytes) to 2ch s16le(4 bytes) */
+ for (index = 0; index < chunksize; ++index) {
+ /* padding zero to byte 0 */
+ *((unsigned short *)dst_buf + index) = *((unsigned char *)src_buf + index) << 8;
+ }
+ }
+ else if ((fmt == SNDRV_PCM_FORMAT_FLOAT) && (channel == 2)) {
+ /* transfer from 2ch float(8 bytes) to 2ch s16le(4 bytes) */
+ for (index = 0; index < (chunksize / 4); ++index) {
+ f_data = *((float_data_t *)src_buf + index);
+
+ if (!f_data.sign) {
+ data = (f_data.frac + 0x800000) >> (8 - (f_data.exp - 127));
+ }
+ else {
+ data = ~((f_data.frac + 0x800000) >> (8 - (f_data.exp - 127))) + 1;
+ }
+
+ *((unsigned short *)dst_buf + index) = data;
+ }
+ }
+ else if ((fmt == SNDRV_PCM_FORMAT_FLOAT) && (channel == 1)) {
+ /* transfer from 1ch float(4 bytes) to 2ch s16le(4 bytes) */
+ for (index = 0; index < (chunksize / 4); ++index) {
+ f_data = *((float_data_t *)src_buf + index);
+
+ if (!f_data.sign) {
+ data = (unsigned short)((f_data.frac + 0x800000) >> (8 - (f_data.exp - 127)));
+ }
+ else {
+ data = (unsigned short)(~((f_data.frac + 0x800000) >> (8 - (f_data.exp - 127))) + 1);
+ }
+
+ *((unsigned int *)dst_buf + index) = (data << 16) | data;
+ }
+ }
+}
+
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt_swmixer.h b/ANDROID_3.4.5/sound/soc/wmt/wmt_swmixer.h
new file mode 100755
index 00000000..3163e5af
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt_swmixer.h
@@ -0,0 +1,33 @@
+/*++
+ * linux/sound/soc/wmt/wmt_swmixer.h
+ * WonderMedia I2S audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+
+typedef struct float_data {
+ unsigned long frac : 23;
+ unsigned long exp : 8;
+ unsigned long sign : 1;
+} float_data_t;
+
+void wmt_sw_u2s(int fmt, char *buffer, unsigned int chunksize);
+void wmt_pcm_fmt_trans(int fmt, int channel, char *src_buf, char *dst_buf, unsigned int chunksize);
+
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt_wm8994.c b/ANDROID_3.4.5/sound/soc/wmt/wmt_wm8994.c
new file mode 100755
index 00000000..8d4cebf9
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt_wm8994.c
@@ -0,0 +1,304 @@
+/*
+ * wmt_wm8994.c
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Chanwoo Choi <cw00.choi@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include <asm/mach-types.h>
+#include <mach/gpio.h>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc-dapm.h>
+#include <sound/hwdep.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "wmt-soc.h"
+#include "wmt-pcm.h"
+#include "wmt_hwdep.h"
+#include "../codecs/wm8994.h"
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/mfd/wm8994/core.h>
+
+#include <linux/i2c.h>
+
+static struct snd_soc_card wmt;
+static struct platform_device *wmt_snd_device;
+static int wmt_incall = 0;
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+extern void wmt_set_i2s_share_pin(void);
+
+static const struct snd_pcm_hardware wmt_voice_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .period_bytes_min = 16,
+ .period_bytes_max = 4 * 1024,
+ .periods_min = 2,
+ .periods_max = 16,
+ .buffer_bytes_max = 16 * 1024,
+};
+
+static int wmt_snd_suspend_post(struct snd_soc_card *card)
+{
+ /* Disable BIT15:I2S clock, BIT4:ARFP clock, BIT3:ARF clock */
+ PMCEU_VAL &= ~(BIT15 | BIT4 | BIT3);
+ return 0;
+}
+
+static int wmt_snd_resume_pre(struct snd_soc_card *card)
+{
+ /* Enable MCLK before VT1602 codec enable, otherwise the codec will be disabled. */
+
+ /* set to 24.576MHz */
+ auto_pll_divisor(DEV_I2S, CLK_ENABLE , 0, 0);
+ auto_pll_divisor(DEV_I2S, SET_PLLDIV, 1, 24576);
+ /* Enable BIT4:ARFP clock, BIT3:ARF clock */
+ PMCEU_VAL |= (BIT4 | BIT3);
+ /* Enable BIT2:AUD clock */
+ PMCE3_VAL |= BIT2;
+
+ wmt_set_i2s_share_pin();
+
+ return 0;
+}
+
+static int wmt_wm8994_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret = 0;
+ unsigned int pll_in = 48000;
+ unsigned int pll_out = 12000000;
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_LRCLK,
+ pll_in, pll_out);
+ if (ret < 0)
+ return ret;
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, pll_out,
+ SND_SOC_CLOCK_IN);
+
+ wmt_soc_hwdep_new(rtd->codec);
+
+ return 0;
+}
+
+static int wmt_hifi_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;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ unsigned int pll_in = 48000;
+ unsigned int pll_out = 12000000;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF);
+ if (ret< 0) {
+ printk("<<ret:%d snd_soc_dai_set_fmt(codec) hifi\n", ret);
+ return ret;
+ }
+
+ /* Set cpu DAI configuration for I2S */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S);
+ if (ret < 0) {
+ printk("<<ret:%d snd_soc_dai_set_fmt(cpu) hifi\n", ret);
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_LRCLK,
+ pll_in, pll_out);
+ if (ret < 0) {
+ printk("<<ret:%d snd_soc_dai_set_pll hifi\n", ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, pll_out,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ printk("<<ret:%d snd_soc_dai_set_sysclk hifi\n", ret);
+
+ if (!wmt_incall)
+ snd_soc_update_bits(codec_dai->codec, WM8994_CLOCKING_1, WM8994_SYSCLK_SRC, 0);
+
+ return 0;
+}
+
+static int wmt_voice_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ ret = snd_soc_set_runtime_hwparams(substream, &wmt_voice_hardware);
+
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,SNDRV_PCM_HW_PARAM_PERIODS);
+ return ret;
+}
+
+static int wmt_voice_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 ret = 0;
+ unsigned int pll_in = 2048000;
+ unsigned int pll_out = 12288000;
+
+ if (params_rate(params) != 8000)
+ return -EINVAL;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0) {
+ printk("<<ret:%d snd_soc_dai_set_fmt voice\n", ret);
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, WM8994_FLL_SRC_BCLK,
+ pll_in, pll_out);
+ if (ret < 0) {
+ printk("<<ret:%d snd_soc_dai_set_pll voice\n", ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2, pll_out,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ printk("<<ret:%d snd_soc_dai_set_sysclk voice\n", ret);
+
+ wmt_incall = 1;
+ return ret;
+}
+
+static int wmt_voice_hw_free(struct snd_pcm_substream *substream)
+{
+ wmt_incall = 0;
+ return 0;
+}
+
+static struct snd_soc_ops wmt_hifi_ops = {
+ .hw_params = wmt_hifi_hw_params,
+};
+
+static struct snd_soc_ops wmt_voice_ops = {
+ .startup = wmt_voice_startup,
+ .hw_params = wmt_voice_hw_params,
+ .hw_free = wmt_voice_hw_free,
+};
+
+static struct snd_soc_dai_driver voice_dai[] = {
+ {
+ .name = "wmt-voice-dai",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ },
+};
+
+static struct snd_soc_dai_link wmt_dai[] = {
+ {
+ .name = "WM8994",
+ .stream_name = "WM8994 HiFi",
+ .cpu_dai_name = "wmt-i2s.0",
+ .codec_dai_name = "wm8994-aif1",
+ .platform_name = "wmt-audio-pcm.0",
+ .codec_name = "wm8994-codec",
+ .init = wmt_wm8994_init,
+ .ops = &wmt_hifi_ops,
+ },
+ {
+ .name = "WM8994 Voice",
+ .stream_name = "Voice",
+ .cpu_dai_name = "wmt-voice-dai",
+ .codec_dai_name = "wm8994-aif2",
+ .codec_name = "wm8994-codec",
+ .ops = &wmt_voice_ops,
+ },
+};
+
+static struct snd_soc_card wmt = {
+ .name = "WMT_WM8994",
+ .dai_link = wmt_dai,
+ .num_links = ARRAY_SIZE(wmt_dai),
+ .suspend_post = wmt_snd_suspend_post,
+ .resume_pre = wmt_snd_resume_pre,
+};
+
+static int __init wmt_init(void)
+{
+ int ret;
+ char buf[128];
+ int len = ARRAY_SIZE(buf);
+
+ if (wmt_getsyspara("wmt.audio.i2s", buf, &len) != 0)
+ return -EINVAL;
+
+ if (strncmp(buf, "wm8994", strlen("wm8994")))
+ return -ENODEV;
+
+ wmt_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!wmt_snd_device)
+ return -ENOMEM;
+
+ /* register voice DAI here */
+ ret = snd_soc_register_dais(&wmt_snd_device->dev, voice_dai, ARRAY_SIZE(voice_dai));
+ if (ret) {
+ platform_device_put(wmt_snd_device);
+ return ret;
+ }
+
+ platform_set_drvdata(wmt_snd_device, &wmt);
+ ret = platform_device_add(wmt_snd_device);
+
+ if (ret) {
+ snd_soc_unregister_dai(&wmt_snd_device->dev);
+ platform_device_put(wmt_snd_device);
+ }
+
+ return ret;
+}
+
+static void __exit wmt_exit(void)
+{
+ snd_soc_unregister_dai(&wmt_snd_device->dev);
+ platform_device_unregister(wmt_snd_device);
+}
+
+module_init(wmt_init);
+module_exit(wmt_exit);
+
+MODULE_DESCRIPTION("ALSA SoC WM8994 WMT(wm8950)");
+MODULE_AUTHOR("Loonzhong <Loonzhong@wondermedia.com.cn>");
+MODULE_LICENSE("GPL");