[linux-yocto] [PATCH 08/65] sound: add atc260x sound driver
Jiang Lu
lu.jiang at windriver.com
Wed Dec 21 01:16:09 PST 2016
From: wurui <wurui at actions-semi.com>
commit 7e4c4b402be5b73e78aebbd95aae6b0e34634155 from
https://github.com/xapp-le/kernel.git
Change-Id: Icc9349e8d47821633fe252d84631cd13a50caaa2
---
arch/arm/mach-owl/include/mach/switch.h | 53 +
sound/Kconfig | 9 +
sound/core/pcm_lib.c | 25 +-
sound/core/pcm_native.c | 90 +
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/atc260x/.gitignore | 24 +
sound/soc/atc260x/Kconfig | 30 +
sound/soc/atc260x/Makefile | 16 +
.../atc260x/atc2603a-codec/atc2603a-audio-codec.c | 1648 +++++++++++++++
.../atc260x/atc2603a-codec/atc2603a-audio-regs.h | 395 ++++
.../atc260x/atc2603c-codec/atc2603c-audio-codec.c | 2189 ++++++++++++++++++++
.../atc260x/atc2603c-codec/atc2603c-audio-regs.h | 244 +++
sound/soc/atc260x/common-regs-owl.h | 205 ++
sound/soc/atc260x/dai-owl.c | 875 ++++++++
sound/soc/atc260x/dmaengine-pcm-owl.c | 438 ++++
sound/soc/atc260x/hdmi-audio-owl.c | 814 ++++++++
sound/soc/atc260x/link-owl.c | 642 ++++++
sound/soc/atc260x/pcm-owl.c | 528 +++++
sound/soc/atc260x/sndrv-owl.h | 101 +
20 files changed, 8325 insertions(+), 3 deletions(-)
create mode 100644 arch/arm/mach-owl/include/mach/switch.h
mode change 100644 => 100755 sound/Kconfig
mode change 100644 => 100755 sound/core/pcm_lib.c
mode change 100644 => 100755 sound/core/pcm_native.c
mode change 100644 => 100755 sound/soc/Kconfig
mode change 100644 => 100755 sound/soc/Makefile
create mode 100755 sound/soc/atc260x/.gitignore
create mode 100755 sound/soc/atc260x/Kconfig
create mode 100755 sound/soc/atc260x/Makefile
create mode 100755 sound/soc/atc260x/atc2603a-codec/atc2603a-audio-codec.c
create mode 100755 sound/soc/atc260x/atc2603a-codec/atc2603a-audio-regs.h
create mode 100755 sound/soc/atc260x/atc2603c-codec/atc2603c-audio-codec.c
create mode 100755 sound/soc/atc260x/atc2603c-codec/atc2603c-audio-regs.h
create mode 100755 sound/soc/atc260x/common-regs-owl.h
create mode 100755 sound/soc/atc260x/dai-owl.c
create mode 100755 sound/soc/atc260x/dmaengine-pcm-owl.c
create mode 100755 sound/soc/atc260x/hdmi-audio-owl.c
create mode 100755 sound/soc/atc260x/link-owl.c
create mode 100755 sound/soc/atc260x/pcm-owl.c
create mode 100755 sound/soc/atc260x/sndrv-owl.h
diff --git a/arch/arm/mach-owl/include/mach/switch.h b/arch/arm/mach-owl/include/mach/switch.h
new file mode 100644
index 0000000..3e4c748
--- /dev/null
+++ b/arch/arm/mach-owl/include/mach/switch.h
@@ -0,0 +1,53 @@
+/*
+ * Switch class driver
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood at android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#ifndef __LINUX_SWITCH_H__
+#define __LINUX_SWITCH_H__
+
+struct switch_dev {
+ const char *name;
+ struct device *dev;
+ int index;
+ int state;
+
+ ssize_t (*print_name)(struct switch_dev *sdev, char *buf);
+ ssize_t (*print_state)(struct switch_dev *sdev, char *buf);
+};
+
+struct gpio_switch_platform_data {
+ const char *name;
+ unsigned gpio;
+
+ /* if NULL, switch_dev.name will be printed */
+ const char *name_on;
+ const char *name_off;
+ /* if NULL, "0" or "1" will be printed */
+ const char *state_on;
+ const char *state_off;
+};
+
+extern int switch_dev_register(struct switch_dev *sdev);
+extern void switch_dev_unregister(struct switch_dev *sdev);
+
+static inline int switch_get_state(struct switch_dev *sdev)
+{
+ return sdev->state;
+}
+
+extern void switch_set_state(struct switch_dev *sdev, int state);
+
+#endif /* __LINUX_SWITCH_H__ */
diff --git a/sound/Kconfig b/sound/Kconfig
old mode 100644
new mode 100755
index 5a240e0..062e7d1
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -134,3 +134,12 @@ config AC97_BUS
sound subsystem and other function drivers completely unrelated to
sound although they're sharing the AC97 bus. Concerned drivers
should "select" this.
+
+config SND_UBUNTU
+ tristate "support for ubuntu."
+ help
+ Say Y or M to ubuntu support. This
+ feature support ubuntu related.
+
+ Many programs require this feature, so you should enable it
+ unless you know what you're doing.
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
old mode 100644
new mode 100755
index 253a2da..7b9f3c8
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1924,7 +1924,7 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
}
wait_time = msecs_to_jiffies(wait_time * 1000);
}
-
+ wait_time = 50;
for (;;) {
if (signal_pending(current)) {
err = -ERESTARTSYS;
@@ -1999,8 +1999,17 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
return err;
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+#ifdef CONFIG_SND_UBUNTU
+ if(runtime->format == SNDRV_PCM_FORMAT_S16_LE){
+ memcpy(hwbuf, buf, frames_to_bytes(runtime, frames));
+ }else{
+ if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
+ return -EFAULT;
+ }
+#else
if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
return -EFAULT;
+#endif
}
return 0;
}
@@ -2135,8 +2144,8 @@ snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const v
if (err < 0)
return err;
runtime = substream->runtime;
- nonblock = !!(substream->f_flags & O_NONBLOCK);
-
+ //nonblock = !!(substream->f_flags & O_NONBLOCK);
+ nonblock = 0;
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
runtime->channels > 1)
return -EINVAL;
@@ -2221,8 +2230,18 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
return err;
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+#ifdef CONFIG_SND_UBUNTU
+ if(runtime->format == SNDRV_PCM_FORMAT_S16_LE){
+ memcpy(buf, hwbuf, frames_to_bytes(runtime, frames));
+ }else{
+ if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
+ return -EFAULT;
+ }
+
+#else
if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
return -EFAULT;
+#endif
}
return 0;
}
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
old mode 100644
new mode 100755
index aa999e7..0801db3
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2839,8 +2839,54 @@ static int snd_pcm_playback_ioctl1(struct file *file,
return -EFAULT;
if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
return -EFAULT;
+#ifdef CONFIG_SND_UBUNTU
+ //when pcm data is 16bits
+ if(runtime->format == SNDRV_PCM_FORMAT_S16_LE){
+ unsigned int len, i;
+ int *p = NULL;
+ short *p_buf = NULL;
+ len = frames_to_bytes(runtime, xferi.frames);
+ p = kmalloc(2*len, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ p_buf = kmalloc(len, GFP_KERNEL);
+ if (!p_buf)
+ return -ENOMEM;
+ if (copy_from_user((char *)p_buf, xferi.buf, len)){
+ printk(KERN_ERR"%s,%d\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ for(i=0; i<(len/2); i++) {
+ p[i] = ((int)p_buf[i]) << 16;
+ }
+ result = snd_pcm_lib_write(substream, p, 2*(xferi.frames));
+ if(result > 0){
+ __put_user(result/2, &_xferi->result);
+ if((2*xferi.frames) != result){
+ printk(KERN_ERR"%s,%d, frames:%ld, result:%ld\n", __func__, __LINE__,xferi.frames, result);
+ __put_user(xferi.frames, &_xferi->result);
+ }
+ }
+ else
+ __put_user(result, &_xferi->result);
+ if(NULL != p_buf){
+ kfree(p_buf);
+ p_buf = NULL;
+ }
+ if(NULL != p){
+ kfree(p);
+ p = NULL;
+ }
+ }else{
+ result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
+ __put_user(result, &_xferi->result);
+ }
+ if(result < 0)
+ printk(KERN_ERR"%s,%d,result:%ld\n", __func__, __LINE__, result);
+#else
result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
__put_user(result, &_xferi->result);
+#endif
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
@@ -2919,8 +2965,51 @@ static int snd_pcm_capture_ioctl1(struct file *file,
return -EFAULT;
if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
return -EFAULT;
+#ifdef CONFIG_SND_UBUNTU
+ //when pcm data is 16bits
+ if(runtime->format == SNDRV_PCM_FORMAT_S16_LE){
+ unsigned int len, i;
+ int *p = NULL;
+ short *p_buf = NULL;
+ len = frames_to_bytes(runtime, xferi.frames);
+ p = kmalloc(2*len, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ p_buf = kmalloc(len, GFP_KERNEL);
+ if (!p_buf)
+ return -ENOMEM;
+ result = snd_pcm_lib_read(substream, p, 2*(xferi.frames));
+ __put_user(result/2, &_xferi->result);
+ for(i=0; i<(len/2); i++) {
+ p_buf[i] = (short)(p[i] >> 16);
+ }
+ if (copy_to_user(xferi.buf, (char *)p_buf, len)){
+ printk(KERN_ERR"%s,%d\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ if(NULL != p_buf){
+ kfree(p_buf);
+ p_buf = NULL;
+ }
+ if(NULL != p){
+ kfree(p);
+ p = NULL;
+ }
+
+ if(result < 0){
+ if(result == -EFAULT)
+ printk(KERN_ERR"%s,%d,result:EFAULT\n", __func__, __LINE__);
+ else
+ printk(KERN_ERR"%s,%d,result:%ld\n", __func__, __LINE__, result);
+ }
+ }else{
+ result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
+ __put_user(result, &_xferi->result);
+ }
+#else
result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
__put_user(result, &_xferi->result);
+#endif
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_READN_FRAMES:
@@ -3513,6 +3602,7 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
return -ENXIO;
return snd_pcm_mmap_control(substream, file, area);
default:
+ printk(KERN_ERR"%s,default\n", __func__);
return snd_pcm_mmap_data(substream, file, area);
}
return 0;
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
old mode 100644
new mode 100755
index 3ba52da..9f93342
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -31,6 +31,7 @@ config SND_SOC_GENERIC_DMAENGINE_PCM
select SND_DMAENGINE_PCM
# All the supported SoCs
+source "sound/soc/atc260x/Kconfig"
source "sound/soc/adi/Kconfig"
source "sound/soc/atmel/Kconfig"
source "sound/soc/au1x/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
old mode 100644
new mode 100755
index 974ba70..d8d1f1c
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -12,6 +12,7 @@ endif
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
obj-$(CONFIG_SND_SOC) += generic/
+obj-$(CONFIG_SND_SOC) += atc260x/
obj-$(CONFIG_SND_SOC) += adi/
obj-$(CONFIG_SND_SOC) += atmel/
obj-$(CONFIG_SND_SOC) += au1x/
diff --git a/sound/soc/atc260x/.gitignore b/sound/soc/atc260x/.gitignore
new file mode 100755
index 0000000..d12ccb3
--- /dev/null
+++ b/sound/soc/atc260x/.gitignore
@@ -0,0 +1,24 @@
+#
+# NOTE! Please use 'git ls-files -i --exclude-standard'
+# command after changing this file, to see if there are
+# any tracked files which get ignored after the change.
+#
+# Normal rules
+#
+*.o
+*.o.*
+*.ko
+*.ko.*
+*.mod.c
+*.order
+*.bak
+
+#
+# Top-level generic files
+#
+/Module.symvers
+
+#
+# git files that we don't want to ignore even it they are dot-files
+#
+!.gitignore
diff --git a/sound/soc/atc260x/Kconfig b/sound/soc/atc260x/Kconfig
new file mode 100755
index 0000000..e7b7df0
--- /dev/null
+++ b/sound/soc/atc260x/Kconfig
@@ -0,0 +1,30 @@
+config SND_SOC_OWL
+ tristate
+ depends on SND_SOC
+
+config SND_SOC_ATC2603A
+ tristate
+ depends on SND_SOC_OWL
+
+config SND_SOC_ATC2603C
+ tristate
+ depends on SND_SOC_OWL
+
+config SND_SOC_HDMI_OWL
+ tristate
+ depends on SND_SOC_OWL
+
+config SND_SOC_DAI_OWL
+ tristate
+ depends on SND_SOC_OWL
+
+config SND_SOC_ALL_PMU_OWL
+ tristate "SoC Audio support for all pmus owl board"
+ select SND_SOC_OWL
+ select SND_SOC_HDMI_OWL
+ select SND_SOC_DAI_OWL
+ select SND_SOC_ATC2603A
+ select SND_SOC_ATC2603C
+ help
+ Say Y if you want to add support for SoC audio on atc2603a-based
+ ATM7059 board.
\ No newline at end of file
diff --git a/sound/soc/atc260x/Makefile b/sound/soc/atc260x/Makefile
new file mode 100755
index 0000000..bad9c2b
--- /dev/null
+++ b/sound/soc/atc260x/Makefile
@@ -0,0 +1,16 @@
+# AT91 Platform Support
+snd-soc-atc2603a-objs := atc2603a-codec/atc2603a-audio-codec.o
+snd-soc-atc2603c-objs := atc2603c-codec/atc2603c-audio-codec.o
+snd-soc-dai-owl-objs := dai-owl.o
+snd-soc-hdmi-owl-objs := hdmi-audio-owl.o
+snd-soc-pcm-owl-objs := pcm-owl.o dmaengine-pcm-owl.o
+snd-soc-link-owl-objs := link-owl.o
+
+obj-$(CONFIG_SND_SOC_OWL) += snd-soc-pcm-owl.o
+obj-$(CONFIG_SND_SOC_ATC2603A) += snd-soc-atc2603a.o
+obj-$(CONFIG_SND_SOC_ATC2603C) += snd-soc-atc2603c.o
+obj-$(CONFIG_SND_SOC_DAI_OWL) += snd-soc-dai-owl.o
+obj-$(CONFIG_SND_SOC_HDMI_OWL) += snd-soc-hdmi-owl.o
+
+# ATC2603A PMU Machine Support
+obj-$(CONFIG_SND_SOC_ALL_PMU_OWL) += snd-soc-link-owl.o
diff --git a/sound/soc/atc260x/atc2603a-codec/atc2603a-audio-codec.c b/sound/soc/atc260x/atc2603a-codec/atc2603a-audio-codec.c
new file mode 100755
index 0000000..f9336a4
--- /dev/null
+++ b/sound/soc/atc260x/atc2603a-codec/atc2603a-audio-codec.c
@@ -0,0 +1,1648 @@
+#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/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 <linux/clk.h> /* clk_enable */
+#include "../sndrv-owl.h"
+#include "atc2603a-audio-regs.h"
+#include "../common-regs-owl.h"
+#include <mach/clkname.h>
+#include <mach/module-owl.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+
+#include <linux/io.h>
+#include <linux/ioport.h>
+
+#include <linux/suspend.h>
+#include <linux/earlysuspend.h>
+
+#include <linux/mfd/atc260x/atc260x.h>
+
+
+static int direct_drive_disable;//0:ֱ\C7\FD\A3\AC 1:\B7\C7ֱ\C7\FD
+
+static const char *audio_device_node = "actions,atc2603a-audio";
+static const char *snd_earphone_output_mode = "earphone_output_mode";
+static const char *snd_mic_num = "mic_num";
+static const char *snd_mic0_gain = "mic0_gain";
+static const char *snd_speaker_gain = "speaker_gain";
+static const char *snd_earphone_gain = "earphone_gain";
+/*
+static const char *snd_speaker_volume = "speaker_volume";
+static const char *snd_earphone_volume = "earphone_volume";
+static const char *snd_earphone_detect_mode = "earphone_detect_mode";
+*/
+static const char *speaker_ctrl_name = "speaker_gpios";
+static int speaker_gpio_num;
+
+static audio_hw_cfg_t audio_hw_cfg;
+
+static int atc2603a_open_count;
+static unsigned int user_lock = 1;
+static volatile unsigned int hw_init_flag = false;
+static DEFINE_MUTEX(atc2603a_pa_down_lock);
+
+//20141013 yuchen: to check pmu ic type
+static int atc2603a_ictype = PMU_NOT_USED;
+
+struct reg_val {
+ int reg;
+ unsigned short val;
+ short delay; /* ms */
+};
+
+struct atc2603a_priv_data {
+ int mode;
+};
+static struct atc260x_dev *atc260x;
+struct snd_soc_codec *atc2603a_codec;
+
+#define ATC2603A_AIF 0
+#define REG_BASE ATC2603A_AUDIO_IN_OUT_BASE
+
+#define AUDIOINOUT_CTL 0x00
+#define AUDIO_DEBUGOUTCTL 0x01
+#define DAC_FILTERCTL0 0x02
+#define DAC_FILTERCTL1 0x03
+#define DAC_DIGITALCTL 0x04
+#define DAC_VOLUMECTL0 0x05
+#define DAC_VOLUMECTL1 0x06
+#define DAC_VOLUMECTL2 0x07
+#define DAC_VOLUMECTL3 0x08
+#define DAC_ANANLOG0 0x09
+#define DAC_ANANLOG1 0x0a
+#define DAC_ANANLOG2 0x0b
+#define DAC_ANANLOG3 0x0c
+#define DAC_ANANLOG4 0x0d
+#define CLASSD_CTL0 0x0e
+#define CLASSD_CTL1 0x0f
+#define CLASSD_CTL2 0x10
+
+#define ADC0_DIGITALCTL 0x11
+#define ADC0_HPFCTL 0x12
+#define ADC0_CTL 0x13
+#define AGC0_CTL0 0x14
+#define AGC0_CTL1 0x15
+#define AGC0_CTL2 0x16
+#define ADC_ANANLOG0 0x17
+#define ADC_ANANLOG1 0x18
+#define ADC1_DIGITALCTL 0x19
+#define ADC1_CTL 0x1a
+#define AGC1_CTL0 0x1b
+#define AGC1_CTL1 0x1c
+#define AGC1_CTL2 0x1d
+
+static const u16 atc2603a_reg[AGC1_CTL2 +1] = {
+ [AUDIOINOUT_CTL] = 0x00,
+ [AUDIO_DEBUGOUTCTL] = 0x00,
+ [DAC_FILTERCTL0] = 0x00,
+ [DAC_FILTERCTL1] = 0x00,
+ [DAC_DIGITALCTL] = 0x00,
+ [DAC_VOLUMECTL0] = 0xbebe,
+ [DAC_VOLUMECTL1] = 0xbebe,
+ [DAC_VOLUMECTL2] = 0xbebe,
+ [DAC_VOLUMECTL3] = 0xbebe,
+ [DAC_ANANLOG0] = 0x5355,
+ [DAC_ANANLOG1] = 0x0040,
+ [DAC_ANANLOG2] = 0x00,
+ [DAC_ANANLOG3] = 0x880b,
+ [DAC_ANANLOG4] = 0x00,
+ [CLASSD_CTL0] = 0x8000,
+ [CLASSD_CTL1] = 0x0450,
+ [CLASSD_CTL2] = 0x06d0,
+ [ADC0_DIGITALCTL] = 0x00,
+ [ADC0_HPFCTL] = 0x00,
+ [ADC0_CTL] = 0x0881,
+ [AGC0_CTL0] = 0x9933,
+ [AGC0_CTL1] = 0x051a,
+ [AGC0_CTL2] = 0x8c40,
+ [ADC_ANANLOG0] = 0x8269,
+ [ADC_ANANLOG1] = 0x8955,
+ [ADC1_DIGITALCTL] = 0x00,
+ [ADC1_CTL] = 0x00,
+ [AGC1_CTL0] = 0x9933,
+ [AGC1_CTL1] = 0x051a,
+ [AGC1_CTL2] = 0x8c40,
+};
+
+struct reg_val atc2603a_pa_up_list[] = {
+ {DAC_VOLUMECTL0, 0xbebe, 0},
+ {DAC_ANANLOG0, 0x00, 0},
+ {DAC_ANANLOG1, 0x00, 0},
+ {DAC_ANANLOG2, 0x00, 0},
+ {DAC_ANANLOG3, 0x00, 0},
+ {DAC_ANANLOG4, 0x00, 0},
+ {AUDIOINOUT_CTL, 0x02, 0},
+ {DAC_DIGITALCTL, 0x03, 0},
+ {DAC_ANANLOG4, 0x08c0, 0},
+ {DAC_ANANLOG0, 0x26b3, 0},
+ {DAC_ANANLOG3, 0x8b0b, 0},
+ {DAC_ANANLOG2, 0x07, 0},
+ {DAC_ANANLOG3, 0x8b0f, 100},
+ {DAC_ANANLOG2, 0x0f, 0},
+ {DAC_ANANLOG4, 0x88c0, 0},
+ {DAC_ANANLOG2, 0x1f, 600},
+ {DAC_ANANLOG2, 0x17, 0},
+};
+
+struct reg_val atc2603a_pa_down_list[] = {
+ {DAC_VOLUMECTL0, 0xbebe, 0},
+ {DAC_ANANLOG4, 0x08c0, 0},
+ {DAC_ANANLOG2, 0x1f, 0},
+ {DAC_ANANLOG2, 0x0f, 0},
+};
+
+struct asoc_codec_resource {
+ void __iomem *base[MAX_RES_NUM];/*virtual base for every resource*/
+ void __iomem *baseptr; /*pointer to every virtual base*/
+ struct clk *clk;
+ int irq;
+ unsigned int setting;
+};
+
+
+/*
+static void set_dai_reg_base(int num)
+{
+ codec_res.baseptr = codec_res.base[num];
+}
+
+static u32 snd_dai_readl(u32 reg)
+{
+ return readl(codec_res.baseptr + reg);
+}
+
+static void snd_dai_writel(u32 val, u32 reg)
+{
+ writel(val, codec_res.baseptr + reg);
+}
+*/
+
+/*
+static void snd_codec_writel_debug(unsigned int val, unsigned int reg)
+{
+ snd_dai_writel(val, reg);
+ snd_dbg("%s: reg[0x%x]=[0x%x][0x%x]\n"
+ , __func__, reg, val, snd_dai_readl(reg));
+}
+*/
+
+static int atc2603a_write_pmu(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct atc260x_dev *atc260x = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = atc260x_reg_write(atc260x, reg, value);
+ if(ret < 0)
+ {
+ snd_err("atc2603a_write: reg = %#X, ret = %d \n", reg, ret);
+ }
+
+ snd_dbg("%s: reg[0x%x]=[0x%x][0x%x]\n"
+ , __func__, reg, value, atc260x_reg_read(atc260x, reg));
+
+ return ret;
+}
+static int atc2603a_read_pmu(struct snd_soc_codec *codec, unsigned int reg)
+{
+ struct atc260x_dev *atc260x = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = atc260x_reg_read(atc260x, reg);
+ if(ret < 0)
+ {
+ snd_err("atc2603a_read: reg = %#X, ret = %d \n", reg, ret);
+ }
+
+ return ret;
+}
+static int snd_soc_update_bits_pmu(struct snd_soc_codec *codec, unsigned short reg,
+ unsigned int mask, unsigned int value)
+{
+ bool change;
+ unsigned int old, new;
+ int ret;
+
+ {
+ ret = atc2603a_read_pmu(codec, reg);
+ if (ret < 0)
+ return ret;
+
+ old = ret;
+ new = (old & ~mask) | (value & mask);
+ change = old != new;
+ if (change)
+ ret = atc2603a_write_pmu(codec, reg, new);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return change;
+
+}
+
+
+static void ramp_undirect(unsigned int begv, unsigned int endv) {
+ unsigned int val = 0;
+ int count = 0;
+
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ while (endv < begv) {
+ count++;
+ if ((count & 0x7F) == 0) {
+ mdelay(1);
+ }
+ val = snd_dai_readl(I2S_FIFOCTL);
+ while ((val & (0x1 << 8)) != 0) {
+ val = snd_dai_readl(I2S_FIFOCTL);
+ };
+ snd_dai_writel(endv, I2STX_DAT);
+ endv -= 0x36000;
+ }
+ while (begv <= endv) {
+ count++;
+ if ((count & 0x7F) == 0) {
+ mdelay(1);
+ }
+ val = snd_dai_readl(I2S_FIFOCTL);
+ while ((val & (0x1 << 8)) != 0) {
+ val = snd_dai_readl(I2S_FIFOCTL);
+ };
+ snd_dai_writel(endv, I2STX_DAT);
+ endv -= 0x36000;
+ }
+}
+
+static int atc2603a_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ int ret = 0;
+
+ struct atc260x_dev *atc260x = snd_soc_codec_get_drvdata(codec);
+
+ ret = atc260x_reg_write(atc260x, reg + REG_BASE, value);
+ snd_dbg("%s: reg[0x%x]=[0x%x]\r\n", __func__, reg + REG_BASE, value);
+ if (ret < 0)
+ snd_err("atc2603a_write: reg = %#X, ret = %d failed\n",
+ reg, ret);
+
+ snd_dbg("%s: reg[0x%x]=[0x%x][0x%x]\n"
+ , __func__, reg, value, atc260x_reg_read(atc260x, reg + REG_BASE));
+
+ return ret;
+}
+
+static unsigned int atc2603a_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ int ret = 0;
+
+ ret = atc260x_reg_read(atc260x, reg + REG_BASE);
+ snd_dbg("%s: reg[0x%x]\r\n", __func__, reg + REG_BASE);
+ if (ret < 0)
+ snd_err("atc2603a_read: reg = %#X, ret = %d failed\n", reg, ret);
+
+ return ret;
+}
+
+#define ADC0_GAIN_MAX 25
+
+static int atc2603a_adc_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+ unsigned int val;
+ snd_soc_cache_sync(codec);
+ val = snd_soc_read(codec, mc->reg);
+ ucontrol->value.integer.value[0] =
+ ((val & AGC_CTL0_AMP1GL_MSK) >> mc->shift);
+ ucontrol->value.integer.value[1] =
+ (val & AGC_CTL0_AMP1GR_MSK) >> mc->rshift;
+
+ return 0;
+}
+
+static int atc2603a_adc_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+ unsigned int val, val1, val2;
+
+ val = snd_soc_read(codec, mc->reg);
+ val1 = ucontrol->value.integer.value[0];
+ val2 = val1;
+ val = val & (~AGC_CTL0_AMP1GL_MSK);
+ val = val & (~AGC_CTL0_AMP1GR_MSK);
+ val = val | (val1<< mc->shift);
+ val = val | (val2 << mc->rshift);
+
+ snd_soc_component_update_bits(component, mc->reg,
+ AGC_CTL0_AMP1GL_MSK | AGC_CTL0_AMP1GR_MSK,
+ val);
+
+ snd_soc_cache_sync(codec);
+
+ return 0;
+}
+
+static int atc2603a_mic_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.mic0_gain[0];
+ ucontrol->value.integer.value[1] =
+ audio_hw_cfg.mic0_gain[1];
+
+ return 0;
+}
+
+static int atc2603a_earphone_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.earphone_gain[0];
+ ucontrol->value.integer.value[1] =
+ audio_hw_cfg.earphone_gain[1];
+
+ return 0;
+}
+
+static int atc2603a_speaker_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.speaker_gain[0];
+ ucontrol->value.integer.value[1] =
+ audio_hw_cfg.speaker_gain[1];
+
+ return 0;
+}
+
+static int atc2603a_speaker_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.speaker_volume;
+
+ return 0;
+}
+
+static int atc2603a_earphone_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.earphone_volume;
+
+ return 0;
+}
+
+static int atc2603a_mic_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.mic_num;
+
+ return 0;
+}
+
+const char *atc2603a_mic0_mode[] = {
+ "Differential", "Single ended"};
+
+static const SOC_ENUM_SINGLE_DECL(
+ atc2603a_mic0_mode_enum, ADC0_CTL,
+ ADC0_CTL_MIC0FDSE_SFT, atc2603a_mic0_mode);
+
+static const char *pa_output_swing[] = {
+ "Vpp2.4", "Vpp1.6"};
+
+static const SOC_ENUM_SINGLE_DECL(
+ pa_output_swing_enum, DAC_ANANLOG1,
+ DAC_ANALOG1_PASW_SFT, pa_output_swing);
+
+const struct snd_kcontrol_new atc2603a_snd_controls[] = {
+
+ SOC_DOUBLE_EXT_TLV("Dummy mic Gain",
+ 0, 0, 1, 0xf, 0,
+ atc2603a_mic_gain_get,
+ NULL,
+ NULL),
+
+ SOC_DOUBLE_EXT_TLV("Dummy earphone gain",
+ 0, 0, 1, 0xff, 0,
+ atc2603a_earphone_gain_get,
+ NULL,
+ NULL),
+
+ SOC_DOUBLE_EXT_TLV("Dummy speaker gain",
+ 0, 0, 1, 0xff, 0,
+ atc2603a_speaker_gain_get,
+ NULL,
+ NULL),
+
+ SOC_SINGLE_EXT_TLV("Dummy speaker volume",
+ 0, 0, 0x28, 0,
+ atc2603a_speaker_volume_get,
+ NULL,
+ NULL),
+
+ SOC_SINGLE_EXT_TLV("Dummy earphone volume",
+ 0, 0, 0x28, 0,
+ atc2603a_earphone_volume_get,
+ NULL,
+ NULL),
+
+ SOC_SINGLE_EXT_TLV("Dummy mic num",
+ 0, 0, 0x2, 0,
+ atc2603a_mic_num_get,
+ NULL,
+ NULL),
+
+ SOC_DOUBLE_EXT_TLV("Adc0 Gain",
+ AGC0_CTL0,
+ AGC0_CTL0_AMP1G0L_SFT,
+ AGC0_CTL0_AMP1G0R_SFT,
+ 0xf,
+ 0,
+ atc2603a_adc_gain_get,
+ atc2603a_adc_gain_put,
+ NULL),
+
+ SOC_SINGLE_TLV("AMP1 Gain boost Range select",
+ AGC0_CTL0,
+ AGC0_CTL0_AMP0GR1_SET,
+ 0x7,
+ 0,
+ NULL),
+
+ SOC_SINGLE_TLV("ADC0 Digital Gain control",
+ ADC0_DIGITALCTL,
+ ADC0_DIGITALCTL_ADGC0_SFT,
+ 0xF,
+ 0,
+ NULL),
+
+ SOC_DOUBLE_EXT_TLV("Adc1 Gain",
+ AGC1_CTL0,
+ AGC1_CTL0_AMP1G1L_SFT,
+ AGC1_CTL0_AMP1G1R_SFT,
+ 0xf,
+ 0,
+ atc2603a_adc_gain_get,
+ atc2603a_adc_gain_put,
+ NULL),
+
+ SOC_ENUM("Mic0 Mode Mux", atc2603a_mic0_mode_enum),
+
+ SOC_ENUM("PA Output Swing Mux", pa_output_swing_enum),
+
+ SOC_SINGLE_TLV("DAC PA Volume",
+ DAC_ANANLOG1,
+ DAC_ANALOG1_VOLUME_SFT,
+ 0x28,
+ 0,
+ NULL),
+
+ SOC_SINGLE("DAC FL FR PLAYBACK Switch",
+ DAC_ANANLOG1,
+ DAC_ANALOG1_DACFL_FRMUTE_SFT,
+ 1,
+ 0),
+
+ SOC_SINGLE_TLV("DAC FL Gain",
+ DAC_VOLUMECTL0,
+ DAC_VOLUMECTL0_DACFL_VOLUME_SFT,
+ 0xFF,
+ 0,
+ NULL),
+
+ SOC_SINGLE_TLV("DAC FR Gain",
+ DAC_VOLUMECTL0,
+ DAC_VOLUMECTL0_DACFR_VOLUME_SFT,
+ 0xFF,
+ 0,
+ NULL),
+
+ SOC_SINGLE("DAC PA Switch",
+ DAC_ANANLOG4,
+ DAC_ANALOG4_PAEN_FR_FL_SFT,
+ 1,
+ 0),
+
+ SOC_SINGLE("DAC PA OUTPUT Stage Switch",
+ DAC_ANANLOG4,
+ DAC_ANALOG4_PAOSEN_FR_FL_SFT,
+ 1,
+ 0),
+
+ SOC_DOUBLE("DAC Digital FL FR Switch",
+ DAC_DIGITALCTL,
+ DAC_DIGITALCTL_DEFL_SFT,
+ DAC_DIGITALCTL_DEFR_SFT,
+ 1,
+ 0),
+
+ SOC_SINGLE("Internal Mic Power Switch", ADC0_CTL,
+ ADC0_CTL_VMICINEN_SFT,
+ 1,
+ 0),
+
+ SOC_SINGLE("External Mic Power Switch",
+ ADC0_CTL,
+ ADC0_CTL_VMICEXEN_SFT,
+ 1,
+ 0),
+
+ SOC_SINGLE_TLV("External MIC Power Voltage",
+ ADC0_CTL,
+ ADC0_CTL_VMICEXST_SFT,
+ 0x3,
+ 0,
+ NULL),
+
+ SOC_SINGLE_TLV("Adc0 Digital Gain",
+ ADC0_DIGITALCTL,
+ 6,
+ 0xf, 0, NULL),
+
+ SOC_SINGLE_TLV("Adc1 Digital Gain",
+ ADC1_DIGITALCTL,
+ 2,
+ 0xf, 0, NULL),
+};
+
+const char *atc2603a_adc0_src[] = {"MIC0", "FM", "AOUT MIXER"};
+
+static const SOC_ENUM_SINGLE_DECL(
+ atc2603a_adc0_enum, ADC0_CTL,
+ ADC0_CTL_ADCIS_SFT, atc2603a_adc0_src);
+
+
+const struct snd_kcontrol_new atc2603a_adc0_mux =
+SOC_DAPM_ENUM("ADC0 Source", atc2603a_adc0_enum);
+
+const struct snd_kcontrol_new atc2603a_dac_lr_mix[] = {
+ SOC_DAPM_SINGLE("FL FR Switch", DAC_ANANLOG1,
+ DAC_ANALOG1_DACFL_FRMUTE_SFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC Switch", DAC_ANANLOG1,
+ DAC_ANALOG1_DACMICMUTE_SFT, 1, 0),
+ SOC_DAPM_SINGLE("FM Switch", DAC_ANANLOG1,
+ DAC_ANALOG1_DACFMMUTE_SFT, 1, 0),
+};
+
+static int atc2603a_mic0_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = snd_soc_read(codec, ADC0_CTL);
+ /* single end or full differential */
+ if (val & ADC0_CTL_MIC0FDSE) {//if single end
+ snd_soc_update_bits_pmu(codec, ATC2603A_MFP_CTL1,
+ 0x03 << 10, 0x03 << 10);//reserved?
+ snd_soc_update_bits_pmu(codec, ATC2603A_MFP_CTL1,
+ 0x03 << 8, 0);//MICINL&MICINR
+ }
+ snd_soc_update_bits(codec, ADC1_CTL,
+ 0x3 << 7, 0x3 << 7);//(VRDN output0 enable) | (VRDN output1 enable)
+ snd_soc_update_bits(codec, ADC0_HPFCTL,
+ 0x3, 0x3);//(High Pass filter0 L enable) | (High Pass filter0 R enable)
+
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static void i2s_clk_disable(void)
+{
+}
+
+static int i2s_clk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ i2s_clk_disable();
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget atc2603a_dapm_widgets[] = {
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MICIN0LP"),
+ SND_SOC_DAPM_INPUT("MICIN0LN"),
+ SND_SOC_DAPM_INPUT("MICIN0RP"),
+ SND_SOC_DAPM_INPUT("MICIN0RN"),
+ SND_SOC_DAPM_INPUT("MICIN1LP"),
+ SND_SOC_DAPM_INPUT("MICIN1LN"),
+ SND_SOC_DAPM_INPUT("MICIN1RP"),
+ SND_SOC_DAPM_INPUT("MICIN1RN"),
+ SND_SOC_DAPM_INPUT("FMINL"),
+ SND_SOC_DAPM_INPUT("FMINR"),
+
+ SND_SOC_DAPM_SUPPLY("I2S_CLK", SND_SOC_NOPM,
+ 0, 0, i2s_clk_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA("MICIN0L", ADC0_CTL,
+ ADC0_CTL_MIC0LEN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MICIN0R", ADC0_CTL,
+ ADC0_CTL_MIC0REN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MICIN1L", ADC1_CTL,
+ ADC1_CTL_MIC1LEN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MICIN1R", ADC1_CTL,
+ ADC1_CTL_MIC1REN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("FM L", ADC0_CTL,
+ ADC0_CTL_FMLEN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("FM R", ADC0_CTL,
+ ADC0_CTL_FMREN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIC("MICIN0", atc2603a_mic0_event),
+ SND_SOC_DAPM_MIC("MICIN1", NULL),
+ SND_SOC_DAPM_MIC("FM", NULL),
+ /* ADC0 MUX */
+ SND_SOC_DAPM_MUX("ADC0 Mux", SND_SOC_NOPM, 0, 0,
+ &atc2603a_adc0_mux),
+ /* ADCS */
+ SND_SOC_DAPM_ADC("ADC0 L", NULL, ADC0_CTL,
+ ADC0_CTL_AD0LEN_SFT, 0),
+ SND_SOC_DAPM_ADC("ADC0 R", NULL, ADC0_CTL,
+ ADC0_CTL_AD0REN_SFT, 0),
+ SND_SOC_DAPM_ADC("ADC1 L", NULL, ADC1_CTL,
+ ADC1_CTL_AD1LEN_SFT, 0),
+ SND_SOC_DAPM_ADC("ADC1 R", NULL, ADC1_CTL,
+ ADC1_CTL_AD1REN_SFT, 0),
+ /* DAC Mixer */
+ SND_SOC_DAPM_MIXER("AOUT FL FR Mixer",
+ SND_SOC_NOPM, 0, 0,
+ atc2603a_dac_lr_mix,
+ ARRAY_SIZE(atc2603a_dac_lr_mix)),
+ SND_SOC_DAPM_DAC("DAC FL", NULL, DAC_ANANLOG4,
+ DAC_ANALOG4_DACEN_FL_SFT, 0),
+ SND_SOC_DAPM_DAC("DAC FR", NULL, DAC_ANANLOG4,
+ DAC_ANALOG4_DACEN_FR_SFT, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "AIF Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ /* output lines */
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("SP"),
+};
+
+static const struct snd_soc_dapm_route atc2603a_dapm_routes[] = {
+ {"MICIN0L", NULL, "MICIN0LP"},
+ {"MICIN0L", NULL, "MICIN0LN"},
+ {"MICIN0R", NULL, "MICIN0RP"},
+ {"MICIN0R", NULL, "MICIN0RN"},
+ {"MICIN1L", NULL, "MICIN1LP"},
+ {"MICIN1L", NULL, "MICIN1LN"},
+ {"MICIN1R", NULL, "MICIN1RP"},
+ {"MICIN1R", NULL, "MICIN1RN"},
+ {"FM L", NULL, "FMINL"},
+ {"FM R", NULL, "FMINR"},
+ {"MICIN0", NULL, "MICIN0L"},
+ {"MICIN0", NULL, "MICIN0R"},
+ {"MICIN1", NULL, "MICIN1L"},
+ {"MICIN1", NULL, "MICIN1R"},
+ {"FM", NULL, "FM L"},
+ {"FM", NULL, "FM R"},
+ {"ADC0 Mux", "MIC0", "MICIN0"},
+ {"ADC0 Mux", "FM", "FM"},
+ {"ADC0 Mux", "AOUT MIXER", "AOUT FL FR Mixer"},
+ {"ADC0 L", NULL, "ADC0 Mux"},
+ {"ADC0 R", NULL, "ADC0 Mux"},
+ {"AIFTX", NULL, "ADC0 L"},
+ {"AIFTX", NULL, "ADC0 R"},
+ {"AOUT FL FR Mixer", "FL FR Switch", "AIFRX"},
+ {"AOUT FL FR Mixer", "MIC Switch", "MICIN0"},
+ {"AOUT FL FR Mixer", "FM Switch", "FM"},
+ {"DAC FL", NULL, "AOUT FL FR Mixer"},
+ {"DAC FR", NULL, "AOUT FL FR Mixer"},
+ {"HP", NULL, "DAC FL"},
+ {"HP", NULL, "DAC FR"},
+ {"SP", NULL, "DAC FL"},
+ {"SP", NULL, "DAC FR"},
+};
+
+static void pa_up(struct snd_soc_codec *codec) {
+ /* DAC\B3\E4\B5\E7\B5ķ\BD\B7\A8:\B7\C7ֱ\C7\FD
+ a) \CB\F9\D3\D0audio analog\BCĴ\E6\C6\F7\B6\BCд0\A3\AC\D6\F7\BF\D8TX\BA\CDTXFIFO\B6\BCʹ\C4ܡ\A3
+ b) \CF\F2\D6\F7\BF\D8\C0\EF\C3\E6д\D7\EEСֵ\A3\A80x80000000\A3\A9
+ c) \D6\F7\BFغ\CDatc2603aѡ\D4\F1N wireģʽ\A3\AC2.0 channelģʽ
+ d) atc2603a\B5\C4DAC_D&A\B6\BCʹ\C4\DC
+ e) PA BIAS EN \A3\ACPA EN\A3\ACLOOP2 EN\A3\ACatc2603_DAC_ANALOG1дȫ0
+ f) Delay10ms
+ g) DAC \BF\AAʼ\B7\C5ramp\CA\FD\BEݣ\A8\B4\D30X80000000\B5\BD0x7fffffff\A3\ACÿ\B8\F40x36000дһ\B4\CE
+ \A3\ACʹ\D3\C3mips\B2\E9ѯд5201\B5\C4I2S_TX FIFO\A3\A9,ͬʱ\BF\AA\C6\F4ramp connect
+ h) \B5ȴ\FDԼ500 ms
+ i) \BF\AA\C6\F4pa\CA\E4\B3\F6\BC\B6en\A3\AC\B6Ͽ\AAramp connect\A3\ACLOOP2 Disable\A1\A3
+ */
+
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ /* i2stx fifo en */
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) | 0x3, I2S_FIFOCTL);
+ /* i2s tx en */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | 0x3, I2S_CTL);
+ snd_soc_write(codec, DAC_VOLUMECTL0, 0xbebe);
+ snd_soc_write(codec, DAC_ANANLOG0, 0);
+ snd_soc_write(codec, DAC_ANANLOG1, 0);
+ snd_soc_write(codec, DAC_ANANLOG2, 0);
+ snd_soc_write(codec, DAC_ANANLOG3, 0);
+ snd_soc_write(codec, DAC_ANANLOG4, 0);
+
+
+ if (direct_drive_disable == 0) {
+ snd_dai_writel(0x1 << 31, I2STX_DAT);
+ snd_dai_writel(0x1 << 31, I2STX_DAT);
+ } else {
+ snd_dai_writel(0x7ffffe00, I2STX_DAT);
+ snd_dai_writel(0x7ffffe00, I2STX_DAT);
+ }
+
+ /* 2.0-Channel Mode */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x7 << 4), I2S_CTL);
+ /* I2S input en */
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x01 << 1, 0x01 << 1);
+ /* FL&FR enable, dac_mute = 0(NOT MUTE) */
+ snd_soc_write(codec, DAC_DIGITALCTL, 0x3);
+ /* DAC analog FL&FR en, PA en, out stage disable */
+ snd_soc_write(codec, DAC_ANANLOG4, 0x08c0);
+ /* dac OPDA BIAS\C9\E8Ϊ100 */
+ snd_soc_write(codec, DAC_ANANLOG0, 0x26b3);
+ /*snd_soc_write(codec, DAC_ANANLOG3, 0x8b0b);
+ dac pa bias en\A3\ACPA LOOP2 en */
+ snd_soc_write(codec, DAC_ANANLOG3, 0xab0b);
+
+
+ if (direct_drive_disable == 0) {
+ /* vro IQ biggest */
+ snd_soc_update_bits(codec, DAC_ANANLOG2, 0x07, 0x7);
+ snd_soc_update_bits(codec, DAC_ANANLOG3, 0x07, 0x7);
+ }
+
+ snd_dai_writel(((snd_dai_readl(I2S_CTL) & ~(0x3 << 11)) | (0x1 << 11)), I2S_CTL);
+ if (direct_drive_disable == 0) {
+ msleep(100);
+
+ /* antipop_VRO Resistant Connect */
+ snd_soc_update_bits(codec, DAC_ANANLOG2, 0x1 << 3, 0x1 << 3);
+ /* out stage en */
+ snd_soc_update_bits(codec, DAC_ANANLOG4, 0x1 << 15, 0x1 << 15);
+ /* DAC_OPVRO enable */
+ snd_soc_update_bits(codec, DAC_ANANLOG2, 0x1 << 4, 0x1 << 4);
+
+ } else {
+ msleep(100);
+ /* ramp Connect EN */
+ snd_soc_update_bits(codec, DAC_ANANLOG2, 0x3 << 9, 0x3 << 9);
+ ramp_undirect(0x80000000, 0x7ffffe00);
+ }
+ if (direct_drive_disable == 0) {
+ //INIT_DELAYED_WORK(&dwork_pa, pa_up_handler);
+ //schedule_delayed_work(&dwork_pa, msecs_to_jiffies(500));
+ msleep(500);//\B3\E4\B7\D6\C5\D4·\B7ŵ\E7
+ /* antipop_VRO Resistant disconnect */
+ snd_soc_update_bits(codec, DAC_ANANLOG2, 0x1 << 3, 0);
+
+ } else {
+
+ msleep(400);
+ snd_soc_update_bits(codec, DAC_ANANLOG4,
+ DAC_ANALOG4_PAOSEN_FR_FL, DAC_ANALOG4_PAOSEN_FR_FL);/* out stage en */
+ snd_soc_update_bits(codec, DAC_ANANLOG3,
+ DAC_ANALOG3_ATPLP2_FR_FL, 0);/* PA LOOP2 disable */
+ msleep(100);
+ snd_soc_update_bits(codec, DAC_ANANLOG2,
+ DAC_ANALOG2_ATP2CE, 0);/* ramp disconnect */
+ }
+
+ snd_dai_writel(0x0, I2STX_DAT);
+ snd_dai_writel(0x0, I2STX_DAT);
+ msleep(20);
+}
+static void atc2603a_pa_down(struct snd_soc_codec *codec)
+{
+ /* \B7\C7ֱ\C7\FDPA\CFµ\E7\B9\FD\B3\CC:
+ a) \D2\F4\C1\BF\C9\E8Ϊ0
+ b) PA \CA\E4\B3\F6\BC\B6disable\A3\ACLOOP2 EN, \B8\F4ֱ\B5\E7\C8\DDDischarge \BF\AA\C6\F4
+ c) DAC \BF\AAʼ\B7\C5\D7\EEС\CA\FD\BE\DD,Delay \BA\F3\BF\AA\C6\F4ramp connect
+ d) \B7\C5ramp\CA\FD\BE\DD,\B7\C5\CD\EA\BA\F3\B5ȴ\FDԼ600 ms
+ e) \BCĴ\E6\C6\F7\B5\BDĬ\C8\CFֵ.
+ */
+
+ if(hw_init_flag == true)
+ {
+ snd_soc_write(codec, DAC_VOLUMECTL0, 0xbebe);
+ snd_soc_update_bits(codec, DAC_ANANLOG4,
+ DAC_ANALOG4_PAOSEN_FR_FL, 0);//pa outout stage disable
+
+ if (direct_drive_disable == 0) {
+ snd_soc_update_bits(codec,
+ DAC_ANANLOG2, DAC_ANALOG2_DDATPR, DAC_ANALOG2_DDATPR);//connect the resistant
+ snd_soc_update_bits(codec,
+ DAC_ANANLOG2, DAC_ANALOG2_OPVROEN, 0);//disable
+ } else {
+ snd_soc_update_bits(codec, DAC_ANANLOG3,
+ DAC_ANALOG3_ATPLP2_FR_FL, DAC_ANALOG3_ATPLP2_FR_FL);/* PA LOOP2 en */
+ /* \B8\F4ֱ\B5\E7\C8\DDDischarge \BF\AA\C6\F4 */
+ snd_soc_update_bits(codec, DAC_ANANLOG2,
+ DAC_ANALOG2_PAVDC, DAC_ANALOG2_PAVDC);
+ snd_dai_writel(0x7ffffe00, I2STX_DAT);
+ snd_dai_writel(0x7ffffe00, I2STX_DAT);
+ msleep(100);
+
+ /*act_snd_writel(act_snd_readl(CO_DAC_ANALOG2) | (0x1 << 9),
+ CO_DAC_ANALOG2); ramp Connect EN */
+ snd_soc_update_bits(codec,
+ DAC_ANANLOG2, DAC_ANALOG2_ATP2CE, DAC_ANALOG2_ATP2CE);
+ ramp_undirect(0x80000000, 0x7ffffe00);
+ msleep(600);
+ }
+ snd_soc_write(codec, DAC_VOLUMECTL0, 0);
+ hw_init_flag = false;
+ }
+}
+
+void atc2603a_pa_up_all(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ /* \B1\EAʶclassd\B5\C4״̬\CAǷ\F1\B1\BB\C9\E8\D6\C3Ϊ\B9̶\A8\B2\BB\D0\E8Ҫ\B8ı䣬ȱʡ\CA\C7TRUE */
+ static int classd_flag = 1;
+ struct clk *apll_clk;
+
+ module_clk_disable(MOD_ID_I2SRX);
+ module_clk_disable(MOD_ID_I2STX);
+ module_clk_enable(MOD_ID_I2SRX);
+ module_clk_enable(MOD_ID_I2STX);
+
+ /*for the fucked bug of motor shaking while startup,
+ because GPIOB(1) is mfp with I2S_LRCLK1*/
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ snd_dai_writel(snd_dai_readl(GPIO_BOUTEN) | 2, GPIO_BOUTEN);
+
+ if(((snd_dai_readl(I2S_CTL) & 0x3) == 0x0)) {
+ /* disable i2s tx&rx */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 0), I2S_CTL);
+
+ /* avoid sound while reset fifo */
+ snd_soc_update_bits(codec, DAC_ANANLOG1, 0x1 << 10, 0);
+
+ /* reset i2s rx&&tx fifo, avoid left & right channel wrong */
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) & ~(0x3 << 9) & ~0x3, I2S_FIFOCTL);
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) | (0x3 << 9) | 0x3, I2S_FIFOCTL);
+
+ /* this should before enable rx/tx, or after suspend, data may be corrupt */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 11),I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | (0x1 << 11),I2S_CTL);
+ /* set i2s mode I2S_RX_ClkSel==1 */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | (0x1 << 10), I2S_CTL);
+
+ /* enable i2s rx/tx at the same time */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | 0x3, I2S_CTL);
+
+ apll_clk = clk_get(NULL, CLKNAME_AUDIOPLL);
+ clk_prepare(apll_clk);
+ clk_enable(apll_clk);
+
+ /* i2s rx 00: 2.0-Channel Mode */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 8), I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 4), I2S_CTL);
+ }
+
+ /* enable i2s pad,i2s 4 wire pad all en */
+ /* EXTIRQ pad enable,ʹ\D6ж\CF\D0ź\C5\C4\DC\CB͵\BD\D6\F7\BF\D85201 */
+ snd_soc_update_bits_pmu(codec, ATC2603A_PAD_EN, 0x3901, 0x3901);
+
+ /* I2S: 2.0 Channel, SEL 4WIRE MODE */
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL,0x1<<7, 0);
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x3 << 5, 0x01 << 5);
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL,0x1<<4, 0);
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL,0x3<<2, 0);
+
+ /* bit5:2 I2S_DIN Multiplexing=I2S_Din,SEL 4WIRE MODE */
+ ret = snd_soc_update_bits_pmu(codec, ATC2603A_MFP_CTL0, 0x0f << 2, 0);//I2S,not GPIO
+
+ /* \B4\F2\BF\AA\C4ڲ\BF\B5Ļ\F9\C2˲\A8\C6\F7,\BC\F5С\B5\D7\D4룬\CC\E1\C9\FDaudio\D0\D4\C4\DC */
+ /*set_bdg_ctl();*/
+ /* for atc2603a those burn Efuse */
+ /* bit4:0 should not be changed, otherwise VREF isn't accurate */
+ ret = snd_soc_update_bits_pmu(codec, ATC2603A_PMU_BDG_CTL, 0x01 << 6, 0x01 << 6);
+ ret = snd_soc_update_bits_pmu(codec, ATC2603A_PMU_BDG_CTL, 0x01 << 5, 0);
+
+ pa_up(codec);
+
+ if (direct_drive_disable == 0) {
+ snd_soc_update_bits(codec, DAC_ANANLOG3, 0xa0f, 0xa0f);
+ snd_soc_update_bits(codec, DAC_ANANLOG2, 0x07, 0x3);
+ snd_soc_update_bits(codec, DAC_ANANLOG3, 0x07, 0x3);
+ }
+
+ if (classd_flag == 1) {
+ snd_soc_update_bits(codec, DAC_ANANLOG3, 0x1 << 9, 0x1 << 9);
+ snd_soc_update_bits(codec, DAC_ANANLOG2, 0x1 << 15, 0x1 << 15);
+ snd_soc_update_bits(codec, DAC_ANANLOG3, 0x1 << 13, 0x1 << 13);
+ }
+
+ //after pa_up, the regs status should be the same as outstandby?
+ snd_soc_update_bits(codec, DAC_ANANLOG1, DAC_ANALOG1_DACFL_FRMUTE, 0);//mute
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x01 << 1, 0);//I2S input disable
+ snd_soc_update_bits(codec, DAC_ANANLOG1, DAC_ANALOG1_PASW, DAC_ANALOG1_PASW);//1.6v
+ snd_soc_update_bits(codec, DAC_FILTERCTL0, 0x03, 2);
+ snd_soc_update_bits(codec, DAC_ANANLOG1, 0x3f, 40);//
+ snd_soc_write(codec, DAC_VOLUMECTL0, 0xb5b5);//0//0xbebe
+
+ if (direct_drive_disable == 0) {
+ }
+ else {
+ snd_soc_write(codec, DAC_ANANLOG2, 0x8400);
+ snd_soc_write(codec, DAC_ANANLOG3, 0xaa0b);
+ }
+
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~0x3, I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) & ~(0x3 << 9) & ~0x3, I2S_FIFOCTL);
+
+
+}
+EXPORT_SYMBOL_GPL(atc2603a_pa_up_all);
+
+static int atc2603a_audio_set_dai_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute) {
+ snd_soc_update_bits(codec, DAC_ANANLOG1,
+ DAC_ANALOG1_DACFL_FRMUTE, 0);
+ } else {
+ snd_soc_update_bits(codec, DAC_ANANLOG1,
+ DAC_ANALOG1_DACFL_FRMUTE,
+ DAC_ANALOG1_DACFL_FRMUTE);
+ }
+
+ return 0;
+}
+static int atc2603a_audio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ /* enable the atc2603a i2s input function */
+ struct snd_soc_codec *codec = dai->codec;
+
+ if(hw_init_flag == false) {
+ atc2603a_pa_up_all(codec);
+ hw_init_flag = true;
+ }
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x03 << 5, 0x01 << 5);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (direct_drive_disable == 0) {
+
+ } else {
+ snd_soc_write(codec, DAC_ANANLOG2, 0x8400);
+ snd_soc_write(codec, DAC_ANANLOG3, 0xaa0b);
+ }
+
+ snd_soc_update_bits(codec,
+ AUDIOINOUT_CTL, 0x01 << 1, 0x01 << 1);
+ snd_soc_update_bits(codec,
+ DAC_ANANLOG4, 0x03 << 6, 0x03 << 6);
+ } else {
+ snd_soc_update_bits(codec,
+ ADC1_CTL, 0x3 << 7, 0x3 << 7);//VRDA0EN | VRDA1EN
+ snd_soc_update_bits(codec,
+ ADC0_HPFCTL, 0x3 << 0, 0x3 << 0);//adc0 hpf disable
+ snd_soc_update_bits(codec,
+ AUDIOINOUT_CTL, 0x01 << 8, 0x01 << 8);
+ }
+ atc2603a_open_count++;
+
+ return 0;
+}
+
+static int atc2603a_audio_hw_free(
+ struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ snd_dbg("atc2603a_audio_hw_free\n");
+ /* disable the atc2603a i2s input function */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_soc_update_bits(codec,
+ AUDIOINOUT_CTL,
+ 0x01 << 1, 0);
+ } else {
+ snd_soc_update_bits(codec,
+ AUDIOINOUT_CTL,
+ 0x01 << 8, 0);
+ }
+ return 0;
+}
+
+static int atc2603a_audio_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ /* nothing should to do here now */
+ return 0;
+}
+
+static int atc2603a_audio_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ snd_soc_update_bits(codec, DAC_FILTERCTL0, 0x03, 2);
+ /* we set the i2s 2 channel-mode by default */
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x03 << 2, 0);
+
+ return 0;
+}
+
+static int atc2603a_audio_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ /* nothing should to do here now */
+ return 0;
+}
+
+static void atc2603a_power_down(struct snd_soc_codec *codec)
+{
+ atc2603a_pa_down(codec);
+}
+
+static int atc2603a_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+#if 0
+ int ret;
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_dbg("%s: SND_SOC_BIAS_ON\n", __func__);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ snd_dbg("%s: SND_SOC_BIAS_PREPARE\n", __func__);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ #ifdef SND_SOC_BIAS_DEBUG
+ snd_dbg("%s: SND_SOC_BIAS_STANDBY\n", __func__);
+ #endif
+
+ if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+ codec->cache_only = false;
+ codec->cache_sync = 1;
+ ret = snd_soc_cache_sync(codec);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_dbg("%s: SND_SOC_BIAS_OFF\n", __func__);
+ break;
+
+ default:
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+#endif
+ return 0;
+}
+
+static void reenable_audio_block(struct snd_soc_codec *codec)
+{
+ snd_soc_update_bits_pmu(codec,
+ ATC2603A_CMU_DEVRST, 0x01 << 4, 0);//audio block reset
+
+ snd_soc_update_bits_pmu(codec, ATC2603A_CMU_DEVRST,
+ 0x01 << 10, 0x01 << 10);//SCLK to Audio Clock Enable Control
+ snd_soc_update_bits_pmu(codec,
+ ATC2603A_CMU_DEVRST, 0x01 << 4, 0x01 << 4);
+
+}
+
+static int atc2603a_probe(struct snd_soc_codec *codec)
+{
+ snd_dbg("atc2603a_probe!\n");
+ if (codec == NULL)
+ snd_dbg("NULL codec \r\n");
+
+ snd_dbg("codec->name = %s\r\n", codec->component.name);
+
+ atc2603a_codec = codec;
+
+ hw_init_flag = false;
+ reenable_audio_block(codec);
+
+ atc2603a_pa_up_all(codec);
+ hw_init_flag = true;
+
+
+ codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
+
+ return 0;
+}
+
+static int atc2603a_remove(struct snd_soc_codec *codec)
+{
+ atc2603a_pa_down(codec);
+ return 0;
+}
+
+static int atc2603a_suspend(struct snd_soc_codec *codec)
+{
+#if 0
+ atc2603a_power_down(codec);
+ atc2603a_set_bias_level(codec, SND_SOC_BIAS_OFF);
+#endif
+ return 0;
+}
+
+static int atc2603a_resume(struct snd_soc_codec *codec)
+{
+ reenable_audio_block(codec);
+#if 0
+ atc2603a_pa_up_all(codec);
+ hw_init_flag = true;
+#endif
+ atc2603a_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+static void atc2603a_audio_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+#if 0
+ struct snd_soc_codec *codec = dai->codec;
+
+ atc2603a_power_down(codec);
+ atc2603a_set_bias_level(codec, SND_SOC_BIAS_OFF);
+#endif
+ return;
+}
+
+#define ATC2603A_RATES SNDRV_PCM_RATE_8000_192000
+#define ATC2603A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai_ops atc2603a_aif_dai_ops = {
+ .shutdown = atc2603a_audio_shutdown,
+ .hw_params = atc2603a_audio_hw_params,
+ .hw_free = atc2603a_audio_hw_free,
+ .prepare = atc2603a_audio_prepare,
+ .set_fmt = atc2603a_audio_set_dai_fmt,
+ .set_sysclk = atc2603a_audio_set_dai_sysclk,
+ .digital_mute = atc2603a_audio_set_dai_digital_mute,
+};
+
+struct snd_soc_dai_driver codec_atc2603a_dai[] = {
+ {
+ .name = "atc2603a-dai",
+ .id = ATC2603A_AIF,
+ .playback = {
+ .stream_name = "AIF Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = ATC2603A_RATES,
+ .formats = ATC2603A_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = ATC2603A_RATES,
+ .formats = ATC2603A_FORMATS,
+ },
+ .ops = &atc2603a_aif_dai_ops,
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_atc2603a = {
+ .probe = atc2603a_probe,
+ .remove = atc2603a_remove,
+
+ .suspend = atc2603a_suspend,
+ .resume = atc2603a_resume,
+ //.set_bias_level = atc2603a_set_bias_level,
+ .idle_bias_off = true,
+
+ .reg_cache_size = (AGC1_CTL2 + 1),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = atc2603a_reg,
+ .reg_cache_step = 1,
+
+ .controls = atc2603a_snd_controls,
+ .num_controls = ARRAY_SIZE(atc2603a_snd_controls),
+ .dapm_widgets = atc2603a_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(atc2603a_dapm_widgets),
+ .dapm_routes = atc2603a_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(atc2603a_dapm_routes),
+ .write = atc2603a_write,
+ .read = atc2603a_read,
+};
+
+static ssize_t error_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", error_switch);
+ return cnt;
+}
+
+static ssize_t error_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ error_switch = tmp;
+ break;
+ default:
+ printk(KERN_ERR"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+static ssize_t debug_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", debug_switch);
+ return cnt;
+}
+
+static ssize_t debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ debug_switch = tmp;
+ break;
+ default:
+ printk(KERN_INFO"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+static struct device_attribute atc2603a_attr[] = {
+ __ATTR(error, S_IRUSR | S_IWUSR, error_show, error_store),
+ __ATTR(debug, S_IRUSR | S_IWUSR, debug_show, debug_store),
+};
+
+int atc2603a_audio_get_pmu_status(void)
+{
+ return atc2603a_ictype;
+}
+
+EXPORT_SYMBOL_GPL(atc2603a_audio_get_pmu_status);
+
+static int atc2603a_platform_probe(struct platform_device *pdev)
+{
+ int i;
+ int ret = 0;
+
+/*
+ dn = of_find_compatible_node(NULL, NULL, "actions,atm7039c-i2s");
+ if (!dn) {
+ snd_err("Fail to get device_node actions,atm7039c-i2s\r\n");
+ //goto of_get_failed;
+ }
+*/
+
+ /*FIXME: what if error in second or third loop*/
+/*
+ for(i=0; i<2; i++)
+ {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ snd_err("no memory resource i=%d\n", i);
+ return -ENODEV;
+ }
+
+ if (!devm_request_mem_region (&pdev->dev, res->start,
+ resource_size(res), "gl5203-audio-i2s")) {
+ snd_err("Unable to request register region\n");
+ return -EBUSY;
+ }
+
+ codec_res.base[i] = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (codec_res.base[i] == NULL) {
+ snd_err("Unable to ioremap register region\n");
+ return -ENXIO;
+ }
+
+ snd_err("it's ok %d\n", i);
+ }
+*/
+
+
+ for (i = 0; i < ARRAY_SIZE(atc2603a_attr); i++) {
+ snd_err("add file!\r\n");
+ ret = device_create_file(&pdev->dev, &atc2603a_attr[i]);
+ }
+
+ atc260x = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, atc260x);
+
+ atc2603a_ictype = ATC260X_ICTYPE_2603A;
+
+ pdev->dev.init_name = "atc260x-audio";
+ snd_err("register codec\r\n");
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_atc2603a,
+ codec_atc2603a_dai, ARRAY_SIZE(codec_atc2603a_dai));
+}
+
+static int atc2603a_platform_remove(struct platform_device *pdev)
+{
+ int i = 0;
+ struct device *dev;
+
+ dev = bus_find_device_by_name(&platform_bus_type, NULL, "atc260x-audio");
+ if (dev) {
+ for (i = 0; i < ARRAY_SIZE(atc2603a_attr); i++) {
+ snd_err("remove file!\r\n");
+ device_remove_file(dev, &atc2603a_attr[i]);
+ }
+ } else {
+ snd_err("Find platform device atc2603a-audio failed!\r\n");
+ return -ENODEV;
+ }
+
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static void atc2603a_platform_shutdown(struct platform_device *pdev)
+{
+ gpio_direction_output(speaker_gpio_num, 0);
+ snd_soc_write(atc2603a_codec, DAC_VOLUMECTL0, 0xBEBE);
+ snd_soc_write(atc2603a_codec, DAC_ANANLOG1, 0x0);
+ snd_soc_write(atc2603a_codec, DAC_ANANLOG3, 0x8B0B);
+ snd_soc_write(atc2603a_codec, DAC_ANANLOG4, 0x08C0);
+ snd_soc_write(atc2603a_codec, DAC_ANANLOG2, 0x0100);
+ snd_soc_update_bits(atc2603a_codec,
+ AUDIOINOUT_CTL,
+ 0x01 << 1, 0);
+ atc2603a_power_down(atc2603a_codec);
+ return;
+}
+
+static const struct of_device_id atc2603a_audio_of_match[]= {
+ {.compatible = "actions,atc2603a-audio",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, atc2603a_audio_of_match);
+
+static struct platform_driver atc2603a_platform_driver = {
+ .probe = atc2603a_platform_probe,
+ .remove = atc2603a_platform_remove,
+ .driver = {
+ .name = "atc2603a-audio",
+ .owner = THIS_MODULE,
+ .of_match_table = atc2603a_audio_of_match,
+ },
+ .shutdown = atc2603a_platform_shutdown,
+};
+
+static int atc2603a_get_cfg(void)
+{
+ u32 ret = 1;
+ struct device_node *dn;
+
+ dn = of_find_compatible_node(NULL, NULL, audio_device_node);
+ if (!dn) {
+ snd_err("Fail to get device_node\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32(dn, snd_earphone_output_mode,
+ &audio_hw_cfg.earphone_output_mode);
+ if (ret) {
+ snd_err("Fail to get snd_earphone_output_mode\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32(dn, snd_mic_num,
+ &audio_hw_cfg.mic_num);
+ if (ret) {
+ snd_err("Fail to get snd_mic_num\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32_array(dn, snd_mic0_gain,
+ audio_hw_cfg.mic0_gain, 2);
+ if (ret) {
+ snd_err("Fail to get snd_mic_gain\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32_array(dn, snd_speaker_gain,
+ audio_hw_cfg.speaker_gain, 2);
+ if (ret) {
+ snd_err("Fail to get snd_speaker_gain\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32_array(dn, snd_earphone_gain,
+ audio_hw_cfg.earphone_gain, 2);
+ if (ret) {
+ snd_err("Fail to get snd_earphone_gain\r\n");
+ goto of_get_failed;
+ }
+
+/*
+ ret = of_property_read_u32(dn, snd_speaker_volume,
+ &audio_hw_cfg.speaker_volume);
+ if (ret) {
+ snd_err("Fail to get snd_speaker_volume\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32(dn, snd_earphone_volume,
+ &audio_hw_cfg.earphone_volume);
+ if (ret) {
+ snd_err("Fail to get snd_earphone_volume\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32(dn, snd_earphone_detect_mode,
+ &audio_hw_cfg.earphone_detect_mode);
+ if (ret) {
+ snd_err("Fail to get snd_earphone_detect_mode\r\n");
+ goto of_get_failed;
+ }
+*/
+ audio_hw_cfg.speaker_volume = 0x28;
+ audio_hw_cfg.earphone_volume = 0x28;
+ audio_hw_cfg.earphone_detect_mode = 0;
+
+
+ speaker_gpio_num = of_get_named_gpio_flags(dn, speaker_ctrl_name, 0, NULL);
+ if (speaker_gpio_num < 0) {
+ snd_err("get gpio[%s] fail\r\n", speaker_ctrl_name);
+ }
+
+ snd_err("Success to get device_node\r\n");
+ return 0;
+of_get_failed:
+ return ret;
+}
+
+static void atc2603a_dump_cfg(void)
+{
+#if 0
+ printk(KERN_ERR"earphone_detect_mode = %d\r\n",audio_hw_cfg.earphone_detect_mode);
+ printk(KERN_ERR"earphone_gain[0] = %d\r\n",audio_hw_cfg.earphone_gain[0]);
+ printk(KERN_ERR"earphone_gain[1] = %d\r\n",audio_hw_cfg.earphone_gain[1]);
+ printk(KERN_ERR"speaker_gain[0] = %d\r\n",audio_hw_cfg.speaker_gain[0]);
+ printk(KERN_ERR"speaker_gain[1] = %d\r\n",audio_hw_cfg.speaker_gain[1]);
+ printk(KERN_ERR"mic0_gain[0] = %d\r\n",audio_hw_cfg.mic0_gain[0]);
+ printk(KERN_ERR"mic0_gain[1] = %d\r\n",audio_hw_cfg.mic0_gain[1]);
+ printk(KERN_ERR"earphone_volume = %d\r\n",audio_hw_cfg.earphone_volume);
+ printk(KERN_ERR"speaker_volume = %d\r\n",audio_hw_cfg.speaker_volume);
+ printk(KERN_ERR"earphone_output_mode = %d\r\n",audio_hw_cfg.earphone_output_mode);
+ printk(KERN_ERR"mic_num = %d\r\n",audio_hw_cfg.mic_num);
+#endif
+}
+
+static int atc2603a_pa_down_notifier(struct notifier_block *notifier,
+ unsigned long pm_event, void *v)
+{
+ mutex_lock(&atc2603a_pa_down_lock);
+
+ switch (pm_event) {
+
+ case PM_SUSPEND_PREPARE:
+ user_lock = 1;
+ snd_soc_update_bits(atc2603a_codec,
+ AUDIOINOUT_CTL,
+ 0x01 << 1, 1);
+ atc2603a_power_down(atc2603a_codec);
+ snd_soc_update_bits(atc2603a_codec,
+ AUDIOINOUT_CTL,
+ 0x01 << 1, 0);
+ break;
+
+ case PM_POST_SUSPEND:
+ user_lock = 0;
+ break;
+ }
+ mutex_unlock(&atc2603a_pa_down_lock);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block atc2603a_pa_down_nb = {
+ .notifier_call = atc2603a_pa_down_notifier,
+};
+
+static int __init atc2603a_init(void)
+{
+ u32 ret = 0;
+
+ printk("atc2603a_init\n");
+
+ ret = atc2603a_get_cfg();
+ if (ret){
+ snd_err("audio get cfg failed!\r\n");
+ goto audio_get_cfg_failed;
+ }
+
+ atc2603a_dump_cfg();
+
+ direct_drive_disable = audio_hw_cfg.earphone_output_mode;
+
+ ret = platform_driver_register(&atc2603a_platform_driver);
+ if(ret){
+ snd_err("platform_driver_register failed!\r\n");
+ goto platform_driver_register_failed;
+ }
+
+ //dev = bus_find_device_by_name(&platform_bus_type, NULL, "atc260x-audio");
+
+
+ register_pm_notifier(&atc2603a_pa_down_nb);
+
+ return 0;
+platform_driver_register_failed:
+audio_get_cfg_failed:
+ return ret;
+}
+
+static void __exit atc2603a_exit(void)
+{
+ platform_driver_unregister(&atc2603a_platform_driver);
+}
+
+module_init(atc2603a_init);
+module_exit(atc2603a_exit);
+
+
+MODULE_AUTHOR("sall.xie <sall.xie at actions-semi.com>");
+MODULE_DESCRIPTION("ATC2603A AUDIO module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atc260x/atc2603a-codec/atc2603a-audio-regs.h b/sound/soc/atc260x/atc2603a-codec/atc2603a-audio-regs.h
new file mode 100755
index 0000000..7acf8f9
--- /dev/null
+++ b/sound/soc/atc260x/atc2603a-codec/atc2603a-audio-regs.h
@@ -0,0 +1,395 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2009 Actions Semi Inc.
+ */
+
+#ifndef __ATV5302_AUDIO_REGS_H__
+#define __ATV5302_AUDIO_REGS_H__
+
+/* AUDIOINOUT_CTL */
+#define AUDIOINOUT_CTL_INEN (0x1 << 1)
+#define AUDIOINOUT_CTL_IMD(x) (((x) & 0x3) << 2)
+#define AUDIOINOUT_CTL_LB (0x1 << 4)
+#define AUDIOINOUT_CTL_IMS(x) (((x) & 0x3) << 5)
+#define AUDIOINOUT_CTL_OMD (0x1 << 7)
+#define AUDIOINOUT_CTL_OEN (0x1 << 8)
+#define AUDIOINOUT_CTL_OCIEN (0x1 << 9)
+#define AUDIOINOUT_CTL_OHSCIEN (0x1 << 10)
+#define AUDIOINOUT_CTL_MDD (0x1 << 11)
+#define AUDIOINOUT_CTL_EIDR (0x1 << 12)
+
+/* DAC_FILTERCTL0 */
+#define DAC_FILTERCTL0_DOSRSFL_FR(x) (((x) & 0x3) << 0)
+#define DAC_FILTERCTL0_DOSRSSW_C(x) (((x) & 0x3) << 2)
+#define DAC_FILTERCTL0_DOSRSSL_SR(x) (((x) & 0x3) << 4)
+#define DAC_FILTERCTL0_DOSRSSBL_SBR(x) (((x) & 0x3) << 6)
+#define DAC_FILTERCTL0_DISRS (0x1 << 8)
+#define DAC_FILTERCTL0_DEDFL_FR (0x1 << 9)
+#define DAC_FILTERCTL0_DEDSW_C (0x1 << 10)
+#define DAC_FILTERCTL0_DEDSL_SR (0x1 << 11)
+#define DAC_FILTERCTL0_DEDSBL_SBR (0x1 << 12)
+
+/* DAC_FILTERCTL1 */
+#define DAC_FILTERCTL1_DBWFL_FR(x) (((x) & 0x3) << 0)
+#define DAC_FILTERCTL1_DBWSW_C(x) (((x) & 0x3) << 2)
+#define DAC_FILTERCTL1_DBWSL_SR(x) (((x) & 0x3) << 4)
+#define DAC_FILTERCTL1_DBWSBL_SBR(x) (((x) & 0x3) << 6)
+
+/* DAC_DIGITALCTL */
+#define DAC_DIGITALCTL_DEFL (0x1 << 0)
+#define DAC_DIGITALCTL_DEFL_SFT (0)
+#define DAC_DIGITALCTL_DEFR (0x1 << 1)
+#define DAC_DIGITALCTL_DEFR_SFT (1)
+#define DAC_DIGITALCTL_DESW (0x1 << 2)
+#define DAC_DIGITALCTL_DEC (0x1 << 3)
+#define DAC_DIGITALCTL_DESL (0x1 << 4)
+#define DAC_DIGITALCTL_DESR (0x1 << 5)
+#define DAC_DIGITALCTL_DESBL (0x1 << 6)
+#define DAC_DIGITALCTL_DESBR (0x1 << 7)
+#define DAC_DIGITALCTL_DMFL (0x1 << 8)
+#define DAC_DIGITALCTL_DMFR (0x1 << 9)
+#define DAC_DIGITALCTL_DMSW (0x1 << 10)
+#define DAC_DIGITALCTL_DMC (0x1 << 11)
+#define DAC_DIGITALCTL_DMSL (0x1 << 12)
+#define DAC_DIGITALCTL_DMSR (0x1 << 13)
+#define DAC_DIGITALCTL_DMSBL (0x1 << 14)
+#define DAC_DIGITALCTL_DMSBR (0x1 << 15)
+
+/* DAC_VOLUMECTL0 */
+#define DAC_VOLUMECTL0_DACFL_VOLUME(x) (((x) & 0xff) << 0)
+#define DAC_VOLUMECTL0_DACFL_VOLUME_SFT (0)
+#define DAC_VOLUMECTL0_DACFR_VOLUME(x) (((x) & 0xff) << 8)
+#define DAC_VOLUMECTL0_DACFR_VOLUME_SFT (8)
+
+/* DAC_VOLUMECTL1 */
+#define DAC_VOLUMECTL1_DACSW_VOLUME(x) (((x) & 0xff) << 0)
+#define DAC_VOLUMECTL1_DACC_VOLUME(x) (((x) & 0xff) << 8)
+
+/* DAC_VOLUMECTL2 */
+#define DAC_VOLUMECTL2_DACSL_VOLUME(x) (((x) & 0xff) << 0)
+#define DAC_VOLUMECTL2_DACSR_VOLUME(x) (((x) & 0xff) << 8)
+
+/* DAC_VOLUMECTL3 */
+#define DAC_VOLUMECTL3_DACSBL_VOLUME(x) (((x) & 0xff) << 0)
+#define DAC_VOLUMECTL3_DACSBR_VOLUME(x) (((x) & 0xff) << 8)
+
+/* DAC_ANALOG0 */
+#define DAC_ANALOG0_OPGIB(x) (((x) & 0x7) << 0)
+#define DAC_ANALOG0_KFEN (0x1 << 3)
+#define DAC_ANALOG0_OPVBIB(x) (((x) & 0x3) << 4)
+#define DAC_ANALOG0_OPDTSIB(x) (((x) & 0x3) << 6)
+#define DAC_ANALOG0_OPDAIB(x) (((x) & 0x7) << 8)
+#define DAC_ANALOG0_OPDAVB(x) (((x) & 0x3) << 12)
+#define DAC_ANALOG0_PAIB(x) (((x) & 0x3) << 14)
+
+/* DAC_ANALOG1 */
+#define DAC_ANALOG1_VOLUME(x) (((x) & 0x3f) << 0)
+#define DAC_ANALOG1_VOLUME_SFT (0)
+#define DAC_ANALOG1_PASW (0x1 << 6)
+#define DAC_ANALOG1_PASW_SFT (6)
+#define DAC_ANALOG1_ZERODT (0x1 << 7)
+#define DAC_ANALOG1_PAIQ(x) (((x) & 0x3) << 8)
+#define DAC_ANALOG1_DACFL_FRMUTE (0x1 << 10)
+#define DAC_ANALOG1_DACFL_FRMUTE_SFT (10)
+#define DAC_ANALOG1_DACSW_CMUTE (0x1 << 11)
+#define DAC_ANALOG1_DACSL_SRMUTE (0x1 << 12)
+#define DAC_ANALOG1_DACSBL_SBRMUTE (0x1 << 13)
+#define DAC_ANALOG1_DACFMMUTE (0x1 << 14)
+#define DAC_ANALOG1_DACFMMUTE_SFT (14)
+#define DAC_ANALOG1_DACMICMUTE (0x1 << 15)
+#define DAC_ANALOG1_DACMICMUTE_SFT (15)
+
+/* DAC_ANALOG2 */
+#define DAC_ANALOG2_OPVROOSIB(x) (((x) & 0x7) << 0)
+#define DAC_ANALOG2_DDATPR (0x1 << 3)
+#define DAC_ANALOG2_OPVROEN (0x1 << 4)
+#define DAC_ANALOG2_DDOVV (0x1 << 5)
+#define DAC_ANALOG2_CLDMIX(x) (((x) & 0x3) << 6)
+#define DAC_ANALOG2_PAVDC (0x1 << 8)
+#define DAC_ANALOG2_ATP2CE (0x1 << 9)
+#define DAC_ANALOG2_P2IB (0x1 << 10)
+#define DAC_ANALOG2_DACI (0x1 << 11)
+#define DAC_ANALOG2_ZERODETECT (0x1 << 15)
+
+/* DAC_ANALOG3 */
+#define DAC_ANALOG3_OPVROIB(x) (((x) & 0x7) << 0)
+#define DAC_ANALOG3_OPCM1IB(x) (((x) & 0x3) << 3)
+#define DAC_ANALOG3_ATPLP2_SBR_SBL (0x1 << 5)
+#define DAC_ANALOG3_ATPLP2_SR_SL (0x1 << 6)
+#define DAC_ANALOG3_ATPLP2_SW_C (0x1 << 7)
+#define DAC_ANALOG3_ATPLP2_FR_FL (0x1 << 8)
+#define DAC_ANALOG3_BIASEN (0x1 << 9)
+#define DAC_ANALOG3_EIDEN (0x1 << 10)
+#define DAC_ANALOG3_VLCHD (0x1 << 13)
+#define DAC_ANALOG3_OVLS (0x1 << 14)
+#define DAC_ANALOG3_EIDS (0x1 << 15)
+
+/* DAC_ANALOG4 */
+#define DAC_ANALOG4_DACEN_SBR (0x1 << 0)
+#define DAC_ANALOG4_DACEN_SBL (0x1 << 1)
+#define DAC_ANALOG4_DACEN_SR (0x1 << 2)
+#define DAC_ANALOG4_DACEN_SL (0x1 << 3)
+#define DAC_ANALOG4_DACEN_C (0x1 << 4)
+#define DAC_ANALOG4_DACEN_SW (0x1 << 5)
+#define DAC_ANALOG4_DACEN_FR (0x1 << 6)
+#define DAC_ANALOG4_DACEN_FR_SFT (6)
+#define DAC_ANALOG4_DACEN_FL (0x1 << 7)
+#define DAC_ANALOG4_DACEN_FL_SFT (7)
+#define DAC_ANALOG4_PAEN_SBR_SBL (0x1 << 8)
+#define DAC_ANALOG4_PAEN_SR_SL (0x1 << 9)
+#define DAC_ANALOG4_PAEN_SW_C (0x1 << 10)
+#define DAC_ANALOG4_PAEN_FR_FL (0x1 << 11)
+#define DAC_ANALOG4_PAEN_FR_FL_SFT (11)
+#define DAC_ANALOG4_PAOSEN_SBR_SBL (0x1 << 12)
+#define DAC_ANALOG4_PAOSEN_SR_SL (0x1 << 13)
+#define DAC_ANALOG4_PAOSEN_SW_C (0x1 << 14)
+#define DAC_ANALOG4_PAOSEN_FR_FL (0x1 << 15)
+#define DAC_ANALOG4_PAOSEN_FR_FL_SFT (15)
+
+/* CLASSD_CTL0 */
+#define CLASSD_CTL0_CLD1EN (0x1 << 0)
+#define CLASSD_CTL0_CLD2EN (0x1 << 1)
+#define CLASSD_CTL0_MUTE (0x1 << 2)
+#define CLASSD_CTL0_PEN (0x1 << 3)
+#define CLASSD_CTL0_FBEN (0x1 << 4)
+#define CLASSD_CTL0_OTPEN (0x1 << 5)
+#define CLASSD_CTL0_SCEN (0x1 << 6)
+#define CLASSD_CTL0_SSEN (0x1 << 7)
+#define CLASSD_CTL0_SABD (0x1 << 8)
+#define CLASSD_CTL0_NCLPEN (0x1 << 9)
+#define CLASSD_CTL0_DBGIN (0x1 << 10)
+#define CLASSD_CTL0_GAIN (0x1 << 11)
+#define CLASSD_CTL0_OTPR(x) (((x) & 0x7) << 13)
+
+/* CLASSD_CTL1 */
+#define CLASSD_CTL1_IBREG(x) (((x) & 0xf) << 0)
+#define CLASSD_CTL1_EDG(x) (((x) & 0x3) << 4)
+#define CLASSD_CTL1_NCLPR(x) (((x) & 0x3) << 6)
+#define CLASSD_CTL1_FSEN(x) (((x) & 0x3) << 8)
+#define CLASSD_CTL1_SSR(x) (((x) & 0x3) << 10)
+
+/* CLASSD_CTL2 */
+#define CLASSD_CTL2_VREC(x) (((x) & 0x3) << 0)
+#define CLASSD_CTL2_RTIME(x) (((x) & 0x7) << 6)
+#define CLASSD_CTL2_ATIME(x) (((x) & 0x7) << 9)
+#define CLASSD_CTL2_SCWN1 (0x1 << 12)
+#define CLASSD_CTL2_OHWN1 (0x1 << 13)
+#define CLASSD_CTL2_SCWN2 (0x1 << 14)
+#define CLASSD_CTL2_OHWN2 (0x1 << 15)
+
+
+/* ADC0_DIGITALCTL */
+#define ADC0_DIGITALCTL_DMREN (0x1 << 0)
+#define ADC0_DIGITALCTL_DMLEN (0x1 << 1)
+#define ADC0_DIGITALCTL_DRFS (0x1 << 2)
+#define ADC0_DIGITALCTL_VREN (0x1 << 3)
+#define ADC0_DIGITALCTL_DCEN (0x1 << 4)
+#define ADC0_DIGITALCTL_DCD (0x1 << 5)
+#define ADC0_DIGITALCTL_ADGC0(x) (((x) & 0xf) << 6)
+#define ADC0_DIGITALCTL_ADGC0_SFT (6)
+#define ADC0_DIGITALCTL_AD0DEN (0x1 << 10)
+#define ADC0_DIGITALCTL_AD0DLR (0x1 << 11)
+
+/* ADC0_HPFCTL */
+#define ADC0_HPFCTL_HPF0REN (0x1 << 0)
+#define ADC0_HPFCTL_HPF0LEN (0x1 << 1)
+#define ADC0_HPFCTL_HPF0DW (0x1 << 2)
+#define ADC0_HPFCTL_WNHPF0CUT(x) (((x) & 0x7) << 3)
+#define ADC0_HPFCTL_SRSEL0(x) (((x) & 0x3) << 6)
+
+/* ADC0_CTL */
+#define ADC0_CTL_ADCIS(x) (((x) & 0x3) << 0)
+#define ADC0_CTL_ADCIS_SFT (0)
+#define ADC0_CTL_AD0REN (0x1 << 2)
+#define ADC0_CTL_AD0REN_SFT (2)
+#define ADC0_CTL_AD0LEN (0x1 << 3)
+#define ADC0_CTL_AD0LEN_SFT (3)
+#define ADC0_CTL_MIC0FDSE (0x1 << 4)
+#define ADC0_CTL_MIC0FDSE_SFT (4)
+#define ADC0_CTL_MIC0REN (0x1 << 5)
+#define ADC0_CTL_MIC0REN_SFT (5)
+#define ADC0_CTL_MIC0LEN (0x1 << 6)
+#define ADC0_CTL_MIC0LEN_SFT (6)
+#define ADC0_CTL_VMICEXST(x) (((x) & 0x3) << 7)
+#define ADC0_CTL_VMICEXST_SFT (7)
+#define ADC0_CTL_VMICEXEN (0x1 << 9)
+#define ADC0_CTL_VMICEXEN_SFT (9)
+#define ADC0_CTL_FMGAIN(x) (((x) & 0x7) << 10)
+#define ADC0_CTL_FMGAIN_SFT (10)
+#define ADC0_CTL_FMREN (0x1 << 13)
+#define ADC0_CTL_FMREN_SFT (13)
+#define ADC0_CTL_FMLEN (0x1 << 14)
+#define ADC0_CTL_FMLEN_SFT (14)
+#define ADC0_CTL_VMICINEN (0x1 << 15)
+#define ADC0_CTL_VMICINEN_SFT (15)
+
+/* AGC0_CTL0 */
+#define AGC0_CTL0_AMP0GR1(x) (((x) & 0x7) << 0)
+#define AGC0_CTL0_AMP0GR1_SET (0)
+#define AGC0_CTL0_IMICSHD (0x1 << 7)
+#define AGC0_CTL0_AMP1G0R(x) (((x) & 0xf) << 8)
+#define AGC0_CTL0_AMP1G0R_SFT (8)
+#define AGC0_CTL0_AMP1G0L(x) (((x) & 0xf) << 12)
+#define AGC0_CTL0_AMP1G0L_SFT (12)
+
+#define AGC_CTL0_AMP1GR1_MSK (0x7 << 0)
+#define AGC_CTL0_AMP1GR_MSK (0xf << 8)
+#define AGC_CTL0_AMP1GL_MSK (0xf << 12)
+
+/* AGC0_CTL1 */
+#define AGC0_CTL1_RMSCY0(x) (((x) & 0x3) << 0)
+#define AGC0_CTL1_CMR0(x) (((x) & 0x3) << 2)
+#define AGC0_CTL1_DCYT0(x) (((x) & 0x7) << 4)
+#define AGC0_CTL1_ATKT0(x) (((x) & 0x7) << 7)
+#define AGC0_CTL1_NGT0(x) (((x) & 0x7) << 10)
+#define AGC0_CTL1_RCTMEN0 (0x1 << 15)
+
+/* AGC0_CTL1 */
+#define AGC0_CTL2_AGC0REN (0x1 << 0)
+#define AGC0_CTL2_AGC0LEN (0x1 << 1)
+#define AGC0_CTL2_GREN0 (0x1 << 2)
+#define AGC0_CTL2_ZEROC0 (0x1 << 3)
+#define AGC0_CTL2_NGTEN0 (0x1 << 4)
+#define AGC0_CTL2_NGSLEN0 (0x1 << 5)
+#define AGC0_CTL2_RMSCEN0 (0x1 << 6)
+#define AGC0_CTL2_RMSINSEL0 (0x1 << 7)
+#define AGC0_CTL2_ADBEN (0x1 << 8)
+#define AGC0_CTL2_MICAAEN (0x1 << 9)
+#define AGC0_CTL2_NGTHSEL0(x) (((x) & 0x7) << 10)
+#define AGC0_CTL2_TARGL0(x) (((x) & 0x7) << 13)
+
+/* ADC_ANALOG0 */
+#define ADC_ANALOG0_VRDABC(x) (((x) & 0x7) << 0)
+#define ADC_ANALOG0_OPBC23(x) (((x) & 0x3) << 3)
+#define ADC_ANALOG0_OPBC1(x) (((x) & 0x7) << 5)
+#define ADC_ANALOG0_IVSRMSTN(x) (((x) & 0x7) << 13)
+
+/* ADC_ANALOG1 */
+#define ADC_ANALOG1_FMBC(x) (((x) & 0x3) << 0)
+#define ADC_ANALOG1_FD1BUFBC(x) (((x) & 0x3) << 2)
+#define ADC_ANALOG1_FD2BC(x) (((x) & 0x3) << 4)
+#define ADC_ANALOG1_FD1BC(x) (((x) & 0x3) << 6)
+#define ADC_ANALOG1_ADCBIAS (0x1 << 10)
+#define ADC_ANALOG1_LPFBUFBC(x) (((x) & 0x3) << 11)
+#define ADC_ANALOG1_LPFBC(x) (((x) & 0x7) << 13)
+
+/* ADC1_DIGITALCTL */
+#define ADC1_DIGITALCTL_ADGC1(x) (((x) & 0xf) << 2)
+#define ADC1_DIGITALCTL_HPF1REN (0x1 << 6)
+#define ADC1_DIGITALCTL_HPF1LEN (0x1 << 7)
+#define ADC1_DIGITALCTL_HPF1DW (0x1 << 8)
+#define ADC1_DIGITALCTL_WNHPF1CUT(x) (((x) & 0x7) << 9)
+#define ADC1_DIGITALCTL_SRSEL0(x) (((x) & 0x3) << 12)
+#define ADC1_DIGITALCTL_AD1DEN (0x1 << 14)
+#define ADC1_DIGITALCTL_AD1LR (0x1 << 15)
+
+/* ADC1_CTL */
+#define ADC1_CTL_AD1REN (0x1 << 0)
+#define ADC1_CTL_AD1REN_SFT (0)
+#define ADC1_CTL_AD1LEN (0x1 << 1)
+#define ADC1_CTL_AD1LEN_SFT (1)
+#define ADC1_CTL_MIC1FDSE (0x1 << 2)
+#define ADC1_CTL_MIC1REN (0x1 << 3)
+#define ADC1_CTL_MIC1REN_SFT (3)
+#define ADC1_CTL_MIC1LEN (0x1 << 4)
+#define ADC1_CTL_MIC1LEN_SFT (4)
+#define ADC1_CTL_VRDA1EN (0x1 << 7)
+#define ADC1_CTL_VRDA0EN (0x1 << 8)
+
+/* AGC1_CTL0 */
+#define AGC1_CTL0_AMP1GR1(x) (((x) & 0x7) << 0)
+#define AGC1_CTL0_AMP1G1R(x) (((x) & 0xf) << 8)
+#define AGC1_CTL0_AMP1G1R_SFT (8)
+#define AGC1_CTL0_AMP1G1L(x) (((x) & 0xf) << 12)
+#define AGC1_CTL0_AMP1G1L_SFT (12)
+
+/* AGC1_CTL1 */
+#define AGC1_CTL1_RMSCY1(x) (((x) & 0x3) << 0)
+#define AGC1_CTL1_CMR1(x) (((x) & 0x3) << 2)
+#define AGC1_CTL1_DCYT1(x) (((x) & 0x7) << 4)
+#define AGC1_CTL1_ATKT1(x) (((x) & 0x7) << 7)
+#define AGC1_CTL1_NGT1(x) (((x) & 0x7) << 10)
+#define AGC1_CTL1_RCTMEN1 (0x1 << 15)
+
+/* AGC1_CTL2 */
+#define AGC1_CTL2_AGC1REN (0x1 << 0)
+#define AGC1_CTL2_AGC1LEN (0x1 << 1)
+#define AGC1_CTL2_GREN1 (0x1 << 2)
+#define AGC1_CTL2_ZEROC1 (0x1 << 3)
+#define AGC1_CTL2_NGTEN1 (0x1 << 4)
+#define AGC1_CTL2_NGSLEN1 (0x1 << 5)
+#define AGC1_CTL2_RMSCEN1 (0x1 << 6)
+#define AGC1_CTL2_RMSINSEL1 (0x1 << 7)
+#define AGC1_CTL2_NGTHSEL1(x) (((x) & 0x7) << 10)
+#define AGC1_CTL2_TARGL1(x) (((x) & 0x7) << 13)
+
+/* I2S_CTL */
+#define I2S_CTL_I2STEN (0x1 << 0)
+#define I2S_CTL_I2SREN (0x1 << 1)
+#define I2S_CTL_I2STOWL (0x1 << 2)
+#define I2S_CTL_I2STTDMRF (0x1 << 3)
+#define I2S_CTL_I2STXM(x) (((x) & 0x7) << 4)
+#define I2S_CTL_I2SRXM(x) (((x) & 0x3) << 8)
+#define I2S_CTL_I2SRCS (0x1 << 10)
+#define I2S_CTL_I2SPM(x) (((x) & 0x3) << 11)
+
+/* I2S_FIFOCTL */
+#define I2S_FIFOCTL_I2STFR (0x1 << 0)
+#define I2S_FIFOCTL_I2STFDEN (0x1 << 1)
+#define I2S_FIFOCTL_I2STFIEN (0x1 << 2)
+#define I2S_FIFOCTL_I2STFIP (0x1 << 3)
+#define I2S_FIFOCTL_I2STFDCF(x) (((x) & 0x7) << 4)
+#define I2S_FIFOCTL_I2STFDRF (0x1 << 7)
+#define I2S_FIFOCTL_I2STFFF (0x1 << 8)
+#define I2S_FIFOCTL_I2SRFR (0x1 << 9)
+#define I2S_FIFOCTL_I2SRFDEN (0x1 << 10)
+#define I2S_FIFOCTL_I2SRFIEN (0x1 << 11)
+#define I2S_FIFOCTL_I2SRFIP (0x1 << 12)
+#define I2S_FIFOCTL_I2SRFDCF(x) (((x) & 0x3) << 13)
+#define I2S_FIFOCTL_I2STXKA (0x1 << 15)
+#define I2S_FIFOCTL_I2SRFDRF (0x1 << 16)
+#define I2S_FIFOCTL_I2SRFEF (0x1 << 17)
+#define I2S_FIFOCTL_I2STFSS (0x1 << 18)
+#define I2S_FIFOCTL_KMCMMI2ST(x) (((x) & 0x3) << 19)
+
+/* SPDIF_HDMI_CTL */
+#define SPDIF_HDMI_CTL_SPDFR (0x1 << 0)
+#define SPDIF_HDMI_CTL_HDMIFR (0x1 << 1)
+#define SPDIF_HDMI_CTL_SPDFIP (0x1 << 2)
+#define SPDIF_HDMI_CTL_SPDFFF (0x1 << 3)
+#define SPDIF_HDMI_CTL_SPDFDEN (0x1 << 4)
+#define SPDIF_HDMI_CTL_SPDFIEN (0x1 << 5)
+#define SPDIF_HDMI_CTL_HDMFIP (0x1 << 6)
+#define SPDIF_HDMI_CTL_HDMFFF (0x1 << 7)
+#define SPDIF_HDMI_CTL_HDMFDEN (0x1 << 8)
+#define SPDIF_HDMI_CTL_HDMFIEN (0x1 << 9)
+#define SPDIF_HDMI_CTL_SPDEN (0x1 << 10)
+#define SPDIF_HDMI_CTL_SPDKA (0x1 << 11)
+#define SPDIF_HDMI_CTL_HDMKA (0x1 << 12)
+#define SPDIF_HDMI_CTL_SPDFSS (0x1 << 13)
+#define SPDIF_HDMI_CTL_HDMFSS (0x1 << 14)
+#define SPDIF_HDMI_CTL_KMCMMHDM(x) (((x) & 0x3) << 15)
+
+/* CMU_AUDIOPLL */
+#define CMU_AUDIOPLL_AUDIOPLLS (0x1 << 0)
+#define CMU_AUDIOPLL_APEN (0x1 << 4)
+#define CMU_AUDIOPLL_I2STX_CLK(x) (((x) & 0xf) << 16)
+#define CMU_AUDIOPLL_I2SRX_CLK(x) (((x) & 0xf) << 20)
+#define CMU_AUDIOPLL_HDMIA_CLK(x) (((x) & 0xf) << 24)
+#define CMU_AUDIOPLL_SPDIF_CLK(x) (((x) & 0xf) << 28)
+
+/* CMU_DEVCLKEN */
+#define CMU_DEVCLKEN0_I2STX (0x1 << 20)
+#define CMU_DEVCLKEN0_I2SRX (0x1 << 21)
+#define CMU_DEVCLKEN0_HDMIA (0x1 << 22)
+#define CMU_DEVCLKEN0_SPDIF (0x1 << 23)
+
+#define CMU_DEVRST0_AUDIO (0x1 << 17)
+
+#define GL5302_DEVRST_AUDIO_CLK_EN (0x1 << 10)
+#define GL5302_DEVRST_AUDIO_RST (0x1 << 4)
+
+#endif /* ifndef __ATV5302_AUDIO_REGS_H__ */
diff --git a/sound/soc/atc260x/atc2603c-codec/atc2603c-audio-codec.c b/sound/soc/atc260x/atc2603c-codec/atc2603c-audio-codec.c
new file mode 100755
index 0000000..e5f2a34
--- /dev/null
+++ b/sound/soc/atc260x/atc2603c-codec/atc2603c-audio-codec.c
@@ -0,0 +1,2189 @@
+#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/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 <linux/clk.h> /* clk_enable */
+#include "../sndrv-owl.h"
+#include "atc2603c-audio-regs.h"
+#include "../common-regs-owl.h"
+#include <mach/clkname.h>
+#include <mach/module-owl.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+
+#include <linux/io.h>
+#include <linux/ioport.h>
+
+#include <linux/suspend.h>
+#include <linux/earlysuspend.h>
+
+#include <linux/mfd/atc260x/atc260x.h>
+#include <linux/interrupt.h>
+#include <mach/switch.h>
+
+
+static int direct_drive_disable;
+
+static int adc_detect_mode; //0:earphone irq or gpio detect, 1: earphone adc detect
+
+static const char *audio_device_node = "actions,atc2603c-audio";
+static const char *snd_earphone_output_mode = "earphone_output_mode";
+static const char *snd_mic_num = "mic_num";
+static const char *snd_mic0_gain = "mic0_gain";
+static const char *snd_speaker_gain = "speaker_gain";
+static const char *snd_earphone_gain = "earphone_gain";
+static const char *speaker_ctrl_name = "speaker_en_gpios";
+static const char *earphone_detect_gpio = "earphone_detect_gpios";
+//20141202 new_code by yuchen: add new item in dts file to config mic mode differential or single end
+static const char *snd_mic_mode = "mic_mode";
+static const char *snd_earphone_detect_method = "earphone_detect_method";
+static const char *snd_adc_plugin_threshold = "adc_plugin_threshold";
+static const char *snd_adc_level = "adc_level";
+
+
+static int speaker_gpio_num;
+static int earphone_gpio_num;
+
+static enum of_gpio_flags speaker_gpio_level;
+static int speaker_gpio_active;
+
+static audio_hw_cfg_t audio_hw_cfg;
+
+static int atc2603c_open_count;
+static unsigned int user_lock = 1;
+static volatile unsigned int hw_init_flag = false;
+static DEFINE_MUTEX(atc2603c_pa_down_lock);
+
+//20141013 yuchen: to check pmu ic type
+static int atc2603c_ictype = PMU_NOT_USED;
+static int earphone_irq = -1;
+
+static int earphone_poll_ms = 50;
+
+
+struct reg_val {
+ int reg;
+ unsigned short val;
+ short delay; /* ms */
+};
+
+/*
+typedef struct ear_detect_dev {
+ struct cdev chrdev;
+ int dev_idx;
+ int irq;
+} ear_detect_dev_t;
+*/
+
+struct atc2603c_priv_data {
+ int mode;
+};
+static struct atc260x_dev *atc260x;
+struct snd_soc_codec *atc2603c_codec;
+
+static struct switch_dev headphone_sdev;
+
+#define ATC2603C_AIF 0
+#define REG_BASE ATC2603C_AUDIO_OUT_BASE
+
+#define AUDIOINOUT_CTL (0x0)
+#define AUDIO_DEBUGOUTCTL (0x1)
+#define DAC_DIGITALCTL (0x2)
+#define DAC_VOLUMECTL0 (0x3)
+#define DAC_ANALOG0 (0x4)
+#define DAC_ANALOG1 (0x5)
+#define DAC_ANALOG2 (0x6)
+#define DAC_ANALOG3 (0x7)
+
+//--------------Bits Location------------------------------------------//
+//--------------AUDIO_IN-------------------------------------------//
+
+
+//--------------Register Address---------------------------------------//
+
+#define ADC_DIGITALCTL (0x8)
+#define ADC_HPFCTL (0x9)
+#define ADC_CTL (0xa)
+#define AGC_CTL0 (0xb)
+#define AGC_CTL1 (0xc)
+#define AGC_CTL2 (0xd)
+#define ADC_ANALOG0 (0xe)
+#define ADC_ANALOG1 (0xf)
+
+/*
+#define ATC2603C_CMU_DEVRST (ATC2603C_CMU_CONTROL_BASE+0x01) //0xc1
+#define ATC2603C_PAD_EN (ATC2603C_MFP_BASE+0x6) //0xd6
+#define ATC2603C_MFP_CTL (ATC2603C_MFP_BASE+0x00) //0xd0
+#define ATC2603C_PMU_BDG_CTL (ATC2603C_PMU_BASE+0x51) //0x51
+*/
+
+static const u16 atc2603c_reg[ADC_ANALOG1+1] = {
+ [AUDIOINOUT_CTL] = 0x00,
+ [AUDIO_DEBUGOUTCTL] = 0x00,
+ [DAC_DIGITALCTL] = 0x03,
+ [DAC_VOLUMECTL0] = 0x00,
+ [DAC_ANALOG0] = 0x00,
+ [DAC_ANALOG1] = 0x00,
+ [DAC_ANALOG2] = 0x00,
+ [DAC_ANALOG3] = 0x00,
+ [ADC_DIGITALCTL] = 0x00,
+ [ADC_HPFCTL] = 0x00,
+ [ADC_CTL] = 0x00,
+ [AGC_CTL0] = 0x00,
+ [AGC_CTL1] = 0x00,
+ [AGC_CTL2] = 0x00,
+ [ADC_ANALOG0] = 0x00,
+ [ADC_ANALOG1] = 0x00,
+};
+
+/*
+struct reg_val atc2603c_pa_up_list[] = {
+ {DAC_VOLUMECTL0, 0xbebe, 0},
+ {DAC_ANALOG0, 0x00, 0},
+ {DAC_ANALOG1, 0x00, 0},
+ {DAC_ANALOG2, 0x00, 0},
+ {DAC_ANALOG3, 0x00, 0},
+ {DAC_ANALOG4, 0x00, 0},
+ {AUDIOINOUT_CTL, 0x02, 0},
+ {DAC_DIGITALCTL, 0x03, 0},
+ {DAC_ANALOG4, 0x08c0, 0},
+ {DAC_ANALOG0, 0x26b3, 0},
+ {DAC_ANALOG3, 0x8b0b, 0},
+ {DAC_ANALOG2, 0x07, 0},
+ {DAC_ANALOG3, 0x8b0f, 100},
+ {DAC_ANALOG2, 0x0f, 0},
+ {DAC_ANALOG4, 0x88c0, 0},
+ {DAC_ANALOG2, 0x1f, 600},
+ {DAC_ANALOG2, 0x17, 0},
+};
+
+struct reg_val atc2603c_pa_down_list[] = {
+ {DAC_VOLUMECTL0, 0xbebe, 0},
+ {DAC_ANALOG4, 0x08c0, 0},
+ {DAC_ANALOG2, 0x1f, 0},
+ {DAC_ANALOG2, 0x0f, 0},
+};
+*/
+
+struct asoc_codec_resource {
+ void __iomem *base[MAX_RES_NUM];/*virtual base for every resource*/
+ void __iomem *baseptr; /*pointer to every virtual base*/
+ struct clk *clk;
+ int irq;
+ unsigned int setting;
+};
+
+//codec resources
+//static struct asoc_codec_resource codec_res;
+
+static int earphone_is_in_for_irq(void);
+static irqreturn_t earphone_detect_irq_handler(int irq, void *data);
+
+static struct delayed_work dwork_adc_detect;
+
+
+/*
+static void set_dai_reg_base(int num)
+{
+ codec_res.baseptr = codec_res.base[num];
+}
+
+static u32 snd_dai_readl(u32 reg)
+{
+ return readl(codec_res.baseptr + reg);
+}
+
+static void snd_dai_writel(u32 val, u32 reg)
+{
+ writel(val, codec_res.baseptr + reg);
+}
+*/
+
+/*
+static void snd_codec_writel_debug(unsigned int val, unsigned int reg)
+{
+ snd_dai_writel(val, reg);
+ snd_dbg("%s: reg[0x%x]=[0x%x][0x%x]\n"
+ , __func__, reg, val, snd_dai_readl(reg));
+}
+*/
+
+static int atc2603c_write_pmu(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct atc260x_dev *atc260x = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = atc260x_reg_write(atc260x, reg, value);
+ if(ret < 0)
+ {
+ snd_err("atc2603c_write: reg = %#X, ret = %d \n", reg, ret);
+ }
+
+// snd_dbg("%s: reg[0x%x]=[0x%x][0x%x]\n"
+// , __func__, reg, value, atc260x_reg_read(atc260x, reg));
+
+ return ret;
+}
+static int atc2603c_read_pmu(struct snd_soc_codec *codec, unsigned int reg)
+{
+ struct atc260x_dev *atc260x = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = atc260x_reg_read(atc260x, reg);
+ if(ret < 0)
+ {
+ snd_err("atc2603c_read: reg = %#X, ret = %d \n", reg, ret);
+ }
+
+ return ret;
+}
+
+
+static int snd_soc_update_bits_pmu(struct snd_soc_codec *codec, unsigned short reg,
+ unsigned int mask, unsigned int value)
+{
+ bool change;
+ unsigned int old, new;
+ int ret;
+
+ {
+ ret = atc2603c_read_pmu(codec, reg);
+ if (ret < 0)
+ return ret;
+
+ old = ret;
+ new = (old & ~mask) | (value & mask);
+ change = old != new;
+ if (change)
+ ret = atc2603c_write_pmu(codec, reg, new);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return change;
+
+}
+
+
+static void ramp_undirect(unsigned int begv, unsigned int endv) {
+ unsigned int val = 0;
+ int count = 0;
+
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ while (endv < begv) {
+ count++;
+ if ((count & 0x7F) == 0) {
+ mdelay(1);
+ }
+ val = snd_dai_readl(I2S_FIFOCTL);
+ while ((val & (0x1 << 8)) != 0) {
+ val = snd_dai_readl(I2S_FIFOCTL);
+ };
+ snd_dai_writel(endv, I2STX_DAT);
+ endv -= 0x36000;
+ }
+ while (begv <= endv) {
+ count++;
+ if ((count & 0x7F) == 0) {
+ mdelay(1);
+ }
+ val = snd_dai_readl(I2S_FIFOCTL);
+ while ((val & (0x1 << 8)) != 0) {
+ val = snd_dai_readl(I2S_FIFOCTL);
+ };
+ snd_dai_writel(endv, I2STX_DAT);
+ endv -= 0x36000;
+ }
+}
+
+static int atc2603c_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ int ret = 0;
+
+ struct atc260x_dev *atc260x = snd_soc_codec_get_drvdata(codec);
+/*
+ if (!snd_soc_codec_volatile_register(codec, reg) &&
+ (reg) < codec->driver->reg_cache_size &&
+ !codec->cache_bypass) {
+ ret = snd_soc_cache_write(codec, reg, value);
+ snd_dbg("%s: reg[0x%x]=[0x%x]\r\n", __func__, reg, value);
+ if (ret < 0)
+ return -1;
+ }
+
+ if (codec->cache_only) {
+ codec->cache_sync = 1;
+ return 0;
+ }
+*/
+
+ ret = atc260x_reg_write(atc260x, reg + REG_BASE, value);
+ //snd_err("%s: reg[0x%x]=[0x%x]\r\n", __FUNCTION__, reg + REG_BASE, value);
+ if (ret < 0)
+ snd_err("atc2603c_write: reg = %#X, ret = %d failed\n",
+ reg, ret);
+
+// snd_dbg("%s: reg[0x%x]=[0x%x][0x%x]\n"
+// , __func__, reg + REG_BASE, value, atc260x_reg_read(atc260x, reg + REG_BASE));
+
+ return ret;
+}
+
+static unsigned int atc2603c_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ int ret = 0;
+/*
+ if (!snd_soc_codec_volatile_register(codec, reg) &&
+ (reg) < codec->driver->reg_cache_size &&
+ !codec->cache_bypass) {
+ ret = snd_soc_cache_read(codec, reg, &val);
+ snd_dbg("%s: reg[0x%x]\r\n", __func__, reg);
+ if (ret < 0) {
+ snd_err("atc2603c_read: reg=%#X,ret=%d\n",
+ reg, ret);
+ return ret;
+ }
+ return val;
+ }
+*/
+ ret = atc260x_reg_read(atc260x, reg + REG_BASE);
+// snd_dbg("%s: reg[0x%x]\r\n", __func__, reg + REG_BASE);
+
+ if (ret < 0)
+ snd_err("atc2603c_read: reg = %#X, ret = %d failed\n", reg, ret);
+
+ return ret;
+}
+
+#define ADC0_GAIN_MAX 25
+
+static int atc2603c_adc_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+ unsigned int val;
+ //snd_soc_cache_sync(codec);
+ val = snd_soc_read(codec, mc->reg);
+ ucontrol->value.integer.value[0] =
+ ((val & AGC_CTL0_AMP1GL_MSK) >> mc->shift);
+ ucontrol->value.integer.value[1] =
+ (val & AGC_CTL0_AMP1GR_MSK) >> mc->rshift;
+
+ return 0;
+}
+
+static int atc2603c_adc_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+ unsigned int val, val1, val2;
+
+ val = snd_soc_read(codec, mc->reg);
+ val1 = ucontrol->value.integer.value[0];
+ val2 = val1;
+ val = val & (~AGC_CTL0_AMP1GL_MSK);
+ val = val & (~AGC_CTL0_AMP1GR_MSK);
+ val = val | (val1<< mc->shift);
+ val = val | (val2 << mc->rshift);
+
+ snd_soc_component_update_bits(component, mc->reg,
+ AGC_CTL0_AMP1GL_MSK | AGC_CTL0_AMP1GR_MSK,
+ val);
+
+ return 0;
+}
+
+static int atc2603c_mic_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.mic0_gain[0];
+ ucontrol->value.integer.value[1] =
+ audio_hw_cfg.mic0_gain[1];
+
+ return 0;
+}
+
+static int atc2603c_earphone_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.earphone_gain[0];
+ ucontrol->value.integer.value[1] =
+ audio_hw_cfg.earphone_gain[1];
+
+ return 0;
+}
+
+static int atc2603c_speaker_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.speaker_gain[0];
+ ucontrol->value.integer.value[1] =
+ audio_hw_cfg.speaker_gain[1];
+
+ return 0;
+}
+
+static int atc2603c_speaker_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.speaker_volume;
+
+ return 0;
+}
+
+static int atc2603c_earphone_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.earphone_volume;
+
+ return 0;
+}
+
+static int atc2603c_mic_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.mic_num;
+
+ return 0;
+}
+
+//20141202 by yuchen: new_code, get mic mode config from dts, 1 for differential, 2 for single end
+static int atc2603c_mic_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ ucontrol->value.integer.value[0] =
+ audio_hw_cfg.mic_mode;
+
+ return 0;
+}
+
+static int atc2603c_earphone_detect_method_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if(earphone_gpio_num < 0)
+ {
+ //no gpio, we use irq
+ ucontrol->value.integer.value[0] = 0;
+ }
+ else
+ {
+ //with gpio, we use gpio
+ ucontrol->value.integer.value[0] = 1;
+ }
+ return 0;
+}
+
+
+
+const char *atc2603c_mic0_mode[] = {
+ "Differential", "Single ended"};
+
+static const SOC_ENUM_SINGLE_DECL(
+ atc2603c_mic0_mode_enum, ADC_CTL,
+ ADC_CTL_MIC0FDSE_SFT, atc2603c_mic0_mode);
+
+static const char *pa_output_swing[] = {
+ "Vpp2.4", "Vpp1.6"};
+
+static const SOC_ENUM_SINGLE_DECL(
+ pa_output_swing_enum, DAC_ANALOG1,
+ DAC_ANALOG1_PASW_SFT, pa_output_swing);
+
+const struct snd_kcontrol_new atc2603c_snd_controls[] = {
+
+ SOC_DOUBLE_EXT_TLV("Dummy mic Gain",
+ 0, 0, 1, 0xf, 0,
+ atc2603c_mic_gain_get,
+ NULL,
+ NULL),
+
+ SOC_DOUBLE_EXT_TLV("Dummy earphone gain",
+ 0, 0, 1, 0xff, 0,
+ atc2603c_earphone_gain_get,
+ NULL,
+ NULL),
+
+ SOC_DOUBLE_EXT_TLV("Dummy speaker gain",
+ 0, 0, 1, 0xff, 0,
+ atc2603c_speaker_gain_get,
+ NULL,
+ NULL),
+
+ SOC_SINGLE_EXT_TLV("Dummy speaker volume",
+ 0, 0, 0x28, 0,
+ atc2603c_speaker_volume_get,
+ NULL,
+ NULL),
+
+ SOC_SINGLE_EXT_TLV("Dummy earphone volume",
+ 0, 0, 0x28, 0,
+ atc2603c_earphone_volume_get,
+ NULL,
+ NULL),
+
+ SOC_SINGLE_EXT_TLV("Dummy mic num",
+ 0, 0, 0x2, 0,
+ atc2603c_mic_num_get,
+ NULL,
+ NULL),
+
+ SOC_DOUBLE_EXT_TLV("Adc0 Gain",
+ AGC_CTL0,
+ AGC_CTL0_AMP1G0L_SFT,
+ AGC_CTL0_AMP1G0R_SFT,
+ 0xf,
+ 0,
+ atc2603c_adc_gain_get,
+ atc2603c_adc_gain_put,
+ NULL),
+
+ SOC_SINGLE_TLV("AMP1 Gain boost Range select",
+ AGC_CTL0,
+ AGC_CTL0_AMP0GR1_SET,
+ 0x7,
+ 0,
+ NULL),
+
+ SOC_SINGLE_TLV("ADC0 Digital Gain control",
+ ADC_DIGITALCTL,
+ ADC_DIGITALCTL_ADGC0_SFT,
+ 0xF,
+ 0,
+ NULL),
+
+ SOC_ENUM("Mic0 Mode Mux", atc2603c_mic0_mode_enum),
+
+ SOC_SINGLE_EXT_TLV("Dummy mic mode",
+ 0, 0, 1, 0,
+ atc2603c_mic_mode_get,
+ NULL,
+ NULL),
+
+ SOC_SINGLE_EXT_TLV("Dummy earphone detect method",
+ 0, 0, 1, 0,
+ atc2603c_earphone_detect_method_get,
+ NULL,
+ NULL),
+
+
+
+ SOC_ENUM("PA Output Swing Mux", pa_output_swing_enum),
+
+ SOC_SINGLE_TLV("DAC PA Volume",
+ DAC_ANALOG1,
+ DAC_ANALOG1_VOLUME_SFT,
+ 0x28,
+ 0,
+ NULL),
+
+ SOC_SINGLE("DAC FL FR PLAYBACK Switch",
+ DAC_ANALOG1,
+ DAC_ANALOG1_DACFL_FRMUTE_SFT,
+ 1,
+ 0),
+
+ SOC_SINGLE_TLV("DAC FL Gain",
+ DAC_VOLUMECTL0,
+ DAC_VOLUMECTL0_DACFL_VOLUME_SFT,
+ 0xFF,
+ 0,
+ NULL),
+
+ SOC_SINGLE_TLV("DAC FR Gain",
+ DAC_VOLUMECTL0,
+ DAC_VOLUMECTL0_DACFR_VOLUME_SFT,
+ 0xFF,
+ 0,
+ NULL),
+
+ SOC_SINGLE("DAC PA Switch",
+ DAC_ANALOG3,
+ DAC_ANALOG3_PAEN_FR_FL_SFT,
+ 1,
+ 0),
+
+ SOC_SINGLE("DAC PA OUTPUT Stage Switch",
+ DAC_ANALOG3,
+ DAC_ANALOG3_PAOSEN_FR_FL_SFT,
+ 1,
+ 0),
+
+ SOC_DOUBLE("DAC Digital FL FR Switch",
+ DAC_DIGITALCTL,
+ DAC_DIGITALCTL_DEFL_SFT,
+ DAC_DIGITALCTL_DEFR_SFT,
+ 1,
+ 0),
+
+ SOC_SINGLE("Internal Mic Power Switch",
+ AGC_CTL0,
+ AGC_CTL0_VMICINEN_SFT,
+ 1,
+ 0),
+
+ SOC_SINGLE("External Mic Power Switch",
+ AGC_CTL0,
+ AGC_CTL0_VMICEXEN_SFT,
+ 1,
+ 0),
+
+ SOC_SINGLE_TLV("External MIC Power Voltage",
+ AGC_CTL0,
+ AGC_CTL0_VMICEXST_SFT,
+ 0x3,
+ 0,
+ NULL),
+
+ SOC_SINGLE_TLV("Adc0 Digital Gain",
+ ADC_DIGITALCTL,
+ ADC_DIGITALCTL_ADGC0_SFT,
+ 0xf, 0, NULL),
+
+};
+#ifdef CONFIG_SND_UBUNTU
+static int atc2603c_playback_set_controls(struct snd_soc_codec *codec)
+{
+ snd_soc_update_bits(codec, DAC_ANALOG1, 0x3f << DAC_ANALOG1_VOLUME_SFT, 0x28 << DAC_ANALOG1_VOLUME_SFT);
+ snd_soc_update_bits(codec, DAC_VOLUMECTL0, 0xff << DAC_VOLUMECTL0_DACFL_VOLUME_SFT, 0xb5 << DAC_VOLUMECTL0_DACFL_VOLUME_SFT);
+ snd_soc_update_bits(codec, DAC_VOLUMECTL0, 0xff << DAC_VOLUMECTL0_DACFR_VOLUME_SFT, 0xb5 << DAC_VOLUMECTL0_DACFR_VOLUME_SFT);
+ snd_soc_update_bits(codec, DAC_ANALOG3, 0x1 << DAC_ANALOG3_PAEN_FR_FL_SFT, 0x01 << DAC_ANALOG3_PAEN_FR_FL_SFT);
+ snd_soc_update_bits(codec, DAC_ANALOG3, 0x1 << DAC_ANALOG3_PAOSEN_FR_FL_SFT, 0x01 << DAC_ANALOG3_PAOSEN_FR_FL_SFT);
+ snd_soc_update_bits(codec, DAC_DIGITALCTL, 0x03 << DAC_DIGITALCTL_DEFL_SFT, 0x03 << DAC_DIGITALCTL_DEFL_SFT);
+ snd_soc_update_bits(codec, DAC_ANALOG1, 0x01 << DAC_ANALOG1_PASW_SFT, 0x01 << DAC_ANALOG1_PASW_SFT);
+ snd_soc_update_bits(codec, DAC_ANALOG3, 0x03 << 0, 0x03);
+
+ return 0;
+}
+
+static int atc2603c_capture_set_controls(struct snd_soc_codec *codec)
+{
+ snd_soc_update_bits(codec, DAC_ANALOG1, 0x1 << DAC_ANALOG1_DACMICMUTE_SFT, 0x0 << DAC_ANALOG1_DACMICMUTE_SFT);
+ snd_soc_update_bits(codec, DAC_ANALOG1, 0x1 << DAC_ANALOG1_DACFMMUTE_SFT, 0x0 << DAC_ANALOG1_DACFMMUTE_SFT);
+ snd_soc_update_bits(codec, ADC_CTL, 0x3 << 6, 0x3 << 6);
+ snd_soc_update_bits(codec, ADC_CTL, 0x1f << 0, 0x12 << 0);
+
+ snd_soc_update_bits(codec, AGC_CTL0, 0xf << AGC_CTL0_AMP1G0L_SFT, 0x7 << AGC_CTL0_AMP1G0L_SFT);
+ snd_soc_update_bits(codec, AGC_CTL0, 0xf << AGC_CTL0_AMP1G0R_SFT, 0x7 << AGC_CTL0_AMP1G0R_SFT);
+ snd_soc_update_bits(codec, ADC_DIGITALCTL, 0xF << ADC_DIGITALCTL_ADGC0_SFT, 0x3 << ADC_DIGITALCTL_ADGC0_SFT);
+ snd_soc_update_bits(codec, AGC_CTL0, 0x7 << AGC_CTL0_AMP0GR1_SET, 0x07 << AGC_CTL0_AMP0GR1_SET);
+ snd_soc_update_bits(codec, AGC_CTL0, 0x1 << AGC_CTL0_VMICINEN_SFT, 0x1 << AGC_CTL0_VMICINEN_SFT);
+ snd_soc_update_bits(codec, AGC_CTL0, 0x1 << AGC_CTL0_VMICEXEN_SFT, 0x1 << AGC_CTL0_VMICEXEN_SFT);
+ snd_soc_update_bits(codec, AGC_CTL0, 0x3 << AGC_CTL0_VMICEXST_SFT, 0x1 << AGC_CTL0_VMICEXST_SFT);
+
+ if(audio_hw_cfg.mic_mode == 1){ //if differential.
+ snd_soc_update_bits(codec, ADC_CTL, 0x1 << ADC_CTL_MIC0FDSE_SFT, 0x0 << ADC_CTL_MIC0FDSE_SFT);
+ //FIXME no fd on 705a for now
+ snd_soc_update_bits_pmu(codec, ATC2603C_MFP_CTL,
+ 0x3<<9 , 0x2<<9);//MICINL&MICINR
+ snd_soc_update_bits_pmu(codec, ATC2603C_MFP_CTL,
+ 0x01 , 0x01);//MICINL&MICINR
+ }else{ //if single end. mic_mode=2
+ snd_soc_update_bits(codec, ADC_CTL, 0x1 << ADC_CTL_MIC0FDSE_SFT, 0x0 << ADC_CTL_MIC0FDSE_SFT);
+ snd_soc_update_bits_pmu(codec, ATC2603C_MFP_CTL,
+ 0x03 , 0);//MICINL&MICINR
+ }
+
+ return 0;
+}
+#endif
+/*FIXME on input source selection */
+const char *atc2603c_adc0_src[] = {"None", "FM","MIC0", "FM MIC0", "PAOUT"};
+
+static const SOC_ENUM_SINGLE_DECL(
+ atc2603c_adc0_enum, ADC_CTL,
+ ADC_CTL_ATAD_MTA_FTA_SFT, atc2603c_adc0_src);
+
+const struct snd_kcontrol_new atc2603c_adc0_mux =
+SOC_DAPM_ENUM("ADC0 Source", atc2603c_adc0_enum);
+
+
+const struct snd_kcontrol_new atc2603c_dac_lr_mix[] = {
+ SOC_DAPM_SINGLE("FL FR Switch", DAC_ANALOG1,
+ DAC_ANALOG1_DACFL_FRMUTE_SFT, 1, 0),
+ SOC_DAPM_SINGLE("MIC Switch", DAC_ANALOG1,
+ DAC_ANALOG1_DACMICMUTE_SFT, 1, 0),
+ SOC_DAPM_SINGLE("FM Switch", DAC_ANALOG1,
+ DAC_ANALOG1_DACFMMUTE_SFT, 1, 0),
+};
+
+static int atc2603c_mic0_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = snd_soc_read(codec, ADC_CTL);
+ /* single end or full differential */
+ if (val & ADC_CTL_MIC0FDSE) {//if single end
+ snd_soc_update_bits_pmu(codec, ATC2603C_MFP_CTL,
+ 0x03 , 0);//MICINL&MICINR
+ }
+ else
+ {
+ //FIXME no fd on 705a for now
+ snd_soc_update_bits_pmu(codec, ATC2603C_MFP_CTL,
+ 0x3<<9 , 0x2<<9);//MICINL&MICINR
+ snd_soc_update_bits_pmu(codec, ATC2603C_MFP_CTL,
+ 0x01 , 0x01);//MICINL&MICINR
+
+ }
+ //snd_soc_update_bits(codec, ADC1_CTL,
+ // 0x3 << 7, 0x3 << 7);//(VRDN output0 enable) | (VRDN output1 enable)
+ snd_soc_update_bits(codec, ADC_HPFCTL,
+ 0x3, 0x3);//(High Pass filter0 L enable) | (High Pass filter0 R enable)
+
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static void i2s_clk_disable(void)
+{
+}
+
+static int i2s_clk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ i2s_clk_disable();
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget atc2603c_dapm_widgets[] = {
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MICIN0LP"),
+ SND_SOC_DAPM_INPUT("MICIN0LN"),
+ SND_SOC_DAPM_INPUT("MICIN0RP"),
+ SND_SOC_DAPM_INPUT("MICIN0RN"),
+ SND_SOC_DAPM_INPUT("FMINL"),
+ SND_SOC_DAPM_INPUT("FMINR"),
+
+ SND_SOC_DAPM_SUPPLY("I2S_CLK", SND_SOC_NOPM,
+ 0, 0, i2s_clk_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+// SND_SOC_DAPM_PGA("MICIN0L", ADC_CTL,
+// ADC_CTL_MIC0LEN_SFT, 0, NULL, 0),
+// SND_SOC_DAPM_PGA("MICIN0R", ADC_CTL,
+// ADC_CTL_MIC0REN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("FM L", ADC_CTL,
+ ADC_CTL_FMLEN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("FM R", ADC_CTL,
+ ADC_CTL_FMREN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIC("MICIN0", atc2603c_mic0_event),
+ SND_SOC_DAPM_MIC("FM", NULL),
+ /* ADC0 MUX */
+ SND_SOC_DAPM_MUX("ADC0 Mux", SND_SOC_NOPM, 0, 0,
+ &atc2603c_adc0_mux),
+ /* ADCS */
+ SND_SOC_DAPM_ADC("ADC0 L", NULL, ADC_CTL,
+ ADC_CTL_AD0LEN_SFT, 0),
+ SND_SOC_DAPM_ADC("ADC0 R", NULL, ADC_CTL,
+ ADC_CTL_AD0REN_SFT, 0),
+ /* DAC Mixer */
+ SND_SOC_DAPM_MIXER("AOUT FL FR Mixer",
+ SND_SOC_NOPM, 0, 0,
+ atc2603c_dac_lr_mix,
+ ARRAY_SIZE(atc2603c_dac_lr_mix)),
+ SND_SOC_DAPM_DAC("DAC FL", NULL, DAC_ANALOG3,
+ DAC_ANALOG3_DACEN_FL_SFT, 0),
+ SND_SOC_DAPM_DAC("DAC FR", NULL, DAC_ANALOG3,
+ DAC_ANALOG3_DACEN_FR_SFT, 0),
+
+ SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "AIF Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ /* output lines */
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("SP"),
+};
+
+static const struct snd_soc_dapm_route atc2603c_dapm_routes[] = {
+// {"MICIN0L", NULL, "MICIN0LP"},
+// {"MICIN0L", NULL, "MICIN0LN"},
+// {"MICIN0R", NULL, "MICIN0RP"},
+// {"MICIN0R", NULL, "MICIN0RN"},
+ {"FM L", NULL, "FMINL"},
+ {"FM R", NULL, "FMINR"},
+// {"MICIN0", NULL, "MICIN0L"},
+// {"MICIN0", NULL, "MICIN0R"},
+ {"FM", NULL, "FM L"},
+ {"FM", NULL, "FM R"},
+ {"ADC0 Mux", "MIC0", "MICIN0"},
+ {"ADC0 Mux", "FM", "FM"},
+ {"ADC0 Mux", "AOUT MIXER", "AOUT FL FR Mixer"},
+ {"ADC0 L", NULL, "ADC0 Mux"},
+ {"ADC0 R", NULL, "ADC0 Mux"},
+ {"AIFTX", NULL, "ADC0 L"},
+ {"AIFTX", NULL, "ADC0 R"},
+ {"AOUT FL FR Mixer", "FL FR Switch", "AIFRX"},
+ {"AOUT FL FR Mixer", "MIC Switch", "MICIN0"},
+ {"AOUT FL FR Mixer", "FM Switch", "FM"},
+ {"DAC FL", NULL, "AOUT FL FR Mixer"},
+ {"DAC FR", NULL, "AOUT FL FR Mixer"},
+ {"HP", NULL, "DAC FL"},
+ {"HP", NULL, "DAC FR"},
+ {"SP", NULL, "DAC FL"},
+ {"SP", NULL, "DAC FR"},
+};
+
+static void pa_up(struct snd_soc_codec *codec) {
+ /* DAC\B3\E4\B5\E7\B5ķ\BD\B7\A8:\B7\C7ֱ\C7\FD
+ a) \CB\F9\D3\D0audio analog\BCĴ\E6\C6\F7\B6\BCд0\A3\AC\D6\F7\BF\D8TX\BA\CDTXFIFO\B6\BCʹ\C4ܡ\A3
+ b) \CF\F2\D6\F7\BF\D8\C0\EF\C3\E6д\D7\EEСֵ\A3\A80x80000000\A3\A9
+ c) \D6\F7\BFغ\CDatc2603cѡ\D4\F1N wireģʽ\A3\AC2.0 channelģʽ
+ d) atc2603c\B5\C4DAC_D&A\B6\BCʹ\C4\DC
+ e) PA BIAS EN \A3\ACPA EN\A3\ACLOOP2 EN\A3\ACatc2603c_DAC_ANALOG1дȫ0
+ f) Delay10ms
+ g) DAC \BF\AAʼ\B7\C5ramp\CA\FD\BEݣ\A8\B4\D30X80000000\B5\BD0x7fffffff\A3\ACÿ\B8\F40x36000дһ\B4\CE
+ \A3\ACʹ\D3\C3mips\B2\E9ѯд5201\B5\C4I2S_TX FIFO\A3\A9,ͬʱ\BF\AA\C6\F4ramp connect
+ h) \B5ȴ\FDԼ500 ms
+ i) \BF\AA\C6\F4pa\CA\E4\B3\F6\BC\B6en\A3\AC\B6Ͽ\AAramp connect\A3\ACLOOP2 Disable\A1\A3
+ */
+
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ /* i2stx fifo en */
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) | 0x3, I2S_FIFOCTL);
+ /* i2s tx en */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | 0x3, I2S_CTL);
+ snd_soc_write(codec, DAC_VOLUMECTL0, 0xbebe);
+ snd_soc_write(codec, DAC_ANALOG0, 0);
+ snd_soc_write(codec, DAC_ANALOG1, 0);
+ snd_soc_write(codec, DAC_ANALOG2, 0);
+ snd_soc_write(codec, DAC_ANALOG3, 0);
+
+ if (direct_drive_disable == 0) {
+ snd_dai_writel(0x1 << 31, I2STX_DAT);
+ snd_dai_writel(0x1 << 31, I2STX_DAT);
+ } else {
+ snd_dai_writel(0x7ffffe00, I2STX_DAT);
+ snd_dai_writel(0x7ffffe00, I2STX_DAT);
+ }
+
+ /* 2.0-Channel Mode */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x7 << 4), I2S_CTL);
+
+ if (direct_drive_disable != 0)
+ {
+ /* I2S OUTPUT input ENABLE,SEL 4WIRE MODE,0x1a6:4 wire,tx 4.0,rx 5.1 */
+ /* I2S input en */
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x01 << 1, 0x01 << 1);
+ /* I2S output en */
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x03 << 5, 0x01 << 5);
+ /* I2S TX&RX MODE, SEL 4WIRE MODE*/
+ //snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x01 << 8, 0x01 << 8);
+ /* dac_2ch FL&FR enable */
+ snd_soc_update_bits(codec, DAC_DIGITALCTL, 0x3, 0x3);
+ }
+ /* da_a EN,PA EN,all bias en */
+ snd_soc_write(codec, DAC_ANALOG3, 0x4b7);
+ /* DAC PA BIAS */
+ snd_soc_write(codec, DAC_ANALOG0, 0x26b3);
+ /* PA VOLUME=0 */
+ snd_soc_write(codec, DAC_ANALOG1, 0x0000);
+ /* */
+ snd_soc_write(codec, DAC_ANALOG2, 0x03);
+
+ if (direct_drive_disable != 0)
+ {
+ /* da_a EN,PA EN,OUTPSTAGE DIS,all bias en,loop2 en*/
+ snd_soc_write(codec, DAC_ANALOG3, 0x6b7);
+ }
+ else
+ {
+ /*PA EN,OUTPSTAGE en,all bias en,loop2en*/
+ snd_soc_write(codec, DAC_ANALOG3, 0x6bc);
+ }
+
+ snd_dai_writel(((snd_dai_readl(I2S_CTL) & ~(0x3 << 11)) | (0x1 << 11)), I2S_CTL);
+ if (direct_drive_disable == 0) {
+ //direct mode
+ msleep(100);
+ /* antipop_VRO Resistant Connect */
+ snd_soc_write(codec, DAC_ANALOG2, 0x0b);
+ /* out stage en */
+ snd_soc_update_bits(codec, DAC_ANALOG2 , 0x1<<4, 0x1<<4);
+ /* DAC_OPVRO enable */
+ snd_soc_update_bits(codec, DAC_ANALOG2, 0x1<<3, 0);
+ //ramp_direct(0x80000000, 0x7ffffe00);
+ } else {
+ //non direct mode
+ msleep(100);
+ /* PA RAMP2 CONNECT */
+ snd_soc_update_bits(codec, DAC_ANALOG2 , 0x1<<4, 0x1<<4);
+
+ ramp_undirect(0x80000000, 0x7ffffe00);
+ }
+
+ if (direct_drive_disable != 0)
+ {
+ //non direct mode
+ msleep(400);
+ /* out stage en */
+ /*EN,PA EN,OUTPSTAGE EN,all bias en,loop2 en*/
+ snd_soc_update_bits(codec, DAC_ANALOG3, 0x1<<3, 0x1<<3);
+ msleep(100);
+
+ /*EN,PA EN,OUTPSTAGE EN,all bias en,loop2 DISABLE */
+ snd_soc_update_bits(codec, DAC_ANALOG3, 0x1<<9, 0);
+ /* ramp disconnect */
+ snd_soc_update_bits(codec, DAC_ANALOG2, 0x1<<9, 0);
+ }
+
+ snd_dai_writel(0x0, I2STX_DAT);
+ snd_dai_writel(0x0, I2STX_DAT);
+ msleep(20);
+}
+
+
+static void atc2603c_pa_down(struct snd_soc_codec *codec)
+{
+ if(hw_init_flag == true)
+ {
+ if (direct_drive_disable == 0) {
+ /* antipop_VRO Resistant Connect */
+ snd_soc_update_bits(codec, DAC_ANALOG2, 0x3<<3, 0x3<<3);
+ /* PA EN,OUTPSTAGE disen,all bias en,loop2 en */
+ snd_soc_write(codec, DAC_ANALOG2, 0x6b4);
+ /* bit3 vro antipop res connect,vro disen */
+ snd_soc_update_bits(codec, DAC_ANALOG2, 0x1<<4, 0);
+ /* bit3 vro antipop res disconnect,vro disen */
+ snd_soc_update_bits(codec, DAC_ANALOG2, 0x1<<3, 0);
+
+ } else {
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) | 0x3, I2S_FIFOCTL);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | 0x3, I2S_CTL);
+ //act_snd_writel(act_snd_readl(CO_AUDIOINOUT_CTL) | (0x1 << 1), CO_AUDIOINOUT_CTL);
+
+ snd_soc_write(codec, DAC_VOLUMECTL0, 0xbebe);
+
+ /* PA LOOP2 en */
+ snd_soc_update_bits(codec, DAC_ANALOG3, 0x1<<9, 0x1<<9);
+ /* \B8\F4ֱ\B5\E7\C8\DDDischarge \BF\AA\C6\F4 */
+ snd_soc_update_bits(codec, DAC_ANALOG2, 0x1<<8, 0x1<<8);
+
+ snd_dai_writel(0x7ffffe00, I2STX_DAT);
+ snd_dai_writel(0x7ffffe00, I2STX_DAT);
+ msleep(300);
+
+ /* ramp Connect EN */
+ snd_soc_update_bits(codec, DAC_ANALOG2, 0x1 << 9, 0x1<<9);
+ ramp_undirect(0x80000000, 0x7ffffe00);
+ msleep(600);
+ }
+ snd_soc_write(codec, DAC_VOLUMECTL0, 0);
+ hw_init_flag = false;
+ }
+}
+
+static void atc2603c_adckeypad_config(struct snd_soc_codec *codec)
+{
+ //external mic power enable
+ snd_soc_update_bits(codec, AGC_CTL0, 0x1 << 6, 0x1 << 6);
+ snd_soc_update_bits(codec, AGC_CTL0, 0x3 << 4, 0x3 << 4);
+ //printk("atc2603_ADC0_CTL:0x%x\n",act_snd_readl(atc2603_ADC0_CTL));
+
+ //enable auxadc2
+ snd_soc_update_bits_pmu(codec, ATC2603C_PMU_AUXADC_CTL0, 0x1 << 12, 0x1 << 12);
+
+ //GL5201_ADCKEY_INFO("open:AuxADC_CTL0 = 0x%x\n",act_snd_readl(atc2603_PMU_AuxADC_CTL0));
+}
+
+static int detect_dep = 0;
+static int old_adc_state = SPEAKER_ON;
+static int now_adc_state = SPEAKER_ON;
+static int earphone_is_in_for_adc(void)
+{
+ int adc_val = 0;
+
+ if(audio_hw_cfg.adc_level == 1)
+ {
+
+ adc_val = 1024-atc2603c_read_pmu(atc2603c_codec, ATC2603C_PMU_AUXADC2);
+ //printk("direct 0x%x\n", adc_val);
+ if(adc_val < audio_hw_cfg.adc_plugin_threshold)
+ {
+ return HEADSET_NO_MIC;
+ }
+
+ return SPEAKER_ON;
+ }
+ else
+ {
+ adc_val = atc2603c_read_pmu(atc2603c_codec, ATC2603C_PMU_AUXADC2);
+ //printk("undirect 0x%x\n", adc_val);
+ if(adc_val < audio_hw_cfg.adc_plugin_threshold)
+ {
+ return HEADSET_NO_MIC;
+ }
+
+ return SPEAKER_ON;
+ }
+
+
+}
+
+
+static void adc_poll(struct work_struct *data)
+{
+ now_adc_state = earphone_is_in_for_adc();
+
+ if(now_adc_state != old_adc_state)
+ {
+ if(detect_dep >= 4)
+ {
+ //state is stable
+ detect_dep = 0;
+ }
+ else
+ {
+ //printk("detect_dep %d\n", detect_dep);
+ detect_dep++;
+ goto POLL_EXIT;
+ }
+ }
+ else //now_state == old_state
+ {
+ if(detect_dep > 0)
+ {
+ //state is not stable yet
+ detect_dep = 0;
+ }
+
+ goto POLL_EXIT;
+ }
+
+
+ //we get here because state changed and stable
+ if (now_adc_state == HEADSET_NO_MIC) {
+ printk("sndrv: speaker off \n");
+ /* 1:headset with mic; 2:headset without mic */
+ switch_set_state(&headphone_sdev, HEADSET_NO_MIC);
+
+ }
+ else
+ {
+ printk("sndrv: speaker on state %d, old_state %d\n",now_adc_state, old_adc_state);
+ switch_set_state(&headphone_sdev, SPEAKER_ON);
+ }
+
+ old_adc_state = now_adc_state;
+
+POLL_EXIT:
+ schedule_delayed_work(&dwork_adc_detect, msecs_to_jiffies(earphone_poll_ms));
+}
+
+
+void atc2603c_pa_up_all(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ /* \B1\EAʶclassd\B5\C4״̬\CAǷ\F1\B1\BB\C9\E8\D6\C3Ϊ\B9̶\A8\B2\BB\D0\E8Ҫ\B8ı䣬ȱʡ\CA\C7TRUE */
+ static int classd_flag = 1;
+ struct clk *apll_clk;
+
+ module_clk_disable(MOD_ID_I2SRX);
+ module_clk_disable(MOD_ID_I2STX);
+ module_clk_enable(MOD_ID_I2SRX);
+ module_clk_enable(MOD_ID_I2STX);
+
+ /*for the fucked bug of motor shaking while startup,
+ because GPIOB(1) is mfp with I2S_LRCLK1*/
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ snd_dai_writel(snd_dai_readl(GPIO_BOUTEN) | 2, GPIO_BOUTEN);
+
+ if(((snd_dai_readl(I2S_CTL) & 0x3) == 0x0)) {
+ /* disable i2s tx&rx */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 0), I2S_CTL);
+
+ /* avoid sound while reset fifo */
+ snd_soc_update_bits(codec,DAC_ANALOG1, 0x1 << 10, 0);
+
+ /* reset i2s rx&&tx fifo, avoid left & right channel wrong */
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) & ~(0x3 << 9) & ~0x3, I2S_FIFOCTL);
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) | (0x3 << 9) | 0x3, I2S_FIFOCTL);
+
+ /* this should before enable rx/tx, or after suspend, data may be corrupt */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 11),I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | (0x1 << 11),I2S_CTL);
+ /* set i2s mode I2S_RX_ClkSel==1 */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | (0x1 << 10), I2S_CTL);
+
+ /* enable i2s rx/tx at the same time */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | 0x3, I2S_CTL);
+
+ apll_clk = clk_get(NULL, CLKNAME_AUDIOPLL);
+ clk_prepare(apll_clk);
+ clk_enable(apll_clk);
+
+ /* i2s rx 00: 2.0-Channel Mode */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 8), I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 4), I2S_CTL);
+ }
+
+ /* EXTIRQ pad enable, ʹ\D6ж\CF\D0ź\C5\C4\DC\CB͵\BD\D6\F7\BF\D8 */
+ snd_soc_update_bits_pmu(codec, ATC2603C_PAD_EN, 0x73, 0x73);
+ /* I2S: 2.0 Channel, SEL 4WIRE MODE */
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x122, 0);
+
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x20, 0x20);
+
+ //FIXME: it's default to differential here, how to config single end?
+ //snd_soc_update_bits_pmu(codec, ATC2603C_MFP_CTL, 0xf << 3, 0);
+ if(audio_hw_cfg.mic_mode == 1)
+ {
+ snd_soc_update_bits_pmu(codec, ATC2603C_MFP_CTL, 0x3<<9 , 0x2<<9);//MICINL&MICINR
+ snd_soc_update_bits_pmu(codec, ATC2603C_MFP_CTL, 0x01 , 0x01);//MICINL&MICINR
+ }
+ else
+ {
+ snd_soc_update_bits_pmu(codec, ATC2603C_MFP_CTL, 0x3 , 0);//MICINL&MICINR
+ }
+
+
+ /* \B4\F2\BF\AA\C4ڲ\BF\B5Ļ\F9\C2˲\A8\C6\F7,\BC\F5С\B5\D7\D4룬\CC\E1\C9\FDaudio\D0\D4\C4\DC */
+ /*set_bdg_ctl();*/
+ /* for atc2603c those burn Efuse */
+ /* bit4:0 should not be changed, otherwise VREF isn't accurate */
+ ret = snd_soc_update_bits_pmu(codec, ATC2603C_PMU_BDG_CTL, 0x01 << 6, 0x01 << 6);
+ ret = snd_soc_update_bits_pmu(codec, ATC2603C_PMU_BDG_CTL, 0x01 << 5, 0);
+
+ pa_up(codec);
+
+ //snd_soc_update_bits(codec, DAC_DIGITALCTL, 0x3<<2, 0x3<<2);
+
+ if (direct_drive_disable == 0)
+ {
+ snd_soc_update_bits(codec, DAC_ANALOG3, 0x1<<10, 0x1<<10);
+ snd_soc_update_bits(codec, DAC_ANALOG2, 0x7, 0x3);
+ snd_soc_update_bits(codec, DAC_ANALOG3, 0x7<<4, 0x3<<4);
+ }
+
+ if (classd_flag == 1)
+ {
+ // DAC&PA Bias enable
+ snd_soc_update_bits(codec, DAC_ANALOG3, 0x1 << 10, 0x1 << 10);
+ // pa zero cross enable
+ snd_soc_update_bits(codec, DAC_ANALOG2, 0x1 << 15, 0x1 << 15);
+ snd_soc_update_bits(codec, DAC_ANALOG3, 0x1 << 13, 0x1 << 13);
+ }
+
+
+ //after pa_up, the regs status should be the same as outstandby?
+ snd_soc_update_bits(codec, DAC_ANALOG1, DAC_ANALOG1_DACFL_FRMUTE, 0);//mute
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x01 << 1, 0);//I2S input disable
+ snd_soc_update_bits(codec, DAC_ANALOG1, DAC_ANALOG1_PASW, DAC_ANALOG1_PASW);//1.6v
+ snd_soc_update_bits(codec, DAC_DIGITALCTL, 0x3<<4, 0x2<<4);
+ snd_soc_update_bits(codec, DAC_ANALOG1, 0x3f, 40);//
+ snd_soc_write(codec, DAC_VOLUMECTL0, 0xb5b5);//0//0xbebe
+
+/*
+ if (direct_drive_disable == 0) {
+ }
+ else {
+ snd_soc_write(codec, DAC_ANALOG2, 0x8400);
+ snd_soc_write(codec, DAC_ANALOG3, 0xaa0b);
+ }
+*/
+
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~0x3, I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) & ~(0x3 << 9) & ~0x3, I2S_FIFOCTL);
+
+
+ //FIXME: for earphone irq detect and gpio detect compatibility
+ //we need these 2 bits always set to keep irq detect always enabled when no gpio detect available
+ //if we use dapm to control these 2 bits, irq detect cant always be enabled
+ //and if we dont use dapm and dont set these bits here, mic recording wont function
+ //in gpio detect scenario.
+ //so we always set these 2 bits here and delete related dapm configs
+ //need to check any negative results
+ snd_soc_update_bits(codec, ADC_CTL, 0x1 << 6, 0x1 << 6);
+ snd_soc_update_bits(codec, ADC_CTL, 0x1 << 7, 0x1 << 7);
+
+ //FIXME: set earphone irq detect
+ //enable pmu interupts mask for audio
+ //ret = snd_soc_update_bits_pmu(codec, ATC2603C_INTS_MSK, 0x1, 0x1);
+ //headset or earphone INOUT DECTECT IRQ enable
+ if((earphone_gpio_num < 0) && (adc_detect_mode == 0) && (earphone_irq != -1))
+ {
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x1 << 10, 0x1 << 10);
+ // External MIC Power VMIC enabled
+ //snd_soc_update_bits(codec, AGC_CTL0, 0x1 << 3, 0x1 << 3);
+ snd_soc_update_bits(codec, AGC_CTL0, 0x1 << 6, 0x1 << 6);
+ snd_soc_update_bits(codec, AGC_CTL0, 0x1 << 7, 0x1 << 7);
+
+ snd_soc_update_bits(codec, ADC_ANALOG1, 0x1 << 8, 0x1 << 8);
+ }
+// snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x1 << 2, 0);
+// snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x1 << 3, 0);
+}
+EXPORT_SYMBOL_GPL(atc2603c_pa_up_all);
+
+static int atc2603c_audio_set_dai_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute) {
+ snd_soc_update_bits(codec, DAC_ANALOG1,
+ DAC_ANALOG1_DACFL_FRMUTE, 0);
+ } else {
+ snd_soc_update_bits(codec, DAC_ANALOG1,
+ DAC_ANALOG1_DACFL_FRMUTE,
+ DAC_ANALOG1_DACFL_FRMUTE);
+ }
+
+ return 0;
+}
+static int atc2603c_audio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ /* enable the atc2603c i2s input function */
+ struct snd_soc_codec *codec = dai->codec;
+
+ //printk("atc2603c_audio_hw_params %d", hw_init_flag);
+ if(hw_init_flag == false) {
+ //printk("request IRQ %d!!!!", earphone_irq);
+
+ //printk("atc2603c_pa_up_all %d!!!!", hw_init_flag);
+ atc2603c_pa_up_all(codec);
+ hw_init_flag = true;
+
+ }
+ //snd_err("%s %d\n", __FILE__,__LINE__);
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x03 << 5, 0x01 << 5);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+/*
+ if (direct_drive_disable == 0) {
+
+ } else {
+ snd_soc_write(codec, DAC_ANALOG2, 0x8400);
+ snd_soc_write(codec, DAC_ANALOG3, 0xaa0b);
+ }
+*/
+ //snd_err("%s %d\n", __FILE__,__LINE__);
+ snd_soc_update_bits(codec,
+ AUDIOINOUT_CTL, 0x01 << 1, 0x01 << 1);
+ snd_soc_update_bits(codec,
+ DAC_ANALOG3, 0x03, 0x03);
+#ifdef CONFIG_SND_UBUNTU
+ atc2603c_playback_set_controls(codec);
+ audio_set_output_mode(substream, O_MODE_I2S);
+#endif
+ } else {
+ snd_soc_update_bits(codec,
+ ADC_HPFCTL, 0x3 << 0, 0x3 << 0);//adc0 hpf disable
+ //snd_err("%s %d\n", __FILE__,__LINE__);
+ snd_soc_update_bits(codec,
+ AUDIOINOUT_CTL, 0x01 << 8, 0x01 << 8);
+#ifdef CONFIG_SND_UBUNTU
+ atc2603c_capture_set_controls(codec);
+ if(params_channels(params) == 1){
+ snd_soc_update_bits(codec, ADC_CTL, 0x3 << 6, 0x2 << 6);
+ snd_soc_update_bits(codec, ADC_CTL, 0x3 << 3, 0x2 << 3);
+ }else{
+ snd_soc_update_bits(codec, ADC_CTL, 0x3 << 6, 0x3 << 6);
+ snd_soc_update_bits(codec, ADC_CTL, 0x3 << 3, 0x3 << 3);
+ }
+#endif
+ }
+ atc2603c_open_count++;
+
+ return 0;
+}
+
+static int atc2603c_audio_hw_free(
+ struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ snd_dbg("atc2603c_audio_hw_free\n");
+ /* disable the atc2603c i2s input function */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ //printk("SNDRV_PCM_STREAM_PLAYBACK\n");
+ snd_soc_update_bits(codec,
+ AUDIOINOUT_CTL,
+ 0x01 << 1, 0);
+ } else {
+ //printk("SNDRV_PCM_STREAM_CAPTURE\n");
+ snd_soc_update_bits(codec,
+ AUDIOINOUT_CTL,
+ 0x01 << 8, 0);
+ }
+
+ if(atc2603c_open_count > 0)
+ atc2603c_open_count--;
+ return 0;
+}
+
+static int atc2603c_audio_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ /* nothing should to do here now */
+ return 0;
+}
+
+static int atc2603c_audio_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, DAC_DIGITALCTL, 0x3<<4, 0x2<<4);
+ /* we set the i2s 2 channel-mode by default */
+ //snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x03 << 2, 0);
+
+ return 0;
+}
+
+static int atc2603c_audio_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ /* nothing should to do here now */
+ return 0;
+}
+
+static void atc2603c_power_down(struct snd_soc_codec *codec)
+{
+ atc2603c_pa_down(codec);
+}
+
+static int atc2603c_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+#if 0
+ int ret;
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_dbg("%s: SND_SOC_BIAS_ON\n", __func__);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ snd_dbg("%s: SND_SOC_BIAS_PREPARE\n", __func__);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ #ifdef SND_SOC_BIAS_DEBUG
+ snd_dbg("%s: SND_SOC_BIAS_STANDBY\n", __func__);
+ #endif
+
+ if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+ codec->cache_only = false;
+ codec->cache_sync = 1;
+ //ret = snd_soc_cache_sync(codec);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_dbg("%s: SND_SOC_BIAS_OFF\n", __func__);
+ break;
+
+ default:
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+#endif
+ return 0;
+}
+
+static void reenable_audio_block(struct snd_soc_codec *codec)
+{
+ snd_soc_update_bits_pmu(codec,
+ ATC2603C_CMU_DEVRST, 0x1 << 4, 0);//audio block reset
+
+ snd_soc_update_bits_pmu(codec, ATC2603C_CMU_DEVRST,
+ 0x1 << 10, 0x1 << 10);//SCLK to Audio Clock Enable Control
+ snd_soc_update_bits_pmu(codec,
+ ATC2603C_CMU_DEVRST, 0x1 << 4, 0x1 << 4);
+
+}
+
+static int atc2603c_probe(struct snd_soc_codec *codec)
+{
+ snd_dbg("atc2603c_probe!\n");
+ if (codec == NULL)
+ snd_dbg("NULL codec \r\n");
+
+ snd_dbg("codec->name = %s\r\n", codec->component.name);
+
+ atc2603c_codec = codec;
+
+ hw_init_flag = false;
+ reenable_audio_block(codec);
+
+ atc2603c_pa_up_all(codec);
+ hw_init_flag = true;
+
+ if((earphone_gpio_num < 0) && (adc_detect_mode == 0) && (earphone_irq != -1))
+ {
+ int ret = devm_request_threaded_irq(codec->dev, earphone_irq, NULL,
+ earphone_detect_irq_handler, IRQF_TRIGGER_HIGH, "atc260x-audio",
+ NULL);
+
+ if(ret < 0)
+ {
+ snd_err("NO IRQ %d\n", ret);
+ }
+ }
+
+ if(adc_detect_mode == 1)
+ {
+ //config adc detect
+ atc2603c_adckeypad_config(codec);
+
+ old_adc_state = SPEAKER_ON;
+ now_adc_state = HEADSET_NO_MIC;
+
+ INIT_DELAYED_WORK(&dwork_adc_detect, adc_poll);
+ schedule_delayed_work(&dwork_adc_detect, msecs_to_jiffies(500));
+ }
+
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x1 << 2, 0);
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x1 << 3, 0);
+
+ return 0;
+}
+
+static int atc2603c_remove(struct snd_soc_codec *codec)
+{
+ if (earphone_irq != -1)
+ {
+ free_irq(earphone_irq,codec->dev);
+ earphone_irq = -1;
+ }
+
+ atc2603c_pa_down(codec);
+
+ if(adc_detect_mode == 1)
+ {
+ cancel_delayed_work_sync(&dwork_adc_detect);
+ }
+ return 0;
+}
+
+static int atc2603c_suspend(struct snd_soc_codec *codec)
+{
+ snd_err("atc2603c_suspend\n");
+ //atc2603c_store_regs();
+#ifdef CONFIG_SND_UBUNTU
+ atc2603c_power_down(codec);
+ atc2603c_set_bias_level(codec, SND_SOC_BIAS_OFF);
+#endif
+ if(adc_detect_mode == 1)
+ {
+ cancel_delayed_work_sync(&dwork_adc_detect);
+ }
+ return 0;
+}
+
+static int atc2603c_resume(struct snd_soc_codec *codec)
+{
+ int i;
+ snd_err("atc2603c_resume\n");
+ printk("atc2603c_resume\n");
+ reenable_audio_block(codec);
+
+#ifdef CONFIG_SND_UBUNTU
+ atc2603c_pa_up_all(codec);
+ hw_init_flag = true;
+#endif
+ //atc2603c_restore_regs();
+ //atc2603c_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ for(i=0;i<8;i++)
+ {
+ unsigned short reg_now;
+ reg_now = (unsigned short)atc260x_reg_read(atc260x, i + REG_BASE);
+ printk("[0xa%d]:0x%x\n",i, reg_now);
+ }
+
+ if(adc_detect_mode == 1)
+ {
+ schedule_delayed_work(&dwork_adc_detect, msecs_to_jiffies(500));
+ atc2603c_adckeypad_config(codec);
+ }
+#ifdef CONFIG_SND_UBUNTU //suspend/resume
+ if(atc2603c_open_count > 0){
+ atc2603c_playback_set_controls(codec);
+ snd_soc_update_bits(codec, AUDIOINOUT_CTL, 0x01 << 1, 0x01 << 1);
+ }
+#endif
+ return 0;
+}
+
+static void atc2603c_audio_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+#if 0
+ struct snd_soc_codec *codec = dai->codec;
+
+ atc2603c_power_down(codec);
+ atc2603c_set_bias_level(codec, SND_SOC_BIAS_OFF);
+#endif
+ return;
+}
+
+#define ATC2603C_RATES SNDRV_PCM_RATE_8000_192000
+#ifdef CONFIG_SND_UBUNTU
+#define ATC2603C_FORMATS (SNDRV_PCM_FMTBIT_S32_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+#else
+#define ATC2603C_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+#endif
+
+struct snd_soc_dai_ops atc2603c_aif_dai_ops = {
+ .shutdown = atc2603c_audio_shutdown,
+ .hw_params = atc2603c_audio_hw_params,
+ .hw_free = atc2603c_audio_hw_free,
+ .prepare = atc2603c_audio_prepare,
+ .set_fmt = atc2603c_audio_set_dai_fmt,
+ .set_sysclk = atc2603c_audio_set_dai_sysclk,
+ .digital_mute = atc2603c_audio_set_dai_digital_mute,
+};
+
+struct snd_soc_dai_driver codec_atc2603c_dai[] = {
+ {
+ .name = "atc2603c-dai",
+ .id = ATC2603C_AIF,
+ .playback = {
+ .stream_name = "AIF Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = ATC2603C_RATES,
+ .formats = ATC2603C_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = ATC2603C_RATES,
+ .formats = ATC2603C_FORMATS,
+ },
+ .ops = &atc2603c_aif_dai_ops,
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_atc2603c = {
+ .probe = atc2603c_probe,
+ .remove = atc2603c_remove,
+
+ .suspend = atc2603c_suspend,
+ .resume = atc2603c_resume,
+ //.set_bias_level = atc2603c_set_bias_level,
+ .idle_bias_off = true,
+
+ .reg_cache_size = (ADC_ANALOG1 + 1),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_step = 1,
+
+ .controls = atc2603c_snd_controls,
+ .num_controls = ARRAY_SIZE(atc2603c_snd_controls),
+ .dapm_widgets = atc2603c_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(atc2603c_dapm_widgets),
+ .dapm_routes = atc2603c_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(atc2603c_dapm_routes),
+ .write = atc2603c_write,
+ .read = atc2603c_read,
+};
+
+static ssize_t error_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", error_switch);
+ return cnt;
+}
+
+static ssize_t error_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ error_switch = tmp;
+ break;
+ default:
+ printk(KERN_ERR"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+static ssize_t debug_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", debug_switch);
+ return cnt;
+}
+
+static ssize_t debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ debug_switch = tmp;
+ break;
+ default:
+ printk(KERN_INFO"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+static struct device_attribute atc2603c_attr[] = {
+ __ATTR(error, S_IRUSR | S_IWUSR, error_show, error_store),
+ __ATTR(debug, S_IRUSR | S_IWUSR, debug_show, debug_store),
+};
+
+int atc2603c_audio_get_pmu_status(void)
+{
+ return atc2603c_ictype;
+}
+
+EXPORT_SYMBOL_GPL(atc2603c_audio_get_pmu_status);
+
+static int earphone_is_in_for_irq(void)
+{
+ int val = 0;
+ val = atc260x_reg_read(atc260x, ADC_ANALOG0+REG_BASE);
+ if((val & 0x1<<8) != 0)
+ {
+ return HEADSET_NO_MIC;
+ }
+
+ val = atc260x_reg_read(atc260x, AGC_CTL1+REG_BASE);
+ if((val & 0x1<<13) != 0)
+ {
+ return HEADSET_MIC;
+ }
+
+ return SPEAKER_ON;
+}
+
+static int old_state = -1;
+static irqreturn_t earphone_detect_irq_handler(int irq, void *data)
+{
+ int state;
+
+ printk("earphone in/out\n");
+ state = earphone_is_in_for_irq();
+
+ if (state == HEADSET_NO_MIC) {
+ printk("sndrv: speaker off \n");
+ /* 1:headset with mic; 2:headset without mic */
+ switch_set_state(&headphone_sdev, HEADSET_NO_MIC);
+
+ atc260x_reg_write(atc260x,AUDIOINOUT_CTL+REG_BASE, atc260x_reg_read(atc260x, AUDIOINOUT_CTL+REG_BASE) | 0x1<<3);
+ atc260x_reg_write(atc260x,AUDIOINOUT_CTL+REG_BASE, atc260x_reg_read(atc260x, AUDIOINOUT_CTL+REG_BASE) & ~(0x1<<3));
+
+ }
+ else if (state == HEADSET_MIC)
+ {
+ printk("sndrv: speaker off, mic in \n");
+ /* 1:headset with mic; 2:headset without mic */
+ switch_set_state(&headphone_sdev, HEADSET_MIC);
+
+ atc260x_reg_write(atc260x,AUDIOINOUT_CTL+REG_BASE, atc260x_reg_read(atc260x, AUDIOINOUT_CTL+REG_BASE) | 0x1<<3);
+ atc260x_reg_write(atc260x,AUDIOINOUT_CTL+REG_BASE, atc260x_reg_read(atc260x, AUDIOINOUT_CTL+REG_BASE) & ~(0x1<<3));
+
+ }
+ else
+ {
+ printk("sndrv: speaker on \n");
+ switch_set_state(&headphone_sdev, SPEAKER_ON);
+
+ atc260x_reg_write(atc260x,AUDIOINOUT_CTL+REG_BASE, atc260x_reg_read(atc260x, AUDIOINOUT_CTL+REG_BASE) | 0x1<<2);
+ atc260x_reg_write(atc260x,AUDIOINOUT_CTL+REG_BASE, atc260x_reg_read(atc260x, AUDIOINOUT_CTL+REG_BASE) & ~(0x1<<2));
+ }
+
+ if(old_state == -1)
+ {
+ old_state = state;
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+static int atc2603c_platform_probe(struct platform_device *pdev)
+{
+ int i;
+ int ret = 0;
+
+ snd_err("atc2603c_platform_probe\r\n");
+
+/*
+ dn = of_find_compatible_node(NULL, NULL, "actions,atm7039c-i2s");
+ if (!dn) {
+ snd_err("Fail to get device_node actions,atm7039c-i2s\r\n");
+ //goto of_get_failed;
+ }
+*/
+
+ /*FIXME: what if error in second or third loop*/
+/*
+ for(i=0; i<2; i++)
+ {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ snd_err("no memory resource i=%d\n", i);
+ return -ENODEV;
+ }
+
+ if (!devm_request_mem_region (&pdev->dev, res->start,
+ resource_size(res), "gl5203-audio-i2s")) {
+ snd_err("Unable to request register region\n");
+ return -EBUSY;
+ }
+
+ codec_res.base[i] = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (codec_res.base[i] == NULL) {
+ snd_err("Unable to ioremap register region\n");
+ return -ENXIO;
+ }
+
+ snd_err("it's ok %d\n", i);
+ }
+*/
+ for (i = 0; i < ARRAY_SIZE(atc2603c_attr); i++) {
+ //snd_err("add file!\r\n");
+ ret = device_create_file(&pdev->dev, &atc2603c_attr[i]);
+ }
+
+
+ atc260x = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, atc260x);
+
+ atc2603c_ictype = ATC260X_ICTYPE_2603C;
+ pdev->dev.init_name = "atc260x-audio";
+
+ //we use VMICEXT to detect earphone
+ if((earphone_gpio_num<0)&&(adc_detect_mode == 0))
+ {
+ earphone_irq = platform_get_irq(pdev, 0);
+ snd_err("what's my lucky draw %d\n", earphone_irq);
+ }
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_atc2603c,
+ codec_atc2603c_dai, ARRAY_SIZE(codec_atc2603c_dai));
+}
+
+static int atc2603c_platform_remove(struct platform_device *pdev)
+{
+ int i = 0;
+ struct device *dev;
+
+ dev = bus_find_device_by_name(&platform_bus_type, NULL, "atc260x-audio");
+ if (dev) {
+ for (i = 0; i < ARRAY_SIZE(atc2603c_attr); i++) {
+ //snd_err("remove file!\r\n");
+ device_remove_file(dev, &atc2603c_attr[i]);
+ }
+ } else {
+ snd_err("Find platform device atc260x-audio failed!\r\n");
+ return -ENODEV;
+ }
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ return 0;
+}
+
+static void atc2603c_platform_shutdown(struct platform_device *pdev)
+{
+ gpio_direction_output(speaker_gpio_num, !speaker_gpio_active);
+/*
+ snd_soc_write(atc2603c_codec, DAC_VOLUMECTL0, 0xBEBE);
+ snd_soc_write(atc2603c_codec, DAC_ANALOG1, 0x0);
+ snd_soc_write(atc2603c_codec, DAC_ANALOG3, 0x8B0B);
+ snd_soc_write(atc2603c_codec, DAC_ANALOG4, 0x08C0);
+ snd_soc_write(atc2603c_codec, DAC_ANALOG2, 0x0100);
+ snd_soc_update_bits(atc2603c_codec,
+ AUDIOINOUT_CTL,
+ 0x01 << 1, 0);
+*/
+ atc2603c_power_down(atc2603c_codec);
+ return;
+}
+
+static const struct of_device_id atc2603c_audio_of_match[]= {
+ {.compatible = "actions,atc2603c-audio",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, atc2603c_audio_of_match);
+
+static struct platform_driver atc2603c_platform_driver = {
+ .probe = atc2603c_platform_probe,
+ .remove = atc2603c_platform_remove,
+ .driver = {
+ .name = "atc2603c-audio",
+ .owner = THIS_MODULE,
+ .of_match_table = atc2603c_audio_of_match,
+ },
+ .shutdown = atc2603c_platform_shutdown,
+};
+
+static int atc2603c_get_cfg(void)
+{
+ u32 ret = 1;
+ struct device_node *dn;
+
+ dn = of_find_compatible_node(NULL, NULL, audio_device_node);
+ if (!dn) {
+ snd_err("Fail to get device_node\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32(dn, snd_earphone_output_mode,
+ &audio_hw_cfg.earphone_output_mode);
+ if (ret) {
+ snd_err("Fail to get snd_earphone_output_mode\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32(dn, snd_mic_num,
+ &audio_hw_cfg.mic_num);
+ if (ret) {
+ snd_err("Fail to get snd_mic_num\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32_array(dn, snd_mic0_gain,
+ audio_hw_cfg.mic0_gain, 2);
+ if (ret) {
+ snd_err("Fail to get snd_mic_gain\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32_array(dn, snd_speaker_gain,
+ audio_hw_cfg.speaker_gain, 2);
+ if (ret) {
+ snd_err("Fail to get snd_speaker_gain\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32_array(dn, snd_earphone_gain,
+ audio_hw_cfg.earphone_gain, 2);
+ if (ret) {
+ snd_err("Fail to get snd_earphone_gain\r\n");
+ goto of_get_failed;
+ }
+
+/*
+ ret = of_property_read_u32(dn, snd_speaker_volume,
+ &audio_hw_cfg.speaker_volume);
+ if (ret) {
+ snd_err("Fail to get snd_speaker_volume\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32(dn, snd_earphone_volume,
+ &audio_hw_cfg.earphone_volume);
+ if (ret) {
+ snd_err("Fail to get snd_earphone_volume\r\n");
+ goto of_get_failed;
+ }
+
+ ret = of_property_read_u32(dn, snd_earphone_detect_mode,
+ &audio_hw_cfg.earphone_detect_mode);
+ if (ret) {
+ snd_err("Fail to get snd_earphone_detect_mode\r\n");
+ goto of_get_failed;
+ }
+*/
+ audio_hw_cfg.speaker_volume = 0x28;
+ audio_hw_cfg.earphone_volume = 0x28;
+ audio_hw_cfg.earphone_detect_mode = 0;
+
+ //20141202 change_code by yuchen : dont force fail on this one, there's a default value.
+ //default to differential
+ ret = of_property_read_u32(dn, snd_mic_mode,
+ &audio_hw_cfg.mic_mode);
+ if (ret) {
+ printk("fail get snd_mic_mode\r\n");
+ audio_hw_cfg.mic_mode = 1;
+ //goto of_get_failed;
+ }
+
+ //20150104 change_code by yuchen : add earphone detect method config
+ //0 for gpio, 1 for irq, 2 for adc
+ ret = of_property_read_u32(dn, snd_earphone_detect_method,
+ &audio_hw_cfg.earphone_detect_method);
+ if (ret) {
+ printk("fail get earphone_detect_method\r\n");
+ audio_hw_cfg.earphone_detect_method = 0;
+ //goto of_get_failed;
+ }
+
+ if(audio_hw_cfg.earphone_detect_method == 2)
+ {
+ ret = of_property_read_u32(dn, snd_adc_plugin_threshold,
+ &audio_hw_cfg.adc_plugin_threshold);
+ if (ret) {
+ printk("fail get snd_adc_plugin_threshold\r\n");
+ audio_hw_cfg.adc_plugin_threshold = 900;
+ //goto of_get_failed;
+ }
+ }
+
+ //fixme
+ ret = of_property_read_u32(dn, snd_adc_level,
+ &audio_hw_cfg.adc_level);
+ if (ret) {
+ printk("fail get adc level\r\n");
+ audio_hw_cfg.adc_level = 1;
+ //goto of_get_failed;
+ }
+
+ speaker_gpio_num = of_get_named_gpio_flags(dn, speaker_ctrl_name, 0, &speaker_gpio_level);
+ if (speaker_gpio_num < 0) {
+ snd_err("get gpio[%s] fail\r\n", speaker_ctrl_name);
+ }
+ speaker_gpio_active = (speaker_gpio_level & OF_GPIO_ACTIVE_LOW);
+
+ earphone_gpio_num = of_get_named_gpio_flags(dn, earphone_detect_gpio, 0, NULL);
+ if (earphone_gpio_num < 0) {
+ snd_dbg("no earphone detect gpio\r\n");
+ }
+
+
+ return 0;
+of_get_failed:
+ return ret;
+}
+
+static void atc2603c_dump_cfg(void)
+{
+#if 0
+ printk(KERN_ERR"earphone_detect_mode = %d\r\n",audio_hw_cfg.earphone_detect_mode);
+ printk(KERN_ERR"earphone_gain[0] = %d\r\n",audio_hw_cfg.earphone_gain[0]);
+ printk(KERN_ERR"earphone_gain[1] = %d\r\n",audio_hw_cfg.earphone_gain[1]);
+ printk(KERN_ERR"speaker_gain[0] = %d\r\n",audio_hw_cfg.speaker_gain[0]);
+ printk(KERN_ERR"speaker_gain[1] = %d\r\n",audio_hw_cfg.speaker_gain[1]);
+ printk(KERN_ERR"mic0_gain[0] = %d\r\n",audio_hw_cfg.mic0_gain[0]);
+ printk(KERN_ERR"mic0_gain[1] = %d\r\n",audio_hw_cfg.mic0_gain[1]);
+ printk(KERN_ERR"earphone_volume = %d\r\n",audio_hw_cfg.earphone_volume);
+ printk(KERN_ERR"speaker_volume = %d\r\n",audio_hw_cfg.speaker_volume);
+ printk(KERN_ERR"earphone_output_mode = %d\r\n",audio_hw_cfg.earphone_output_mode);
+ printk(KERN_ERR"mic_num = %d\r\n",audio_hw_cfg.mic_num);
+#endif
+}
+
+static int atc2603c_pa_down_notifier(struct notifier_block *notifier,
+ unsigned long pm_event, void *v)
+{
+ mutex_lock(&atc2603c_pa_down_lock);
+
+ switch (pm_event) {
+
+ case PM_SUSPEND_PREPARE:
+ user_lock = 1;
+ snd_soc_update_bits(atc2603c_codec,
+ AUDIOINOUT_CTL,
+ 0x01 << 1, 0x01 << 1);
+ atc2603c_power_down(atc2603c_codec);
+ snd_soc_update_bits(atc2603c_codec,
+ AUDIOINOUT_CTL,
+ 0x01 << 1, 0);
+ break;
+
+ case PM_POST_SUSPEND:
+ user_lock = 0;
+ break;
+ }
+ mutex_unlock(&atc2603c_pa_down_lock);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block atc2603c_pa_down_nb = {
+ .notifier_call = atc2603c_pa_down_notifier,
+};
+
+static int dbgflag;
+
+
+static ssize_t dbgflag_show_file (struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", dbgflag);
+}
+
+static ssize_t dbgflag_store_file(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ char *endp;
+ size_t size;
+ int flag = 0;
+
+ if (count > 0) {
+ flag = simple_strtoul(buf,&endp,0);
+ size = endp - buf;
+
+ if (*endp && (((*endp)==0x20)||((*endp)=='\n')||((*endp)=='\t')))
+ size++;
+
+ if (size != count)
+ {
+ snd_err("%s %d %s, %d", __FUNCTION__, __LINE__, buf, flag);
+ return -EINVAL;
+ }
+ }
+
+ snd_err("%s %d", __FUNCTION__, __LINE__);
+ dbgflag = flag;
+
+ return 0;
+}
+static DEVICE_ATTR(dbgflag, 0744, dbgflag_show_file, dbgflag_store_file);
+
+static int __init atc2603c_init(void)
+{
+ u32 ret = 0;
+
+ printk("atc2603c_init\n");
+
+ ret = atc2603c_get_cfg();
+ if (ret){
+ snd_err("audio get cfg failed!\r\n");
+ goto audio_get_cfg_failed;
+ }
+
+ atc2603c_dump_cfg();
+
+ direct_drive_disable = audio_hw_cfg.earphone_output_mode;
+ //snd_err("direct_drive_disable %d\n", direct_drive_disable);
+
+ ret = platform_driver_register(&atc2603c_platform_driver);
+ if(ret){
+ snd_err("platform_driver_register failed!\r\n");
+ goto platform_driver_register_failed;
+ }
+
+ //dev = bus_find_device_by_name(&platform_bus_type, NULL, "atc260x-audio");
+
+
+ register_pm_notifier(&atc2603c_pa_down_nb);
+
+ //20150103 change_code by yuchen: set adc_detect_mode by earphone detect method
+ if(audio_hw_cfg.earphone_detect_method == 2)
+ {
+ adc_detect_mode = 1;
+ }
+ else
+ {
+ adc_detect_mode = 0;
+ }
+
+ if((audio_hw_cfg.earphone_detect_method == 1) || (audio_hw_cfg.earphone_detect_method == 2))
+ {
+ headphone_sdev.name = "h2w";
+ ret = switch_dev_register(&headphone_sdev);
+ if (ret < 0) {
+ snd_err("failed to register switch dev for h2w\n");
+ }
+
+ dbgflag = 0;
+ ret = device_create_file(headphone_sdev.dev, &dev_attr_dbgflag);
+ if (ret)
+ {
+ snd_err("failed to device_create_file\n");
+ }
+
+
+ }
+
+ return 0;
+platform_driver_register_failed:
+audio_get_cfg_failed:
+ return ret;
+}
+
+static void __exit atc2603c_exit(void)
+{
+ platform_driver_unregister(&atc2603c_platform_driver);
+ switch_dev_unregister(&headphone_sdev);
+}
+
+module_init(atc2603c_init);
+module_exit(atc2603c_exit);
+
+
+MODULE_AUTHOR("sall.xie <sall.xie at actions-semi.com>");
+MODULE_DESCRIPTION("ATC2603C AUDIO module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atc260x/atc2603c-codec/atc2603c-audio-regs.h b/sound/soc/atc260x/atc2603c-codec/atc2603c-audio-regs.h
new file mode 100755
index 0000000..033bd37
--- /dev/null
+++ b/sound/soc/atc260x/atc2603c-codec/atc2603c-audio-regs.h
@@ -0,0 +1,244 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2009 Actions Semi Inc.
+ */
+
+#ifndef __ATC2603C_AUDIO_REGS_H__
+#define __ATC2603C_AUDIO_REGS_H__
+
+/* AUDIOINOUT_CTL */
+#define AUDIOINOUT_CTL_INEN (0x1 << 1)
+#define AUDIOINOUT_CTL_LB (0x1 << 4)
+#define AUDIOINOUT_CTL_IMS(x) (((x) & 0x3) << 5)
+#define AUDIOINOUT_CTL_OMD (0x1 << 7)
+#define AUDIOINOUT_CTL_OEN (0x1 << 8)
+#define AUDIOINOUT_CTL_OCIEN (0x1 << 9)
+#define AUDIOINOUT_CTL_OHSCIEN (0x1 << 10)
+#define AUDIOINOUT_CTL_MDD (0x1 << 11)
+#define AUDIOINOUT_CTL_EIDR (0x1 << 12)
+
+/* DAC_DIGITALCTL */
+#define DAC_DIGITALCTL_DEFL (0x1 << 0)
+#define DAC_DIGITALCTL_DEFL_SFT (0)
+#define DAC_DIGITALCTL_DEFR (0x1 << 1)
+#define DAC_DIGITALCTL_DEFR_SFT (1)
+#define DAC_DIGITALCTL_DESW (0x1 << 2)
+#define DAC_DIGITALCTL_DEC (0x1 << 3)
+#define DAC_DIGITALCTL_DESL (0x1 << 4)
+#define DAC_DIGITALCTL_DESR (0x1 << 5)
+#define DAC_DIGITALCTL_DESBL (0x1 << 6)
+#define DAC_DIGITALCTL_DESBR (0x1 << 7)
+#define DAC_DIGITALCTL_DMFL (0x1 << 8)
+#define DAC_DIGITALCTL_DMFR (0x1 << 9)
+#define DAC_DIGITALCTL_DMSW (0x1 << 10)
+#define DAC_DIGITALCTL_DMC (0x1 << 11)
+#define DAC_DIGITALCTL_DMSL (0x1 << 12)
+#define DAC_DIGITALCTL_DMSR (0x1 << 13)
+#define DAC_DIGITALCTL_DMSBL (0x1 << 14)
+#define DAC_DIGITALCTL_DMSBR (0x1 << 15)
+
+/* DAC_VOLUMECTL0 */
+#define DAC_VOLUMECTL0_DACFL_VOLUME(x) (((x) & 0xff) << 0)
+#define DAC_VOLUMECTL0_DACFL_VOLUME_SFT (0)
+#define DAC_VOLUMECTL0_DACFR_VOLUME(x) (((x) & 0xff) << 8)
+#define DAC_VOLUMECTL0_DACFR_VOLUME_SFT (8)
+
+
+/* DAC_ANALOG0 */
+#define DAC_ANALOG0_OPGIB(x) (((x) & 0x7) << 0)
+#define DAC_ANALOG0_KFEN (0x1 << 3)
+#define DAC_ANALOG0_OPVBIB(x) (((x) & 0x3) << 4)
+#define DAC_ANALOG0_OPDTSIB(x) (((x) & 0x3) << 6)
+#define DAC_ANALOG0_OPDAIB(x) (((x) & 0x7) << 8)
+#define DAC_ANALOG0_OPDAVB(x) (((x) & 0x3) << 12)
+#define DAC_ANALOG0_PAIB(x) (((x) & 0x3) << 14)
+
+/* DAC_ANALOG1 */
+#define DAC_ANALOG1_VOLUME(x) (((x) & 0x3f) << 0)
+#define DAC_ANALOG1_VOLUME_SFT (0)
+#define DAC_ANALOG1_PASW (0x1 << 6)
+#define DAC_ANALOG1_PASW_SFT (6)
+#define DAC_ANALOG1_ZERODT (0x1 << 7)
+#define DAC_ANALOG1_PAIQ(x) (((x) & 0x3) << 8)
+#define DAC_ANALOG1_DACFL_FRMUTE (0x1 << 10)
+#define DAC_ANALOG1_DACFL_FRMUTE_SFT (10)
+#define DAC_ANALOG1_DACSW_CMUTE (0x1 << 11)
+#define DAC_ANALOG1_DACSL_SRMUTE (0x1 << 12)
+#define DAC_ANALOG1_DACSBL_SBRMUTE (0x1 << 13)
+#define DAC_ANALOG1_DACFMMUTE (0x1 << 14)
+#define DAC_ANALOG1_DACFMMUTE_SFT (14)
+#define DAC_ANALOG1_DACMICMUTE (0x1 << 15)
+#define DAC_ANALOG1_DACMICMUTE_SFT (15)
+
+/* DAC_ANALOG2 */
+#define DAC_ANALOG2_OPVROOSIB(x) (((x) & 0x7) << 0)
+#define DAC_ANALOG2_DDATPR (0x1 << 3)
+#define DAC_ANALOG2_OPVROEN (0x1 << 4)
+#define DAC_ANALOG2_DDOVV (0x1 << 5)
+#define DAC_ANALOG2_CLDMIX(x) (((x) & 0x3) << 6)
+#define DAC_ANALOG2_PAVDC (0x1 << 8)
+#define DAC_ANALOG2_ATP2CE (0x1 << 9)
+#define DAC_ANALOG2_P2IB (0x1 << 10)
+#define DAC_ANALOG2_DACI (0x1 << 11)
+#define DAC_ANALOG2_ZERODETECT (0x1 << 15)
+
+/* DAC_ANALOG3 */
+#define DAC_ANALOG3_DACEN_FR (0x1)
+#define DAC_ANALOG3_DACEN_FR_SFT (0)
+#define DAC_ANALOG3_DACEN_FL (0x1 << 1)
+#define DAC_ANALOG3_DACEN_FL_SFT (1)
+#define DAC_ANALOG3_PAEN_FR_FL (0x1 << 2)
+#define DAC_ANALOG3_PAEN_FR_FL_SFT (2)
+#define DAC_ANALOG3_PAOSEN_FR_FL (0x1 << 3)
+#define DAC_ANALOG3_PAOSEN_FR_FL_SFT (3)
+
+/*
+#define DAC_ANALOG3_OPVROIB(x) (((x) & 0x7) << 0)
+#define DAC_ANALOG3_OPCM1IB(x) (((x) & 0x3) << 3)
+#define DAC_ANALOG3_ATPLP2_SBR_SBL (0x1 << 5)
+#define DAC_ANALOG3_ATPLP2_SR_SL (0x1 << 6)
+#define DAC_ANALOG3_ATPLP2_SW_C (0x1 << 7)
+#define DAC_ANALOG3_ATPLP2_FR_FL (0x1 << 8)
+#define DAC_ANALOG3_BIASEN (0x1 << 9)
+#define DAC_ANALOG3_EIDEN (0x1 << 10)
+#define DAC_ANALOG3_VLCHD (0x1 << 13)
+#define DAC_ANALOG3_OVLS (0x1 << 14)
+#define DAC_ANALOG3_EIDS (0x1 << 15)
+*/
+
+/* ADC_DIGITALCTL */
+#define ADC_DIGITALCTL_ADGC0 (((x) & 0xf) << 6)
+#define ADC_DIGITALCTL_ADGC0_SFT (6)
+
+/* ADC0_HPFCTL */
+#define ADC0_HPFCTL_HPF0REN (0x1 << 0)
+#define ADC0_HPFCTL_HPF0LEN (0x1 << 1)
+#define ADC0_HPFCTL_HPF0DW (0x1 << 2)
+#define ADC0_HPFCTL_WNHPF0CUT(x) (((x) & 0x7) << 3)
+#define ADC0_HPFCTL_SRSEL0(x) (((x) & 0x3) << 6)
+
+
+/* ADC_CTL */
+#define ADC_CTL_ATAD_MTA_FTA_SFT (0)
+#define ADC_CTL_AD0REN (0x1 << 3)
+#define ADC_CTL_AD0REN_SFT (3)
+#define ADC_CTL_AD0LEN (0x1 << 4)
+#define ADC_CTL_AD0LEN_SFT (4)
+#define ADC_CTL_MIC0FDSE (0x1 << 5)
+#define ADC_CTL_MIC0FDSE_SFT (5)
+#define ADC_CTL_MIC0REN (0x1 << 6)
+#define ADC_CTL_MIC0REN_SFT (6)
+#define ADC_CTL_MIC0LEN (0x1 << 7)
+#define ADC_CTL_MIC0LEN_SFT (7)
+#define ADC_CTL_FMREN (0x1 << 13)
+#define ADC_CTL_FMREN_SFT (13)
+#define ADC_CTL_FMLEN (0x1 << 14)
+#define ADC_CTL_FMLEN_SFT (14)
+
+
+
+/* AGC_CTL0 */
+#define AGC_CTL0_AMP0GR1(x) (((x) & 0x7) << 0)
+#define AGC_CTL0_AMP0GR1_SET (0)
+#define AGC_CTL0_IMICSHD (0x1 << 7)
+#define AGC_CTL0_AMP1G0R(x) (((x) & 0xf) << 8)
+#define AGC_CTL0_AMP1G0R_SFT (8)
+#define AGC_CTL0_AMP1G0L(x) (((x) & 0xf) << 12)
+#define AGC_CTL0_AMP1G0L_SFT (12)
+
+#define AGC_CTL0_AMP1GR1_MSK (0x7 << 0)
+#define AGC_CTL0_AMP1GR_MSK (0xf << 8)
+#define AGC_CTL0_AMP1GL_MSK (0xf << 12)
+
+#define AGC_CTL0_VMICINEN (0x1 << 3)
+#define AGC_CTL0_VMICINEN_SFT (3)
+#define AGC_CTL0_VMICEXST (((x) & 0x3) << 4)
+#define AGC_CTL0_VMICEXST_SFT (4)
+#define AGC_CTL0_VMICEXEN (0x1 << 6)
+#define AGC_CTL0_VMICEXEN_SFT (6)
+
+/* ADC_ANALOG0 */
+#define ADC_ANALOG0_VRDABC(x) (((x) & 0x7) << 0)
+#define ADC_ANALOG0_OPBC23(x) (((x) & 0x3) << 3)
+#define ADC_ANALOG0_OPBC1(x) (((x) & 0x7) << 5)
+#define ADC_ANALOG0_IVSRMSTN(x) (((x) & 0x7) << 13)
+
+/* ADC_ANALOG1 */
+#define ADC_ANALOG1_FMBC(x) (((x) & 0x3) << 0)
+#define ADC_ANALOG1_FD1BUFBC(x) (((x) & 0x3) << 2)
+#define ADC_ANALOG1_FD2BC(x) (((x) & 0x3) << 4)
+#define ADC_ANALOG1_FD1BC(x) (((x) & 0x3) << 6)
+#define ADC_ANALOG1_ADCBIAS (0x1 << 10)
+#define ADC_ANALOG1_LPFBUFBC(x) (((x) & 0x3) << 11)
+#define ADC_ANALOG1_LPFBC(x) (((x) & 0x7) << 13)
+
+/* I2S_CTL */
+#define I2S_CTL_I2STEN (0x1 << 0)
+#define I2S_CTL_I2SREN (0x1 << 1)
+#define I2S_CTL_I2STOWL (0x1 << 2)
+#define I2S_CTL_I2STTDMRF (0x1 << 3)
+#define I2S_CTL_I2STXM(x) (((x) & 0x7) << 4)
+#define I2S_CTL_I2SRXM(x) (((x) & 0x3) << 8)
+#define I2S_CTL_I2SRCS (0x1 << 10)
+#define I2S_CTL_I2SPM(x) (((x) & 0x3) << 11)
+
+/* I2S_FIFOCTL */
+#define I2S_FIFOCTL_I2STFR (0x1 << 0)
+#define I2S_FIFOCTL_I2STFDEN (0x1 << 1)
+#define I2S_FIFOCTL_I2STFIEN (0x1 << 2)
+#define I2S_FIFOCTL_I2STFIP (0x1 << 3)
+#define I2S_FIFOCTL_I2STFDCF(x) (((x) & 0x7) << 4)
+#define I2S_FIFOCTL_I2STFDRF (0x1 << 7)
+#define I2S_FIFOCTL_I2STFFF (0x1 << 8)
+#define I2S_FIFOCTL_I2SRFR (0x1 << 9)
+#define I2S_FIFOCTL_I2SRFDEN (0x1 << 10)
+#define I2S_FIFOCTL_I2SRFIEN (0x1 << 11)
+#define I2S_FIFOCTL_I2SRFIP (0x1 << 12)
+#define I2S_FIFOCTL_I2SRFDCF(x) (((x) & 0x3) << 13)
+#define I2S_FIFOCTL_I2STXKA (0x1 << 15)
+#define I2S_FIFOCTL_I2SRFDRF (0x1 << 16)
+#define I2S_FIFOCTL_I2SRFEF (0x1 << 17)
+#define I2S_FIFOCTL_I2STFSS (0x1 << 18)
+#define I2S_FIFOCTL_KMCMMI2ST(x) (((x) & 0x3) << 19)
+
+/* SPDIF_HDMI_CTL */
+#define SPDIF_HDMI_CTL_SPDFR (0x1 << 0)
+#define SPDIF_HDMI_CTL_HDMIFR (0x1 << 1)
+#define SPDIF_HDMI_CTL_SPDFIP (0x1 << 2)
+#define SPDIF_HDMI_CTL_SPDFFF (0x1 << 3)
+#define SPDIF_HDMI_CTL_SPDFDEN (0x1 << 4)
+#define SPDIF_HDMI_CTL_SPDFIEN (0x1 << 5)
+#define SPDIF_HDMI_CTL_HDMFIP (0x1 << 6)
+#define SPDIF_HDMI_CTL_HDMFFF (0x1 << 7)
+#define SPDIF_HDMI_CTL_HDMFDEN (0x1 << 8)
+#define SPDIF_HDMI_CTL_HDMFIEN (0x1 << 9)
+#define SPDIF_HDMI_CTL_SPDEN (0x1 << 10)
+#define SPDIF_HDMI_CTL_SPDKA (0x1 << 11)
+#define SPDIF_HDMI_CTL_HDMKA (0x1 << 12)
+#define SPDIF_HDMI_CTL_SPDFSS (0x1 << 13)
+#define SPDIF_HDMI_CTL_HDMFSS (0x1 << 14)
+#define SPDIF_HDMI_CTL_KMCMMHDM(x) (((x) & 0x3) << 15)
+
+/* CMU_AUDIOPLL */
+#define CMU_AUDIOPLL_AUDIOPLLS (0x1 << 0)
+#define CMU_AUDIOPLL_APEN (0x1 << 4)
+#define CMU_AUDIOPLL_I2STX_CLK(x) (((x) & 0xf) << 16)
+#define CMU_AUDIOPLL_I2SRX_CLK(x) (((x) & 0xf) << 20)
+#define CMU_AUDIOPLL_HDMIA_CLK(x) (((x) & 0xf) << 24)
+#define CMU_AUDIOPLL_SPDIF_CLK(x) (((x) & 0xf) << 28)
+
+/* CMU_DEVCLKEN */
+#define CMU_DEVCLKEN0_I2STX (0x1 << 20)
+#define CMU_DEVCLKEN0_I2SRX (0x1 << 21)
+#define CMU_DEVCLKEN0_HDMIA (0x1 << 22)
+#define CMU_DEVCLKEN0_SPDIF (0x1 << 23)
+
+#define CMU_DEVRST0_AUDIO (0x1 << 17)
+
+#define GL5302_DEVRST_AUDIO_CLK_EN (0x1 << 10)
+#define GL5302_DEVRST_AUDIO_RST (0x1 << 4)
+
+#endif /* ifndef __ATC2603C_AUDIO_REGS_H__ */
diff --git a/sound/soc/atc260x/common-regs-owl.h b/sound/soc/atc260x/common-regs-owl.h
new file mode 100755
index 0000000..5668f3a
--- /dev/null
+++ b/sound/soc/atc260x/common-regs-owl.h
@@ -0,0 +1,205 @@
+/*
+ * reg-atm7059.h
+ *
+ */
+
+#ifndef __REG_5206_H__
+#define __REG_5206_H__
+
+/*I2S register definition*/
+#define I2S_SPDIF_BASE 0xB0100000
+
+#define I2S_CTL (0x0000)
+#define I2S_FIFOCTL (0x0004)
+#define I2STX_DAT (0x0008)
+#define I2SRX_DAT (0x000c)
+#define SPDIF_HDMI_CTL (0x0010)
+#define SPDIF_DAT (0x0014)
+#define SPDIF_CLSTAT (0x0018)
+#define SPDIF_CHSTAT (0x001c)
+#define HDMI_DAT (0x0020)
+#define I2STX_DAT_DBG (0x0024)
+#define I2SRX_DAT_DBG (0x0028)
+#define I2STX_SPDIF_HDMI_CTL (0x002c)
+#define I2STX_SPDIF_HDMI_DAT (0x0030)
+
+/*GPIO MFP register definition*/
+#define GPIO_MFP_PWM_BASE 0xB01B0000
+
+#define GPIO_AOUTEN (0x0000)
+#define GPIO_AINEN (0x0004)
+#define GPIO_ADAT (0x0008)
+#define GPIO_BOUTEN (0x000C)
+#define GPIO_BINEN (0x0010)
+#define GPIO_BDAT (0x0014)
+#define GPIO_COUTEN (0x0018)
+#define GPIO_CINEN (0x001C)
+#define GPIO_CDAT (0x0020)
+#define GPIO_DOUTEN (0x0024)
+#define GPIO_DINEN (0x0028)
+#define GPIO_DDAT (0x002C)
+#define GPIO_EOUTEN (0x0030)
+#define GPIO_EINEN (0x0034)
+#define GPIO_EDAT (0x0038)
+#define MFP_CTL0 (0x0040)
+#define MFP_CTL1 (0x0044)
+#define MFP_CTL2 (0x0048)
+#define MFP_CTL3 (0x004C)
+#define PWM_CTL0 (0X50)
+#define PWM_CTL1 (0X54)
+#define PWM_CTL2 (0X58)
+#define PWM_CTL3 (0X5C)
+#define PAD_PULLCTL0 (0x0060)
+#define PAD_PULLCTL1 (0x0064)
+#define PAD_PULLCTL2 (0x0068)
+#define PAD_ST0 (0x006C)
+#define PAD_ST1 (0x0070)
+#define PAD_CTL (0x0074)
+#define SPEED_SENSOR_CTL (0x007C)
+#define PAD_DRV0 (0x0080)
+#define PAD_DRV1 (0x0084)
+#define PAD_DRV2 (0x0088)
+#define DEBUG_SEL (0x0090)
+#define DEBUG_OEN0 (0x0094)
+#define DEBUG_OEN1 (0x0098)
+#define DEBUG_IEN0 (0x009C)
+#define DEBUG_IEN1 (0x00A0)
+#define MEM_MARGIN_CTRL0 (0x00B0)
+#define MEM_MARGIN_CTRL1 (0x00B4)
+#define BIST_START0 (0x00C0)
+#define BIST_START1 (0x00C4)
+#define BIST_DONE0 (0x00C8)
+#define BIST_DONE1 (0x00CC)
+#define BIST_FAIL0 (0x00D0)
+#define BIST_FAIL1 (0x00D4)
+#define INTC_EXTCTL (0x0200)
+#define INTC_GPIOCTL (0x0204)
+#define INTC_GPIOA_PD (0x0208)
+#define INTC_GPIOA_MSK (0x020c)
+#define INTC_GPIOB_PD (0x0210)
+#define INTC_GPIOB_MSK (0x0214)
+#define INTC_GPIOC_PD (0x0218)
+#define INTC_GPIOC_MSK (0x021c)
+#define INTC_GPIOD_PD (0x0220)
+#define INTC_GPIOD_MSK (0x0224)
+#define INTC_GPIOE_PD (0x0228)
+#define INTC_GPIOE_MSK (0x022c)
+#define INTC_GPIOA_TYPE0 (0x0230)
+#define INTC_GPIOA_TYPE1 (0x0234)
+#define INTC_GPIOB_TYPE0 (0x0238)
+#define INTC_GPIOB_TYPE1 (0x023c)
+#define INTC_GPIOC_TYPE0 (0x0240)
+#define INTC_GPIOC_TYPE1 (0x0244)
+#define INTC_GPIOD_TYPE0 (0x0248)
+#define INTC_GPIOD_TYPE1 (0x024C)
+#define INTC_GPIOE_TYPE (0x0250)
+
+
+/* HDMI register definition*/
+#define HDMIHW_REG_MEM_BASE 0xB02C0000
+
+#define HDMI_VICTL (0x0000)
+#define HDMI_VIVSYNC (0x0004)
+#define HDMI_VIVHSYNC (0x0008)
+#define HDMI_VIALSEOF (0x000C)
+#define HDMI_VIALSEEF (0x0010)
+#define HDMI_VIADLSE (0x0014)
+#define HDMI_AIFRAMEC (0x0020)
+#define HDMI_AICHSTABYTE0TO3 (0x0024)
+#define HDMI_AICHSTABYTE4TO7 (0x0028)
+#define HDMI_AICHSTABYTE8TO11 (0x002C)
+#define HDMI_AICHSTABYTE12TO15 (0x0030)
+#define HDMI_AICHSTABYTE16TO19 (0x0034)
+#define HDMI_AICHSTABYTE20TO23 (0x0038)
+#define HDMI_AICHSTASCN (0x003C)
+#define HDMI_VR (0x0050)
+#define HDMI_CR (0x0054)
+#define HDMI_SCHCR (0x0058)
+#define HDMI_ICR (0x005C)
+#define HDMI_SCR (0x0060)
+#define HDMI_LPCR (0x0064)
+#define HDCP_CR (0x0068)
+#define HDCP_SR (0x006C)
+#define HDCP_ANLR (0x0070)
+#define HDCP_ANMR (0x0074)
+#define HDCP_ANILR (0x0078)
+#define HDCP_ANIMR (0x007C)
+#define HDCP_DPKLR (0x0080)
+#define HDCP_DPKMR (0x0084)
+#define HDCP_LIR (0x0088)
+#define HDCP_SHACR (0x008C)
+#define HDCP_SHADR (0x0090)
+#define HDCP_ICR (0x0094)
+#define HDCP_KMMR (0x0098)
+#define HDCP_KMLR (0x009C)
+#define HDCP_MILR (0x00A0)
+#define HDCP_MIMR (0x00A4)
+#define HDCP_KOWR (0x00A8)
+#define HDCP_OWR (0x00AC)
+#define TMDS_STR0 (0x00B8)
+#define TMDS_STR1 (0x00BC)
+#define TMDS_EODR0 (0x00C0)
+#define TMDS_EODR1 (0x00C4)
+#define HDMI_ASPCR (0x00D0)
+#define HDMI_ACACR (0x00D4)
+#define HDMI_ACRPCR (0x00D8)
+#define HDMI_ACRPCTSR (0x00DC)
+#define HDMI_ACRPPR (0x00E0)
+#define HDMI_GCPCR (0x00E4)
+#define HDMI_RPCR (0x00E8)
+#define HDMI_RPRBDR (0x00EC)
+#define HDMI_OPCR (0x00F0)
+#define HDMI_DIPCCR (0x00F4)
+#define HDMI_ORP6PH (0x00F8)
+#define HDMI_ORSP6W0 (0x00FC)
+#define HDMI_ORSP6W1 (0x0100)
+#define HDMI_ORSP6W2 (0x0104)
+#define HDMI_ORSP6W3 (0x0108)
+#define HDMI_ORSP6W4 (0x010C)
+#define HDMI_ORSP6W5 (0x0110)
+#define HDMI_ORSP6W6 (0x0114)
+#define HDMI_ORSP6W7 (0x0118)
+#define HDMI_CECCR (0x011C)
+#define HDMI_CECRTCR (0x0120)
+#define HDMI_CECRXCR (0x0124)
+#define HDMI_CECTXCR (0x0128)
+#define HDMI_CECTXDR (0x012C)
+#define HDMI_CECRXDR (0x0130)
+#define HDMI_CECRXTCR (0x0134)
+#define HDMI_CECTXTCR0 (0x0138)
+#define HDMI_CECTXTCR1 (0x013C)
+#define HDMI_CRCCR (0x0140)
+#define HDMI_CRCDOR (0x0144)
+#define HDMI_TX_1 (0x0154)
+#define HDMI_TX_2 (0x0158)
+#define CEC_DDC_HPD (0x015C)
+#define MHL_PHYCTL1 (0x0160)
+#define MHL_PHYCTL2 (0x0164)
+#define MHL_PHYCTL3 (0x0168)
+#define MHL_CR (0x0180)
+#define MHL_INTMSK (0x0184)
+#define MHL_INTPD (0x0188)
+#define MHL_INTSR (0x018c)
+#define MSC_REQMSGR (0x0190)
+#define MSC_REQRMSGR (0x0194)
+#define MSC_RSPRMR (0x0198)
+#define MSC_RSPRFIFO (0x019c)
+#define MSC_RSPRRMR (0x01a0)
+#define MHL_DDCCSR (0x01a4)
+#define MHL_DDCPR (0x01a8)
+#define CBUS_DCR0TO3 (0x01b0)
+#define CBUS_DCR4TO7 (0x01b4)
+#define CBUS_DCR8TOB (0x01b8)
+#define CBUS_DCRCTOF (0x01bc)
+#define CBUS_DEVINTR (0x01c0)
+#define CBUS_DEVSR (0x01c4)
+#define CBUS_SPR0TO3 (0x01c8)
+#define CBUS_SPR4TO7 (0x01cc)
+#define CBUS_SPR8TOB (0x01d0)
+#define CBUS_SPRCTOF (0x01d4)
+#define CBUS_VID (0x01d8)
+#define CBUS_LLTCR (0x01e0)
+#define CBUS_TLTCR (0x01e4)
+#define MHL_DEBUG (0x1f0)
+
+#endif
diff --git a/sound/soc/atc260x/dai-owl.c b/sound/soc/atc260x/dai-owl.c
new file mode 100755
index 0000000..e10232f
--- /dev/null
+++ b/sound/soc/atc260x/dai-owl.c
@@ -0,0 +1,875 @@
+#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 <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include <linux/clk.h> /* clk_enable */
+#include <mach/clkname.h>
+#include <linux/cpufreq.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include "sndrv-owl.h"
+#include "common-regs-owl.h"
+#include <mach/module-owl.h>
+
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+
+static int dai_clk_i2s_count;
+static int dai_clk_hdmi_count;
+static int dai_clk_spdif_count;
+static int dai_mode_i2s_count;
+static int dai_mode_hdmi_count;
+
+#ifdef CONFIG_SND_UBUNTU
+static int is_i2s_playback;
+static int is_i2s_record;
+#endif
+
+struct asoc_dai_resource {
+ void __iomem *base[MAX_RES_NUM];/*virtual base for every resource*/
+ void __iomem *baseptr; /*pointer to every virtual base*/
+ struct clk *clk;
+ int irq;
+ unsigned int setting;
+};
+
+//dai resources
+static struct asoc_dai_resource dai_res;
+
+void set_dai_reg_base(int num)
+{
+ dai_res.baseptr = dai_res.base[num];
+}
+
+EXPORT_SYMBOL_GPL(set_dai_reg_base);
+
+u32 snd_dai_readl(u32 reg)
+{
+ return readl(dai_res.baseptr + reg);
+}
+
+EXPORT_SYMBOL_GPL(snd_dai_readl);
+
+void snd_dai_writel(u32 val, u32 reg)
+{
+ writel(val, dai_res.baseptr + reg);
+}
+
+EXPORT_SYMBOL_GPL(snd_dai_writel);
+
+
+
+static ssize_t error_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", error_switch);
+ return cnt;
+}
+
+static ssize_t error_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ error_switch = tmp;
+ break;
+ default:
+ printk(KERN_ERR"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+static ssize_t debug_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", debug_switch);
+ return cnt;
+}
+
+static ssize_t debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ debug_switch = tmp;
+ break;
+ default:
+ printk(KERN_INFO"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+static struct device_attribute dai_attr[] = {
+ __ATTR(error, S_IRUSR | S_IWUSR, error_show, error_store),
+ __ATTR(debug, S_IRUSR | S_IWUSR, debug_show, debug_store),
+};
+
+/******************************************************************************/
+/*!
+ * \par Description:
+ * 将采样率转换成矊 *¬件寄存器设置所需的索引值
+ * \param[in] sample_rate 采样率
+ * \param[in] mode 输入输出模
+ *式 (如果是spdif或者hdmi,index不同)
+ * \return 索引值
+ * \retval -1 failed
+ * \ingroup sndrv
+ *****************************************/
+static int get_sf_index(int sample_rate, int mode)
+{
+ int i = 0;
+ char fs = sample_rate / 1000;
+ // 44k 在基础上寄存器bit位基础上加16
+ static fs_t fs_list[] = {
+ { 384, { -1, 0} },
+ { 352, { -1, 16} },
+ { 192, { 0, 1} },
+ { 176, { 16, 17} },
+ { 96, { 1, 3} },
+ { 88, { 17, 19} },
+ { 64, { 2, -1} },
+ { 48, { 3, 5} },
+ { 44, { 19, 21} },
+ { 32, { 4, 6} },
+ { 24, { 5, -1} },
+ { 22, {21, -1} },
+ { 16, { 6, -1} },
+ { 12, { 7, -1} },
+ { 11, {23, -1} },
+ { 8, { 8, -1} },
+ { -1, {-1, -1} }
+ };
+
+ while ((fs_list[i].sample_rate > 0) && (fs_list[i].sample_rate != fs))
+ i++;
+
+ if ((mode == O_MODE_HDMI) || (mode == O_MODE_SPDIF))
+ return fs_list[i].index[1];
+ else
+ return fs_list[i].index[0];
+}
+
+static int atm7059_dai_record_clk_set(int mode, int rate)
+{
+ struct clk *apll_clk;
+ unsigned long reg_val;
+ int sf_index, ret;
+
+ if (dai_clk_i2s_count > 0) {
+ dai_clk_i2s_count++;
+ return 0;
+ }
+ module_clk_enable(MOD_ID_I2SRX);
+ module_clk_enable(MOD_ID_I2STX);
+
+
+ sf_index = get_sf_index(rate, mode);
+ if (sf_index & 0x10)
+ reg_val = 45158400;
+ else
+ reg_val = 49152000;
+
+ apll_clk = clk_get(NULL, CLKNAME_AUDIOPLL);
+ clk_prepare(apll_clk);
+ ret = clk_set_rate(apll_clk, reg_val);
+ if (ret < 0) {
+ snd_dbg("audiopll set error!\n");
+ return ret;
+ }
+
+ apll_clk = clk_get(NULL, CLKNAME_I2STX_CLK);
+ ret = clk_set_rate(apll_clk, rate << 8);
+ if (ret) {
+ snd_dbg("i2stx clk rate set error!!\n");
+ return ret;
+ }
+ apll_clk = clk_get(NULL, CLKNAME_I2SRX_CLK);
+ ret = clk_set_rate(apll_clk, rate << 8);
+ if (ret) {
+ snd_dbg("i2srx clk rate set error!!\n");
+ return ret;
+ }
+ dai_clk_i2s_count++;
+
+ return 0;
+}
+
+static int atm7059_dai_clk_set(int mode, int rate)
+{
+ struct clk *apll_clk;
+ unsigned long reg_val;
+ int sf_index, ret;
+
+ module_clk_enable(MOD_ID_I2SRX);
+ module_clk_enable(MOD_ID_I2STX);
+
+ sf_index = get_sf_index(rate, mode);
+ if (sf_index & 0x10)
+ reg_val = 45158400;
+ else
+ reg_val = 49152000;
+
+ apll_clk = clk_get(NULL, CLKNAME_AUDIOPLL);
+ clk_prepare(apll_clk);
+ ret = clk_set_rate(apll_clk, reg_val);
+ if (ret < 0) {
+ snd_dbg("audiopll set error!\n");
+ return ret;
+ }
+
+ switch (mode) {
+ case O_MODE_I2S:
+ if (dai_clk_i2s_count == 0) {
+ apll_clk = clk_get(NULL, CLKNAME_I2STX_CLK);
+ ret = clk_set_rate(apll_clk, rate << 8);
+ if (ret) {
+ snd_dbg("i2stx clk rate set error!!\n");
+ return ret;
+ }
+ apll_clk = clk_get(NULL, CLKNAME_I2SRX_CLK);
+ ret = clk_set_rate(apll_clk, rate << 8);
+ if (ret) {
+ snd_dbg("i2srx clk rate set error!!\n");
+ return ret;
+ }
+ }
+ dai_clk_i2s_count++;
+ break;
+
+ case O_MODE_HDMI:
+ if (dai_clk_hdmi_count == 0) {
+ apll_clk = clk_get(NULL, CLKNAME_HDMIA_CLK);
+ ret = clk_set_rate(apll_clk, rate << 7);
+ if (ret) {
+ snd_dbg("hdmi clk rate set error!!\n");
+ return ret;
+ }
+ }
+ dai_clk_hdmi_count++;
+ break;
+
+ case O_MODE_SPDIF:
+ if (dai_clk_spdif_count == 0) {
+ apll_clk = clk_get(NULL, CLKNAME_SPDIF_CLK);
+ ret = clk_set_rate(apll_clk, rate << 7);
+ if (ret) {
+ snd_dbg("spdif clk rate set error!!\n");
+ return ret;
+ }
+ }
+ dai_clk_spdif_count++;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int atm7059_dai_record_clk_disable(void)
+{
+ if(dai_clk_i2s_count > 0)
+ {
+ dai_clk_i2s_count--;
+ return 0;
+ }
+
+ //module_clk_disable(MOD_ID_I2SRX);
+ //module_clk_disable(MOD_ID_I2STX);
+
+ return 0;
+}
+
+static int atm7059_dai_clk_disable(int mode)
+{
+ switch (mode) {
+ case O_MODE_I2S:
+ /* we disable the i2s_clk in another place */
+ /*
+ apll_clk = clk_get_sys(CLK_NAME_I2STX_CLK, NULL);
+ clk_disable(apll_clk);
+ apll_clk = clk_get_sys(CLK_NAME_I2SRX_CLK, NULL);
+ clk_disable(apll_clk);
+ */
+ if(dai_clk_i2s_count > 0)
+ dai_clk_i2s_count--;
+ break;
+
+ case O_MODE_HDMI:
+ if(dai_clk_hdmi_count > 0)
+ dai_clk_hdmi_count--;
+ break;
+
+ case O_MODE_SPDIF:
+ if(dai_clk_spdif_count > 0)
+ dai_clk_spdif_count--;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#if 0
+static int atm7059_i2s_4wire_config(struct atm7059_pcm_priv *pcm_priv,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+
+ pcm_priv->pc = pinctrl_get(dai->dev);
+ if (IS_ERR(pcm_priv->pc) || (pcm_priv->pc == NULL)) {
+ snd_dbg("i2s pin control failed!\n");
+ return -EAGAIN;
+ }
+
+ pcm_priv->ps = pinctrl_lookup_state(pcm_priv->pc, "default");
+ if (IS_ERR(pcm_priv->ps) || (pcm_priv->ps == NULL)) {
+ snd_dbg("i2s pin state get failed!\n");
+ return -EAGAIN;
+ }
+
+ ret = pinctrl_select_state(pcm_priv->pc, pcm_priv->ps);
+ if (ret) {
+ snd_dbg("i2s pin state set failed!\n");
+ return -EAGAIN;
+ }
+
+ /*
+ * set 4wire mode
+ snd_dai_writel(snd_dai_readl(PAD_CTL) | (0x1 << 1), PAD_CTL);
+ snd_dai_writel(snd_dai_readl(MFP_CTL0) & ~(0x1 << 2), MFP_CTL0);
+ snd_dai_writel(snd_dai_readl(MFP_CTL0) & ~(0x1 << 5), MFP_CTL0);
+ */
+
+ /* disable i2s tx&rx */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 0), I2S_CTL);
+
+ /* reset i2s rx&&tx fifo, avoid left & right channel wrong */
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) & ~(0x3 << 9) & ~0x3, I2S_FIFOCTL);
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) | (0x3 << 9) | 0x3, I2S_FIFOCTL);
+
+ /* this should before enable rx/tx,
+ or after suspend, data may be corrupt */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 11), I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | (0x1 << 11), I2S_CTL);
+ /* set i2s mode I2S_RX_ClkSel==1 */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | (0x1 << 10), I2S_CTL);
+
+ /* enable i2s rx/tx at the same time */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | 0x3, I2S_CTL);
+
+ /* i2s rx 00: 2.0-Channel Mode */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 8), I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x7 << 4), I2S_CTL);
+
+ return 0;
+}
+#endif
+
+static int atm7059_dai_record_mode_set(struct atm7059_pcm_priv *pcm_priv,
+ struct snd_soc_dai *dai)
+{
+ /*ret = atm7059_i2s_4wire_config(pcm_priv, dai);*/
+ /*snd_dai_writel(snd_dai_readl(PAD_CTL) | (0x1 << 1), PAD_CTL);*/
+ if (dai_mode_i2s_count == 0) {
+// set_dai_reg_base(GPIO_MFP_NUM);
+// snd_dai_writel(snd_dai_readl(MFP_CTL0) & ~(0x1 << 2), MFP_CTL0);
+// snd_dai_writel(snd_dai_readl(MFP_CTL0) & ~(0x3 << 3), MFP_CTL0);
+
+ /* disable i2s tx&rx */
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 0), I2S_CTL);
+
+ /* reset i2s rx&&tx fifo, avoid left & right channel wrong */
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL)
+ & ~(0x3 << 9) & ~0x3, I2S_FIFOCTL);
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL)
+ | (0x3 << 9) | 0x3, I2S_FIFOCTL);
+
+ /* this should before enable rx/tx,
+ or after suspend, data may be corrupt */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 11), I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | (0x1 << 11), I2S_CTL);
+ /* set i2s mode I2S_RX_ClkSel==1 */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | (0x1 << 10), I2S_CTL);
+
+ /* enable i2s rx/tx at the same time */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | 0x3, I2S_CTL);
+
+ /* i2s rx 00: 2.0-Channel Mode */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 8), I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x7 << 4), I2S_CTL);
+ }
+ dai_mode_i2s_count++;
+
+ return 0;
+}
+
+
+static int atm7059_dai_mode_set(struct atm7059_pcm_priv *pcm_priv,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+
+ switch (pcm_priv->output_mode) {
+ case O_MODE_I2S:
+ /*ret = atm7059_i2s_4wire_config(pcm_priv, dai);*/
+ /*snd_dai_writel(snd_dai_readl(PAD_CTL) | (0x1 << 1), PAD_CTL);*/
+ if (dai_mode_i2s_count == 0) {
+// set_dai_reg_base(GPIO_MFP_NUM);
+// snd_dai_writel(snd_dai_readl(MFP_CTL0) & ~(0x1 << 2), MFP_CTL0);
+// snd_dai_writel(snd_dai_readl(MFP_CTL0) & ~(0x3 << 3), MFP_CTL0);
+
+ /* disable i2s tx&rx */
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 0), I2S_CTL);
+
+ /* reset i2s rx&&tx fifo, avoid left & right channel wrong */
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL)
+ & ~(0x3 << 9) & ~0x3, I2S_FIFOCTL);
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL)
+ | (0x3 << 9) | 0x3, I2S_FIFOCTL);
+
+ /* this should before enable rx/tx,
+ or after suspend, data may be corrupt */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 11), I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | (0x1 << 11), I2S_CTL);
+ /* set i2s mode I2S_RX_ClkSel==1 */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | (0x1 << 10), I2S_CTL);
+
+ /* enable i2s rx/tx at the same time */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) | 0x3, I2S_CTL);
+
+ /* i2s rx 00: 2.0-Channel Mode */
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x3 << 8), I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~(0x7 << 4), I2S_CTL);
+ }
+ dai_mode_i2s_count++;
+ break;
+
+ case O_MODE_HDMI:
+ /* HDMI&SPDIF fifo reset */
+ if (dai_mode_hdmi_count == 0) {
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ snd_dai_writel(snd_dai_readl(SPDIF_HDMI_CTL) & ~0x3,
+ SPDIF_HDMI_CTL);
+ /* HDMI fifo enable,DRQ enable */
+ snd_dai_writel(snd_dai_readl(SPDIF_HDMI_CTL) |
+ 0x102, SPDIF_HDMI_CTL);
+ }
+ dai_mode_hdmi_count++;
+ break;
+ case O_MODE_SPDIF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int atm7059_dai_record_mode_unset(void)
+{
+ if(dai_mode_i2s_count > 0)
+ dai_mode_i2s_count--;
+ if (dai_mode_i2s_count == 0) {
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~0x3, I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) & ~0x3, I2S_FIFOCTL);
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) & ~(0x3 << 9), I2S_FIFOCTL);
+#ifdef CONFIG_SND_UBUNTU
+ is_i2s_record = 0;
+#endif
+ /*pinctrl_put(pcm_priv->pc);*/
+ }
+ return 0;
+}
+
+static int atm7059_dai_mode_unset(struct atm7059_pcm_priv *pcm_priv)
+{
+ switch (pcm_priv->output_mode) {
+ case O_MODE_I2S:
+ if(dai_mode_i2s_count > 0)
+ dai_mode_i2s_count--;
+ if (dai_mode_i2s_count == 0) {
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ snd_dai_writel(snd_dai_readl(I2S_CTL) & ~0x3, I2S_CTL);
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) & ~0x3, I2S_FIFOCTL);
+ snd_dai_writel(snd_dai_readl(I2S_FIFOCTL) & ~(0x3 << 9), I2S_FIFOCTL);
+ /*pinctrl_put(pcm_priv->pc);*/
+#ifdef CONFIG_SND_UBUNTU
+ is_i2s_playback = 0;
+#endif
+ }
+ break;
+ case O_MODE_HDMI:
+ /* HDMI fifo disable */
+ if(dai_mode_hdmi_count > 0)
+ dai_mode_hdmi_count--;
+ if (dai_mode_hdmi_count == 0) {
+ set_dai_reg_base(I2S_SPDIF_NUM);
+ snd_dai_writel(snd_dai_readl(SPDIF_HDMI_CTL) & ~0x2, SPDIF_HDMI_CTL);
+ }
+ break;
+ case O_MODE_SPDIF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int atm7059_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct atm7059_pcm_priv *pcm_priv
+ = snd_soc_platform_get_drvdata(platform);
+
+#ifdef CONFIG_SND_UBUNTU
+ if(((pcm_priv->output_mode == O_MODE_I2S)&&(is_i2s_playback == 1)) ||
+ (is_i2s_record == 1))
+ {
+ //snd_err("param setted\n");
+ return 0;
+ }
+#endif
+
+// if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+// //snd_err("playback hw param\n");
+// }
+// else
+// {
+// //snd_err("record hw param\n");
+// }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ #ifdef CONFIG_SND_UBUNTU
+ printk(KERN_ERR"%s,SNDRV_PCM_FORMAT_S16_LE\n", __func__);
+ break;
+ #endif
+ case SNDRV_PCM_FORMAT_S32_LE:
+ #ifdef CONFIG_SND_UBUNTU
+ printk(KERN_ERR"%s,SNDRV_PCM_FORMAT_S32_LE\n", __func__);
+ #endif
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (SNDRV_PCM_STREAM_CAPTURE == substream->stream ) {
+ atm7059_dai_record_clk_set(pcm_priv->output_mode, params_rate(params));
+ atm7059_dai_record_mode_set(pcm_priv, dai);
+#ifdef CONFIG_SND_UBUNTU
+ is_i2s_record = 1;
+#endif
+ }
+
+ if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream ) {
+ atm7059_dai_clk_set(pcm_priv->output_mode, params_rate(params));
+ atm7059_dai_mode_set(pcm_priv, dai);
+#ifdef CONFIG_SND_UBUNTU
+ if(pcm_priv->output_mode == O_MODE_I2S)
+ {
+ is_i2s_playback = 1;
+ }
+#endif
+ }
+ return 0;
+}
+
+static int atm7059_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct atm7059_pcm_priv *pcm_priv =
+ snd_soc_platform_get_drvdata(platform);
+
+
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ //snd_err("playback free\n");
+ }
+ else
+ {
+ //snd_err("record free\n");
+ }
+#ifdef CONFIG_SND_UBUNTU
+ if (SNDRV_PCM_STREAM_CAPTURE == substream->stream && is_i2s_playback == 0 )
+#else
+ if (SNDRV_PCM_STREAM_CAPTURE == substream->stream)
+#endif
+ {
+ atm7059_dai_record_clk_disable();
+ atm7059_dai_record_mode_unset();
+ }
+
+#ifdef CONFIG_SND_UBUNTU
+ if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream && is_i2s_record == 0 )
+#else
+ if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream)
+#endif
+ {
+ atm7059_dai_clk_disable(pcm_priv->output_mode);
+ atm7059_dai_mode_unset(pcm_priv);
+ }
+
+
+ return 0;
+}
+
+struct snd_soc_dai_ops atm7059_dai_dai_ops = {
+ .hw_params = atm7059_dai_hw_params,
+ .hw_free = atm7059_dai_hw_free,
+};
+
+#ifdef CONFIG_SND_UBUNTU
+static u32 i2s_ctl_reg;
+static u32 i2s_fifoctl_reg;
+static u32 hdmi_ctl_reg;
+static int dai_regs_stored = -1;
+static int atm7059_dai_store_regs(void)
+{
+ set_dai_reg_base(I2S_SPDIF_NUM);
+
+ i2s_ctl_reg = snd_dai_readl(I2S_CTL);
+ i2s_fifoctl_reg = snd_dai_readl(I2S_FIFOCTL);
+ hdmi_ctl_reg = snd_dai_readl(SPDIF_HDMI_CTL);
+ dai_regs_stored = 1;
+ return 0;
+}
+
+static int atm7059_dai_restore_regs(void)
+{
+ if(dai_regs_stored < 0)
+ {
+ printk("no initial regs value yet\n");
+ return 0;
+ }
+ set_dai_reg_base(I2S_SPDIF_NUM);
+
+ snd_dai_writel(i2s_ctl_reg, I2S_CTL);
+ snd_dai_writel(i2s_fifoctl_reg, I2S_FIFOCTL);
+ snd_dai_writel(hdmi_ctl_reg, SPDIF_HDMI_CTL);
+ dai_regs_stored = -1;
+
+ return 0;
+}
+
+static int atm7059_dai_suspend(struct snd_soc_dai *dai)
+{
+ return atm7059_dai_store_regs();
+}
+static int atm7059_dai_resume(struct snd_soc_dai *dai)
+{
+ return atm7059_dai_restore_regs();
+}
+#endif
+
+#define ATM7059_STEREO_CAPTURE_RATES SNDRV_PCM_RATE_8000_96000
+#define ATM7059_STEREO_PLAYBACK_RATES SNDRV_PCM_RATE_8000_192000
+#define ATM7059_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+struct snd_soc_dai_driver atm7059_dai = {
+ .name = "gl5203-audio-i2s",
+ .id = ATM7059_AIF_I2S,
+ .playback = {
+ .stream_name = "atm7059 dai Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = ATM7059_STEREO_PLAYBACK_RATES,
+ #ifdef CONFIG_SND_UBUNTU
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ #else
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ #endif
+ },
+ .capture = {
+ .stream_name = "atm7059 dai Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = ATM7059_STEREO_CAPTURE_RATES,
+ #ifdef CONFIG_SND_UBUNTU
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ #else
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ #endif
+ },
+#ifdef CONFIG_SND_UBUNTU
+ .suspend = atm7059_dai_suspend,
+ .resume = atm7059_dai_resume,
+#endif
+ .ops = &atm7059_dai_dai_ops,
+};
+
+static const struct snd_soc_component_driver atm7059_component = {
+ .name = "atm7059ac97c",
+};
+
+static const struct of_device_id owl_i2s_of_match[] = {
+ {.compatible = "actions,owl-audio-i2s",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, owl_i2s_of_match);
+
+static int atm7059_dai_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int i;
+ int ret = 0;
+
+ struct device_node *dn;
+
+ dn = of_find_compatible_node(NULL, NULL, "actions,owl-audio-i2s");
+ if (!dn) {
+ snd_err("Fail to get device_node actions,owl-audio-i2s\r\n");
+ //goto of_get_failed;
+ }
+
+ /*FIXME: what if error in second or third loop*/
+ //for(i=0; i<MAX_RES_NUM; i++)
+ for(i=0; i<1; i++)
+ {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ snd_err("no memory resource i=%d\n", i);
+ return -ENODEV;
+ }
+
+ if (!devm_request_mem_region (&pdev->dev, res->start,
+ resource_size(res), "owl-audio-i2s")) {
+ snd_err("Unable to request register region\n");
+ return -EBUSY;
+ }
+
+ dai_res.base[i] = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (dai_res.base[i] == NULL) {
+ snd_err("Unable to ioremap register region\n");
+ return -ENXIO;
+ }
+
+ snd_err("it's ok %d\n", i);
+ }
+
+
+ if (1)
+ {
+ for (i = 0; i < ARRAY_SIZE(dai_attr); i++)
+ {
+ ret = device_create_file(&pdev->dev, &dai_attr[i]);
+ if (ret) {
+ snd_err("Add device file failed");
+ //goto device_create_file_failed;
+ }
+ }
+ }
+ else
+ {
+ snd_err("Find device failed");
+ //goto err_bus_find_device;
+ }
+
+
+ dev_warn(&pdev->dev, "atm7059_dai_probe\n");
+ //snd_err("dai probe fine\n");
+
+ pdev->dev.init_name = "owl-audio-i2s";
+
+ return snd_soc_register_component(&pdev->dev, &atm7059_component,
+ &atm7059_dai, 1);
+
+}
+
+static int atm7059_dai_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+ return 0;
+}
+
+
+
+static struct platform_driver atm7059_dai_driver = {
+ .driver = {
+ .name = "owl-audio-i2s",
+ .owner = THIS_MODULE,
+ .of_match_table = owl_i2s_of_match,
+ },
+
+ .probe = atm7059_dai_probe,
+ .remove = atm7059_dai_remove,
+};
+
+//static struct platform_device *atm7059_dai_device;
+
+static int __init atm7059_dai_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&atm7059_dai_driver);
+ if (ret) {
+ snd_err(
+ "ASoC: Platform driver atm7059-dai register failed\n");
+ goto err_driver_register;
+ }
+
+
+ return 0;
+
+err_driver_register:
+ return ret;
+}
+
+static void __exit atm7059_dai_exit(void)
+{
+ int i = 0;
+ struct device *dev = NULL;
+
+ dev = bus_find_device_by_name(&platform_bus_type, NULL, "owl-audio-i2s");
+
+ if (dev)
+ {
+ for (i = 0; i < ARRAY_SIZE(dai_attr); i++)
+ {
+ device_remove_file(dev, &dai_attr[i]);
+ }
+ }
+
+ platform_driver_unregister(&atm7059_dai_driver);
+ //platform_device_unregister(atm7059_dai_device);
+ //atm7059_dai_device = NULL;
+}
+
+module_init(atm7059_dai_init);
+module_exit(atm7059_dai_exit);
+
+
+/* Module information */
+MODULE_AUTHOR("sall.xie <sall.xie at actions-semi.com>");
+MODULE_DESCRIPTION("ATM7059 I2S Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atc260x/dmaengine-pcm-owl.c b/sound/soc/atc260x/dmaengine-pcm-owl.c
new file mode 100755
index 0000000..bf3ecbb
--- /dev/null
+++ b/sound/soc/atc260x/dmaengine-pcm-owl.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2012, Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars at metafoo.de>
+ *
+ * Based on:
+ * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer <s.hauer at pengutronix.de>
+ * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <buytenh at wantstofly.org>
+ * Copyright (C) 2006 Applied Data Systems
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/dmaengine.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <mach/hdmac-owl.h>
+
+struct dmaengine_pcm_runtime_data {
+ struct dma_chan *dma_chan;
+ dma_cookie_t cookie;
+
+ unsigned int pos;
+ unsigned int startflag;
+};
+
+static inline struct dmaengine_pcm_runtime_data *substream_to_prtd(
+ const struct snd_pcm_substream *substream)
+{
+ return substream->runtime->private_data;
+}
+
+struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+
+ return prtd->dma_chan;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan);
+
+/**
+ * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config
+ * @substream: PCM substream
+ * @params: hw_params
+ * @slave_config: DMA slave config
+ *
+ * This function can be used to initialize a dma_slave_config from a substream
+ * and hw_params in a dmaengine based PCM driver implementation.
+ */
+int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream,
+ const struct snd_pcm_hw_params *params,
+ struct dma_slave_config *slave_config)
+{
+ enum dma_slave_buswidth buswidth;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ slave_config->direction = DMA_MEM_TO_DEV;
+ slave_config->dst_addr_width = buswidth;
+ } else {
+ slave_config->direction = DMA_DEV_TO_MEM;
+ slave_config->src_addr_width = buswidth;
+ }
+
+ slave_config->device_fc = false;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config);
+
+/**
+ * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config
+ * using DAI DMA data.
+ * @substream: PCM substream
+ * @dma_data: DAI DMA data
+ * @slave_config: DMA slave configuration
+ *
+ * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and
+ * slave_id fields of the DMA slave config from the same fields of the DAI DMA
+ * data struct. The src and dst fields will be initialized depending on the
+ * direction of the substream. If the substream is a playback stream the dst
+ * fields will be initialized, if it is a capture stream the src fields will be
+ * initialized. The {dst,src}_addr_width field will only be initialized if the
+ * addr_width field of the DAI DMA data struct is not equal to
+ * DMA_SLAVE_BUSWIDTH_UNDEFINED.
+ */
+void snd_dmaengine_pcm_set_config_from_dai_data(
+ const struct snd_pcm_substream *substream,
+ const struct snd_dmaengine_dai_dma_data *dma_data,
+ struct dma_slave_config *slave_config)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ slave_config->dst_addr = dma_data->addr;
+ slave_config->dst_maxburst = dma_data->maxburst;
+ if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ slave_config->dst_addr_width = dma_data->addr_width;
+ } else {
+ slave_config->src_addr = dma_data->addr;
+ slave_config->src_maxburst = dma_data->maxburst;
+ if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ slave_config->src_addr_width = dma_data->addr_width;
+ }
+
+ slave_config->slave_id = dma_data->slave_id;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data);
+
+static void dmaengine_pcm_dma_complete(void *arg)
+{
+ struct snd_pcm_substream *substream = arg;
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+
+ if (PCM_RUNTIME_CHECK(substream))
+ return;
+ if(prtd->startflag == 1)
+ {
+ if (SNDRV_PCM_STREAM_CAPTURE == substream->stream )
+ {
+ prtd->pos += snd_pcm_lib_period_bytes(substream);
+ if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream))
+ prtd->pos = 0;
+ }
+ else if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
+ int prev_pos = prtd->pos;
+ int diff;
+ int this_interrupt_num;
+ int sum_periods = (snd_pcm_lib_buffer_bytes(substream)
+ /snd_pcm_lib_period_bytes(substream));
+ int remain_frame_cnt = read_remain_frame_cnt(prtd->dma_chan);
+ remain_frame_cnt = remain_frame_cnt%sum_periods;
+ prtd->pos = snd_pcm_lib_buffer_bytes(substream)
+ - (remain_frame_cnt*snd_pcm_lib_period_bytes(substream));
+ if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream))
+ prtd->pos = 0;
+
+ diff = (prtd->pos - prev_pos);
+ if(diff>0)
+ {
+ this_interrupt_num = diff/snd_pcm_lib_period_bytes(substream);
+ if((this_interrupt_num != 1))
+ printk(KERN_ERR"[audio]:[%d]lost IRQ:num = %d\n",
+ __LINE__, this_interrupt_num);
+ }
+ else
+ {
+ this_interrupt_num = (diff + snd_pcm_lib_buffer_bytes(substream))
+ /snd_pcm_lib_period_bytes(substream);
+ if((this_interrupt_num != 1))
+ printk(KERN_ERR"[audio]:[%d]lost IRQ:num = %d\n",
+ __LINE__, this_interrupt_num);
+ }
+ //printk("1\n");
+ }
+
+ snd_pcm_period_elapsed(substream);
+ }
+ else
+ {
+ //printk(KERN_ERR"5,%d\n", prtd->startflag);
+ }
+}
+
+static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct dma_chan *chan = prtd->dma_chan;
+ struct dma_async_tx_descriptor *desc;
+ enum dma_transfer_direction direction;
+ unsigned long flags = DMA_CTRL_ACK;
+
+ direction = snd_pcm_substream_to_dma_direction(substream);
+
+ if (!substream->runtime->no_period_wakeup)
+ flags |= DMA_PREP_INTERRUPT;
+
+ prtd->pos = 0;
+ desc = dmaengine_prep_dma_cyclic(chan,
+ substream->runtime->dma_addr,
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream), direction, flags);
+
+ if (!desc)
+ return -ENOMEM;
+
+ desc->callback = dmaengine_pcm_dma_complete;
+ desc->callback_param = substream;
+ prtd->cookie = dmaengine_submit(desc);
+
+ return 0;
+}
+
+/**
+ * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation
+ * @substream: PCM substream
+ * @cmd: Trigger command
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ *
+ * This function can be used as the PCM trigger callback for dmaengine based PCM
+ * driver implementations.
+ */
+int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ret = dmaengine_pcm_prepare_and_submit(substream);
+ if (ret)
+ return ret;
+ dma_async_issue_pending(prtd->dma_chan);
+ prtd->startflag = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ #ifdef CONFIG_SND_UBUNTU
+ ret = dmaengine_pcm_prepare_and_submit(substream);
+ if (ret)
+ return ret;
+ dma_async_issue_pending(prtd->dma_chan);
+ prtd->startflag = 1;
+ #else
+ dmaengine_resume(prtd->dma_chan);
+ #endif
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ #ifdef CONFIG_SND_UBUNTU
+ dmaengine_terminate_all(prtd->dma_chan);
+ prtd->startflag = 0;
+ #else
+ dmaengine_pause(prtd->dma_chan);
+ #endif
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ dmaengine_terminate_all(prtd->dma_chan);
+ prtd->startflag = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
+
+/**
+ * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation
+ * @substream: PCM substream
+ *
+ * This function is deprecated and should not be used by new drivers, as its
+ * results may be unreliable.
+ */
+snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ return bytes_to_frames(substream->runtime, prtd->pos);
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue);
+
+/**
+ * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation
+ * @substream: PCM substream
+ *
+ * This function can be used as the PCM pointer callback for dmaengine based PCM
+ * driver implementations.
+ */
+snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+
+ struct dma_tx_state state;
+ enum dma_status status;
+ unsigned int buf_size;
+ unsigned int pos = 0;
+
+ status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+ if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
+ buf_size = snd_pcm_lib_buffer_bytes(substream);
+ if (state.residue > 0 && state.residue <= buf_size)
+ pos = buf_size - state.residue;
+ }
+
+ return bytes_to_frames(substream->runtime, pos);
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
+
+/**
+ * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM
+ * @filter_fn: Filter function used to request the DMA channel
+ * @filter_data: Data passed to the DMA filter function
+ *
+ * Returns NULL or the requested DMA channel.
+ *
+ * This function request a DMA channel for usage with dmaengine PCM.
+ */
+struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
+ void *filter_data)
+{
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_CYCLIC, mask);
+
+ return dma_request_channel(mask, filter_fn, filter_data);
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
+
+/**
+ * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream
+ * @substream: PCM substream
+ * @chan: DMA channel to use for data transfers
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ *
+ * The function should usually be called from the pcm open callback. Note that
+ * this function will use private_data field of the substream's runtime. So it
+ * is not availabe to your pcm driver implementation.
+ */
+int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
+ struct dma_chan *chan)
+{
+ struct dmaengine_pcm_runtime_data *prtd;
+ int ret;
+
+ if (!chan)
+ return -ENXIO;
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (!prtd)
+ return -ENOMEM;
+
+ prtd->dma_chan = chan;
+
+ substream->runtime->private_data = prtd;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open);
+
+/**
+ * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel
+ * @substream: PCM substream
+ * @filter_fn: Filter function used to request the DMA channel
+ * @filter_data: Data passed to the DMA filter function
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ *
+ * This function will request a DMA channel using the passed filter function and
+ * data. The function should usually be called from the pcm open callback. Note
+ * that this function will use private_data field of the substream's runtime. So
+ * it is not availabe to your pcm driver implementation.
+ */
+int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream,
+ dma_filter_fn filter_fn, void *filter_data)
+{
+ return snd_dmaengine_pcm_open(substream,
+ snd_dmaengine_pcm_request_channel(filter_fn, filter_data));
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
+
+/**
+ * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream
+ * @substream: PCM substream
+ */
+int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+
+ kfree(prtd);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
+
+/**
+ * snd_dmaengine_pcm_release_chan_close - Close a dmaengine based PCM substream and release channel
+ * @substream: PCM substream
+ *
+ * Releases the DMA channel associated with the PCM substream.
+ */
+int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+
+ if(prtd->dma_chan)
+ {
+ if(prtd->dma_chan->private)
+ {
+ //printk("sndrv: free\n");
+ kfree(prtd->dma_chan->private);
+ }
+ prtd->dma_chan->private = NULL;
+ }
+
+ dma_release_channel(prtd->dma_chan);
+
+ return snd_dmaengine_pcm_close(substream);
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atc260x/hdmi-audio-owl.c b/sound/soc/atc260x/hdmi-audio-owl.c
new file mode 100755
index 0000000..37119b3
--- /dev/null
+++ b/sound/soc/atc260x/hdmi-audio-owl.c
@@ -0,0 +1,814 @@
+#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 <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/clk.h> /* clk_enable */
+#include "sndrv-owl.h"
+#include <mach/module-owl.h>
+#include "common-regs-owl.h"
+
+#include <linux/io.h>
+#include <linux/ioport.h>
+
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)&&defined(CONFIG_SND_UBUNTU)
+#include <linux/earlysuspend.h>
+ struct early_suspend early_suspend;
+ static int is_suspended = 0;
+#endif
+
+static int audio_clk_enable;
+
+#define Audio60958 1 /*1--IEC60958; 0--IEC61937*/
+#define HDMI_RAMPKT_AUDIO_SLOT 1
+#define HDMI_RAMPKT_PERIOD 1
+
+static int Speaker; //Speaker Placement, stereo
+
+/* for register io remap
+struct asoc_hdmi_resource {
+ void __iomem *base[MAX_RES_NUM];//virtual base for every resource
+ void __iomem *baseptr; //pointer to every virtual base
+ struct clk *clk;
+ int irq;
+ unsigned int setting;
+};
+
+static struct asoc_hdmi_resource hdmi_res;
+
+static void set_hdmi_reg_base(int num)
+{
+ hdmi_res.baseptr = hdmi_res.base[num];
+}
+
+static u32 snd_hdmi_readl(u32 reg)
+{
+ return readl(hdmi_res.baseptr + reg);
+}
+
+static void snd_hdmi_writel(u32 val, u32 reg)
+{
+ writel(val, hdmi_res.baseptr + reg);
+}
+*/
+#if defined(CONFIG_HAS_EARLYSUSPEND)&&defined(CONFIG_SND_UBUNTU)
+static void hdmi_audio_early_suspend(struct early_suspend *h)
+{
+ is_suspended = 1;
+}
+
+static void hdmi_audio_late_resume(struct early_suspend *h)
+{
+ is_suspended = 0;
+
+}
+#endif
+
+static ssize_t error_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", error_switch);
+ return cnt;
+}
+
+static ssize_t error_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ error_switch = tmp;
+ break;
+ default:
+ printk(KERN_ERR"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+static ssize_t debug_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", debug_switch);
+ return cnt;
+}
+
+static ssize_t debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ debug_switch = tmp;
+ break;
+ default:
+ printk(KERN_INFO"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+static struct device_attribute hdmi_attr[] = {
+ __ATTR(error, S_IRUSR | S_IWUSR, error_show, error_store),
+ __ATTR(debug, S_IRUSR | S_IWUSR, debug_show, debug_store),
+};
+
+static int hdmi_EnableWriteRamPacket(void)
+{
+ int i;
+ //set_hdmi_reg_base(0);
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_OPCR) | (0x1 << 31), (u16)HDMI_OPCR);
+ while ((hdmihw_read_reg((u16)HDMI_OPCR) & (0x1 << 31)) != 0) {
+ for (i = 0; i < 10; i++)
+ ;
+ }
+ return 0;
+}
+
+static int hdmi_SetRamPacket(unsigned no, unsigned char *pkt)
+{
+ unsigned char tpkt[36];
+ unsigned int *reg = (unsigned int *) tpkt;
+ unsigned int addr = 126 + no * 14;
+
+ if (no > 5)
+ return -EINVAL;
+
+ /**
+ * according to change by genganan 2008-09-24
+ */
+ /* Packet Header */
+ tpkt[0] = pkt[0];
+ tpkt[1] = pkt[1];
+ tpkt[2] = pkt[2];
+ tpkt[3] = 0;
+ /* Packet Word0 */
+ tpkt[4] = pkt[3];
+ tpkt[5] = pkt[4];
+ tpkt[6] = pkt[5];
+ tpkt[7] = pkt[6];
+ /* Packet Word1 */
+ tpkt[8] = pkt[7];
+ tpkt[9] = pkt[8];
+ tpkt[10] = pkt[9];
+ tpkt[11] = 0;
+ /* Packet Word2 */
+ tpkt[12] = pkt[10];
+ tpkt[13] = pkt[11];
+ tpkt[14] = pkt[12];
+ tpkt[15] = pkt[13];
+ /* Packet Word3 */
+ tpkt[16] = pkt[14];
+ tpkt[17] = pkt[15];
+ tpkt[18] = pkt[16];
+ tpkt[19] = 0;
+ /* Packet Word4 */
+ tpkt[20] = pkt[17];
+ tpkt[21] = pkt[18];
+ tpkt[22] = pkt[19];
+ tpkt[23] = pkt[20];
+ /* Packet Word5 */
+ tpkt[24] = pkt[21];
+ tpkt[25] = pkt[22];
+ tpkt[26] = pkt[23];
+ tpkt[27] = 0;
+ /* Packet Word6 */
+ tpkt[28] = pkt[24];
+ tpkt[29] = pkt[25];
+ tpkt[30] = pkt[26];
+ tpkt[31] = pkt[27];
+ /* Packet Word7 */
+ tpkt[32] = pkt[28];
+ tpkt[33] = pkt[29];
+ tpkt[34] = pkt[30];
+ tpkt[35] = 0;
+
+ /* write mode */
+ //set_hdmi_reg_base(0);
+ hdmihw_write_reg((1 << 8) | (((addr) & 0xFF) << 0), (u16)HDMI_OPCR);
+ hdmihw_write_reg(reg[0], (u16)HDMI_ORP6PH);
+ hdmihw_write_reg(reg[1], (u16)HDMI_ORSP6W0);
+ hdmihw_write_reg(reg[2], (u16)HDMI_ORSP6W1);
+ hdmihw_write_reg(reg[3], (u16)HDMI_ORSP6W2);
+ hdmihw_write_reg(reg[4], (u16)HDMI_ORSP6W3);
+ hdmihw_write_reg(reg[5], (u16)HDMI_ORSP6W4);
+ hdmihw_write_reg(reg[6], (u16)HDMI_ORSP6W5);
+ hdmihw_write_reg(reg[7], (u16)HDMI_ORSP6W6);
+ hdmihw_write_reg(reg[8], (u16)HDMI_ORSP6W7);
+
+ hdmi_EnableWriteRamPacket();
+
+ return 0;
+}
+
+static int hdmi_SetRamPacketPeriod(unsigned int no, int period)
+{
+ if (no > 5)
+ return -EINVAL;
+
+ if ((period > 0xf) || (period < 0))
+ return -EINVAL;
+
+ /* disable */
+ //set_hdmi_reg_base(0);
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_RPCR) & (unsigned int) (~(1 << no)),
+ (u16)HDMI_RPCR);
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_RPCR) &
+ (unsigned int) (~(0xf << (no * 4 + 8))),
+ (u16)HDMI_RPCR);
+
+ if (period != 0) {
+ /* enable and set period */
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_RPCR) |
+ (unsigned int) (period << (no * 4 + 8)),
+ (u16)HDMI_RPCR);
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_RPCR) | (unsigned int) (1 << no),
+ (u16)HDMI_RPCR);
+ }
+ return 0;
+}
+
+static int hdmi_gen_audio_infoframe(int audio_channel)
+{
+ unsigned char pkt[32];
+ unsigned int checksum = 0;
+ int i;
+ memset(pkt, 0, 32);
+ /* header */
+ pkt[0] = 0x80 | 0x04;
+ pkt[1] = 1;
+ pkt[2] = 0x1f & 10;
+ pkt[3] = 0x00;
+ pkt[4] = audio_channel & 0x7;
+ pkt[5] = 0x0;
+ pkt[6] = 0x0;
+ pkt[7] = Speaker;
+ pkt[8] = (0x0 << 7) | (0x0 << 3);
+
+ /* count checksum */
+ for (i = 0; i < 31; i++)
+ checksum += pkt[i];
+
+ pkt[3] = (unsigned char)((~checksum + 1) & 0xff);
+ /* set to RAM Packet */
+ hdmi_SetRamPacket(HDMI_RAMPKT_AUDIO_SLOT, pkt);
+ hdmi_SetRamPacketPeriod(HDMI_RAMPKT_AUDIO_SLOT,
+ HDMI_RAMPKT_PERIOD);
+ return 0;
+}
+void set_hdmi_audio_interface(int channel, int samplerate)
+{
+ unsigned int tmp03;
+ unsigned int tmp47;
+ unsigned int CRP_N = 0;
+ unsigned int ASPCR = 0;
+ unsigned int ACACR = 0;
+
+ //改变音频相关参数时需要首先disable audio, 配置完成后enable
+ //snd_err("HDMI_ICR 0x%x", hdmihw_read_reg((u16)HDMI_ICR));
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_ICR) & ~(0x1 << 25), (u16)HDMI_ICR);
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_ACRPCR) | (0x1 << 31), (u16)HDMI_ACRPCR);
+ hdmihw_read_reg((u16)HDMI_ACRPCR); /*flush write buffer effect*/
+
+ //set_hdmi_reg_base(0);
+ tmp03 = hdmihw_read_reg((u16)HDMI_AICHSTABYTE0TO3);
+ tmp03 &= (~(0xf << 24));
+
+ tmp47 = hdmihw_read_reg((u16)HDMI_AICHSTABYTE4TO7);
+ tmp47 &= (~(0xf << 4));
+ tmp47 |= 0xb;
+
+ switch (samplerate) {
+ /* 32000, 44100, 48000, 88200, 96000,
+ 176400, 192000, 352.8kHz, 384kHz */
+ case 1:
+ tmp03 |= (0x3 << 24);
+ tmp47 |= (0xc << 4);
+ CRP_N = 4096;
+ break;
+
+ case 2:
+ tmp03 |= (0x0 << 24);
+ tmp47 |= (0xf << 4);
+ CRP_N = 6272;
+ break;
+
+ case 3:
+ tmp03 |= (0x2 << 24);
+ tmp47 |= (0xd << 4);
+ CRP_N = 6144;
+ break;
+
+ case 4:
+ tmp03 |= (0x8 << 24);
+ tmp47 |= (0x7 << 4);
+ CRP_N = 12544;
+ break;
+
+ case 5:
+ tmp03 |= (0xa << 24);
+ tmp47 |= (0x5 << 4);
+ CRP_N = 12288;
+ break;
+
+ case 6:
+ tmp03 |= (0xc << 24);
+ tmp47 |= (0x3 << 4);
+ CRP_N = 12288;
+ break;
+
+ case 7:
+ tmp03 |= (0xe << 24);
+ tmp47 |= (0x1 << 4);
+ CRP_N = 24576;
+ break;
+
+ case 8:
+ tmp03 |= (0x1 << 24);
+ CRP_N = 12544;
+ break;
+
+ case 9:
+ tmp03 |= (0x1 << 24);
+ CRP_N = 12288;
+ break;
+
+ default:
+ break;
+ }
+ hdmihw_write_reg(tmp03, (u16)HDMI_AICHSTABYTE0TO3);
+ hdmihw_write_reg(tmp47, (u16)HDMI_AICHSTABYTE4TO7);
+
+ hdmihw_write_reg(0x0, (u16)HDMI_AICHSTABYTE8TO11);
+ hdmihw_write_reg(0x0, (u16)HDMI_AICHSTABYTE12TO15);
+ hdmihw_write_reg(0x0, (u16)HDMI_AICHSTABYTE16TO19);
+ hdmihw_write_reg(0x0, (u16)HDMI_AICHSTABYTE20TO23);
+
+ switch (channel) {
+ case 2:
+ hdmihw_write_reg(0x20001, (u16)HDMI_AICHSTASCN);
+ break;
+
+ case 3:
+ hdmihw_write_reg(0x121, (u16)HDMI_AICHSTASCN);
+ break;
+
+ case 4:
+ hdmihw_write_reg(0x2121, (u16)HDMI_AICHSTASCN);
+ break;
+
+ case 5:
+ hdmihw_write_reg(0x12121, (u16)HDMI_AICHSTASCN);
+ break;
+
+ case 6:
+ hdmihw_write_reg(0x212121, (u16)HDMI_AICHSTASCN);
+ break;
+
+ case 7:
+ hdmihw_write_reg(0x1212121, (u16)HDMI_AICHSTASCN);
+ break;
+
+ case 8:
+ hdmihw_write_reg(0x21212121, (u16)HDMI_AICHSTASCN);
+ break;
+
+ default:
+ break;
+ }
+ /* TODO samplesize 16bit, 20bit */
+ /* 24 bit */
+ hdmihw_write_reg((hdmihw_read_reg((u16)HDMI_AICHSTABYTE4TO7) & ~0xf),
+ (u16)HDMI_AICHSTABYTE4TO7);
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_AICHSTABYTE4TO7) | 0xb, (u16)HDMI_AICHSTABYTE4TO7);
+ if (Audio60958 == 1) {
+ switch (channel) {
+ case 2:
+ ASPCR = 0x00000011;
+ ACACR = 0xfac688;
+ Speaker = 0x0;
+ break;
+
+ case 3:
+ ASPCR = 0x0002d713;
+ ACACR = 0x4008;
+ Speaker = 0x1;
+ break;
+
+ case 4:
+ ASPCR = 0x0003df1b;
+ ACACR = 0x4608;
+ Speaker = 0x3;
+ break;
+
+ case 5:
+ ASPCR = 0x0003df3b;
+ ACACR = 0x2c608;
+ Speaker = 0x7;
+ break;
+
+ case 6:
+ ASPCR = 0x0003df3f;
+ ACACR = 0x2c688;
+ Speaker = 0xb;
+ break;
+
+ case 7:
+ ASPCR = 0x0007ff7f;
+ ACACR = 0x1ac688;
+ Speaker = 0xf;
+ break;
+
+ case 8:
+ ASPCR = 0x0007ffff;
+ ACACR = 0xfac688;
+ Speaker = 0x13;
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ ASPCR = 0x7f87c003;
+ ACACR = 0xfac688;
+
+ tmp03 = hdmihw_read_reg((u16)HDMI_AICHSTABYTE0TO3);
+ tmp03 |= 0x1;
+ hdmihw_write_reg(tmp03, (u16)HDMI_AICHSTABYTE0TO3);
+ hdmihw_write_reg(0x21, (u16)HDMI_AICHSTASCN);
+ }
+
+ /* enable Audio FIFO_FILL disable wait cycle */
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_CR) | 0x50, (u16)HDMI_CR);
+
+ if (samplerate > 7)
+ ASPCR |= (0x1 << 31);
+
+ hdmihw_write_reg(ASPCR, (u16)HDMI_ASPCR);
+ hdmihw_write_reg(ACACR, (u16)HDMI_ACACR);
+ /*非压缩格式23~30位写0
+ * 如果针对压缩码流,
+ 则HDMI_AICHSTABYTE0TO3的bit[1:0]=0x2(5005新加);
+ * 如果针对线性PCM码流,则HDMI_AICHSTABYTE0TO3
+ 的bit[1:0]=0x0(同227A);
+ */
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_AICHSTABYTE0TO3) & ~0x3,
+ (u16)HDMI_AICHSTABYTE0TO3);
+
+ /* 如果针对压缩码流,则
+ HDMI_ASPCR的bit[30:23]=0xff(5005新加);
+ * 如果针对线性PCM码流,
+ 则HDMI_ASPCR的bit[30:23]=0x0(同227A);
+ */
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_ASPCR) & ~(0xff << 23), (u16)HDMI_ASPCR);
+
+ hdmihw_write_reg(CRP_N | (0x1 << 31), (u16)HDMI_ACRPCR);
+ hdmi_gen_audio_infoframe(channel - 1);
+
+ /*****配置完音频相关参数后enable audio*/
+ /* enable CRP */
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_ACRPCR) & ~(0x1 << 31), (u16)HDMI_ACRPCR);
+
+ /* enable Audio Interface */
+ hdmihw_write_reg(hdmihw_read_reg((u16)HDMI_ICR) | (0x1 << 25), (u16)HDMI_ICR);
+}
+
+static int get_hdmi_audio_fs(int sample_rate)
+{
+ int AudioFS;
+ int fs = sample_rate / 1000;
+
+ /* for O_MODE_HDMI */
+ /* 32000, 44100, 48000, 88200, 96000, 176400,
+ 192000, 352.8kHz, 384kHz */
+ switch (fs) {
+ case 32:
+ AudioFS = 1;
+ break;
+ case 44:
+ AudioFS = 2;
+ break;
+ case 48:
+ AudioFS = 3;
+ break;
+ case 88:
+ AudioFS = 4;
+ break;
+ case 96:
+ AudioFS = 5;
+ break;
+ case 176:
+ AudioFS = 6;
+ break;
+ case 192:
+ AudioFS = 7;
+ break;
+ case 352:
+ AudioFS = 8;
+ break;
+ case 384:
+ AudioFS = 9;
+ break;
+ default:
+ AudioFS = 2;
+ break;
+ }
+ return AudioFS;
+}
+
+static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ int rate = params_rate(params);
+ int audio_fs = get_hdmi_audio_fs(rate);
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)&&defined(CONFIG_SND_UBUNTU)
+ if(is_suspended == 1){
+ printk(KERN_ERR"system has suspended!\n");
+ return 0;
+ }
+#endif
+ if (audio_clk_enable == 0) {
+ module_clk_enable(MOD_ID_HDMIA);
+ audio_clk_enable = 1;
+ }
+ /* we set the hdmi audio channels stereo now*/
+ set_hdmi_audio_interface(2, audio_fs);
+ #ifdef CONFIG_SND_UBUNTU
+ audio_set_output_mode(substream, O_MODE_HDMI);
+ #endif
+ return 0;
+}
+static int hdmi_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+
+ if (audio_clk_enable == 1) {
+ module_clk_disable(MOD_ID_HDMIA);
+ audio_clk_enable = 0;
+ }
+
+ return 0;
+}
+
+static int hdmi_audio_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ /* nothing should to do here now */
+ return 0;
+}
+
+static int hdmi_audio_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ /* nothing should to do here now */
+ return 0;
+}
+
+static int hdmi_audio_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ /* nothing should to do here now */
+ return 0;
+}
+
+#define atm7059_HDMI_RATES SNDRV_PCM_RATE_8000_192000
+#ifdef CONFIG_SND_UBUNTU
+#define atm7059_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S32_LE \
+| SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+#else
+#define atm7059_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \
+| SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+#endif
+
+struct snd_soc_dai_ops hdmi_aif_dai_ops = {
+ .hw_params = hdmi_audio_hw_params,
+ .prepare = hdmi_audio_prepare,
+ .set_fmt = hdmi_audio_set_dai_fmt,
+ .set_sysclk = hdmi_audio_set_dai_sysclk,
+ .hw_free = hdmi_dai_hw_free,
+};
+
+struct snd_soc_dai_driver codec_hdmi_dai[] = {
+ {
+ .name = "atm7059-hdmi-dai",
+ .id = ATM7059_AIF_HDMI,
+ .playback = {
+ .stream_name = "atm7059 hdmi Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = atm7059_HDMI_RATES,
+ .formats = atm7059_HDMI_FORMATS,
+ },
+ .ops = &hdmi_aif_dai_ops,
+ },
+};
+
+
+
+static int codec_hdmi_probe(struct snd_soc_codec *codec)
+{
+ /* nothing should to do here now */
+ snd_dbg("codec_hdmi_probe!\n");
+ return 0;
+}
+
+static int codec_hdmi_remove(struct snd_soc_codec *codec)
+{
+ /* nothing should to do here now */
+ return 0;
+}
+
+
+
+static struct snd_soc_codec_driver soc_codec_hdmi = {
+ .probe = codec_hdmi_probe,
+ .remove = codec_hdmi_remove,
+};
+
+static int atm7059_hdmi_probe(struct platform_device *pdev)
+{
+/*
+ struct resource *res;
+ int i;
+ int ret;
+
+ struct device_node *dn;
+
+ dn = of_find_compatible_node(NULL, NULL, "actions,gl5203-audio-hdmi");
+ if (!dn) {
+ snd_err("Fail to get device_node actions,atm7039c-hdmi\r\n");
+ //goto of_get_failed;
+ }
+
+ for(i=0; i<1; i++)
+ {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ snd_err("no memory resource\n");
+ return -ENODEV;
+ }
+
+ if (!devm_request_mem_region (&pdev->dev, res->start,
+ resource_size(res), "gl5203-audio-hdmi")) {
+ snd_err("Unable to request register region\n");
+ return -EBUSY;
+ }
+
+ hdmi_res.base[i] = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (hdmi_res.base[i] == NULL) {
+ snd_err("Unable to ioremap register region\n");
+ return -ENXIO;
+ }
+ }
+
+ if (1)
+ {
+ for (i = 0; i < ARRAY_SIZE(hdmi_attr); i++)
+ {
+ ret = device_create_file(&pdev->dev, &hdmi_attr[i]);
+ if (ret) {
+ snd_err("Add device file failed");
+ //goto device_create_file_failed;
+ }
+ }
+ }
+ else
+ {
+ snd_err("Find device failed");
+ //goto err_bus_find_device;
+ }
+*/
+#if defined(CONFIG_HAS_EARLYSUSPEND)&&defined(CONFIG_SND_UBUNTU)
+ early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ //ts->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1;
+ early_suspend.suspend = hdmi_audio_early_suspend;
+ early_suspend.resume = hdmi_audio_late_resume;
+ register_early_suspend(&early_suspend);
+#endif
+
+ dev_warn(&pdev->dev,
+ "atm7059_hdmi_probe!!\n");
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_hdmi,
+ codec_hdmi_dai, ARRAY_SIZE(codec_hdmi_dai));
+}
+
+static int atm7059_hdmi_remove(struct platform_device *pdev)
+{
+#if defined(CONFIG_HAS_EARLYSUSPEND)&&defined(CONFIG_SND_UBUNTU)
+ unregister_early_suspend(&early_suspend);
+#endif
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id gl5203_hdmi_of_match[] = {
+ {.compatible = "actions,gl5203-audio-hdmi",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, gl5203_hdmi_of_match);
+
+
+static struct platform_driver atm7059_hdmi_driver = {
+ .driver = {
+ .name = "atm7059-hdmi-audio",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = atm7059_hdmi_probe,
+ .remove = atm7059_hdmi_remove,
+};
+
+static struct platform_device *atm7059_hdmi_device;
+static int __init atm7059_hdmi_init(void)
+{
+ int ret;
+ int i = 0;
+
+ atm7059_hdmi_device = platform_device_alloc("atm7059-hdmi-audio", -1);
+ if (!atm7059_hdmi_device) {
+ snd_err(
+ "ASoC: Platform device atm7059-hdmi-audio allocation failed\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = platform_device_add(atm7059_hdmi_device);
+ if (ret) {
+ snd_err(
+ "ASoC: Platform device atm7059-hdmi-audio add failed\n");
+ goto err_device_add;
+ }
+
+ ret = platform_driver_register(&atm7059_hdmi_driver);
+ if (ret) {
+ snd_err(
+ "ASoC: Platform driver atm7059-hdmi-audio register failed\n");
+ goto err_driver_register;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_attr); i++) {
+ ret = device_create_file(
+ &atm7059_hdmi_device->dev, &hdmi_attr[i]);
+ if (ret) {
+ snd_err("Add device file failed");
+ goto device_create_file_failed;
+ }
+ }
+
+ return 0;
+
+device_create_file_failed:
+err_driver_register:
+ platform_device_unregister(atm7059_hdmi_device);
+
+err_device_add:
+ platform_device_put(atm7059_hdmi_device);
+
+err:
+ return ret;
+}
+static void __exit atm7059_hdmi_exit(void)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_attr); i++) {
+ device_remove_file(&atm7059_hdmi_device->dev, &hdmi_attr[i]);
+ }
+
+ platform_driver_unregister(&atm7059_hdmi_driver);
+ platform_device_unregister(atm7059_hdmi_device);
+ atm7059_hdmi_device = NULL;
+}
+
+module_init(atm7059_hdmi_init);
+module_exit(atm7059_hdmi_exit);
+
+MODULE_AUTHOR("sall.xie <sall.xie at actions-semi.com>");
+MODULE_DESCRIPTION("atm7059 HDMI AUDIO module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atc260x/link-owl.c b/sound/soc/atc260x/link-owl.c
new file mode 100755
index 0000000..3d8e1ea
--- /dev/null
+++ b/sound/soc/atc260x/link-owl.c
@@ -0,0 +1,642 @@
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <mach/switch.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include "sndrv-owl.h"
+
+#include <linux/mfd/atc260x/atc260x.h>
+
+extern int atc2603a_audio_get_pmu_status(void);
+extern int atc2603c_audio_get_pmu_status(void);
+
+
+
+#define SOUND_MAJOR 14
+#define SNDRV_MAJOR SOUND_MAJOR
+#define SNDRV_NAME "sound"
+
+const char *earphone_ctrl_link_name = "earphone_detect_gpios";
+const char *speaker_ctrl_link_name = "speaker_en_gpios";
+const char *audio_atc2603a_link_node = "actions,atc2603a-audio";
+const char *audio_atc2603c_link_node = "actions,atc2603c-audio";
+
+static int earphone_gpio_num = -1;
+enum of_gpio_flags earphone_gpio_level;
+static int speaker_gpio_num;
+static enum of_gpio_flags speaker_gpio_level;
+static int speaker_gpio_active;
+static int flag = 0;
+static bool speaker_exist=true;
+
+typedef struct {
+ struct switch_dev sdev;
+ struct delayed_work dwork;
+ struct workqueue_struct *wq;
+} headphone_dev_t;
+
+static headphone_dev_t headphone_sdev;
+
+static ssize_t error_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", error_switch);
+ return cnt;
+}
+
+static ssize_t error_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ error_switch = tmp;
+ break;
+ default:
+ printk(KERN_ERR"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+
+static ssize_t debug_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", debug_switch);
+ return cnt;
+}
+
+static ssize_t debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ debug_switch = tmp;
+ break;
+ default:
+ printk(KERN_INFO"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+static struct device_attribute link_attr[] = {
+ __ATTR(error, S_IRUSR | S_IWUSR, error_show, error_store),
+ __ATTR(debug, S_IRUSR | S_IWUSR, debug_show, debug_store),
+};
+
+static void set_pa_onoff(int status)
+{
+ int ret;
+ if(speaker_exist){
+ if(status){
+ ret = gpio_direction_output(speaker_gpio_num, speaker_gpio_active);
+ }else{
+ ret = gpio_direction_output(speaker_gpio_num, !speaker_gpio_active);
+ }
+
+ }
+
+}
+
+static int speaker_gpio_get(struct snd_kcontrol * kcontrol,
+ struct snd_ctl_elem_value * ucontrol)
+{
+
+ int state = 0;
+ if(speaker_exist) {
+ state = !!(gpio_get_value_cansleep(speaker_gpio_num));
+ ucontrol->value.bytes.data[0] = state;
+ }
+ return 0;
+}
+static int speaker_gpio_put(struct snd_kcontrol * kcontrol,
+ struct snd_ctl_elem_value * ucontrol)
+{
+
+ int state = 0;
+ state = ucontrol->value.bytes.data[0];
+ set_pa_onoff(state);
+ return 0;
+}
+
+static const struct snd_kcontrol_new owl_outpa_controls[] = {
+ SOC_SINGLE_BOOL_EXT("speaker on off switch",
+ 0, speaker_gpio_get, speaker_gpio_put),
+};
+
+struct device_node *atm7059_audio_get_device_node(const char *name)
+{
+ struct device_node *dn;
+
+ dn = of_find_compatible_node(NULL, NULL, name);
+ if (!dn) {
+ snd_err("Fail to get device_node\r\n");
+ goto fail;
+ }
+
+ return dn;
+fail:
+ return NULL;
+}
+
+/*earphone gpio worked as a external interrupt */
+static int atm7059_audio_gpio_init(struct device_node *dn,
+ const char *name,enum of_gpio_flags *flags)
+{
+ int gpio;
+ int ret = 0;
+
+ if (!of_find_property(dn, name, NULL)) {
+ snd_err("find %s property fail\n", name);
+ goto fail;
+ }
+
+ gpio = of_get_named_gpio_flags(dn, name, 0, flags);
+ if (gpio < 0) {
+ snd_err("get gpio[%s] fail\r\n", name);
+ goto fail;
+ }
+
+ ret = gpio_request(gpio, name);
+ if (ret) {
+ snd_err("GPIO[%d] request failed\r\n", gpio);
+ goto fail;
+ }
+ return gpio;
+
+fail:
+ return -ENOMEM;
+}
+
+static int earphone_is_in(void)
+{
+ int state = 0;
+ if (earphone_gpio_num < 0)
+ {
+ //use irq to detect earphone
+ }
+ else
+ {
+ //use gpio to detect earphone
+ state = !!(gpio_get_value_cansleep(earphone_gpio_num));
+ state ^= earphone_gpio_level;
+ }
+ return state;
+}
+
+static void gl5203_earphone_monitor(struct work_struct *work)
+{
+ if (earphone_is_in())
+ switch_set_state(&headphone_sdev.sdev, SPEAKER_ON);
+ else
+ switch_set_state(&headphone_sdev.sdev, HEADSET_NO_MIC);
+
+ if(flag == 0)
+ queue_delayed_work(headphone_sdev.wq,
+ &headphone_sdev.dwork, msecs_to_jiffies(200));
+}
+
+static int atm7059_set_gpio_ear_detect(void)
+{
+ headphone_sdev.wq = create_singlethread_workqueue("earphone_detect_wq");
+ if ( !headphone_sdev.wq ) {
+ snd_err("Create workqueue failed");
+ goto create_workqueue_failed;
+ }
+
+ INIT_DELAYED_WORK(&headphone_sdev.dwork, gl5203_earphone_monitor);
+ queue_delayed_work(headphone_sdev.wq,
+ &headphone_sdev.dwork, msecs_to_jiffies(200));
+
+ return 0;
+create_workqueue_failed:
+ return -ENODEV;
+}
+
+static int atm7059_link_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;
+ snd_dbg("###atm7059_link_hw_params\n");
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops atm7059_link_ops = {
+ .hw_params = atm7059_link_hw_params,
+};
+
+/*
+ * Logic for a link as connected on a atm7059 board.
+ */
+static int atm7059_link_snd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ snd_dbg("atm7059_link_init() called\n");
+
+ return 0;
+}
+
+static struct snd_soc_dai_link atm7059_atc2603a_link_dai[] = {
+ {
+ .name = "ATM7059 ATC2603A",
+ .stream_name = "ATC2603A PCM",
+ .cpu_dai_name = "owl-audio-i2s",
+ .codec_dai_name = "atc2603a-dai",
+ .init = atm7059_link_snd_init,
+ .platform_name = "atm7059-pcm-audio",
+ .codec_name = "atc260x-audio",
+ .ops = &atm7059_link_ops,
+ },
+
+ {
+ .name = "ATM7059 HDMI AUDIO",
+ .stream_name = "HDMI PCM",
+ .cpu_dai_name = "owl-audio-i2s",
+ .codec_dai_name = "atm7059-hdmi-dai",
+ .init = atm7059_link_snd_init,
+ .platform_name = "atm7059-pcm-audio",
+ .codec_name = "atm7059-hdmi-audio",
+ .ops = &atm7059_link_ops,
+ }
+};
+
+static struct snd_soc_dai_link atm7059_atc2603c_link_dai[] = {
+ {
+ .name = "ATM7059 ATC2603C",
+ .stream_name = "ATC2603C PCM",
+ .cpu_dai_name = "owl-audio-i2s",
+ .codec_dai_name = "atc2603c-dai",
+ .init = atm7059_link_snd_init,
+ .platform_name = "atm7059-pcm-audio",
+ .codec_name = "atc260x-audio",
+ .ops = &atm7059_link_ops,
+ },
+
+ {
+ .name = "ATM7059 HDMI AUDIO",
+ .stream_name = "HDMI PCM",
+ .cpu_dai_name = "owl-audio-i2s",
+ .codec_dai_name = "atm7059-hdmi-dai",
+ .init = atm7059_link_snd_init,
+ .platform_name = "atm7059-pcm-audio",
+ .codec_name = "atm7059-hdmi-audio",
+ .ops = &atm7059_link_ops,
+ }
+};
+
+
+static struct snd_soc_card snd_soc_atm7059_atc2603a_link = {
+ .name = "atm7059_link",
+ .owner = THIS_MODULE,
+ .dai_link = atm7059_atc2603a_link_dai,
+ .num_links = ARRAY_SIZE(atm7059_atc2603a_link_dai),
+ .controls = owl_outpa_controls,
+ .num_controls = ARRAY_SIZE(owl_outpa_controls),
+};
+
+static struct snd_soc_card snd_soc_atm7059_atc2603c_link = {
+ .name = "atm7059_link",
+ .owner = THIS_MODULE,
+ .dai_link = atm7059_atc2603c_link_dai,
+ .num_links = ARRAY_SIZE(atm7059_atc2603c_link_dai),
+ .controls = owl_outpa_controls,
+ .num_controls = ARRAY_SIZE(owl_outpa_controls),
+};
+
+
+static struct platform_device *atm7059_link_snd_device;
+/*
+static int compare_audio_device_name(struct device *dev, void *data)
+{
+ const char *name = (const char *)data;
+ char *device_name = dev_name(dev);
+ char *match_start = strrchr(device_name, '.');
+
+ snd_err("device_name %s, match_start %x\n", device_name, match_start);
+ if(match_start==NULL)
+ {
+ if(strcmp(name, device_name)==0)
+ {
+ //got a match
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ if((int)(match_start-device_name+1) >= (int)strlen(device_name))
+ {
+ //should not happen but in case
+ return 0;
+ }
+
+ snd_err("name %s to match %s\n", name, match_start+1);
+ if(strcmp(name, match_start+1)==0)
+ {
+ //got a match
+ return 1;
+ }
+
+ return 0;
+}
+*/
+
+static int dbgflag;
+
+
+static ssize_t dbgflag_show_file (struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d", dbgflag);
+}
+
+static ssize_t dbgflag_store_file(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ char *endp;
+ size_t size;
+ int flag = 0;
+
+ if (count > 0) {
+
+ flag = simple_strtoul(buf,&endp,0);
+ size = endp - buf;
+
+ if (*endp && (((*endp)==0x20)||((*endp)=='\n')||((*endp)=='\t')))
+ size++;
+ if (size != count)
+ {
+ snd_err("%s %d %s, %d", __FUNCTION__, __LINE__, buf, flag);
+ return -EINVAL;
+ }
+ }
+
+ dbgflag = flag;
+
+ return 0;
+}
+
+static DEVICE_ATTR(dbgflag, S_IRUGO | S_IWUSR, dbgflag_show_file, dbgflag_store_file);
+
+//david add for i2s switch.
+static int i2s_switch_gpio_num = -1;
+enum of_gpio_flags i2s_switch_gpio_level;
+const char *i2s_switch_gpio_name = "i2s_switch_gpio";
+static int i2s_switch_gpio_active;
+
+static int __init atm7059_link_init(void)
+{
+ int i = 0;
+ int ret = 0;
+ struct device_node *dn = NULL;
+ int pmu_type;
+
+ snd_err("atm7059_link_init\n");
+
+ //20141013 yuchen: check pmu type to select correct codec param
+ pmu_type = atc2603a_audio_get_pmu_status();
+ if(pmu_type == ATC260X_ICTYPE_2603A)
+ {
+ dn = atm7059_audio_get_device_node(audio_atc2603a_link_node);
+ if (!dn)
+ goto no_device_node;
+ }
+
+ if(pmu_type == PMU_NOT_USED)
+ {
+ pmu_type = atc2603c_audio_get_pmu_status();
+ if(pmu_type == ATC260X_ICTYPE_2603C)
+ {
+ dn = atm7059_audio_get_device_node(audio_atc2603c_link_node);
+ if (!dn)
+ goto no_device_node;
+ }
+ }
+
+ if(pmu_type == PMU_NOT_USED)
+ {
+ snd_err("ASoC: No PMU type detected!\n");
+ goto no_device_node;
+ }
+
+ /*****************20151012 david add***************/
+ printk(KERN_ERR"%s,%d\n", __func__, __LINE__);
+ i2s_switch_gpio_num =
+ atm7059_audio_gpio_init(dn, i2s_switch_gpio_name, &i2s_switch_gpio_level);
+ if(i2s_switch_gpio_num > 0){
+ printk(KERN_ERR"%s,%d,num:%d\n", __func__, __LINE__, i2s_switch_gpio_num);
+ i2s_switch_gpio_active = (i2s_switch_gpio_level & OF_GPIO_ACTIVE_LOW);
+ gpio_direction_output(i2s_switch_gpio_num, i2s_switch_gpio_active);
+ }
+ /*************************************************/
+
+
+ earphone_gpio_num =
+ atm7059_audio_gpio_init(dn, earphone_ctrl_link_name, &earphone_gpio_level);
+ if (earphone_gpio_num < 0)
+ {
+ }
+ else
+ {
+ gpio_direction_input(earphone_gpio_num);
+ }
+
+ speaker_gpio_num =
+ atm7059_audio_gpio_init(dn, speaker_ctrl_link_name, &speaker_gpio_level);
+ speaker_gpio_active = (speaker_gpio_level & OF_GPIO_ACTIVE_LOW);
+ if(speaker_gpio_num>0)
+ speaker_exist=true;
+ else
+ speaker_exist=false;
+ if(speaker_exist)
+ gpio_direction_output(speaker_gpio_num, !speaker_gpio_active);
+
+ if (earphone_gpio_num > 0)
+ {
+ ret = atm7059_set_gpio_ear_detect();
+ if(ret)
+ goto set_earphone_detect_failed;
+ }
+ else
+ {
+ switch(pmu_type)
+ {
+ case ATC260X_ICTYPE_2603A:
+ snd_err("CANT GET EARPHONE GPIO!!!\n");
+ break;
+
+ case ATC260X_ICTYPE_2603C:
+ //atc2603c_set_irq_ear_detect();
+ snd_err("maybe using irq");
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ if (earphone_gpio_num > 0)
+ {
+ //FIXME: we register h2w in codec if using irq?
+ headphone_sdev.sdev.name = "h2w";
+ ret = switch_dev_register(&headphone_sdev.sdev);
+ if (ret < 0) {
+ snd_err("failed to register switch dev for "SNDRV_NAME"\n");
+ goto switch_dev_register_failed;
+ }
+ dbgflag = 0;
+ ret = device_create_file(headphone_sdev.sdev.dev, &dev_attr_dbgflag);
+ if (ret)
+ {
+ snd_err("failed to device_create_file\n");
+ }
+
+ }
+
+ atm7059_link_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!atm7059_link_snd_device) {
+ snd_err("ASoC: Platform device allocation failed\n");
+ ret = -ENOMEM;
+ goto platform_device_alloc_failed;
+ }
+
+ /* 2014.09.10 check the real device names*/
+
+ //20141013 yuchen: check pmu type to select correct codec param
+ switch(pmu_type)
+ {
+ case ATC260X_ICTYPE_2603A:
+ platform_set_drvdata(atm7059_link_snd_device,
+ &snd_soc_atm7059_atc2603a_link);
+ break;
+
+ case ATC260X_ICTYPE_2603C:
+ platform_set_drvdata(atm7059_link_snd_device,
+ &snd_soc_atm7059_atc2603c_link);
+ break;
+ default:
+ break;
+ }
+
+ ret = platform_device_add(atm7059_link_snd_device);
+ if (ret) {
+ snd_err("ASoC: Platform device allocation failed\n");
+ goto platform_device_add_failed;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(link_attr); i++) {
+ ret = device_create_file(
+ &atm7059_link_snd_device->dev, &link_attr[i]);
+ if (ret) {
+ snd_err("Add device file failed");
+ goto device_create_file_failed;
+ }
+ }
+
+
+ return 0;
+
+device_create_file_failed:
+ platform_device_del(atm7059_link_snd_device);
+platform_device_add_failed:
+ platform_device_put(atm7059_link_snd_device);
+platform_device_alloc_failed:
+ if (earphone_gpio_num > 0)
+ {
+ switch_dev_unregister(&headphone_sdev.sdev);
+ }
+switch_dev_register_failed:
+ if (earphone_gpio_num > 0)
+ {
+ destroy_workqueue(headphone_sdev.wq);
+ }
+set_earphone_detect_failed:
+ if(speaker_exist)
+ gpio_free(speaker_gpio_num);
+ if (earphone_gpio_num > 0)
+ {
+ gpio_free(earphone_gpio_num);
+ }
+no_device_node:
+ return ret;
+}
+
+static void __exit atm7059_link_exit(void)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(link_attr); i++) {
+ device_remove_file(&atm7059_link_snd_device->dev, &link_attr[i]);
+ }
+ if (headphone_sdev.wq) {
+ flag = 1;
+ msleep(500);/*clear the queue,ensure no cpu write to it*/
+ flush_delayed_work(&headphone_sdev.dwork);
+ destroy_workqueue(headphone_sdev.wq);
+ headphone_sdev.wq = NULL;
+ }
+
+ if(earphone_gpio_num > 0)
+ {
+ gpio_free(earphone_gpio_num);
+ earphone_gpio_num = -1;
+ switch_dev_unregister(&headphone_sdev.sdev);
+ }
+ if(speaker_exist){
+ gpio_free(speaker_gpio_num);
+ }
+ speaker_gpio_num = -1;
+
+ /*---------------david add-----------------------*/
+ if(i2s_switch_gpio_num > 0)
+ {
+ gpio_free(i2s_switch_gpio_num);
+ i2s_switch_gpio_num = -1;
+ }
+ /*----------------------------------------------*/
+
+ platform_device_unregister(atm7059_link_snd_device);
+}
+
+module_init(atm7059_link_init);
+module_exit(atm7059_link_exit);
+
+/* Module information */
+MODULE_AUTHOR("sall.xie <sall.xie at actions-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atc260x/pcm-owl.c b/sound/soc/atc260x/pcm-owl.c
new file mode 100755
index 0000000..2be508b
--- /dev/null
+++ b/sound/soc/atc260x/pcm-owl.c
@@ -0,0 +1,528 @@
+/*
+ * atm7059-pcm.c -- ALSA PCM interface for the OMAP SoC
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Contact: Jarkko Nikula <jarkko.nikula at bitmer.com>
+ * Peter Ujfalusi <peter.ujfalusi at ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include <linux/dma-mapping.h>
+
+#include <mach/hardware.h>
+#include "sndrv-owl.h"
+
+static struct snd_pcm_hardware atm7059_playback_hw_info = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 8,
+ #ifdef CONFIG_SND_UBUNTU
+ .buffer_bytes_max = 32 * 1024,
+ #else
+ .buffer_bytes_max = 64 * 1024,
+ #endif
+ .period_bytes_min = 256,
+ .period_bytes_max = 32*1024,
+ .periods_min = 2,
+ .periods_max = 16,
+};
+
+static struct snd_pcm_hardware atm7059_capture_hw_info = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 4,
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_min = 256,
+ .period_bytes_max = 32*1024,
+ .periods_min = 2,
+ .periods_max = PAGE_SIZE / 16,
+};
+#ifdef CONFIG_SND_UBUNTU
+int audio_set_output_mode(struct snd_pcm_substream *substream, int value)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct atm7059_pcm_priv *pcm_priv =
+ snd_soc_platform_get_drvdata(platform);
+ pcm_priv->output_mode = value;
+ return 0;
+}
+#endif
+static ssize_t error_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", error_switch);
+ return cnt;
+}
+
+static ssize_t error_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ error_switch = tmp;
+ break;
+ default:
+ printk(KERN_ERR"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+
+static ssize_t debug_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cnt;
+
+ cnt = sprintf(buf, "%d\n(Note: 1: open, 0:close)\n", debug_switch);
+ return cnt;
+}
+
+static ssize_t debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cnt, tmp;
+ cnt = sscanf(buf, "%d", &tmp);
+ switch (tmp) {
+ case 0:
+ case 1:
+ debug_switch = tmp;
+ break;
+ default:
+ printk(KERN_INFO"invalid input\n");
+ break;
+ }
+ return count;
+}
+
+static struct device_attribute pcm_attr[] = {
+ __ATTR(error, S_IRUSR | S_IWUSR, error_show, error_store),
+ __ATTR(debug, S_IRUSR | S_IWUSR, debug_show, debug_store),
+};
+
+
+static const char *const audio_output_mode[]
+ = {"i2s", "hdmi", "spdif"};
+static const SOC_ENUM_SINGLE_DECL(audio_output_enum,
+ 0, 0, audio_output_mode);
+
+static int audio_output_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform =
+ snd_kcontrol_chip(kcontrol);
+ struct atm7059_pcm_priv *pcm_priv =
+ snd_soc_platform_get_drvdata(platform);
+ ucontrol->value.integer.value[0] = pcm_priv->output_mode;
+ return 0;
+}
+
+static int audio_output_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform =
+ snd_kcontrol_chip(kcontrol);
+ struct atm7059_pcm_priv *pcm_priv =
+ snd_soc_platform_get_drvdata(platform);
+
+ pcm_priv->output_mode = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static const struct snd_kcontrol_new atm7059_pcm_controls[] = {
+ SOC_ENUM_EXT("audio output mode switch", audio_output_enum,
+ audio_output_mode_get, audio_output_mode_put),
+};
+
+/* this may get called several times by oss emulation */
+static int atm7059_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ //struct imx_pcm_dma_params *dma_params;
+ struct dma_slave_config slave_config;
+ int ret;
+
+ struct owl_dma_slave *atslave = (struct owl_dma_slave *)chan->private;
+ dma_addr_t dst_addr;
+ enum dma_slave_buswidth dst_addr_width;
+
+
+ //dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+ //direction, dst_addr_width or src_addr_width
+ ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config);
+ if (ret)
+ return ret;
+
+ slave_config.device_fc = false;
+
+ atslave->dma_dev = chan->device->dev;
+ atslave->trans_type = SLAVE;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ struct snd_soc_platform *platform = rtd->platform;
+ struct atm7059_pcm_priv *pcm_priv =
+ snd_soc_platform_get_drvdata(platform);
+
+ switch (pcm_priv->output_mode) {
+ case O_MODE_SPDIF:
+ atslave->mode = PRIORITY_SEVEN| SRC_INCR |
+ DST_CONSTANT | SRC_DCU | DST_DEV | HDMIAUDIO | CRITICAL_BIT;
+ dst_addr = SPDIF_DAT;
+ break;
+ case O_MODE_HDMI:
+ atslave->mode = PRIORITY_SEVEN | SRC_INCR |
+ DST_CONSTANT | SRC_DCU | DST_DEV | HDMIAUDIO | CRITICAL_BIT;
+ dst_addr = HDMI_DAT;
+ break;
+ case O_MODE_I2S:
+ default:
+ atslave->mode = PRIORITY_SEVEN | SRC_INCR |
+ DST_CONSTANT | SRC_DCU | DST_DEV | I2S_T | CRITICAL_BIT;
+ dst_addr = I2STX_DAT;
+ break;
+ }
+
+ slave_config.dst_addr = dst_addr;
+
+ dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slave_config.dst_addr_width = dst_addr_width;
+
+ } else {
+ atslave->mode = PRIORITY_SEVEN | SRC_CONSTANT |
+ DST_INCR | DST_DCU | SRC_DEV | I2S_R | CRITICAL_BIT;
+
+ slave_config.src_addr = I2SRX_DAT;
+
+ slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ }
+
+ ret = dmaengine_slave_config(chan, &slave_config);
+ if (ret)
+ return ret;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ /*
+ * Set DMA transfer frame size equal to ALSA period size and frame
+ * count as no. of ALSA periods. Then with DMA frame interrupt enabled,
+ * we can transfer the whole ALSA buffer with single DMA transfer but
+ * still can get an interrupt at each period bounary
+ */
+
+ return 0;
+}
+
+static int atm7059_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_set_runtime_buffer(substream, NULL);
+
+ return 0;
+}
+
+static int atm7059_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int atm7059_pcm_open(struct snd_pcm_substream *substream)
+{
+ int ret;
+ struct dma_chan *chan;
+ struct owl_dma_slave *atslave = kzalloc(sizeof(*atslave), GFP_KERNEL);
+ if (!atslave)
+ return -ENOMEM;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_soc_set_runtime_hwparams(substream, &atm7059_playback_hw_info);
+ } else {
+ snd_soc_set_runtime_hwparams(substream, &atm7059_capture_hw_info);
+ }
+
+ //snd_pcm_hw_constraint_integer&&\B7\D6\C5\E4\D4\CB\D0\D0ʱ&&\C9\EA\C7\EBͨ\B5\C0
+ //ret = snd_dmaengine_pcm_open(substream, filter, dma_data);
+ ret = snd_dmaengine_pcm_open_request_chan(substream, NULL, NULL);
+ if (ret) {
+ return 0;
+ }
+
+ //snd_dmaengine_pcm_set_data(substream, dma_data);
+
+ chan = snd_dmaengine_pcm_get_chan(substream);
+ chan->private = (void *)atslave;
+ return 0;
+}
+
+static int atm7059_pcm_close(struct snd_pcm_substream *substream)
+{
+ //struct imx_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream);
+ //\CAͷ\C5ͨ\B5\C0\A3\AC\CAͷ\C5\D4\CB\D0\D0ʱ
+ snd_dmaengine_pcm_close_release_chan(substream);
+ //printk("%s %d\n", __FUNCTION__, __LINE__);
+
+ //kfree(dma_data);
+
+ return 0;
+}
+
+static int atm7059_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret= dma_mmap_coherent(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return ret;
+}
+
+static struct snd_pcm_ops atm7059_pcm_ops = {
+ .open = atm7059_pcm_open,
+ .close = atm7059_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = atm7059_pcm_hw_params,
+ .hw_free = atm7059_pcm_hw_free,
+ .prepare = atm7059_pcm_prepare,
+ .trigger = snd_dmaengine_pcm_trigger,
+ .pointer = snd_dmaengine_pcm_pointer_no_residue,
+ .mmap = atm7059_pcm_mmap,
+};
+
+static u64 atm7059_pcm_dmamask = DMA_BIT_MASK(32);
+
+static int atm7059_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;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ size = atm7059_playback_hw_info.buffer_bytes_max;
+ else
+ size = atm7059_capture_hw_info.buffer_bytes_max;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_coherent(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+
+ return 0;
+}
+
+static void atm7059_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_coherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+
+ buf->area = NULL;
+ }
+}
+
+static int atm7059_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &atm7059_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 = atm7059_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ret = atm7059_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+
+out:
+ /* free preallocated buffers in case of error */
+ if (ret)
+ atm7059_pcm_free_dma_buffers(pcm);
+
+ return ret;
+}
+
+static struct snd_soc_platform_driver atm7059_soc_platform = {
+ .ops = &atm7059_pcm_ops,
+ .pcm_new = atm7059_pcm_new,
+ .pcm_free = atm7059_pcm_free_dma_buffers,
+ .component_driver = {
+ .controls = atm7059_pcm_controls,
+ .num_controls = ARRAY_SIZE(atm7059_pcm_controls),
+ },
+};
+
+static int atm7059_pcm_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev,
+ "atm7059_pcm_probe!!\n");
+ pdev->dev.init_name = "atm7059-pcm-audio";
+ return snd_soc_register_platform(&pdev->dev,
+ &atm7059_soc_platform);
+}
+
+static int atm7059_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver atm7059_pcm_driver = {
+ .driver = {
+ .name = "atm7059-pcm-audio",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = atm7059_pcm_probe,
+ .remove = atm7059_pcm_remove,
+};
+
+static struct platform_device *atm7059_pcm_device;
+static int __init atm7059_pcm_init(void)
+{
+ int ret;
+ int i = 0;
+ struct atm7059_pcm_priv *pcm_priv;
+
+ snd_dbg("atm7059_pcm_init!!\n");
+ atm7059_pcm_device = platform_device_alloc("atm7059-pcm-audio", -1);
+ if (!atm7059_pcm_device) {
+ snd_dbg(
+ "ASoC: Platform device atm7059-pcm-audio allocation failed\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = platform_device_add(atm7059_pcm_device);
+ if (ret) {
+ snd_dbg(
+ "ASoC: Platform device atm7059-pcm-audio add failed\n");
+ goto err_device_add;
+ }
+
+ pcm_priv = kzalloc(sizeof(struct atm7059_pcm_priv), GFP_KERNEL);
+ if (NULL == pcm_priv)
+ return -ENOMEM;
+ pcm_priv->output_mode = O_MODE_I2S;
+ platform_set_drvdata(atm7059_pcm_device, pcm_priv);
+
+ ret = platform_driver_register(&atm7059_pcm_driver);
+ if (ret) {
+ snd_dbg(
+ "ASoC: Platform driver atm7059-pcm-audio register failed\n");
+ goto err_driver_register;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pcm_attr); i++) {
+ ret = device_create_file(
+ &atm7059_pcm_device->dev, &pcm_attr[i]);
+ if (ret) {
+ snd_err("Add device file failed");
+ goto device_create_file_failed;
+ }
+ }
+
+ return 0;
+
+device_create_file_failed:
+err_driver_register:
+ platform_device_unregister(atm7059_pcm_device);
+err_device_add:
+ platform_device_put(atm7059_pcm_device);
+err:
+ return ret;
+}
+static void __exit atm7059_pcm_exit(void)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(pcm_attr); i++) {
+ device_remove_file(&atm7059_pcm_device->dev, &pcm_attr[i]);
+ }
+
+ platform_driver_unregister(&atm7059_pcm_driver);
+ platform_device_unregister(atm7059_pcm_device);
+ atm7059_pcm_device = NULL;
+}
+
+module_init(atm7059_pcm_init);
+module_exit(atm7059_pcm_exit);
+
+MODULE_AUTHOR("sall.xie <sall.xie at actions-semi.com>");
+MODULE_DESCRIPTION("ATM7059 PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atc260x/sndrv-owl.h b/sound/soc/atc260x/sndrv-owl.h
new file mode 100755
index 0000000..51a2077
--- /dev/null
+++ b/sound/soc/atc260x/sndrv-owl.h
@@ -0,0 +1,101 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2009 Actions Semi Inc.
+ */
+
+#ifndef __ATM7059_SNDRV_H__
+#define __ATM7059_SNDRV_H__
+#include <sound/pcm.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <linux/atomic.h>
+#include <mach/hdmac-owl.h>
+#include <linux/dmaengine.h>
+#include <linux/pinctrl/consumer.h>
+
+#define PMU_NOT_USED -1
+
+#define COMPILETEST 0
+
+#define ATM7059_AIF_I2S 0
+#define ATM7059_AIF_HDMI 0
+static int error_switch = 1;
+static int debug_switch = 1;
+
+#define SND_DEBUG
+#ifdef SND_DEBUG
+#define snd_err(fmt, args...) \
+ if (error_switch) \
+ printk(KERN_ERR"[SNDRV]:[%s] "fmt"\n", __func__, ##args)
+
+#define snd_dbg(fmt, args...) \
+ if (debug_switch) \
+ printk(KERN_DEBUG"[SNDRV]:[%s] "fmt"\n", __func__, ##args)
+#endif
+
+enum {
+ O_MODE_I2S,
+ O_MODE_HDMI,
+ O_MODE_SPDIF
+};
+
+enum {
+ SPEAKER_ON = 0,
+ HEADSET_MIC = 1,
+ HEADSET_NO_MIC = 2,
+};
+
+
+
+typedef struct {
+ short sample_rate; /* 真实采样率除以1000 */
+ char index[2]; /* 对应硬件寄存器的索引值 */
+} fs_t;
+
+typedef struct {
+ unsigned int earphone_gpios;
+ unsigned int speaker_gpios;
+ unsigned int earphone_output_mode;
+ unsigned int mic_num;
+ unsigned int mic0_gain[2];
+ unsigned int speaker_gain[2];
+ unsigned int earphone_gain[2];
+ unsigned int speaker_volume;
+ unsigned int earphone_volume;
+ unsigned int earphone_detect_mode;
+ unsigned int mic_mode;
+ unsigned int earphone_detect_method;
+ unsigned int adc_plugin_threshold;
+ unsigned int adc_level;
+} audio_hw_cfg_t;
+
+//extern audio_hw_cfg_t audio_hw_cfg;
+
+struct atm7059_pcm_priv {
+ int output_mode;
+ struct pinctrl *pc;
+ struct pinctrl_state *ps;
+};
+
+enum{
+I2S_SPDIF_NUM = 0,
+GPIO_MFP_NUM,
+HDMI_NUM,
+MAX_RES_NUM
+};
+
+void set_dai_reg_base(int num);
+u32 snd_dai_readl(u32 reg);
+void snd_dai_writel(u32 val, u32 reg);
+
+extern void hdmihw_write_reg(u32 val, const u16 idx);
+extern int hdmihw_read_reg(const u16 idx);
+#ifdef CONFIG_SND_UBUNTU
+int audio_set_output_mode(struct snd_pcm_substream *substream, int value);
+#endif
+#endif /* ifndef __ATM7059_SNDRV_H__ */
--
2.7.4
More information about the linux-yocto
mailing list