summaryrefslogtreecommitdiff
path: root/sound/soc/sprd/codec
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /sound/soc/sprd/codec
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'sound/soc/sprd/codec')
-rw-r--r--sound/soc/sprd/codec/null-codec/Kconfig2
-rw-r--r--sound/soc/sprd/codec/null-codec/Makefile6
-rw-r--r--sound/soc/sprd/codec/null-codec/null-codec.c110
-rw-r--r--sound/soc/sprd/codec/null-codec/null-codec.h20
-rw-r--r--sound/soc/sprd/codec/sprd/Kconfig30
-rw-r--r--sound/soc/sprd/codec/sprd/v1/Kconfig2
-rw-r--r--sound/soc/sprd/codec/sprd/v1/Makefile7
-rwxr-xr-xsound/soc/sprd/codec/sprd/v1/sprd-audio-power.c614
-rw-r--r--sound/soc/sprd/codec/sprd/v1/sprd-audio-power.h33
-rw-r--r--sound/soc/sprd/codec/sprd/v1/sprd-codec.c3006
-rw-r--r--sound/soc/sprd/codec/sprd/v1/sprd-codec.h375
-rwxr-xr-xsound/soc/sprd/codec/sprd/v3/Kconfig15
-rw-r--r--sound/soc/sprd/codec/sprd/v3/Makefile7
-rw-r--r--sound/soc/sprd/codec/sprd/v3/sprd-audio-power.c654
-rwxr-xr-xsound/soc/sprd/codec/sprd/v3/sprd-audio-power.h35
-rwxr-xr-xsound/soc/sprd/codec/sprd/v3/sprd-codec.c3858
-rwxr-xr-xsound/soc/sprd/codec/sprd/v3/sprd-codec.h404
-rw-r--r--sound/soc/sprd/codec/sprd/v4/Kconfig15
-rw-r--r--sound/soc/sprd/codec/sprd/v4/Makefile7
-rw-r--r--sound/soc/sprd/codec/sprd/v4/sprd-audio-power.c672
-rwxr-xr-xsound/soc/sprd/codec/sprd/v4/sprd-audio-power.h36
-rw-r--r--sound/soc/sprd/codec/sprd/v4/sprd-codec.c4943
-rw-r--r--sound/soc/sprd/codec/sprd/v4/sprd-codec.h593
23 files changed, 15444 insertions, 0 deletions
diff --git a/sound/soc/sprd/codec/null-codec/Kconfig b/sound/soc/sprd/codec/null-codec/Kconfig
new file mode 100644
index 00000000..22c2e902
--- /dev/null
+++ b/sound/soc/sprd/codec/null-codec/Kconfig
@@ -0,0 +1,2 @@
+config SND_SOC_NULL_CODEC
+ tristate # "null codec"
diff --git a/sound/soc/sprd/codec/null-codec/Makefile b/sound/soc/sprd/codec/null-codec/Makefile
new file mode 100644
index 00000000..d6806e1d
--- /dev/null
+++ b/sound/soc/sprd/codec/null-codec/Makefile
@@ -0,0 +1,6 @@
+
+SPRD_SOUND_TREE := sound/soc/sprd/
+KBUILD_CFLAGS += -I$(SPRD_SOUND_TREE) -I$(SPRD_SOUND_TREE)/codec
+
+snd-soc-sprd-null-codec-objs := null-codec.o
+obj-$(CONFIG_SND_SOC_NULL_CODEC) += snd-soc-sprd-null-codec.o
diff --git a/sound/soc/sprd/codec/null-codec/null-codec.c b/sound/soc/sprd/codec/null-codec/null-codec.c
new file mode 100644
index 00000000..0a8a9a0a
--- /dev/null
+++ b/sound/soc/sprd/codec/null-codec/null-codec.c
@@ -0,0 +1,110 @@
+/*
+ * sound/soc/sprd/codec/null-codec/null-codec.c
+ *
+ * NULL-CODEC -- SpreadTrum just for codec code.
+ *
+ * Copyright (C) 2012 SpreadTrum Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "sprd-asoc-debug.h"
+#define pr_fmt(fmt) pr_sprd_fmt("NULCD") fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/stat.h>
+#include <linux/of.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+
+#include "sprd-asoc-common.h"
+#include "null-codec.h"
+
+/* PCM Playing and Recording default in full duplex mode */
+struct snd_soc_dai_driver null_codec_dai[] = {
+ {
+ .name = "null-codec-dai",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_null_codec = { 0 };
+
+static int null_codec_codec_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ ret = snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_null_codec, null_codec_dai,
+ ARRAY_SIZE(null_codec_dai));
+ if (ret != 0) {
+ pr_err("ERR:Failed to register NULL CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+
+}
+
+static int null_codec_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id codec_of_match[] = {
+ {.compatible = "sprd,null-codec",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, codec_of_match);
+#endif
+
+static struct platform_driver null_codec_codec_driver = {
+ .driver = {
+ .name = "null-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(codec_of_match),
+ },
+ .probe = null_codec_codec_probe,
+ .remove = null_codec_codec_remove,
+};
+
+module_platform_driver(null_codec_codec_driver);
+
+MODULE_DESCRIPTION("NULL-CODEC ALSA SoC codec driver");
+MODULE_AUTHOR("Ken Kuang <ken.kuang@spreadtrum.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("codec:null-codec");
diff --git a/sound/soc/sprd/codec/null-codec/null-codec.h b/sound/soc/sprd/codec/null-codec/null-codec.h
new file mode 100644
index 00000000..de413f0c
--- /dev/null
+++ b/sound/soc/sprd/codec/null-codec/null-codec.h
@@ -0,0 +1,20 @@
+/*
+ * sound/soc/sprd/codec/null-codec/null-codec.h
+ *
+ * NULL-CODEC -- SpreadTrum just for codec code.
+ *
+ * Copyright (C) 2012 SpreadTrum Ltd.
+ *
+ * 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 ork FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __NULL_CODEC_H
+#define __NULL_CODEC_H
+
+#endif /* __NULL_CODEC_H */
diff --git a/sound/soc/sprd/codec/sprd/Kconfig b/sound/soc/sprd/codec/sprd/Kconfig
new file mode 100644
index 00000000..1661b5e9
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/Kconfig
@@ -0,0 +1,30 @@
+menu "SPRD Codec"
+
+source "sound/soc/sprd/codec/sprd/v1/Kconfig"
+source "sound/soc/sprd/codec/sprd/v3/Kconfig"
+source "sound/soc/sprd/codec/sprd/v4/Kconfig"
+
+config SND_SOC_SPRD_CODEC_EAR_WITH_IN_SPK
+ bool "Earpiece with in Speaker"
+ help
+ Say Y if your device is use ear&spk in one,
+ and ear have TDD noise when in call mode.
+ this feature should to open.
+
+config SND_SOC_SPRD_CODEC_NO_HP_POP
+ bool "Codec no HP POP"
+ help
+ Say Y if you want *NOT* the CODEC do hp pop ramp up/down process.
+ some customer use the external hp amplifier will need this feature,
+ Or will cause hp pop noise serious.
+
+config SND_SOC_SPRD_HP_POP_DELAY_TIME
+ int "HP Switch delay time(0-1000) unit(MS)"
+ depends on SND_SOC_SPRD_CODEC_NO_HP_POP
+ range 0 1000
+ default 350
+ help
+ if defined CODEC_NO_HP_POP , maybe need this delay
+ for reduce external hp amplifier noise.
+ this delay time value, unit is MS.
+endmenu
diff --git a/sound/soc/sprd/codec/sprd/v1/Kconfig b/sound/soc/sprd/codec/sprd/v1/Kconfig
new file mode 100644
index 00000000..72fa453d
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v1/Kconfig
@@ -0,0 +1,2 @@
+config SND_SOC_SPRD_CODEC_V1
+ tristate # "Sprd-codec-v1"
diff --git a/sound/soc/sprd/codec/sprd/v1/Makefile b/sound/soc/sprd/codec/sprd/v1/Makefile
new file mode 100644
index 00000000..e092d558
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v1/Makefile
@@ -0,0 +1,7 @@
+#SPRD CODEC
+
+SPRD_SOUND_TREE := sound/soc/sprd/
+KBUILD_CFLAGS += -I$(SPRD_SOUND_TREE) -I$(SPRD_SOUND_TREE)/codec
+
+snd-soc-sprd-sprd-codec-v1-objs := sprd-codec.o sprd-audio-power.o
+obj-$(CONFIG_SND_SOC_SPRD_CODEC_V1) += snd-soc-sprd-sprd-codec-v1.o
diff --git a/sound/soc/sprd/codec/sprd/v1/sprd-audio-power.c b/sound/soc/sprd/codec/sprd/v1/sprd-audio-power.c
new file mode 100755
index 00000000..ea29f53e
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v1/sprd-audio-power.c
@@ -0,0 +1,614 @@
+/*
+ * sound/soc/sprd/codec/sprd/v1/sprd-audio-power.c
+ *
+ * SPRD-AUDIO-POWER -- SpreadTrum intergrated audio power supply.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "sprd-asoc-debug.h"
+#define pr_fmt(fmt) pr_sprd_fmt("POWER") fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#include <mach/sprd-audio.h>
+#include "sprd-audio-power.h"
+#include "sprd-asoc-common.h"
+
+#ifdef CONFIG_AUDIO_POWER_ALLOW_UNSUPPORTED
+#define UNSUP_MASK 0x0000
+#else
+#define UNSUP_MASK 0x8000
+#endif
+
+#define UNSUP(x) (UNSUP_MASK | (x))
+#define IS_UNSUP(x) (UNSUP_MASK & (x))
+#define LDO_MV(x) (~UNSUP_MASK & (x))
+
+struct sprd_audio_power_info {
+ int id;
+ /* enable/disable register information */
+ int en_reg;
+ int en_bit;
+ int (*power_enable) (void);
+ int (*power_disable) (void);
+ /* configure deepsleep cut off or not */
+ int (*sleep_ctrl) (void *, int);
+ /* voltage level register information */
+ int v_reg;
+ int v_mask;
+ int v_shift;
+ int v_table_len;
+ const u16 *v_table;
+ int min_uV;
+ int max_uV;
+
+ /* unit: us */
+ int on_delay;
+ int off_delay;
+
+ /* used by regulator core */
+ struct regulator_desc desc;
+
+ void *data;
+};
+
+#define SPRD_AUDIO_POWER_LDO_LOW(label, r_supply, d_id, \
+e_reg, e_bit, p_enable, p_disable, s_ctrl, \
+reg, mask, shift, table, table_size, d_on_delay, d_off_delay) \
+static struct regulator_consumer_supply label[] = { \
+ { .supply = #label },\
+}; \
+static struct regulator_init_data label##_data = { \
+ .supply_regulator = r_supply, \
+ .constraints = { \
+ .name = "SRG_" #label, \
+ }, \
+ .num_consumer_supplies = ARRAY_SIZE(label), \
+ .consumer_supplies = label, \
+}; \
+static struct sprd_audio_power_info SPRD_AUDIO_POWER_INFO_##label = { \
+ .id = d_id, \
+ .en_reg = e_reg, \
+ .en_bit = e_bit, \
+ .power_enable = p_enable, \
+ .power_disable = p_disable, \
+ .sleep_ctrl = s_ctrl, \
+ .v_reg = reg, \
+ .v_mask = mask, \
+ .v_shift = shift, \
+ .v_table_len = table_size, \
+ .v_table = table, \
+ .on_delay = d_on_delay, \
+ .off_delay = d_off_delay, \
+ .desc = { \
+ .name = #label, \
+ .id = SPRD_AUDIO_POWER_##label, \
+ .n_voltages = table_size, \
+ .ops = &sprd_audio_power_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+}
+
+#define SPRD_AUDIO_POWER_LDO(label, r_supply, id, en_reg, en_bit, sleep_ctrl, \
+ v_reg, v_mask, v_shift, v_table, on_delay, off_delay) \
+SPRD_AUDIO_POWER_LDO_LOW(label, r_supply, id, en_reg, en_bit, 0, 0, sleep_ctrl, \
+ v_reg, v_mask, v_shift, v_table, ARRAY_SIZE(v_table), on_delay, off_delay)
+
+#define SPRD_AUDIO_POWER_SIMPLE_LDO(label, r_supply, id, en_reg, en_bit, sleep_ctrl, \
+ on_delay, off_delay) \
+SPRD_AUDIO_POWER_LDO_LOW(label, r_supply, id, en_reg, en_bit, 0, 0, sleep_ctrl, \
+ 0, 0, 0, 0, 0, on_delay, off_delay)
+
+#define SPRD_AUDIO_POWER_REG_LDO(label, id, power_enable, power_disable) \
+SPRD_AUDIO_POWER_LDO_LOW(label, 0, id, 0, 0, power_enable, power_disable, \
+ 0, 0, 0, 0, 0, 0, 0, 0)
+
+static inline int sprd_power_read(struct sprd_audio_power_info *info, int reg)
+{
+ return arch_audio_power_read(reg);
+}
+
+static inline int
+sprd_power_write(struct sprd_audio_power_info *info, int reg, int val)
+{
+ int ret = 0;
+ ret = arch_audio_power_write(reg, val);
+ sp_asoc_pr_reg("[0x%04x] W:[0x%08x] R:[0x%08x]\n", reg & 0xFFFF, val,
+ arch_audio_power_read(reg));
+ return ret;
+}
+
+static inline int
+sprd_power_u_bits(struct sprd_audio_power_info *info,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ int ret = 0;
+ ret = arch_audio_power_write_mask(reg, value, mask);
+ sp_asoc_pr_reg("[0x%04x] U:[0x%08x] R:[0x%08x]\n", reg & 0xFFFF, value,
+ arch_audio_power_read(reg));
+ return ret;
+}
+
+static int sprd_audio_power_is_enabled(struct regulator_dev *rdev)
+{
+ int is_enable;
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+
+ sp_asoc_pr_dbg("%s Is Enabled\n", rdev->desc->name);
+
+ if (info->en_reg) {
+ is_enable = sprd_power_read(info, info->en_reg) & info->en_bit;
+ } else {
+ is_enable = info->en_bit;
+ }
+
+ return ! !is_enable;
+}
+
+static int audio_power_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int vsel;
+
+ BUG_ON(!info->v_table);
+
+ for (vsel = 0; vsel < info->v_table_len; vsel++) {
+ int mV = info->v_table[vsel];
+ int uV;
+
+ if (IS_UNSUP(mV))
+ continue;
+ uV = LDO_MV(mV) * 1000;
+
+ /* use the first in-range value */
+ if (min_uV <= uV && uV <= max_uV) {
+ sp_asoc_pr_dbg("%s: %dus\n", __func__, uV);
+ sprd_power_u_bits(info,
+ info->v_reg,
+ info->v_mask << info->v_shift,
+ vsel << info->v_shift);
+
+ return vsel;
+ }
+ }
+ return -EDOM;
+}
+
+static int sprd_audio_power_enable(struct regulator_dev *rdev)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Enable\n", rdev->desc->name);
+
+ if (info->en_reg) {
+ ret = sprd_power_u_bits(info,
+ info->en_reg, info->en_bit,
+ info->en_bit);
+ } else {
+ if (info->power_enable)
+ ret = info->power_enable();
+ info->en_bit = 1;
+ }
+
+ udelay(info->on_delay);
+
+ if (info->v_table) {
+ if (info->min_uV || info->max_uV)
+ audio_power_set_voltage(rdev, info->min_uV,
+ info->max_uV);
+ }
+ return ret;
+}
+
+static int sprd_audio_power_disable(struct regulator_dev *rdev)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Disable\n", rdev->desc->name);
+
+ if (info->en_reg) {
+ ret = sprd_power_u_bits(info, info->en_reg, info->en_bit, 0);
+ } else {
+ if (info->power_disable)
+ ret = info->power_disable();
+ info->en_bit = 0;
+ }
+
+ udelay(info->off_delay);
+
+ return ret;
+}
+
+static int sprd_audio_power_get_status(struct regulator_dev *rdev)
+{
+ sp_asoc_pr_dbg("%s Get Status\n", rdev->desc->name);
+
+ if (!sprd_audio_power_is_enabled(rdev))
+ return REGULATOR_STATUS_OFF;
+ return REGULATOR_STATUS_NORMAL;
+}
+
+static int sprd_audio_power_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ int ret = 0;
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+
+ sp_asoc_pr_dbg("%s Set Mode 0x%x\n", rdev->desc->name, mode);
+
+ if (!info->sleep_ctrl) {
+ return ret;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ ret = info->sleep_ctrl(info, 0);
+ break;
+ case REGULATOR_MODE_STANDBY:
+ ret = info->sleep_ctrl(info, 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static const u16 VCOM_VSEL_table[] = {
+ 2900, 3100, 3200, 3300,
+ 3400, 3500, 3600, 3800,
+};
+
+static const u16 VPA_VSEL_table[] = {
+ 2900, 3100, 3200, 3300,
+ 3400, 3500, 3600, 3800,
+};
+
+static const u16 VMIC_VSEL_table[] = {
+ 2100, 1900, 2300, 2500,
+};
+
+static const u16 VBG_IBAIS_VSEL_table[] = {
+ 150, 200, 250, 300,
+};
+
+static int sprd_audio_power_list_voltage(struct regulator_dev *rdev,
+ unsigned index)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int mV;
+
+ sp_asoc_pr_dbg("%s List Voltage\n", rdev->desc->name);
+
+ if (!info->v_table) {
+ return 0;
+ }
+
+ if (index >= info->v_table_len) {
+ return 0;
+ }
+
+ mV = info->v_table[index];
+ return IS_UNSUP(mV) ? 0 : (LDO_MV(mV) * 1000);
+}
+
+static int
+sprd_audio_power_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned *selector)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int vsel;
+
+ sp_asoc_pr_info("%s Set Voltage %d--%d\n", rdev->desc->name, min_uV,
+ max_uV);
+
+ if (!info->v_table) {
+ return 0;
+ }
+ info->min_uV = min_uV;
+ info->max_uV = max_uV;
+
+ vsel = audio_power_set_voltage(rdev, min_uV, max_uV);
+ if (vsel != -EDOM)
+ *selector = vsel;
+
+ return vsel;
+}
+
+static int sprd_audio_power_get_voltage(struct regulator_dev *rdev)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int vsel;
+
+ sp_asoc_pr_dbg("%s Get Voltage\n", rdev->desc->name);
+
+ if (!info->v_table) {
+ return 0;
+ }
+
+ vsel = (sprd_power_read(info, info->v_reg) & info->v_mask)
+ >> info->v_shift;
+ return LDO_MV(info->v_table[vsel]) * 1000;
+}
+
+static struct regulator_ops sprd_audio_power_ops = {
+ .list_voltage = sprd_audio_power_list_voltage,
+
+ .set_voltage = sprd_audio_power_set_voltage,
+ .get_voltage = sprd_audio_power_get_voltage,
+
+ .enable = sprd_audio_power_enable,
+ .disable = sprd_audio_power_disable,
+ .is_enabled = sprd_audio_power_is_enabled,
+
+ .set_mode = sprd_audio_power_set_mode,
+
+ .get_status = sprd_audio_power_get_status,
+};
+
+static inline int vreg_enable(void)
+{
+ int ret = 0;
+ arch_audio_codec_switch(AUDIO_TO_ARM_CTRL);
+ arch_audio_codec_analog_reg_enable();
+ arch_audio_codec_analog_reset();
+ return ret;
+}
+
+static inline int vreg_disable(void)
+{
+ arch_audio_codec_reset();
+ arch_audio_codec_analog_reg_disable();
+ return 0;
+}
+
+/*
+ * We list regulators here if systems need some level of
+ * software control over them after boot.
+ */
+SPRD_AUDIO_POWER_REG_LDO(VREG, 1, vreg_enable, vreg_disable);
+SPRD_AUDIO_POWER_SIMPLE_LDO(VB, "VREG", 2, PMUR1, BIT(VB_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(BG, "VB", 3, PMUR1, BIT(BG_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(BG_IBIAS, "BG", 4, PMUR1, BIT(BG_IBIAS_EN), NULL, 0,
+ 0);
+SPRD_AUDIO_POWER_LDO(VCOM, "BG_IBIAS", 5, PMUR1, BIT(VCM_EN), NULL, PMUR3,
+ VCM_V_MASK, VCM_V, VCOM_VSEL_table, 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(VCOM_BUF, "VCOM", 6, PMUR1, BIT(VCM_BUF_EN), NULL,
+ 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(VBO, "VB", 7, PMUR1, BIT(VBO_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_LDO(MICBIAS, "VCOM", 8, PMUR1, BIT(MICBIAS_EN), NULL,
+ PMUR3, MICBIAS_V_MASK, MICBIAS_V, VMIC_VSEL_table, 0, 0);
+SPRD_AUDIO_POWER_LDO(AUXMICBIAS, "VCOM", 9, PMUR1, BIT(AUXMICBIAS_EN), NULL,
+ PMUR3, AUXMICBIAS_V_MASK, AUXMICBIAS_V, VMIC_VSEL_table, 0,
+ 0);
+
+#define SPRD_OF_MATCH(comp, label) \
+ { \
+ .compatible = comp, \
+ .data = &SPRD_AUDIO_POWER_INFO_##label, \
+ }
+
+static const struct of_device_id sprd_audio_power_of_match[] = {
+ SPRD_OF_MATCH("sp,audio-vreg", VREG),
+ SPRD_OF_MATCH("sp,audio-vb", VB),
+ SPRD_OF_MATCH("sp,audio-bg", BG),
+ SPRD_OF_MATCH("sp,audio-bg-ibias", BG_IBIAS),
+ SPRD_OF_MATCH("sp,audio-vcom", VCOM),
+ SPRD_OF_MATCH("sp,audio-vcom-buf", VCOM_BUF),
+ SPRD_OF_MATCH("sp,audio-vbo", VBO),
+ SPRD_OF_MATCH("sp,audio-micbias", MICBIAS),
+ SPRD_OF_MATCH("sp,audio-auxmicbias", AUXMICBIAS),
+};
+
+static int sprd_audio_power_probe(struct platform_device *pdev)
+{
+ int i, id;
+ struct sprd_audio_power_info *info;
+ const struct sprd_audio_power_info *template;
+ struct regulator_init_data *initdata;
+ struct regulation_constraints *c;
+ struct regulator_dev *rdev;
+ struct regulator_config config = { };
+ int min_index = 0;
+ int max_index = 0;
+
+ id = pdev->id;
+
+ sp_asoc_pr_info("Probe %d\n", id);
+
+ initdata = pdev->dev.platform_data;
+ for (i = 0, template = NULL; i < ARRAY_SIZE(sprd_audio_power_of_match);
+ i++) {
+ template = sprd_audio_power_of_match[i].data;
+ if (!template || template->desc.id != id)
+ continue;
+ break;
+ }
+
+ if (!template)
+ return -ENODEV;
+
+ if (!initdata)
+ return -EINVAL;
+
+ info = kmemdup(template, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ /* Constrain board-specific capabilities according to what
+ * this driver and the chip itself can actually do.
+ */
+ c = &initdata->constraints;
+ c->valid_modes_mask |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY;
+ c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS;
+
+ if (info->v_table) {
+ for (i = 0; i < info->v_table_len; i++) {
+ if (info->v_table[i] < info->v_table[min_index])
+ min_index = i;
+ if (info->v_table[i] > info->v_table[max_index])
+ max_index = i;
+ }
+ c->min_uV = LDO_MV((info->v_table)[min_index]) * 1000;
+ c->max_uV = LDO_MV((info->v_table)[max_index]) * 1000;
+ sp_asoc_pr_info("min_uV:%d, max_uV:%d\n", c->min_uV, c->max_uV);
+ }
+ info->min_uV = 0;
+ info->max_uV = 0;
+
+ config.dev = &pdev->dev;
+ config.init_data = initdata;
+ config.driver_data = info;
+ config.of_node = pdev->dev.of_node;
+
+ rdev = regulator_register(&info->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "ERR:Can't Register %s, %ld\n",
+ info->desc.name, PTR_ERR(rdev));
+ kfree(info);
+ return PTR_ERR(rdev);
+ }
+ sp_asoc_pr_info("Register %s Success!\n", info->desc.name);
+
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static int sprd_audio_power_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct twlreg_info *info = rdev->reg_data;
+
+ regulator_unregister(platform_get_drvdata(pdev));
+ kfree(info);
+ return 0;
+}
+
+static struct platform_driver sprd_audio_power_driver = {
+ .driver = {
+ .name = "sprd-audio-power",
+ .owner = THIS_MODULE,
+ },
+ .probe = sprd_audio_power_probe,
+ .remove = sprd_audio_power_remove,
+};
+
+static int __init sprd_audio_power_regulator_init(void)
+{
+ return platform_driver_register(&sprd_audio_power_driver);
+}
+
+postcore_initcall(sprd_audio_power_regulator_init);
+
+static void __exit sprd_audio_power_regulator_exit(void)
+{
+ platform_driver_unregister(&sprd_audio_power_driver);
+}
+
+module_exit(sprd_audio_power_regulator_exit);
+
+MODULE_DESCRIPTION("SPRD audio power regulator driver");
+MODULE_AUTHOR("Ken Kuang <ken.kuang@spreadtrum.com>");
+MODULE_ALIAS("platform:sprd-audio-power");
+MODULE_LICENSE("GPL");
+
+#define SPRD_AUDIO_DEVICE(label) \
+{ \
+ .name = "sprd-audio-power", \
+ .id = SPRD_AUDIO_POWER_##label, \
+ .dev = { \
+ .platform_data = &label##_data, \
+ }, \
+}
+
+static struct platform_device sprd_audio_regulator_devices[] = {
+ SPRD_AUDIO_DEVICE(VREG),
+ SPRD_AUDIO_DEVICE(VB),
+ SPRD_AUDIO_DEVICE(BG),
+ SPRD_AUDIO_DEVICE(BG_IBIAS),
+ SPRD_AUDIO_DEVICE(VCOM),
+ SPRD_AUDIO_DEVICE(VCOM_BUF),
+ SPRD_AUDIO_DEVICE(VBO),
+ SPRD_AUDIO_DEVICE(MICBIAS),
+ SPRD_AUDIO_DEVICE(AUXMICBIAS),
+};
+
+#ifdef CONFIG_OF
+static int sprd_audio_power_version(void)
+{
+ struct device_node *node = of_find_node_by_path("/sprd-audio-devices");
+ struct device_node *child;
+ if (node) {
+ u32 val;
+ for_each_child_of_node(node, child) {
+ if (!of_property_read_u32
+ (child, "sprd,audio_power_ver", &val)) {
+ sp_asoc_pr_dbg
+ ("Configure Audio Power Version is %d\n",
+ val);
+ return val;
+ }
+ }
+ pr_err("ERR:Not found sprd,audio_power_ver!\n");
+ } else {
+ pr_err("ERR:No sprd-audio-devices Node!\n");
+ }
+ return 0;
+}
+#else
+int sprd_audio_power_version(void)
+ __attribute__ ((weak, alias("__sprd_audio_power_version")));
+
+static int __sprd_audio_power_version(void)
+{
+ return 1;
+}
+#endif
+
+static int sprd_audio_power_add_devices(void)
+{
+ int ret = 0;
+ int i;
+ if (1 != sprd_audio_power_version()) {
+ return 0;
+ }
+ for (i = 0; i < ARRAY_SIZE(sprd_audio_regulator_devices); i++) {
+ ret =
+ platform_device_register(&sprd_audio_regulator_devices[i]);
+ if (ret < 0) {
+ pr_err("ERR:Add Regulator %d Failed %d\n",
+ sprd_audio_regulator_devices[i].id, ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int __init sprd_audio_power_init(void)
+{
+ return sprd_audio_power_add_devices();
+}
+
+postcore_initcall(sprd_audio_power_init);
diff --git a/sound/soc/sprd/codec/sprd/v1/sprd-audio-power.h b/sound/soc/sprd/codec/sprd/v1/sprd-audio-power.h
new file mode 100644
index 00000000..6bd73bb4
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v1/sprd-audio-power.h
@@ -0,0 +1,33 @@
+/*
+ * sound/soc/sprd/codec/sprd/v1/sprd-audio-power.h
+ *
+ * SPRD-AUDIO-POWER -- SpreadTrum intergrated audio power supply.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * 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 __SPRD_AUDIO_POWER_H
+#define __SPRD_AUDIO_POWER_H
+
+#include "sprd-codec.h"
+
+#define SPRD_AUDIO_POWER_VREG 1
+#define SPRD_AUDIO_POWER_VB 2
+#define SPRD_AUDIO_POWER_BG 3
+#define SPRD_AUDIO_POWER_BG_IBIAS 4
+#define SPRD_AUDIO_POWER_VCOM 5
+#define SPRD_AUDIO_POWER_VCOM_BUF 6
+#define SPRD_AUDIO_POWER_VBO 7
+#define SPRD_AUDIO_POWER_MICBIAS 8
+#define SPRD_AUDIO_POWER_AUXMICBIAS 9
+
+#endif
diff --git a/sound/soc/sprd/codec/sprd/v1/sprd-codec.c b/sound/soc/sprd/codec/sprd/v1/sprd-codec.c
new file mode 100644
index 00000000..0fb6de91
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v1/sprd-codec.c
@@ -0,0 +1,3006 @@
+/*
+ * sound/soc/sprd/codec/sprd/v1/sprd-codec.c
+ *
+ * SPRD-CODEC -- SpreadTrum Tiger intergrated codec.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "sprd-asoc-debug.h"
+#define pr_fmt(fmt) pr_sprd_fmt("CDCV1") fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/stat.h>
+#include <linux/atomic.h>
+#include <linux/regulator/consumer.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/power_supply.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/sprd-audio-hook.h>
+
+#include <mach/sprd-audio.h>
+#include <mach/globalregs.h>
+#include "sprd-codec.h"
+#include "sprd-asoc-common.h"
+
+#define SOC_REG(r) ((unsigned short)(r))
+#define FUN_REG(f) ((unsigned short)(-((f) + 1)))
+#define ID_FUN(id, lr) ((unsigned short)(((id) << 1) | (lr)))
+
+#define SPRD_CODEC_AP_BASE_HI (SPRD_CODEC_AP_BASE & 0xFFFF0000)
+#define SPRD_CODEC_DP_BASE_HI (SPRD_CODEC_DP_BASE & 0xFFFF0000)
+
+enum {
+ SPRD_CODEC_PLAYBACK,
+ SPRD_CODEC_CAPTRUE,
+ SPRD_CODEC_CHAN_MAX,
+};
+
+static const char *sprd_codec_chan_name[SPRD_CODEC_CHAN_MAX] = {
+ "DAC",
+ "ADC",
+};
+
+static inline const char *sprd_codec_chan_get_name(int chan_id)
+{
+ return sprd_codec_chan_name[chan_id];
+}
+
+enum {
+ SPRD_CODEC_PGA_SPKL = 0,
+ SPRD_CODEC_PGA_SPKR,
+ SPRD_CODEC_PGA_HPL,
+ SPRD_CODEC_PGA_HPR,
+ SPRD_CODEC_PGA_EAR,
+ SPRD_CODEC_PGA_ADCL,
+ SPRD_CODEC_PGA_ADCR,
+
+ SPRD_CODEC_PGA_MAX
+};
+
+static const char *sprd_codec_pga_debug_str[SPRD_CODEC_PGA_MAX] = {
+ "SPKL",
+ "SPKR",
+ "HPL",
+ "HPR",
+ "EAR",
+ "ADCL",
+ "ADCR",
+};
+
+typedef int (*sprd_codec_pga_set) (struct snd_soc_codec * codec, int pgaval);
+
+struct sprd_codec_pga {
+ sprd_codec_pga_set set;
+ int min;
+};
+
+struct sprd_codec_pga_op {
+ int pgaval;
+ sprd_codec_pga_set set;
+};
+
+enum {
+ SPRD_CODEC_LEFT = 0,
+ SPRD_CODEC_RIGHT = 1,
+};
+
+enum {
+ SPRD_CODEC_MIXER_START = 0,
+ SPRD_CODEC_AIL = SPRD_CODEC_MIXER_START,
+ SPRD_CODEC_AIR,
+ SPRD_CODEC_MAIN_MIC,
+ SPRD_CODEC_AUX_MIC,
+ SPRD_CODEC_HP_MIC,
+ SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_HP_DACL = SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_HP_DACR,
+ SPRD_CODEC_HP_DAC_MIXER_MAX,
+ SPRD_CODEC_HP_ADCL = SPRD_CODEC_HP_DAC_MIXER_MAX,
+ SPRD_CODEC_HP_ADCR,
+ SPRD_CODEC_HP_MIXER_MAX,
+ SPRD_CODEC_SPK_DACL = SPRD_CODEC_HP_MIXER_MAX,
+ SPRD_CODEC_SPK_DACR,
+ SPRD_CODEC_SPK_DAC_MIXER_MAX,
+ SPRD_CODEC_SPK_ADCL = SPRD_CODEC_SPK_DAC_MIXER_MAX,
+ SPRD_CODEC_SPK_ADCR,
+ SPRD_CODEC_SPK_MIXER_MAX,
+ SPRD_CODEC_EAR_DACL = SPRD_CODEC_SPK_MIXER_MAX,
+ SPRD_CODEC_EAR_MIXER_MAX,
+
+ SPRD_CODEC_MIXER_MAX = SPRD_CODEC_EAR_MIXER_MAX << SPRD_CODEC_RIGHT
+};
+
+static const char *sprd_codec_mixer_debug_str[SPRD_CODEC_MIXER_MAX] = {
+ "AIL->ADCL",
+ "AIL->ADCR",
+ "AIR->ADCL",
+ "AIR->ADCR",
+ "MAIN MIC->ADCL",
+ "MAIN MIC->ADCR",
+ "AUX MIC->ADCL",
+ "AUX MIC->ADCR",
+ "HP MIC->ADCL",
+ "HP MIC->ADCR",
+ "DACL->HPL",
+ "DACL->HPR",
+ "DACR->HPL",
+ "DACR->HPR",
+ "ADCL->HPL",
+ "ADCL->HPR",
+ "ADCR->HPL",
+ "ADCR->HPR",
+ "DACL->SPKL",
+ "DACL->SPKR",
+ "DACR->SPKL",
+ "DACR->SPKR",
+ "ADCL->SPKL",
+ "ADCL->SPKR",
+ "ADCR->SPKL",
+ "ADCR->SPKR",
+ "DACL->EAR",
+ "DACR->EAR(bug)"
+};
+
+#define IS_SPRD_CODEC_MIXER_RANG(reg) (((reg) >= SPRD_CODEC_MIXER_START) && ((reg) <= SPRD_CODEC_MIXER_MAX))
+
+typedef int (*sprd_codec_mixer_set) (struct snd_soc_codec * codec, int on);
+struct sprd_codec_mixer {
+ int on;
+ sprd_codec_mixer_set set;
+};
+
+uint32_t sprd_get_vbat_voltage(void)
+ __attribute__ ((weak, alias("__sprd_get_vbat_voltage")));
+static uint32_t __sprd_get_vbat_voltage(void)
+{
+ pr_err("ERR: Can't get vbat!\n");
+ return 3800;
+}
+
+struct sprd_codec_ldo_v_map {
+ int ldo_v_level;
+ int volt;
+};
+
+const static struct sprd_codec_ldo_v_map ldo_v_map[] = {
+ /*{ , 3400}, */
+ {LDO_V_29, 3600},
+ {LDO_V_31, 3700},
+ {LDO_V_32, 3800},
+ {LDO_V_33, 3900},
+ {LDO_V_34, 4000},
+ {LDO_V_35, 4100},
+ {LDO_V_36, 4200},
+ {LDO_V_38, 4300},
+};
+
+const static struct sprd_codec_ldo_v_map ldo_vcom_v_map[] = {
+ /*{ , 3400}, */
+ {LDO_V_29, 3800},
+ {LDO_V_31, 3900},
+ {LDO_V_32, 4000},
+ {LDO_V_33, 4100},
+ {LDO_V_34, 4200},
+ {LDO_V_35, 4300},
+ {LDO_V_36, 4400},
+ {LDO_V_38, 4500},
+};
+
+struct sprd_codec_ldo_cfg {
+ const struct sprd_codec_ldo_v_map *v_map;
+ int v_map_size;
+};
+
+struct sprd_codec_inter_pa {
+ /* FIXME little endian */
+ int LDO_V_sel:4;
+ int DTRI_F_sel:4;
+ int is_DEMI_mode:1;
+ int is_classD_mode:1;
+ int is_LDO_mode:1;
+ int is_auto_LDO_mode:1;
+ int RESV:20;
+};
+
+struct sprd_codec_pa_setting {
+ union {
+ struct sprd_codec_inter_pa setting;
+ u32 value;
+ };
+ int set;
+};
+
+enum {
+ SPRD_CODEC_MIC_BIAS,
+ SPRD_CODEC_AUXMIC_BIAS,
+ SPRD_CODEC_MIC_BIAS_MAX
+};
+
+static const char *mic_bias_name[SPRD_CODEC_MIC_BIAS_MAX] = {
+ "Mic Bias",
+ "AuxMic Bias",
+};
+
+/* codec private data */
+struct sprd_codec_priv {
+ struct snd_soc_codec *codec;
+ atomic_t adie_dac_refcount;
+ atomic_t adie_adc_refcount;
+ int da_sample_val;
+ int ad_sample_val;
+ struct sprd_codec_mixer mixer[SPRD_CODEC_MIXER_MAX];
+ struct sprd_codec_pga_op pga[SPRD_CODEC_PGA_MAX];
+ int mic_bias[SPRD_CODEC_MIC_BIAS_MAX];
+
+ int ap_irq;
+ struct completion completion_hp_pop;
+
+ int dp_irq;
+ struct completion completion_dac_mute;
+ struct power_supply audio_ldo;
+
+ struct sprd_codec_pa_setting inter_pa;
+ struct mutex inter_pa_mutex;
+
+ struct regulator *main_mic;
+ struct regulator *aux_mic;
+ atomic_t ldo_refcount;
+ struct delayed_work ovp_delayed_work;
+
+ struct sprd_codec_ldo_cfg s_vcom;
+ struct sprd_codec_ldo_cfg s_vpa;
+
+#define SPRD_CODEC_PA_SW_AOL (BIT(0))
+#define SPRD_CODEC_PA_SW_EAR (BIT(1))
+#define SPRD_CODEC_PA_SW_FUN (SPRD_CODEC_PA_SW_AOL | SPRD_CODEC_PA_SW_EAR)
+ int sprd_codec_fun;
+ spinlock_t sprd_codec_fun_lock;
+ spinlock_t sprd_codec_pa_sw_lock;
+};
+
+static int sprd_codec_power_get(struct device *dev, struct regulator **regu,
+ const char *id)
+{
+ if (!*regu) {
+ *regu = regulator_get(dev, id);
+ if (IS_ERR(*regu)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(*regu), id);
+ *regu = 0;
+ return PTR_ERR(*regu);
+ }
+ }
+ return 0;
+}
+
+static int sprd_codec_power_put(struct regulator **regu)
+{
+ if (*regu) {
+ regulator_set_mode(*regu, REGULATOR_MODE_NORMAL);
+ regulator_disable(*regu);
+ regulator_put(*regu);
+ *regu = 0;
+ }
+ return 0;
+}
+
+static int sprd_codec_mic_bias_set(struct regulator **regu, int on)
+{
+ int ret = 0;
+ if (*regu) {
+ if (on) {
+ ret = regulator_enable(*regu);
+ if (ret) {
+ sprd_codec_power_put(regu);
+ return ret;
+ }
+ } else {
+ ret = regulator_disable(*regu);
+ }
+ }
+ return ret;
+}
+
+#ifdef CONFIG_SND_SOC_SPRD_AUDIO_DEBUG
+static const char *get_event_name(int event)
+{
+ const char *ev_name;
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ev_name = "PRE_PMU";
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ ev_name = "POST_PMU";
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ ev_name = "PRE_PMD";
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ev_name = "POST_PMD";
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+ return ev_name;
+}
+#endif
+
+static void sprd_codec_set_fun(struct sprd_codec_priv *sprd_codec, int fun)
+{
+ spin_lock(&sprd_codec->sprd_codec_fun_lock);
+ sprd_codec->sprd_codec_fun |= fun;
+ spin_unlock(&sprd_codec->sprd_codec_fun_lock);
+}
+
+static void sprd_codec_clr_fun(struct sprd_codec_priv *sprd_codec, int fun)
+{
+ spin_lock(&sprd_codec->sprd_codec_fun_lock);
+ sprd_codec->sprd_codec_fun &= ~fun;
+ spin_unlock(&sprd_codec->sprd_codec_fun_lock);
+}
+
+static int sprd_codec_test_fun(struct sprd_codec_priv *sprd_codec, int fun)
+{
+ int ret;
+ spin_lock(&sprd_codec->sprd_codec_fun_lock);
+ ret = sprd_codec->sprd_codec_fun & fun;
+ spin_unlock(&sprd_codec->sprd_codec_fun_lock);
+ return ret;
+}
+
+static void sprd_codec_wait(u32 wait_time)
+{
+ if (wait_time)
+ schedule_timeout_uninterruptible(msecs_to_jiffies(wait_time));
+}
+
+#if 0 /* sometime we will use this print function */
+static unsigned int sprd_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg);
+static void sprd_codec_print_regs(struct snd_soc_codec *codec)
+{
+ int reg;
+ pr_warn("sprd_codec register digital part\n");
+ for (reg = SPRD_CODEC_DP_BASE; reg < SPRD_CODEC_DP_END; reg += 0x10) {
+ pr_warn("0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (reg - SPRD_CODEC_DP_BASE)
+ , sprd_codec_read(codec, reg + 0x00)
+ , sprd_codec_read(codec, reg + 0x04)
+ , sprd_codec_read(codec, reg + 0x08)
+ , sprd_codec_read(codec, reg + 0x0C)
+ );
+ }
+ pr_warn("sprd_codec register analog part\n");
+ for (reg = SPRD_CODEC_AP_BASE; reg < SPRD_CODEC_AP_END; reg += 0x10) {
+ pr_warn("0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (reg - SPRD_CODEC_AP_BASE)
+ , sprd_codec_read(codec, reg + 0x00)
+ , sprd_codec_read(codec, reg + 0x04)
+ , sprd_codec_read(codec, reg + 0x08)
+ , sprd_codec_read(codec, reg + 0x0C)
+ );
+ }
+}
+#endif
+
+static inline int _sprd_codec_ldo_cfg_set(struct sprd_codec_ldo_cfg *p_v_cfg, const struct sprd_codec_ldo_v_map
+ *v_map, const int v_map_size)
+{
+ p_v_cfg->v_map = v_map;
+ p_v_cfg->v_map_size = v_map_size;
+ return 0;
+}
+
+static inline int _sprd_codec_hold(int index, int volt,
+ const struct sprd_codec_ldo_v_map *v_map)
+{
+ int scope = 40; /* unit: mv */
+ int ret = ((v_map[index].volt - volt) <= scope);
+ if (index >= 1) {
+ return (ret || ((volt - v_map[index - 1].volt) <= scope));
+ }
+ return ret;
+}
+
+static int sprd_codec_auto_ldo_volt(struct snd_soc_codec *codec,
+ void (*set_level) (struct snd_soc_codec *,
+ int),
+ const struct sprd_codec_ldo_v_map *v_map,
+ const int v_map_size, int init)
+{
+ int i;
+ int volt = sprd_get_vbat_voltage();
+ sp_asoc_pr_dbg("Get vbat voltage %d\n", volt);
+ for (i = 0; i < v_map_size; i++) {
+ if (volt <= ldo_v_map[i].volt) {
+ sp_asoc_pr_dbg("Hold %d\n",
+ _sprd_codec_hold(i, volt, v_map));
+ if (init || !_sprd_codec_hold(i, volt, v_map)) {
+ set_level(codec, v_map[i].ldo_v_level);
+ }
+ return 0;
+ }
+ }
+ return -EFAULT;
+}
+
+static inline void sprd_codec_vcm_v_sel(struct snd_soc_codec *codec, int v_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("VCM Set %d\n", v_sel);
+ mask = VCM_V_MASK << VCM_V;
+ val = (v_sel << VCM_V) & mask;
+ snd_soc_update_bits(codec, SOC_REG(PMUR3), mask, val);
+}
+
+static int sprd_codec_pga_spk_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DCGR3;
+ val = (pgaval & 0xF) << 4;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0xF0, val);
+}
+
+static int sprd_codec_pga_spkr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DCGR3;
+ val = pgaval & 0xF;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x0F, val);
+}
+
+static int sprd_codec_pga_hpl_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DCGR1;
+ val = (pgaval & 0xF) << 4;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0xF0, val);
+}
+
+static int sprd_codec_pga_hpr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DCGR1;
+ val = pgaval & 0xF;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x0F, val);
+}
+
+static int sprd_codec_pga_ear_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DCGR2;
+ val = ((pgaval & 0xF) << 4);
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0xF0, val);
+}
+
+static int sprd_codec_pga_adcl_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = ACGR;
+ val = (pgaval & 0xF) << 4;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0xF0, val);
+}
+
+static int sprd_codec_pga_adcr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = ACGR;
+ val = pgaval & 0xF;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x0F, val);
+}
+
+static struct sprd_codec_pga sprd_codec_pga_cfg[SPRD_CODEC_PGA_MAX] = {
+ {sprd_codec_pga_spk_set, 0},
+ {sprd_codec_pga_spkr_set, 0},
+ {sprd_codec_pga_hpl_set, 0},
+ {sprd_codec_pga_hpr_set, 0},
+ {sprd_codec_pga_ear_set, 0},
+
+ {sprd_codec_pga_adcl_set, 0},
+ {sprd_codec_pga_adcr_set, 0},
+};
+
+/* adc mixer */
+
+static int vcmadcl_set(struct snd_soc_codec *codec)
+{
+ int need_on;
+ need_on = snd_soc_read(codec, AAICR3) & (BIT(AIL_ADCL) | BIT(AIR_ADCL));
+ return snd_soc_update_bits(codec, SOC_REG(AAICR3), BIT(VCM_ADCL),
+ (! !need_on) << VCM_ADCL);
+}
+
+static int vcmadcr_set(struct snd_soc_codec *codec)
+{
+ int need_on;
+ need_on = snd_soc_read(codec, AAICR3) & (BIT(AIL_ADCR) | BIT(AIR_ADCR));
+ return snd_soc_update_bits(codec, SOC_REG(AAICR3), BIT(VCM_ADCR),
+ (! !need_on) << VCM_ADCR);
+}
+
+static int ailadcl_set(struct snd_soc_codec *codec, int on)
+{
+ int ret;
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ ret = snd_soc_update_bits(codec, SOC_REG(AAICR3), BIT(AIL_ADCL),
+ on << AIL_ADCL);
+ if (ret < 0)
+ return ret;
+ return vcmadcl_set(codec);
+}
+
+static int ailadcr_set(struct snd_soc_codec *codec, int on)
+{
+ int ret;
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ ret = snd_soc_update_bits(codec, SOC_REG(AAICR3), BIT(AIL_ADCR),
+ on << AIL_ADCR);
+ if (ret < 0)
+ return ret;
+ return vcmadcr_set(codec);
+}
+
+static int airadcl_set(struct snd_soc_codec *codec, int on)
+{
+ int ret;
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ ret = snd_soc_update_bits(codec, SOC_REG(AAICR3), BIT(AIR_ADCL),
+ on << AIR_ADCL);
+ if (ret < 0)
+ return ret;
+ return vcmadcl_set(codec);
+}
+
+static int airadcr_set(struct snd_soc_codec *codec, int on)
+{
+ int ret;
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ ret = snd_soc_update_bits(codec, SOC_REG(AAICR3), BIT(AIR_ADCR),
+ on << AIR_ADCR);
+ if (ret < 0)
+ return ret;
+ return vcmadcr_set(codec);
+}
+
+static int mainmicadcl_set(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ mask = BIT(MICP_ADCL) | BIT(MICN_ADCL);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR1), mask, on ? mask : 0);
+}
+
+static int mainmicadcr_set(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ mask = BIT(MICP_ADCR) | BIT(MICN_ADCR);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR1), mask, on ? mask : 0);
+}
+
+static int auxmicadcl_set(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ mask = BIT(AUXMICP_ADCL) | BIT(AUXMICN_ADCL);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2), mask, on ? mask : 0);
+}
+
+static int auxmicadcr_set(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ mask = BIT(AUXMICP_ADCR) | BIT(AUXMICN_ADCR);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2), mask, on ? mask : 0);
+}
+
+static int hpmicadcl_set(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ mask = BIT(HPMICP_ADCL) | BIT(HPMICN_ADCL);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR1), mask, on ? mask : 0);
+}
+
+static int hpmicadcr_set(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ mask = BIT(HPMICP_ADCR) | BIT(HPMICN_ADCR);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR1), mask, on ? mask : 0);
+}
+
+/* adcpga*_en setting */
+static int adcpgax_auto_setting(struct snd_soc_codec *codec)
+{
+ unsigned int reg1, reg2, reg3;
+ int val;
+ int mask;
+ reg1 = snd_soc_read(codec, DAOCR1);
+ reg2 = snd_soc_read(codec, DAOCR2);
+ reg3 = snd_soc_read(codec, DAOCR3);
+ val = 0;
+ if ((reg1 & BIT(ADCL_P_HPL)) || (reg2 & BIT(ADCL_P_AOLP))
+ || (reg3 & BIT(ADCL_P_AORP))) {
+ val |= BIT(ADCPGAL_P_EN);
+ }
+ if ((reg1 & BIT(ADCL_N_HPR)) || (reg2 & BIT(ADCL_N_AOLN))
+ || (reg3 & BIT(ADCL_N_AORN))) {
+ val |= BIT(ADCPGAL_N_EN);
+ }
+ if ((reg1 & (BIT(ADCR_P_HPL) | BIT(ADCR_P_HPR)))
+ || (reg2 & BIT(ADCR_P_AOLP)) || (reg3 & BIT(ADCR_P_AORP))) {
+ val |= BIT(ADCPGAR_P_EN);
+ }
+ if ((reg2 & BIT(ADCR_N_AOLN)) || (reg3 & BIT(ADCR_N_AORN))) {
+ val |= BIT(ADCPGAR_N_EN);
+ }
+ mask =
+ BIT(ADCPGAL_P_EN) | BIT(ADCPGAL_N_EN) | BIT(ADCPGAR_P_EN) |
+ BIT(ADCPGAR_N_EN);
+ return snd_soc_update_bits(codec, SOC_REG(AACR2), mask, val);
+}
+
+/* hp mixer */
+
+static int daclhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR1), BIT(DACL_P_HPL),
+ on << DACL_P_HPL);
+}
+
+static int daclhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR1), BIT(DACL_N_HPR),
+ on << DACL_N_HPR);
+}
+
+static int dacrhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR1), BIT(DACR_P_HPL),
+ on << DACR_P_HPL);
+}
+
+static int dacrhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR1), BIT(DACR_P_HPR),
+ on << DACR_P_HPR);
+}
+
+static int adclhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ snd_soc_update_bits(codec, SOC_REG(DAOCR1), BIT(ADCL_P_HPL),
+ on << ADCL_P_HPL);
+ return adcpgax_auto_setting(codec);
+}
+
+static int adclhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ snd_soc_update_bits(codec, SOC_REG(DAOCR1), BIT(ADCL_N_HPR),
+ on << ADCL_N_HPR);
+ return adcpgax_auto_setting(codec);
+}
+
+static int adcrhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ snd_soc_update_bits(codec, SOC_REG(DAOCR1), BIT(ADCR_P_HPL),
+ on << ADCR_P_HPL);
+ return adcpgax_auto_setting(codec);
+}
+
+static int adcrhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ snd_soc_update_bits(codec, SOC_REG(DAOCR1), BIT(ADCR_P_HPR),
+ on << ADCR_P_HPR);
+ return adcpgax_auto_setting(codec);
+}
+
+/* spkl mixer */
+
+static int daclspkl_set(struct snd_soc_codec *codec, int on)
+{
+ int mask = BIT(DACL_P_AOLP) | BIT(DACL_N_AOLN);
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR2), mask, on ? mask : 0);
+}
+
+static int dacrspkl_set(struct snd_soc_codec *codec, int on)
+{
+ int mask = BIT(DACR_P_AOLP) | BIT(DACR_N_AOLN);
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR2), mask, on ? mask : 0);
+}
+
+static int adclspkl_set(struct snd_soc_codec *codec, int on)
+{
+ int mask = BIT(ADCL_P_AOLP) | BIT(ADCL_N_AOLN);
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ snd_soc_update_bits(codec, SOC_REG(DAOCR2), mask, on ? mask : 0);
+ return adcpgax_auto_setting(codec);
+}
+
+static int adcrspkl_set(struct snd_soc_codec *codec, int on)
+{
+ int mask = BIT(ADCR_P_AOLP) | BIT(ADCR_N_AOLN);
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ snd_soc_update_bits(codec, SOC_REG(DAOCR2), mask, on ? mask : 0);
+ return adcpgax_auto_setting(codec);
+}
+
+/* spkr mixer */
+
+static int daclspkr_set(struct snd_soc_codec *codec, int on)
+{
+ int mask = BIT(DACL_P_AORP) | BIT(DACL_N_AORN);
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR3), mask, on ? mask : 0);
+}
+
+static int dacrspkr_set(struct snd_soc_codec *codec, int on)
+{
+ int mask = BIT(DACR_P_AORP) | BIT(DACR_N_AORN);
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR3), mask, on ? mask : 0);
+}
+
+static int adclspkr_set(struct snd_soc_codec *codec, int on)
+{
+ int mask = BIT(ADCL_P_AORP) | BIT(ADCL_N_AORN);
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ snd_soc_update_bits(codec, SOC_REG(DAOCR3), mask, on ? mask : 0);
+ return adcpgax_auto_setting(codec);
+}
+
+static int adcrspkr_set(struct snd_soc_codec *codec, int on)
+{
+ int mask = BIT(ADCR_P_AORP) | BIT(ADCR_N_AORN);
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ snd_soc_update_bits(codec, SOC_REG(DAOCR3), mask, on ? mask : 0);
+ return adcpgax_auto_setting(codec);
+}
+
+/* ear mixer */
+
+static int daclear_set(struct snd_soc_codec *codec, int on)
+{
+ int mask = BIT(DACL_P_EARP) | BIT(DACL_N_EARN);
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR4), mask, on ? mask : 0);
+}
+
+static sprd_codec_mixer_set mixer_setting[SPRD_CODEC_MIXER_MAX] = {
+ /* adc mixer */
+ ailadcl_set, ailadcr_set,
+ airadcl_set, airadcr_set,
+ mainmicadcl_set, mainmicadcr_set,
+ auxmicadcl_set, auxmicadcr_set,
+ hpmicadcl_set, hpmicadcr_set,
+ /* hp mixer */
+ daclhpl_set, daclhpr_set,
+ dacrhpl_set, dacrhpr_set,
+ adclhpl_set, adclhpr_set,
+ adcrhpl_set, adcrhpr_set,
+ /* spk mixer */
+ daclspkl_set, daclspkr_set,
+ dacrspkl_set, dacrspkr_set,
+ adclspkl_set, adclspkr_set,
+ adcrspkl_set, adcrspkr_set,
+ /* ear mixer */
+ daclear_set, 0,
+};
+
+/* DO NOT USE THIS FUNCTION */
+static inline void __sprd_codec_pa_sw_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ mask = BIT(PA_SW_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(PMUR2), mask, val);
+}
+
+static inline void sprd_codec_pa_sw_set(struct snd_soc_codec *codec, int fun)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ sp_asoc_pr_dbg("%s fun 0x%08x\n", __func__, fun);
+ spin_lock(&sprd_codec->sprd_codec_pa_sw_lock);
+ sprd_codec_set_fun(sprd_codec, fun);
+ __sprd_codec_pa_sw_en(codec, 1);
+ spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock);
+}
+
+static inline void sprd_codec_pa_sw_clr(struct snd_soc_codec *codec, int fun)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ sp_asoc_pr_dbg("%s fun 0x%08x\n", __func__, fun);
+ spin_lock(&sprd_codec->sprd_codec_pa_sw_lock);
+ sprd_codec_clr_fun(sprd_codec, fun);
+ if (!sprd_codec_test_fun(sprd_codec, SPRD_CODEC_PA_SW_FUN))
+ __sprd_codec_pa_sw_en(codec, 0);
+ spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock);
+}
+
+/* inter PA */
+
+static inline void sprd_codec_pa_d_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ mask = BIT(PA_D_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR2), mask, val);
+}
+
+static inline void sprd_codec_pa_demi_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ mask = BIT(PA_DEMI_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR2), mask, val);
+
+ mask = BIT(DRV_OCP_AOL_PD) | BIT(DRV_OCP_AOR_PD);
+ val = mask;
+ snd_soc_update_bits(codec, SOC_REG(DCR3), mask, val);
+}
+
+static inline void sprd_codec_pa_ldo_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ mask = BIT(PA_LDO_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(PMUR2), mask, val);
+ if (on) {
+ sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL);
+ }
+}
+
+static inline void sprd_codec_pa_ldo_v_sel(struct snd_soc_codec *codec,
+ int v_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, v_sel);
+ mask = PA_LDO_V_MASK << PA_LDO_V;
+ val = (v_sel << PA_LDO_V) & mask;
+ snd_soc_update_bits(codec, SOC_REG(PMUR4), mask, val);
+}
+
+static inline void sprd_codec_pa_ovp_v_sel(struct snd_soc_codec *codec,
+ int v_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, v_sel);
+ mask = PA_OVP_V_MASK << PA_OVP_V;
+ val = (v_sel << PA_OVP_V) & mask;
+ snd_soc_update_bits(codec, SOC_REG(PMUR6), mask, val);
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_OVPTMR_CTL), 0x1A, 0x3F);
+}
+
+static inline void sprd_codec_pa_dtri_f_sel(struct snd_soc_codec *codec,
+ int f_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, f_sel);
+ mask = PA_DTRI_F_MASK << PA_DTRI_F;
+ val = (f_sel << PA_DTRI_F) & mask;
+ snd_soc_update_bits(codec, SOC_REG(DCR2), mask, val);
+}
+
+static inline void sprd_codec_pa_en(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ spin_lock(&sprd_codec->sprd_codec_pa_sw_lock);
+ if (on) {
+ mask = BIT(PA_EN);
+ val = mask;
+ } else {
+ if (!sprd_codec_test_fun(sprd_codec, SPRD_CODEC_PA_SW_FUN))
+ mask = BIT(PA_EN) | BIT(PA_SW_EN) | BIT(PA_LDO_EN);
+ else
+ mask = BIT(PA_EN) | BIT(PA_LDO_EN);
+ val = 0;
+ }
+ snd_soc_update_bits(codec, SOC_REG(PMUR2), mask, val);
+ spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock);
+}
+
+static inline void sprd_codec_inter_pa_init(struct sprd_codec_priv *sprd_codec)
+{
+ sprd_codec->inter_pa.setting.LDO_V_sel = 0x03;
+ sprd_codec->inter_pa.setting.DTRI_F_sel = 0x01;
+}
+
+static void sprd_codec_ovp_irq_enable(struct snd_soc_codec *codec)
+{
+ int mask = BIT(OVP_IRQ);
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_CLR), mask, mask);
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), mask, mask);
+}
+
+static void sprd_codec_ovp_start(struct sprd_codec_priv *sprd_codec, int ms)
+{
+ schedule_delayed_work(&sprd_codec->ovp_delayed_work,
+ msecs_to_jiffies(ms));
+}
+
+static int sprd_ovp_irq(struct snd_soc_codec *codec, int is_ovp)
+{
+ int volt = sprd_get_vbat_voltage();
+ sp_asoc_pr_dbg("OVP %d curr_volt %d\n", is_ovp, volt);
+ if (is_ovp) {
+ sprd_codec_pa_en(codec, 0);
+ snd_soc_update_bits(codec, SOC_REG(DCR1), BIT(AOL_EN), 0);
+ } else {
+ snd_soc_update_bits(codec, SOC_REG(DCR1), BIT(AOL_EN),
+ BIT(AOL_EN));
+ sprd_codec_pa_en(codec, 1);
+ }
+ return 0;
+}
+
+#define to_sprd_codec(x) container_of((x), struct sprd_codec_priv, ovp_delayed_work)
+#define to_delay_worker(x) container_of((x), struct delayed_work, work)
+static void sprd_codec_ovp_delay_worker(struct work_struct *work)
+{
+ struct sprd_codec_priv *sprd_codec =
+ to_sprd_codec(to_delay_worker(work));
+ struct snd_soc_codec *codec = sprd_codec->codec;
+
+ if (!(snd_soc_read(codec, IFR1) & BIT(OVP_FLAG))) {
+ sprd_ovp_irq(codec, 0);
+ } else {
+ sprd_ovp_irq(codec, 1);
+ sprd_codec_ovp_start(sprd_codec, 500);
+ }
+}
+
+static inline int sprd_codec_pa_ldo_auto(struct snd_soc_codec *codec, int init)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ return sprd_codec_auto_ldo_volt
+ (codec, sprd_codec_pa_ldo_v_sel, sprd_codec->s_vpa.v_map,
+ sprd_codec->s_vpa.v_map_size, init);
+}
+
+static inline int sprd_codec_pa_ldo_cfg(struct sprd_codec_priv *sprd_codec, const struct sprd_codec_ldo_v_map
+ *v_map, const int v_map_size)
+{
+ return _sprd_codec_ldo_cfg_set(&sprd_codec->s_vpa, v_map, v_map_size);
+}
+
+static int sprd_inter_speaker_pa(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ struct sprd_codec_inter_pa *p_setting = &sprd_codec->inter_pa.setting;
+ sp_asoc_pr_info("inter PA Switch %s\n", STR_ON_OFF(on));
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+ if (on) {
+ sprd_codec_pa_ovp_v_sel(codec, PA_OVP_465);
+ sprd_codec_ovp_irq_enable(codec);
+ sprd_codec_pa_d_en(codec, p_setting->is_classD_mode);
+ sprd_codec_pa_demi_en(codec, p_setting->is_DEMI_mode);
+ sprd_codec_pa_ldo_en(codec, p_setting->is_LDO_mode);
+ if (p_setting->is_LDO_mode) {
+ if (p_setting->is_auto_LDO_mode) {
+ sprd_codec_pa_ldo_auto(codec, 1);
+ } else {
+ sprd_codec_pa_ldo_v_sel(codec,
+ p_setting->LDO_V_sel);
+ }
+ } else {
+ sprd_codec_pa_sw_set(codec, SPRD_CODEC_PA_SW_AOL);
+ }
+ sprd_codec_pa_dtri_f_sel(codec, p_setting->DTRI_F_sel);
+ sprd_codec_pa_en(codec, 1);
+ sprd_codec->inter_pa.set = 1;
+ } else {
+ sprd_codec->inter_pa.set = 0;
+ sprd_codec_pa_en(codec, 0);
+ sprd_codec_wait(2);
+ sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL);
+ sprd_codec_pa_ldo_en(codec, 0);
+ }
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ return 0;
+}
+
+static int spk_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+ return sprd_inter_speaker_pa(w->codec, on);
+}
+
+static int sprd_codec_set_sample_rate(struct snd_soc_codec *codec, int rate,
+ int mask, int shift)
+{
+ switch (rate) {
+ case 8000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_8000 << shift);
+ break;
+ case 11025:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_11025 << shift);
+ break;
+ case 16000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_16000 << shift);
+ break;
+ case 22050:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_22050 << shift);
+ break;
+ case 32000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_32000 << shift);
+ break;
+ case 44100:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_44100 << shift);
+ break;
+ case 48000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_48000 << shift);
+ break;
+ case 96000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_96000 << shift);
+ break;
+ default:
+ pr_err("ERR:SPRD-CODEC not support this rate %d\n", rate);
+ break;
+ }
+ sp_asoc_pr_dbg("Set Playback rate 0x%x\n",
+ snd_soc_read(codec, AUD_DAC_CTL));
+ return 0;
+}
+
+static int sprd_codec_set_ad_sample_rate(struct snd_soc_codec *codec, int rate,
+ int mask, int shift)
+{
+ int set;
+ set = rate / 4000;
+ if (set > 13) {
+ pr_err("ERR:SPRD-CODEC not support this ad rate %d\n", rate);
+ }
+ snd_soc_update_bits(codec, SOC_REG(AUD_ADC_CTL), mask, set << shift);
+ return 0;
+}
+
+static int sprd_codec_sample_rate_setting(struct sprd_codec_priv *sprd_codec)
+{
+ sp_asoc_pr_dbg("%s AD %d DA %d \n", __func__,
+ sprd_codec->ad_sample_val, sprd_codec->da_sample_val);
+ if (sprd_codec->ad_sample_val) {
+ sprd_codec_set_ad_sample_rate(sprd_codec->codec,
+ sprd_codec->ad_sample_val, 0x0F,
+ 0);
+ }
+ if (sprd_codec->da_sample_val) {
+ sprd_codec_set_sample_rate(sprd_codec->codec,
+ sprd_codec->da_sample_val, 0x0F, 0);
+ }
+ return 0;
+}
+
+static inline int sprd_codec_vcom_ldo_auto(struct snd_soc_codec *codec,
+ int init)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ return sprd_codec_auto_ldo_volt(codec, sprd_codec_vcm_v_sel,
+ sprd_codec->s_vcom.v_map,
+ sprd_codec->s_vcom.v_map_size, init);
+}
+
+static inline int sprd_codec_vcom_ldo_cfg(struct sprd_codec_priv *sprd_codec, const struct sprd_codec_ldo_v_map
+ *v_map, const int v_map_size)
+{
+ return _sprd_codec_ldo_cfg_set(&sprd_codec->s_vcom, v_map, v_map_size);
+}
+
+static DEFINE_MUTEX(ldo_mutex);
+static int sprd_codec_ldo_on(struct sprd_codec_priv *sprd_codec)
+{
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ mutex_lock(&ldo_mutex);
+ atomic_inc(&sprd_codec->ldo_refcount);
+ if (atomic_read(&sprd_codec->ldo_refcount) == 1) {
+ sp_asoc_pr_dbg("LDO ON!\n");
+ arch_audio_codec_analog_enable();
+ sprd_codec_vcom_ldo_auto(sprd_codec->codec, 1);
+ }
+ mutex_unlock(&ldo_mutex);
+
+ return 0;
+}
+
+static int sprd_codec_ldo_off(struct sprd_codec_priv *sprd_codec)
+{
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ mutex_lock(&ldo_mutex);
+ if (atomic_dec_and_test(&sprd_codec->ldo_refcount)) {
+ arch_audio_codec_analog_disable();
+ sp_asoc_pr_dbg("LDO OFF!\n");
+ }
+ mutex_unlock(&ldo_mutex);
+
+ return 0;
+}
+
+static int sprd_codec_analog_open(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+ sprd_codec_sample_rate_setting(sprd_codec);
+
+ /* SC8825 ask from ASIC to set initial value */
+ snd_soc_write(codec, AUD_SDM_CTL0, 0x400);
+ snd_soc_write(codec, AUD_SDM_CTL1, 0);
+
+ return ret;
+}
+
+static int sprd_codec_digital_open(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ /* FIXME : Some Clock SYNC bug will cause MUTE */
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), BIT(DAC_MUTE_EN), 0);
+
+ sprd_codec_sample_rate_setting(sprd_codec);
+
+ return ret;
+}
+
+static void sprd_codec_power_enable(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ ret = sprd_codec_ldo_on(sprd_codec);
+ if (ret != 0)
+ pr_err("ERR:SPRD-CODEC open ldo error %d\n", ret);
+
+ sprd_codec_analog_open(codec);
+}
+
+static void sprd_codec_power_disable(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ sprd_codec_ldo_off(sprd_codec);
+}
+
+static int digital_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ arch_audio_codec_digital_reg_enable();
+ arch_audio_codec_digital_reset();
+ sprd_codec_digital_open(w->codec);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ arch_audio_codec_digital_reg_disable();
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int analog_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ sprd_codec_power_enable(codec);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ sprd_codec_power_disable(codec);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int chan_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int chan_id = FUN_REG(w->reg);
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+
+ sp_asoc_pr_info("%s %s\n", sprd_codec_chan_get_name(chan_id),
+ STR_ON_OFF(on));
+
+ return 0;
+}
+
+static int adie_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ atomic_inc(&sprd_codec->adie_dac_refcount);
+ if (atomic_read(&sprd_codec->adie_dac_refcount) == 1) {
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_ENB),
+ BIT(AUDIFA_DAC_EN),
+ BIT(AUDIFA_DAC_EN));
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (atomic_dec_and_test(&sprd_codec->adie_dac_refcount)) {
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_ENB),
+ BIT(AUDIFA_DAC_EN), 0);
+ }
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int adie_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ atomic_inc(&sprd_codec->adie_adc_refcount);
+ if (atomic_read(&sprd_codec->adie_adc_refcount) == 1) {
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_ENB),
+ BIT(AUDIFA_ADC_EN),
+ BIT(AUDIFA_ADC_EN));
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (atomic_dec_and_test(&sprd_codec->adie_adc_refcount)) {
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_ENB),
+ BIT(AUDIFA_ADC_EN), 0);
+ }
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int dfm_out_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+
+ sp_asoc_pr_info("DFM-OUT %s\n", STR_ON_OFF(on));
+
+ return 0;
+}
+static int _mixer_set_mixer(struct snd_soc_codec *codec, int id, int lr,
+ int try_on, int need_set)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int reg = ID_FUN(id, lr);
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[reg]);
+ if (try_on) {
+ mixer->set = mixer_setting[reg];
+ /* NOTES: reduce linein pop noise must open ADCL/R ->HP MIXER
+ AFTER delay 250ms for both ADCL/ADCR switch complete. */
+ if (need_set)
+ return mixer->set(codec, mixer->on);
+ } else {
+ mixer_setting[reg] (codec, 0);
+ mixer->set = 0;
+ }
+ return 0;
+}
+
+static inline int _mixer_setting(struct snd_soc_codec *codec, int start,
+ int end, int lr, int try_on, int need_set)
+{
+ int id;
+ for (id = start; id < end; id++) {
+ _mixer_set_mixer(codec, id, lr, try_on, need_set);
+ }
+ return 0;
+}
+
+static inline int _mixer_setting_one(struct snd_soc_codec *codec, int id,
+ int try_on, int need_set)
+{
+ int lr = id & 0x1;
+ id >>= 1;
+ return _mixer_setting(codec, id, id + 1, lr, try_on, need_set);
+}
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+static void sprd_codec_hp_pop_irq_enable(struct snd_soc_codec *codec)
+{
+ int mask = BIT(AUDIO_POP_IRQ);
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_CLR), mask, mask);
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), mask, mask);
+}
+#endif
+
+static irqreturn_t sprd_codec_ap_irq(int irq, void *dev_id)
+{
+ int mask;
+ struct sprd_codec_priv *sprd_codec = dev_id;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ mask = snd_soc_read(codec, AUDIF_INT_MASK);
+ sp_asoc_pr_dbg("HP POP IRQ Mask = 0x%x\n", mask);
+ if (BIT(AUDIO_POP_IRQ) & mask) {
+ mask = BIT(AUDIO_POP_IRQ);
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), mask, 0);
+ complete(&sprd_codec->completion_hp_pop);
+ }
+ if (BIT(OVP_IRQ) & mask) {
+ mask = BIT(OVP_IRQ);
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), mask, 0);
+ sprd_codec_ovp_start(sprd_codec, 1);
+ sprd_codec_ovp_irq_enable(codec);
+ }
+ return IRQ_HANDLED;
+}
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+static inline int is_hp_pop_compelet(struct snd_soc_codec *codec)
+{
+ int val;
+ val = snd_soc_read(codec, IFR1);
+ val = (val >> HP_POP_FLG) & HP_POP_FLG_MASK;
+ sp_asoc_pr_dbg("HP POP= 0x%x\n", val);
+ return HP_POP_FLG_NEAR_CMP == val;
+}
+
+static inline int hp_pop_wait_for_compelet(struct snd_soc_codec *codec)
+{
+ int i;
+ int hp_pop_complete;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ hp_pop_complete = msecs_to_jiffies(SPRD_CODEC_HP_POP_TIMEOUT);
+ for (i = 0; i < 2; i++) {
+ sp_asoc_pr_dbg("HP POP %d IRQ Enable\n", i);
+ sprd_codec_hp_pop_irq_enable(codec);
+ init_completion(&sprd_codec->completion_hp_pop);
+ hp_pop_complete =
+ wait_for_completion_timeout(&sprd_codec->completion_hp_pop,
+ hp_pop_complete);
+ sp_asoc_pr_dbg("HP POP %d Completion %d\n", i, hp_pop_complete);
+ if (!hp_pop_complete) {
+ if (!is_hp_pop_compelet(codec)) {
+ pr_err("ERR:HP POP %d timeout not complete\n",
+ i);
+ } else {
+ pr_err("ERR:HP POP %d timeout but complete\n",
+ i);
+ }
+ } else {
+ /* 01 change to 10 maybe walk to 11 shortly,
+ so, check it double. */
+ sprd_codec_wait(2);
+ if (is_hp_pop_compelet(codec)) {
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+static int hp_pop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int mask;
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mask = HP_POP_STEP_MASK << HP_POP_STEP;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR1), mask,
+ HP_POP_STEP_2 << HP_POP_STEP);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR1), mask,
+ HP_POP_CTL_UP << HP_POP_CTL);
+ sp_asoc_pr_dbg("U PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR1));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ mask = HP_POP_STEP_MASK << HP_POP_STEP;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR1), mask,
+ HP_POP_STEP_1 << HP_POP_STEP);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR1), mask,
+ HP_POP_CTL_DOWN << HP_POP_CTL);
+ sp_asoc_pr_dbg("D PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR1));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ ret = hp_pop_wait_for_compelet(codec);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR1), mask,
+ HP_POP_CTL_HOLD << HP_POP_CTL);
+ sp_asoc_pr_dbg("HOLD PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR1));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = hp_pop_wait_for_compelet(codec);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR1), mask,
+ HP_POP_CTL_DIS << HP_POP_CTL);
+ sp_asoc_pr_dbg("DIS PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR1));
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+#else
+#ifndef CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME
+#define CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME (0)
+#endif
+static int hp_pop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+ sp_asoc_pr_dbg("%s Event is %s wait %dms\n", __func__,
+ get_event_name(event),
+ CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ sprd_codec_wait(CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+ return ret;
+}
+#endif
+
+static int hp_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ int mask;
+#endif
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+#if 0 /* do not enable the diff function from weifeng.ni */
+ snd_soc_update_bits(codec, SOC_REG(DCR1), BIT(DIFF_EN),
+ BIT(DIFF_EN));
+#endif
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR1), mask,
+ HP_POP_CTL_DIS << HP_POP_CTL);
+ sp_asoc_pr_dbg("DIS(en) PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR1));
+#endif
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, SOC_REG(DCR1), BIT(DIFF_EN), 0);
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR1), mask,
+ HP_POP_CTL_HOLD << HP_POP_CTL);
+ sp_asoc_pr_dbg("HOLD(en) PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR1));
+#endif
+ goto _pre_pmd;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* do nothing now */
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ _mixer_setting(codec, SPRD_CODEC_HP_DACL,
+ SPRD_CODEC_HP_DAC_MIXER_MAX, SPRD_CODEC_LEFT,
+ snd_soc_read(codec, DCR1) & BIT(HPL_EN), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_HP_DACL,
+ SPRD_CODEC_HP_DAC_MIXER_MAX, SPRD_CODEC_RIGHT,
+ snd_soc_read(codec, DCR1) & BIT(HPR_EN), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_HP_ADCL,
+ SPRD_CODEC_HP_MIXER_MAX, SPRD_CODEC_LEFT,
+ snd_soc_read(codec, DCR1) & BIT(HPL_EN), 0);
+
+ _mixer_setting(codec, SPRD_CODEC_HP_ADCL,
+ SPRD_CODEC_HP_MIXER_MAX, SPRD_CODEC_RIGHT,
+ snd_soc_read(codec, DCR1) & BIT(HPR_EN), 0);
+
+_pre_pmd:
+
+ return ret;
+}
+
+static int spk_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ if (snd_soc_read(codec, DCR1) & BIT(AOL_EN)) {
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ sprd_codec_pa_sw_set(codec, SPRD_CODEC_PA_SW_AOL);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL);
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_DACL,
+ SPRD_CODEC_SPK_DAC_MIXER_MAX, SPRD_CODEC_LEFT,
+ (snd_soc_read(codec, DCR1) & BIT(AOL_EN)), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_DACL,
+ SPRD_CODEC_SPK_DAC_MIXER_MAX, SPRD_CODEC_RIGHT,
+ (snd_soc_read(codec, DCR1) & BIT(AOR_EN)), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_ADCL,
+ SPRD_CODEC_SPK_MIXER_MAX, SPRD_CODEC_LEFT,
+ (snd_soc_read(codec, DCR1) & BIT(AOL_EN)), 0);
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_ADCL,
+ SPRD_CODEC_SPK_MIXER_MAX, SPRD_CODEC_RIGHT,
+ (snd_soc_read(codec, DCR1) & BIT(AOR_EN)), 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_EAR_WITH_IN_SPK
+static int ear_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ sprd_codec_pa_sw_set(codec, SPRD_CODEC_PA_SW_EAR);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_EAR);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+#endif
+
+static int adc_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ _mixer_setting(codec, SPRD_CODEC_AIL, SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_LEFT,
+ (!(snd_soc_read(codec, AACR2) & BIT(ADCPGAL_PD))), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_AIL, SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_RIGHT,
+ (!(snd_soc_read(codec, AACR2) & BIT(ADCPGAR_PD))), 1);
+
+ return 0;
+}
+
+static int pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = FUN_REG(w->reg);
+ struct sprd_codec_pga_op *pga = &(sprd_codec->pga[id]);
+ int ret = 0;
+ int min = sprd_codec_pga_cfg[id].min;
+
+ sp_asoc_pr_dbg("%s Set %s(%d) Event is %s\n", __func__,
+ sprd_codec_pga_debug_str[id], pga->pgaval,
+ get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ pga->set = sprd_codec_pga_cfg[id].set;
+ ret = pga->set(codec, pga->pgaval);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ pga->set = 0;
+ ret = sprd_codec_pga_cfg[id].set(codec, min);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ana_loop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = FUN_REG(w->reg);
+ struct sprd_codec_mixer *mixer;
+ int ret = 0;
+ static int s_need_wait = 1;
+
+ sp_asoc_pr_dbg("Entering %s event is %s\n", __func__,
+ get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* NOTES: reduce linein pop noise must open ADCL/R ->HP/SPK MIXER
+ AFTER delay 250ms for both ADCL/ADCR switch complete.
+ */
+ if (s_need_wait == 1) {
+ /* NOTES: reduce linein pop noise must delay 250ms
+ after linein mixer switch on.
+ actually this function perform after
+ adc_switch_event function for
+ both ADCL/ADCR switch complete.
+ */
+ sprd_codec_wait(250);
+ s_need_wait++;
+ sp_asoc_pr_dbg("ADC Switch ON delay\n");
+ }
+ mixer = &(sprd_codec->mixer[id]);
+ if (mixer->set) {
+ mixer->set(codec, mixer->on);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ s_need_wait = 1;
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = FUN_REG(w->reg);
+ int ret = 0;
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ struct regulator **regu;
+
+ sp_asoc_pr_dbg("%s %s Event is %s\n", __func__,
+ mic_bias_name[id], get_event_name(event));
+
+ switch (id) {
+ case SPRD_CODEC_MIC_BIAS:
+ regu = &sprd_codec->main_mic;
+ break;
+ case SPRD_CODEC_AUXMIC_BIAS:
+ regu = &sprd_codec->aux_mic;
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ ret = sprd_codec_mic_bias_set(regu, on);
+
+ return ret;
+}
+
+static int mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = FUN_REG(w->reg);
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s event is %s\n", sprd_codec_mixer_debug_str[id],
+ get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mixer->on = 1;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ mixer->on = 0;
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+ if (ret >= 0)
+ _mixer_setting_one(codec, id, mixer->on, 1);
+
+ return ret;
+}
+
+static int mixer_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_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = wlist->widgets[0]->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = FUN_REG(mc->reg);
+ ucontrol->value.integer.value[0] = sprd_codec->mixer[id].on;
+ return 0;
+}
+
+static int mixer_need_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol, bool need_set)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = wlist->widgets[0]->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = FUN_REG(mc->reg);
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]);
+ int ret = 0;
+
+ if (mixer->on == ucontrol->value.integer.value[0])
+ return 0;
+
+ sp_asoc_pr_info("Set %s Switch %s\n", sprd_codec_mixer_debug_str[id],
+ STR_ON_OFF(ucontrol->value.integer.value[0]));
+
+ /*notice the sequence */
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ /*update reg: must be set after snd_soc_dapm_put_enum_double->change = snd_soc_test_bits(widget->codec, e->reg, mask, val); */
+ mixer->on = ucontrol->value.integer.value[0];
+
+ if (mixer->set && need_set)
+ ret = mixer->set(codec, mixer->on);
+
+ return ret;
+}
+
+static int mixer_set_mem(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return mixer_need_set(kcontrol, ucontrol, 0);
+}
+
+static int mixer_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return mixer_need_set(kcontrol, ucontrol, 1);
+}
+
+#define SPRD_CODEC_MIXER(xname, xreg)\
+ SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, mixer_get, mixer_set)
+
+/*Just for LINE IN path, mixer_set not really set mixer (ADCL/R -> HP/SPK L/R) here but
+setting in ana_loop_event, just remeber state here*/
+#define SPRD_CODEC_MIXER_NOSET(xname, xreg)\
+ SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, mixer_get, mixer_set_mem)
+
+/* ADCL Mixer */
+static const struct snd_kcontrol_new adcl_mixer_controls[] = {
+ SPRD_CODEC_MIXER("AILADCL Switch",
+ ID_FUN(SPRD_CODEC_AIL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("AIRADCL Switch",
+ ID_FUN(SPRD_CODEC_AIR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("MainMICADCL Switch",
+ ID_FUN(SPRD_CODEC_MAIN_MIC, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("AuxMICADCL Switch",
+ ID_FUN(SPRD_CODEC_AUX_MIC, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("HPMICADCL Switch",
+ ID_FUN(SPRD_CODEC_HP_MIC, SPRD_CODEC_LEFT)),
+};
+
+/* ADCR Mixer */
+static const struct snd_kcontrol_new adcr_mixer_controls[] = {
+ SPRD_CODEC_MIXER("AILADCR Switch",
+ ID_FUN(SPRD_CODEC_AIL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("AIRADCR Switch",
+ ID_FUN(SPRD_CODEC_AIR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("MainMICADCR Switch",
+ ID_FUN(SPRD_CODEC_MAIN_MIC, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("AuxMICADCR Switch",
+ ID_FUN(SPRD_CODEC_AUX_MIC, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("HPMICADCR Switch",
+ ID_FUN(SPRD_CODEC_HP_MIC, SPRD_CODEC_RIGHT)),
+};
+
+/* HPL Mixer */
+static const struct snd_kcontrol_new hpl_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_DACL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("DACRHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_DACR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_LEFT)),
+};
+
+/* HPR Mixer */
+static const struct snd_kcontrol_new hpr_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_DACL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("DACRHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_DACR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_RIGHT)),
+};
+
+/* SPKL Mixer */
+static const struct snd_kcontrol_new spkl_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("DACRSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_LEFT)),
+};
+
+/* SPKR Mixer */
+static const struct snd_kcontrol_new spkr_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("DACRSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_RIGHT)),
+};
+
+/*ANA LOOP SWITCH*/
+#define SPRD_CODEC_LOOP_SWITCH(xname, xreg)\
+ SND_SOC_DAPM_PGA_S(xname, 7, FUN_REG(xreg), 0, 0, ana_loop_event,\
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#ifndef SND_SOC_DAPM_MICBIAS_E
+#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
+{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .event = wevent, .event_flags = wflags}
+#endif
+
+static const struct snd_soc_dapm_widget sprd_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VBO", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VCOM_BUF", 0, 0),
+ SND_SOC_DAPM_SUPPLY_S("Digital Power", 1, SND_SOC_NOPM, 0, 0,
+ digital_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Analog Power", 2, SND_SOC_NOPM, 0, 0,
+ analog_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("DA Clk", 3, SOC_REG(CCR), DAC_CLK_EN, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DRV Clk", 4, SOC_REG(CCR), DRV_CLK_EN, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("AD IBUF", 3, SOC_REG(AACR1), ADC_IBUF_PD,
+ 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AD Clk", 4, SOC_REG(CCR), ADC_CLK_PD, 1, NULL,
+ 0),
+
+ SND_SOC_DAPM_PGA_S("Digital DACL Switch", 4, SOC_REG(AUD_TOP_CTL),
+ DAC_EN_L, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Digital DACR Switch", 4, SOC_REG(AUD_TOP_CTL),
+ DAC_EN_R, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADie Digital DACL Switch", 5, SND_SOC_NOPM, 0, 0,
+ adie_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADie Digital DACR Switch", 5, SND_SOC_NOPM, 0, 0,
+ adie_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("DACL Switch", 6, SOC_REG(DACR), DACL_EN, 0, NULL,
+ 0),
+ SND_SOC_DAPM_PGA_S("DACR Switch", 6, SOC_REG(DACR), DACR_EN, 0, NULL,
+ 0),
+ SND_SOC_DAPM_DAC_E("DAC", "Playback-Vaudio",
+ FUN_REG(SPRD_CODEC_PLAYBACK), 0,
+ 0,
+ chan_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DFM-OUT", "DFM-Output",
+ SND_SOC_NOPM, 0,
+ 0,
+ dfm_out_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ SND_SOC_DAPM_PGA_S("HP POP", 9, SND_SOC_NOPM, 0, 0, hp_pop_event,
+ SND_SOC_DAPM_POST_PMU),
+#else
+ SND_SOC_DAPM_SUPPLY_S("HP POP", 5, SND_SOC_NOPM, 0, 0, hp_pop_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+#endif
+ SND_SOC_DAPM_PGA_S("HPL Switch", 5, SOC_REG(DCR1), HPL_EN, 0,
+ hp_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPR Switch", 5, SOC_REG(DCR1), HPR_EN, 0,
+ hp_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPL Mute", 6, FUN_REG(SPRD_CODEC_PGA_HPL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPR Mute", 6, FUN_REG(SPRD_CODEC_PGA_HPR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0,
+ &hpl_mixer_controls[0],
+ ARRAY_SIZE(hpl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0,
+ &hpr_mixer_controls[0],
+ ARRAY_SIZE(hpr_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SPKL Mixer", SND_SOC_NOPM, 0, 0,
+ &spkl_mixer_controls[0],
+ ARRAY_SIZE(spkl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SPKR Mixer", SND_SOC_NOPM, 0, 0,
+ &spkr_mixer_controls[0],
+ ARRAY_SIZE(spkr_mixer_controls)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLHPL Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRHPL Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLHPR Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRHPR Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLSPKL Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRSPKL Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLSPKR Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRSPKR Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_RIGHT)),
+ SND_SOC_DAPM_PGA_S("SPKL Switch", 5, SOC_REG(DCR1), AOL_EN, 0,
+ spk_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("SPKR Switch", 5, SOC_REG(DCR1), AOR_EN, 0,
+ spk_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("SPKL Mute", 6, FUN_REG(SPRD_CODEC_PGA_SPKL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("SPKR Mute", 6, FUN_REG(SPRD_CODEC_PGA_SPKR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("EAR Mixer", 5,
+ FUN_REG(ID_FUN
+ (SPRD_CODEC_EAR_DACL, SPRD_CODEC_LEFT)), 0,
+ 0, mixer_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_EAR_WITH_IN_SPK
+ SND_SOC_DAPM_PGA_S("EAR Switch", 6, SOC_REG(DCR1), EAR_EN, 0,
+ ear_switch_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+#else
+ SND_SOC_DAPM_PGA_S("EAR Switch", 6, SOC_REG(DCR1), EAR_EN, 0, NULL, 0),
+#endif
+
+ SND_SOC_DAPM_PGA_S("EAR Mute", 7, FUN_REG(SPRD_CODEC_PGA_EAR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MIXER("ADCL Mixer", SND_SOC_NOPM, 0, 0,
+ &adcl_mixer_controls[0],
+ ARRAY_SIZE(adcl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("ADCR Mixer", SND_SOC_NOPM, 0, 0,
+ &adcr_mixer_controls[0],
+ ARRAY_SIZE(adcr_mixer_controls)),
+ SND_SOC_DAPM_PGA_S("Digital ADCL Switch", 3, SOC_REG(AUD_TOP_CTL),
+ ADC_EN_L, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Digital ADCR Switch", 3, SOC_REG(AUD_TOP_CTL),
+ ADC_EN_R, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADie Digital ADCL Switch", 2, SND_SOC_NOPM, 0, 0,
+ adie_adc_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADie Digital ADCR Switch", 2, SND_SOC_NOPM, 0, 0,
+ adie_adc_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADCL Switch", 1, SOC_REG(AACR1), ADCL_PD, 1, NULL,
+ 0),
+ SND_SOC_DAPM_PGA_S("ADCR Switch", 1, SOC_REG(AACR1), ADCR_PD, 1, NULL,
+ 0),
+ SND_SOC_DAPM_PGA_E("ADCL PGA", SOC_REG(AACR2), ADCPGAL_PD, 1, NULL, 0,
+ adc_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ADCR PGA", SOC_REG(AACR2), ADCPGAR_PD, 1, NULL, 0,
+ adc_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADCL Mute", 3, FUN_REG(SPRD_CODEC_PGA_ADCL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADCR Mute", 3, FUN_REG(SPRD_CODEC_PGA_ADCR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC", "Main-Capture-Vaudio",
+ FUN_REG(SPRD_CODEC_CAPTRUE),
+ 0, 0,
+ chan_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS_E("Mic Bias", FUN_REG(SPRD_CODEC_MIC_BIAS), 0, 0,
+ mic_bias_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MICBIAS_E("AuxMic Bias", FUN_REG(SPRD_CODEC_AUXMIC_BIAS),
+ 0, 0,
+ mic_bias_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_PGA_S("Spk PA Switch", 15, SND_SOC_NOPM, 0, 0,
+ spk_pa_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("HEAD_P_L"),
+ SND_SOC_DAPM_OUTPUT("HEAD_P_R"),
+ SND_SOC_DAPM_OUTPUT("AOL"),
+ SND_SOC_DAPM_OUTPUT("AOR"),
+ SND_SOC_DAPM_OUTPUT("EAR"),
+
+ SND_SOC_DAPM_OUTPUT("HP PA"),
+ SND_SOC_DAPM_OUTPUT("Spk PA"),
+ SND_SOC_DAPM_OUTPUT("Spk2 PA"),
+
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_INPUT("AUXMIC"),
+ SND_SOC_DAPM_INPUT("HPMIC"),
+ SND_SOC_DAPM_INPUT("AIL"),
+ SND_SOC_DAPM_INPUT("AIR"),
+};
+
+/* sprd_codec supported interconnection*/
+static const struct snd_soc_dapm_route sprd_codec_intercon[] = {
+ /* Power */
+ {"Analog Power", NULL, "VCOM_BUF"},
+ {"Analog Power", NULL, "VBO"},
+ {"DA Clk", NULL, "Analog Power"},
+ {"DA Clk", NULL, "Digital Power"},
+ {"DAC", NULL, "DA Clk"},
+ {"DFM-OUT", NULL, "DA Clk"},
+
+ {"AD IBUF", NULL, "Analog Power"},
+ {"AD Clk", NULL, "Digital Power"},
+ {"AD Clk", NULL, "AD IBUF"},
+ {"ADC", NULL, "AD Clk"},
+
+ {"ADCL PGA", NULL, "AD IBUF"},
+ {"ADCR PGA", NULL, "AD IBUF"},
+
+ {"HP POP", NULL, "DRV Clk"},
+ {"HPL Switch", NULL, "DRV Clk"},
+ {"HPR Switch", NULL, "DRV Clk"},
+ {"SPKL Switch", NULL, "DRV Clk"},
+ {"SPKR Switch", NULL, "DRV Clk"},
+ {"EAR Switch", NULL, "DRV Clk"},
+
+ /* Playback */
+ {"Digital DACL Switch", NULL, "DFM-OUT"},
+ {"Digital DACR Switch", NULL, "DFM-OUT"},
+ {"Digital DACL Switch", NULL, "DAC"},
+ {"Digital DACR Switch", NULL, "DAC"},
+ {"ADie Digital DACL Switch", NULL, "Digital DACL Switch"},
+ {"ADie Digital DACR Switch", NULL, "Digital DACR Switch"},
+ {"DACL Switch", NULL, "ADie Digital DACL Switch"},
+ {"DACR Switch", NULL, "ADie Digital DACR Switch"},
+
+ /* Output */
+ {"HPL Mixer", "DACLHPL Switch", "DACL Switch"},
+ {"HPL Mixer", "DACRHPL Switch", "DACR Switch"},
+ {"HPR Mixer", "DACLHPR Switch", "DACL Switch"},
+ {"HPR Mixer", "DACRHPR Switch", "DACR Switch"},
+
+ {"ADCLHPL Loop Switch", NULL, "ADCL PGA"},
+ {"ADCLHPR Loop Switch", NULL, "ADCL PGA"},
+ {"ADCRHPL Loop Switch", NULL, "ADCR PGA"},
+ {"ADCRHPR Loop Switch", NULL, "ADCR PGA"},
+
+ {"HPL Mixer", "ADCLHPL Switch", "ADCLHPL Loop Switch"},
+ {"HPL Mixer", "ADCRHPL Switch", "ADCRHPL Loop Switch"},
+ {"HPR Mixer", "ADCLHPR Switch", "ADCLHPR Loop Switch"},
+ {"HPR Mixer", "ADCRHPR Switch", "ADCRHPR Loop Switch"},
+
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ {"HEAD_P_L", NULL, "HP POP"},
+ {"HEAD_P_R", NULL, "HP POP"},
+ {"HP POP", NULL, "HPL Switch"},
+ {"HP POP", NULL, "HPR Switch"},
+#else
+ {"HPL Switch", NULL, "HP POP"},
+ {"HPR Switch", NULL, "HP POP"},
+#endif
+ {"HPL Switch", NULL, "HPL Mixer"},
+ {"HPR Switch", NULL, "HPR Mixer"},
+ {"HPL Mute", NULL, "HPL Switch"},
+ {"HPR Mute", NULL, "HPR Switch"},
+ {"HEAD_P_L", NULL, "HPL Mute"},
+ {"HEAD_P_R", NULL, "HPR Mute"},
+
+ {"SPKL Mixer", "DACLSPKL Switch", "DACL Switch"},
+ {"SPKL Mixer", "DACRSPKL Switch", "DACR Switch"},
+ {"SPKR Mixer", "DACLSPKR Switch", "DACL Switch"},
+ {"SPKR Mixer", "DACRSPKR Switch", "DACR Switch"},
+
+ {"ADCLSPKL Loop Switch", NULL, "ADCL PGA"},
+ {"ADCLSPKR Loop Switch", NULL, "ADCL PGA"},
+ {"ADCRSPKL Loop Switch", NULL, "ADCR PGA"},
+ {"ADCRSPKR Loop Switch", NULL, "ADCR PGA"},
+
+ {"SPKL Mixer", "ADCLSPKL Switch", "ADCLSPKL Loop Switch"},
+ {"SPKL Mixer", "ADCRSPKL Switch", "ADCRSPKL Loop Switch"},
+ {"SPKR Mixer", "ADCLSPKR Switch", "ADCLSPKR Loop Switch"},
+ {"SPKR Mixer", "ADCRSPKR Switch", "ADCRSPKR Loop Switch"},
+
+ {"SPKL Switch", NULL, "SPKL Mixer"},
+ {"SPKR Switch", NULL, "SPKR Mixer"},
+ {"SPKL Mute", NULL, "SPKL Switch"},
+ {"SPKR Mute", NULL, "SPKR Switch"},
+ {"AOL", NULL, "SPKL Mute"},
+ {"AOR", NULL, "SPKR Mute"},
+
+ {"Spk PA", NULL, "Spk PA Switch"},
+ {"Spk PA Switch", NULL, "SPKL Mute"},
+
+ {"EAR Mixer", NULL, "DACL Switch"},
+ {"EAR Switch", NULL, "EAR Mixer"},
+ {"EAR Mute", NULL, "EAR Switch"},
+ {"EAR", NULL, "EAR Mute"},
+
+ {"ADCL Mute", NULL, "ADCL Mixer"},
+ {"ADCR Mute", NULL, "ADCR Mixer"},
+ {"ADCL PGA", NULL, "ADCL Mute"},
+ {"ADCR PGA", NULL, "ADCR Mute"},
+ /* LineIN */
+ {"ADCL Mixer", "AILADCL Switch", "AIL"},
+ {"ADCR Mixer", "AILADCR Switch", "AIL"},
+ {"ADCL Mixer", "AIRADCL Switch", "AIR"},
+ {"ADCR Mixer", "AIRADCR Switch", "AIR"},
+ /* Input */
+ {"ADCL Mixer", "MainMICADCL Switch", "Mic Bias"},
+ {"ADCR Mixer", "MainMICADCR Switch", "Mic Bias"},
+ {"ADCL Mixer", "AuxMICADCL Switch", "AuxMic Bias"},
+ {"ADCR Mixer", "AuxMICADCR Switch", "AuxMic Bias"},
+ {"ADCL Mixer", "HPMICADCL Switch", "HPMIC"},
+ {"ADCR Mixer", "HPMICADCR Switch", "HPMIC"},
+ /* Captrue */
+ {"ADCL Switch", NULL, "ADCL PGA"},
+ {"ADCR Switch", NULL, "ADCR PGA"},
+ {"ADie Digital ADCL Switch", NULL, "ADCL Switch"},
+ {"ADie Digital ADCR Switch", NULL, "ADCR Switch"},
+ {"Digital ADCL Switch", NULL, "ADie Digital ADCL Switch"},
+ {"Digital ADCR Switch", NULL, "ADie Digital ADCR Switch"},
+ {"ADC", NULL, "Digital ADCL Switch"},
+ {"ADC", NULL, "Digital ADCR Switch"},
+
+ {"Mic Bias", NULL, "MIC"},
+ {"AuxMic Bias", NULL, "AUXMIC"},
+ /* Bias independent */
+ {"Mic Bias", NULL, "Analog Power"},
+ {"AuxMic Bias", NULL, "Analog Power"},
+};
+
+static int sprd_codec_vol_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = FUN_REG(mc->reg);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ struct sprd_codec_pga_op *pga = &(sprd_codec->pga[reg]);
+ int ret = 0;
+
+ sp_asoc_pr_info("Set PGA[%s] to %ld\n", sprd_codec_pga_debug_str[reg],
+ ucontrol->value.integer.value[0]);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+ pga->pgaval = val;
+ if (pga->set) {
+ ret = pga->set(codec, pga->pgaval);
+ }
+ return ret;
+}
+
+static int sprd_codec_vol_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = FUN_REG(mc->reg);
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+ struct sprd_codec_pga_op *pga = &(sprd_codec->pga[reg]);
+
+ ucontrol->value.integer.value[0] = pga->pgaval;
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static int sprd_codec_inter_pa_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret = 0;
+
+ sp_asoc_pr_info("Config inter PA 0x%08x\n",
+ (int)ucontrol->value.integer.value[0]);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+ sprd_codec->inter_pa.value = (u32) val;
+ if (sprd_codec->inter_pa.set) {
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ sprd_inter_speaker_pa(codec, 1);
+ } else {
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ }
+ return ret;
+}
+
+static int sprd_codec_inter_pa_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+ ucontrol->value.integer.value[0] = sprd_codec->inter_pa.value;
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static int sprd_codec_mic_bias_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = FUN_REG(mc->reg);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret = 0;
+ int id = reg;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+
+ if (invert)
+ val = max - val;
+ if (sprd_codec->mic_bias[id] == val) {
+ return 0;
+ }
+
+ sp_asoc_pr_info("%s Switch %s\n", mic_bias_name[id], STR_ON_OFF(val));
+
+ sprd_codec->mic_bias[id] = val;
+ if (val) {
+ ret =
+ snd_soc_dapm_force_enable_pin(&codec->card->dapm,
+ mic_bias_name[id]);
+ } else {
+ ret =
+ snd_soc_dapm_disable_pin(&codec->card->dapm,
+ mic_bias_name[id]);
+ }
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync(&codec->card->dapm);
+
+ return ret;
+}
+
+static int sprd_codec_mic_bias_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = FUN_REG(mc->reg);
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+ int id = reg;
+
+ ucontrol->value.integer.value[0] = sprd_codec->mic_bias[id];
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -3600, 300, 1);
+static const DECLARE_TLV_DB_SCALE(ear_tlv, -3600, 300, 1);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -2400, 300, 1);
+
+#define SPRD_CODEC_PGA(xname, xreg, tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, FUN_REG(xreg), 0, 15, 0, \
+ sprd_codec_vol_get, sprd_codec_vol_put, tlv_array)
+
+#define SPRD_CODEC_MIC_BIAS(xname, xreg) \
+ SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, \
+ sprd_codec_mic_bias_get, sprd_codec_mic_bias_put)
+
+static const struct snd_kcontrol_new sprd_codec_snd_controls[] = {
+ SPRD_CODEC_PGA("SPKL Playback Volume", SPRD_CODEC_PGA_SPKL, spk_tlv),
+ SPRD_CODEC_PGA("SPKR Playback Volume", SPRD_CODEC_PGA_SPKR, spk_tlv),
+ SPRD_CODEC_PGA("HPL Playback Volume", SPRD_CODEC_PGA_HPL, hp_tlv),
+ SPRD_CODEC_PGA("HPR Playback Volume", SPRD_CODEC_PGA_HPR, hp_tlv),
+ SPRD_CODEC_PGA("EAR Playback Volume", SPRD_CODEC_PGA_EAR, ear_tlv),
+
+ SPRD_CODEC_PGA("ADCL Capture Volume", SPRD_CODEC_PGA_ADCL, adc_tlv),
+ SPRD_CODEC_PGA("ADCR Capture Volume", SPRD_CODEC_PGA_ADCR, adc_tlv),
+
+ SOC_SINGLE_EXT("Inter PA Config", 0, 0, LONG_MAX, 0,
+ sprd_codec_inter_pa_get, sprd_codec_inter_pa_put),
+
+ SPRD_CODEC_MIC_BIAS("MIC Bias Switch", SPRD_CODEC_MIC_BIAS),
+
+ SPRD_CODEC_MIC_BIAS("AUXMIC Bias Switch", SPRD_CODEC_AUXMIC_BIAS),
+};
+
+static unsigned int sprd_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ /* Because snd_soc_update_bits reg is 16 bits short type,
+ so muse do following convert
+ */
+ if (IS_SPRD_CODEC_AP_RANG(reg | SPRD_CODEC_AP_BASE_HI)) {
+ reg |= SPRD_CODEC_AP_BASE_HI;
+ return arch_audio_codec_read(reg);
+ } else if (IS_SPRD_CODEC_DP_RANG(reg | SPRD_CODEC_DP_BASE_HI)) {
+ reg |= SPRD_CODEC_DP_BASE_HI;
+ return __raw_readl((void __iomem *)reg);
+ } else if (IS_SPRD_CODEC_MIXER_RANG(FUN_REG(reg))) {
+ struct sprd_codec_priv *sprd_codec =
+ snd_soc_codec_get_drvdata(codec);
+ int id = FUN_REG(reg);
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]);
+ return mixer->on;
+ }
+
+ sp_asoc_pr_dbg("read the register is not codec's reg = 0x%x\n", reg);
+ return 0;
+}
+
+static int sprd_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ int ret = 0;
+ if (IS_SPRD_CODEC_AP_RANG(reg | SPRD_CODEC_AP_BASE_HI)) {
+ reg |= SPRD_CODEC_AP_BASE_HI;
+ ret = arch_audio_codec_write(reg, val);
+ sp_asoc_pr_reg("A[0x%04x] W:[0x%08x] R:[0x%08x]\n",
+ reg & 0xFFFF, val, arch_audio_codec_read(reg));
+ return ret;
+ } else if (IS_SPRD_CODEC_DP_RANG(reg | SPRD_CODEC_DP_BASE_HI)) {
+ reg |= SPRD_CODEC_DP_BASE_HI;
+ __raw_writel(val, (void __iomem *)reg);
+ sp_asoc_pr_reg("D[0x%04x] W:[0x%08x] R:[0x%08x]\n",
+ reg & 0xFFFF, val,
+ __raw_readl((void __iomem *)reg));
+ return ret;
+ }
+ sp_asoc_pr_dbg("write the register is not codec's reg = 0x%x\n", reg);
+ return ret;
+}
+
+static int sprd_codec_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask = 0x0F;
+ int shift = 0;
+ int rate = params_rate(params);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sprd_codec->da_sample_val = rate;
+ sp_asoc_pr_info("Playback rate is [%d]\n", rate);
+ sprd_codec_set_sample_rate(codec, rate, mask, shift);
+ } else {
+ sprd_codec->ad_sample_val = rate;
+ sp_asoc_pr_info("Capture rate is [%d]\n", rate);
+ sprd_codec_set_ad_sample_rate(codec, rate, mask, shift);
+ }
+
+ return 0;
+}
+
+static int sprd_codec_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+#if 0
+static void sprd_codec_dac_mute_irq_enable(struct snd_soc_codec *codec)
+{
+ int mask = BIT(DAC_MUTE_D);
+ snd_soc_update_bits(codec, SOC_REG(AUD_INT_CLR), mask, mask);
+ snd_soc_update_bits(codec, SOC_REG(AUD_INT_EN), mask, mask);
+}
+#endif
+
+static irqreturn_t sprd_codec_dp_irq(int irq, void *dev_id)
+{
+ int mask;
+ struct sprd_codec_priv *sprd_codec = dev_id;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ mask = snd_soc_read(codec, AUD_AUD_STS0);
+ sp_asoc_pr_dbg("dac mute irq mask = 0x%x\n", mask);
+ if (BIT(DAC_MUTE_D_MASK) & mask) {
+ mask = BIT(DAC_MUTE_D);
+ complete(&sprd_codec->completion_dac_mute);
+ }
+ if (BIT(DAC_MUTE_U_MASK) & mask) {
+ mask = BIT(DAC_MUTE_U);
+ }
+ snd_soc_update_bits(codec, SOC_REG(AUD_INT_EN), mask, 0);
+ return IRQ_HANDLED;
+}
+
+#if 0
+static int sprd_codec_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ if (atomic_read(&sprd_codec->ldo_refcount) >= 1) {
+ sp_asoc_pr_dbg("mute %i\n", mute);
+
+ ret =
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL),
+ BIT(DAC_MUTE_START),
+ mute ? BIT(DAC_MUTE_START) : 0);
+#if 0
+ if (mute && ret) {
+ int dac_mute_complete;
+ struct sprd_codec_priv *sprd_codec =
+ snd_soc_codec_get_drvdata(codec);
+ sp_asoc_pr_dbg("Dac Mute IRQ enable\n");
+ sprd_codec_dac_mute_irq_enable(codec);
+ init_completion(&sprd_codec->completion_dac_mute);
+ dac_mute_complete =
+ wait_for_completion_timeout
+ (&sprd_codec->completion_dac_mute,
+ msecs_to_jiffies(SPRD_CODEC_DAC_MUTE_TIMEOUT));
+ sp_asoc_pr_dbg("Dac Mute Completion %d\n",
+ dac_mute_complete);
+ if (!dac_mute_complete) {
+ pr_err("Dac Mute Timeout\n");
+ }
+ }
+#endif
+
+ }
+
+ return ret;
+}
+#endif
+
+static struct snd_soc_dai_ops sprd_codec_dai_ops = {
+ .hw_params = sprd_codec_pcm_hw_params,
+ .hw_free = sprd_codec_pcm_hw_free,
+};
+
+#ifdef CONFIG_PM
+static int sprd_codec_soc_suspend(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static int sprd_codec_soc_resume(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+#else
+#define sprd_codec_soc_suspend NULL
+#define sprd_codec_soc_resume NULL
+#endif
+
+/*
+ * proc interface
+ */
+
+#ifdef CONFIG_PROC_FS
+static void sprd_codec_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct sprd_codec_priv *sprd_codec = entry->private_data;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ int reg;
+
+ snd_iprintf(buffer, "%s digital part\n", codec->name);
+ for (reg = SPRD_CODEC_DP_BASE; reg < SPRD_CODEC_DP_END; reg += 0x10) {
+ snd_iprintf(buffer, "0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (unsigned int)(reg - SPRD_CODEC_DP_BASE)
+ , snd_soc_read(codec, reg + 0x00)
+ , snd_soc_read(codec, reg + 0x04)
+ , snd_soc_read(codec, reg + 0x08)
+ , snd_soc_read(codec, reg + 0x0C)
+ );
+ }
+ snd_iprintf(buffer, "%s analog part\n", codec->name);
+ for (reg = SPRD_CODEC_AP_BASE; reg < SPRD_CODEC_AP_END; reg += 0x10) {
+ snd_iprintf(buffer, "0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (unsigned int)(reg - SPRD_CODEC_AP_BASE)
+ , snd_soc_read(codec, reg + 0x00)
+ , snd_soc_read(codec, reg + 0x04)
+ , snd_soc_read(codec, reg + 0x08)
+ , snd_soc_read(codec, reg + 0x0C)
+ );
+ }
+}
+
+static void sprd_codec_proc_init(struct sprd_codec_priv *sprd_codec)
+{
+ struct snd_info_entry *entry;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+
+ if (!snd_card_proc_new(codec->card->snd_card, "sprd-codec", &entry))
+ snd_info_set_text_ops(entry, sprd_codec, sprd_codec_proc_read);
+}
+#else /* !CONFIG_PROC_FS */
+static inline void sprd_codec_proc_init(struct sprd_codec_priv *sprd_codec)
+{
+}
+#endif
+
+static void sprd_codec_power_changed(struct power_supply *psy)
+{
+ struct sprd_codec_priv *sprd_codec = 0;
+ sp_asoc_pr_dbg("%s %s\n", __func__, psy->name);
+#if 1
+ sprd_codec = container_of(psy, struct sprd_codec_priv, audio_ldo);
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+ if (sprd_codec->inter_pa.set && sprd_codec->inter_pa.setting.is_LDO_mode
+ && sprd_codec->inter_pa.setting.is_auto_LDO_mode) {
+ sprd_codec_pa_ldo_auto(sprd_codec->codec, 0);
+ }
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ if (atomic_read(&sprd_codec->ldo_refcount) >= 1) {
+ sprd_codec_vcom_ldo_auto(sprd_codec->codec, 0);
+ }
+#endif
+}
+
+static int sprd_codec_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ return -EINVAL;
+}
+
+static int sprd_codec_audio_ldo(struct sprd_codec_priv *sprd_codec)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ sprd_codec->audio_ldo.name = "audio-ldo";
+ sprd_codec->audio_ldo.get_property = sprd_codec_power_get_property;
+ sprd_codec->audio_ldo.external_power_changed = sprd_codec_power_changed;
+ ret = power_supply_register(codec->dev, &sprd_codec->audio_ldo);
+ if (ret) {
+ pr_err("ERR:Register power supply error!\n");
+ return -EFAULT;
+ }
+ return ret;
+}
+
+#define SPRD_CODEC_PCM_RATES \
+ (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define SPRD_CODEC_PCM_AD_RATES \
+ (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_48000)
+
+/* PCM Playing and Recording default in full duplex mode */
+static struct snd_soc_dai_driver sprd_codec_dai[] = {
+ {
+ .name = "sprd-codec-v1-i2s",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Main-Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_AD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+#ifdef CONFIG_SND_SOC_SPRD_VAUDIO
+ {
+ .name = "sprd-codec-v1-vaudio",
+ .playback = {
+ .stream_name = "Playback-Vaudio",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Main-Capture-Vaudio",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_AD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+#endif
+ {
+ .name = "sprd-codec-v1-fm",
+ .playback = {
+ .stream_name = "DFM-Output",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+};
+
+static int sprd_codec_soc_probe(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ codec->dapm.idle_bias_off = 1;
+
+ sprd_codec->codec = codec;
+
+ sprd_codec_proc_init(sprd_codec);
+
+ sprd_codec_audio_ldo(sprd_codec);
+
+ return ret;
+}
+
+/* power down chip */
+static int sprd_codec_soc_remove(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static int sprd_codec_power_regulator_init(struct sprd_codec_priv *sprd_codec,
+ struct device *dev)
+{
+ sprd_codec_power_get(dev, &sprd_codec->main_mic, "MICBIAS");
+ sprd_codec_power_get(dev, &sprd_codec->aux_mic, "AUXMICBIAS");
+ return 0;
+}
+
+static void sprd_codec_power_regulator_exit(struct sprd_codec_priv *sprd_codec)
+{
+ sprd_codec_power_put(&sprd_codec->main_mic);
+ sprd_codec_power_put(&sprd_codec->aux_mic);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_sprd_codec = {
+ .probe = sprd_codec_soc_probe,
+ .remove = sprd_codec_soc_remove,
+ .suspend = sprd_codec_soc_suspend,
+ .resume = sprd_codec_soc_resume,
+ .read = sprd_codec_read,
+ .write = sprd_codec_write,
+ .reg_word_size = sizeof(u16),
+ .reg_cache_step = 2,
+ .dapm_widgets = sprd_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sprd_codec_dapm_widgets),
+ .dapm_routes = sprd_codec_intercon,
+ .num_dapm_routes = ARRAY_SIZE(sprd_codec_intercon),
+ .controls = sprd_codec_snd_controls,
+ .num_controls = ARRAY_SIZE(sprd_codec_snd_controls),
+};
+
+static int sprd_codec_probe(struct platform_device *pdev)
+{
+ struct sprd_codec_priv *sprd_codec;
+ int ret;
+ struct device_node *node = pdev->dev.of_node;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ arch_audio_codec_switch(AUDIO_TO_ARM_CTRL);
+
+ sprd_codec =
+ devm_kzalloc(&pdev->dev, sizeof(struct sprd_codec_priv),
+ GFP_KERNEL);
+ if (sprd_codec == NULL)
+ return -ENOMEM;
+
+ sprd_codec->da_sample_val = 44100; /*inital value for FM route */
+
+ if (node) {
+ u32 val;
+ if (!of_property_read_u32(node, "sprd,ap_irq", &val)) {
+ sprd_codec->ap_irq = val;
+ sp_asoc_pr_dbg("Set AP IRQ is %d!\n", val);
+ } else {
+ pr_err("ERR:Must give me the AP IRQ!\n");
+ return -EINVAL;
+ }
+ if (!of_property_read_u32(node, "sprd,dp_irq", &val)) {
+ sprd_codec->dp_irq = val;
+ sp_asoc_pr_dbg("Set DP IRQ is %d!\n", val);
+ } else {
+ pr_err("ERR:Must give me the DP IRQ!\n");
+ return -EINVAL;
+ }
+ if (!of_property_read_u32(node, "sprd,def_da_fs", &val)) {
+ sprd_codec->da_sample_val = val;
+ sp_asoc_pr_dbg("Change DA default fs to %d!\n", val);
+ }
+ } else {
+ sprd_codec->ap_irq = CODEC_AP_IRQ;
+ sprd_codec->dp_irq = CODEC_DP_IRQ;
+ }
+
+ platform_set_drvdata(pdev, sprd_codec);
+
+ atomic_set(&sprd_codec->adie_adc_refcount, 0);
+ atomic_set(&sprd_codec->adie_dac_refcount, 0);
+
+ ret =
+ request_irq(sprd_codec->ap_irq, sprd_codec_ap_irq, 0,
+ "sprd_codec_ap", sprd_codec);
+ if (ret) {
+ pr_err("ERR:Request irq ap failed!\n");
+ goto err_irq;
+ }
+
+ INIT_DELAYED_WORK(&sprd_codec->ovp_delayed_work,
+ sprd_codec_ovp_delay_worker);
+
+ ret =
+ request_irq(sprd_codec->dp_irq, sprd_codec_dp_irq, 0,
+ "sprd_codec_dp", sprd_codec);
+ if (ret) {
+ pr_err("ERR:Request irq dp failed!\n");
+ goto dp_err_irq;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_sprd_codec,
+ sprd_codec_dai,
+ ARRAY_SIZE(sprd_codec_dai));
+ if (ret != 0) {
+ pr_err("ERR:Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ sprd_codec_inter_pa_init(sprd_codec);
+
+ sprd_codec_power_regulator_init(sprd_codec, &pdev->dev);
+
+ mutex_init(&sprd_codec->inter_pa_mutex);
+ spin_lock_init(&sprd_codec->sprd_codec_fun_lock);
+ spin_lock_init(&sprd_codec->sprd_codec_pa_sw_lock);
+
+ /* SC8825C need change the v_map */
+ sprd_codec_vcom_ldo_cfg(sprd_codec, ldo_vcom_v_map,
+ ARRAY_SIZE(ldo_vcom_v_map));
+ sprd_codec_pa_ldo_cfg(sprd_codec, ldo_v_map, ARRAY_SIZE(ldo_v_map));
+
+ return 0;
+
+dp_err_irq:
+ free_irq(sprd_codec->ap_irq, sprd_codec);
+err_irq:
+ return -EINVAL;
+}
+
+static int sprd_codec_remove(struct platform_device *pdev)
+{
+ struct sprd_codec_priv *sprd_codec = platform_get_drvdata(pdev);
+ sprd_codec_power_regulator_exit(sprd_codec);
+ free_irq(sprd_codec->ap_irq, sprd_codec);
+ free_irq(sprd_codec->dp_irq, sprd_codec);
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id codec_of_match[] = {
+ {.compatible = "sprd,sprd-codec-v1",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, codec_of_match);
+#endif
+
+static struct platform_driver sprd_codec_codec_driver = {
+ .driver = {
+ .name = "sprd-codec-v1",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(codec_of_match),
+ },
+ .probe = sprd_codec_probe,
+ .remove = sprd_codec_remove,
+};
+
+module_platform_driver(sprd_codec_codec_driver);
+
+MODULE_DESCRIPTION("SPRD-CODEC ALSA SoC codec driver");
+MODULE_AUTHOR("Zhenfang Wang <zhenfang.wang@spreadtrum.com>");
+MODULE_AUTHOR("Ken Kuang <ken.kuang@spreadtrum.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("codec:sprd-codec");
diff --git a/sound/soc/sprd/codec/sprd/v1/sprd-codec.h b/sound/soc/sprd/codec/sprd/v1/sprd-codec.h
new file mode 100644
index 00000000..9ff3760a
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v1/sprd-codec.h
@@ -0,0 +1,375 @@
+/*
+ * sound/soc/sprd/codec/sprd/v1/sprd-codec.h
+ *
+ * SPRD-CODEC -- SpreadTrum Tiger intergrated codec.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * 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 ork FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __SPRD_CODEC_H
+#define __SPRD_CODEC_H
+
+#include <mach/hardware.h>
+#include <mach/globalregs.h>
+#include <mach/sprd-audio.h>
+#include <mach/adi.h>
+#include <asm/io.h>
+
+#ifndef CONFIG_SPRD_CODEC_USE_INT
+/* #define CONFIG_SPRD_CODEC_USE_INT */
+#endif
+#ifndef CONFIG_CODEC_DAC_MUTE_WAIT
+/* #define CONFIG_CODEC_DAC_MUTE_WAIT */
+#endif
+
+/* unit: ms */
+#define SPRD_CODEC_LDO_WAIT_TIME (5)
+#define SPRD_CODEC_LDO_VCM_TIME (2)
+#ifdef CONFIG_SPRD_CODEC_USE_INT
+#define SPRD_CODEC_DAC_MUTE_TIMEOUT (600)
+#else
+#define SPRD_CODEC_DAC_MUTE_WAIT_TIME (40)
+#endif
+
+#ifdef CONFIG_SPRD_CODEC_USE_INT
+#define SPRD_CODEC_HP_POP_TIMEOUT (1000)
+#else
+#define SPRD_CODEC_HP_POP_TIME_STEP (10)
+#define SPRD_CODEC_HP_POP_TIME_COUNT (80) /* max 800ms will timeout */
+#endif
+
+#define SPRD_CODEC_RATE_8000 (10)
+#define SPRD_CODEC_RATE_9600 ( 9)
+#define SPRD_CODEC_RATE_11025 ( 8)
+#define SPRD_CODEC_RATE_12000 ( 7)
+#define SPRD_CODEC_RATE_16000 ( 6)
+#define SPRD_CODEC_RATE_22050 ( 5)
+#define SPRD_CODEC_RATE_24000 ( 4)
+#define SPRD_CODEC_RATE_32000 ( 3)
+#define SPRD_CODEC_RATE_44100 ( 2)
+#define SPRD_CODEC_RATE_48000 ( 1)
+#define SPRD_CODEC_RATE_96000 ( 0)
+
+/* AUD_TOP_CTL */
+#define DAC_EN_L (0)
+#define ADC_EN_L (1)
+#define DAC_EN_R (2)
+#define ADC_EN_R (3)
+
+/* AUD_DAC_CTL */
+#define DAC_MUTE_START (14)
+#define DAC_MUTE_EN (15)
+
+/* AUD_AUD_STS0 */
+#define DAC_MUTE_U_MASK (5)
+#define DAC_MUTE_D_MASK (4)
+#define DAC_MUTE_U_RAW (3)
+#define DAC_MUTE_D_RAW (2)
+#define DAC_MUTE_ST (0)
+#define DAC_MUTE_ST_MASK (0x3)
+
+/* AUD_INT_CLR */
+/* AUD_INT_EN */
+#define DAC_MUTE_U (1)
+#define DAC_MUTE_D (0)
+
+/* AUDIF_ENB */
+#define AUDIFA_DAC_EN (0)
+#define AUDIFA_ADC_EN (1)
+
+/* AUDIF_INT_EN */
+/* AUDIF_INT_CLR */
+/* AUDIF_INT_RAW */
+/* AUDIF_INT_MASK */
+#define AUDIO_POP_IRQ (7)
+#define OVP_IRQ (6)
+#define OTP_IRQ (5)
+#define PA_OCP_IRQ (4)
+#define LOR_OCP_IRQ (3)
+#define LOL_OCP_IRQ (2)
+#define EAR_OCP_IRQ (1)
+#define HP_OCP_IRQ (0)
+
+/* PMUR1 */
+#define VB_EN (7)
+#define BG_EN (6)
+#define BG_IBIAS_EN (5)
+#define VCM_EN (4)
+#define VCM_BUF_EN (3)
+#define VBO_EN (2)
+#define MICBIAS_EN (1)
+#define AUXMICBIAS_EN (0)
+
+/* PMUR2 */
+#define PA_SW_EN (7)
+#define PA_LDO_EN (6)
+#define PA_EN (5)
+#define PAR_SW_EN (4)
+#define PAR_LDO_EN (3)
+#define PAR_EN (2)
+#define OVP_PD (1)
+#define OVP_LDO_EN (0)
+
+#define LDO_V_29 (0)
+#define LDO_V_31 (1)
+#define LDO_V_32 (2)
+#define LDO_V_33 (3)
+#define LDO_V_34 (4)
+#define LDO_V_35 (5)
+#define LDO_V_36 (6)
+#define LDO_V_38 (7)
+
+#define MIC_LDO_V_21 (0)
+#define MIC_LDO_V_19 (1)
+#define MIC_LDO_V_23 (2)
+#define MIC_LDO_V_25 (3)
+
+/* PMUR3 */
+#define VCM_V (5)
+#define VCM_V_MASK (0x7)
+#define MICBIAS_V (3)
+#define MICBIAS_V_MASK (0x3)
+#define AUXMICBIAS_V (1)
+#define AUXMICBIAS_V_MASK (0x3)
+
+/* PMUR4 */
+#define PA_SWOCP_PD (7)
+#define PA_LDOOCP_PD (6)
+#define PA_LDO_V (3)
+#define PA_LDO_V_MASK (0x7)
+#define BG_RST (2)
+#define BG_I (0)
+#define BG_I_MASK (0x3)
+
+/* PMUR6 */
+#define PA_OVP_V (5)
+#define PA_OVP_V_MASK (0x7)
+#define PA_OVP_542 (0)
+#define PA_OVP_527 (1)
+#define PA_OVP_512 (2)
+#define PA_OVP_496 (3)
+#define PA_OVP_481 (4)
+#define PA_OVP_465 (5)
+#define PA_OVP_450 (6)
+#define PA_OVP_435 (7)
+
+/* AACR1 */
+#define ADC_IBUF_PD (7)
+#define ADC_VREF1P5 (6)
+#define ADCL_PD (5)
+#define ADCL_RST (4)
+#define ADCR_PD (3)
+#define ADCR_RST (2)
+#define FM_REC_EN (1)
+
+/* AACR2 */
+#define ADCPGAL_PD (7)
+#define ADCPGAL_BYP (6)
+#define ADCPGAR_PD (5)
+#define ADCPGAR_BYP (4)
+#define ADCPGAL_P_EN (3)
+#define ADCPGAL_N_EN (2)
+#define ADCPGAR_P_EN (1)
+#define ADCPGAR_N_EN (0)
+
+/* AAICR1 */
+#define MICP_ADCL (7)
+#define MICN_ADCL (6)
+#define MICP_ADCR (5)
+#define MICN_ADCR (4)
+#define HPMICP_ADCL (3)
+#define HPMICN_ADCL (2)
+#define HPMICP_ADCR (1)
+#define HPMICN_ADCR (0)
+
+/* AAICR2 */
+#define AUXMICP_ADCL (7)
+#define AUXMICN_ADCL (6)
+#define AUXMICP_ADCR (5)
+#define AUXMICN_ADCR (4)
+
+/* AAICR3 */
+#define AIL_ADCL (7)
+#define AIR_ADCL (6)
+#define VCM_ADCL (5)
+#define AIL_ADCR (4)
+#define AIR_ADCR (3)
+#define VCM_ADCR (2)
+
+/* DACR */
+#define DACL_EN (7)
+#define DACR_EN (6)
+#define DACBUF_I (5)
+
+/* DAOCR1 */
+#define ADCL_P_HPL (7)
+#define ADCL_N_HPR (6)
+#define ADCR_P_HPL (5)
+#define ADCR_P_HPR (4)
+#define DACL_P_HPL (3)
+#define DACL_N_HPR (2)
+#define DACR_P_HPL (1)
+#define DACR_P_HPR (0)
+
+/* DAOCR2 */
+#define ADCL_P_AOLP (7)
+#define ADCL_N_AOLN (6)
+#define ADCR_P_AOLP (5)
+#define ADCR_N_AOLN (4)
+#define DACL_P_AOLP (3)
+#define DACL_N_AOLN (2)
+#define DACR_P_AOLP (1)
+#define DACR_N_AOLN (0)
+
+/* DAOCR3 */
+#define ADCL_P_AORP (7)
+#define ADCL_N_AORN (6)
+#define ADCR_P_AORP (5)
+#define ADCR_N_AORN (4)
+#define DACL_P_AORP (3)
+#define DACL_N_AORN (2)
+#define DACR_P_AORP (1)
+#define DACR_N_AORN (0)
+
+/* DAOCR4 */
+#define DACL_P_EARP (7)
+#define DACL_N_EARN (6)
+
+/* DCR1 */
+#define HPL_EN (7)
+#define HPR_EN (6)
+#define EAR_EN (5)
+#define AOL_EN (4)
+#define AOR_EN (3)
+#define DIFF_EN (2)
+
+/* DCR2 */
+#define PA_D_EN (7)
+#define PA_DTRI_F (5)
+#define PA_DTRI_F_MASK (0x03)
+#define PA_DEMI_EN (4)
+#define PA_SS_EN (3)
+#define PA_SS_RST (2)
+#define DRV_STOP_EN (1)
+
+/* DCR3 */
+#define DRV_OCP_AOL_PD (7)
+#define DRV_OCP_AOR_PD (6)
+#define DRV_OCP_EAR_PD (5)
+#define DRV_OCP_HP_PD (4)
+
+/* PNRCR1 */
+#define HP_POP_CTL (6)
+#define HP_POP_CTL_MASK (0x03)
+#define HP_POP_CTL_DIS (0)
+#define HP_POP_CTL_UP (1)
+#define HP_POP_CTL_DOWN (2)
+#define HP_POP_CTL_HOLD (3)
+
+#define HP_POP_STEP (3)
+#define HP_POP_STEP_MASK (0x07)
+#define HP_POP_STEP_012 (0)
+#define HP_POP_STEP_025 (1)
+#define HP_POP_STEP_05 (2)
+#define HP_POP_STEP_1 (3)
+#define HP_POP_STEP_2 (4)
+#define HP_POP_STEP_4 (5)
+#define HP_POP_STEP_8 (6)
+#define HP_POP_STEP_16 (7)
+
+/* CCR */
+#define CLK_REVERSE (7)
+#define ADC_CLK_PD (6)
+#define ADC_CLK_RST (5)
+#define DAC_CLK_EN (4)
+#define DRV_CLK_EN (3)
+
+/* IFR1 */
+#define HP_POP_FLG (4)
+#define HP_POP_FLG_MASK (0x03)
+#define HP_POP_FLG_NEAR_CMP (3)
+#define OVP_FLAG (2)
+
+#define SPRD_CODEC_DP_BASE (CODEC_DP_BASE)
+
+#define AUD_TOP_CTL (SPRD_CODEC_DP_BASE + 0x0000)
+#define AUD_AUD_CTL (SPRD_CODEC_DP_BASE + 0x0004)
+#define AUD_I2S_CTL (SPRD_CODEC_DP_BASE + 0x0008)
+#define AUD_DAC_CTL (SPRD_CODEC_DP_BASE + 0x000C)
+#define AUD_SDM_CTL0 (SPRD_CODEC_DP_BASE + 0x0010)
+#define AUD_SDM_CTL1 (SPRD_CODEC_DP_BASE + 0x0014)
+#define AUD_ADC_CTL (SPRD_CODEC_DP_BASE + 0x0018)
+#define AUD_LOOP_CTL (SPRD_CODEC_DP_BASE + 0x001C)
+#define AUD_AUD_STS0 (SPRD_CODEC_DP_BASE + 0x0020)
+#define AUD_INT_CLR (SPRD_CODEC_DP_BASE + 0x0024)
+#define AUD_INT_EN (SPRD_CODEC_DP_BASE + 0x0028)
+
+#define SPRD_CODEC_DP_END (SPRD_CODEC_DP_BASE + 0x002C)
+#define IS_SPRD_CODEC_DP_RANG(reg) (((reg) >= SPRD_CODEC_DP_BASE) && ((reg) < SPRD_CODEC_DP_END))
+
+#define SPRD_CODEC_AP_BASE (CODEC_AP_BASE)
+
+#define AUDIF_ENB (SPRD_CODEC_AP_BASE + 0x0000)
+#define AUDIF_CLR (SPRD_CODEC_AP_BASE + 0x0004)
+#define AUDIF_SYNC_CTL (SPRD_CODEC_AP_BASE + 0x0008)
+#define AUDIF_OCPTMR_CTL (SPRD_CODEC_AP_BASE + 0x000C)
+#define AUDIF_OTPTMR_CTL (SPRD_CODEC_AP_BASE + 0x0010)
+#define AUDIF_SHUTDOWN_CTL (SPRD_CODEC_AP_BASE + 0x0014)
+#define AUDIF_INT_EN (SPRD_CODEC_AP_BASE + 0x0018)
+#define AUDIF_INT_CLR (SPRD_CODEC_AP_BASE + 0x001C)
+#define AUDIF_INT_RAW (SPRD_CODEC_AP_BASE + 0x0020)
+#define AUDIF_INT_MASK (SPRD_CODEC_AP_BASE + 0x0024)
+#define AUDIF_OVPTMR_CTL (SPRD_CODEC_AP_BASE + 0x0028)
+/* 0x002C ~ 0x003C is reserved for ADIE digital part */
+
+#define PMUR1 (SPRD_CODEC_AP_BASE + 0x0040)
+#define PMUR2 (SPRD_CODEC_AP_BASE + 0x0044)
+#define PMUR3 (SPRD_CODEC_AP_BASE + 0x0048)
+#define PMUR4 (SPRD_CODEC_AP_BASE + 0x004C)
+#define PMUR5 (SPRD_CODEC_AP_BASE + 0x0050)
+#define PMUR6 (SPRD_CODEC_AP_BASE + 0x0054)
+
+#define HIBDR (SPRD_CODEC_AP_BASE + 0x0058)
+
+#define AACR1 (SPRD_CODEC_AP_BASE + 0x005C)
+#define AACR2 (SPRD_CODEC_AP_BASE + 0x0060)
+#define AAICR1 (SPRD_CODEC_AP_BASE + 0x0064)
+#define AAICR2 (SPRD_CODEC_AP_BASE + 0x0068)
+#define AAICR3 (SPRD_CODEC_AP_BASE + 0x006C)
+#define ACGR (SPRD_CODEC_AP_BASE + 0x0070)
+
+#define DACR (SPRD_CODEC_AP_BASE + 0x0074)
+#define DAOCR1 (SPRD_CODEC_AP_BASE + 0x0078)
+#define DAOCR2 (SPRD_CODEC_AP_BASE + 0x007C)
+#define DAOCR3 (SPRD_CODEC_AP_BASE + 0x0080)
+#define DAOCR4 (SPRD_CODEC_AP_BASE + 0x00BC)
+#define DCR1 (SPRD_CODEC_AP_BASE + 0x0084)
+#define DCR2 (SPRD_CODEC_AP_BASE + 0x0088)
+#define DCR3 (SPRD_CODEC_AP_BASE + 0x008C)
+#define DCR4 (SPRD_CODEC_AP_BASE + 0x0090)
+#define DCGR1 (SPRD_CODEC_AP_BASE + 0x0094)
+#define DCGR2 (SPRD_CODEC_AP_BASE + 0x0098)
+#define DCGR3 (SPRD_CODEC_AP_BASE + 0x009C)
+
+#define PNRCR1 (SPRD_CODEC_AP_BASE + 0x00A0)
+#define PNRCR2 (SPRD_CODEC_AP_BASE + 0x00A4)
+#define PNRCR3 (SPRD_CODEC_AP_BASE + 0x00A8)
+
+#define CCR (SPRD_CODEC_AP_BASE + 0x00AC)
+
+#define IFR1 (SPRD_CODEC_AP_BASE + 0x00B0)
+#define IFR2 (SPRD_CODEC_AP_BASE + 0x00B4)
+#define IFR3 (SPRD_CODEC_AP_BASE + 0x00B8)
+
+#define SPRD_CODEC_AP_END (SPRD_CODEC_AP_BASE + 0x00C0)
+#define IS_SPRD_CODEC_AP_RANG(reg) (((reg) >= SPRD_CODEC_AP_BASE) && ((reg) < SPRD_CODEC_AP_END))
+
+#endif /* __SPRD_CODEC_H */
diff --git a/sound/soc/sprd/codec/sprd/v3/Kconfig b/sound/soc/sprd/codec/sprd/v3/Kconfig
new file mode 100755
index 00000000..588501bb
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v3/Kconfig
@@ -0,0 +1,15 @@
+config SND_SOC_SPRD_CODEC_V3
+ tristate # "Sprd-codec-v3"
+
+config SND_SOC_SPRD_AUDIO_USE_INTER_HP_PA
+ depends on SND_SOC_SPRD_CODEC_V3
+ bool "Use spreadtrum internal HeadPhone PA"
+ help
+ Say Y if you want use sprdtrume internal headphone pa
+
+config SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ depends on SND_SOC_SPRD_CODEC_V3
+ bool "Use spreadtrum ear jack type 1 or 3"
+ help
+ Say Y if you want to use sprdtrume ear jack type 1 or 3
+
diff --git a/sound/soc/sprd/codec/sprd/v3/Makefile b/sound/soc/sprd/codec/sprd/v3/Makefile
new file mode 100644
index 00000000..16244710
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v3/Makefile
@@ -0,0 +1,7 @@
+#SPRD CODEC
+
+SPRD_SOUND_TREE := sound/soc/sprd/
+KBUILD_CFLAGS += -I$(SPRD_SOUND_TREE) -I$(SPRD_SOUND_TREE)/codec
+
+snd-soc-sprd-sprd-codec-v3-objs := sprd-codec.o sprd-audio-power.o
+obj-$(CONFIG_SND_SOC_SPRD_CODEC_V3) += snd-soc-sprd-sprd-codec-v3.o
diff --git a/sound/soc/sprd/codec/sprd/v3/sprd-audio-power.c b/sound/soc/sprd/codec/sprd/v3/sprd-audio-power.c
new file mode 100644
index 00000000..73d86615
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v3/sprd-audio-power.c
@@ -0,0 +1,654 @@
+/*
+ * sound/soc/sprd/codec/sprd/v3/sprd-audio-power.c
+ *
+ * SPRD-AUDIO-POWER -- SpreadTrum intergrated audio power supply.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "sprd-asoc-debug.h"
+#define pr_fmt(fmt) pr_sprd_fmt("POWER") fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#include <soc/sprd/sprd-audio.h>
+#include "sprd-audio-power.h"
+#include "sprd-asoc-common.h"
+
+#ifdef CONFIG_AUDIO_POWER_ALLOW_UNSUPPORTED
+#define UNSUP_MASK 0x0000
+#else
+#define UNSUP_MASK 0x8000
+#endif
+
+#define UNSUP(x) (UNSUP_MASK | (x))
+#define IS_UNSUP(x) (UNSUP_MASK & (x))
+#define LDO_MV(x) (~UNSUP_MASK & (x))
+
+struct sprd_audio_power_info {
+ int id;
+ /* enable/disable register information */
+ int en_reg;
+ int en_bit;
+ int (*power_enable) (void);
+ int (*power_disable) (void);
+ /* configure deepsleep cut off or not */
+ int (*sleep_ctrl) (void *, int);
+ /* voltage level register information */
+ int v_reg;
+ int v_mask;
+ int v_shift;
+ int v_table_len;
+ const u16 *v_table;
+ int min_uV;
+ int max_uV;
+
+ /* unit: us */
+ int on_delay;
+ int off_delay;
+
+ /* used by regulator core */
+ struct regulator_desc desc;
+
+ void *data;
+};
+
+#define SPRD_AUDIO_POWER_LDO_LOW(label, r_supply, d_id, \
+e_reg, e_bit, p_enable, p_disable, s_ctrl, \
+reg, mask, shift, table, table_size, d_on_delay, d_off_delay) \
+static struct regulator_consumer_supply label[] = { \
+ { .supply = #label },\
+}; \
+static struct regulator_init_data label##_data = { \
+ .supply_regulator = r_supply, \
+ .constraints = { \
+ .name = "SRG_" #label, \
+ }, \
+ .num_consumer_supplies = ARRAY_SIZE(label), \
+ .consumer_supplies = label, \
+}; \
+static struct sprd_audio_power_info SPRD_AUDIO_POWER_INFO_##label = { \
+ .id = d_id, \
+ .en_reg = e_reg, \
+ .en_bit = e_bit, \
+ .power_enable = p_enable, \
+ .power_disable = p_disable, \
+ .sleep_ctrl = s_ctrl, \
+ .v_reg = reg, \
+ .v_mask = mask, \
+ .v_shift = shift, \
+ .v_table_len = table_size, \
+ .v_table = table, \
+ .on_delay = d_on_delay, \
+ .off_delay = d_off_delay, \
+ .desc = { \
+ .name = #label, \
+ .id = SPRD_AUDIO_POWER_##label, \
+ .n_voltages = table_size, \
+ .ops = &sprd_audio_power_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+}
+
+#define SPRD_AUDIO_POWER_LDO(label, r_supply, id, en_reg, en_bit, sleep_ctrl, \
+ v_reg, v_mask, v_shift, v_table, on_delay, off_delay) \
+SPRD_AUDIO_POWER_LDO_LOW(label, r_supply, id, en_reg, en_bit, 0, 0, sleep_ctrl, \
+ v_reg, v_mask, v_shift, v_table, ARRAY_SIZE(v_table), on_delay, off_delay)
+
+#define SPRD_AUDIO_POWER_SIMPLE_LDO(label, r_supply, id, en_reg, en_bit, sleep_ctrl, \
+ on_delay, off_delay) \
+SPRD_AUDIO_POWER_LDO_LOW(label, r_supply, id, en_reg, en_bit, 0, 0, sleep_ctrl, \
+ 0, 0, 0, 0, 0, on_delay, off_delay)
+
+#define SPRD_AUDIO_POWER_REG_LDO(label, id, power_enable, power_disable) \
+SPRD_AUDIO_POWER_LDO_LOW(label, 0, id, 0, 0, power_enable, power_disable, \
+ 0, 0, 0, 0, 0, 0, 0, 0)
+
+static inline int sprd_power_read(struct sprd_audio_power_info *info, int reg)
+{
+ return arch_audio_power_read(reg);
+}
+
+static inline int
+sprd_power_write(struct sprd_audio_power_info *info, int reg, int val)
+{
+ int ret = 0;
+ ret = arch_audio_power_write(reg, val);
+ sp_asoc_pr_reg("[0x%04x] W:[0x%08x] R:[0x%08x]\n", reg & 0xFFFF, val,
+ arch_audio_power_read(reg));
+ return ret;
+}
+
+static inline int
+sprd_power_u_bits(struct sprd_audio_power_info *info,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ int ret = 0;
+ ret = arch_audio_power_write_mask(reg, value, mask);
+ sp_asoc_pr_reg("[0x%04x] U:[0x%08x] R:[0x%08x]\n", reg & 0xFFFF, value,
+ arch_audio_power_read(reg));
+ return ret;
+}
+
+static int sprd_audio_power_is_enabled(struct regulator_dev *rdev)
+{
+ int is_enable;
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+
+ sp_asoc_pr_dbg("%s Is Enabled\n", rdev->desc->name);
+
+ if (info->en_reg) {
+ is_enable = sprd_power_read(info, info->en_reg) & info->en_bit;
+ } else {
+ is_enable = info->en_bit;
+ }
+
+ return ! !is_enable;
+}
+
+static int audio_power_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int vsel;
+
+ BUG_ON(!info->v_table);
+
+ for (vsel = 0; vsel < info->v_table_len; vsel++) {
+ int mV = info->v_table[vsel];
+ int uV;
+
+ if (IS_UNSUP(mV))
+ continue;
+ uV = LDO_MV(mV) * 1000;
+
+ /* use the first in-range value */
+ if (min_uV <= uV && uV <= max_uV) {
+ sp_asoc_pr_dbg("%s: %dus\n", __func__, uV);
+ sprd_power_u_bits(info,
+ info->v_reg,
+ info->v_mask << info->v_shift,
+ vsel << info->v_shift);
+
+ return vsel;
+ }
+ }
+ return -EDOM;
+}
+
+static int sprd_audio_power_enable(struct regulator_dev *rdev)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Enable\n", rdev->desc->name);
+
+ if (info->en_reg) {
+ ret = sprd_power_u_bits(info,
+ info->en_reg, info->en_bit,
+ info->en_bit);
+ } else {
+ if (info->power_enable)
+ ret = info->power_enable();
+ info->en_bit = 1;
+ }
+
+ udelay(info->on_delay);
+
+ if (info->v_table) {
+ if (info->min_uV || info->max_uV)
+ audio_power_set_voltage(rdev, info->min_uV,
+ info->max_uV);
+ }
+ return ret;
+}
+
+static int sprd_audio_power_disable(struct regulator_dev *rdev)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Disable\n", rdev->desc->name);
+
+ if (info->en_reg) {
+ ret = sprd_power_u_bits(info, info->en_reg, info->en_bit, 0);
+ } else {
+ if (info->power_disable)
+ ret = info->power_disable();
+ info->en_bit = 0;
+ }
+
+ udelay(info->off_delay);
+
+ return ret;
+}
+
+static int sprd_audio_power_get_status(struct regulator_dev *rdev)
+{
+ sp_asoc_pr_dbg("%s Get Status\n", rdev->desc->name);
+
+ if (!sprd_audio_power_is_enabled(rdev))
+ return REGULATOR_STATUS_OFF;
+ return REGULATOR_STATUS_NORMAL;
+}
+
+static int sprd_audio_power_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ int ret = 0;
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+
+ sp_asoc_pr_dbg("%s Set Mode 0x%x\n", rdev->desc->name, mode);
+
+ if (!info->sleep_ctrl) {
+ return ret;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ ret = info->sleep_ctrl(info, 0);
+ break;
+ case REGULATOR_MODE_STANDBY:
+ ret = info->sleep_ctrl(info, 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static const u16 VCOM_VSEL_table[] = {
+ 2900, 3100, 3200, 3300,
+ 3400, 3500, 3600, 3800,
+};
+
+static const u16 VPA_VSEL_table[] = {
+ 2900, 3100, 3200, 3300,
+ 3400, 3500, 3600, 3800,
+};
+
+static const u16 VMIC_VSEL_table[] = {
+ 1700, 2210, 1900, 2470,
+ 2100, 2730, 2300, 3000,
+};
+
+static const u16 VBG_IBAIS_VSEL_table[] = {
+ 150, 200, 250, 300,
+};
+
+static const u16 HIB_VSEL_table[] = {
+ 1000, 950, 900, 850, 800, 750,
+ 700, 650, 600, 550, 500, 450, 400,
+};
+
+static int sprd_audio_power_list_voltage(struct regulator_dev *rdev,
+ unsigned index)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int mV;
+
+ sp_asoc_pr_dbg("%s List Voltage\n", rdev->desc->name);
+
+ if (!info->v_table) {
+ return 0;
+ }
+
+ if (index >= info->v_table_len) {
+ return 0;
+ }
+
+ mV = info->v_table[index];
+ return IS_UNSUP(mV) ? 0 : (LDO_MV(mV) * 1000);
+}
+
+static int
+sprd_audio_power_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned *selector)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int vsel;
+
+ sp_asoc_pr_info("%s Set Voltage %d--%d\n", rdev->desc->name, min_uV,
+ max_uV);
+
+ if (!info->v_table) {
+ return 0;
+ }
+ info->min_uV = min_uV;
+ info->max_uV = max_uV;
+
+ vsel = audio_power_set_voltage(rdev, min_uV, max_uV);
+ if (vsel != -EDOM)
+ *selector = vsel;
+
+ return vsel;
+}
+
+static int sprd_audio_power_get_voltage(struct regulator_dev *rdev)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int vsel;
+
+ sp_asoc_pr_dbg("%s Get Voltage\n", rdev->desc->name);
+
+ if (!info->v_table) {
+ return 0;
+ }
+
+ vsel = (sprd_power_read(info, info->v_reg) & info->v_mask)
+ >> info->v_shift;
+ return LDO_MV(info->v_table[vsel]) * 1000;
+}
+
+static struct regulator_ops sprd_audio_power_ops = {
+ .list_voltage = sprd_audio_power_list_voltage,
+
+ .set_voltage = sprd_audio_power_set_voltage,
+ .get_voltage = sprd_audio_power_get_voltage,
+
+ .enable = sprd_audio_power_enable,
+ .disable = sprd_audio_power_disable,
+ .is_enabled = sprd_audio_power_is_enabled,
+
+ .set_mode = sprd_audio_power_set_mode,
+
+ .get_status = sprd_audio_power_get_status,
+};
+
+static int sprd_audio_power_sleep_ctrl(void *data, int en)
+{
+ struct sprd_audio_power_info *info = data;
+ int reg, mask, value;
+ switch (info->desc.id) {
+ case SPRD_AUDIO_POWER_HEADMICBIAS:
+ reg = PMUR2_PMUR1;
+ mask = BIT(HEADMIC_SLEEP_EN);
+ value = en ? mask : 0;
+ return sprd_power_u_bits(info, reg, mask, value);
+ break;
+ default:
+ BUG_ON(0);
+ }
+ return 0;
+}
+
+static inline int vreg_enable(void)
+{
+ int ret = 0;
+ static int i = 0;
+ arch_audio_codec_switch(AUDIO_TO_ARM_CTRL);
+ arch_audio_codec_analog_reg_enable();
+ if (i == 0) {
+ arch_audio_codec_analog_reset();
+ /*set initial value from xun.zhang*/
+ arch_audio_codec_write_mask(PMUR4_PMUR3, BIT(ICM_PLUS_EN),
+ BIT(ICM_PLUS_EN));
+ i = 1;
+ }
+ return ret;
+}
+
+static inline int vreg_disable(void)
+{
+ /*arch_audio_codec_reset();*/
+ arch_audio_codec_analog_reg_disable();
+ return 0;
+}
+
+/*
+ * We list regulators here if systems need some level of
+ * software control over them after boot.
+ */
+SPRD_AUDIO_POWER_REG_LDO(VREG, 1, vreg_enable, vreg_disable);
+SPRD_AUDIO_POWER_SIMPLE_LDO(VB, "VREG", 2, PMUR2_PMUR1, BIT(VB_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(BG, "VB", 3, PMUR4_PMUR3, BIT(BG_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_LDO(BG_IBIAS, "BG", 4, PMUR4_PMUR3, BIT(BG_IBIAS_EN), NULL,
+ PMUR4_PMUR3, BG_I_MASK, BG_I, VBG_IBAIS_VSEL_table, 0, 0);
+SPRD_AUDIO_POWER_LDO(VCOM, "BG_IBIAS", 5, PMUR4_PMUR3, BIT(VCM_EN), NULL,
+ PMUR4_PMUR3, VCM_V_MASK, VCM_V, VCOM_VSEL_table, 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(VCOM_BUF, "VCOM", 6, PMUR4_PMUR3, BIT(VCM_BUF_EN),
+ NULL, 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(VBO, "VB", 7, PMUR2_PMUR1, BIT(VBO_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_LDO(VMICBIAS, "VCOM", 8, 0, 0, NULL,
+ PMUR2_PMUR1, MICBIAS_V_MASK, MICBIAS_V, VMIC_VSEL_table, 0,
+ 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(MICBIAS, "VMICBIAS", 9, PMUR4_PMUR3,
+ BIT(MICBIAS_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(AUXMICBIAS, "VMICBIAS", 10, PMUR4_PMUR3,
+ BIT(AUXMICBIAS_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_LDO(HEADMICBIAS, "VMICBIAS", 11, PMUR2_PMUR1,
+ BIT(HEADMICBIAS_EN), sprd_audio_power_sleep_ctrl,
+ HIBDR2_HIBDR1, HIB_SBUT_MASK, HIB_SBUT, HIB_VSEL_table, 0,
+ 0);
+
+#define SPRD_OF_MATCH(comp, label) \
+ { \
+ .compatible = comp, \
+ .data = &SPRD_AUDIO_POWER_INFO_##label, \
+ }
+
+static const struct of_device_id sprd_audio_power_of_match[] = {
+ SPRD_OF_MATCH("sp,audio-vreg", VREG),
+ SPRD_OF_MATCH("sp,audio-vb", VB),
+ SPRD_OF_MATCH("sp,audio-bg", BG),
+ SPRD_OF_MATCH("sp,audio-bg-ibias", BG_IBIAS),
+ SPRD_OF_MATCH("sp,audio-vcom", VCOM),
+ SPRD_OF_MATCH("sp,audio-vcom-buf", VCOM_BUF),
+ SPRD_OF_MATCH("sp,audio-vbo", VBO),
+ SPRD_OF_MATCH("sp,audio-vmicbias", VMICBIAS),
+ SPRD_OF_MATCH("sp,audio-micbias", MICBIAS),
+ SPRD_OF_MATCH("sp,audio-auxmicbias", AUXMICBIAS),
+ SPRD_OF_MATCH("sp,audio-headmicbias", HEADMICBIAS),
+};
+
+static int sprd_audio_power_probe(struct platform_device *pdev)
+{
+ int i, id;
+ struct sprd_audio_power_info *info;
+ const struct sprd_audio_power_info *template;
+ struct regulator_init_data *initdata;
+ struct regulation_constraints *c;
+ struct regulator_dev *rdev;
+ struct regulator_config config = { };
+ int min_index = 0;
+ int max_index = 0;
+
+ id = pdev->id;
+
+ sp_asoc_pr_info("Probe %d\n", id);
+
+ initdata = pdev->dev.platform_data;
+ for (i = 0, template = NULL; i < ARRAY_SIZE(sprd_audio_power_of_match);
+ i++) {
+ template = sprd_audio_power_of_match[i].data;
+ if (!template || template->desc.id != id)
+ continue;
+ break;
+ }
+
+ if (!template)
+ return -ENODEV;
+
+ if (!initdata)
+ return -EINVAL;
+
+ info = kmemdup(template, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ /* Constrain board-specific capabilities according to what
+ * this driver and the chip itself can actually do.
+ */
+ c = &initdata->constraints;
+ c->valid_modes_mask |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY;
+ c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS;
+
+ if (info->v_table) {
+ for (i = 0; i < info->v_table_len; i++) {
+ if (info->v_table[i] < info->v_table[min_index])
+ min_index = i;
+ if (info->v_table[i] > info->v_table[max_index])
+ max_index = i;
+ }
+ c->min_uV = LDO_MV((info->v_table)[min_index]) * 1000;
+ c->max_uV = LDO_MV((info->v_table)[max_index]) * 1000;
+ sp_asoc_pr_info("min_uV:%d, max_uV:%d\n", c->min_uV, c->max_uV);
+ }
+ info->min_uV = 0;
+ info->max_uV = 0;
+
+ config.dev = &pdev->dev;
+ config.init_data = initdata;
+ config.driver_data = info;
+ config.of_node = pdev->dev.of_node;
+
+ rdev = regulator_register(&info->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "ERR:Can't Register %s, %ld\n",
+ info->desc.name, PTR_ERR(rdev));
+ kfree(info);
+ return PTR_ERR(rdev);
+ }
+ sp_asoc_pr_info("Register %s Success!\n", info->desc.name);
+
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static int sprd_audio_power_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct twlreg_info *info = rdev->reg_data;
+
+ regulator_unregister(platform_get_drvdata(pdev));
+ kfree(info);
+ return 0;
+}
+
+static struct platform_driver sprd_audio_power_driver = {
+ .driver = {
+ .name = "sprd-audio-power",
+ .owner = THIS_MODULE,
+ },
+ .probe = sprd_audio_power_probe,
+ .remove = sprd_audio_power_remove,
+};
+
+static int __init sprd_audio_power_regulator_init(void)
+{
+ return platform_driver_register(&sprd_audio_power_driver);
+}
+
+postcore_initcall(sprd_audio_power_regulator_init);
+
+static void __exit sprd_audio_power_regulator_exit(void)
+{
+ platform_driver_unregister(&sprd_audio_power_driver);
+}
+
+module_exit(sprd_audio_power_regulator_exit);
+
+MODULE_DESCRIPTION("SPRD audio power regulator driver");
+MODULE_AUTHOR("Ken Kuang <ken.kuang@spreadtrum.com>");
+MODULE_ALIAS("platform:sprd-audio-power");
+MODULE_LICENSE("GPL");
+
+#define SPRD_AUDIO_DEVICE(label) \
+{ \
+ .name = "sprd-audio-power", \
+ .id = SPRD_AUDIO_POWER_##label, \
+ .dev = { \
+ .platform_data = &label##_data, \
+ }, \
+}
+
+static struct platform_device sprd_audio_regulator_devices[] = {
+ SPRD_AUDIO_DEVICE(VREG),
+ SPRD_AUDIO_DEVICE(VB),
+ SPRD_AUDIO_DEVICE(BG),
+ SPRD_AUDIO_DEVICE(BG_IBIAS),
+ SPRD_AUDIO_DEVICE(VCOM),
+ SPRD_AUDIO_DEVICE(VCOM_BUF),
+ SPRD_AUDIO_DEVICE(VBO),
+ SPRD_AUDIO_DEVICE(VMICBIAS),
+ SPRD_AUDIO_DEVICE(MICBIAS),
+ SPRD_AUDIO_DEVICE(AUXMICBIAS),
+ SPRD_AUDIO_DEVICE(HEADMICBIAS),
+};
+
+#ifdef CONFIG_OF
+static int sprd_audio_power_version(void)
+{
+ struct device_node *node = of_find_node_by_path("/sprd-audio-devices");
+ struct device_node *child;
+ if (node) {
+ u32 val;
+ for_each_child_of_node(node, child) {
+ if (!of_property_read_u32
+ (child, "sprd,audio_power_ver", &val)) {
+ sp_asoc_pr_dbg
+ ("Configure Audio Power Version is %d\n",
+ val);
+ return val;
+ }
+ }
+ pr_err("ERR:Not found sprd,audio_power_ver!\n");
+ } else {
+ pr_err("ERR:No sprd-audio-devices Node!\n");
+ }
+ return 0;
+}
+#else
+int sprd_audio_power_version(void)
+ __attribute__ ((weak, alias("__sprd_audio_power_version")));
+
+static int __sprd_audio_power_version(void)
+{
+ return 3;
+}
+#endif
+
+static int sprd_audio_power_add_devices(void)
+{
+ int ret = 0;
+ int i;
+ if (3 != sprd_audio_power_version()) {
+ return 0;
+ }
+ for (i = 0; i < ARRAY_SIZE(sprd_audio_regulator_devices); i++) {
+ ret =
+ platform_device_register(&sprd_audio_regulator_devices[i]);
+ if (ret < 0) {
+ pr_err("ERR:Add Regulator %d Failed %d\n",
+ sprd_audio_regulator_devices[i].id, ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int __init sprd_audio_power_init(void)
+{
+ return sprd_audio_power_add_devices();
+}
+
+device_initcall(sprd_audio_power_init);
diff --git a/sound/soc/sprd/codec/sprd/v3/sprd-audio-power.h b/sound/soc/sprd/codec/sprd/v3/sprd-audio-power.h
new file mode 100755
index 00000000..ecdfac5b
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v3/sprd-audio-power.h
@@ -0,0 +1,35 @@
+/*
+ * sound/soc/sprd/codec/sprd/v3/sprd-audio-power.h
+ *
+ * SPRD-AUDIO-POWER -- SpreadTrum intergrated audio power supply.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * 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 __SPRD_AUDIO_POWER_H
+#define __SPRD_AUDIO_POWER_H
+
+#include "sprd-codec.h"
+
+#define SPRD_AUDIO_POWER_VREG 1
+#define SPRD_AUDIO_POWER_VB 2
+#define SPRD_AUDIO_POWER_BG 3
+#define SPRD_AUDIO_POWER_BG_IBIAS 4
+#define SPRD_AUDIO_POWER_VCOM 5
+#define SPRD_AUDIO_POWER_VCOM_BUF 6
+#define SPRD_AUDIO_POWER_VBO 7
+#define SPRD_AUDIO_POWER_VMICBIAS 8
+#define SPRD_AUDIO_POWER_MICBIAS 9
+#define SPRD_AUDIO_POWER_AUXMICBIAS 10
+#define SPRD_AUDIO_POWER_HEADMICBIAS 11
+
+#endif
diff --git a/sound/soc/sprd/codec/sprd/v3/sprd-codec.c b/sound/soc/sprd/codec/sprd/v3/sprd-codec.c
new file mode 100755
index 00000000..8d778aaf
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v3/sprd-codec.c
@@ -0,0 +1,3858 @@
+/*
+ * sound/soc/sprd/codec/sprd/v3/sprd-codec.c
+ *
+ * SPRD-CODEC -- SpreadTrum Tiger intergrated codec.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "sprd-asoc-debug.h"
+#define pr_fmt(fmt) pr_sprd_fmt("CDCV3") fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/stat.h>
+#include <linux/atomic.h>
+#include <linux/regulator/consumer.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/power_supply.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/sprd-audio-hook.h>
+
+#include <soc/sprd/sprd-audio.h>
+#include <soc/sprd/globalregs.h>
+#include "sprd-codec.h"
+#include "sprd-asoc-common.h"
+#include <soc/sprd/arch_misc.h>
+#include <linux/of_irq.h>
+
+#ifdef CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE
+#if (CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE != 32000) && (CONFIG_SND_SOC_CODEC_SRC_SAMPLE_RATE != 48000)
+#error "We hope the CODEC sample rate is 32KHz or 48KHz!"
+#endif
+#define SRC_SUPPORT_RATE (SNDRV_PCM_RATE_44100)
+#else /* !CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE */
+#define SRC_SUPPORT_RATE (0)
+#endif
+
+int hp_register_notifier(struct notifier_block *nb);
+int hp_unregister_notifier(struct notifier_block *nb);
+int get_hp_plug_state(void);
+
+#define SOC_REG(r) ((unsigned short)(r))
+#define FUN_REG(f) ((unsigned short)(-((f) + 1)))
+#define ID_FUN(id, lr) ((unsigned short)(((id) << 1) | (lr)))
+
+#define SPRD_CODEC_AP_BASE_HI (SPRD_CODEC_AP_BASE & 0xFFFF0000)
+#define SPRD_CODEC_DP_BASE_HI (SPRD_CODEC_DP_BASE & 0xFFFF0000)
+
+enum {
+ SPRD_CODEC_HP_PA_ORDER = 100,
+ SPRD_CODEC_HP_SWITCH_ORDER = 101,
+ SPRD_CODEC_ANA_MIXER_ORDER = 102, /*must be later with switch order*/
+ SPRD_CODEC_HP_PA_POST_ORDER = 103,
+ SPRD_CODEC_HP_POP_ORDER = 104,
+ SPRD_CODEC_PA_ORDER = 104, /*last power up, later with ANA_MIXER order*/
+};
+
+enum {
+ SPRD_CODEC_PLAYBACK,
+ SPRD_CODEC_CAPTRUE,
+ SPRD_CODEC_CAPTRUE1,
+ SPRD_CODEC_CHAN_MAX,
+};
+
+static const char *sprd_codec_chan_name[SPRD_CODEC_CHAN_MAX] = {
+ "DAC",
+ "ADC",
+ "ADC1",
+};
+
+static inline const char *sprd_codec_chan_get_name(int chan_id)
+{
+ return sprd_codec_chan_name[chan_id];
+}
+
+enum {
+ SPRD_CODEC_PGA_START = 0,
+ SPRD_CODEC_PGA_SPKL = SPRD_CODEC_PGA_START,
+ SPRD_CODEC_PGA_SPKR,
+ SPRD_CODEC_PGA_HPL,
+ SPRD_CODEC_PGA_HPR,
+ SPRD_CODEC_PGA_EAR,
+ SPRD_CODEC_PGA_ADCL,
+ SPRD_CODEC_PGA_ADCR,
+ SPRD_CODEC_PGA_DACL,
+ SPRD_CODEC_PGA_DACR,
+ SPRD_CODEC_PGA_MIC,
+ SPRD_CODEC_PGA_AUXMIC,
+ SPRD_CODEC_PGA_HEADMIC,
+ SPRD_CODEC_PGA_AIL,
+ SPRD_CODEC_PGA_AIR,
+
+ SPRD_CODEC_PGA_END
+};
+
+#define GET_PGA_ID(x) ((x)-SPRD_CODEC_PGA_START)
+#define SPRD_CODEC_PGA_MAX (SPRD_CODEC_PGA_END - SPRD_CODEC_PGA_START)
+static const char *sprd_codec_pga_debug_str[SPRD_CODEC_PGA_MAX] = {
+ "SPKL",
+ "SPKR",
+ "HPL",
+ "HPR",
+ "EAR",
+ "ADCL",
+ "ADCR",
+ "DACL",
+ "DACR",
+ "MIC",
+ "AUXMIC",
+ "HEADMIC",
+ "AIL",
+ "AIR",
+};
+
+typedef int (*sprd_codec_pga_set) (struct snd_soc_codec * codec, int pgaval);
+
+struct sprd_codec_pga {
+ sprd_codec_pga_set set;
+ int min;
+};
+
+struct sprd_codec_pga_op {
+ int pgaval;
+ sprd_codec_pga_set set;
+};
+
+enum {
+ SPRD_CODEC_LEFT = 0,
+ SPRD_CODEC_RIGHT = 1,
+};
+
+enum {
+ SPRD_CODEC_MIXER_START = SPRD_CODEC_PGA_END + 20,
+ SPRD_CODEC_AIL = SPRD_CODEC_MIXER_START,
+ SPRD_CODEC_AIR,
+ SPRD_CODEC_MAIN_MIC,
+ SPRD_CODEC_AUX_MIC,
+ SPRD_CODEC_HP_MIC,
+ SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_HP_DACL = SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_HP_DACR,
+ SPRD_CODEC_HP_DAC_MIXER_MAX,
+ SPRD_CODEC_HP_ADCL = SPRD_CODEC_HP_DAC_MIXER_MAX,
+ SPRD_CODEC_HP_ADCR,
+ SPRD_CODEC_HP_MIXER_MAX,
+ SPRD_CODEC_SPK_DACL = SPRD_CODEC_HP_MIXER_MAX,
+ SPRD_CODEC_SPK_DACR,
+ SPRD_CODEC_SPK_DAC_MIXER_MAX,
+ SPRD_CODEC_SPK_ADCL = SPRD_CODEC_SPK_DAC_MIXER_MAX,
+ SPRD_CODEC_SPK_ADCR,
+ SPRD_CODEC_SPK_MIXER_MAX,
+ SPRD_CODEC_EAR_DACL = SPRD_CODEC_SPK_MIXER_MAX,
+ SPRD_CODEC_EAR_MIXER_MAX,
+
+ SPRD_CODEC_MIXER_END
+};
+
+#define GET_MIXER_ID(x) ((unsigned short)(x)-ID_FUN(SPRD_CODEC_MIXER_START, SPRD_CODEC_LEFT))
+#define SPRD_CODEC_MIXER_MAX (ID_FUN(SPRD_CODEC_MIXER_END, SPRD_CODEC_LEFT) -ID_FUN(SPRD_CODEC_MIXER_START, SPRD_CODEC_LEFT))
+static const char *sprd_codec_mixer_debug_str[SPRD_CODEC_MIXER_MAX] = {
+ "AIL->ADCL",
+ "AIL->ADCR",
+ "AIR->ADCL",
+ "AIR->ADCR",
+ "MAIN MIC->ADCL",
+ "MAIN MIC->ADCR",
+ "AUX MIC->ADCL",
+ "AUX MIC->ADCR",
+ "HP MIC->ADCL",
+ "HP MIC->ADCR",
+ "DACL->HPL",
+ "DACL->HPR",
+ "DACR->HPL",
+ "DACR->HPR",
+ "ADCL->HPL",
+ "ADCL->HPR",
+ "ADCR->HPL",
+ "ADCR->HPR",
+ "DACL->SPKL",
+ "DACL->SPKR",
+ "DACR->SPKL",
+ "DACR->SPKR",
+ "ADCL->SPKL",
+ "ADCL->SPKR",
+ "ADCR->SPKL",
+ "ADCR->SPKR",
+ "DACL->EAR",
+ "DACR->EAR(bug)"
+};
+
+#define IS_SPRD_CODEC_MIXER_RANG(reg) (((reg) >= ID_FUN(SPRD_CODEC_MIXER_START, SPRD_CODEC_LEFT)) && ((reg) <= ID_FUN(SPRD_CODEC_MIXER_END, SPRD_CODEC_LEFT)))
+#define IS_SPRD_CODEC_PGA_RANG(reg) (((reg) >= SPRD_CODEC_PGA_START) && ((reg) <= SPRD_CODEC_PGA_END))
+#define IS_SPRD_CODEC_MIC_BIAS_RANG(reg) (((reg) >= SPRD_CODEC_MIC_BIAS_START) && ((reg) <= SPRD_CODEC_MIC_BIAS_END))
+
+typedef int (*sprd_codec_mixer_set) (struct snd_soc_codec * codec, int on);
+struct sprd_codec_mixer {
+ int on;
+ sprd_codec_mixer_set set;
+};
+
+uint32_t sprd_get_vbat_voltage(void)
+ __attribute__ ((weak, alias("__sprd_get_vbat_voltage")));
+static uint32_t __sprd_get_vbat_voltage(void)
+{
+ pr_err("ERR: Can't get vbat!\n");
+ return 3800;
+}
+
+struct sprd_codec_ldo_v_map {
+ int ldo_v_level;
+ int volt;
+};
+
+const static struct sprd_codec_ldo_v_map ldo_v_map[] = {
+ /*{ , 3400}, */
+ {LDO_V_29, 3600},
+ {LDO_V_31, 3700},
+ {LDO_V_32, 3800},
+ {LDO_V_33, 3900},
+ {LDO_V_34, 4000},
+ {LDO_V_35, 4100},
+ {LDO_V_36, 4200},
+ {LDO_V_38, 4300},
+};
+
+const static struct sprd_codec_ldo_v_map ldo_vcom_v_map[] = {
+ /*{ , 3400}, */
+ {LDO_V_29, 3800},
+ {LDO_V_31, 3900},
+ {LDO_V_32, 4000},
+ {LDO_V_33, 4100},
+ {LDO_V_34, 4200},
+ {LDO_V_35, 4300},
+ {LDO_V_36, 4400},
+ {LDO_V_38, 4500},
+};
+
+struct sprd_codec_ldo_cfg {
+ const struct sprd_codec_ldo_v_map *v_map;
+ int v_map_size;
+};
+
+struct sprd_codec_inter_pa {
+ /* FIXME little endian */
+ u32 LDO_V_sel:4;
+ u32 DTRI_F_sel:4;
+ u32 is_DEMI_mode:1;
+ u32 is_classD_mode:1;
+ u32 is_LDO_mode:1;
+ u32 is_auto_LDO_mode:1;
+ u32 RESV:20;
+};
+
+struct sprd_codec_pa_setting {
+ union {
+ struct sprd_codec_inter_pa setting;
+ u32 value;
+ };
+ int set;
+};
+
+struct sprd_codec_inter_hp_pa {
+ /* FIXME little endian */
+ u32 class_g_osc:2;
+ u32 class_g_mode:2;
+ u32 class_g_low_power:2;
+ u32 class_g_cgcal:2;
+ u32 class_g_unmute_delay_100ms:3;
+ u32 class_g_cgcal_delay_10ms:3;
+ u32 class_g_close_delay_10ms:2;
+ u32 class_g_open_delay_10ms:4;
+ u32 class_g_vdd_delay_10ms:6;
+ u32 class_g_chp_delay_30ms:4;
+ u32 class_g_all_close_delay_100ms:2;
+ //u32 RESV:0;
+};
+struct sprd_codec_inter_hp_pa_delay {
+ u32 class_g_delay1:4;
+ u32 class_g_delay2:4;
+};
+
+struct sprd_codec_hp_pa_setting {
+ union {
+ struct sprd_codec_inter_hp_pa setting;
+ u32 value;
+ };
+ union{
+ struct sprd_codec_inter_hp_pa_delay delay_setting;
+ u32 delay_value;
+ };
+ int set;
+};
+
+enum {
+ SPRD_CODEC_MIC_BIAS_START =
+ ID_FUN(SPRD_CODEC_MIXER_END, SPRD_CODEC_LEFT) + 100,
+ SPRD_CODEC_MIC_BIAS = SPRD_CODEC_MIC_BIAS_START,
+ SPRD_CODEC_AUXMIC_BIAS,
+ SPRD_CODEC_HEADMIC_BIAS,
+
+ SPRD_CODEC_MIC_BIAS_END
+};
+
+#define GET_MIC_BIAS_ID(x) ((x)-SPRD_CODEC_MIC_BIAS_START)
+#define SPRD_CODEC_MIC_BIAS_MAX (SPRD_CODEC_MIC_BIAS_END -SPRD_CODEC_MIC_BIAS_START)
+static const char *mic_bias_name[SPRD_CODEC_MIC_BIAS_MAX] = {
+ "Mic Bias",
+ "AuxMic Bias",
+ "HeadMic Bias",
+};
+
+enum {
+ SPRD_CODEC_HP_PA_VER_1,
+ SPRD_CODEC_HP_PA_VER_2,
+ SPRD_CODEC_HP_PA_VER_MAX,
+};
+
+static unsigned long sprd_codec_dp_base = 0;
+/* codec private data */
+struct sprd_codec_priv {
+ struct snd_soc_codec *codec;
+ int da_sample_val;
+ int ad_sample_val;
+ int ad1_sample_val;
+ struct sprd_codec_mixer mixer[SPRD_CODEC_MIXER_MAX];
+ struct sprd_codec_pga_op pga[SPRD_CODEC_PGA_MAX];
+ int mic_bias[SPRD_CODEC_MIC_BIAS_MAX];
+
+ int ap_irq;
+ struct completion completion_hp_pop;
+
+ int dp_irq;
+ struct completion completion_dac_mute;
+ struct power_supply audio_ldo;
+
+ struct sprd_codec_pa_setting inter_pa;
+ struct mutex inter_pa_mutex;
+
+ int hp_ver;
+ struct sprd_codec_hp_pa_setting inter_hp_pa;
+ int class_g_delay1;
+ int class_g_delay2;
+ struct mutex inter_hp_pa_mutex;
+ struct mutex inter_hp_pa_delay_mutex;
+
+ struct regulator *main_mic;
+ struct regulator *aux_mic;
+ struct regulator *head_mic;
+ atomic_t ldo_refcount;
+ struct delayed_work ovp_delayed_work;
+
+ struct sprd_codec_ldo_cfg s_vcom;
+ struct sprd_codec_ldo_cfg s_vpa;
+
+#define SPRD_CODEC_PA_SW_AOL (BIT(0))
+#define SPRD_CODEC_PA_SW_EAR (BIT(1))
+#define SPRD_CODEC_PA_SW_FUN (SPRD_CODEC_PA_SW_AOL | SPRD_CODEC_PA_SW_EAR)
+ int sprd_codec_fun;
+ spinlock_t sprd_codec_fun_lock;
+ spinlock_t sprd_codec_pa_sw_lock;
+
+ struct notifier_block nb;
+};
+
+static int sprd_codec_power_get(struct device *dev, struct regulator **regu,
+ const char *id)
+{
+ if (!*regu) {
+ *regu = regulator_get(dev, id);
+ if (IS_ERR(*regu)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(*regu), id);
+ *regu = 0;
+ return PTR_ERR(*regu);
+ }
+ }
+ return 0;
+}
+
+static int sprd_codec_power_put(struct regulator **regu)
+{
+ if (*regu) {
+ regulator_set_mode(*regu, REGULATOR_MODE_NORMAL);
+ regulator_disable(*regu);
+ regulator_put(*regu);
+ *regu = 0;
+ }
+ return 0;
+}
+
+static int sprd_codec_mic_bias_set(struct regulator **regu, int on)
+{
+ int ret = 0;
+ if (*regu) {
+ if (on) {
+ ret = regulator_enable(*regu);
+ if (ret) {
+ sprd_codec_power_put(regu);
+ return ret;
+ }
+ } else {
+ ret = regulator_disable(*regu);
+ }
+ }
+ return ret;
+}
+
+#ifdef CONFIG_SND_SOC_SPRD_AUDIO_DEBUG
+static const char *get_event_name(int event)
+{
+ const char *ev_name;
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ev_name = "PRE_PMU";
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ ev_name = "POST_PMU";
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ ev_name = "PRE_PMD";
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ev_name = "POST_PMD";
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+ return ev_name;
+}
+#endif
+
+static void sprd_codec_set_fun(struct sprd_codec_priv *sprd_codec, int fun)
+{
+ spin_lock(&sprd_codec->sprd_codec_fun_lock);
+ sprd_codec->sprd_codec_fun |= fun;
+ spin_unlock(&sprd_codec->sprd_codec_fun_lock);
+}
+
+static void sprd_codec_clr_fun(struct sprd_codec_priv *sprd_codec, int fun)
+{
+ spin_lock(&sprd_codec->sprd_codec_fun_lock);
+ sprd_codec->sprd_codec_fun &= ~fun;
+ spin_unlock(&sprd_codec->sprd_codec_fun_lock);
+}
+
+static int sprd_codec_test_fun(struct sprd_codec_priv *sprd_codec, int fun)
+{
+ int ret;
+ spin_lock(&sprd_codec->sprd_codec_fun_lock);
+ ret = sprd_codec->sprd_codec_fun & fun;
+ spin_unlock(&sprd_codec->sprd_codec_fun_lock);
+ return ret;
+}
+
+static void sprd_codec_wait(u32 wait_time)
+{
+ if (wait_time)
+ schedule_timeout_uninterruptible(msecs_to_jiffies(wait_time));
+}
+
+#if 0 /* sometime we will use this print function */
+static unsigned int sprd_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg);
+static void sprd_codec_print_regs(struct snd_soc_codec *codec)
+{
+ int reg;
+ pr_warn("sprd_codec register digital part\n");
+ for (reg = SPRD_CODEC_DP_BASE; reg < SPRD_CODEC_DP_END; reg += 0x10) {
+ pr_warn("0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (reg - SPRD_CODEC_DP_BASE)
+ , sprd_codec_read(codec, reg + 0x00)
+ , sprd_codec_read(codec, reg + 0x04)
+ , sprd_codec_read(codec, reg + 0x08)
+ , sprd_codec_read(codec, reg + 0x0C)
+ );
+ }
+ pr_warn("sprd_codec register analog part\n");
+ for (reg = SPRD_CODEC_AP_BASE; reg < SPRD_CODEC_AP_END; reg += 0x10) {
+ pr_warn("0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (reg - SPRD_CODEC_AP_BASE)
+ , sprd_codec_read(codec, reg + 0x00)
+ , sprd_codec_read(codec, reg + 0x04)
+ , sprd_codec_read(codec, reg + 0x08)
+ , sprd_codec_read(codec, reg + 0x0C)
+ );
+ }
+}
+#endif
+
+static inline int _sprd_codec_ldo_cfg_set(struct sprd_codec_ldo_cfg *p_v_cfg, const struct sprd_codec_ldo_v_map
+ *v_map, const int v_map_size)
+{
+ p_v_cfg->v_map = v_map;
+ p_v_cfg->v_map_size = v_map_size;
+ return 0;
+}
+
+static inline int _sprd_codec_hold(int index, int volt,
+ const struct sprd_codec_ldo_v_map *v_map)
+{
+ int scope = 40; /* unit: mv */
+ int ret = ((v_map[index].volt - volt) <= scope);
+ if (index >= 1) {
+ return (ret || ((volt - v_map[index - 1].volt) <= scope));
+ }
+ return ret;
+}
+
+static int sprd_codec_auto_ldo_volt(struct snd_soc_codec *codec,
+ void (*set_level) (struct snd_soc_codec *,
+ int),
+ const struct sprd_codec_ldo_v_map *v_map,
+ const int v_map_size, int init)
+{
+ int i;
+ int volt = sprd_get_vbat_voltage();
+ sp_asoc_pr_dbg("Get vbat voltage %d\n", volt);
+ for (i = 0; i < v_map_size; i++) {
+ if (volt <= ldo_v_map[i].volt) {
+ sp_asoc_pr_dbg("Hold %d\n",
+ _sprd_codec_hold(i, volt, v_map));
+ if (init || !_sprd_codec_hold(i, volt, v_map)) {
+ set_level(codec, v_map[i].ldo_v_level);
+ }
+ return 0;
+ }
+ }
+ return -EFAULT;
+}
+
+static inline void sprd_codec_vcm_v_sel(struct snd_soc_codec *codec, int v_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("VCM Set %d\n", v_sel);
+ mask = VCM_V_MASK << VCM_V;
+ val = (v_sel << VCM_V) & mask;
+ snd_soc_update_bits(codec, SOC_REG(PMUR4_PMUR3), mask, val);
+}
+
+static inline void sprd_codec_auxadc_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("AUXADC Set %d\n", on);
+ mask = BIT(AUXADC_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(PMUR4_PMUR3), mask, val);
+}
+
+static int sprd_codec_pga_spk_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DCGR3;
+ val = (pgaval & 0xF) << 4;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0xF0, val);
+}
+
+static int sprd_codec_pga_spkr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DCGR3;
+ val = pgaval & 0xF;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x0F, val);
+}
+
+static int sprd_codec_pga_hpl_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DCGR2_DCGR1;
+ val = (pgaval & 0xF) << 4;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0xF0, val);
+}
+
+static int sprd_codec_pga_hpr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DCGR2_DCGR1;
+ val = pgaval & 0xF;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x0F, val);
+}
+
+static int sprd_codec_pga_ear_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DCGR2_DCGR1;
+ val = ((pgaval & 0xF) << 12);
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0xF000, val);
+}
+
+static int sprd_codec_pga_adcl_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = ACGR3_ACGR2;
+ val = pgaval & 0x3F;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x3F, val);
+}
+
+static int sprd_codec_pga_adcr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = ACGR3_ACGR2;
+ val = (pgaval & 0x3F) << 8;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x3F00, val);
+}
+
+static int sprd_codec_pga_dacl_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DACGR_DACR;
+ val = (pgaval & 0x07) << 12;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x7000, val);
+}
+
+static int sprd_codec_pga_dacr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = DACGR_DACR;
+ val = (pgaval & 0x07) << 8;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x0700, val);
+}
+
+static int sprd_codec_pga_mic_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = ACGR1;
+ val = (pgaval & 0x03) << 6;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0xC0, val);
+}
+
+static int sprd_codec_pga_auxmic_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = ACGR1;
+ val = (pgaval & 0x03) << 4;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x30, val);
+}
+
+static int sprd_codec_pga_headmic_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = ACGR1;
+ val = (pgaval & 0x03) << 2;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x0C, val);
+}
+
+static int sprd_codec_pga_ailr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val;
+ reg = ACGR1;
+ val = pgaval & 0x03;
+ return snd_soc_update_bits(codec, SOC_REG(reg), 0x03, val);
+}
+
+static struct sprd_codec_pga sprd_codec_pga_cfg[SPRD_CODEC_PGA_MAX] = {
+ {sprd_codec_pga_spk_set, 0},
+ {sprd_codec_pga_spkr_set, 0},
+ {sprd_codec_pga_hpl_set, 0},
+ {sprd_codec_pga_hpr_set, 0},
+ {sprd_codec_pga_ear_set, 0},
+
+ {sprd_codec_pga_adcl_set, 0},
+ {sprd_codec_pga_adcr_set, 0},
+
+ {sprd_codec_pga_dacl_set, 0},
+ {sprd_codec_pga_dacr_set, 0},
+ {sprd_codec_pga_mic_set, 0},
+ {sprd_codec_pga_auxmic_set, 0},
+ {sprd_codec_pga_headmic_set, 0},
+ {sprd_codec_pga_ailr_set, 0},
+ {sprd_codec_pga_ailr_set, 0},
+};
+
+/* adc mixer */
+
+static int ailadcl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(AIL_ADCL),
+ on << AIL_ADCL);
+}
+
+static int ailadcr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(AIL_ADCR),
+ on << AIL_ADCR);
+}
+
+static int airadcl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(AIR_ADCL),
+ on << AIR_ADCL);
+}
+
+static int airadcr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(AIR_ADCR),
+ on << AIR_ADCR);
+}
+
+static int mainmicadcl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(MIC_ADCL),
+ on << MIC_ADCL);
+}
+
+static int mainmicadcr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1), BIT(MIC_ADCR),
+ on << MIC_ADCR);
+}
+
+static int auxmicadcl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1),
+ BIT(AUXMIC_ADCL), on << AUXMIC_ADCL);
+}
+
+static int auxmicadcr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1),
+ BIT(AUXMIC_ADCR), on << AUXMIC_ADCR);
+}
+
+static int hpmicadcl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1),
+ BIT(HEADMIC_ADCL), on << HEADMIC_ADCL);
+}
+
+static int hpmicadcr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(AAICR2_AAICR1),
+ BIT(HEADMIC_ADCR), on << HEADMIC_ADCR);
+}
+
+/* hp mixer */
+
+static int daclhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1),
+ BIT(DACL_P_HPL), on << DACL_P_HPL);
+}
+
+static int daclhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1),
+ BIT(DACL_N_HPR), on << DACL_N_HPR);
+}
+
+static int dacrhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1),
+ BIT(DACR_P_HPL), on << DACR_P_HPL);
+}
+
+static int dacrhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1),
+ BIT(DACR_P_HPR), on << DACR_P_HPR);
+}
+
+static int adclhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1),
+ BIT(ADCL_P_HPL), on << ADCL_P_HPL);
+}
+
+static int adclhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1),
+ BIT(ADCL_N_HPR), on << ADCL_N_HPR);
+}
+
+static int adcrhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1),
+ BIT(ADCR_P_HPL), on << ADCR_P_HPL);
+}
+
+static int adcrhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1),
+ BIT(ADCR_P_HPR), on << ADCR_P_HPR);
+}
+
+/* spkl mixer */
+
+static int daclspkl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(DACL_AOL),
+ on << DACL_AOL);
+}
+
+static int dacrspkl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(DACR_AOL),
+ on << DACR_AOL);
+}
+
+static int adclspkl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(ADCL_AOL),
+ on << ADCL_AOL);
+}
+
+static int adcrspkl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(ADCR_AOL),
+ on << ADCR_AOL);
+}
+
+/* spkr mixer */
+
+static int daclspkr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(DACL_AOR),
+ on << DACL_AOR);
+}
+
+static int dacrspkr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(DACR_AOR),
+ on << DACR_AOR);
+}
+
+static int adclspkr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(ADCL_AOR),
+ on << ADCL_AOR);
+}
+
+static int adcrspkr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR2), BIT(ADCR_AOR),
+ on << ADCR_AOR);
+}
+
+/* ear mixer */
+
+static int daclear_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(DAOCR3_DAOCR1), BIT(DACL_EAR),
+ on << DACL_EAR);
+}
+
+static sprd_codec_mixer_set mixer_setting[SPRD_CODEC_MIXER_MAX] = {
+ /* adc mixer */
+ ailadcl_set, ailadcr_set,
+ airadcl_set, airadcr_set,
+ mainmicadcl_set, mainmicadcr_set,
+ auxmicadcl_set, auxmicadcr_set,
+ hpmicadcl_set, hpmicadcr_set,
+ /* hp mixer */
+ daclhpl_set, daclhpr_set,
+ dacrhpl_set, dacrhpr_set,
+ adclhpl_set, adclhpr_set,
+ adcrhpl_set, adcrhpr_set,
+ /* spk mixer */
+ daclspkl_set, daclspkr_set,
+ dacrspkl_set, dacrspkr_set,
+ adclspkl_set, adclspkr_set,
+ adcrspkl_set, adcrspkr_set,
+ /* ear mixer */
+ daclear_set, 0,
+};
+
+/* DO NOT USE THIS FUNCTION */
+static inline void __sprd_codec_pa_sw_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ mask = BIT(PA_SW_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(PMUR2_PMUR1), mask, val);
+}
+
+static inline void sprd_codec_pa_sw_set(struct snd_soc_codec *codec, int fun)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ sp_asoc_pr_dbg("%s fun 0x%08x\n", __func__, fun);
+ spin_lock(&sprd_codec->sprd_codec_pa_sw_lock);
+ sprd_codec_set_fun(sprd_codec, fun);
+ __sprd_codec_pa_sw_en(codec, 1);
+ spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock);
+}
+
+static inline void sprd_codec_pa_sw_clr(struct snd_soc_codec *codec, int fun)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ sp_asoc_pr_dbg("%s fun 0x%08x\n", __func__, fun);
+ spin_lock(&sprd_codec->sprd_codec_pa_sw_lock);
+ sprd_codec_clr_fun(sprd_codec, fun);
+ if (!sprd_codec_test_fun(sprd_codec, SPRD_CODEC_PA_SW_FUN))
+ __sprd_codec_pa_sw_en(codec, 0);
+ spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock);
+}
+
+/* inter PA */
+
+static inline void sprd_codec_pa_d_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ mask = BIT(PA_D_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), mask, val);
+}
+
+static inline void sprd_codec_pa_demi_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ if (on)
+ /*when emi is on, need to set this register from xun && junjie.chen*/
+ snd_soc_update_bits(codec, SOC_REG(DCR4_DCR3), 0xffff, 0x4);
+ else
+ snd_soc_update_bits(codec, SOC_REG(DCR4_DCR3), 0xffff, 0);
+
+ mask = BIT(PA_DEMI_EN);
+ snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), mask, on?mask:0);
+
+ /*open ocp from xun && weifeng*/
+ mask = BIT(DRV_OCP_AOL_PD) | BIT(DRV_OCP_AOR_PD);
+ snd_soc_update_bits(codec, SOC_REG(DCR4_DCR3), mask, 0);
+}
+
+static inline void sprd_codec_pa_ldo_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ mask = BIT(PA_LDO_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(PMUR2_PMUR1), mask, val);
+ if (on) {
+ sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL);
+ }
+}
+
+static inline void sprd_codec_pa_ldo_v_sel(struct snd_soc_codec *codec,
+ int v_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, v_sel);
+ mask = PA_LDO_V_MASK << PA_LDO_V;
+ val = (v_sel << PA_LDO_V) & mask;
+ snd_soc_update_bits(codec, SOC_REG(PMUR6_PMUR5), mask, val);
+}
+
+static inline void sprd_codec_pa_ovp_v_sel(struct snd_soc_codec *codec,
+ int v_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, v_sel);
+ mask = PA_OVP_V_MASK << PA_OVP_V;
+ val = (v_sel << PA_OVP_V) & mask;
+ snd_soc_update_bits(codec, SOC_REG(PMUR8_PMUR7), mask, val);
+}
+
+static inline void sprd_codec_pa_dtri_f_sel(struct snd_soc_codec *codec,
+ int f_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, f_sel);
+ mask = PA_DTRI_F_MASK << PA_DTRI_F;
+ val = (f_sel << PA_DTRI_F) & mask;
+ snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), mask, val);
+}
+
+static inline void sprd_codec_pa_en(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ spin_lock(&sprd_codec->sprd_codec_pa_sw_lock);
+ if (on) {
+ mask = BIT(PA_EN);
+ val = mask;
+ } else {
+ if (!sprd_codec_test_fun(sprd_codec, SPRD_CODEC_PA_SW_FUN))
+ mask = BIT(PA_EN) | BIT(PA_SW_EN) | BIT(PA_LDO_EN);
+ else
+ mask = BIT(PA_EN) | BIT(PA_LDO_EN);
+ val = 0;
+ }
+ snd_soc_update_bits(codec, SOC_REG(PMUR2_PMUR1), mask, val);
+ spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock);
+}
+
+static inline void sprd_codec_ovp_ldo_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ mask = BIT(OVP_LDO_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(PMUR2_PMUR1), mask, val);
+}
+
+static inline void sprd_codec_inter_pa_init(struct sprd_codec_priv *sprd_codec)
+{
+ sprd_codec->inter_pa.setting.LDO_V_sel = 0x03;
+ sprd_codec->inter_pa.setting.DTRI_F_sel = 0x01;
+}
+
+static void sprd_codec_ovp_irq_enable(struct snd_soc_codec *codec)
+{
+ int mask = BIT(OVP_IRQ);
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_CLR), mask, mask);
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), mask, mask);
+}
+
+static void sprd_codec_ovp_start(struct sprd_codec_priv *sprd_codec, int ms)
+{
+ schedule_delayed_work(&sprd_codec->ovp_delayed_work,
+ msecs_to_jiffies(ms));
+}
+
+static int sprd_ovp_irq(struct snd_soc_codec *codec, int is_ovp)
+{
+ int volt = sprd_get_vbat_voltage();
+ sp_asoc_pr_dbg("OVP %d curr_volt %d\n", is_ovp, volt);
+ if (is_ovp) {
+ sprd_codec_pa_en(codec, 0);
+ snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(AOL_EN), 0);
+ } else {
+ snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(AOL_EN),
+ BIT(AOL_EN));
+ sprd_codec_pa_en(codec, 1);
+ }
+ return 0;
+}
+
+#define to_sprd_codec(x) container_of((x), struct sprd_codec_priv, ovp_delayed_work)
+#define to_delay_worker(x) container_of((x), struct delayed_work, work)
+static void sprd_codec_ovp_delay_worker(struct work_struct *work)
+{
+ struct sprd_codec_priv *sprd_codec =
+ to_sprd_codec(to_delay_worker(work));
+ struct snd_soc_codec *codec = sprd_codec->codec;
+
+ if (!(snd_soc_read(codec, IFR2_IFR1) & BIT(OVP_FLAG))) {
+ } else {
+ sprd_codec_pa_ldo_v_sel(codec, 0x7);
+ }
+}
+
+static inline int sprd_codec_pa_ldo_auto(struct snd_soc_codec *codec, int init)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ return sprd_codec_auto_ldo_volt
+ (codec, sprd_codec_pa_ldo_v_sel, sprd_codec->s_vpa.v_map,
+ sprd_codec->s_vpa.v_map_size, init);
+}
+
+static inline int sprd_codec_pa_ldo_cfg(struct sprd_codec_priv *sprd_codec, const struct sprd_codec_ldo_v_map
+ *v_map, const int v_map_size)
+{
+ return _sprd_codec_ldo_cfg_set(&sprd_codec->s_vpa, v_map, v_map_size);
+}
+
+static int sprd_inter_speaker_pa(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ struct sprd_codec_inter_pa *p_setting = &sprd_codec->inter_pa.setting;
+ sp_asoc_pr_info("inter PA Switch %s\n", STR_ON_OFF(on));
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+ if (on) {
+ if (sprd_codec->hp_ver != SPRD_CODEC_HP_PA_VER_2)
+ sprd_codec_pa_ovp_v_sel(codec, PA_OVP_435);
+ else
+ sprd_codec_pa_ovp_v_sel(codec, PA_OVP_435);
+ sprd_codec_pa_ldo_v_sel(codec, 0x7);
+ sprd_codec_ovp_ldo_en(codec,1);
+ sprd_codec_ovp_irq_enable(codec);
+ sprd_codec_pa_d_en(codec, p_setting->is_classD_mode);
+ sprd_codec_pa_demi_en(codec, p_setting->is_DEMI_mode);
+ sprd_codec_pa_ldo_en(codec, p_setting->is_LDO_mode);
+ if (p_setting->is_LDO_mode) {
+ if (p_setting->is_auto_LDO_mode) {
+ sprd_codec_pa_ldo_auto(codec, 1);
+ } else {
+ sprd_codec_pa_ldo_v_sel(codec,
+ p_setting->LDO_V_sel);
+ }
+ } else {
+ sprd_codec_pa_sw_set(codec, SPRD_CODEC_PA_SW_AOL);
+ }
+ sprd_codec_pa_dtri_f_sel(codec, p_setting->DTRI_F_sel);
+ sprd_codec_pa_en(codec, 1);
+ sprd_codec->inter_pa.set = 1;
+ } else {
+ sprd_codec->inter_pa.set = 0;
+ sprd_codec_pa_en(codec, 0);
+ sprd_codec_wait(2);
+ sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL);
+ sprd_codec_pa_ldo_en(codec, 0);
+ }
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ return 0;
+}
+
+static int spk_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+ return sprd_inter_speaker_pa(w->codec, on);
+}
+
+#define SPRD_CODEC_HP_VER(bit) \
+do { \
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); \
+ switch(sprd_codec->hp_ver) { \
+ case SPRD_CODEC_HP_PA_VER_2: \
+ mask = BIT(V2_##bit); \
+ break; \
+ case SPRD_CODEC_HP_PA_VER_1: \
+ default: \
+ mask = BIT(bit); \
+ break; \
+ } \
+} while(0)
+
+static inline void sprd_codec_hp_classg_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ SPRD_CODEC_HP_VER(AUDIO_CLASSG_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_lpw(struct snd_soc_codec *codec, int lpw)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, lpw);
+ switch (sprd_codec->hp_ver) {
+ case SPRD_CODEC_HP_PA_VER_2:
+ if (sci_get_ana_chip_ver() >= SC2711BA) {
+ mask = V2_AUDIO_CHP_LPW_MASK_BA << V2_AUDIO_CHP_LPW_BA;
+ val = (lpw << V2_AUDIO_CHP_LPW_BA) & mask;
+ } else {
+ mask = V2_AUDIO_CHP_LPW_MASK << V2_AUDIO_CHP_LPW;
+ val = (lpw << V2_AUDIO_CHP_LPW) & mask;
+ }
+ break;
+ case SPRD_CODEC_HP_PA_VER_1:
+ default:
+ if (sci_get_ana_chip_ver() >= SC2713CA) {
+ mask = AUDIO_CHP_LPW_MASK_CA << AUDIO_CHP_LPW_CA;
+ val = (lpw << AUDIO_CHP_LPW_CA) & mask;
+ } else {
+ mask = AUDIO_CHP_LPW_MASK << AUDIO_CHP_LPW;
+ val = (lpw << AUDIO_CHP_LPW) & mask;
+ }
+ break;
+ }
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_mode(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ switch (sprd_codec->hp_ver) {
+ case SPRD_CODEC_HP_PA_VER_2:
+ if (sci_get_ana_chip_ver() >= SC2711BA)
+ mask = BIT(V2_AUDIO_CHP_MODE_BA );
+ else
+ mask = BIT(V2_AUDIO_CHP_MODE);
+ break;
+ case SPRD_CODEC_HP_PA_VER_1:
+ default:
+ if (sci_get_ana_chip_ver() >= SC2713CA)
+ mask = BIT(AUDIO_CHP_MODE_CA );
+ else
+ mask = BIT(AUDIO_CHP_MODE );
+ break;
+ }
+ val = on? mask :0;
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_osc(struct snd_soc_codec *codec, int osc)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, osc);
+ switch (sprd_codec->hp_ver) {
+ case SPRD_CODEC_HP_PA_VER_2:
+ mask = V2_AUDIO_CHP_OSC_MASK << V2_AUDIO_CHP_OSC;
+ val = (osc << V2_AUDIO_CHP_OSC) & mask;
+ break;
+ case SPRD_CODEC_HP_PA_VER_1:
+ default:
+ mask = AUDIO_CHP_OSC_MASK << AUDIO_CHP_OSC;
+ val = (osc << AUDIO_CHP_OSC) & mask;
+ break;
+ }
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ SPRD_CODEC_HP_VER(AUDIO_CHP_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_hpl_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ SPRD_CODEC_HP_VER(AUDIO_CHP_HPL_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_hpr_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ SPRD_CODEC_HP_VER(AUDIO_CHP_HPR_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_hpl_mute(struct snd_soc_codec *codec,
+ int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ SPRD_CODEC_HP_VER(AUDIO_CHP_LMUTE);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_hpr_mute(struct snd_soc_codec *codec,
+ int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ SPRD_CODEC_HP_VER(AUDIO_CHP_RMUTE);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_cgcal_en(struct snd_soc_codec *codec,
+ int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ SPRD_CODEC_HP_VER(AUDIO_CGCAL_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+}
+
+static inline void sprd_codec_inter_hp_pa_init(struct sprd_codec_priv
+ *sprd_codec)
+{
+ sprd_codec->inter_hp_pa.setting.class_g_osc = 0x01;
+ sprd_codec->inter_hp_pa.setting.class_g_cgcal = 1;
+ sprd_codec->inter_hp_pa.setting.class_g_cgcal_delay_10ms = 3;
+ sprd_codec->inter_hp_pa.setting.class_g_unmute_delay_100ms = 5;
+ sprd_codec->inter_hp_pa.setting.class_g_close_delay_10ms = 0;
+ sprd_codec->inter_hp_pa.setting.class_g_open_delay_10ms = 0;
+ sprd_codec->inter_hp_pa.setting.class_g_vdd_delay_10ms = 0;
+ sprd_codec->inter_hp_pa.setting.class_g_chp_delay_30ms = 0;
+ sprd_codec->inter_hp_pa.setting.class_g_all_close_delay_100ms = 0;
+}
+
+
+static void sprd_inter_headphone_pa_pre(struct sprd_codec_priv *sprd_codec,int on)
+{
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ static struct regulator *regulator = 0;
+ struct sprd_codec_inter_hp_pa *p_setting =
+ &sprd_codec->inter_hp_pa.setting;
+
+ static struct regulator *regulator_reg= 0;
+
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ static struct regulator *regulator_vb= 0;
+ int classg_vol = 2200000;
+#endif
+
+ if (on) {
+ if (!regulator) {
+ sp_asoc_pr_dbg("%s set vddclsg on\n", __func__);
+ /*1.CG_EN */
+ //open clk
+ regulator_reg = regulator_get(0, "VREG");
+ if (IS_ERR(regulator_reg)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(regulator_reg), "VREG");
+ BUG_ON(1);
+ }
+ regulator_set_mode(regulator_reg, REGULATOR_MODE_STANDBY);
+ if (regulator_enable(regulator_reg) < 0) {
+ } else {
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ //open vb
+ regulator_vb = regulator_get(0, "VB");
+ if (IS_ERR(regulator_vb)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(regulator_vb), "VB");
+ BUG_ON(1);
+ }
+ regulator_set_mode(regulator_vb, REGULATOR_MODE_STANDBY);
+ if (regulator_enable(regulator_vb) < 0) {
+ } else {
+#endif
+ sprd_codec_hp_classg_en(codec, 1);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ }
+#endif
+ }
+ //close clk
+ regulator_set_mode(regulator_reg,
+ REGULATOR_MODE_NORMAL);
+ regulator_disable(regulator_reg);
+ regulator_put(regulator_reg);
+ regulator_reg = 0;
+ sprd_codec_wait(p_setting->class_g_open_delay_10ms * 10);
+ /*2. LDO PD */
+ regulator = regulator_get(0, CLASS_G_LDO_ID);
+ if (IS_ERR(regulator)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(regulator), CLASS_G_LDO_ID);
+ BUG_ON(1);
+ }
+ regulator_set_mode(regulator, REGULATOR_MODE_STANDBY);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ if (sprd_codec->hp_ver == SPRD_CODEC_HP_PA_VER_1)
+ classg_vol = 1200000;
+ regulator_set_voltage(regulator, classg_vol, classg_vol);
+#endif
+ if (regulator_enable(regulator) < 0) {
+ regulator_set_mode(regulator,
+ REGULATOR_MODE_NORMAL);
+ regulator_disable(regulator);
+ regulator_put(regulator);
+ regulator = 0;
+ }
+ }
+ } else{
+ if (regulator) {
+ sp_asoc_pr_dbg("%s set vddclsg off\n", __func__);
+ /*LDO PD*/
+ regulator_set_mode(regulator, REGULATOR_MODE_NORMAL);
+ regulator_disable(regulator);
+ regulator_put(regulator);
+ regulator = 0;
+ /*CG_EN*/
+ //open clk
+ regulator_reg = regulator_get(0, "VREG");
+ if (IS_ERR(regulator_reg)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(regulator_reg), "VREG");
+ BUG_ON(1);
+ }
+ regulator_set_mode(regulator_reg, REGULATOR_MODE_STANDBY);
+ if (regulator_enable(regulator_reg) < 0) {
+ } else {
+ sprd_codec_hp_classg_en(codec, 0);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ if(regulator_vb) {
+ //close vb
+ regulator_set_mode(regulator_vb, REGULATOR_MODE_NORMAL);
+ regulator_disable(regulator_vb);
+ regulator_put(regulator_vb);
+ regulator_vb = 0;
+ }
+#endif
+ }
+ //close clk
+ regulator_set_mode(regulator_reg,
+ REGULATOR_MODE_NORMAL);
+ regulator_disable(regulator_reg);
+ regulator_put(regulator_reg);
+ regulator_reg = 0;
+ }
+ }
+}
+static int hp_notifier_handler (struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct sprd_codec_priv *sprd_codec = container_of (nb, struct sprd_codec_priv, nb);
+ sprd_inter_headphone_pa_pre (sprd_codec, !!action);
+ return 0;
+}
+
+static int sprd_inter_headphone_pa(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ struct sprd_codec_inter_hp_pa *p_setting =
+ &sprd_codec->inter_hp_pa.setting;
+ /*static struct regulator *regulator = 0;*/
+ sp_asoc_pr_info("inter HP PA Switch %s\n", STR_ON_OFF(on));
+ mutex_lock(&sprd_codec->inter_hp_pa_mutex);
+ if (on) {
+ /*mute */
+ sprd_codec_hp_pa_hpl_mute(codec, 1);
+ sprd_codec_hp_pa_hpr_mute(codec, 1);
+ sprd_codec_auxadc_en(codec, 1);
+ sprd_codec_hp_pa_lpw(codec, p_setting->class_g_low_power);
+ sprd_codec_hp_pa_mode(codec, p_setting->class_g_mode);
+ sprd_codec_hp_pa_osc(codec, p_setting->class_g_osc);
+ sprd_codec_hp_pa_cgcal_en(codec, p_setting->class_g_cgcal);
+ sprd_codec_wait(sprd_codec->class_g_delay1 * 30);
+ /*3. CHP EN */
+ sprd_codec_hp_pa_en(codec, 1);
+ sprd_codec_wait(p_setting->class_g_chp_delay_30ms * 30);
+ /*4. L/R EN */
+ sprd_codec_hp_pa_hpl_en(codec, 1);
+ sprd_codec_hp_pa_hpr_en(codec, 1);
+ sprd_codec_wait(sprd_codec->class_g_delay2* 30);
+ if (p_setting->class_g_cgcal) {
+ sprd_codec_wait(p_setting->class_g_cgcal_delay_10ms *10);
+ }
+ } else {
+ sprd_codec_wait(p_setting->class_g_close_delay_10ms * 10);
+ sprd_codec_auxadc_en(codec, 0);
+ /*1. L/R disable */
+ sprd_codec_hp_pa_hpl_en(codec, 0);
+ sprd_codec_hp_pa_hpr_en(codec, 0);
+ /*2. CHP disable */
+ sprd_codec_hp_pa_en(codec, 0);
+ /*3. unmute */
+ sprd_codec_hp_pa_hpl_mute(codec, 0);
+ sprd_codec_hp_pa_hpr_mute(codec, 0);
+ sprd_codec_wait(p_setting->class_g_all_close_delay_100ms* 100);
+ }
+ mutex_unlock(&sprd_codec->inter_hp_pa_mutex);
+ return 0;
+}
+
+static int sprd_inter_headphone_pa_post(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ struct sprd_codec_inter_hp_pa *p_setting =
+ &sprd_codec->inter_hp_pa.setting;
+ mutex_lock(&sprd_codec->inter_hp_pa_mutex);
+ if (on) {
+ /*open classG mute delay time */
+ sprd_codec_wait(p_setting->class_g_unmute_delay_100ms * 100);
+ /*unmute */
+ sprd_codec_hp_pa_hpl_mute(codec, 0);
+ sprd_codec_hp_pa_hpr_mute(codec, 0);
+
+ sprd_codec->inter_hp_pa.set = 1;
+ } else {
+ sprd_codec->inter_hp_pa.set = 0;
+ /*mute */
+ sprd_codec_hp_pa_hpl_mute(codec, 1);
+ sprd_codec_hp_pa_hpr_mute(codec, 1);
+ sprd_codec_wait(10);
+ }
+ mutex_unlock(&sprd_codec->inter_hp_pa_mutex);
+ return 0;
+}
+
+#ifdef CONFIG_SND_SOC_SPRD_AUDIO_USE_INTER_HP_PA
+static int hp_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+ return sprd_inter_headphone_pa(w->codec, on);
+}
+
+static int hp_pa_post_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+ return sprd_inter_headphone_pa_post(w->codec, on);
+}
+#else
+static int hp_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return 0;
+}
+
+static int hp_pa_post_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return 0;
+}
+#endif
+
+static int sprd_codec_set_sample_rate(struct snd_soc_codec *codec, int rate,
+ int mask, int shift)
+{
+ switch (rate) {
+ case 8000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_8000 << shift);
+ break;
+ case 11025:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_11025 << shift);
+ break;
+ case 16000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_16000 << shift);
+ break;
+ case 22050:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_22050 << shift);
+ break;
+ case 32000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_32000 << shift);
+ break;
+ case 44100:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_44100 << shift);
+ break;
+ case 48000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_48000 << shift);
+ break;
+ case 96000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_96000 << shift);
+ break;
+ default:
+ pr_err("ERR:SPRD-CODEC not support this rate %d\n", rate);
+ break;
+ }
+ sp_asoc_pr_dbg("Set Playback rate 0x%x\n",
+ snd_soc_read(codec, AUD_DAC_CTL));
+ return 0;
+}
+
+static int sprd_codec_set_ad_sample_rate(struct snd_soc_codec *codec, int rate,
+ int mask, int shift)
+{
+ int set;
+#ifdef CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE
+ if (rate == 44100)
+ rate = CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE;
+#endif
+ set = rate / 4000;
+ if (set > 13) {
+ pr_err("ERR:SPRD-CODEC not support this ad rate %d\n", rate);
+ }
+ snd_soc_update_bits(codec, SOC_REG(AUD_ADC_CTL), mask, set << shift);
+ return 0;
+}
+
+static int sprd_codec_sample_rate_setting(struct sprd_codec_priv *sprd_codec)
+{
+ sp_asoc_pr_dbg("%s AD %d DA %d AD1 %d\n", __func__,
+ sprd_codec->ad_sample_val, sprd_codec->da_sample_val,
+ sprd_codec->ad1_sample_val);
+ if (sprd_codec->ad_sample_val) {
+ sprd_codec_set_ad_sample_rate(sprd_codec->codec,
+ sprd_codec->ad_sample_val, 0x0F,
+ 0);
+ }
+ if (sprd_codec->ad1_sample_val) {
+ sprd_codec_set_ad_sample_rate(sprd_codec->codec,
+ sprd_codec->ad1_sample_val, 0xF0,
+ 4);
+ }
+ if (sprd_codec->da_sample_val) {
+ sprd_codec_set_sample_rate(sprd_codec->codec,
+ sprd_codec->da_sample_val, 0x0F, 0);
+ }
+ return 0;
+}
+
+static inline int sprd_codec_vcom_ldo_auto(struct snd_soc_codec *codec,
+ int init)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ return sprd_codec_auto_ldo_volt(codec, sprd_codec_vcm_v_sel,
+ sprd_codec->s_vcom.v_map,
+ sprd_codec->s_vcom.v_map_size, init);
+}
+
+static inline int sprd_codec_vcom_ldo_cfg(struct sprd_codec_priv *sprd_codec, const struct sprd_codec_ldo_v_map
+ *v_map, const int v_map_size)
+{
+ return _sprd_codec_ldo_cfg_set(&sprd_codec->s_vcom, v_map, v_map_size);
+}
+
+static DEFINE_MUTEX(ldo_mutex);
+static int sprd_codec_ldo_on(struct sprd_codec_priv *sprd_codec)
+{
+ struct regulator **regu = &sprd_codec->head_mic;
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ mutex_lock(&ldo_mutex);
+ atomic_inc(&sprd_codec->ldo_refcount);
+ if (atomic_read(&sprd_codec->ldo_refcount) == 1) {
+ sp_asoc_pr_dbg("LDO ON!\n");
+ if (*regu) {
+ regulator_set_mode(*regu, REGULATOR_MODE_NORMAL);
+ }
+ arch_audio_codec_analog_enable();
+ sprd_codec_vcom_ldo_auto(sprd_codec->codec, 1);
+ }
+ mutex_unlock(&ldo_mutex);
+
+ return 0;
+}
+
+static int sprd_codec_ldo_off(struct sprd_codec_priv *sprd_codec)
+{
+ struct regulator **regu = &sprd_codec->head_mic;
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ mutex_lock(&ldo_mutex);
+ if (atomic_dec_and_test(&sprd_codec->ldo_refcount)) {
+ if (*regu && regulator_is_enabled(*regu)) {
+ regulator_set_mode(*regu, REGULATOR_MODE_STANDBY);
+ }
+ arch_audio_codec_analog_disable();
+ sp_asoc_pr_dbg("LDO OFF!\n");
+ }
+ mutex_unlock(&ldo_mutex);
+
+ return 0;
+}
+
+static int sprd_codec_analog_open(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+ sprd_codec_sample_rate_setting(sprd_codec);
+
+ /* SC7710/SC8830 ask from ASIC to set initial value */
+ snd_soc_update_bits(codec, SOC_REG(PMUR4_PMUR3), BIT(SEL_VCMI),
+ BIT(SEL_VCMI));
+ snd_soc_update_bits(codec, SOC_REG(PMUR4_PMUR3), BIT(VCMI_FAST_EN),
+ BIT(VCMI_FAST_EN));
+ snd_soc_write(codec, AUD_SDM_CTL0, 0x400);
+
+ return ret;
+}
+
+static int sprd_codec_digital_open(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ /* FIXME : Some Clock SYNC bug will cause MUTE */
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), BIT(DAC_MUTE_EN), 0);
+
+ sprd_codec_sample_rate_setting(sprd_codec);
+
+ return ret;
+}
+
+static void sprd_codec_power_enable(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ ret = sprd_codec_ldo_on(sprd_codec);
+ if (ret != 0)
+ pr_err("ERR:SPRD-CODEC open ldo error %d\n", ret);
+
+ sprd_codec_analog_open(codec);
+}
+
+static void sprd_codec_power_disable(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ sprd_codec_ldo_off(sprd_codec);
+}
+
+static int digital_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ arch_audio_codec_digital_reg_enable();
+ arch_audio_codec_digital_enable();
+ arch_audio_codec_digital_reset();
+ sprd_codec_digital_open(w->codec);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*maybe ADC module use it, so we cann't close it */
+ /*arch_audio_codec_digital_disable(); */
+ arch_audio_codec_digital_reg_disable();
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int analog_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ sprd_codec_power_enable(codec);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ sprd_codec_power_disable(codec);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int chan_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int chan_id = FUN_REG(w->reg);
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+
+ sp_asoc_pr_info("%s %s\n", sprd_codec_chan_get_name(chan_id),
+ STR_ON_OFF(on));
+
+ return 0;
+}
+
+static int dfm_out_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+
+ sp_asoc_pr_info("DFM-OUT %s\n", STR_ON_OFF(on));
+ sprd_codec->ad_sample_val = sprd_codec->da_sample_val;
+ sprd_codec_sample_rate_setting(sprd_codec);
+
+ return 0;
+}
+
+static int _mixer_set_mixer(struct snd_soc_codec *codec, int id, int lr,
+ int try_on, int need_set)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int reg = GET_MIXER_ID(ID_FUN(id, lr));
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[reg]);
+ if (try_on) {
+ mixer->set = mixer_setting[reg];
+ /* NOTES: reduce linein pop noise must open ADCL/R ->HP MIXER
+ AFTER delay 250ms for both ADCL/ADCR switch complete. */
+ if (need_set)
+ return mixer->set(codec, mixer->on);
+ } else {
+ mixer_setting[reg] (codec, 0);
+ mixer->set = 0;
+ }
+ return 0;
+}
+
+static inline int _mixer_setting(struct snd_soc_codec *codec, int start,
+ int end, int lr, int try_on, int need_set)
+{
+ int id;
+ for (id = start; id < end; id++) {
+ _mixer_set_mixer(codec, id, lr, try_on, need_set);
+ }
+ return 0;
+}
+
+static inline int _mixer_setting_one(struct snd_soc_codec *codec, int id,
+ int try_on, int need_set)
+{
+ int lr = id & 0x1;
+ id = (id >>1) + SPRD_CODEC_MIXER_START;
+ return _mixer_setting(codec, id, id + 1, lr, try_on, need_set);
+}
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+static void sprd_codec_hp_pop_irq_enable(struct snd_soc_codec *codec)
+{
+ int mask = BIT(AUDIO_POP_IRQ);
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_CLR), mask, mask);
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), mask, mask);
+}
+#endif
+
+static irqreturn_t sprd_codec_ap_irq(int irq, void *dev_id)
+{
+ int mask;
+ struct sprd_codec_priv *sprd_codec = dev_id;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ mask = snd_soc_read(codec, AUDIF_INT_MASK);
+ sp_asoc_pr_dbg("HP POP IRQ Mask = 0x%x\n", mask);
+ if (BIT(AUDIO_POP_IRQ) & mask) {
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), BIT(AUDIO_POP_IRQ), 0);
+ complete(&sprd_codec->completion_hp_pop);
+ }
+ if (BIT(OVP_IRQ) & mask) {
+ snd_soc_update_bits(codec, SOC_REG(AUDIF_INT_EN), BIT(OVP_IRQ), 0);
+ sprd_codec_ovp_start(sprd_codec, 1);
+ sprd_codec_ovp_irq_enable(codec);
+ }
+ return IRQ_HANDLED;
+}
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+static inline int is_hp_pop_compelet(struct snd_soc_codec *codec)
+{
+ int val;
+ val = snd_soc_read(codec, IFR2_IFR1);
+ val = (val >> HP_POP_FLG) & HP_POP_FLG_MASK;
+ sp_asoc_pr_dbg("HP POP= 0x%x\n", val);
+ return HP_POP_FLG_NEAR_CMP == val;
+}
+
+static inline int hp_pop_wait_for_compelet(struct snd_soc_codec *codec)
+{
+ int i;
+ int hp_pop_complete;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ hp_pop_complete = msecs_to_jiffies(SPRD_CODEC_HP_POP_TIMEOUT);
+ for (i = 0; i < 2; i++) {
+ sp_asoc_pr_dbg("HP POP %d IRQ Enable\n", i);
+ sprd_codec_hp_pop_irq_enable(codec);
+ init_completion(&sprd_codec->completion_hp_pop);
+ hp_pop_complete =
+ wait_for_completion_timeout(&sprd_codec->completion_hp_pop,
+ hp_pop_complete);
+ sp_asoc_pr_dbg("HP POP %d Completion %d\n", i, hp_pop_complete);
+ if (!hp_pop_complete) {
+ if (!is_hp_pop_compelet(codec)) {
+ pr_err("ERR:HP POP %d timeout not complete\n",
+ i);
+ } else {
+ pr_err("ERR:HP POP %d timeout but complete\n",
+ i);
+ }
+ } else {
+ /* 01 change to 10 maybe walk to 11 shortly,
+ so, check it double. */
+ sprd_codec_wait(2);
+ if (is_hp_pop_compelet(codec)) {
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+static int hp_pop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int mask;
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mask = HP_POP_STEP_MASK << HP_POP_STEP;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask,
+ HP_POP_STEP_2 << HP_POP_STEP);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask,
+ HP_POP_CTL_UP << HP_POP_CTL);
+ sp_asoc_pr_dbg("U PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR2_PNRCR1));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ mask = HP_POP_STEP_MASK << HP_POP_STEP;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask,
+ HP_POP_STEP_1 << HP_POP_STEP);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask,
+ HP_POP_CTL_DOWN << HP_POP_CTL);
+ sp_asoc_pr_dbg("D PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR2_PNRCR1));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ ret = hp_pop_wait_for_compelet(codec);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask,
+ HP_POP_CTL_HOLD << HP_POP_CTL);
+ sp_asoc_pr_dbg("HOLD PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR2_PNRCR1));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = hp_pop_wait_for_compelet(codec);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask,
+ HP_POP_CTL_DIS << HP_POP_CTL);
+ sp_asoc_pr_dbg("DIS PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR2_PNRCR1));
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+#else
+#ifndef CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME
+#define CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME (0)
+#endif
+static int hp_pop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+ sp_asoc_pr_dbg("%s Event is %s wait %dms\n", __func__,
+ get_event_name(event),
+ CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ sprd_codec_wait(CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+ return ret;
+}
+#endif
+
+static int hp_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ int mask;
+#endif
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+#if 1 /* enable the diff function in shark from weifeng.ni */
+ snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(DIFF_EN),
+ BIT(DIFF_EN));
+ /*enable from */
+ snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(HP_VCMI_EN),
+ BIT(HP_VCMI_EN));
+
+#endif
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask,
+ HP_POP_CTL_DIS << HP_POP_CTL);
+ sp_asoc_pr_dbg("DIS(en) PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR2_PNRCR1));
+#endif
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(DIFF_EN), 0);
+ snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(HP_VCMI_EN), 0);
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(PNRCR2_PNRCR1), mask,
+ HP_POP_CTL_HOLD << HP_POP_CTL);
+ sp_asoc_pr_dbg("HOLD(en) PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, PNRCR2_PNRCR1));
+#endif
+ goto _pre_pmd;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* do nothing now */
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ _mixer_setting(codec, SPRD_CODEC_HP_DACL,
+ SPRD_CODEC_HP_DAC_MIXER_MAX, SPRD_CODEC_LEFT,
+ snd_soc_read(codec, DCR2_DCR1) & BIT(HPL_EN), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_HP_DACL,
+ SPRD_CODEC_HP_DAC_MIXER_MAX, SPRD_CODEC_RIGHT,
+ snd_soc_read(codec, DCR2_DCR1) & BIT(HPR_EN), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_HP_ADCL,
+ SPRD_CODEC_HP_MIXER_MAX, SPRD_CODEC_LEFT,
+ snd_soc_read(codec, DCR2_DCR1) & BIT(HPL_EN), 0);
+
+ _mixer_setting(codec, SPRD_CODEC_HP_ADCL,
+ SPRD_CODEC_HP_MIXER_MAX, SPRD_CODEC_RIGHT,
+ snd_soc_read(codec, DCR2_DCR1) & BIT(HPR_EN), 0);
+
+_pre_pmd:
+
+ return ret;
+}
+
+static int spk_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ if (snd_soc_read(codec, DCR2_DCR1) & BIT(AOL_EN)) {
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ sprd_codec_pa_sw_set(codec, SPRD_CODEC_PA_SW_AOL);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL);
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_DACL,
+ SPRD_CODEC_SPK_DAC_MIXER_MAX, SPRD_CODEC_LEFT,
+ (snd_soc_read(codec, DCR2_DCR1) & BIT(AOL_EN)), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_DACL,
+ SPRD_CODEC_SPK_DAC_MIXER_MAX, SPRD_CODEC_RIGHT,
+ (snd_soc_read(codec, DCR2_DCR1) & BIT(AOR_EN)), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_ADCL,
+ SPRD_CODEC_SPK_MIXER_MAX, SPRD_CODEC_LEFT,
+ (snd_soc_read(codec, DCR2_DCR1) & BIT(AOL_EN)), 0);
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_ADCL,
+ SPRD_CODEC_SPK_MIXER_MAX, SPRD_CODEC_RIGHT,
+ (snd_soc_read(codec, DCR2_DCR1) & BIT(AOR_EN)), 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_EAR_WITH_IN_SPK
+static int ear_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ sprd_codec_pa_sw_set(codec, SPRD_CODEC_PA_SW_EAR);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_EAR);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+#endif
+
+static int adcpgal_set(struct snd_soc_codec *codec, int on)
+{
+ int mask = ADCPGAL_EN_MASK << ADCPGAL_EN;
+ return snd_soc_update_bits(codec, SOC_REG(AACR2_AACR1), mask,
+ on ? mask : 0);
+}
+
+static int adcpgar_set(struct snd_soc_codec *codec, int on)
+{
+ int mask = ADCPGAR_EN_MASK << ADCPGAR_EN;
+ return snd_soc_update_bits(codec, SOC_REG(AACR2_AACR1), mask,
+ on ? mask : 0);
+}
+
+static int adc_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int is_right = (w->shift == SPRD_CODEC_RIGHT);
+ int on = (! !SND_SOC_DAPM_EVENT_ON(event));
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ if (is_right) {
+ adcpgar_set(codec, on);
+ _mixer_setting(codec, SPRD_CODEC_AIL, SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_RIGHT, on, 1);
+ } else {
+ adcpgal_set(codec, on);
+ _mixer_setting(codec, SPRD_CODEC_AIL, SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_LEFT, on, 1);
+ }
+
+ return 0;
+}
+
+static int pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = GET_PGA_ID(FUN_REG(w->reg));
+ struct sprd_codec_pga_op *pga = &(sprd_codec->pga[id]);
+ int ret = 0;
+ int min = sprd_codec_pga_cfg[id].min;
+
+ sp_asoc_pr_dbg("%s Set %s(%d) Event is %s\n", __func__,
+ sprd_codec_pga_debug_str[id], pga->pgaval,
+ get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ pga->set = sprd_codec_pga_cfg[id].set;
+ ret = pga->set(codec, pga->pgaval);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ pga->set = 0;
+ ret = sprd_codec_pga_cfg[id].set(codec, min);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int adcpgar_byp_set(struct snd_soc_codec *codec, int value)
+{
+ int mask = ADCPGAR_BYP_MASK << ADCPGAR_BYP;
+ return snd_soc_update_bits(codec, SOC_REG(AACR2_AACR1), mask,
+ value << ADCPGAR_BYP);
+}
+
+static int adcpgar_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ adcpgar_byp_set(codec, 2);
+ sprd_codec_wait(100);
+ adcpgar_byp_set(codec, 0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int ana_loop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(w->reg));
+ struct sprd_codec_mixer *mixer;
+ int ret = 0;
+ static int s_need_wait = 1;
+
+ sp_asoc_pr_dbg("Entering %s event is %s\n", __func__,
+ get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* NOTES: reduce linein pop noise must open ADCL/R ->HP/SPK MIXER
+ AFTER delay 250ms for both ADCL/ADCR switch complete.
+ */
+ if (s_need_wait == 1) {
+ /* NOTES: reduce linein pop noise must delay 250ms
+ after linein mixer switch on.
+ actually this function perform after
+ adc_switch_event function for
+ both ADCL/ADCR switch complete.
+ */
+ sprd_codec_wait(250);
+ s_need_wait++;
+ sp_asoc_pr_dbg("ADC Switch ON delay\n");
+ }
+ mixer = &(sprd_codec->mixer[id]);
+ if (mixer->set) {
+ mixer->set(codec, mixer->on);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ s_need_wait = 1;
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int reg = FUN_REG(w->reg);
+ int ret = 0;
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ struct regulator **regu;
+
+ sp_asoc_pr_dbg("%s %s Event is %s\n", __func__,
+ mic_bias_name[GET_MIC_BIAS_ID(reg)],
+ get_event_name(event));
+
+ switch (reg) {
+ case SPRD_CODEC_MIC_BIAS:
+ regu = &sprd_codec->main_mic;
+ break;
+ case SPRD_CODEC_AUXMIC_BIAS:
+ regu = &sprd_codec->aux_mic;
+ break;
+ case SPRD_CODEC_HEADMIC_BIAS:
+ regu = &sprd_codec->head_mic;
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ ret = sprd_codec_mic_bias_set(regu, on);
+
+ return ret;
+}
+
+static int mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(w->reg));
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s event is %s\n", sprd_codec_mixer_debug_str[id],
+ get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mixer->on = 1;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ mixer->on = 0;
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+ if (ret >= 0)
+ _mixer_setting_one(codec, id, mixer->on, 1);
+
+ return ret;
+}
+
+static int mixer_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_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = wlist->widgets[0]->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(mc->reg));
+ ucontrol->value.integer.value[0] = sprd_codec->mixer[id].on;
+ return 0;
+}
+
+static int mixer_need_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol, bool need_set)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = wlist->widgets[0]->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(mc->reg));
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]);
+ int ret = 0;
+
+ if (mixer->on == ucontrol->value.integer.value[0])
+ return 0;
+
+ sp_asoc_pr_info("Set %s Switch %s\n", sprd_codec_mixer_debug_str[id],
+ STR_ON_OFF(ucontrol->value.integer.value[0]));
+
+ /*notice the sequence */
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ /*update reg: must be set after snd_soc_dapm_put_enum_double->change = snd_soc_test_bits(widget->codec, e->reg, mask, val); */
+
+ if (mixer->set && need_set)
+ ret = mixer->set(codec, mixer->on);
+
+ return ret;
+}
+
+static int mixer_set_mem(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return mixer_need_set(kcontrol, ucontrol, 0);
+}
+
+static int mixer_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return mixer_need_set(kcontrol, ucontrol, 1);
+}
+
+#define SPRD_CODEC_MIXER(xname, xreg)\
+ SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, mixer_get, mixer_set)
+
+/*Just for LINE IN path, mixer_set not really set mixer (ADCL/R -> HP/SPK L/R) here but
+setting in ana_loop_event, just remeber state here*/
+#define SPRD_CODEC_MIXER_NOSET(xname, xreg)\
+ SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, mixer_get, mixer_set_mem)
+
+/* ADCL Mixer */
+static const struct snd_kcontrol_new adcl_mixer_controls[] = {
+ SPRD_CODEC_MIXER("AILADCL Switch",
+ ID_FUN(SPRD_CODEC_AIL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("AIRADCL Switch",
+ ID_FUN(SPRD_CODEC_AIR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("MainMICADCL Switch",
+ ID_FUN(SPRD_CODEC_MAIN_MIC, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("AuxMICADCL Switch",
+ ID_FUN(SPRD_CODEC_AUX_MIC, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("HPMICADCL Switch",
+ ID_FUN(SPRD_CODEC_HP_MIC, SPRD_CODEC_LEFT)),
+};
+
+/* ADCR Mixer */
+static const struct snd_kcontrol_new adcr_mixer_controls[] = {
+ SPRD_CODEC_MIXER("AILADCR Switch",
+ ID_FUN(SPRD_CODEC_AIL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("AIRADCR Switch",
+ ID_FUN(SPRD_CODEC_AIR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("MainMICADCR Switch",
+ ID_FUN(SPRD_CODEC_MAIN_MIC, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("AuxMICADCR Switch",
+ ID_FUN(SPRD_CODEC_AUX_MIC, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("HPMICADCR Switch",
+ ID_FUN(SPRD_CODEC_HP_MIC, SPRD_CODEC_RIGHT)),
+};
+
+/* HPL Mixer */
+static const struct snd_kcontrol_new hpl_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_DACL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("DACRHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_DACR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_LEFT)),
+};
+
+/* HPR Mixer */
+static const struct snd_kcontrol_new hpr_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_DACL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("DACRHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_DACR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_RIGHT)),
+};
+
+/* SPKL Mixer */
+static const struct snd_kcontrol_new spkl_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("DACRSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_LEFT)),
+};
+
+/* SPKR Mixer */
+static const struct snd_kcontrol_new spkr_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("DACRSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_RIGHT)),
+};
+
+/*ANA LOOP SWITCH*/
+#define SPRD_CODEC_LOOP_SWITCH(xname, xreg)\
+ SND_SOC_DAPM_PGA_S(xname, SPRD_CODEC_ANA_MIXER_ORDER, FUN_REG(xreg), 0, 0, ana_loop_event,\
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#ifndef SND_SOC_DAPM_MICBIAS_E
+#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
+{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .event = wevent, .event_flags = wflags}
+#endif
+
+static const struct snd_soc_dapm_widget sprd_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VBO", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VCOM_BUF", 0, 0),
+ SND_SOC_DAPM_SUPPLY_S("Digital Power", 1, SND_SOC_NOPM, 0, 0,
+ digital_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Analog Power", 2, SND_SOC_NOPM, 0, 0,
+ analog_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("DA Clk", 3, SOC_REG(CCR), DAC_CLK_EN, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DRV Clk", 4, SOC_REG(CCR), DRV_CLK_EN, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("AD IBUF", 3, SOC_REG(AACR2_AACR1), ADC_IBUF_PD,
+ 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AD Clk", 3, SOC_REG(CCR), ADC_CLK_EN, 0, NULL,
+ 0),
+
+ SND_SOC_DAPM_PGA_S("Digital DACL Switch", 4, SOC_REG(AUD_TOP_CTL),
+ DAC_EN_L, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Digital DACR Switch", 4, SOC_REG(AUD_TOP_CTL),
+ DAC_EN_R, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADie Digital DACL Switch", 5, SOC_REG(AUDIF_ENB),
+ AUDIFA_DACL_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADie Digital DACR Switch", 5, SOC_REG(AUDIF_ENB),
+ AUDIFA_DACR_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("DACL Switch", 6, SOC_REG(DACGR_DACR), DACL_EN, 0,
+ NULL,
+ 0),
+ SND_SOC_DAPM_PGA_S("DACR Switch", 6, SOC_REG(DACGR_DACR), DACR_EN, 0,
+ NULL,
+ 0),
+ SND_SOC_DAPM_PGA_S("DACL Mute", 6, FUN_REG(SPRD_CODEC_PGA_DACL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("DACR Mute", 6, FUN_REG(SPRD_CODEC_PGA_DACR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("DAC", "Playback-Vaudio",
+ FUN_REG(SPRD_CODEC_PLAYBACK), 0,
+ 0,
+ chan_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DFM-OUT", "DFM-Output",
+ SND_SOC_NOPM, 0,
+ 0,
+ dfm_out_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ SND_SOC_DAPM_PGA_S("HP POP", SPRD_CODEC_HP_POP_ORDER, SND_SOC_NOPM, 0,
+ 0, hp_pop_event,
+ SND_SOC_DAPM_POST_PMU),
+#else
+ SND_SOC_DAPM_SUPPLY_S("HP POP", 5, SND_SOC_NOPM, 0, 0, hp_pop_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+#endif
+ SND_SOC_DAPM_PGA_S("HPL Switch", SPRD_CODEC_HP_SWITCH_ORDER,
+ SOC_REG(DCR2_DCR1), HPL_EN, 0,
+ hp_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPR Switch", SPRD_CODEC_HP_SWITCH_ORDER,
+ SOC_REG(DCR2_DCR1), HPR_EN, 0,
+ hp_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPL Mute", 6, FUN_REG(SPRD_CODEC_PGA_HPL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPR Mute", 6, FUN_REG(SPRD_CODEC_PGA_HPR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0,
+ &hpl_mixer_controls[0],
+ ARRAY_SIZE(hpl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0,
+ &hpr_mixer_controls[0],
+ ARRAY_SIZE(hpr_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SPKL Mixer", SND_SOC_NOPM, 0, 0,
+ &spkl_mixer_controls[0],
+ ARRAY_SIZE(spkl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SPKR Mixer", SND_SOC_NOPM, 0, 0,
+ &spkr_mixer_controls[0],
+ ARRAY_SIZE(spkr_mixer_controls)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLHPL Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRHPL Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLHPR Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRHPR Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLSPKL Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRSPKL Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLSPKR Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRSPKR Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_RIGHT)),
+ SND_SOC_DAPM_PGA_S("SPKL Switch", 5, SOC_REG(DCR2_DCR1), AOL_EN, 0,
+ spk_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("SPKR Switch", 5, SOC_REG(DCR2_DCR1), AOR_EN, 0,
+ spk_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("SPKL Mute", 6, FUN_REG(SPRD_CODEC_PGA_SPKL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("SPKR Mute", 6, FUN_REG(SPRD_CODEC_PGA_SPKR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("EAR Mixer", 5,
+ FUN_REG(ID_FUN
+ (SPRD_CODEC_EAR_DACL, SPRD_CODEC_LEFT)), 0,
+ 0, mixer_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_EAR_WITH_IN_SPK
+ SND_SOC_DAPM_PGA_S("EAR Switch", 6, SOC_REG(DCR2_DCR1), EAR_EN, 0,
+ ear_switch_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+#else
+ SND_SOC_DAPM_PGA_S("EAR Switch", 6, SOC_REG(DCR2_DCR1), EAR_EN, 0, NULL,
+ 0),
+#endif
+
+ SND_SOC_DAPM_PGA_S("EAR Mute", 7, FUN_REG(SPRD_CODEC_PGA_EAR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MIXER("ADCL Mixer", SND_SOC_NOPM, 0, 0,
+ &adcl_mixer_controls[0],
+ ARRAY_SIZE(adcl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("ADCR Mixer", SND_SOC_NOPM, 0, 0,
+ &adcr_mixer_controls[0],
+ ARRAY_SIZE(adcr_mixer_controls)),
+ SND_SOC_DAPM_PGA_S("Digital ADCL Switch", 3, SOC_REG(AUD_TOP_CTL),
+ ADC_EN_L, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Digital ADCR Switch", 3, SOC_REG(AUD_TOP_CTL),
+ ADC_EN_R, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADie Digital ADCL Switch", 2, SOC_REG(AUDIF_ENB),
+ AUDIFA_ADCL_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADie Digital ADCR Switch", 2, SOC_REG(AUDIF_ENB),
+ AUDIFA_ADCR_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADCL Switch", 1, SOC_REG(AACR2_AACR1), ADCL_PD, 1,
+ NULL,
+ 0),
+ SND_SOC_DAPM_PGA_S("ADCR Switch", 1, SOC_REG(AACR2_AACR1), ADCR_PD, 1,
+ adcpgar_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("ADCL PGA", SND_SOC_NOPM, SPRD_CODEC_LEFT, 0, NULL,
+ 0,
+ adc_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ADCR PGA", SND_SOC_NOPM, SPRD_CODEC_RIGHT, 0, NULL,
+ 0,
+ adc_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADCL Mute", 3, FUN_REG(SPRD_CODEC_PGA_ADCL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADCR Mute", 3, FUN_REG(SPRD_CODEC_PGA_ADCR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC", "Main-Capture-Vaudio",
+ FUN_REG(SPRD_CODEC_CAPTRUE),
+ 0, 0,
+ chan_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_S("MIC Boost", 3, FUN_REG(SPRD_CODEC_PGA_MIC), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("AUXMIC Boost", 3, FUN_REG(SPRD_CODEC_PGA_AUXMIC), 0,
+ 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HEADMIC Boost", 3, FUN_REG(SPRD_CODEC_PGA_HEADMIC),
+ 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("AIL Boost", 3, FUN_REG(SPRD_CODEC_PGA_AIL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("AIR Boost", 3, FUN_REG(SPRD_CODEC_PGA_AIR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MICBIAS_E("Mic Bias", FUN_REG(SPRD_CODEC_MIC_BIAS), 0, 0,
+ mic_bias_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MICBIAS_E("AuxMic Bias", FUN_REG(SPRD_CODEC_AUXMIC_BIAS),
+ 0, 0,
+ mic_bias_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MICBIAS_E("HeadMic Bias", FUN_REG(SPRD_CODEC_HEADMIC_BIAS),
+ 0, 0,
+ mic_bias_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("Digital ADC1L Switch", 5, SOC_REG(AUD_TOP_CTL),
+ ADC1_EN_L, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Digital ADC1R Switch", 5, SOC_REG(AUD_TOP_CTL),
+ ADC1_EN_R, 0, NULL, 0),
+ SND_SOC_DAPM_ADC_E("ADC1", "Ext-Capture-Vaudio",
+ FUN_REG(SPRD_CODEC_CAPTRUE1),
+ 0, 0,
+ chan_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /*add DMIC */
+ SND_SOC_DAPM_PGA_S("Digital ADC DMIC In", 4, SOC_REG(AUD_TOP_CTL),
+ ADC_DMIC_SEL, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Digital ADC1 DMIC In", 4, SOC_REG(AUD_TOP_CTL),
+ ADC1_DMIC1_SEL, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("DMIC Switch", 3, SOC_REG(AUD_DMIC_CTL),
+ ADC_DMIC_EN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA_S("DMIC1 Switch", 3, SOC_REG(AUD_DMIC_CTL),
+ ADC1_DMIC1_EN, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_PGA_S("HP PA Switch", SPRD_CODEC_HP_PA_ORDER, SND_SOC_NOPM,
+ 0, 0, hp_pa_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("HP PA POST Switch", SPRD_CODEC_HP_PA_POST_ORDER,
+ SND_SOC_NOPM, 0, 0, hp_pa_post_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Spk PA Switch", SPRD_CODEC_PA_ORDER, SND_SOC_NOPM,
+ 0, 0,
+ spk_pa_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_OUTPUT("HEAD_P_L"),
+ SND_SOC_DAPM_OUTPUT("HEAD_P_R"),
+ SND_SOC_DAPM_OUTPUT("AOL"),
+ SND_SOC_DAPM_OUTPUT("AOR"),
+ SND_SOC_DAPM_OUTPUT("EAR"),
+
+ SND_SOC_DAPM_OUTPUT("HP PA"),
+ SND_SOC_DAPM_OUTPUT("Spk PA"),
+ SND_SOC_DAPM_OUTPUT("Spk2 PA"),
+
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_INPUT("AUXMIC"),
+ SND_SOC_DAPM_INPUT("HPMIC"),
+ SND_SOC_DAPM_INPUT("AIL"),
+ SND_SOC_DAPM_INPUT("AIR"),
+};
+
+/* sprd_codec supported interconnection*/
+static const struct snd_soc_dapm_route sprd_codec_intercon[] = {
+ /* Power */
+ {"Analog Power", NULL, "VCOM_BUF"},
+ {"Analog Power", NULL, "VBO"},
+ {"DA Clk", NULL, "Analog Power"},
+ {"DA Clk", NULL, "Digital Power"},
+ {"DAC", NULL, "DA Clk"},
+ {"DFM-OUT", NULL, "DA Clk"},
+
+ {"AD IBUF", NULL, "Analog Power"},
+ {"AD Clk", NULL, "Digital Power"},
+ {"AD Clk", NULL, "AD IBUF"},
+ {"ADC", NULL, "AD Clk"},
+
+ {"ADC1", NULL, "AD Clk"},
+
+ {"ADCL PGA", NULL, "AD IBUF"},
+ {"ADCR PGA", NULL, "AD IBUF"},
+
+ {"HP POP", NULL, "DRV Clk"},
+ {"HPL Switch", NULL, "DRV Clk"},
+ {"HPR Switch", NULL, "DRV Clk"},
+ {"SPKL Switch", NULL, "DRV Clk"},
+ {"SPKR Switch", NULL, "DRV Clk"},
+ {"EAR Switch", NULL, "DRV Clk"},
+
+ /* Playback */
+ {"Digital DACL Switch", NULL, "DFM-OUT"},
+ {"Digital DACR Switch", NULL, "DFM-OUT"},
+ {"Digital DACL Switch", NULL, "DAC"},
+ {"Digital DACR Switch", NULL, "DAC"},
+ {"ADie Digital DACL Switch", NULL, "Digital DACL Switch"},
+ {"ADie Digital DACR Switch", NULL, "Digital DACR Switch"},
+ {"DACL Mute", NULL, "ADie Digital DACL Switch"},
+ {"DACR Mute", NULL, "ADie Digital DACR Switch"},
+ {"DACL Switch", NULL, "DACL Mute"},
+ {"DACR Switch", NULL, "DACR Mute"},
+
+ /* Output */
+ {"HPL Mixer", "DACLHPL Switch", "DACL Switch"},
+ {"HPL Mixer", "DACRHPL Switch", "DACR Switch"},
+ {"HPR Mixer", "DACLHPR Switch", "DACL Switch"},
+ {"HPR Mixer", "DACRHPR Switch", "DACR Switch"},
+
+ {"ADCLHPL Loop Switch", NULL, "ADCL PGA"},
+ {"ADCLHPR Loop Switch", NULL, "ADCL PGA"},
+ {"ADCRHPL Loop Switch", NULL, "ADCR PGA"},
+ {"ADCRHPR Loop Switch", NULL, "ADCR PGA"},
+
+ {"HPL Mixer", "ADCLHPL Switch", "ADCLHPL Loop Switch"},
+ {"HPL Mixer", "ADCRHPL Switch", "ADCRHPL Loop Switch"},
+ {"HPR Mixer", "ADCLHPR Switch", "ADCLHPR Loop Switch"},
+ {"HPR Mixer", "ADCRHPR Switch", "ADCRHPR Loop Switch"},
+
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ {"HEAD_P_L", NULL, "HP POP"},
+ {"HEAD_P_R", NULL, "HP POP"},
+ {"HP POP", NULL, "HPL Switch"},
+ {"HP POP", NULL, "HPR Switch"},
+#else
+ {"HPL Switch", NULL, "HP POP"},
+ {"HPR Switch", NULL, "HP POP"},
+#endif
+ {"HPL Switch", NULL, "HPL Mixer"},
+ {"HPR Switch", NULL, "HPR Mixer"},
+ {"HPL Mute", NULL, "HPL Switch"},
+ {"HPR Mute", NULL, "HPR Switch"},
+ {"HEAD_P_L", NULL, "HPL Mute"},
+ {"HEAD_P_R", NULL, "HPR Mute"},
+
+ {"HP PA", NULL, "HP PA POST Switch"},
+ {"HP PA POST Switch", NULL, "HP PA Switch"},
+ {"HP PA Switch", NULL, "HPL Mute"},
+ {"HP PA Switch", NULL, "HPR Mute"},
+
+ {"SPKL Mixer", "DACLSPKL Switch", "DACL Switch"},
+ {"SPKL Mixer", "DACRSPKL Switch", "DACR Switch"},
+ {"SPKR Mixer", "DACLSPKR Switch", "DACL Switch"},
+ {"SPKR Mixer", "DACRSPKR Switch", "DACR Switch"},
+
+ {"ADCLSPKL Loop Switch", NULL, "ADCL PGA"},
+ {"ADCLSPKR Loop Switch", NULL, "ADCL PGA"},
+ {"ADCRSPKL Loop Switch", NULL, "ADCR PGA"},
+ {"ADCRSPKR Loop Switch", NULL, "ADCR PGA"},
+
+ {"SPKL Mixer", "ADCLSPKL Switch", "ADCLSPKL Loop Switch"},
+ {"SPKL Mixer", "ADCRSPKL Switch", "ADCRSPKL Loop Switch"},
+ {"SPKR Mixer", "ADCLSPKR Switch", "ADCLSPKR Loop Switch"},
+ {"SPKR Mixer", "ADCRSPKR Switch", "ADCRSPKR Loop Switch"},
+
+ {"SPKL Switch", NULL, "SPKL Mixer"},
+ {"SPKR Switch", NULL, "SPKR Mixer"},
+ {"SPKL Mute", NULL, "SPKL Switch"},
+ {"SPKR Mute", NULL, "SPKR Switch"},
+ {"AOL", NULL, "SPKL Mute"},
+ {"AOR", NULL, "SPKR Mute"},
+
+ {"Spk PA", NULL, "Spk PA Switch"},
+ {"Spk PA Switch", NULL, "SPKL Mute"},
+
+ {"EAR Mixer", NULL, "DACL Switch"},
+ {"EAR Switch", NULL, "EAR Mixer"},
+ {"EAR Mute", NULL, "EAR Switch"},
+ {"EAR", NULL, "EAR Mute"},
+
+ {"ADCL Mute", NULL, "ADCL Mixer"},
+ {"ADCR Mute", NULL, "ADCR Mixer"},
+ {"ADCL PGA", NULL, "ADCL Mute"},
+ {"ADCR PGA", NULL, "ADCR Mute"},
+ /* LineIN */
+ {"ADCL Mixer", "AILADCL Switch", "AIL Boost"},
+ {"ADCR Mixer", "AILADCR Switch", "AIL Boost"},
+ {"ADCL Mixer", "AIRADCL Switch", "AIR Boost"},
+ {"ADCR Mixer", "AIRADCR Switch", "AIR Boost"},
+ {"AIL Boost", NULL, "AIL"},
+ {"AIR Boost", NULL, "AIR"},
+ /* Input */
+ {"ADCL Mixer", "MainMICADCL Switch", "MIC Boost"},
+ {"ADCR Mixer", "MainMICADCR Switch", "MIC Boost"},
+ {"MIC Boost", NULL, "Mic Bias"},
+ {"ADCL Mixer", "AuxMICADCL Switch", "AUXMIC Boost"},
+ {"ADCR Mixer", "AuxMICADCR Switch", "AUXMIC Boost"},
+ {"AUXMIC Boost", NULL, "AuxMic Bias"},
+ {"ADCL Mixer", "HPMICADCL Switch", "HEADMIC Boost"},
+ {"ADCR Mixer", "HPMICADCR Switch", "HEADMIC Boost"},
+ {"HEADMIC Boost", NULL, "HeadMic Bias"},
+ /* Captrue */
+ {"ADCL Switch", NULL, "ADCL PGA"},
+ {"ADCR Switch", NULL, "ADCR PGA"},
+ {"ADie Digital ADCL Switch", NULL, "ADCL Switch"},
+ {"ADie Digital ADCR Switch", NULL, "ADCR Switch"},
+ {"Digital ADCL Switch", NULL, "ADie Digital ADCL Switch"},
+ {"Digital ADCR Switch", NULL, "ADie Digital ADCR Switch"},
+ {"ADC", NULL, "Digital ADCL Switch"},
+ {"ADC", NULL, "Digital ADCR Switch"},
+
+ {"Digital ADC1L Switch", NULL, "ADie Digital ADCL Switch"},
+ {"Digital ADC1R Switch", NULL, "ADie Digital ADCR Switch"},
+ {"ADC1", NULL, "Digital ADC1L Switch"},
+ {"ADC1", NULL, "Digital ADC1R Switch"},
+
+ {"Mic Bias", NULL, "MIC"},
+ {"AuxMic Bias", NULL, "AUXMIC"},
+ {"HeadMic Bias", NULL, "HPMIC"},
+
+ /* DMIC0 */
+ {"DMIC Switch", NULL, "DMIC"},
+ {"Digital ADC DMIC In", NULL, "DMIC Switch"},
+ {"Digital ADCL Switch", NULL, "Digital ADC DMIC In"},
+ {"Digital ADCR Switch", NULL, "Digital ADC DMIC In"},
+ /* DMIC1 */
+ {"DMIC1 Switch", NULL, "DMIC1"},
+ {"Digital ADC1 DMIC In", NULL, "DMIC1 Switch"},
+ {"Digital ADC1L Switch", NULL, "Digital ADC1 DMIC In"},
+ {"Digital ADC1R Switch", NULL, "Digital ADC1 DMIC In"},
+
+ /* Bias independent */
+ {"Mic Bias", NULL, "Analog Power"},
+ {"AuxMic Bias", NULL, "Analog Power"},
+ {"HeadMic Bias", NULL, "Analog Power"},
+};
+
+static int sprd_codec_vol_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int id = GET_PGA_ID(FUN_REG(mc->reg));
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ struct sprd_codec_pga_op *pga = &(sprd_codec->pga[id]);
+ int ret = 0;
+
+ sp_asoc_pr_info("Set PGA[%s] to %ld\n", sprd_codec_pga_debug_str[id],
+ ucontrol->value.integer.value[0]);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+ pga->pgaval = val;
+ if (pga->set) {
+ ret = pga->set(codec, pga->pgaval);
+ }
+ return ret;
+}
+
+static int sprd_codec_vol_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int id = GET_PGA_ID(FUN_REG(mc->reg));
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+ struct sprd_codec_pga_op *pga = &(sprd_codec->pga[id]);
+
+ ucontrol->value.integer.value[0] = pga->pgaval;
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static int sprd_codec_inter_pa_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret = 0;
+
+ sp_asoc_pr_info("Config inter PA 0x%08x\n",
+ (int)ucontrol->value.integer.value[0]);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+ sprd_codec->inter_pa.value = (u32) val;
+ if (sprd_codec->inter_pa.set) {
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ sprd_inter_speaker_pa(codec, 1);
+ } else {
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ }
+ return ret;
+}
+
+static int sprd_codec_inter_pa_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+ ucontrol->value.integer.value[0] = sprd_codec->inter_pa.value;
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static int sprd_codec_inter_hp_pa_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret = 0;
+ struct sprd_codec_inter_hp_pa *p_setting =
+ &sprd_codec->inter_hp_pa.setting;
+
+ sp_asoc_pr_info("Config inter HP PA 0x%08x\n",
+ (int)ucontrol->value.integer.value[0]);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+ mutex_lock(&sprd_codec->inter_hp_pa_mutex);
+ sprd_codec->inter_hp_pa.value = (u32) val;
+ if (sprd_codec->inter_hp_pa.set) {
+ mutex_unlock(&sprd_codec->inter_hp_pa_mutex);
+ sprd_codec_hp_pa_lpw(codec, p_setting->class_g_low_power);
+ sprd_codec_hp_pa_mode(codec, p_setting->class_g_mode);
+ sprd_codec_hp_pa_osc(codec, p_setting->class_g_osc);
+ sprd_codec_hp_pa_cgcal_en(codec, p_setting->class_g_cgcal);
+ } else {
+ mutex_unlock(&sprd_codec->inter_hp_pa_mutex);
+ }
+ return ret;
+}
+
+static int sprd_codec_inter_hp_pa_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+
+ mutex_lock(&sprd_codec->inter_hp_pa_mutex);
+ ucontrol->value.integer.value[0] = sprd_codec->inter_hp_pa.value;
+ mutex_unlock(&sprd_codec->inter_hp_pa_mutex);
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static int sprd_codec_inter_hp_pa_delay_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret = 0;
+ struct sprd_codec_inter_hp_pa *p_setting =
+ &sprd_codec->inter_hp_pa.setting;
+
+ sp_asoc_pr_info("Config inter HP PA DELAY 0x%08x\n",
+ (int)ucontrol->value.integer.value[0]);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+ mutex_lock(&sprd_codec->inter_hp_pa_delay_mutex);
+ sprd_codec->inter_hp_pa.delay_value = (u32) val;
+ sprd_codec->class_g_delay1 = sprd_codec->inter_hp_pa.delay_setting.class_g_delay1;
+ sprd_codec->class_g_delay2 = sprd_codec->inter_hp_pa.delay_setting.class_g_delay2;
+ mutex_unlock(&sprd_codec->inter_hp_pa_delay_mutex);
+ return ret;
+}
+
+static int sprd_codec_inter_hp_pa_delay_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+
+ mutex_lock(&sprd_codec->inter_hp_pa_delay_mutex);
+ ucontrol->value.integer.value[0] = sprd_codec->inter_hp_pa.delay_value;
+ mutex_unlock(&sprd_codec->inter_hp_pa_delay_mutex);
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static int sprd_codec_mic_bias_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = FUN_REG(mc->reg);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret = 0;
+ int id = GET_MIC_BIAS_ID(reg);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+
+ if (invert)
+ val = max - val;
+ if (sprd_codec->mic_bias[id] == val) {
+ return 0;
+ }
+
+ sp_asoc_pr_info("%s Switch %s\n", mic_bias_name[id], STR_ON_OFF(val));
+
+ sprd_codec->mic_bias[id] = val;
+ if (val) {
+ ret =
+ snd_soc_dapm_force_enable_pin(&codec->card->dapm,
+ mic_bias_name[id]);
+ } else {
+ ret =
+ snd_soc_dapm_disable_pin(&codec->card->dapm,
+ mic_bias_name[id]);
+ }
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync(&codec->card->dapm);
+
+ return ret;
+}
+
+static int sprd_codec_mic_bias_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = FUN_REG(mc->reg);
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+ int id = GET_MIC_BIAS_ID(reg);
+
+ ucontrol->value.integer.value[0] = sprd_codec->mic_bias[id];
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -600, 75, 0);
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -3600, 300, 1);
+static const DECLARE_TLV_DB_SCALE(ear_tlv, -3600, 300, 1);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -2400, 300, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(auxmic_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(headmic_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(ailr_tlv, 0, 600, 0);
+
+#define SPRD_CODEC_PGA(xname, xreg, tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, FUN_REG(xreg), 0, 15, 0, \
+ sprd_codec_vol_get, sprd_codec_vol_put, tlv_array)
+
+#define SPRD_CODEC_PGA_MAX(xname, xreg, max, tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, FUN_REG(xreg), 0, max, 0, \
+ sprd_codec_vol_get, sprd_codec_vol_put, tlv_array)
+
+#define SPRD_CODEC_MIC_BIAS(xname, xreg) \
+ SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, \
+ sprd_codec_mic_bias_get, sprd_codec_mic_bias_put)
+
+static const struct snd_kcontrol_new sprd_codec_snd_controls[] = {
+ SPRD_CODEC_PGA("SPKL Playback Volume", SPRD_CODEC_PGA_SPKL, spk_tlv),
+ SPRD_CODEC_PGA("SPKR Playback Volume", SPRD_CODEC_PGA_SPKR, spk_tlv),
+ SPRD_CODEC_PGA("HPL Playback Volume", SPRD_CODEC_PGA_HPL, hp_tlv),
+ SPRD_CODEC_PGA("HPR Playback Volume", SPRD_CODEC_PGA_HPR, hp_tlv),
+ SPRD_CODEC_PGA("EAR Playback Volume", SPRD_CODEC_PGA_EAR, ear_tlv),
+
+ SPRD_CODEC_PGA_MAX("ADCL Capture Volume", SPRD_CODEC_PGA_ADCL, 63,
+ adc_tlv),
+ SPRD_CODEC_PGA_MAX("ADCR Capture Volume", SPRD_CODEC_PGA_ADCR, 63,
+ adc_tlv),
+
+ SPRD_CODEC_PGA_MAX("DACL Playback Volume", SPRD_CODEC_PGA_DACL, 7,
+ dac_tlv),
+ SPRD_CODEC_PGA_MAX("DACR Playback Volume", SPRD_CODEC_PGA_DACR, 7,
+ dac_tlv),
+ SPRD_CODEC_PGA_MAX("MIC Boost", SPRD_CODEC_PGA_MIC, 3, mic_tlv),
+ SPRD_CODEC_PGA_MAX("AUXMIC Boost", SPRD_CODEC_PGA_AUXMIC, 3,
+ auxmic_tlv),
+ SPRD_CODEC_PGA_MAX("HEADMIC Boost", SPRD_CODEC_PGA_HEADMIC, 3,
+ headmic_tlv),
+ SPRD_CODEC_PGA_MAX("Linein Boost", SPRD_CODEC_PGA_AIL, 3, ailr_tlv),
+
+ SOC_SINGLE_EXT("Inter PA Config", 0, 0, LONG_MAX, 0,
+ sprd_codec_inter_pa_get, sprd_codec_inter_pa_put),
+
+ SOC_SINGLE_EXT("Inter HP PA Config", 0, 0, LONG_MAX, 0,
+ sprd_codec_inter_hp_pa_get, sprd_codec_inter_hp_pa_put),
+
+ SOC_SINGLE_EXT("Inter HP PA Delay", 0, 0, LONG_MAX, 0,
+ sprd_codec_inter_hp_pa_delay_get, sprd_codec_inter_hp_pa_delay_put),
+
+ SPRD_CODEC_MIC_BIAS("MIC Bias Switch", SPRD_CODEC_MIC_BIAS),
+
+ SPRD_CODEC_MIC_BIAS("AUXMIC Bias Switch", SPRD_CODEC_AUXMIC_BIAS),
+
+ SPRD_CODEC_MIC_BIAS("HEADMIC Bias Switch", SPRD_CODEC_HEADMIC_BIAS),
+};
+
+static unsigned int sprd_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ /* Because snd_soc_update_bits reg is 16 bits short type,
+ so muse do following convert
+ */
+ if (IS_SPRD_CODEC_AP_RANG(reg | SPRD_CODEC_AP_BASE_HI)) {
+ reg |= SPRD_CODEC_AP_BASE_HI;
+ return arch_audio_codec_read(reg);
+ } else if (IS_SPRD_CODEC_DP_RANG(reg | SPRD_CODEC_DP_BASE_HI)) {
+ reg |= SPRD_CODEC_DP_BASE_HI;
+ return __raw_readl((void __iomem *)(reg - CODEC_DP_BASE + sprd_codec_dp_base));
+ } else if (IS_SPRD_CODEC_MIXER_RANG(FUN_REG(reg))) {
+ struct sprd_codec_priv *sprd_codec =
+ snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(reg));
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]);
+ return mixer->on;
+ } else if (IS_SPRD_CODEC_PGA_RANG(FUN_REG(reg))) {
+ } else if (IS_SPRD_CODEC_MIC_BIAS_RANG(FUN_REG(reg))) {
+ } else
+ sp_asoc_pr_dbg("read the register is not codec's reg = 0x%x\n",
+ reg);
+ return 0;
+}
+
+static int sprd_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ int ret = 0;
+ if (IS_SPRD_CODEC_AP_RANG(reg | SPRD_CODEC_AP_BASE_HI)) {
+ reg |= SPRD_CODEC_AP_BASE_HI;
+ ret = arch_audio_codec_write(reg, val);
+ sp_asoc_pr_reg("A[0x%04x] W:[0x%08x] R:[0x%08x]\n",
+ (reg - CODEC_AP_BASE) & 0xFFFF, val, arch_audio_codec_read(reg));
+ return ret;
+ } else if (IS_SPRD_CODEC_DP_RANG(reg | SPRD_CODEC_DP_BASE_HI)) {
+ reg |= SPRD_CODEC_DP_BASE_HI;
+ __raw_writel(val, (void __iomem *)(reg - CODEC_DP_BASE + sprd_codec_dp_base));
+ sp_asoc_pr_reg("D[0x%04x] W:[0x%08x] R:[0x%08x]\n",
+ (reg - CODEC_DP_BASE) & 0xFFFF, val,
+ __raw_readl((void __iomem *)(reg - CODEC_DP_BASE + sprd_codec_dp_base)));
+ return ret;
+ } else if (IS_SPRD_CODEC_MIXER_RANG(FUN_REG(reg))) {
+ struct sprd_codec_priv *sprd_codec =
+ snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(reg));
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]);
+ mixer->on = val ? 1 : 0;
+ } else if (IS_SPRD_CODEC_PGA_RANG(FUN_REG(reg))) {
+ } else if (IS_SPRD_CODEC_MIC_BIAS_RANG(FUN_REG(reg))) {
+ } else
+ sp_asoc_pr_dbg("write the register is not codec's reg = 0x%x\n",
+ reg);
+ return ret;
+}
+
+static int sprd_codec_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask = 0x0F;
+ int shift = 0;
+ int rate = params_rate(params);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sprd_codec->da_sample_val = rate;
+ sp_asoc_pr_info("Playback rate is [%d]\n", rate);
+ sprd_codec_set_sample_rate(codec, rate, mask, shift);
+ } else {
+ sp_asoc_pr_info("Capture rate is [%d]\n", rate);
+ if (dai->id != SPRD_CODEC_IIS1_ID) {
+ sprd_codec->ad_sample_val = rate;
+ sprd_codec_set_ad_sample_rate(codec, rate, mask, shift);
+ } else {
+ sprd_codec->ad1_sample_val = rate;
+ sprd_codec_set_ad_sample_rate(codec, rate,
+ ADC1_SRC_N_MASK,
+ ADC1_SRC_N);
+ }
+ }
+
+ return 0;
+}
+
+static int sprd_codec_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+#if 0
+static void sprd_codec_dac_mute_irq_enable(struct snd_soc_codec *codec)
+{
+ int mask = BIT(DAC_MUTE_D);
+ snd_soc_update_bits(codec, SOC_REG(AUD_INT_CLR), mask, mask);
+ snd_soc_update_bits(codec, SOC_REG(AUD_INT_EN), mask, mask);
+}
+#endif
+
+static irqreturn_t sprd_codec_dp_irq(int irq, void *dev_id)
+{
+ int mask;
+ struct sprd_codec_priv *sprd_codec = dev_id;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ mask = snd_soc_read(codec, AUD_AUD_STS0);
+ sp_asoc_pr_dbg("dac mute irq mask = 0x%x\n", mask);
+ if (BIT(DAC_MUTE_D_MASK) & mask) {
+ mask = BIT(DAC_MUTE_D);
+ complete(&sprd_codec->completion_dac_mute);
+ }
+ if (BIT(DAC_MUTE_U_MASK) & mask) {
+ mask = BIT(DAC_MUTE_U);
+ }
+ snd_soc_update_bits(codec, SOC_REG(AUD_INT_EN), mask, 0);
+ return IRQ_HANDLED;
+}
+
+#if 0
+static int sprd_codec_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+ if (dai->id == SPRD_CODEC_IIS1_ID) /*codec_dai_1 has no playback */
+ return 0;
+
+ if (atomic_read(&sprd_codec->ldo_refcount) >= 1) {
+ sp_asoc_pr_dbg("mute %i\n", mute);
+
+ ret =
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL),
+ BIT(DAC_MUTE_START),
+ mute ? BIT(DAC_MUTE_START) : 0);
+#if 0
+ if (mute && ret) {
+ int dac_mute_complete;
+ struct sprd_codec_priv *sprd_codec =
+ snd_soc_codec_get_drvdata(codec);
+ sp_asoc_pr_dbg("Dac Mute IRQ enable\n");
+ sprd_codec_dac_mute_irq_enable(codec);
+ init_completion(&sprd_codec->completion_dac_mute);
+ dac_mute_complete =
+ wait_for_completion_timeout
+ (&sprd_codec->completion_dac_mute,
+ msecs_to_jiffies(SPRD_CODEC_DAC_MUTE_TIMEOUT));
+ sp_asoc_pr_dbg("Dac Mute Completion %d\n",
+ dac_mute_complete);
+ if (!dac_mute_complete) {
+ pr_err("Dac Mute Timeout\n");
+ }
+ }
+#endif
+
+ }
+
+ return ret;
+}
+#endif
+
+static struct snd_soc_dai_ops sprd_codec_dai_ops = {
+ .hw_params = sprd_codec_pcm_hw_params,
+ .hw_free = sprd_codec_pcm_hw_free,
+};
+
+#ifdef CONFIG_PM
+static int sprd_codec_soc_suspend(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static int sprd_codec_soc_resume(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+#else
+#define sprd_codec_soc_suspend NULL
+#define sprd_codec_soc_resume NULL
+#endif
+
+/*
+ * proc interface
+ */
+
+#ifdef CONFIG_PROC_FS
+static void sprd_codec_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct sprd_codec_priv *sprd_codec = entry->private_data;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ int reg;
+
+ snd_iprintf(buffer, "%s digital part\n", codec->name);
+ for (reg = SPRD_CODEC_DP_BASE; reg < SPRD_CODEC_DP_END; reg += 0x10) {
+ snd_iprintf(buffer, "0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (unsigned int)(reg - SPRD_CODEC_DP_BASE)
+ , snd_soc_read(codec, reg + 0x00)
+ , snd_soc_read(codec, reg + 0x04)
+ , snd_soc_read(codec, reg + 0x08)
+ , snd_soc_read(codec, reg + 0x0C)
+ );
+ }
+ snd_iprintf(buffer, "%s analog part\n", codec->name);
+ for (reg = SPRD_CODEC_AP_BASE; reg < SPRD_CODEC_AP_END; reg += 0x10) {
+ snd_iprintf(buffer, "0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (unsigned int)(reg - SPRD_CODEC_AP_BASE)
+ , snd_soc_read(codec, reg + 0x00)
+ , snd_soc_read(codec, reg + 0x04)
+ , snd_soc_read(codec, reg + 0x08)
+ , snd_soc_read(codec, reg + 0x0C)
+ );
+ }
+}
+
+static void sprd_codec_proc_init(struct sprd_codec_priv *sprd_codec)
+{
+ struct snd_info_entry *entry;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+
+ if (!snd_card_proc_new(codec->card->snd_card, "sprd-codec", &entry))
+ snd_info_set_text_ops(entry, sprd_codec, sprd_codec_proc_read);
+}
+#else /* !CONFIG_PROC_FS */
+static inline void sprd_codec_proc_init(struct sprd_codec_priv *sprd_codec)
+{
+}
+#endif
+
+static void sprd_codec_power_changed(struct power_supply *psy)
+{
+ struct sprd_codec_priv *sprd_codec = 0;
+ sp_asoc_pr_dbg("%s %s\n", __func__, psy->name);
+#if 1
+ sprd_codec = container_of(psy, struct sprd_codec_priv, audio_ldo);
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+ if (sprd_codec->inter_pa.set && sprd_codec->inter_pa.setting.is_LDO_mode
+ && sprd_codec->inter_pa.setting.is_auto_LDO_mode) {
+ sprd_codec_pa_ldo_auto(sprd_codec->codec, 0);
+ }
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ if (atomic_read(&sprd_codec->ldo_refcount) >= 1) {
+ sprd_codec_vcom_ldo_auto(sprd_codec->codec, 0);
+ }
+#endif
+}
+
+static int sprd_codec_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ return -EINVAL;
+}
+
+static int sprd_codec_audio_ldo(struct sprd_codec_priv *sprd_codec)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ sprd_codec->audio_ldo.name = "audio-ldo";
+ sprd_codec->audio_ldo.get_property = sprd_codec_power_get_property;
+ sprd_codec->audio_ldo.external_power_changed = sprd_codec_power_changed;
+ ret = power_supply_register(codec->dev, &sprd_codec->audio_ldo);
+ if (ret) {
+ pr_err("ERR:Register power supply error!\n");
+ return -EFAULT;
+ }
+ return ret;
+}
+
+#define SPRD_CODEC_PCM_RATES \
+ (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define SPRD_CODEC_PCM_AD_RATES \
+ (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SRC_SUPPORT_RATE | \
+ SNDRV_PCM_RATE_48000)
+
+/* PCM Playing and Recording default in full duplex mode */
+static struct snd_soc_dai_driver sprd_codec_dai[] = {
+ {
+ .name = "sprd-codec-v3-i2s",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Main-Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_AD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+#ifdef CONFIG_SND_SOC_SPRD_VAUDIO
+ {
+ .name = "sprd-codec-v3-vaudio",
+ .playback = {
+ .stream_name = "Playback-Vaudio",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Main-Capture-Vaudio",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_AD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+#endif
+ /*digital ad1 */
+ {
+ .id = SPRD_CODEC_IIS1_ID,
+ .name = "codec-i2s-ext",
+ .capture = {
+ .stream_name = "Ext-Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_AD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+#ifdef CONFIG_SND_SOC_SPRD_VAUDIO
+ /*digital ad1 */
+ {
+ .id = SPRD_CODEC_IIS1_ID,
+ .name = "codec-vaudio-ext",
+ .capture = {
+ .stream_name = "Ext-Capture-Vaudio",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_AD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+#endif
+ {
+ .name = "sprd-codec-v3-fm",
+ .playback = {
+ .stream_name = "DFM-Output",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+};
+
+static int sprd_codec_soc_probe(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+#ifndef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ int hp_plug_state = get_hp_plug_state();
+#endif
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ codec->dapm.idle_bias_off = 1;
+
+ sprd_codec->codec = codec;
+
+ sprd_codec_proc_init(sprd_codec);
+
+ sprd_codec_audio_ldo(sprd_codec);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ sprd_inter_headphone_pa_pre(sprd_codec, 1);
+#else
+ sprd_inter_headphone_pa_pre(sprd_codec, hp_plug_state);
+#endif
+ return ret;
+}
+
+/* power down chip */
+static int sprd_codec_soc_remove(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static int sprd_codec_power_regulator_init(struct sprd_codec_priv *sprd_codec,
+ struct device *dev)
+{
+ sprd_codec_power_get(dev, &sprd_codec->main_mic, "MICBIAS");
+ sprd_codec_power_get(dev, &sprd_codec->aux_mic, "AUXMICBIAS");
+ sprd_codec_power_get(dev, &sprd_codec->head_mic, "HEADMICBIAS");
+
+ return 0;
+}
+
+static void sprd_codec_power_regulator_exit(struct sprd_codec_priv *sprd_codec)
+{
+ sprd_codec_power_put(&sprd_codec->main_mic);
+ sprd_codec_power_put(&sprd_codec->aux_mic);
+ sprd_codec_power_put(&sprd_codec->head_mic);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_sprd_codec = {
+ .probe = sprd_codec_soc_probe,
+ .remove = sprd_codec_soc_remove,
+ .suspend = sprd_codec_soc_suspend,
+ .resume = sprd_codec_soc_resume,
+ .read = sprd_codec_read,
+ .write = sprd_codec_write,
+ .reg_word_size = sizeof(u16),
+ .reg_cache_step = 2,
+ .dapm_widgets = sprd_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sprd_codec_dapm_widgets),
+ .dapm_routes = sprd_codec_intercon,
+ .num_dapm_routes = ARRAY_SIZE(sprd_codec_intercon),
+ .controls = sprd_codec_snd_controls,
+ .num_controls = ARRAY_SIZE(sprd_codec_snd_controls),
+};
+
+static int sprd_codec_probe(struct platform_device *pdev)
+{
+ struct sprd_codec_priv *sprd_codec;
+ int ret;
+ struct device_node *node = pdev->dev.of_node;
+ struct resource *res;
+ u32 ana_chip_id;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ arch_audio_codec_switch(AUDIO_TO_ARM_CTRL);
+
+ sprd_codec =
+ devm_kzalloc(&pdev->dev, sizeof(struct sprd_codec_priv),
+ GFP_KERNEL);
+ if (sprd_codec == NULL)
+ return -ENOMEM;
+
+ sprd_codec->da_sample_val = 44100; /*inital value for FM route */
+
+ if (node) {
+ u32 val;
+ u32 val_arr[2];
+ if (!of_property_read_u32(node, "sprd,hp_pa_ver", &val)) {
+ if (val < SPRD_CODEC_HP_PA_VER_MAX) {
+ sprd_codec->hp_ver = val;
+ sp_asoc_pr_dbg("Set HP PA Ver is %d!\n", val);
+ } else {
+ pr_err
+ ("ERR:This driver just support less %d version!\n",
+ SPRD_CODEC_HP_PA_VER_MAX);
+ return -EINVAL;
+ }
+ }
+ if (!of_property_read_u32(node, "sprd,dp_irq", &val)) {
+ sprd_codec->dp_irq = val;
+ sp_asoc_pr_dbg("Set DP IRQ is %d!\n", val);
+ } else {
+ pr_err("ERR:Must give me the DP IRQ!\n");
+ return -EINVAL;
+ }
+ if (!of_property_read_u32(node, "sprd,def_da_fs", &val)) {
+ sprd_codec->da_sample_val = val;
+ sp_asoc_pr_dbg("Change DA default fs to %d!\n", val);
+ }
+ if (!of_property_read_u32_array(node, "sprd,reg_dp", &val_arr[0], 2)) {
+ sprd_codec_dp_base = ioremap_nocache(val_arr[0], val_arr[1]);
+ if(!sprd_codec_dp_base) {
+ pr_err("ERR: cannot create iomap address for codec DP!\n");
+ return -EINVAL;
+ }
+ } else {
+ pr_err("ERR:Must give me the codec DP reg address!\n");
+ return -EINVAL;
+ }
+ } else {
+ sprd_codec->ap_irq = CODEC_AP_IRQ;
+ sprd_codec->dp_irq = CODEC_DP_IRQ;
+ sprd_codec_dp_base = ioremap_nocache(CODEC_DP_BASE_DEFAULT, CODEC_DP_SIZE_DEFAULT);
+ if(!sprd_codec_dp_base) {
+ pr_err("ERR: cannot create iomap address for codec DP with default address!\n");
+ return -EINVAL;
+ }
+ }
+ if(node) {
+ sprd_codec->ap_irq = irq_of_parse_and_map(node, 0);
+ }
+ ana_chip_id = sci_get_ana_chip_id() >> 16;
+ if (ana_chip_id == 0x2713)
+ sprd_codec->hp_ver = SPRD_CODEC_HP_PA_VER_1;
+ else if (ana_chip_id == 0x2711)
+ sprd_codec->hp_ver = SPRD_CODEC_HP_PA_VER_2;
+ else {
+ pr_info("ana_chip_id is 0x%x\n", (unsigned int)sci_get_ana_chip_id);
+ }
+ platform_set_drvdata(pdev, sprd_codec);
+
+ ret =
+ request_irq(sprd_codec->ap_irq, sprd_codec_ap_irq, 0,
+ "sprd_codec_ap", sprd_codec);
+ if (ret) {
+ pr_err("ERR:Request irq ap failed!\n");
+ goto err_irq;
+ }
+
+ INIT_DELAYED_WORK(&sprd_codec->ovp_delayed_work,
+ sprd_codec_ovp_delay_worker);
+
+ ret =
+ request_irq(sprd_codec->dp_irq, sprd_codec_dp_irq, 0,
+ "sprd_codec_dp", sprd_codec);
+ if (ret) {
+ pr_err("ERR:Request irq dp failed!\n");
+ goto dp_err_irq;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_sprd_codec,
+ sprd_codec_dai,
+ ARRAY_SIZE(sprd_codec_dai));
+ if (ret != 0) {
+ pr_err("ERR:Failed to register CODEC: %d\n", ret);
+ goto reg_err_irq;
+ }
+
+ sprd_codec_inter_pa_init(sprd_codec);
+ sprd_codec_inter_hp_pa_init(sprd_codec);
+
+ sprd_codec_power_regulator_init(sprd_codec, &pdev->dev);
+
+ mutex_init(&sprd_codec->inter_pa_mutex);
+ mutex_init(&sprd_codec->inter_hp_pa_mutex);
+ mutex_init(&sprd_codec->inter_hp_pa_delay_mutex);
+ spin_lock_init(&sprd_codec->sprd_codec_fun_lock);
+ spin_lock_init(&sprd_codec->sprd_codec_pa_sw_lock);
+
+ sprd_codec_vcom_ldo_cfg(sprd_codec, ldo_v_map, ARRAY_SIZE(ldo_v_map));
+ sprd_codec_pa_ldo_cfg(sprd_codec, ldo_v_map, ARRAY_SIZE(ldo_v_map));
+
+ sprd_codec->nb.notifier_call = hp_notifier_handler;
+#ifndef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ hp_register_notifier(&sprd_codec->nb);
+#endif
+
+ return 0;
+reg_err_irq:
+ free_irq(sprd_codec->dp_irq, sprd_codec);
+dp_err_irq:
+ free_irq(sprd_codec->ap_irq, sprd_codec);
+err_irq:
+ iounmap(sprd_codec_dp_base);
+ return -EINVAL;
+}
+
+static int sprd_codec_remove(struct platform_device *pdev)
+{
+ struct sprd_codec_priv *sprd_codec = platform_get_drvdata(pdev);
+ sprd_codec_power_regulator_exit(sprd_codec);
+ free_irq(sprd_codec->ap_irq, sprd_codec);
+ free_irq(sprd_codec->dp_irq, sprd_codec);
+#ifndef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ hp_unregister_notifier(&sprd_codec->nb);
+#endif
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id codec_of_match[] = {
+ {.compatible = "sprd,sprd-codec-v3",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, codec_of_match);
+#endif
+
+static struct platform_driver sprd_codec_codec_driver = {
+ .driver = {
+ .name = "sprd-codec-v3",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(codec_of_match),
+ },
+ .probe = sprd_codec_probe,
+ .remove = sprd_codec_remove,
+};
+
+module_platform_driver(sprd_codec_codec_driver);
+
+MODULE_DESCRIPTION("SPRD-CODEC ALSA SoC codec driver");
+MODULE_AUTHOR("Zhenfang Wang <zhenfang.wang@spreadtrum.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("codec:sprd-codec");
diff --git a/sound/soc/sprd/codec/sprd/v3/sprd-codec.h b/sound/soc/sprd/codec/sprd/v3/sprd-codec.h
new file mode 100755
index 00000000..daa91865
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v3/sprd-codec.h
@@ -0,0 +1,404 @@
+/*
+ * sound/soc/sprd/codec/sprd/v3/sprd-codec.h
+ *
+ * SPRD-CODEC -- SpreadTrum Tiger intergrated codec.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * 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 ork FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __SPRD_CODEC_H
+#define __SPRD_CODEC_H
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/globalregs.h>
+#include <soc/sprd/sprd-audio.h>
+#include <soc/sprd/adi.h>
+#include <asm/io.h>
+
+
+/* unit: ms */
+#define SPRD_CODEC_LDO_WAIT_TIME (5)
+#define SPRD_CODEC_LDO_VCM_TIME (2)
+#define SPRD_CODEC_DAC_MUTE_TIMEOUT (600)
+
+#define SPRD_CODEC_HP_POP_TIMEOUT (1000)
+
+#define SPRD_CODEC_RATE_8000 (10)
+#define SPRD_CODEC_RATE_9600 ( 9)
+#define SPRD_CODEC_RATE_11025 ( 8)
+#define SPRD_CODEC_RATE_12000 ( 7)
+#define SPRD_CODEC_RATE_16000 ( 6)
+#define SPRD_CODEC_RATE_22050 ( 5)
+#define SPRD_CODEC_RATE_24000 ( 4)
+#define SPRD_CODEC_RATE_32000 ( 3)
+#define SPRD_CODEC_RATE_44100 ( 2)
+#define SPRD_CODEC_RATE_48000 ( 1)
+#define SPRD_CODEC_RATE_96000 ( 0)
+
+/* AUD_TOP_CTL */
+#define DAC_EN_L (0)
+#define ADC_EN_L (1)
+#define DAC_EN_R (2)
+#define ADC_EN_R (3)
+#define ADC_SINC_SEL (8)
+#define ADC_SINC_SEL_MASK (0x3)
+#define ADC_DMIC_SEL (9)
+
+#define ADC1_EN_L (10)
+#define ADC1_EN_R (11)
+#define ADC1_SINC_SEL (14)
+#define ADC1_SINC_SEL_MASK (0x3)
+#define ADC1_DMIC1_SEL (15)
+
+/* AUD_DAC_CTL */
+#define DAC_MUTE_START (14)
+#define DAC_MUTE_EN (15)
+
+/* AUD_ADC_CTL */
+#define ADC_SRC_N (0)
+#define ADC_SRC_N_MASK (0x0f)
+#define ADC1_SRC_N (4)
+#define ADC1_SRC_N_MASK (0xf0)
+
+/* AUD_AUD_STS0 */
+#define DAC_MUTE_U_MASK (5)
+#define DAC_MUTE_D_MASK (4)
+#define DAC_MUTE_U_RAW (3)
+#define DAC_MUTE_D_RAW (2)
+#define DAC_MUTE_ST (0)
+#define DAC_MUTE_ST_MASK (0x3)
+
+/* AUD_INT_CLR */
+/* AUD_INT_EN */
+#define DAC_MUTE_U (1)
+#define DAC_MUTE_D (0)
+
+/*AUD_DMIC_CTL*/
+#define ADC_DMIC_CLK_MODE (0)
+#define ADC_DMIC_CLK_MODE_MASK (0x3)
+#define ADC_DMIC_LR_SEL (2)
+#define ADC1_DMIC_CLK_MODE (3)
+#define ADC1_DMIC_CLK_MODE_MASK (0x3)
+#define ADC1_DMIC_LR_SEL (5)
+#define ADC_DMIC_EN (6)
+#define ADC1_DMIC1_EN (7)
+
+/* AUDIF_ENB */
+#define AUDIFA_DACL_EN (0)
+#define AUDIFA_ADCL_EN (1)
+#define AUDIFA_DACR_EN (2)
+#define AUDIFA_ADCR_EN (3)
+
+/* AUDIF_INT_EN */
+/* AUDIF_INT_CLR */
+/* AUDIF_INT_RAW */
+/* AUDIF_INT_MASK */
+#define AUDIO_POP_IRQ (7)
+#define OVP_IRQ (6)
+#define OTP_IRQ (5)
+#define PA_OCP_IRQ (4)
+#define LOR_OCP_IRQ (3)
+#define LOL_OCP_IRQ (2)
+#define EAR_OCP_IRQ (1)
+#define HP_OCP_IRQ (0)
+
+#define LDO_V_29 (0)
+#define LDO_V_31 (1)
+#define LDO_V_32 (2)
+#define LDO_V_33 (3)
+#define LDO_V_34 (4)
+#define LDO_V_35 (5)
+#define LDO_V_36 (6)
+#define LDO_V_38 (7)
+
+#define MIC_LDO_V_21 (0)
+#define MIC_LDO_V_19 (1)
+#define MIC_LDO_V_23 (2)
+#define MIC_LDO_V_25 (3)
+
+/* PMUR2_PMUR1 */
+#define PA_SW_EN (15)
+#define PA_LDO_EN (14)
+#define PA_EN (13)
+#define PAR_SW_EN (12)
+#define PAR_LDO_EN (11)
+#define PAR_EN (10)
+#define OVP_LDO_EN (8)
+#define VB_EN (7)
+#define VBO_EN (6)
+#define HEADMICBIAS_EN (5)
+#define MICBIAS_V (2)
+#define MICBIAS_V_MASK (0x7)
+/* #define MICBIAS_HV_EN (2) */
+#define HEADMIC_SLEEP_EN (1)
+
+/* PMUR4_PMUR3 */
+#define MICBIAS_EN (15)
+#define AUXMICBIAS_EN (14)
+#define VCM_V (11)
+#define VCM_V_MASK (0x7)
+#define BG_I (9)
+#define BG_I_MASK (0x3)
+#define AUXADC_EN (8)
+#define BG_EN (7)
+#define BG_RST (6)
+#define BG_IBIAS_EN (5)
+#define VCM_EN (4)
+#define VCM_BUF_EN (3)
+#define ICM_PLUS_EN (2)
+#define SEL_VCMI (1)
+#define VCMI_FAST_EN (0)
+
+/* PMUR6_PMUR5 */
+#define PA_SWOCP_PD (15)
+#define PA_LDOOCP_PD (14)
+#define PA_LDO_V (11)
+#define PA_LDO_V_MASK (0x7)
+#define VCM_CAL (0)
+#define VCM_CAL_MASK (0x1F)
+
+/* PMUR8_PMUR7 */
+#define PA_OVP_V (13)
+#define PA_OVP_V_MASK (0x7)
+#define PA_OVP_542 (0) //5.48V for 2711
+#define PA_OVP_527 (1) //5.33V for 2711
+#define PA_OVP_512 (2) //5.19V for 2711
+#define PA_OVP_496 (3) //5.05V for 2711
+#define PA_OVP_481 (4) //4.91V for 2711
+#define PA_OVP_465 (5) //4.76V for 2711
+#define PA_OVP_450 (6) //4.62V for 2711
+#define PA_OVP_435 (7) //4.48V for 2711
+
+/* CCR */
+#define ADC_CLK_EN (7)
+#define ADC_CLK_RST (6)
+#define ADC_CLK_F (4)
+#define ADC_CLK_F_MASK (0x3)
+#define DAC_CLK_EN (3)
+#define DAC_CLK_F (1)
+#define DAC_CLK_F_MASK (0x3)
+#define DRV_CLK_EN (0)
+
+/* AACR2_AACR1 */
+#define ADCPGAL_BYP (14)
+#define ADCPGAL_BYP_MASK (0x3)
+#define ADCPGAL_EN (12)
+#define ADCPGAL_EN_MASK (0x3)
+#define ADCPGAR_BYP (10)
+#define ADCPGAR_BYP_MASK (0x3)
+#define ADCPGAR_EN (8)
+#define ADCPGAR_EN_MASK (0x3)
+#define ADC_IBUF_PD (7)
+#define ADC_VREF1P5 (6)
+#define ADCL_PD (5)
+#define ADCL_RST (4)
+#define ADCR_PD (3)
+#define ADCR_RST (2)
+
+/* AAICR2_AAICR1 */
+#define MIC_ADCR (15)
+#define AUXMIC_ADCR (14)
+#define HEADMIC_ADCR (13)
+#define AIL_ADCR (12)
+#define AIR_ADCR (11)
+#define MIC_ADCL (7)
+#define AUXMIC_ADCL (6)
+#define HEADMIC_ADCL (5)
+#define AIL_ADCL (4)
+#define AIR_ADCL (3)
+
+/* DACGR_DACR */
+#define DACL_EN (7)
+#define DACR_EN (6)
+#define DACBUF_I_S (5)
+
+/* DAOCR2 */
+#define ADCL_AOL (7)
+#define ADCR_AOL (6)
+#define DACL_AOL (5)
+#define DACR_AOL (4)
+#define ADCL_AOR (3)
+#define ADCR_AOR (2)
+#define DACL_AOR (1)
+#define DACR_AOR (0)
+
+/* DAOCR3_DAOCR1 */
+#define DACL_EAR (15)
+#define ADCL_P_HPL (7)
+#define ADCR_P_HPL (6)
+#define DACL_P_HPL (5)
+#define DACR_P_HPL (4)
+#define ADCL_N_HPR (3)
+#define ADCR_P_HPR (2)
+#define DACL_N_HPR (1)
+#define DACR_P_HPR (0)
+
+/* DCR2_DCR1 */
+#define PA_D_EN (15)
+#define PA_DTRI_F (13)
+#define PA_DTRI_F_MASK (0x03)
+#define PA_DEMI_EN (12)
+#define PA_SS_EN (11)
+#define PA_SS_RST (10)
+#define DRV_STOP_EN (9)
+
+#define HPL_EN (7)
+#define HPR_EN (6)
+#define EAR_EN (5)
+#define AOL_EN (4)
+#define AOR_EN (3)
+#define DIFF_EN (2)
+#define HP_VCMI_EN (1)
+
+/* DCR4_DCR3 */
+#define DRV_OCP_AOL_PD (7)
+#define DRV_OCP_AOR_PD (6)
+#define DRV_OCP_EAR_PD (5)
+#define DRV_OCP_HP_PD (4)
+
+/* DCR6_DCR5 */
+#define AUD_NG_EN (7)
+#define AUD_NG_DA_EN (6)
+#define AUD_NG_PA_EN (5)
+/* DCR8_DCR7 */
+/* HP PA V2 */
+#define V2_AUDIO_CLASSG_EN (15)
+#define V2_AUDIO_CHP_EN (14)
+#define V2_AUDIO_CHP_HPL_EN (13)
+#define V2_AUDIO_CHP_HPR_EN (12)
+#define V2_AUDIO_CHP_LMUTE (11)
+#define V2_AUDIO_CHP_RMUTE (10)
+#define V2_AUDIO_CHP_CLK_SEL (9)
+#define V2_AUDIO_CHP_OSC (6)
+#define V2_AUDIO_CHP_OSC_MASK (0x07)
+//befor BA version
+#define V2_AUDIO_CHP_LPW_MASK (0x1)
+#define V2_AUDIO_CHP_LPW (5)
+#define V2_AUDIO_CHP_MODE (4)
+//after BA version
+#define V2_AUDIO_CHP_LPW_MASK_BA (0x3)
+#define V2_AUDIO_CHP_LPW_BA (4)
+#define V2_AUDIO_CHP_MODE_BA (3)
+
+#define V2_AUDIO_CGCAL_EN (2)
+#define V2_AUDIO_CGCAL_STOP_EN (1)
+#define V2_AUDIO_CHP_OCP_PD (0)
+
+
+/* HP PA V0 */
+//after CA version
+#define AUDIO_CHP_LPW_MASK_CA (0x3)
+#define AUDIO_CHP_LPW_CA (14)
+#define AUDIO_CHP_MODE_CA (13)
+//befor CA version
+#define AUDIO_CHP_LPW_MASK (0x1)
+#define AUDIO_CHP_LPW (15)
+#define AUDIO_CHP_MODE (14)
+
+#define AUDIO_CLASSG_EN (7)
+#define AUDIO_CHP_EN (6)
+#define AUDIO_CHP_HPL_EN (5)
+#define AUDIO_CHP_HPR_EN (4)
+#define AUDIO_CHP_LMUTE (3)
+#define AUDIO_CHP_RMUTE (2)
+#define AUDIO_CHP_OSC (0)
+#define AUDIO_CHP_OSC_MASK (0x03)
+#define AUDIO_CGCAL_EN (12)
+#define AUDIO_CGCAL_STOP_EN (11)
+#define AUDIO_CHP_OCP_PD (10)
+
+/* PNRCR2_PNRCR1 */
+#define HP_POP_CTL (6)
+#define HP_POP_CTL_MASK (0x03)
+#define HP_POP_CTL_DIS (0)
+#define HP_POP_CTL_UP (1)
+#define HP_POP_CTL_DOWN (2)
+#define HP_POP_CTL_HOLD (3)
+
+#define HP_POP_STEP (3)
+#define HP_POP_STEP_MASK (0x07)
+#define HP_POP_STEP_012 (0)
+#define HP_POP_STEP_025 (1)
+#define HP_POP_STEP_05 (2)
+#define HP_POP_STEP_1 (3)
+#define HP_POP_STEP_2 (4)
+#define HP_POP_STEP_4 (5)
+#define HP_POP_STEP_8 (6)
+#define HP_POP_STEP_16 (7)
+
+/* IFR2_IFR1 */
+#define HP_POP_FLG (4)
+#define HP_POP_FLG_MASK (0x03)
+#define HP_POP_FLG_NEAR_CMP (3)
+#define OVP_FLAG (2)
+
+ /*HIB*/
+#define HIB_SBUT_MASK (0x0f)
+#define HIB_SBUT (0)
+#define SPRD_CODEC_DP_BASE (CODEC_DP_BASE)
+#define AUD_TOP_CTL (SPRD_CODEC_DP_BASE + 0x0000)
+#define AUD_AUD_CTR (SPRD_CODEC_DP_BASE + 0x0004)
+#define AUD_I2S_CTL (SPRD_CODEC_DP_BASE + 0x0008)
+#define AUD_DAC_CTL (SPRD_CODEC_DP_BASE + 0x000C)
+#define AUD_SDM_CTL0 (SPRD_CODEC_DP_BASE + 0x0010)
+#define AUD_SDM_CTL1 (SPRD_CODEC_DP_BASE + 0x0014)
+#define AUD_ADC_CTL (SPRD_CODEC_DP_BASE + 0x0018)
+#define AUD_LOOP_CTL (SPRD_CODEC_DP_BASE + 0x001C)
+#define AUD_AUD_STS0 (SPRD_CODEC_DP_BASE + 0x0020)
+#define AUD_INT_CLR (SPRD_CODEC_DP_BASE + 0x0024)
+#define AUD_INT_EN (SPRD_CODEC_DP_BASE + 0x0028)
+#define AUD_DMIC_CTL (SPRD_CODEC_DP_BASE + 0x0030)
+#define AUD_ADC1_I2S_CTL (SPRD_CODEC_DP_BASE + 0x0034)
+#define SPRD_CODEC_DP_END (SPRD_CODEC_DP_BASE + 0x0034)
+#define IS_SPRD_CODEC_DP_RANG(reg) (((reg) >= SPRD_CODEC_DP_BASE) && ((reg) < SPRD_CODEC_DP_END))
+#define SPRD_CODEC_AP_BASE (CODEC_AP_BASE)
+#define AUDIF_ENB (SPRD_CODEC_AP_BASE + 0x0000)
+#define AUDIF_OCP_OTP_TMR_CTL (SPRD_CODEC_AP_BASE + 0x0004)
+#define AUDIF_OVPTMR_SHUTDOWN_CTL (SPRD_CODEC_AP_BASE + 0x0008)
+#define AUDIF_INT_CLR (SPRD_CODEC_AP_BASE + 0x000C)
+#define AUDIF_INT_EN (SPRD_CODEC_AP_BASE + 0x0010)
+#define AUDIF_INT_RAW (SPRD_CODEC_AP_BASE + 0x0014)
+#define AUDIF_INT_MASK (SPRD_CODEC_AP_BASE + 0x0018)
+/* 0x001C ~ 0x003C is reserved for ADIE digital part */
+#define PMUR2_PMUR1 (SPRD_CODEC_AP_BASE + 0x0040)
+#define PMUR4_PMUR3 (SPRD_CODEC_AP_BASE + 0x0044)
+#define PMUR6_PMUR5 (SPRD_CODEC_AP_BASE + 0x0048)
+#define PMUR8_PMUR7 (SPRD_CODEC_AP_BASE + 0x004C)
+/* 0x0050 ~ 0x005C is reserved for analog power part */
+#define CCR (SPRD_CODEC_AP_BASE + 0x0060)
+#define AACR2_AACR1 (SPRD_CODEC_AP_BASE + 0x0064)
+#define AAICR2_AAICR1 (SPRD_CODEC_AP_BASE + 0x0068)
+#define ACGR1 (SPRD_CODEC_AP_BASE + 0x006C)
+#define ACGR3_ACGR2 (SPRD_CODEC_AP_BASE + 0x0070)
+#define DACGR_DACR (SPRD_CODEC_AP_BASE + 0x0074)
+#define DAOCR2 (SPRD_CODEC_AP_BASE + 0x0078)
+#define DAOCR3_DAOCR1 (SPRD_CODEC_AP_BASE + 0x007C)
+#define DCR2_DCR1 (SPRD_CODEC_AP_BASE + 0x0080)
+#define DCR4_DCR3 (SPRD_CODEC_AP_BASE + 0x0084)
+#define DCR6_DCR5 (SPRD_CODEC_AP_BASE + 0x0088)
+#define DCR8_DCR7 (SPRD_CODEC_AP_BASE + 0x008C)
+#define DCGR2_DCGR1 (SPRD_CODEC_AP_BASE + 0x0090)
+#define DCGR3 (SPRD_CODEC_AP_BASE + 0x0094)
+#define PNRCR2_PNRCR1 (SPRD_CODEC_AP_BASE + 0x0098)
+#define PNRCR3 (SPRD_CODEC_AP_BASE + 0x009C)
+#define HIBDR2_HIBDR1 (SPRD_CODEC_AP_BASE + 0x00A0)
+#define HIBDR3 (SPRD_CODEC_AP_BASE + 0x00A4)
+/* 0x00A8 ~ 0x00BC is reserved */
+#define IFR2_IFR1 (SPRD_CODEC_AP_BASE + 0x00C0)
+#define AUD_DANGL (SPRD_CODEC_AP_BASE + 0x00C4)
+#define AUD_DANGR (SPRD_CODEC_AP_BASE + 0x00C8)
+/* 0x00C4 ~ 0x00C8 is reserved */
+#define IFR4_IFR3 (SPRD_CODEC_AP_BASE + 0x00CC)
+/* 0x00D0 ~ 0x00D8 is reserved */
+#define SPRD_CODEC_AP_END (SPRD_CODEC_AP_BASE + 0x00D0)
+#define IS_SPRD_CODEC_AP_RANG(reg) (((reg) >= SPRD_CODEC_AP_BASE) && ((reg) < SPRD_CODEC_AP_END))
+#define SPRD_CODEC_IIS1_ID 111
+#endif /* __SPRD_CODEC_H */
diff --git a/sound/soc/sprd/codec/sprd/v4/Kconfig b/sound/soc/sprd/codec/sprd/v4/Kconfig
new file mode 100644
index 00000000..0f089aab
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v4/Kconfig
@@ -0,0 +1,15 @@
+config SND_SOC_SPRD_CODEC_V4
+ tristate # "Sprd-codec-v4"
+
+config SND_SOC_SPRD_AUDIO_USE_INTER_HP_PA_V4
+ depends on SND_SOC_SPRD_CODEC_V4
+ bool "Use spreadtrum internal HeadPhone PA(V4)"
+ help
+ Say Y if you want use sprdtrume internal headphone pa
+
+config SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ depends on SND_SOC_SPRD_CODEC_V4
+ bool "Use spreadtrum ear jack type 1 or 3"
+ help
+ Say Y if you want to use sprdtrume ear jack type 1 or 3
+
diff --git a/sound/soc/sprd/codec/sprd/v4/Makefile b/sound/soc/sprd/codec/sprd/v4/Makefile
new file mode 100644
index 00000000..6b64e9e2
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v4/Makefile
@@ -0,0 +1,7 @@
+#SPRD CODEC
+
+SPRD_SOUND_TREE := sound/soc/sprd/
+KBUILD_CFLAGS += -I$(SPRD_SOUND_TREE) -I$(SPRD_SOUND_TREE)/codec
+
+snd-soc-sprd-sprd-codec-v4-objs := sprd-codec.o sprd-audio-power.o
+obj-$(CONFIG_SND_SOC_SPRD_CODEC_V4) += snd-soc-sprd-sprd-codec-v4.o
diff --git a/sound/soc/sprd/codec/sprd/v4/sprd-audio-power.c b/sound/soc/sprd/codec/sprd/v4/sprd-audio-power.c
new file mode 100644
index 00000000..d443c67f
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v4/sprd-audio-power.c
@@ -0,0 +1,672 @@
+/*
+ * sound/soc/sprd/codec/sprd/v4/sprd-audio-power.c
+ *
+ * SPRD-AUDIO-POWER -- SpreadTrum intergrated audio power supply.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "sprd-asoc-debug.h"
+#define pr_fmt(fmt) pr_sprd_fmt("POWER") fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#include <soc/sprd/sprd-audio.h>
+#include "sprd-audio-power.h"
+#include "sprd-asoc-common.h"
+
+#ifdef CONFIG_AUDIO_POWER_ALLOW_UNSUPPORTED
+#define UNSUP_MASK 0x0000
+#else
+#define UNSUP_MASK 0x8000
+#endif
+
+#define UNSUP(x) (UNSUP_MASK | (x))
+#define IS_UNSUP(x) (UNSUP_MASK & (x))
+#define LDO_MV(x) (~UNSUP_MASK & (x))
+
+struct sprd_audio_power_info {
+ int id;
+ /* enable/disable register information */
+ int en_reg;
+ int en_bit;
+ int (*power_enable) (void);
+ int (*power_disable) (void);
+ /* configure deepsleep cut off or not */
+ int (*sleep_ctrl) (void *, int);
+ /* voltage level register information */
+ int v_reg;
+ int v_mask;
+ int v_shift;
+ int v_table_len;
+ const u16 *v_table;
+ int min_uV;
+ int max_uV;
+
+ /* unit: us */
+ int on_delay;
+ int off_delay;
+
+ /* used by regulator core */
+ struct regulator_desc desc;
+
+ void *data;
+};
+
+#define SPRD_AUDIO_POWER_LDO_LOW(label, r_supply, d_id, \
+e_reg, e_bit, p_enable, p_disable, s_ctrl, \
+reg, mask, shift, table, table_size, d_on_delay, d_off_delay) \
+static struct regulator_consumer_supply label[] = { \
+ { .supply = #label },\
+}; \
+static struct regulator_init_data label##_data = { \
+ .supply_regulator = r_supply, \
+ .constraints = { \
+ .name = "SRG_" #label, \
+ }, \
+ .num_consumer_supplies = ARRAY_SIZE(label), \
+ .consumer_supplies = label, \
+}; \
+static struct sprd_audio_power_info SPRD_AUDIO_POWER_INFO_##label = { \
+ .id = d_id, \
+ .en_reg = e_reg, \
+ .en_bit = e_bit, \
+ .power_enable = p_enable, \
+ .power_disable = p_disable, \
+ .sleep_ctrl = s_ctrl, \
+ .v_reg = reg, \
+ .v_mask = mask, \
+ .v_shift = shift, \
+ .v_table_len = table_size, \
+ .v_table = table, \
+ .on_delay = d_on_delay, \
+ .off_delay = d_off_delay, \
+ .desc = { \
+ .name = #label, \
+ .id = SPRD_AUDIO_POWER_##label, \
+ .n_voltages = table_size, \
+ .ops = &sprd_audio_power_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+}
+
+#define SPRD_AUDIO_POWER_LDO(label, r_supply, id, en_reg, en_bit, sleep_ctrl, \
+ v_reg, v_mask, v_shift, v_table, on_delay, off_delay) \
+SPRD_AUDIO_POWER_LDO_LOW(label, r_supply, id, en_reg, en_bit, 0, 0, sleep_ctrl, \
+ v_reg, v_mask, v_shift, v_table, ARRAY_SIZE(v_table), on_delay, off_delay)
+
+#define SPRD_AUDIO_POWER_SIMPLE_LDO(label, r_supply, id, en_reg, en_bit, sleep_ctrl, \
+ on_delay, off_delay) \
+SPRD_AUDIO_POWER_LDO_LOW(label, r_supply, id, en_reg, en_bit, 0, 0, sleep_ctrl, \
+ 0, 0, 0, 0, 0, on_delay, off_delay)
+
+#define SPRD_AUDIO_POWER_REG_LDO(label, id, power_enable, power_disable) \
+SPRD_AUDIO_POWER_LDO_LOW(label, 0, id, 0, 0, power_enable, power_disable, \
+ 0, 0, 0, 0, 0, 0, 0, 0)
+
+static inline int sprd_power_read(struct sprd_audio_power_info *info, int reg)
+{
+ return arch_audio_power_read(reg);
+}
+
+static inline int
+sprd_power_write(struct sprd_audio_power_info *info, int reg, int val)
+{
+ int ret = 0;
+ ret = arch_audio_power_write(reg, val);
+ sp_asoc_pr_reg("[0x%04x] W:[0x%08x] R:[0x%08x]\n", reg & 0xFFFF, val,
+ arch_audio_power_read(reg));
+ return ret;
+}
+
+static inline int
+sprd_power_u_bits(struct sprd_audio_power_info *info,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ int ret = 0;
+ ret = arch_audio_power_write_mask(reg, value, mask);
+ sp_asoc_pr_reg("[0x%04x] U:[0x%08x] R:[0x%08x] MASK:0x%x NAME:%s\n", reg & 0xFFFF, value,
+ arch_audio_power_read(reg), info->v_mask, info->desc.name);
+ return ret;
+}
+
+static int sprd_audio_power_is_enabled(struct regulator_dev *rdev)
+{
+ int is_enable;
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+
+ sp_asoc_pr_dbg("%s Is Enabled\n", rdev->desc->name);
+
+ if (info->en_reg) {
+ is_enable = sprd_power_read(info, info->en_reg) & info->en_bit;
+ } else {
+ is_enable = info->en_bit;
+ }
+
+ return ! !is_enable;
+}
+
+static int audio_power_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int vsel;
+
+ BUG_ON(!info->v_table);
+
+ for (vsel = 0; vsel < info->v_table_len; vsel++) {
+ int mV = info->v_table[vsel];
+ int uV;
+
+ if (IS_UNSUP(mV))
+ continue;
+ uV = LDO_MV(mV) * 1000;
+
+ /* use the first in-range value */
+ if (min_uV <= uV && uV <= max_uV) {
+ sp_asoc_pr_dbg("%s: %dus\n", __func__, uV);
+ sprd_power_u_bits(info,
+ info->v_reg,
+ info->v_mask << info->v_shift,
+ vsel << info->v_shift);
+
+ return vsel;
+ }
+ }
+ return -EDOM;
+}
+
+static int sprd_audio_power_enable(struct regulator_dev *rdev)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Enable\n", rdev->desc->name);
+
+ if (info->en_reg) {
+ ret = sprd_power_u_bits(info,
+ info->en_reg, info->en_bit,
+ info->en_bit);
+ } else {
+ if (info->power_enable)
+ ret = info->power_enable();
+ info->en_bit = 1;
+ }
+
+ udelay(info->on_delay);
+
+ if (info->v_table) {
+ if (info->min_uV || info->max_uV)
+ audio_power_set_voltage(rdev, info->min_uV,
+ info->max_uV);
+ }
+ return ret;
+}
+
+static int sprd_audio_power_disable(struct regulator_dev *rdev)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Disable\n", rdev->desc->name);
+
+ if (info->en_reg) {
+ ret = sprd_power_u_bits(info, info->en_reg, info->en_bit, 0);
+ } else {
+ if (info->power_disable)
+ ret = info->power_disable();
+ info->en_bit = 0;
+ }
+
+ udelay(info->off_delay);
+
+ return ret;
+}
+
+static int sprd_audio_power_get_status(struct regulator_dev *rdev)
+{
+ sp_asoc_pr_dbg("%s Get Status\n", rdev->desc->name);
+
+ if (!sprd_audio_power_is_enabled(rdev))
+ return REGULATOR_STATUS_OFF;
+ return REGULATOR_STATUS_NORMAL;
+}
+
+static int sprd_audio_power_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ int ret = 0;
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+
+ sp_asoc_pr_dbg("%s Set Mode 0x%x\n", rdev->desc->name, mode);
+
+ if (!info->sleep_ctrl) {
+ return ret;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ ret = info->sleep_ctrl(info, 0);
+ break;
+ case REGULATOR_MODE_STANDBY:
+ ret = info->sleep_ctrl(info, 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static const u16 VCOM_VSEL_table[] = {
+ 2800, 2900, 3000, 3100,
+ 3200, 3300, 3400, 3600,
+};
+
+static const u16 VPA_VSEL_table[] = {
+ 2900, 3100, 3200, 3300,
+ 3400, 3500, 3600, 3800,
+};
+
+static const u16 VMIC_VSEL_table[] = {
+ 2200, 2600, 2800, 3000,
+};
+
+static const u16 VBG_IBAIS_VSEL_table[] = {
+ 150, 200, 250, 300,
+};
+
+static const u16 HIB_VSEL_table[] = {
+ 1000, 950, 900, 850, 800, 750,
+ 700, 650, 600, 550, 500, 450, 400,
+};
+
+static const u16 VCGLDO_VSEL_table[] = {
+ 1600, 1700, 1800, 1900,
+ 2000, 2100, 2200, 2300,
+ 2400, 2500, 2600, 2700,
+ 2800, 2900, 3000, 3100,
+ 3200, 3300, 3400,
+};
+
+static int sprd_audio_power_list_voltage(struct regulator_dev *rdev,
+ unsigned index)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int mV;
+
+ sp_asoc_pr_dbg("%s List Voltage\n", rdev->desc->name);
+
+ if (!info->v_table) {
+ return 0;
+ }
+
+ if (index >= info->v_table_len) {
+ return 0;
+ }
+
+ mV = info->v_table[index];
+ return IS_UNSUP(mV) ? 0 : (LDO_MV(mV) * 1000);
+}
+
+static int
+sprd_audio_power_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned *selector)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int vsel;
+
+ sp_asoc_pr_info("%s Set Voltage %d--%d\n", rdev->desc->name, min_uV,
+ max_uV);
+
+ if (!info->v_table) {
+ return 0;
+ }
+ info->min_uV = min_uV;
+ info->max_uV = max_uV;
+
+ vsel = audio_power_set_voltage(rdev, min_uV, max_uV);
+ if (vsel != -EDOM)
+ *selector = vsel;
+
+ return vsel;
+}
+
+static int sprd_audio_power_get_voltage(struct regulator_dev *rdev)
+{
+ struct sprd_audio_power_info *info = rdev_get_drvdata(rdev);
+ int vsel;
+
+ sp_asoc_pr_dbg("%s Get Voltage\n", rdev->desc->name);
+
+ if (!info->v_table) {
+ return 0;
+ }
+
+ vsel = (sprd_power_read(info, info->v_reg) & info->v_mask)
+ >> info->v_shift;
+ return LDO_MV(info->v_table[vsel]) * 1000;
+}
+
+static struct regulator_ops sprd_audio_power_ops = {
+ .list_voltage = sprd_audio_power_list_voltage,
+
+ .set_voltage = sprd_audio_power_set_voltage,
+ .get_voltage = sprd_audio_power_get_voltage,
+
+ .enable = sprd_audio_power_enable,
+ .disable = sprd_audio_power_disable,
+ .is_enabled = sprd_audio_power_is_enabled,
+
+ .set_mode = sprd_audio_power_set_mode,
+
+ .get_status = sprd_audio_power_get_status,
+};
+
+static int sprd_audio_power_sleep_ctrl(void *data, int en)
+{
+ struct sprd_audio_power_info *info = data;
+ int reg, mask, value;
+ switch (info->desc.id) {
+ case SPRD_AUDIO_POWER_HEADMICBIAS:
+ reg = ANA_PMU0;
+ mask = BIT(HEADMIC_SLEEP_EN);
+ value = en ? mask : 0;
+ return sprd_power_u_bits(info, reg, mask, value);
+ break;
+ default:
+ BUG_ON(0);
+ }
+ return 0;
+}
+
+static inline int vreg_enable(void)
+{
+ int ret = 0;
+ static int i = 0;
+ arch_audio_codec_switch(AUDIO_TO_ARM_CTRL);
+ arch_audio_codec_analog_reg_enable();
+ if (i == 0) {
+ arch_audio_codec_analog_reset();
+ /*set initial value from xun.zhang*/
+ /* changed by jian.chen */
+ /*
+ arch_audio_codec_write_mask(PMUR4_PMUR3, BIT(ICM_PLUS_EN),
+ BIT(ICM_PLUS_EN));
+ */
+ i = 1;
+ }
+ return ret;
+}
+
+static inline int vreg_disable(void)
+{
+ /*arch_audio_codec_reset();*/
+ arch_audio_codec_analog_reg_disable();
+ return 0;
+}
+
+/*
+ * We list regulators here if systems need some level of
+ * software control over them after boot.
+ */
+SPRD_AUDIO_POWER_REG_LDO(VREG, 1, vreg_enable, vreg_disable);
+SPRD_AUDIO_POWER_SIMPLE_LDO(VB, "VREG", 2, ANA_PMU0, BIT(VB_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(BG, "VB", 3, ANA_PMU1, BIT(BG_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_LDO(BG_IBIAS, "BG", 4, ANA_PMU1, BIT(BG_IBIAS_EN), NULL,
+ ANA_PMU3, BG_I_MASK, BG_I, VBG_IBAIS_VSEL_table, 0, 0);
+SPRD_AUDIO_POWER_LDO(VCOM, "BG_IBIAS", 5, ANA_PMU1, BIT(VCM_EN), NULL,
+ ANA_PMU2, VCM_V_MASK, VCM_V, VCOM_VSEL_table, 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(VCOM_BUF, "VCOM", 6, ANA_PMU1, BIT(VCM_BUF_EN),
+ NULL, 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(VBO, "VB", 7, ANA_PMU0, BIT(VBO_EN), NULL, 0, 20*1000);
+SPRD_AUDIO_POWER_LDO(VMICBIAS, "VCOM", 8, 0, 0, NULL,
+ ANA_PMU2, MICBIAS_V_MASK, MICBIAS_V, VMIC_VSEL_table, 0,
+ 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(MICBIAS, "VMICBIAS", 9, ANA_PMU1,
+ BIT(MICBIAS_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_SIMPLE_LDO(AUXMICBIAS, "VMICBIAS", 10, ANA_PMU1,
+ BIT(AUXMICBIAS_EN), NULL, 0, 0);
+SPRD_AUDIO_POWER_LDO(HEADMICBIAS, "VMICBIAS", 11, ANA_PMU0,
+ BIT(HEADMICBIAS_EN), sprd_audio_power_sleep_ctrl,
+ ANA_HDT0, HIB_SBUT_MASK, HIB_SBUT, HIB_VSEL_table, 0,
+ 0);
+SPRD_AUDIO_POWER_LDO(CGLDO, "VREG", 12, ANA_PMU0,
+ BIT(LDOCG_EN), NULL,
+ ANA_PMU3, CG_LDO_V_MASK, CG_LDO_V, VCGLDO_VSEL_table, 0,
+ 0);
+
+
+#define SPRD_OF_MATCH(comp, label) \
+ { \
+ .compatible = comp, \
+ .data = &SPRD_AUDIO_POWER_INFO_##label, \
+ }
+
+static const struct of_device_id sprd_audio_power_of_match[] = {
+ SPRD_OF_MATCH("sp,audio-vreg", VREG),
+ SPRD_OF_MATCH("sp,audio-vb", VB),
+ SPRD_OF_MATCH("sp,audio-bg", BG),
+ SPRD_OF_MATCH("sp,audio-bg-ibias", BG_IBIAS),
+ SPRD_OF_MATCH("sp,audio-vcom", VCOM),
+ SPRD_OF_MATCH("sp,audio-vcom-buf", VCOM_BUF),
+ SPRD_OF_MATCH("sp,audio-vbo", VBO),
+ SPRD_OF_MATCH("sp,audio-vmicbias", VMICBIAS),
+ SPRD_OF_MATCH("sp,audio-micbias", MICBIAS),
+ SPRD_OF_MATCH("sp,audio-auxmicbias", AUXMICBIAS),
+ SPRD_OF_MATCH("sp,audio-headmicbias", HEADMICBIAS),
+ SPRD_OF_MATCH("sp,audio-classgldo", CGLDO),
+};
+
+static int sprd_audio_power_probe(struct platform_device *pdev)
+{
+ int i, id;
+ struct sprd_audio_power_info *info;
+ const struct sprd_audio_power_info *template;
+ struct regulator_init_data *initdata;
+ struct regulation_constraints *c;
+ struct regulator_dev *rdev;
+ struct regulator_config config = { };
+ int min_index = 0;
+ int max_index = 0;
+
+ id = pdev->id;
+
+ sp_asoc_pr_info("Probe %d\n", id);
+
+ initdata = pdev->dev.platform_data;
+ for (i = 0, template = NULL; i < ARRAY_SIZE(sprd_audio_power_of_match);
+ i++) {
+ template = sprd_audio_power_of_match[i].data;
+ if (!template || template->desc.id != id)
+ continue;
+ break;
+ }
+
+ if (!template)
+ return -ENODEV;
+
+ if (!initdata)
+ return -EINVAL;
+
+ info = kmemdup(template, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ /* Constrain board-specific capabilities according to what
+ * this driver and the chip itself can actually do.
+ */
+ c = &initdata->constraints;
+ c->valid_modes_mask |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY;
+ c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS;
+
+ if (info->v_table) {
+ for (i = 0; i < info->v_table_len; i++) {
+ if (info->v_table[i] < info->v_table[min_index])
+ min_index = i;
+ if (info->v_table[i] > info->v_table[max_index])
+ max_index = i;
+ }
+ c->min_uV = LDO_MV((info->v_table)[min_index]) * 1000;
+ c->max_uV = LDO_MV((info->v_table)[max_index]) * 1000;
+ sp_asoc_pr_info("min_uV:%d, max_uV:%d\n", c->min_uV, c->max_uV);
+ }
+ info->min_uV = 0;
+ info->max_uV = 0;
+
+ config.dev = &pdev->dev;
+ config.init_data = initdata;
+ config.driver_data = info;
+ config.of_node = pdev->dev.of_node;
+
+ rdev = regulator_register(&info->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "ERR:Can't Register %s, %ld\n",
+ info->desc.name, PTR_ERR(rdev));
+ kfree(info);
+ return PTR_ERR(rdev);
+ }
+ sp_asoc_pr_info("Register %s Success!\n", info->desc.name);
+
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static int sprd_audio_power_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct twlreg_info *info = rdev->reg_data;
+
+ regulator_unregister(platform_get_drvdata(pdev));
+ kfree(info);
+ return 0;
+}
+
+static struct platform_driver sprd_audio_power_driver = {
+ .driver = {
+ .name = "sprd-audio-power",
+ .owner = THIS_MODULE,
+ },
+ .probe = sprd_audio_power_probe,
+ .remove = sprd_audio_power_remove,
+};
+
+static int __init sprd_audio_power_regulator_init(void)
+{
+ return platform_driver_register(&sprd_audio_power_driver);
+}
+
+//postcore_initcall(sprd_audio_power_regulator_init);
+module_init(sprd_audio_power_regulator_init);
+
+static void __exit sprd_audio_power_regulator_exit(void)
+{
+ platform_driver_unregister(&sprd_audio_power_driver);
+}
+
+module_exit(sprd_audio_power_regulator_exit);
+
+MODULE_DESCRIPTION("SPRD audio power regulator driver");
+MODULE_AUTHOR("Jian Chen <jian.chen@spreadtrum.com>");
+MODULE_ALIAS("platform:sprd-audio-power");
+MODULE_LICENSE("GPL");
+
+#define SPRD_AUDIO_DEVICE(label) \
+{ \
+ .name = "sprd-audio-power", \
+ .id = SPRD_AUDIO_POWER_##label, \
+ .dev = { \
+ .platform_data = &label##_data, \
+ }, \
+}
+
+static struct platform_device sprd_audio_regulator_devices[] = {
+ SPRD_AUDIO_DEVICE(VREG),
+ SPRD_AUDIO_DEVICE(VB),
+ SPRD_AUDIO_DEVICE(BG),
+ SPRD_AUDIO_DEVICE(BG_IBIAS),
+ SPRD_AUDIO_DEVICE(VCOM),
+ SPRD_AUDIO_DEVICE(VCOM_BUF),
+ SPRD_AUDIO_DEVICE(VBO),
+ SPRD_AUDIO_DEVICE(VMICBIAS),
+ SPRD_AUDIO_DEVICE(MICBIAS),
+ SPRD_AUDIO_DEVICE(AUXMICBIAS),
+ SPRD_AUDIO_DEVICE(HEADMICBIAS),
+ SPRD_AUDIO_DEVICE(CGLDO),
+};
+
+#ifdef CONFIG_OF
+static int sprd_audio_power_version(void)
+{
+ struct device_node *node = of_find_node_by_path("/sprd-audio-devices");
+ struct device_node *child;
+ if (node) {
+ u32 val;
+ for_each_child_of_node(node, child) {
+ if (!of_property_read_u32
+ (child, "sprd,audio_power_ver", &val)) {
+ sp_asoc_pr_dbg
+ ("Configure Audio Power Version is %d\n",
+ val);
+ return val;
+ }
+ }
+ pr_err("ERR:Not found sprd,audio_power_ver!\n");
+ } else {
+ pr_err("ERR:No sprd-audio-devices Node!\n");
+ }
+ return 0;
+}
+#else
+int sprd_audio_power_version(void)
+ __attribute__ ((weak, alias("__sprd_audio_power_version")));
+
+static int __sprd_audio_power_version(void)
+{
+ return 4;
+}
+#endif
+
+static int sprd_audio_power_add_devices(void)
+{
+ int ret = 0;
+ int i;
+ if (4 != sprd_audio_power_version()) {
+ return 0;
+ }
+ for (i = 0; i < ARRAY_SIZE(sprd_audio_regulator_devices); i++) {
+ ret =
+ platform_device_register(&sprd_audio_regulator_devices[i]);
+ if (ret < 0) {
+ pr_err("ERR:Add Regulator %d Failed %d\n",
+ sprd_audio_regulator_devices[i].id, ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int __init sprd_audio_power_init(void)
+{
+ return sprd_audio_power_add_devices();
+}
+
+postcore_initcall(sprd_audio_power_init);
diff --git a/sound/soc/sprd/codec/sprd/v4/sprd-audio-power.h b/sound/soc/sprd/codec/sprd/v4/sprd-audio-power.h
new file mode 100755
index 00000000..330d92d0
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v4/sprd-audio-power.h
@@ -0,0 +1,36 @@
+/*
+ * sound/soc/sprd/codec/sprd/v4/sprd-audio-power.h
+ *
+ * SPRD-AUDIO-POWER -- SpreadTrum intergrated audio power supply.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * 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 __SPRD_AUDIO_POWER_H
+#define __SPRD_AUDIO_POWER_H
+
+#include "sprd-codec.h"
+
+#define SPRD_AUDIO_POWER_VREG 1
+#define SPRD_AUDIO_POWER_VB 2
+#define SPRD_AUDIO_POWER_BG 3
+#define SPRD_AUDIO_POWER_BG_IBIAS 4
+#define SPRD_AUDIO_POWER_VCOM 5
+#define SPRD_AUDIO_POWER_VCOM_BUF 6
+#define SPRD_AUDIO_POWER_VBO 7
+#define SPRD_AUDIO_POWER_VMICBIAS 8
+#define SPRD_AUDIO_POWER_MICBIAS 9
+#define SPRD_AUDIO_POWER_AUXMICBIAS 10
+#define SPRD_AUDIO_POWER_HEADMICBIAS 11
+#define SPRD_AUDIO_POWER_CGLDO 12
+
+#endif
diff --git a/sound/soc/sprd/codec/sprd/v4/sprd-codec.c b/sound/soc/sprd/codec/sprd/v4/sprd-codec.c
new file mode 100644
index 00000000..e11b20c9
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v4/sprd-codec.c
@@ -0,0 +1,4943 @@
+/*
+ * sound/soc/sprd/codec/sprd/v4/sprd-codec.c
+ *
+ * SPRD-CODEC -- SpreadTrum Tiger intergrated codec.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "sprd-asoc-debug.h"
+#define pr_fmt(fmt) pr_sprd_fmt("CDCV4") fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/stat.h>
+#include <linux/atomic.h>
+#include <linux/regulator/consumer.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/power_supply.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/sprd-audio-hook.h>
+
+#include <soc/sprd/sprd-audio.h>
+#include <soc/sprd/globalregs.h>
+#include "sprd-codec.h"
+#include "sprd-asoc-common.h"
+#include <soc/sprd/arch_misc.h>
+
+#ifdef CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE
+#if (CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE != 32000) && (CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE != 48000) && (CONFIG_SND_SOC_CODEC_SRC_SAMPLE_RATE != 48000)
+#error "We hope the CODEC sample rate is 32KHz or 48KHz!"
+#endif
+#define SRC_SUPPORT_RATE (SNDRV_PCM_RATE_44100)
+#else /* !CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE */
+#define SRC_SUPPORT_RATE (0)
+#endif
+
+#ifdef CONFIG_INPUT_HEADSET_SPRD_SC2723
+int hp_register_notifier(struct notifier_block *nb);
+int hp_unregister_notifier(struct notifier_block *nb);
+int get_hp_plug_state(void);
+#endif
+
+#define SOC_REG(r) ((unsigned short)(r))
+#define FUN_REG(f) ((unsigned short)(-((f) + 1)))
+#define ID_FUN(id, lr) ((unsigned short)(((id) << 1) | (lr)))
+
+#define SPRD_CODEC_AP_BASE_HI (SPRD_CODEC_AP_BASE & 0xFFFF0000)
+#define SPRD_CODEC_DP_BASE_HI (SPRD_CODEC_DP_BASE & 0xFFFF0000)
+
+enum {
+ SPRD_CODEC_HP_PA_ORDER = 100,
+ SPRD_CODEC_HP_SWITCH_ORDER = 101,
+ SPRD_CODEC_ANA_MIXER_ORDER = 102, /*must be later with switch order*/
+ SPRD_CODEC_HP_PA_POST_ORDER = 103,
+ SPRD_CODEC_HP_POP_ORDER = 104,
+ SPRD_CODEC_PA_ORDER = 104, /*last power up, later with ANA_MIXER order*/
+ SPRD_CODEC_CG_PGA_ORDER
+};
+
+enum {
+ SPRD_CODEC_AILADCL_SET = 0,
+ SPRD_CODEC_AILADCR_SET,
+ SPRD_CODEC_AIRADCL_SET,
+ SPRD_CODEC_AIRADCR_SET,
+ SPRD_CODEC_LINEIN_END,
+};
+
+
+enum {
+ SPRD_CODEC_PLAYBACK,
+ SPRD_CODEC_CAPTRUE,
+ SPRD_CODEC_CAPTRUE1,
+ SPRD_CODEC_CHAN_MAX,
+};
+
+static const char *sprd_codec_chan_name[SPRD_CODEC_CHAN_MAX] = {
+ "DAC",
+ "ADC",
+ "ADC1",
+};
+
+static inline const char *sprd_codec_chan_get_name(int chan_id)
+{
+ return sprd_codec_chan_name[chan_id];
+}
+
+enum {
+ SPRD_CODEC_PGA_START = 0,
+ SPRD_CODEC_PGA_SPKL = SPRD_CODEC_PGA_START,
+ SPRD_CODEC_PGA_SPKR,
+ SPRD_CODEC_PGA_HPL,
+ SPRD_CODEC_PGA_HPR,
+ SPRD_CODEC_PGA_EAR,
+ SPRD_CODEC_PGA_ADCL,
+ SPRD_CODEC_PGA_ADCR,
+ SPRD_CODEC_PGA_DACL,
+ SPRD_CODEC_PGA_DACR,
+ SPRD_CODEC_PGA_MIC,
+ SPRD_CODEC_PGA_AUXMIC,
+ SPRD_CODEC_PGA_HEADMIC,
+ SPRD_CODEC_PGA_AIL,
+ SPRD_CODEC_PGA_AIR,
+ SPRD_CODEC_PGA_CG_HPL_1,
+ SPRD_CODEC_PGA_CG_HPR_1,
+ SPRD_CODEC_PGA_CG_HPL_2,
+ SPRD_CODEC_PGA_CG_HPR_2,
+
+ SPRD_CODEC_PGA_END
+};
+
+#define GET_PGA_ID(x) ((x)-SPRD_CODEC_PGA_START)
+#define SPRD_CODEC_PGA_MAX (SPRD_CODEC_PGA_END - SPRD_CODEC_PGA_START)
+static const char *sprd_codec_pga_debug_str[SPRD_CODEC_PGA_MAX] = {
+ "SPKL",
+ "SPKR",
+ "HPL",
+ "HPR",
+ "EAR",
+ "ADCL",
+ "ADCR",
+ "DACL",
+ "DACR",
+ "MIC",
+ "AUXMIC",
+ "HEADMIC",
+ "AIL",
+ "AIR",
+ "CG_HPL_1",
+ "CG_HPR_1",
+ "CG_HPL_2",
+ "CG_HPR_2",
+};
+
+typedef int (*sprd_codec_pga_set) (struct snd_soc_codec * codec, int pgaval);
+
+struct sprd_codec_pga {
+ sprd_codec_pga_set set;
+ int min;
+};
+
+struct sprd_codec_pga_op {
+ int pgaval;
+ sprd_codec_pga_set set;
+};
+
+typedef int (*sprd_codec_switch_set) (struct snd_soc_codec * codec, int on);
+struct sprd_codec_switch{
+ sprd_codec_switch_set set;
+ int min;
+};
+
+struct sprd_codec_switch_op {
+ int on;
+ sprd_codec_switch_set set;
+};
+
+
+enum {
+ SPRD_CODEC_LEFT = 0,
+ SPRD_CODEC_RIGHT = 1,
+};
+
+enum {
+ SPRD_CODEC_MIXER_START = SPRD_CODEC_PGA_END + 20,
+ SPRD_CODEC_AIL = SPRD_CODEC_MIXER_START,
+ SPRD_CODEC_AIR,
+ SPRD_CODEC_MAIN_MIC,
+ SPRD_CODEC_AUX_MIC,
+ SPRD_CODEC_HP_MIC,
+ SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_HP_DACL = SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_HP_DACR,
+ SPRD_CODEC_HP_DAC_MIXER_MAX,
+ SPRD_CODEC_HP_ADCL = SPRD_CODEC_HP_DAC_MIXER_MAX,
+ SPRD_CODEC_HP_ADCR,
+ SPRD_CODEC_HP_MIXER_MAX,
+ SPRD_CODEC_SPK_DACL = SPRD_CODEC_HP_MIXER_MAX,
+ SPRD_CODEC_SPK_DACR,
+ SPRD_CODEC_SPK_DAC_MIXER_MAX,
+ SPRD_CODEC_SPK_ADCL = SPRD_CODEC_SPK_DAC_MIXER_MAX,
+ SPRD_CODEC_SPK_ADCR,
+ SPRD_CODEC_SPK_MIXER_MAX,
+ SPRD_CODEC_EAR_DACL = SPRD_CODEC_SPK_MIXER_MAX,
+ SPRD_CODEC_EAR_MIXER_MAX,
+
+ SPRD_CODEC_MIXER_END
+};
+
+#define GET_MIXER_ID(x) ((unsigned short)(x)-ID_FUN(SPRD_CODEC_MIXER_START, SPRD_CODEC_LEFT))
+#define SPRD_CODEC_MIXER_MAX (ID_FUN(SPRD_CODEC_MIXER_END, SPRD_CODEC_LEFT) -ID_FUN(SPRD_CODEC_MIXER_START, SPRD_CODEC_LEFT))
+static const char *sprd_codec_mixer_debug_str[SPRD_CODEC_MIXER_MAX] = {
+ "AIL->ADCL",
+ "AIL->ADCR",
+ "AIR->ADCL",
+ "AIR->ADCR",
+ "MAIN MIC->ADCL",
+ "MAIN MIC->ADCR",
+ "AUX MIC->ADCL",
+ "AUX MIC->ADCR",
+ "HP MIC->ADCL",
+ "HP MIC->ADCR",
+ "DACL->HPL",
+ "DACL->HPR",
+ "DACR->HPL",
+ "DACR->HPR",
+ "ADCL->HPL",
+ "ADCL->HPR",
+ "ADCR->HPL",
+ "ADCR->HPR",
+ "DACL->SPKL",
+ "DACL->SPKR",
+ "DACR->SPKL",
+ "DACR->SPKR",
+ "ADCL->SPKL",
+ "ADCL->SPKR",
+ "ADCR->SPKL",
+ "ADCR->SPKR",
+ "DACL->EAR",
+ "DACR->EAR(bug)"
+};
+
+#define IS_SPRD_CODEC_MIXER_RANG(reg) (((reg) >= ID_FUN(SPRD_CODEC_MIXER_START, SPRD_CODEC_LEFT)) && ((reg) <= ID_FUN(SPRD_CODEC_MIXER_END, SPRD_CODEC_LEFT)))
+#define IS_SPRD_CODEC_PGA_RANG(reg) (((reg) >= SPRD_CODEC_PGA_START) && ((reg) <= SPRD_CODEC_PGA_END))
+#define IS_SPRD_CODEC_MIC_BIAS_RANG(reg) (((reg) >= SPRD_CODEC_MIC_BIAS_START) && ((reg) <= SPRD_CODEC_MIC_BIAS_END))
+#define IS_SPRD_CODEC_SWITCH_RANG(reg) (((reg) >= SPRD_CODEC_SWITCH_START) && ((reg) <= SPRD_CODEC_SWITCH_END))
+#define IS_SPRD_CODEC_ADC_LOOP_RANG(reg) (((reg) >= SPRD_CODEC_ADC_LOOP_START) && ((reg) <= SPRD_CODEC_ADC_LOOP_END))
+
+typedef int (*sprd_codec_mixer_set) (struct snd_soc_codec * codec, int on);
+struct sprd_codec_mixer {
+ int on;
+ sprd_codec_mixer_set set;
+};
+
+extern uint32_t sprd_get_vbat_voltage(void);
+
+uint32_t sprd_get_vbat_voltage(void)
+ __attribute__ ((weak, alias("__sprd_get_vbat_voltage")));
+static uint32_t __sprd_get_vbat_voltage(void)
+{
+ pr_err("ERR: Can't get vbat!\n");
+ return 3800;
+}
+
+struct sprd_codec_ldo_v_map {
+ int ldo_v_level;
+ int volt;
+};
+
+const static struct sprd_codec_ldo_v_map ldo_v_map[] = {
+ /*{ , 3400}, */
+ {LDO_V_29, 3500},
+ {LDO_V_30, 3600},
+ {LDO_V_31, 3700},
+ {LDO_V_31, 3800},
+ {LDO_V_31, 3900},
+ {LDO_V_31, 4000},
+ {LDO_V_31, 4100},
+ {LDO_V_31, 4300},
+};
+
+struct sprd_codec_ldo_cfg {
+ const struct sprd_codec_ldo_v_map *v_map;
+ int v_map_size;
+};
+
+struct sprd_codec_inter_pa {
+ /* FIXME little endian */
+ u32 LDO_V_sel:4;
+ u32 DTRI_F_sel:4;
+ u32 is_DEMI_mode:1;
+ u32 is_classD_mode:1;
+ u32 is_LDO_mode:1;
+ u32 is_auto_LDO_mode:1;
+ u32 RESV:20;
+};
+
+struct sprd_codec_pa_setting {
+ union {
+ struct sprd_codec_inter_pa setting;
+ u32 value;
+ };
+ int set;
+};
+
+struct sprd_codec_inter_hp_pa {
+ /* FIXME little endian */
+ u32 class_g_osc:2;
+ u32 class_g_mode:2;
+ u32 class_g_low_power:2;
+ u32 class_g_cgcal:2;
+ u32 class_g_pa_en_delay_10ms:4;
+ u32 class_g_hp_switch_delay_10ms:4;
+ u32 class_g_hp_on_delay_20ms:4;
+/*
+ u32 class_g_unmute_delay_100ms:3;
+ u32 class_g_cgcal_delay_10ms:3;
+ u32 class_g_close_delay_10ms:2;
+ u32 class_g_open_delay_10ms:4;
+ u32 class_g_vdd_delay_10ms:6;
+ u32 class_g_chp_delay_30ms:4;
+ u32 class_g_all_close_delay_100ms:2;
+ //u32 RESV:0;
+*/
+};
+
+struct sprd_codec_hp_pa_setting {
+ union {
+ struct sprd_codec_inter_hp_pa setting;
+ u32 value;
+ };
+ int set;
+};
+
+enum {
+ SPRD_CODEC_MIC_BIAS_START =
+ ID_FUN(SPRD_CODEC_MIXER_END, SPRD_CODEC_LEFT) + 100,
+ SPRD_CODEC_MIC_BIAS = SPRD_CODEC_MIC_BIAS_START,
+ SPRD_CODEC_AUXMIC_BIAS,
+ SPRD_CODEC_HEADMIC_BIAS,
+
+ SPRD_CODEC_MIC_BIAS_END
+};
+
+#define GET_MIC_BIAS_ID(x) ((x)-SPRD_CODEC_MIC_BIAS_START)
+#define SPRD_CODEC_MIC_BIAS_MAX (SPRD_CODEC_MIC_BIAS_END -SPRD_CODEC_MIC_BIAS_START)
+static const char *mic_bias_name[SPRD_CODEC_MIC_BIAS_MAX] = {
+ "Mic Bias",
+ "AuxMic Bias",
+ "HeadMic Bias",
+};
+enum {
+ SPRD_CODEC_SWITCH_START = SPRD_CODEC_MIC_BIAS_END + 20,
+ SPRD_CODEC_DACL = SPRD_CODEC_SWITCH_START,
+ SPRD_CODEC_DACR,
+ SPRD_CODEC_HPLCGL,
+ SPRD_CODEC_HPRCGR,
+
+ SPRD_CODEC_SWITCH_END
+};
+
+#define GET_SWITCH_ID(x) ((x)-SPRD_CODEC_SWITCH_START)
+#define SPRD_CODEC_SWITCH_MAX (SPRD_CODEC_SWITCH_END-SPRD_CODEC_SWITCH_START)
+static const char *switch_name[SPRD_CODEC_SWITCH_MAX] = {
+ "DACL",
+ "DACR",
+ "HPLCGL",
+ "HPRCGR",
+};
+enum {
+ SPRD_CODEC_ADC_LOOP_START = SPRD_CODEC_SWITCH_END + 20,
+ SPRD_CODEC_ADC_DAC_ADIE_LOOP = SPRD_CODEC_ADC_LOOP_START,
+ SPRD_CODEC_ADC1_DAC_ADIE_LOOP,
+ SPRD_CODEC_ADC_DAC_DIGITAL_LOOP,
+ SPRD_CODEC_ADC1_DAC_DIGITAL_LOOP,
+
+ SPRD_CODEC_ADC_LOOP_END
+};
+#define GET_ADC_LOOP_ID(x) ((x)-SPRD_CODEC_ADC_LOOP_START)
+#define SPRD_CODEC_ADC_LOOP_MAX (SPRD_CODEC_ADC_LOOP_END-SPRD_CODEC_ADC_LOOP_START)
+static const char *adc_loop_name[SPRD_CODEC_ADC_LOOP_MAX] = {
+ "ADC->DAC ADIE LOOP",
+ "ADC1->DAC ADIE LOOP",
+ "ADC->DAC DIGITAL LOOP",
+ "ADC1->DAC DIGITAL LOOP",
+};
+
+static unsigned long sprd_codec_dp_base = 0;
+
+enum {
+ SPRD_CODEC_HP_PA_VER_1,
+ SPRD_CODEC_HP_PA_VER_2,
+ SPRD_CODEC_HP_PA_VER_MAX,
+};
+
+/* codec private data */
+struct sprd_codec_priv {
+ struct snd_soc_codec *codec;
+ int da_sample_val;
+ int ad_sample_val;
+ int ad1_sample_val;
+ struct sprd_codec_mixer mixer[SPRD_CODEC_MIXER_MAX];
+ struct sprd_codec_pga_op pga[SPRD_CODEC_PGA_MAX];
+ struct sprd_codec_switch_op switcher[SPRD_CODEC_SWITCH_MAX];
+ int mic_bias[SPRD_CODEC_MIC_BIAS_MAX];
+
+ int ap_irq;
+ struct completion completion_hp_pop;
+
+ int dp_irq;
+ struct completion completion_dac_mute;
+ struct power_supply audio_ldo;
+
+ struct sprd_codec_pa_setting inter_pa;
+ struct mutex inter_pa_mutex;
+
+ int hp_ver;
+ struct sprd_codec_hp_pa_setting inter_hp_pa;
+ struct mutex inter_hp_pa_mutex;
+
+ struct regulator *main_mic;
+ struct regulator *aux_mic;
+ struct regulator *head_mic;
+ struct regulator *cg_ldo;
+ atomic_t ldo_refcount;
+ struct delayed_work ovp_delayed_work;
+
+ struct sprd_codec_ldo_cfg s_vcom;
+ struct sprd_codec_ldo_cfg s_vpa;
+
+#define SPRD_CODEC_PA_SW_AOL (BIT(0))
+#define SPRD_CODEC_PA_SW_EAR (BIT(1))
+#define SPRD_CODEC_PA_SW_FUN (SPRD_CODEC_PA_SW_AOL | SPRD_CODEC_PA_SW_EAR)
+ int sprd_codec_fun;
+ spinlock_t sprd_codec_fun_lock;
+ spinlock_t sprd_codec_pa_sw_lock;
+
+ int sprd_linein_mute;
+ int sprd_linein_set;
+ struct mutex sprd_linein_mute_mutex;
+ struct notifier_block nb;
+ int ad_da_loop_en[SPRD_CODEC_ADC_LOOP_MAX];
+
+ int sprd_dacspkl_enable;
+ int sprd_dacspkl_set;
+ struct mutex sprd_dacspkl_mutex;
+};
+
+const u32 low_power_chip_arr[] = {
+ 0x2723, AUDIO_2723_VER_E,
+ 0x2723, AUDIO_2723_VER_T
+};
+
+static int is_low_power_support()
+{
+ u32 chip_id = 0;
+ int ver_id = 0;
+ int i = 0, j = 0;
+
+ chip_id = sci_get_ana_chip_id() >> 16;
+ ver_id = sci_get_ana_chip_ver();
+
+ pr_err("%s, chip_id=0x%x, ver_id=0x%x\n", __func__, chip_id, ver_id);
+
+ while (i < ARRAY_SIZE(low_power_chip_arr)) {
+ if (low_power_chip_arr[i] == (u32)chip_id
+ && low_power_chip_arr[i + 1] == ver_id)
+ return 1;
+ i += 2;
+ }
+
+ return 0;
+}
+
+static int sprd_codec_power_get(struct device *dev, struct regulator **regu,
+ const char *id)
+{
+ if (!*regu) {
+ *regu = regulator_get(dev, id);
+ if (IS_ERR(*regu)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(*regu), id);
+ *regu = 0;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int sprd_codec_power_put(struct regulator **regu)
+{
+ if (*regu) {
+ regulator_set_mode(*regu, REGULATOR_MODE_NORMAL);
+ regulator_disable(*regu);
+ regulator_put(*regu);
+ *regu = 0;
+ }
+ return 0;
+}
+
+static int sprd_codec_regulator_set(struct regulator **regu, int on)
+{
+ int ret = 0;
+ if (*regu) {
+ if (on) {
+ ret = regulator_enable(*regu);
+ if (ret) {
+ sprd_codec_power_put(regu);
+ return ret;
+ }
+ } else {
+ ret = regulator_disable(*regu);
+ }
+ }
+ return ret;
+}
+
+#ifdef CONFIG_SND_SOC_SPRD_AUDIO_DEBUG
+static const char *get_event_name(int event)
+{
+ const char *ev_name;
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ev_name = "PRE_PMU";
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ ev_name = "POST_PMU";
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ ev_name = "PRE_PMD";
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ev_name = "POST_PMD";
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+ return ev_name;
+}
+#endif
+
+static void sprd_codec_set_fun(struct sprd_codec_priv *sprd_codec, int fun)
+{
+ spin_lock(&sprd_codec->sprd_codec_fun_lock);
+ sprd_codec->sprd_codec_fun |= fun;
+ spin_unlock(&sprd_codec->sprd_codec_fun_lock);
+}
+
+static void sprd_codec_clr_fun(struct sprd_codec_priv *sprd_codec, int fun)
+{
+ spin_lock(&sprd_codec->sprd_codec_fun_lock);
+ sprd_codec->sprd_codec_fun &= ~fun;
+ spin_unlock(&sprd_codec->sprd_codec_fun_lock);
+}
+
+static int sprd_codec_test_fun(struct sprd_codec_priv *sprd_codec, int fun)
+{
+ int ret;
+ spin_lock(&sprd_codec->sprd_codec_fun_lock);
+ ret = sprd_codec->sprd_codec_fun & fun;
+ spin_unlock(&sprd_codec->sprd_codec_fun_lock);
+ return ret;
+}
+
+static void sprd_codec_wait(u32 wait_time)
+{
+ if (wait_time)
+ schedule_timeout_uninterruptible(msecs_to_jiffies(wait_time));
+}
+
+#if 0 /* sometime we will use this print function */
+static unsigned int sprd_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg);
+static void sprd_codec_print_regs(struct snd_soc_codec *codec)
+{
+ int reg;
+ pr_warn("sprd_codec register digital part\n");
+ for (reg = SPRD_CODEC_DP_BASE; reg < SPRD_CODEC_DP_END; reg += 0x10) {
+ pr_warn("0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (reg - SPRD_CODEC_DP_BASE)
+ , sprd_codec_read(codec, reg + 0x00)
+ , sprd_codec_read(codec, reg + 0x04)
+ , sprd_codec_read(codec, reg + 0x08)
+ , sprd_codec_read(codec, reg + 0x0C)
+ );
+ }
+ pr_warn("sprd_codec register analog part\n");
+ for (reg = SPRD_CODEC_AP_BASE; reg < SPRD_CODEC_AP_END; reg += 0x10) {
+ pr_warn("0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (reg - SPRD_CODEC_AP_BASE)
+ , sprd_codec_read(codec, reg + 0x00)
+ , sprd_codec_read(codec, reg + 0x04)
+ , sprd_codec_read(codec, reg + 0x08)
+ , sprd_codec_read(codec, reg + 0x0C)
+ );
+ }
+}
+#endif
+
+static inline int _sprd_codec_ldo_cfg_set(struct sprd_codec_ldo_cfg *p_v_cfg, const struct sprd_codec_ldo_v_map
+ *v_map, const int v_map_size)
+{
+ p_v_cfg->v_map = v_map;
+ p_v_cfg->v_map_size = v_map_size;
+ return 0;
+}
+
+static inline int _sprd_codec_hold(int index, int volt,
+ const struct sprd_codec_ldo_v_map *v_map)
+{
+ int scope = 40; /* unit: mv */
+ int ret = ((v_map[index].volt - volt) <= scope);
+ if (index >= 1) {
+ return (ret || ((volt - v_map[index - 1].volt) <= scope));
+ }
+ return ret;
+}
+
+static int sprd_codec_auto_ldo_volt(struct snd_soc_codec *codec,
+ void (*set_level) (struct snd_soc_codec *,
+ int),
+ const struct sprd_codec_ldo_v_map *v_map,
+ const int v_map_size, int init)
+{
+ int i;
+ int volt = sprd_get_vbat_voltage();
+ sp_asoc_pr_dbg("Get vbat voltage %d\n", volt);
+ for (i = 0; i < v_map_size; i++) {
+ if (volt <= ldo_v_map[i].volt) {
+ sp_asoc_pr_dbg("Hold %d\n",
+ _sprd_codec_hold(i, volt, v_map));
+ if (init || !_sprd_codec_hold(i, volt, v_map)) {
+ set_level(codec, v_map[i].ldo_v_level);
+ }
+ return 0;
+ }
+ }
+ return -EFAULT;
+}
+
+static inline void sprd_codec_vcm_v_sel(struct snd_soc_codec *codec, int v_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("VCM Set %d\n", v_sel);
+ mask = VCM_V_MASK << VCM_V;
+ val = (v_sel << VCM_V) & mask;
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU2), mask, val);
+}
+
+static inline void sprd_codec_auxadc_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("AUXADC Set %d\n", on);
+ /*jian.chen changed*/
+ /*
+ mask = BIT(AUXADC_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(PMUR4_PMUR3), mask, val);
+ */
+}
+
+static int sprd_codec_pga_spk_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC11;
+ mask = AOL_G_MASK << AOL_G;
+ val = (pgaval << AOL_G) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_spkr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC11;
+ mask = AOR_G_MASK << AOR_G;
+ val = (pgaval << AOR_G) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_hpl_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC12;
+ mask = HPL_G_MASK << HPL_G;
+ val = (pgaval << HPL_G) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_hpr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC12;
+ mask = HPR_G_MASK << HPR_G;
+ val = (pgaval << HPR_G) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_ear_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC12;
+ mask = EAR_G_MASK << EAR_G;
+ val = (pgaval << EAR_G) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_adcl_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC9;
+ mask = ADC_PGA_L_MASK << ADC_PGA_L;
+ val = (pgaval << ADC_PGA_L) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_adcr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC9;
+ mask = ADC_PGA_R_MASK << ADC_PGA_R;
+ val = (pgaval << ADC_PGA_R) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_dacl_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC10;
+ if (is_low_power_support()) {
+ if (pgaval > 0) {
+ pgaval = pgaval >= 6 ? pgaval : 6;
+ pgaval -= 5;
+ }
+ }
+ mask = DACL_G_MASK << DACL_G;
+ val = (pgaval << DACL_G) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_dacr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC10;
+ if (is_low_power_support()) {
+ if (pgaval > 0) {
+ pgaval = pgaval >= 6 ? pgaval : 6;
+ pgaval -= 5;
+ }
+ }
+ mask = DACR_G_MASK << DACR_G;
+ val = (pgaval << DACR_G) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_mic_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC8;
+ mask = ADC_MIC_PGA_MASK << ADC_MIC_PGA;
+ val = (pgaval << ADC_MIC_PGA) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_auxmic_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC8;
+ mask = ADC_AUXMIC_PGA_MASK << ADC_AUXMIC_PGA;
+ val = (pgaval << ADC_AUXMIC_PGA) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_headmic_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC8;
+ mask = ADC_HEADMIC_PGA_MASK << ADC_HEADMIC_PGA;
+ val = (pgaval << ADC_HEADMIC_PGA) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_ailr_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC8;
+ mask = ADC_AILR_PGA_MASK << ADC_AILR_PGA;
+ val = (pgaval << ADC_AILR_PGA) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_cg_hpl_1_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC13;
+ mask = CG_HPL_G_1_MASK << CG_HPL_G_1;
+ val = (pgaval << CG_HPL_G_1) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_cg_hpr_1_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC14;
+ mask = CG_HPR_G_1_MASK << CG_HPR_G_1;
+ val = (pgaval << CG_HPR_G_1) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_cg_hpl_2_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC13;
+ mask = CG_HPL_G_2_MASK << CG_HPL_G_2;
+ val = (pgaval << CG_HPL_G_2) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+
+static int sprd_codec_pga_cg_hpr_2_set(struct snd_soc_codec *codec, int pgaval)
+{
+ int reg, val, mask;
+ reg = ANA_CDC14;
+ mask = CG_HPR_G_2_MASK << CG_HPR_G_2;
+ val = (pgaval << CG_HPR_G_2) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+static int sprd_codec_switch_dacl_set(struct snd_soc_codec *codec, int on)
+{
+ int reg, val, mask;
+ reg = ANA_CDC2;
+ mask = 1 << DACL_EN;
+ val = (on << DACL_EN) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+static int sprd_codec_switch_dacr_set(struct snd_soc_codec *codec, int on)
+{
+ int reg, val, mask;
+ reg = ANA_CDC2;
+ mask = 1 << DACR_EN;
+ val = (on << DACR_EN) & mask;
+
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+static int sprd_codec_switch_hplcgl_set(struct snd_soc_codec *codec, int on)
+{
+ int reg, val, mask;
+ reg = ANA_CDC7;
+ mask = BIT(HPL_CGL);
+ val = (on << HPL_CGL) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+static int sprd_codec_switch_hprcgr_set(struct snd_soc_codec *codec, int on)
+{
+ int reg, val, mask;
+ reg = ANA_CDC7;
+ mask = BIT(HPR_CGR);
+ val = (on << HPR_CGR) & mask;
+ return snd_soc_update_bits(codec, SOC_REG(reg), mask, val);
+}
+static struct sprd_codec_pga sprd_codec_pga_cfg[SPRD_CODEC_PGA_MAX] = {
+ {sprd_codec_pga_spk_set, 0},
+ {sprd_codec_pga_spkr_set, 0},
+ {sprd_codec_pga_hpl_set, 0},
+ {sprd_codec_pga_hpr_set, 0},
+ {sprd_codec_pga_ear_set, 0},
+
+ {sprd_codec_pga_adcl_set, 0},
+ {sprd_codec_pga_adcr_set, 0},
+
+ {sprd_codec_pga_dacl_set, 0},
+ {sprd_codec_pga_dacr_set, 0},
+ {sprd_codec_pga_mic_set, 0},
+ {sprd_codec_pga_auxmic_set, 0},
+ {sprd_codec_pga_headmic_set, 0},
+ {sprd_codec_pga_ailr_set, 0},
+ {sprd_codec_pga_ailr_set, 0},
+ {sprd_codec_pga_cg_hpl_1_set, -1},
+ {sprd_codec_pga_cg_hpr_1_set, -1},
+ {sprd_codec_pga_cg_hpl_2_set, -1},
+ {sprd_codec_pga_cg_hpr_2_set, -1},
+};
+static struct sprd_codec_switch sprd_codec_switch_cfg[SPRD_CODEC_SWITCH_MAX] = {
+ {sprd_codec_switch_dacl_set, 0},
+ {sprd_codec_switch_dacr_set, 0},
+ {sprd_codec_switch_hplcgl_set, 0},
+ {sprd_codec_switch_hprcgr_set, 0},
+};
+
+static void _mixer_adc_linein_mute_nolock(struct snd_soc_codec *codec, int need_mute)
+{
+ int val = 0;
+ val = !need_mute;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ if (sprd_codec->sprd_linein_set & BIT(SPRD_CODEC_AILADCL_SET)) {
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC5), BIT(AIL_ADCL), val << AIL_ADCL);
+ }
+ if (sprd_codec->sprd_linein_set & BIT(SPRD_CODEC_AILADCR_SET)) {
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC5), BIT(AIL_ADCR), val << AIL_ADCR);
+ }
+ if (sprd_codec->sprd_linein_set & BIT(SPRD_CODEC_AIRADCL_SET)) {
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC5), BIT(AIR_ADCL), val << AIR_ADCL);
+ }
+ if (sprd_codec->sprd_linein_set & BIT(SPRD_CODEC_AIRADCR_SET)) {
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC5), BIT(AIR_ADCR), val << AIR_ADCR);
+ }
+}
+
+static int _mixer_adc_linein_set(struct snd_soc_codec *codec, int id, unsigned int mask,
+ int on, unsigned int shift)
+{
+ int ret = 0;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&sprd_codec->sprd_linein_mute_mutex);
+ if (on) {
+ sprd_codec->sprd_linein_set |= BIT(id);
+ } else {
+ sprd_codec->sprd_linein_set &= ~(BIT(id));
+ }
+ if (!sprd_codec->sprd_linein_mute) {
+ ret = snd_soc_update_bits(codec, SOC_REG(ANA_CDC5), mask, on << shift);
+ }
+ mutex_unlock(&sprd_codec->sprd_linein_mute_mutex);
+ return ret;
+}
+
+/* adc mixer */
+
+static int ailadcl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return _mixer_adc_linein_set(codec, SPRD_CODEC_AILADCL_SET, BIT(AIL_ADCL),
+ on , AIL_ADCL);
+}
+
+static int ailadcr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return _mixer_adc_linein_set(codec, SPRD_CODEC_AILADCR_SET, BIT(AIL_ADCR),
+ on , AIL_ADCR);
+}
+
+static int airadcl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return _mixer_adc_linein_set(codec, SPRD_CODEC_AIRADCL_SET, BIT(AIR_ADCL),
+ on , AIR_ADCL);
+}
+
+static int airadcr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return _mixer_adc_linein_set(codec, SPRD_CODEC_AIRADCR_SET, BIT(AIR_ADCR),
+ on , AIR_ADCR);
+}
+
+static int mainmicadcl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC5), BIT(MIC_ADCL),
+ on << MIC_ADCL);
+}
+
+static int mainmicadcr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC5), BIT(MIC_ADCR),
+ on << MIC_ADCR);
+}
+
+static int auxmicadcl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC5),
+ BIT(AUXMIC_ADCL), on << AUXMIC_ADCL);
+}
+
+static int auxmicadcr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC5),
+ BIT(AUXMIC_ADCR), on << AUXMIC_ADCR);
+}
+
+static int hpmicadcl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC5),
+ BIT(HEADMIC_ADCL), on << HEADMIC_ADCL);
+}
+
+static int hpmicadcr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC5),
+ BIT(HEADMIC_ADCR), on << HEADMIC_ADCR);
+}
+
+/* hp mixer */
+
+static int daclhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC7),
+ BIT(DACL_P_HPL), on << DACL_P_HPL);
+}
+
+static int daclhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC7),
+ BIT(DACL_N_HPR), on << DACL_N_HPR);
+}
+
+static int dacrhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC7),
+ BIT(DACR_P_HPL), on << DACR_P_HPL);
+}
+
+static int dacrhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC7),
+ BIT(DACR_P_HPR), on << DACR_P_HPR);
+}
+
+static int adclhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC7),
+ BIT(ADCL_P_HPL), on << ADCL_P_HPL);
+}
+
+static int adclhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC7),
+ BIT(ADCL_N_HPR), on << ADCL_N_HPR);
+}
+
+static int adcrhpl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC7),
+ BIT(ADCR_P_HPL), on << ADCR_P_HPL);
+}
+
+static int adcrhpr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC7),
+ BIT(ADCR_P_HPR), on << ADCR_P_HPR);
+}
+
+/* spkl mixer */
+
+static int daclspkl_set(struct snd_soc_codec *codec, int on)
+{
+ int ret = 0;
+ int enable;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ mutex_lock(&sprd_codec->sprd_dacspkl_mutex);
+ if(on){
+ sprd_codec->sprd_dacspkl_set |= BIT(SPRD_CODEC_LEFT);
+ } else {
+ sprd_codec->sprd_dacspkl_set &= ~BIT(SPRD_CODEC_LEFT);
+ }
+ enable = sprd_codec->sprd_dacspkl_enable & BIT(SPRD_CODEC_LEFT);
+ sp_asoc_pr_dbg("%s %d,enalbe :%d\n", __func__, on,enable);
+ if(enable){
+ ret = snd_soc_update_bits(codec, SOC_REG(ANA_CDC6), BIT(DACL_AOL),
+ on << DACL_AOL);
+ }
+ mutex_unlock(&sprd_codec->sprd_dacspkl_mutex);
+}
+
+static int dacrspkl_set(struct snd_soc_codec *codec, int on)
+{
+ int ret = 0;
+ int enable;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ mutex_lock(&sprd_codec->sprd_dacspkl_mutex);
+ if(on){
+ sprd_codec->sprd_dacspkl_set |= BIT(SPRD_CODEC_RIGHT);
+ } else {
+ sprd_codec->sprd_dacspkl_set &= ~BIT(SPRD_CODEC_RIGHT);
+ }
+ enable = sprd_codec->sprd_dacspkl_enable & BIT(SPRD_CODEC_RIGHT);
+ sp_asoc_pr_dbg("%s %d,enalbe :%d\n", __func__, on,enable);
+ if(enable){
+ ret = snd_soc_update_bits(codec, SOC_REG(ANA_CDC6), BIT(DACR_AOL),
+ on << DACR_AOL);
+ }
+ mutex_unlock(&sprd_codec->sprd_dacspkl_mutex);
+ return ret;
+}
+
+static int adclspkl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC6), BIT(ADCL_AOL),
+ on << ADCL_AOL);
+}
+
+static int adcrspkl_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC6), BIT(ADCR_AOL),
+ on << ADCR_AOL);
+}
+
+/* spkr mixer */
+
+static int daclspkr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC6), BIT(DACL_AOR),
+ on << DACL_AOR);
+}
+
+static int dacrspkr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC6), BIT(DACR_AOR),
+ on << DACR_AOR);
+}
+
+static int adclspkr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC6), BIT(ADCL_AOR),
+ on << ADCL_AOR);
+}
+
+static int adcrspkr_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC6), BIT(ADCR_AOR),
+ on << ADCR_AOR);
+}
+
+/* ear mixer */
+
+static int daclear_set(struct snd_soc_codec *codec, int on)
+{
+ sp_asoc_pr_dbg("%s %d\n", __func__, on);
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC7), BIT(DACL_EAR),
+ on << DACL_EAR);
+}
+
+static sprd_codec_mixer_set mixer_setting[SPRD_CODEC_MIXER_MAX] = {
+ /* adc mixer */
+ ailadcl_set, ailadcr_set,
+ airadcl_set, airadcr_set,
+ mainmicadcl_set, mainmicadcr_set,
+ auxmicadcl_set, auxmicadcr_set,
+ hpmicadcl_set, hpmicadcr_set,
+ /* hp mixer */
+ daclhpl_set, daclhpr_set,
+ dacrhpl_set, dacrhpr_set,
+ adclhpl_set, adclhpr_set,
+ adcrhpl_set, adcrhpr_set,
+ /* spk mixer */
+ daclspkl_set, daclspkr_set,
+ dacrspkl_set, dacrspkr_set,
+ adclspkl_set, adclspkr_set,
+ adcrspkl_set, adcrspkr_set,
+ /* ear mixer */
+ daclear_set, 0,
+};
+
+/* DO NOT USE THIS FUNCTION */
+static inline void __sprd_codec_pa_sw_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ mask = BIT(PA_SW_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU0), mask, val);
+}
+
+static inline void __sprd_codec_speaker_pa_ibias_set(struct snd_soc_codec *codec, int c_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s %d\n", __func__, c_sel);
+ mask = PA_AB_I_MASK << PA_AB_I;
+ val = (c_sel << PA_AB_I) & mask;
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU3), mask, val);
+}
+
+static void sprd_inter_speaker_pa_pre(struct snd_soc_codec *codec)
+{
+ /* set class-AB Ibias to 7.4/4.7 from weifeng */
+ __sprd_codec_speaker_pa_ibias_set(codec, 0);
+}
+
+static inline void sprd_codec_pa_sw_set(struct snd_soc_codec *codec, int fun)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ sp_asoc_pr_dbg("%s fun 0x%08x\n", __func__, fun);
+ spin_lock(&sprd_codec->sprd_codec_pa_sw_lock);
+ sprd_codec_set_fun(sprd_codec, fun);
+ __sprd_codec_pa_sw_en(codec, 1);
+ spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock);
+}
+
+static inline void sprd_codec_pa_sw_clr(struct snd_soc_codec *codec, int fun)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ sp_asoc_pr_dbg("%s fun 0x%08x\n", __func__, fun);
+ spin_lock(&sprd_codec->sprd_codec_pa_sw_lock);
+ sprd_codec_clr_fun(sprd_codec, fun);
+ if (!sprd_codec_test_fun(sprd_codec, SPRD_CODEC_PA_SW_FUN))
+ __sprd_codec_pa_sw_en(codec, 0);
+ spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock);
+}
+
+/* inter PA */
+
+static inline void sprd_codec_pa_d_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ mask = BIT(PA_D_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC4), mask, val);
+}
+
+static inline void sprd_codec_pa_demi_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ if (on)
+ /*when emi is on, need to set this register from xun && junjie.chen*/
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU6), 0xffff, 0x400);
+ else
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU6), 0xffff, 0);
+
+ mask = BIT(PA_DEMI_EN);
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC4), mask, on?mask:0);
+
+ /*open ocp from xun && weifeng*/
+ mask = BIT(DRV_OCP_AOL_PD) | BIT(DRV_OCP_AOR_PD);
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU6), mask, 0);
+}
+
+static inline void sprd_codec_pa_ldo_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ mask = BIT(PA_LDO_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU0), mask, val);
+ if (on) {
+ sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL);
+ }
+}
+
+static inline void sprd_codec_pa_ldo_v_sel(struct snd_soc_codec *codec,
+ int v_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, v_sel);
+ mask = PA_LDO_V_MASK << PA_LDO_V;
+ val = (v_sel << PA_LDO_V) & mask;
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU3), mask, val);
+}
+
+static inline void sprd_codec_pa_ovp_v_sel(struct snd_soc_codec *codec,
+ int v_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, v_sel);
+ mask = PA_OVP_V_MASK << PA_OVP_V;
+ val = (v_sel << PA_OVP_V) & mask;
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU5), mask, val);
+}
+
+static inline void sprd_codec_pa_dtri_f_sel(struct snd_soc_codec *codec,
+ int f_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, f_sel);
+ mask = PA_DTRI_F_MASK << PA_DTRI_F;
+ val = (f_sel << PA_DTRI_F) & mask;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC4), mask, val);
+}
+
+static inline void sprd_codec_pa_en(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ spin_lock(&sprd_codec->sprd_codec_pa_sw_lock);
+ if (on) {
+ mask = BIT(PA_EN);
+ val = mask;
+ } else {
+ if (!sprd_codec_test_fun(sprd_codec, SPRD_CODEC_PA_SW_FUN))
+ mask = BIT(PA_EN) | BIT(PA_SW_EN) | BIT(PA_LDO_EN);
+ else
+ mask = BIT(PA_EN) | BIT(PA_LDO_EN);
+ val = 0;
+ }
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU0), mask, val);
+ spin_unlock(&sprd_codec->sprd_codec_pa_sw_lock);
+}
+
+static inline void sprd_codec_ovp_ldo_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ mask = BIT(OVP_LDO_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU0), mask, val);
+}
+
+static inline void sprd_codec_linein_mute_init(struct sprd_codec_priv *sprd_codec)
+{
+ sprd_codec->sprd_linein_mute = 0;
+ sprd_codec->sprd_linein_set = 0;
+ mutex_init(&sprd_codec->sprd_linein_mute_mutex);
+}
+
+static inline void sprd_codec_inter_pa_init(struct sprd_codec_priv *sprd_codec)
+{
+ sprd_codec->inter_pa.setting.LDO_V_sel = 0x03;
+ sprd_codec->inter_pa.setting.DTRI_F_sel = 0x01;
+}
+static inline void sprd_codec_dacspkl_enable_init(struct sprd_codec_priv *sprd_codec)
+{
+ sprd_codec->sprd_dacspkl_enable = BIT(SPRD_CODEC_RIGHT)|BIT(SPRD_CODEC_LEFT);
+ sprd_codec->sprd_dacspkl_set = 0;
+ mutex_init(&sprd_codec->sprd_dacspkl_mutex);
+}
+
+static inline void sprd_codec_switch_init(struct sprd_codec_priv *sprd_codec)
+{
+ int i = 0;
+ for(i=0;i<SPRD_CODEC_SWITCH_MAX;i++){
+ sprd_codec->switcher[i].set = NULL;
+ sprd_codec->switcher[i].on = 1;
+ }
+}
+
+static void sprd_codec_ovp_irq_enable(struct snd_soc_codec *codec)
+{
+ int val_clr, val_en;
+ val_clr = BIT(OVP_IRQ) << AUD_A_INT_CLR;
+ val_en = BIT(OVP_IRQ) << AUD_A_INT_EN;
+ snd_soc_update_bits(codec, SOC_REG(DIG_CFG3), val_clr, val_clr);
+ snd_soc_update_bits(codec, SOC_REG(DIG_CFG4), val_en, val_en);
+}
+
+static void sprd_codec_ovp_start(struct sprd_codec_priv *sprd_codec, int ms)
+{
+ schedule_delayed_work(&sprd_codec->ovp_delayed_work,
+ msecs_to_jiffies(ms));
+}
+
+static int sprd_ovp_irq(struct snd_soc_codec *codec, int is_ovp)
+{
+ int volt = sprd_get_vbat_voltage();
+ sp_asoc_pr_dbg("OVP %d curr_volt %d\n", is_ovp, volt);
+ if (is_ovp) {
+ sprd_codec_pa_en(codec, 0);
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC2), BIT(AOL_EN), 0);
+ } else {
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC2), BIT(AOL_EN),
+ BIT(AOL_EN));
+ sprd_codec_pa_en(codec, 1);
+ }
+ return 0;
+}
+
+#define to_sprd_codec(x) container_of((x), struct sprd_codec_priv, ovp_delayed_work)
+#define to_delay_worker(x) container_of((x), struct delayed_work, work)
+static void sprd_codec_ovp_delay_worker(struct work_struct *work)
+{
+ struct sprd_codec_priv *sprd_codec =
+ to_sprd_codec(to_delay_worker(work));
+ struct snd_soc_codec *codec = sprd_codec->codec;
+
+ if (!(snd_soc_read(codec, ANA_STS0) & BIT(OVP_FLAG))) {
+ sprd_ovp_irq(codec, 0);
+ } else {
+ sprd_ovp_irq(codec, 1);
+ sprd_codec_ovp_start(sprd_codec, 500);
+ sprd_codec_pa_ldo_v_sel(codec, PA_LDO_V_36);
+ }
+}
+
+static inline int sprd_codec_pa_ldo_auto(struct snd_soc_codec *codec, int init)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ return sprd_codec_auto_ldo_volt
+ (codec, sprd_codec_pa_ldo_v_sel, sprd_codec->s_vpa.v_map,
+ sprd_codec->s_vpa.v_map_size, init);
+}
+
+static inline int sprd_codec_pa_ldo_cfg(struct sprd_codec_priv *sprd_codec, const struct sprd_codec_ldo_v_map
+ *v_map, const int v_map_size)
+{
+ return _sprd_codec_ldo_cfg_set(&sprd_codec->s_vpa, v_map, v_map_size);
+}
+
+static int sprd_inter_speaker_pa(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ struct sprd_codec_inter_pa *p_setting = &sprd_codec->inter_pa.setting;
+ sp_asoc_pr_info("inter PA Switch %s\n", STR_ON_OFF(on));
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+ if (on) {
+ sprd_inter_speaker_pa_pre(codec);
+ /* set ldo_v to 3.6v for ovp as weifeng's suggestion */
+ sprd_codec_pa_ldo_v_sel(codec, PA_LDO_V_36);
+ sprd_codec_pa_ovp_v_sel(codec, PA_OVP_435);
+ sprd_codec_ovp_ldo_en(codec,1);
+ sprd_codec_ovp_irq_enable(codec);
+ sprd_codec_pa_d_en(codec, p_setting->is_classD_mode);
+ sprd_codec_pa_demi_en(codec, p_setting->is_DEMI_mode);
+ sprd_codec_pa_ldo_en(codec, p_setting->is_LDO_mode);
+ if (p_setting->is_LDO_mode) {
+ if (p_setting->is_auto_LDO_mode) {
+ sprd_codec_pa_ldo_auto(codec, 1);
+ } else {
+ sprd_codec_pa_ldo_v_sel(codec,
+ p_setting->LDO_V_sel);
+ }
+ } else {
+ sprd_codec_pa_sw_set(codec, SPRD_CODEC_PA_SW_AOL);
+ }
+ sprd_codec_pa_dtri_f_sel(codec, p_setting->DTRI_F_sel);
+ /* delay 20ms as weifeng's suggestion to avoid pop noise from spk */
+ sprd_codec_wait(20);
+ sprd_codec_pa_en(codec, 1);
+ sprd_codec->inter_pa.set = 1;
+ } else {
+ sprd_codec->inter_pa.set = 0;
+ sprd_codec_pa_en(codec, 0);
+ sprd_codec_wait(2);
+ sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL);
+ sprd_codec_pa_ldo_en(codec, 0);
+ }
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ return 0;
+}
+
+static int spk_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+ return sprd_inter_speaker_pa(w->codec, on);
+}
+
+#define SPRD_CODEC_HP_VER(bit) \
+do { \
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec); \
+ switch(sprd_codec->hp_ver) { \
+ case SPRD_CODEC_HP_PA_VER_2: \
+ mask = BIT(V2_##bit); \
+ break; \
+ case SPRD_CODEC_HP_PA_VER_1: \
+ default: \
+ mask = BIT(bit); \
+ break; \
+ } \
+} while(0)
+
+static inline void sprd_codec_hp_classg_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ //SPRD_CODEC_HP_VER(AUDIO_CLASSG_EN);
+ mask = BIT(CG_REF_EN);
+ val = on ? mask : 0;
+ /* FIXME: change by jian.chen */
+ //snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC3), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_lpw(struct snd_soc_codec *codec, int lpw)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, lpw);
+ /* FIXME: change by jian.chen */
+ /*
+ switch (sprd_codec->hp_ver) {
+ case SPRD_CODEC_HP_PA_VER_2:
+ if (sci_get_ana_chip_ver() >= SC2711BA) {
+ mask = V2_AUDIO_CHP_LPW_MASK_BA << V2_AUDIO_CHP_LPW_BA;
+ val = (lpw << V2_AUDIO_CHP_LPW_BA) & mask;
+ } else {
+ mask = V2_AUDIO_CHP_LPW_MASK << V2_AUDIO_CHP_LPW;
+ val = (lpw << V2_AUDIO_CHP_LPW) & mask;
+ }
+ break;
+ case SPRD_CODEC_HP_PA_VER_1:
+ default:
+ if (sci_get_ana_chip_ver() >= SC2713CA) {
+ mask = AUDIO_CHP_LPW_MASK_CA << AUDIO_CHP_LPW_CA;
+ val = (lpw << AUDIO_CHP_LPW_CA) & mask;
+ } else {
+ mask = AUDIO_CHP_LPW_MASK << AUDIO_CHP_LPW;
+ val = (lpw << AUDIO_CHP_LPW) & mask;
+ }
+ break;
+ }
+ */
+ mask = CG_LPW_MASK << CG_LPW;
+ if (is_low_power_support())
+ val = (0x2 << CG_LPW) & mask;
+ else
+ val = (lpw << CG_LPW) & mask;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC3), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_mode(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ /* FIXME: change by jian.chen */
+ /*
+ switch (sprd_codec->hp_ver) {
+ case SPRD_CODEC_HP_PA_VER_2:
+ if (sci_get_ana_chip_ver() >= SC2711BA)
+ mask = BIT(V2_AUDIO_CHP_MODE_BA );
+ else
+ mask = BIT(V2_AUDIO_CHP_MODE);
+ break;
+ case SPRD_CODEC_HP_PA_VER_1:
+ default:
+ if (sci_get_ana_chip_ver() >= SC2713CA)
+ mask = BIT(AUDIO_CHP_MODE_CA );
+ else
+ mask = BIT(AUDIO_CHP_MODE );
+ break;
+ }
+ */
+ mask = BIT(CG_HP_MODE);
+ val = on? mask :0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC3), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_osc(struct snd_soc_codec *codec, int osc)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, osc);
+ /* FIXME: change by jian.chen */
+ /*
+ switch (sprd_codec->hp_ver) {
+ case SPRD_CODEC_HP_PA_VER_2:
+ mask = V2_AUDIO_CHP_OSC_MASK << V2_AUDIO_CHP_OSC;
+ val = (osc << V2_AUDIO_CHP_OSC) & mask;
+ break;
+ case SPRD_CODEC_HP_PA_VER_1:
+ default:
+ mask = AUDIO_CHP_OSC_MASK << AUDIO_CHP_OSC;
+ val = (osc << AUDIO_CHP_OSC) & mask;
+ break;
+ }
+ */
+ mask = CG_CHP_OSC_MASK << CG_CHP_OSC;
+ val = (osc << CG_CHP_OSC) & mask;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC3), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ /* FIXME: change by jian.chen */
+ /*
+ SPRD_CODEC_HP_VER(AUDIO_CHP_EN);
+ */
+ mask = BIT(CG_CHP_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC3), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_hpl_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ /* FIXME: change by jian.chen */
+ /*
+ SPRD_CODEC_HP_VER(AUDIO_CHP_HPL_EN);
+ */
+ mask = BIT(CG_HPL_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC3), mask, val);
+}
+
+static inline void sprd_codec_hp_cg_ldo_en(struct snd_soc_codec *codec, int on)
+{
+ int ret = 0;
+ struct regulator **regu;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ if(!sprd_codec->cg_ldo) {
+ ret = sprd_codec_power_get(0, &sprd_codec->cg_ldo, "CGLDO");
+ if (ret) {
+ pr_err("ERR:%s get CGLDO error %d\n", __func__, ret);
+ return;
+ }
+ }
+ regu = &sprd_codec->cg_ldo;
+ sprd_codec_regulator_set(regu, on);
+}
+
+static inline void sprd_codec_hp_cg_dangl_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ /* set this value from xun.zhang */
+ mask = 0xFFFF;
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DANGL), mask, val);
+}
+
+static inline void sprd_codec_hp_cg_dangr_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ /* set this value from xun.zhang */
+ mask = 0xFFFF;
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DANGR), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_hpr_en(struct snd_soc_codec *codec, int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ /* FIXME: change by jian.chen */
+ /*
+ SPRD_CODEC_HP_VER(AUDIO_CHP_HPR_EN);
+ */
+ mask = BIT(CG_HPR_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC3), mask, val);
+}
+
+static inline void sprd_codec_hp_pa_hpl_mute(struct snd_soc_codec *codec,
+ int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ /* FIXME: change by jian.chen */
+ /*
+ SPRD_CODEC_HP_VER(AUDIO_CHP_LMUTE);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+ */
+}
+
+static inline void sprd_codec_hp_pa_hpr_mute(struct snd_soc_codec *codec,
+ int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ /* FIXME: change by jian.chen */
+ /*
+ SPRD_CODEC_HP_VER(AUDIO_CHP_RMUTE);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(DCR8_DCR7), mask, val);
+ */
+}
+
+static inline void sprd_codec_hp_pa_cgcal_en(struct snd_soc_codec *codec,
+ int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+ /* FIXME: change by jian.chen */
+ /*
+ SPRD_CODEC_HP_VER(AUDIO_CGCAL_EN);
+ */
+ mask = BIT(CG_HPCAL_EN);
+ val = on ? mask : 0;
+ if (is_low_power_support())
+ if (val == 0)
+ val = mask;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC3), mask, val);
+}
+
+static inline void sprd_codec_ldocg_fal_ild_en(struct snd_soc_codec *codec,
+ int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+
+ mask = BIT(LDOCG_EN_FAL_ILD);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU1), mask, val);
+}
+
+static inline void sprd_codec_ldocg_iset_fal_current_sel(struct snd_soc_codec *codec,
+ int i_sel)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, i_sel);
+
+ mask = BIT(LDOCG_ISET_FAL);
+ val = i_sel ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU1), mask, val);
+}
+
+static inline void sprd_codec_ldocg_ldocg_ramp_en(struct snd_soc_codec *codec,
+ int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+
+ mask = BIT(LDOCG_RAMP_EN);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU1), mask, val);
+}
+
+static inline void sprd_codec_head_l_int_pu_pd(struct snd_soc_codec *codec,
+ int on)
+{
+ int mask;
+ int val;
+ sp_asoc_pr_dbg("%s set %d\n", __func__, on);
+
+ mask = BIT(AUD_HEAD_L_INT_PU_PD);
+ val = on ? mask : 0;
+ snd_soc_update_bits(codec, SOC_REG(ANA_HDT1), mask, val);
+}
+
+static inline void sprd_codec_inter_hp_pa_init(struct sprd_codec_priv
+ *sprd_codec)
+{
+ sprd_codec->inter_hp_pa.setting.class_g_osc = 0x01;
+ sprd_codec->inter_hp_pa.setting.class_g_cgcal = 1;
+ sprd_codec->inter_hp_pa.setting.class_g_pa_en_delay_10ms = 1;
+ sprd_codec->inter_hp_pa.setting.class_g_hp_switch_delay_10ms = 3;
+ sprd_codec->inter_hp_pa.setting.class_g_hp_on_delay_20ms = 1;
+
+ //sprd_codec->inter_hp_pa.setting.class_g_open_delay_10ms = 0;
+ //sprd_codec->inter_hp_pa.setting.class_g_vdd_delay_10ms = 0;
+ //sprd_codec->inter_hp_pa.setting.class_g_chp_delay_30ms = 0;
+ //sprd_codec->inter_hp_pa.setting.class_g_all_close_delay_100ms = 0;
+ /* set class-G default pga from weifeng */
+ sprd_codec->pga[SPRD_CODEC_PGA_CG_HPL_1].pgaval = 0x10;
+ sprd_codec->pga[SPRD_CODEC_PGA_CG_HPR_1].pgaval = 0x10;
+ sprd_codec->pga[SPRD_CODEC_PGA_CG_HPL_2].pgaval = 0x7;
+ sprd_codec->pga[SPRD_CODEC_PGA_CG_HPR_2].pgaval = 0x7;
+}
+
+
+static void sprd_inter_headphone_pa_pre(struct sprd_codec_priv *sprd_codec,int on)
+{
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ static struct regulator *regulator = 0;
+ struct sprd_codec_inter_hp_pa *p_setting =
+ &sprd_codec->inter_hp_pa.setting;
+
+ static struct regulator *regulator_reg= 0;
+
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ static struct regulator *regulator_vb= 0;
+ int classg_vol = 2200000;
+#endif
+ return ;
+
+ if (on) {
+ if (!regulator) {
+ sp_asoc_pr_dbg("%s set vddclsg on\n", __func__);
+ /*1.CG_EN */
+ //open clk
+ regulator_reg = regulator_get(0, "VREG");
+ if (IS_ERR(regulator_reg)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(regulator_reg), "VREG");
+ BUG_ON(1);
+ }
+ regulator_set_mode(regulator_reg, REGULATOR_MODE_STANDBY);
+ if (regulator_enable(regulator_reg) < 0) {
+ } else {
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ //open vb
+ regulator_vb = regulator_get(0, "VB");
+ if (IS_ERR(regulator_vb)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(regulator_vb), "VB");
+ BUG_ON(1);
+ }
+ regulator_set_mode(regulator_vb, REGULATOR_MODE_STANDBY);
+ if (regulator_enable(regulator_vb) < 0) {
+ } else {
+#endif
+ sprd_codec_hp_classg_en(codec, 1);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ }
+#endif
+ }
+ //close clk
+ regulator_set_mode(regulator_reg,
+ REGULATOR_MODE_NORMAL);
+ regulator_disable(regulator_reg);
+ regulator_put(regulator_reg);
+ regulator_reg = 0;
+
+ //sprd_codec_wait(p_setting->class_g_open_delay_10ms * 10);
+ /*2. LDO PD */
+ regulator = regulator_get(0, CLASS_G_LDO_ID);
+ if (IS_ERR(regulator)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(regulator), CLASS_G_LDO_ID);
+ BUG_ON(1);
+ }
+ regulator_set_mode(regulator, REGULATOR_MODE_STANDBY);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ if (sprd_codec->hp_ver == SPRD_CODEC_HP_PA_VER_1)
+ classg_vol = 1200000;
+ regulator_set_voltage(regulator, classg_vol, classg_vol);
+#endif
+ if (regulator_enable(regulator) < 0) {
+ regulator_set_mode(regulator,
+ REGULATOR_MODE_NORMAL);
+ regulator_disable(regulator);
+ regulator_put(regulator);
+ regulator = 0;
+ }
+ }
+ } else{
+ if (regulator) {
+ sp_asoc_pr_dbg("%s set vddclsg off\n", __func__);
+ /*LDO PD*/
+ regulator_set_mode(regulator, REGULATOR_MODE_NORMAL);
+ regulator_disable(regulator);
+ regulator_put(regulator);
+ regulator = 0;
+ /*CG_EN*/
+ //open clk
+ regulator_reg = regulator_get(0, "VREG");
+ if (IS_ERR(regulator_reg)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(regulator_reg), "VREG");
+ BUG_ON(1);
+ }
+ regulator_set_mode(regulator_reg, REGULATOR_MODE_STANDBY);
+ if (regulator_enable(regulator_reg) < 0) {
+ } else {
+ sprd_codec_hp_classg_en(codec, 0);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ if(regulator_vb) {
+ //close vb
+ regulator_set_mode(regulator_vb, REGULATOR_MODE_NORMAL);
+ regulator_disable(regulator_vb);
+ regulator_put(regulator_vb);
+ regulator_vb = 0;
+ }
+#endif
+ }
+ //close clk
+ regulator_set_mode(regulator_reg,
+ REGULATOR_MODE_NORMAL);
+ regulator_disable(regulator_reg);
+ regulator_put(regulator_reg);
+ regulator_reg = 0;
+ }
+ }
+}
+static int hp_notifier_handler (struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct sprd_codec_priv *sprd_codec = container_of (nb, struct sprd_codec_priv, nb);
+ sprd_inter_headphone_pa_pre (sprd_codec, !!action);
+ return 0;
+}
+
+static int sprd_inter_headphone_pa(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ struct sprd_codec_inter_hp_pa *p_setting =
+ &sprd_codec->inter_hp_pa.setting;
+ /*static struct regulator *regulator = 0;*/
+ sp_asoc_pr_info("inter HP PA Switch %s\n", STR_ON_OFF(on));
+ mutex_lock(&sprd_codec->inter_hp_pa_mutex);
+ if (on) {
+ sprd_codec_hp_pa_lpw(codec, p_setting->class_g_low_power);
+
+ sprd_codec_hp_pa_osc(codec, p_setting->class_g_osc);
+ /* CG_REF_EN */
+ sprd_codec_hp_classg_en(codec, 1);
+ /* LDOCG_EN */
+ sprd_codec_hp_cg_ldo_en(codec, 1);
+ /* CHP EN */
+ sprd_codec_hp_pa_en(codec, 1);
+
+ sprd_codec_ldocg_fal_ild_en(codec, 1);
+ sprd_codec_ldocg_iset_fal_current_sel(codec, 1);
+ sprd_codec_ldocg_ldocg_ramp_en(codec, 1);
+
+ /* Head_L_INT pull up power down */
+ sprd_codec_head_l_int_pu_pd(codec, 1);
+ /* DANGL/R EN */
+ if (AUDIO_2723_VER_AA == sci_get_ana_chip_ver()) {
+ sprd_codec_hp_cg_dangl_en(codec, 1);
+ sprd_codec_hp_cg_dangr_en(codec, 1);
+ }
+ /* Set CG as weifeng's suggestion */
+ sprd_codec_pga_cg_hpl_1_set(codec, 0x10);
+ sprd_codec_pga_cg_hpr_1_set(codec, 0x10);
+ sprd_codec_pga_cg_hpl_2_set(codec, 0x7);
+ sprd_codec_pga_cg_hpr_2_set(codec, 0x7);
+
+ sprd_codec_wait(p_setting->class_g_hp_switch_delay_10ms * 10);
+
+ } else {
+ /* CG_REF disable */
+ sprd_codec_hp_classg_en(codec, 0);
+
+ /* LDOCG disable */
+ sprd_codec_hp_cg_ldo_en(codec, 0);
+ /* CHP disable */
+ sprd_codec_hp_pa_en(codec, 0);
+ sprd_codec_ldocg_fal_ild_en(codec, 0);
+ sprd_codec_ldocg_iset_fal_current_sel(codec, 0);
+ sprd_codec_ldocg_ldocg_ramp_en(codec, 0);
+
+ /* DANGL/R disable */
+ if (AUDIO_2723_VER_AA == sci_get_ana_chip_ver()) {
+ sprd_codec_hp_cg_dangl_en(codec, 0);
+ sprd_codec_hp_cg_dangr_en(codec, 0);
+ }
+ }
+ mutex_unlock(&sprd_codec->inter_hp_pa_mutex);
+ return 0;
+}
+
+static int sprd_inter_headphone_pa_post(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ struct sprd_codec_inter_hp_pa *p_setting =
+ &sprd_codec->inter_hp_pa.setting;
+ mutex_lock(&sprd_codec->inter_hp_pa_mutex);
+ if (on) {
+ /*open classG mute delay time */
+ //sprd_codec_wait(p_setting->class_g_unmute_delay_100ms * 100);
+ /* L/R EN */
+ sprd_codec_hp_pa_mode(codec, p_setting->class_g_mode);
+ sprd_codec_hp_pa_cgcal_en(codec, p_setting->class_g_cgcal);
+
+ sprd_codec_wait(p_setting->class_g_pa_en_delay_10ms * 10);
+ sprd_codec_hp_pa_hpl_en(codec, 1);
+ sprd_codec_hp_pa_hpr_en(codec, 1);
+ sprd_codec_wait(p_setting->class_g_hp_on_delay_20ms * 20);
+
+ /*unmute */
+ sprd_codec_hp_pa_hpl_mute(codec, 0);
+ sprd_codec_hp_pa_hpr_mute(codec, 0);
+
+ sprd_codec->inter_hp_pa.set = 1;
+ } else {
+ sprd_codec->inter_hp_pa.set = 0;
+ /*mute */
+ sprd_codec_hp_pa_hpl_mute(codec, 1);
+ sprd_codec_hp_pa_hpr_mute(codec, 1);
+ sprd_codec_hp_pa_hpr_en(codec, 0);
+ sprd_codec_hp_pa_hpl_en(codec, 0);
+ sprd_codec_wait(10);
+ sprd_codec_hp_pa_cgcal_en(codec, 0);
+ sprd_codec_hp_pa_mode(codec, 0);
+ }
+ mutex_unlock(&sprd_codec->inter_hp_pa_mutex);
+ return 0;
+}
+
+#ifdef CONFIG_SND_SOC_SPRD_AUDIO_USE_INTER_HP_PA_V4
+static int hp_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+ return sprd_inter_headphone_pa(w->codec, on);
+}
+
+static int hp_pa_post_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+ return sprd_inter_headphone_pa_post(w->codec, on);
+}
+#else
+static int hp_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return 0;
+}
+
+static int hp_pa_post_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return 0;
+}
+#endif
+
+static int sprd_codec_set_sample_rate(struct snd_soc_codec *codec, int rate,
+ int mask, int shift)
+{
+ switch (rate) {
+ case 8000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_8000 << shift);
+ break;
+ case 11025:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_11025 << shift);
+ break;
+ case 16000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_16000 << shift);
+ break;
+ case 22050:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_22050 << shift);
+ break;
+ case 32000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_32000 << shift);
+ break;
+ case 44100:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_44100 << shift);
+ break;
+ case 48000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_48000 << shift);
+ break;
+ case 96000:
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), mask,
+ SPRD_CODEC_RATE_96000 << shift);
+ break;
+ default:
+ pr_err("ERR:SPRD-CODEC not support this rate %d\n", rate);
+ break;
+ }
+ sp_asoc_pr_dbg("Set Playback rate 0x%x\n",
+ snd_soc_read(codec, AUD_DAC_CTL));
+ return 0;
+}
+
+static int sprd_codec_set_ad_sample_rate(struct snd_soc_codec *codec, int rate,
+ int mask, int shift)
+{
+ int set;
+#ifdef CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE
+ if (rate == 44100)
+ rate = CONFIG_SND_SOC_VBC_SRC_SAMPLE_RATE;
+#endif
+ set = rate / 4000;
+ if (set > 13) {
+ pr_err("ERR:SPRD-CODEC not support this ad rate %d\n", rate);
+ }
+ snd_soc_update_bits(codec, SOC_REG(AUD_ADC_CTL), mask, set << shift);
+ return 0;
+}
+
+static int sprd_codec_sample_rate_setting(struct sprd_codec_priv *sprd_codec)
+{
+ sp_asoc_pr_dbg("%s AD %d DA %d AD1 %d\n", __func__,
+ sprd_codec->ad_sample_val, sprd_codec->da_sample_val,
+ sprd_codec->ad1_sample_val);
+ if (sprd_codec->ad_sample_val) {
+ sprd_codec_set_ad_sample_rate(sprd_codec->codec,
+ sprd_codec->ad_sample_val, 0x0F,
+ 0);
+ }
+ if (sprd_codec->ad1_sample_val) {
+ sprd_codec_set_ad_sample_rate(sprd_codec->codec,
+ sprd_codec->ad1_sample_val, 0xF0,
+ 4);
+ }
+ if (sprd_codec->da_sample_val) {
+ sprd_codec_set_sample_rate(sprd_codec->codec,
+ sprd_codec->da_sample_val, 0x0F, 0);
+ }
+ return 0;
+}
+
+static inline int sprd_codec_vcom_ldo_auto(struct snd_soc_codec *codec,
+ int init)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ return sprd_codec_auto_ldo_volt(codec, sprd_codec_vcm_v_sel,
+ sprd_codec->s_vcom.v_map,
+ sprd_codec->s_vcom.v_map_size, init);
+}
+
+static inline int sprd_codec_vcom_ldo_cfg(struct sprd_codec_priv *sprd_codec, const struct sprd_codec_ldo_v_map
+ *v_map, const int v_map_size)
+{
+ return _sprd_codec_ldo_cfg_set(&sprd_codec->s_vcom, v_map, v_map_size);
+}
+
+static DEFINE_MUTEX(ldo_mutex);
+static int sprd_codec_ldo_on(struct sprd_codec_priv *sprd_codec)
+{
+ struct regulator **regu = &sprd_codec->head_mic;
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ mutex_lock(&ldo_mutex);
+ atomic_inc(&sprd_codec->ldo_refcount);
+ if (atomic_read(&sprd_codec->ldo_refcount) == 1) {
+ sp_asoc_pr_dbg("LDO ON!\n");
+ if (*regu) {
+ regulator_set_mode(*regu, REGULATOR_MODE_NORMAL);
+ }
+ arch_audio_codec_analog_enable();
+ sprd_codec_vcom_ldo_auto(sprd_codec->codec, 1);
+ }
+ mutex_unlock(&ldo_mutex);
+
+ return 0;
+}
+
+static int sprd_codec_ldo_off(struct sprd_codec_priv *sprd_codec)
+{
+ struct regulator **regu = &sprd_codec->head_mic;
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ mutex_lock(&ldo_mutex);
+ if (atomic_dec_and_test(&sprd_codec->ldo_refcount)) {
+ if (*regu && regulator_is_enabled(*regu)) {
+ regulator_set_mode(*regu, REGULATOR_MODE_STANDBY);
+ }
+ arch_audio_codec_analog_disable();
+ sp_asoc_pr_dbg("LDO OFF!\n");
+ }
+ mutex_unlock(&ldo_mutex);
+
+ return 0;
+}
+
+static int sprd_codec_analog_open(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ uint32_t val = 0;
+ uint32_t mask = 0;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+ sprd_codec_sample_rate_setting(sprd_codec);
+
+ if (is_low_power_support()) {
+ sp_asoc_pr_info("%s get here, true\n", __func__);
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), (ADC_PGA_IBIAS_CTL_MASK << ADC_PGA_IBIAS_CTL_OFFSET), (ADC_PGA_IBIAS_CTL_05 << ADC_PGA_IBIAS_CTL_OFFSET));
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), (DAC_FILTER_IBIS_CTL_MASK << DAC_FILTER_IBIS_CTL_OFFSET), (DAC_FILTER_IBIS_CTL_3_PER_7 << DAC_FILTER_IBIS_CTL_OFFSET));
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), (RCV_IBIS_CTL_MASK << RCV_IBIS_CTL_OFFSET), (RCV_IBIS_CTL_065 << RCV_IBIS_CTL_OFFSET));
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), (HP_CLASSAB_IBIS_CTL_MASK << HP_CLASSAB_IBIS_CTL_OFFSET), (HP_CLASSAB_IBIS_CTL_3_PER_7 << HP_CLASSAB_IBIS_CTL_OFFSET));
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), BIT(AUDIO_POP_CHGR_PD), BIT(AUDIO_POP_CHGR_PD));
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), BIT(AUDIO_POP_SOFTCHG_EN), BIT(AUDIO_POP_SOFTCHG_EN));
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC3), BIT(CG_HPCAL_EN), BIT(CG_HPCAL_EN));
+ /* B Setting */
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC1), (ADC_PGAL_BYP_SEL_MASK << ADCPGAL_BYP), (ADC_PGAL_BYP_SEL_PGAL1_2_ADCL << ADCPGAL_BYP));
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC1), (ADC_PGAR_BYP_SEL_MASK << ADCPGAR_BYP), (ADC_PGAR_BYP_SEL_PGAR1_2_ADCR << ADCPGAR_BYP));
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC10), (DACL_G_MASK << DACL_G), (DACL_G_MINUS_2_5DB << DACL_G));
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC10), (DACR_G_MASK << DACR_G), (DACR_G_MINUS_2_5DB << DACR_G));
+ }
+ /* SC7710/SC8830 ask from ASIC to set initial value */
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU1), BIT(SEL_VCMI),
+ BIT(SEL_VCMI));
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU1), BIT(VCMI_FAST_EN),
+ BIT(VCMI_FAST_EN));
+ /*Bug 362021*/
+ snd_soc_update_bits(codec,SOC_REG(ANA_CDC2),BIT(DACDC_RMV_EN),BIT(DACDC_RMV_EN));
+ snd_soc_update_bits(codec, SOC_REG(ANA_PMU1), BIT(LDOVBO_FAST_EN),
+ BIT(LDOVBO_FAST_EN));
+ snd_soc_write(codec, AUD_SDM_CTL0, 0x500);
+ return ret;
+}
+
+static int sprd_codec_digital_open(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ /* FIXME : Some Clock SYNC bug will cause MUTE */
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL), BIT(DAC_MUTE_EN), 0);
+
+ sprd_codec_sample_rate_setting(sprd_codec);
+
+ /*Bug 362021*/
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_SDM_L), 0xFFFF, 0X9999);
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_SDM_H), 0xFF, 0x1);
+
+ return ret;
+}
+
+static void sprd_codec_power_enable(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ ret = sprd_codec_ldo_on(sprd_codec);
+ if (ret != 0)
+ pr_err("ERR:SPRD-CODEC open ldo error %d\n", ret);
+
+ sprd_codec_analog_open(codec);
+}
+
+static void sprd_codec_power_disable(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ sprd_codec_ldo_off(sprd_codec);
+}
+
+static void sprd_codec_adc_clock_input_en(struct snd_soc_codec *codec, int on)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+
+ sp_asoc_pr_dbg("%s, on:%d \n", __func__, on);
+
+ if (on) {
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC0), BIT(ADC_CLK_EN), BIT(ADC_CLK_EN));
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC0), BIT(ADC_CLK_RST), BIT(ADC_CLK_RST));
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC0), BIT(ADC_CLK_RST), 0);
+ } else {
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC0), BIT(ADC_CLK_EN), 0);
+ }
+}
+
+
+static int digital_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ arch_audio_codec_digital_reg_enable();
+ arch_audio_codec_digital_enable();
+ arch_audio_codec_digital_reset();
+ sprd_codec_digital_open(w->codec);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*maybe ADC module use it, so we cann't close it */
+ /*arch_audio_codec_digital_disable(); */
+ arch_audio_codec_digital_reg_disable();
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int analog_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ sprd_codec_power_enable(codec);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ sprd_codec_power_disable(codec);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int audio_adc_clock_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ sprd_codec_adc_clock_input_en(codec, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ sprd_codec_adc_clock_input_en(codec, 0);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+
+static int chan_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int chan_id = FUN_REG(w->reg);
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+
+ sp_asoc_pr_info("%s %s\n", sprd_codec_chan_get_name(chan_id),
+ STR_ON_OFF(on));
+
+ return 0;
+}
+
+static int dfm_out_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+
+ sp_asoc_pr_info("DFM-OUT %s\n", STR_ON_OFF(on));
+ sprd_codec->ad_sample_val = sprd_codec->da_sample_val;
+ sprd_codec_sample_rate_setting(sprd_codec);
+
+ return 0;
+}
+
+static int _mixer_set_mixer(struct snd_soc_codec *codec, int id, int lr,
+ int try_on, int need_set)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int reg = GET_MIXER_ID(ID_FUN(id, lr));
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[reg]);
+ if (try_on) {
+ mixer->set = mixer_setting[reg];
+ /* NOTES: reduce linein pop noise must open ADCL/R ->HP MIXER
+ AFTER delay 250ms for both ADCL/ADCR switch complete. */
+ if (need_set)
+ return mixer->set(codec, mixer->on);
+ } else {
+ mixer_setting[reg] (codec, 0);
+ mixer->set = 0;
+ }
+ return 0;
+}
+
+static inline int _mixer_setting(struct snd_soc_codec *codec, int start,
+ int end, int lr, int try_on, int need_set)
+{
+ int id;
+ for (id = start; id < end; id++) {
+ _mixer_set_mixer(codec, id, lr, try_on, need_set);
+ }
+ return 0;
+}
+
+static inline int _mixer_setting_one(struct snd_soc_codec *codec, int id,
+ int try_on, int need_set)
+{
+ int lr = id & 0x1;
+ id = (id >>1) + SPRD_CODEC_MIXER_START;
+ return _mixer_setting(codec, id, id + 1, lr, try_on, need_set);
+}
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+static void sprd_codec_hp_pop_irq_enable(struct snd_soc_codec *codec)
+{
+ int val_clr, val_en;
+ val_clr = BIT(AUDIO_POP_IRQ) << AUD_A_INT_CLR;
+ val_en = BIT(AUDIO_POP_IRQ) << AUD_A_INT_EN;
+ snd_soc_update_bits(codec, SOC_REG(DIG_CFG3), val_clr, val_clr);
+ snd_soc_update_bits(codec, SOC_REG(DIG_CFG4), val_en, val_en);
+}
+#endif
+
+static irqreturn_t sprd_codec_ap_irq(int irq, void *dev_id)
+{
+ int mask;
+ struct sprd_codec_priv *sprd_codec = dev_id;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ mask = (snd_soc_read(codec, DIG_STS1) >> AUD_IRQ_MSK);
+ sp_asoc_pr_dbg("HP POP IRQ Mask = 0x%x\n", mask);
+ if (BIT(AUDIO_POP_IRQ) & mask) {
+ snd_soc_update_bits(codec, SOC_REG(DIG_CFG4), BIT(AUDIO_POP_IRQ) << AUD_A_INT_EN, 0);
+ complete(&sprd_codec->completion_hp_pop);
+ }
+ if (BIT(OVP_IRQ) & mask) {
+ snd_soc_update_bits(codec, SOC_REG(DIG_CFG4), BIT(OVP_IRQ) << AUD_A_INT_EN, 0);
+ sprd_codec_ovp_start(sprd_codec, 1);
+ sprd_codec_ovp_irq_enable(codec);
+ }
+ return IRQ_HANDLED;
+}
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+static inline int is_hp_pop_compelet(struct snd_soc_codec *codec)
+{
+ int val;
+ val = snd_soc_read(codec, ANA_STS0);
+ val = (val >> HP_POP_FLG) & HP_POP_FLG_MASK;
+ sp_asoc_pr_dbg("HP POP= 0x%x\n", val);
+ return HP_POP_FLG_NEAR_CMP == val;
+}
+
+static inline int hp_pop_wait_for_compelet(struct snd_soc_codec *codec)
+{
+ int i;
+ int hp_pop_complete;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ hp_pop_complete = msecs_to_jiffies(SPRD_CODEC_HP_POP_TIMEOUT);
+ for (i = 0; i < 2; i++) {
+ sp_asoc_pr_dbg("HP POP %d IRQ Enable\n", i);
+ sprd_codec_hp_pop_irq_enable(codec);
+ init_completion(&sprd_codec->completion_hp_pop);
+ hp_pop_complete =
+ wait_for_completion_timeout(&sprd_codec->completion_hp_pop,
+ hp_pop_complete);
+ sp_asoc_pr_dbg("HP POP %d Completion %d\n", i, hp_pop_complete);
+ if (!hp_pop_complete) {
+ if (!is_hp_pop_compelet(codec)) {
+ pr_err("ERR:HP POP %d timeout not complete\n",
+ i);
+ } else {
+ pr_err("ERR:HP POP %d timeout but complete\n",
+ i);
+ }
+ } else {
+ /* 01 change to 10 maybe walk to 11 shortly,
+ so, check it double. */
+ sprd_codec_wait(2);
+ if (is_hp_pop_compelet(codec)) {
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+static int hp_pop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int mask;
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mask = HP_POP_STEP_MASK << HP_POP_STEP;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), mask,
+ HP_POP_STEP_2 << HP_POP_STEP);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), mask,
+ HP_POP_CTL_UP << HP_POP_CTL);
+ sp_asoc_pr_dbg("U PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, ANA_CDC16));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ mask = HP_POP_STEP_MASK << HP_POP_STEP;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), mask,
+ HP_POP_STEP_1 << HP_POP_STEP);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), mask,
+ HP_POP_CTL_DOWN << HP_POP_CTL);
+ sp_asoc_pr_dbg("D PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, ANA_CDC16));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ ret = hp_pop_wait_for_compelet(codec);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), mask,
+ HP_POP_CTL_HOLD << HP_POP_CTL);
+ sp_asoc_pr_dbg("HOLD PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, ANA_CDC16));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = hp_pop_wait_for_compelet(codec);
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), mask,
+ HP_POP_CTL_DIS << HP_POP_CTL);
+ sp_asoc_pr_dbg("DIS PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, ANA_CDC16));
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+#else
+#ifndef CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME
+#define CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME (0)
+#endif
+static int hp_pop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+ sp_asoc_pr_dbg("%s Event is %s wait %dms\n", __func__,
+ get_event_name(event),
+ CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ sprd_codec_wait(CONFIG_SND_SOC_SPRD_HP_POP_DELAY_TIME);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+ return ret;
+}
+#endif
+
+static int hp_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ int mask;
+#endif
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+#if 1 /* enable the diff function in shark from weifeng.ni */
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC2), BIT(DIFF_EN),
+ BIT(DIFF_EN));
+ /* FIXME: change by jian.chen */
+/*
+
+ snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(HP_VCMI_EN),
+ BIT(HP_VCMI_EN));
+*/
+#endif
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), mask,
+ HP_POP_CTL_DIS << HP_POP_CTL);
+ sp_asoc_pr_dbg("DIS(en) PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, ANA_CDC16));
+#endif
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC2), BIT(DIFF_EN), 0);
+ /* FIXME: change by jian.chen */
+ /*
+ snd_soc_update_bits(codec, SOC_REG(DCR2_DCR1), BIT(HP_VCMI_EN), 0);
+ */
+
+#ifndef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ mask = HP_POP_CTL_MASK << HP_POP_CTL;
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC16), mask,
+ HP_POP_CTL_HOLD << HP_POP_CTL);
+ sp_asoc_pr_dbg("HOLD(en) PNRCR1 = 0x%x\n",
+ snd_soc_read(codec, ANA_CDC16));
+#endif
+ goto _pre_pmd;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* do nothing now */
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ _mixer_setting(codec, SPRD_CODEC_HP_DACL,
+ SPRD_CODEC_HP_DAC_MIXER_MAX, SPRD_CODEC_LEFT,
+ snd_soc_read(codec, ANA_CDC2) & BIT(HPL_EN), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_HP_DACL,
+ SPRD_CODEC_HP_DAC_MIXER_MAX, SPRD_CODEC_RIGHT,
+ snd_soc_read(codec, ANA_CDC2) & BIT(HPR_EN), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_HP_ADCL,
+ SPRD_CODEC_HP_MIXER_MAX, SPRD_CODEC_LEFT,
+ snd_soc_read(codec, ANA_CDC2) & BIT(HPL_EN), 0);
+
+ _mixer_setting(codec, SPRD_CODEC_HP_ADCL,
+ SPRD_CODEC_HP_MIXER_MAX, SPRD_CODEC_RIGHT,
+ snd_soc_read(codec, ANA_CDC2) & BIT(HPR_EN), 0);
+
+_pre_pmd:
+
+ return ret;
+}
+
+static int spk_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ if (snd_soc_read(codec, ANA_CDC2) & BIT(AOL_EN)) {
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ sprd_codec_pa_sw_set(codec, SPRD_CODEC_PA_SW_AOL);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ sprd_codec_pa_sw_clr(codec, SPRD_CODEC_PA_SW_AOL);
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_DACL,
+ SPRD_CODEC_SPK_DAC_MIXER_MAX, SPRD_CODEC_LEFT,
+ (snd_soc_read(codec, ANA_CDC2) & BIT(AOL_EN)), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_DACL,
+ SPRD_CODEC_SPK_DAC_MIXER_MAX, SPRD_CODEC_RIGHT,
+ (snd_soc_read(codec, ANA_CDC2) & BIT(AOR_EN)), 1);
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_ADCL,
+ SPRD_CODEC_SPK_MIXER_MAX, SPRD_CODEC_LEFT,
+ (snd_soc_read(codec, ANA_CDC2) & BIT(AOL_EN)), 0);
+
+ _mixer_setting(codec, SPRD_CODEC_SPK_ADCL,
+ SPRD_CODEC_SPK_MIXER_MAX, SPRD_CODEC_RIGHT,
+ (snd_soc_read(codec, ANA_CDC2) & BIT(AOR_EN)), 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_EAR_WITH_IN_SPK
+static int ear_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ sprd_codec_pa_sw_set(w->codec, SPRD_CODEC_PA_SW_EAR);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ sprd_codec_pa_sw_clr(w->codec, SPRD_CODEC_PA_SW_EAR);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+#endif
+
+static int adcpgal_set(struct snd_soc_codec *codec, int on)
+{
+ if (is_low_power_support()) {
+ int mask = ADCPGAL_EN_MASK << ADCPGAL_EN;
+ int value = 0x2 << ADCPGAL_EN;
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC1), mask,
+ on ? value : 0);
+ } else {
+ int mask = ADCPGAL_EN_MASK << ADCPGAL_EN;
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC1), mask,
+ on ? mask : 0);
+ }
+}
+
+static int adcpgar_set(struct snd_soc_codec *codec, int on)
+{
+ if (is_low_power_support()) {
+ int mask = ADCPGAR_EN_MASK << ADCPGAR_EN;
+ int value = 0x2 << ADCPGAR_EN;
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC1), mask,
+ on ? value : 0);
+ } else {
+ int mask = ADCPGAR_EN_MASK << ADCPGAR_EN;
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC1), mask,
+ on ? mask : 0);
+ }
+}
+
+static int adc_switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int is_right = (w->shift == SPRD_CODEC_RIGHT);
+ int on = (! !SND_SOC_DAPM_EVENT_ON(event));
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ if (is_right) {
+ adcpgar_set(codec, on);
+ _mixer_setting(codec, SPRD_CODEC_AIL, SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_RIGHT, on, 1);
+ } else {
+ adcpgal_set(codec, on);
+ _mixer_setting(codec, SPRD_CODEC_AIL, SPRD_CODEC_ADC_MIXER_MAX,
+ SPRD_CODEC_LEFT, on, 1);
+ }
+
+ return 0;
+}
+
+static int pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = GET_PGA_ID(FUN_REG(w->reg));
+ struct sprd_codec_pga_op *pga = &(sprd_codec->pga[id]);
+ int ret = 0;
+ int min = sprd_codec_pga_cfg[id].min;
+
+ sp_asoc_pr_dbg("%s Set %s(%d) Event is %s\n", __func__,
+ sprd_codec_pga_debug_str[id], pga->pgaval,
+ get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ pga->set = sprd_codec_pga_cfg[id].set;
+ ret = pga->set(codec, pga->pgaval);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ pga->set = 0;
+ if(min >= 0)
+ ret = sprd_codec_pga_cfg[id].set(codec, min);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int switch_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = GET_SWITCH_ID(FUN_REG(w->reg));
+ int ret = 0;
+ int min = 0;
+ struct sprd_codec_switch_op *switcher = &(sprd_codec->switcher[id]);
+ sp_asoc_pr_info("%s Switch %s(%s) Event is %s\n", __func__,
+ switch_name[id], switcher->on? "on":"off",
+ get_event_name(event));
+ min = sprd_codec_switch_cfg[id].min;
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switcher->set = sprd_codec_switch_cfg[id].set;
+ ret = switcher->set(codec, switcher->on);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ switcher->set = 0;
+ if(min >= 0)
+ ret = sprd_codec_switch_cfg[id].set(codec, min);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int adcpgar_byp_set(struct snd_soc_codec *codec, int value)
+{
+ int mask = ADCPGAR_BYP_MASK << ADCPGAR_BYP;
+ return snd_soc_update_bits(codec, SOC_REG(ANA_CDC1), mask,
+ value << ADCPGAR_BYP);
+}
+
+static int adcpgar_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s Event is %s\n", __func__, get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ adcpgar_byp_set(codec, 2);
+ sprd_codec_wait(100);
+ if (is_low_power_support())
+ adcpgar_byp_set(codec, 1);
+ else
+ adcpgar_byp_set(codec, 0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int ana_loop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(w->reg));
+ struct sprd_codec_mixer *mixer;
+ int ret = 0;
+ static int s_need_wait = 1;
+
+ sp_asoc_pr_dbg("Entering %s event is %s\n", __func__,
+ get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* NOTES: reduce linein pop noise must open ADCL/R ->HP/SPK MIXER
+ AFTER delay 250ms for both ADCL/ADCR switch complete.
+ */
+ if (s_need_wait == 1) {
+ /* NOTES: reduce linein pop noise must delay 250ms
+ after linein mixer switch on.
+ actually this function perform after
+ adc_switch_event function for
+ both ADCL/ADCR switch complete.
+ */
+ sprd_codec_wait(250);
+ s_need_wait++;
+ sp_asoc_pr_dbg("ADC Switch ON delay\n");
+ }
+ mixer = &(sprd_codec->mixer[id]);
+ if (mixer->set) {
+ mixer->set(codec, mixer->on);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ case SND_SOC_DAPM_POST_PMD:
+ s_need_wait = 1;
+ mixer = &(sprd_codec->mixer[id]);
+ if (mixer->set) {
+ mixer->set(codec, mixer->on);
+ }
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int reg = FUN_REG(w->reg);
+ int ret = 0;
+ int on = ! !SND_SOC_DAPM_EVENT_ON(event);
+ struct regulator **regu;
+
+ sp_asoc_pr_dbg("%s %s Event is %s\n", __func__,
+ mic_bias_name[GET_MIC_BIAS_ID(reg)],
+ get_event_name(event));
+
+ switch (reg) {
+ case SPRD_CODEC_MIC_BIAS:
+ regu = &sprd_codec->main_mic;
+ break;
+ case SPRD_CODEC_AUXMIC_BIAS:
+ regu = &sprd_codec->aux_mic;
+ break;
+ case SPRD_CODEC_HEADMIC_BIAS:
+ regu = &sprd_codec->head_mic;
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+
+ ret = sprd_codec_regulator_set(regu, on);
+
+ return ret;
+}
+
+static int mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(w->reg));
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s event is %s\n", sprd_codec_mixer_debug_str[id],
+ get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mixer->on = 1;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ mixer->on = 0;
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ }
+ if (ret >= 0)
+ _mixer_setting_one(codec, id, mixer->on, 1);
+
+ return ret;
+}
+
+static int mixer_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_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = wlist->widgets[0]->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(mc->reg));
+ ucontrol->value.integer.value[0] = sprd_codec->mixer[id].on;
+ return 0;
+}
+
+static int mixer_need_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol, bool need_set)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = wlist->widgets[0]->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(mc->reg));
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]);
+ int ret = 0;
+
+ if (mixer->on == ucontrol->value.integer.value[0])
+ return 0;
+
+ sp_asoc_pr_info("Set %s Switch %s\n", sprd_codec_mixer_debug_str[id],
+ STR_ON_OFF(ucontrol->value.integer.value[0]));
+
+ /*notice the sequence */
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ /*update reg: must be set after snd_soc_dapm_put_enum_double->change = snd_soc_test_bits(widget->codec, e->reg, mask, val); */
+
+ if (mixer->set && need_set)
+ ret = mixer->set(codec, mixer->on);
+
+ return ret;
+}
+
+static int mixer_set_mem(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return mixer_need_set(kcontrol, ucontrol, 0);
+}
+
+static int mixer_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return mixer_need_set(kcontrol, ucontrol, 1);
+}
+
+static int adie_loop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ unsigned int id = GET_ADC_LOOP_ID(FUN_REG(w->reg));
+ int ret = 0;
+
+ sp_asoc_pr_info("%s, %s, Event is %s\n",
+ __func__, adc_loop_name[id], get_event_name(event));
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* enable loop clock */
+ ret = arch_audio_codec_adie_loop_clk_en(1);
+ snd_soc_update_bits(codec, SOC_REG(DIG_CFG0), BIT(AUDIO_ADIE_LOOP_EN), 1 << AUDIO_ADIE_LOOP_EN);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* disable loop clock */
+ ret = arch_audio_codec_adie_loop_clk_en(0);
+ snd_soc_update_bits(codec, SOC_REG(DIG_CFG0), BIT(AUDIO_ADIE_LOOP_EN), 0 << AUDIO_ADIE_LOOP_EN);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int digital_loop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ unsigned int id = GET_ADC_LOOP_ID(FUN_REG(w->reg));
+ int ret = 0;
+
+ sp_asoc_pr_info("%s, %s, Event is %s\n",
+ __func__, adc_loop_name[id], get_event_name(event));
+
+ if (SPRD_CODEC_ADC_DAC_DIGITAL_LOOP == FUN_REG(w->reg))
+ sprd_codec_set_ad_sample_rate(codec, 48000, 0x0F, 0);
+ else if (SPRD_CODEC_ADC1_DAC_DIGITAL_LOOP == FUN_REG(w->reg))
+ sprd_codec_set_ad_sample_rate(codec, 48000, 0xF0, 4);
+ else {
+ sp_asoc_pr_info("%s, wrong id is %d\n", __func__, id);
+ return -EINVAL;
+ }
+ sprd_codec_set_sample_rate(codec, 48000, 0x0F, 0);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, SOC_REG(AUD_LOOP_CTL), BIT(LOOP_ADC_PATH_SEL),
+ ((id-2) << LOOP_ADC_PATH_SEL));
+ snd_soc_update_bits(codec, SOC_REG(AUD_LOOP_CTL), BIT(AUD_LOOP_TEST), 1 << AUD_LOOP_TEST);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, SOC_REG(AUD_LOOP_CTL), BIT(AUD_LOOP_TEST), 0 << AUD_LOOP_TEST);
+ break;
+ default:
+ BUG();
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int sprd_codec_adie_loop_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_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = wlist->widgets[0]->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int id = GET_ADC_LOOP_ID(FUN_REG(mc->reg));
+ int ret = 0;
+
+ ret = !!(ucontrol->value.integer.value[0]);
+ if (ret == sprd_codec->ad_da_loop_en[id]) {
+ return ret;
+ }
+ sp_asoc_pr_info("%s, %s, set %d \n",
+ __func__, adc_loop_name[id], (int)ucontrol->value.integer.value[0]);
+ /*notice the sequence */
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+ sprd_codec->ad_da_loop_en[id] = ret;
+ return ret;
+}
+
+static int sprd_codec_adie_loop_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_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = wlist->widgets[0]->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int id = GET_ADC_LOOP_ID(FUN_REG(mc->reg));
+
+ ucontrol->value.integer.value[0] = !!sprd_codec->ad_da_loop_en[id];
+ return 0;
+}
+
+static int sprd_codec_digital_loop_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_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = wlist->widgets[0]->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int id = GET_ADC_LOOP_ID(FUN_REG(mc->reg));
+ int ret = 0;
+
+ ret = !!(ucontrol->value.integer.value[0]);
+ if (ret == sprd_codec->ad_da_loop_en[id]) {
+ return ret;
+ }
+ sp_asoc_pr_info("%s, %s, set %d \n",
+ __func__, adc_loop_name[id], (int)ucontrol->value.integer.value[0]);
+
+ /*notice the sequence */
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+ sprd_codec->ad_da_loop_en[id] = ret;
+ return ret;
+}
+
+static int sprd_codec_digital_loop_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_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = wlist->widgets[0]->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int id = GET_ADC_LOOP_ID(FUN_REG(mc->reg));
+
+ ucontrol->value.integer.value[0] = !!sprd_codec->ad_da_loop_en[id];
+ return 0;
+}
+#define SPRD_CODEC_MIXER(xname, xreg)\
+ SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, mixer_get, mixer_set)
+
+/*Just for LINE IN path, mixer_set not really set mixer (ADCL/R -> HP/SPK L/R) here but
+setting in ana_loop_event, just remeber state here*/
+#define SPRD_CODEC_MIXER_NOSET(xname, xreg)\
+ SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, mixer_get, mixer_set_mem)
+
+/* ADCL Mixer */
+static const struct snd_kcontrol_new adcl_mixer_controls[] = {
+ SPRD_CODEC_MIXER("AILADCL Switch",
+ ID_FUN(SPRD_CODEC_AIL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("AIRADCL Switch",
+ ID_FUN(SPRD_CODEC_AIR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("MainMICADCL Switch",
+ ID_FUN(SPRD_CODEC_MAIN_MIC, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("AuxMICADCL Switch",
+ ID_FUN(SPRD_CODEC_AUX_MIC, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("HPMICADCL Switch",
+ ID_FUN(SPRD_CODEC_HP_MIC, SPRD_CODEC_LEFT)),
+};
+
+/* ADCR Mixer */
+static const struct snd_kcontrol_new adcr_mixer_controls[] = {
+ SPRD_CODEC_MIXER("AILADCR Switch",
+ ID_FUN(SPRD_CODEC_AIL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("AIRADCR Switch",
+ ID_FUN(SPRD_CODEC_AIR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("MainMICADCR Switch",
+ ID_FUN(SPRD_CODEC_MAIN_MIC, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("AuxMICADCR Switch",
+ ID_FUN(SPRD_CODEC_AUX_MIC, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("HPMICADCR Switch",
+ ID_FUN(SPRD_CODEC_HP_MIC, SPRD_CODEC_RIGHT)),
+};
+
+/* HPL Mixer */
+static const struct snd_kcontrol_new hpl_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_DACL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("DACRHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_DACR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRHPL Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_LEFT)),
+};
+
+/* HPR Mixer */
+static const struct snd_kcontrol_new hpr_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_DACL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("DACRHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_DACR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRHPR Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_RIGHT)),
+};
+
+/* SPKL Mixer */
+static const struct snd_kcontrol_new spkl_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER("DACRSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRSPKL Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_LEFT)),
+};
+
+/* SPKR Mixer */
+static const struct snd_kcontrol_new spkr_mixer_controls[] = {
+ SPRD_CODEC_MIXER("DACLSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER("DACRSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_DACR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCLSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_MIXER_NOSET("ADCRSPKR Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_RIGHT)),
+};
+
+static const struct snd_kcontrol_new sprd_codec_loop_controls[] = {
+ SOC_SINGLE_EXT("switch", FUN_REG(SPRD_CODEC_ADC_DAC_ADIE_LOOP), 0, 1, 0,
+ sprd_codec_adie_loop_get, sprd_codec_adie_loop_put),
+ SOC_SINGLE_EXT("switch", FUN_REG(SPRD_CODEC_ADC1_DAC_ADIE_LOOP), 0, 1, 0,
+ sprd_codec_adie_loop_get, sprd_codec_adie_loop_put),
+ SOC_SINGLE_EXT("switch", FUN_REG(SPRD_CODEC_ADC_DAC_DIGITAL_LOOP), 0, 1, 0,
+ sprd_codec_digital_loop_get, sprd_codec_digital_loop_put),
+ SOC_SINGLE_EXT("switch", FUN_REG(SPRD_CODEC_ADC1_DAC_DIGITAL_LOOP), 0, 1, 0,
+ sprd_codec_digital_loop_get, sprd_codec_digital_loop_put),
+};
+/*ANA LOOP SWITCH*/
+#define SPRD_CODEC_LOOP_SWITCH(xname, xreg)\
+ SND_SOC_DAPM_PGA_S(xname, SPRD_CODEC_ANA_MIXER_ORDER, FUN_REG(xreg), 0, 0, ana_loop_event,\
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#ifndef SND_SOC_DAPM_MICBIAS_E
+#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
+{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
+ .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .event = wevent, .event_flags = wflags}
+#endif
+
+static const struct snd_soc_dapm_widget sprd_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VBO", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VCOM_BUF", 0, 0),
+ SND_SOC_DAPM_SUPPLY_S("Digital Power", 1, SND_SOC_NOPM, 0, 0,
+ digital_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Analog Power", 2, SND_SOC_NOPM, 0, 0,
+ analog_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("DA Clk", 3, SOC_REG(ANA_CDC0), DAC_CLK_EN, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("DRV Clk", 4, SOC_REG(ANA_CDC0), DRV_CLK_EN, 0, NULL,
+ 0),
+ SND_SOC_DAPM_SUPPLY_S("AD IBUF", 3, SOC_REG(ANA_CDC1), ADC_IBUF_PD,
+ 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AD Clk", 3, SND_SOC_NOPM, 0, 0,
+ audio_adc_clock_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_S("Digital DACL Switch", 4, SOC_REG(AUD_TOP_CTL),
+ DAC_EN_L, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Digital DACR Switch", 4, SOC_REG(AUD_TOP_CTL),
+ DAC_EN_R, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADie Digital DACL Switch", 5, SOC_REG(DIG_CFG0),
+ AUDIFA_DACL_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADie Digital DACR Switch", 5, SOC_REG(DIG_CFG0),
+ AUDIFA_DACR_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("DACL Switch", 6, FUN_REG(SPRD_CODEC_DACL), 0, 0,
+ switch_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("DACR Switch", 6, FUN_REG(SPRD_CODEC_DACR), 0, 0,
+ switch_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("DACL Mute", 6, FUN_REG(SPRD_CODEC_PGA_DACL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("DACR Mute", 6, FUN_REG(SPRD_CODEC_PGA_DACR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("DAC", "Playback-Vaudio",
+ FUN_REG(SPRD_CODEC_PLAYBACK), 0,
+ 0,
+ chan_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DFM-OUT", "DFM-Output",
+ SND_SOC_NOPM, 0,
+ 0,
+ dfm_out_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ SND_SOC_DAPM_PGA_S("HP POP", SPRD_CODEC_HP_POP_ORDER, SND_SOC_NOPM, 0,
+ 0, hp_pop_event,
+ SND_SOC_DAPM_POST_PMU),
+#else
+ SND_SOC_DAPM_SUPPLY_S("HP POP", 5, SND_SOC_NOPM, 0, 0, hp_pop_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+#endif
+ SND_SOC_DAPM_PGA_S("HPL Switch", SPRD_CODEC_HP_SWITCH_ORDER,
+ SOC_REG(ANA_CDC2), HPL_EN, 0,
+ hp_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPR Switch", SPRD_CODEC_HP_SWITCH_ORDER,
+ SOC_REG(ANA_CDC2), HPR_EN, 0,
+ hp_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPL Mute", 6, FUN_REG(SPRD_CODEC_PGA_HPL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPR Mute", 6, FUN_REG(SPRD_CODEC_PGA_HPR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0,
+ &hpl_mixer_controls[0],
+ ARRAY_SIZE(hpl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0,
+ &hpr_mixer_controls[0],
+ ARRAY_SIZE(hpr_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SPKL Mixer", SND_SOC_NOPM, 0, 0,
+ &spkl_mixer_controls[0],
+ ARRAY_SIZE(spkl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SPKR Mixer", SND_SOC_NOPM, 0, 0,
+ &spkr_mixer_controls[0],
+ ARRAY_SIZE(spkr_mixer_controls)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLHPL Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRHPL Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLHPR Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRHPR Loop Switch",
+ ID_FUN(SPRD_CODEC_HP_ADCR, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLSPKL Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRSPKL Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_LEFT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCLSPKR Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCL, SPRD_CODEC_RIGHT)),
+ SPRD_CODEC_LOOP_SWITCH("ADCRSPKR Loop Switch",
+ ID_FUN(SPRD_CODEC_SPK_ADCR, SPRD_CODEC_RIGHT)),
+ SND_SOC_DAPM_PGA_S("SPKL Switch", 5, SOC_REG(ANA_CDC2), AOL_EN, 0,
+ spk_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("SPKR Switch", 5, SOC_REG(ANA_CDC2), AOR_EN, 0,
+ spk_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("SPKL Mute", 6, FUN_REG(SPRD_CODEC_PGA_SPKL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("SPKR Mute", 6, FUN_REG(SPRD_CODEC_PGA_SPKR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("EAR Mixer", 5,
+ FUN_REG(ID_FUN
+ (SPRD_CODEC_EAR_DACL, SPRD_CODEC_LEFT)), 0,
+ 0, mixer_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_EAR_WITH_IN_SPK
+ SND_SOC_DAPM_PGA_S("EAR Switch", 6, SOC_REG(ANA_CDC2), EAR_EN, 0,
+ ear_switch_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+#else
+ SND_SOC_DAPM_PGA_S("EAR Switch", 6, SOC_REG(ANA_CDC2), EAR_EN, 0, NULL,
+ 0),
+#endif
+
+ SND_SOC_DAPM_PGA_S("EAR Mute", 7, FUN_REG(SPRD_CODEC_PGA_EAR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MIXER("ADCL Mixer", SND_SOC_NOPM, 0, 0,
+ &adcl_mixer_controls[0],
+ ARRAY_SIZE(adcl_mixer_controls)),
+ SND_SOC_DAPM_MIXER("ADCR Mixer", SND_SOC_NOPM, 0, 0,
+ &adcr_mixer_controls[0],
+ ARRAY_SIZE(adcr_mixer_controls)),
+ SND_SOC_DAPM_PGA_S("Digital ADCL Switch", 3, SOC_REG(AUD_TOP_CTL),
+ ADC_EN_L, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Digital ADCR Switch", 3, SOC_REG(AUD_TOP_CTL),
+ ADC_EN_R, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADie Digital ADCL Switch", 2, SOC_REG(DIG_CFG0),
+ AUDIFA_ADCL_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADie Digital ADCR Switch", 2, SOC_REG(DIG_CFG0),
+ AUDIFA_ADCR_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADCL Switch", 1, SOC_REG(ANA_CDC1), ADCL_PD, 1,
+ NULL,
+ 0),
+ SND_SOC_DAPM_PGA_S("ADCR Switch", 1, SOC_REG(ANA_CDC1), ADCR_PD, 1,
+ adcpgar_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("ADCL PGA", SND_SOC_NOPM, SPRD_CODEC_LEFT, 0, NULL,
+ 0,
+ adc_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ADCR PGA", SND_SOC_NOPM, SPRD_CODEC_RIGHT, 0, NULL,
+ 0,
+ adc_switch_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADCL Mute", 3, FUN_REG(SPRD_CODEC_PGA_ADCL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADCR Mute", 3, FUN_REG(SPRD_CODEC_PGA_ADCR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("ADC", "Main-Capture-Vaudio",
+ FUN_REG(SPRD_CODEC_CAPTRUE),
+ 0, 0,
+ chan_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_S("MIC Boost", 3, FUN_REG(SPRD_CODEC_PGA_MIC), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("AUXMIC Boost", 3, FUN_REG(SPRD_CODEC_PGA_AUXMIC), 0,
+ 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HEADMIC Boost", 3, FUN_REG(SPRD_CODEC_PGA_HEADMIC),
+ 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("AIL Boost", 3, FUN_REG(SPRD_CODEC_PGA_AIL), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("AIR Boost", 3, FUN_REG(SPRD_CODEC_PGA_AIR), 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MICBIAS_E("Mic Bias", FUN_REG(SPRD_CODEC_MIC_BIAS), 0, 0,
+ mic_bias_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MICBIAS_E("AuxMic Bias", FUN_REG(SPRD_CODEC_AUXMIC_BIAS),
+ 0, 0,
+ mic_bias_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MICBIAS_E("HeadMic Bias", FUN_REG(SPRD_CODEC_HEADMIC_BIAS),
+ 0, 0,
+ mic_bias_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("Digital ADC1L Switch", 5, SOC_REG(AUD_TOP_CTL),
+ ADC1_EN_L, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Digital ADC1R Switch", 5, SOC_REG(AUD_TOP_CTL),
+ ADC1_EN_R, 0, NULL, 0),
+ SND_SOC_DAPM_ADC_E("ADC1", "Ext-Capture-Vaudio",
+ FUN_REG(SPRD_CODEC_CAPTRUE1),
+ 0, 0,
+ chan_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /*add DMIC */
+ SND_SOC_DAPM_PGA_S("Digital ADC DMIC In", 4, SOC_REG(AUD_TOP_CTL),
+ ADC_DMIC_SEL, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Digital ADC1 DMIC In", 4, SOC_REG(AUD_TOP_CTL),
+ ADC1_DMIC1_SEL, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("DMIC Switch", 3, SOC_REG(AUD_DMIC_CTL),
+ ADC_DMIC_EN, 0,
+ NULL, 0),
+ SND_SOC_DAPM_PGA_S("DMIC1 Switch", 3, SOC_REG(AUD_DMIC_CTL),
+ ADC1_DMIC1_EN, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_PGA_S("HP PA Switch", SPRD_CODEC_HP_PA_ORDER, SND_SOC_NOPM,
+ 0, 0, hp_pa_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("HP PA POST Switch", SPRD_CODEC_HP_PA_POST_ORDER,
+ SND_SOC_NOPM, 0, 0, hp_pa_post_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Spk PA Switch", SPRD_CODEC_PA_ORDER, SND_SOC_NOPM,
+ 0, 0,
+ spk_pa_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("HPL CGL Switch", SPRD_CODEC_HP_PA_POST_ORDER, FUN_REG(SPRD_CODEC_HPLCGL),
+ HPL_CGL, 0, switch_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPR CGR Switch", SPRD_CODEC_HP_PA_POST_ORDER, FUN_REG(SPRD_CODEC_HPRCGR),
+ HPR_CGR, 0, switch_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPL CG Mute1", SPRD_CODEC_CG_PGA_ORDER, FUN_REG(SPRD_CODEC_PGA_CG_HPL_1),
+ 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPR CG Mute1", SPRD_CODEC_CG_PGA_ORDER, FUN_REG(SPRD_CODEC_PGA_CG_HPR_1),
+ 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPL CG Mute2", SPRD_CODEC_CG_PGA_ORDER, FUN_REG(SPRD_CODEC_PGA_CG_HPL_2),
+ 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("HPR CG Mute2", SPRD_CODEC_CG_PGA_ORDER, FUN_REG(SPRD_CODEC_PGA_CG_HPR_2),
+ 0, 0,
+ pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SWITCH("ADC-DAC Adie Loop", FUN_REG(SPRD_CODEC_ADC_DAC_ADIE_LOOP), 0, 0, &sprd_codec_loop_controls[0]),
+ SND_SOC_DAPM_SWITCH("ADC1-DAC Adie Loop", FUN_REG(SPRD_CODEC_ADC1_DAC_ADIE_LOOP), 0, 0, &sprd_codec_loop_controls[1]),
+ SND_SOC_DAPM_SWITCH("ADC-DAC Digital Loop", FUN_REG(SPRD_CODEC_ADC_DAC_DIGITAL_LOOP), 0, 0, &sprd_codec_loop_controls[2]),
+ SND_SOC_DAPM_SWITCH("ADC1-DAC Digital Loop", FUN_REG(SPRD_CODEC_ADC1_DAC_DIGITAL_LOOP), 0, 0, &sprd_codec_loop_controls[3]),
+
+ SND_SOC_DAPM_PGA_S("ADC-DAC Adie Loop post", SPRD_CODEC_CG_PGA_ORDER, FUN_REG(SPRD_CODEC_ADC_DAC_ADIE_LOOP),
+ 0, 0,
+ adie_loop_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADC1-DAC Adie Loop post", SPRD_CODEC_CG_PGA_ORDER, FUN_REG(SPRD_CODEC_ADC1_DAC_ADIE_LOOP),
+ 0, 0,
+ adie_loop_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADC-DAC Digital Loop post", SPRD_CODEC_CG_PGA_ORDER, FUN_REG(SPRD_CODEC_ADC_DAC_DIGITAL_LOOP),
+ 0, 0,
+ digital_loop_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADC1-DAC Digital Loop post", SPRD_CODEC_CG_PGA_ORDER, FUN_REG(SPRD_CODEC_ADC1_DAC_DIGITAL_LOOP),
+ 0, 0,
+ digital_loop_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_OUTPUT("HEAD_P_L"),
+ SND_SOC_DAPM_OUTPUT("HEAD_P_R"),
+ SND_SOC_DAPM_OUTPUT("AOL"),
+ SND_SOC_DAPM_OUTPUT("AOR"),
+ SND_SOC_DAPM_OUTPUT("EAR"),
+
+ SND_SOC_DAPM_OUTPUT("HP PA"),
+ SND_SOC_DAPM_OUTPUT("Spk PA"),
+ SND_SOC_DAPM_OUTPUT("Spk2 PA"),
+
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_INPUT("AUXMIC"),
+ SND_SOC_DAPM_INPUT("HPMIC"),
+ SND_SOC_DAPM_INPUT("AIL"),
+ SND_SOC_DAPM_INPUT("AIR"),
+};
+
+/* sprd_codec supported interconnection*/
+static const struct snd_soc_dapm_route sprd_codec_intercon[] = {
+ /* Power */
+ {"Analog Power", NULL, "VCOM_BUF"},
+ {"Analog Power", NULL, "VBO"},
+ {"DA Clk", NULL, "Analog Power"},
+ {"DA Clk", NULL, "Digital Power"},
+ {"DAC", NULL, "DA Clk"},
+ {"DFM-OUT", NULL, "DA Clk"},
+
+ {"AD IBUF", NULL, "Analog Power"},
+ {"AD Clk", NULL, "Digital Power"},
+ {"AD Clk", NULL, "AD IBUF"},
+ {"ADC", NULL, "AD Clk"},
+
+ {"ADC1", NULL, "AD Clk"},
+
+ {"ADCL PGA", NULL, "AD IBUF"},
+ {"ADCR PGA", NULL, "AD IBUF"},
+
+ {"HP POP", NULL, "DRV Clk"},
+ {"HPL Switch", NULL, "DRV Clk"},
+ {"HPR Switch", NULL, "DRV Clk"},
+ {"SPKL Switch", NULL, "DRV Clk"},
+ {"SPKR Switch", NULL, "DRV Clk"},
+ {"EAR Switch", NULL, "DRV Clk"},
+
+ /* Playback */
+ {"Digital DACL Switch", NULL, "DFM-OUT"},
+ {"Digital DACR Switch", NULL, "DFM-OUT"},
+ {"Digital DACL Switch", NULL, "DAC"},
+ {"Digital DACR Switch", NULL, "DAC"},
+ {"ADie Digital DACL Switch", NULL, "Digital DACL Switch"},
+ {"ADie Digital DACR Switch", NULL, "Digital DACR Switch"},
+ {"DACL Mute", NULL, "ADie Digital DACL Switch"},
+ {"DACR Mute", NULL, "ADie Digital DACR Switch"},
+ {"DACL Switch", NULL, "DACL Mute"},
+ {"DACR Switch", NULL, "DACR Mute"},
+
+ /* Output */
+ {"HPL Mixer", "DACLHPL Switch", "DACL Switch"},
+ {"HPL Mixer", "DACRHPL Switch", "DACR Switch"},
+ {"HPR Mixer", "DACLHPR Switch", "DACL Switch"},
+ {"HPR Mixer", "DACRHPR Switch", "DACR Switch"},
+
+ {"ADCLHPL Loop Switch", NULL, "ADCL PGA"},
+ {"ADCLHPR Loop Switch", NULL, "ADCL PGA"},
+ {"ADCRHPL Loop Switch", NULL, "ADCR PGA"},
+ {"ADCRHPR Loop Switch", NULL, "ADCR PGA"},
+
+ {"HPL Mixer", "ADCLHPL Switch", "ADCLHPL Loop Switch"},
+ {"HPL Mixer", "ADCRHPL Switch", "ADCRHPL Loop Switch"},
+ {"HPR Mixer", "ADCLHPR Switch", "ADCLHPR Loop Switch"},
+ {"HPR Mixer", "ADCRHPR Switch", "ADCRHPR Loop Switch"},
+
+#ifdef CONFIG_SND_SOC_SPRD_CODEC_NO_HP_POP
+ {"HEAD_P_L", NULL, "HP POP"},
+ {"HEAD_P_R", NULL, "HP POP"},
+ {"HP POP", NULL, "HPL Switch"},
+ {"HP POP", NULL, "HPR Switch"},
+#else
+ {"HPL Switch", NULL, "HP POP"},
+ {"HPR Switch", NULL, "HP POP"},
+#endif
+ {"HPL Switch", NULL, "HPL Mixer"},
+ {"HPR Switch", NULL, "HPR Mixer"},
+ {"HPL Mute", NULL, "HPL Switch"},
+ {"HPR Mute", NULL, "HPR Switch"},
+ {"HEAD_P_L", NULL, "HPL Mute"},
+ {"HEAD_P_R", NULL, "HPR Mute"},
+
+ {"HP PA", NULL, "HP PA POST Switch"},
+ {"HP PA POST Switch", NULL, "HP PA Switch"},
+ {"HPL CGL Switch", NULL, "HPL Mute"},
+ {"HPR CGR Switch", NULL, "HPR Mute"},
+ {"HPL CG Mute1", NULL, "HPL CGL Switch"},
+ {"HPR CG Mute1", NULL, "HPR CGR Switch"},
+ {"HPL CG Mute2", NULL, "HPL CG Mute1"},
+ {"HPR CG Mute2", NULL, "HPR CG Mute1"},
+ {"HP PA Switch", NULL, "HPL CG Mute2"},
+ {"HP PA Switch", NULL, "HPR CG Mute2"},
+
+ {"SPKL Mixer", "DACLSPKL Switch", "DACL Switch"},
+ {"SPKL Mixer", "DACRSPKL Switch", "DACR Switch"},
+ {"SPKR Mixer", "DACLSPKR Switch", "DACL Switch"},
+ {"SPKR Mixer", "DACRSPKR Switch", "DACR Switch"},
+
+ {"ADCLSPKL Loop Switch", NULL, "ADCL PGA"},
+ {"ADCLSPKR Loop Switch", NULL, "ADCL PGA"},
+ {"ADCRSPKL Loop Switch", NULL, "ADCR PGA"},
+ {"ADCRSPKR Loop Switch", NULL, "ADCR PGA"},
+
+ {"SPKL Mixer", "ADCLSPKL Switch", "ADCLSPKL Loop Switch"},
+ {"SPKL Mixer", "ADCRSPKL Switch", "ADCRSPKL Loop Switch"},
+ {"SPKR Mixer", "ADCLSPKR Switch", "ADCLSPKR Loop Switch"},
+ {"SPKR Mixer", "ADCRSPKR Switch", "ADCRSPKR Loop Switch"},
+
+ {"SPKL Switch", NULL, "SPKL Mixer"},
+ {"SPKR Switch", NULL, "SPKR Mixer"},
+ {"SPKL Mute", NULL, "SPKL Switch"},
+ {"SPKR Mute", NULL, "SPKR Switch"},
+ {"AOL", NULL, "SPKL Mute"},
+ {"AOR", NULL, "SPKR Mute"},
+
+ {"Spk PA", NULL, "Spk PA Switch"},
+ {"Spk PA Switch", NULL, "SPKL Mute"},
+
+ {"EAR Mixer", NULL, "DACL Switch"},
+ {"EAR Switch", NULL, "EAR Mixer"},
+ {"EAR Mute", NULL, "EAR Switch"},
+ {"EAR", NULL, "EAR Mute"},
+
+ {"ADCL Mute", NULL, "ADCL Mixer"},
+ {"ADCR Mute", NULL, "ADCR Mixer"},
+ {"ADCL PGA", NULL, "ADCL Mute"},
+ {"ADCR PGA", NULL, "ADCR Mute"},
+ /* LineIN */
+ {"ADCL Mixer", "AILADCL Switch", "AIL Boost"},
+ {"ADCR Mixer", "AILADCR Switch", "AIL Boost"},
+ {"ADCL Mixer", "AIRADCL Switch", "AIR Boost"},
+ {"ADCR Mixer", "AIRADCR Switch", "AIR Boost"},
+ {"AIL Boost", NULL, "AIL"},
+ {"AIR Boost", NULL, "AIR"},
+ /* Input */
+ {"ADCL Mixer", "MainMICADCL Switch", "MIC Boost"},
+ {"ADCR Mixer", "MainMICADCR Switch", "MIC Boost"},
+ {"MIC Boost", NULL, "Mic Bias"},
+ {"ADCL Mixer", "AuxMICADCL Switch", "AUXMIC Boost"},
+ {"ADCR Mixer", "AuxMICADCR Switch", "AUXMIC Boost"},
+ {"AUXMIC Boost", NULL, "AuxMic Bias"},
+ {"ADCL Mixer", "HPMICADCL Switch", "HEADMIC Boost"},
+ {"ADCR Mixer", "HPMICADCR Switch", "HEADMIC Boost"},
+ {"HEADMIC Boost", NULL, "HeadMic Bias"},
+ /* Captrue */
+ {"ADCL Switch", NULL, "ADCL PGA"},
+ {"ADCR Switch", NULL, "ADCR PGA"},
+ {"ADie Digital ADCL Switch", NULL, "ADCL Switch"},
+ {"ADie Digital ADCR Switch", NULL, "ADCR Switch"},
+ {"Digital ADCL Switch", NULL, "ADie Digital ADCL Switch"},
+ {"Digital ADCR Switch", NULL, "ADie Digital ADCR Switch"},
+ {"ADC", NULL, "Digital ADCL Switch"},
+ {"ADC", NULL, "Digital ADCR Switch"},
+
+ {"Digital ADC1L Switch", NULL, "ADie Digital ADCL Switch"},
+ {"Digital ADC1R Switch", NULL, "ADie Digital ADCR Switch"},
+ {"ADC1", NULL, "Digital ADC1L Switch"},
+ {"ADC1", NULL, "Digital ADC1R Switch"},
+
+ {"Mic Bias", NULL, "MIC"},
+ {"AuxMic Bias", NULL, "AUXMIC"},
+ {"HeadMic Bias", NULL, "HPMIC"},
+
+ /* DMIC0 */
+ {"DMIC Switch", NULL, "DMIC"},
+ {"Digital ADC DMIC In", NULL, "DMIC Switch"},
+ {"Digital ADCL Switch", NULL, "Digital ADC DMIC In"},
+ {"Digital ADCR Switch", NULL, "Digital ADC DMIC In"},
+ /* DMIC1 */
+ {"DMIC1 Switch", NULL, "DMIC1"},
+ {"Digital ADC1 DMIC In", NULL, "DMIC1 Switch"},
+ {"Digital ADC1L Switch", NULL, "Digital ADC1 DMIC In"},
+ {"Digital ADC1R Switch", NULL, "Digital ADC1 DMIC In"},
+
+ /* Bias independent */
+ {"Mic Bias", NULL, "Analog Power"},
+ {"AuxMic Bias", NULL, "Analog Power"},
+ {"HeadMic Bias", NULL, "Analog Power"},
+ {"ADC-DAC Adie Loop", "switch", "ADC"},
+ {"ADC1-DAC Adie Loop", "switch", "ADC1"},
+ {"ADC-DAC Adie Loop post", NULL, "ADC-DAC Adie Loop"},
+ {"ADC1-DAC Adie Loop post", NULL, "ADC1-DAC Adie Loop"},
+ {"DAC", NULL, "ADC-DAC Adie Loop post"},
+ {"DAC", NULL, "ADC1-DAC Adie Loop post"},
+
+ {"ADC-DAC Digital Loop", "switch", "ADC"},
+ {"ADC1-DAC Digital Loop", "switch", "ADC1"},
+ {"ADC-DAC Digital Loop post", NULL, "ADC-DAC Digital Loop"},
+ {"ADC1-DAC Digital Loop post", NULL, "ADC1-DAC Digital Loop"},
+ {"DAC", NULL, "ADC-DAC Digital Loop post"},
+ {"DAC", NULL, "ADC1-DAC Digital Loop post"},
+};
+
+static int sprd_codec_vol_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int id = GET_PGA_ID(FUN_REG(mc->reg));
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ struct sprd_codec_pga_op *pga = &(sprd_codec->pga[id]);
+ int ret = 0;
+
+ sp_asoc_pr_info("Set PGA[%s] to %ld\n", sprd_codec_pga_debug_str[id],
+ ucontrol->value.integer.value[0]);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+ pga->pgaval = val;
+ if (pga->set) {
+ ret = pga->set(codec, pga->pgaval);
+ }
+ return ret;
+}
+
+static int sprd_codec_vol_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int id = GET_PGA_ID(FUN_REG(mc->reg));
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+ struct sprd_codec_pga_op *pga = &(sprd_codec->pga[id]);
+
+ ucontrol->value.integer.value[0] = pga->pgaval;
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+static int sprd_codec_switch_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int id = GET_SWITCH_ID(FUN_REG(mc->reg));
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ struct sprd_codec_switch_op *switcher = &(sprd_codec->switcher[id]);
+ int ret = 0;
+
+ sp_asoc_pr_info("Switch [%s] %s\n", switch_name[id],
+ ucontrol->value.integer.value[0]?"on":"off");
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+ switcher->on = val;
+ if (switcher->set) {
+ ret = switcher->set(codec, switcher->on);
+ }
+ return ret;
+}
+
+static int sprd_codec_switch_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int id = GET_SWITCH_ID(FUN_REG(mc->reg));
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+ struct sprd_codec_switch_op *switcher= &(sprd_codec->switcher[id]);
+
+ ucontrol->value.integer.value[0] = switcher->on;
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+static int sprd_codec_inter_pa_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret = 0;
+
+ sp_asoc_pr_info("Config inter PA 0x%lx\n",
+ ucontrol->value.integer.value[0]);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+
+ sprd_codec->inter_pa.value = (u32) val;
+
+ if (sprd_codec->inter_pa.set) {
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ sprd_inter_speaker_pa(codec, 1);
+ } else {
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ }
+ return ret;
+}
+
+static int sprd_codec_inter_pa_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+ ucontrol->value.integer.value[0] = sprd_codec->inter_pa.value;
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static int sprd_codec_info_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int val;
+ int ret = 0;
+
+ sp_asoc_pr_info("%s, %d\n",
+ __func__,(int)ucontrol->value.integer.value[0]);
+
+ val = ucontrol->value.integer.value[0];
+
+ if (SPRD_CODEC_INFO != val) {
+ sp_asoc_pr_info("%s, wrong codec info (%d) \n", __func__, val);
+ }
+ return ret;
+}
+
+static int sprd_codec_info_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 chip_id = 0;
+ int ver_id = 0;
+
+ ucontrol->value.integer.value[0] = AUDIO_CODEC_2723;
+
+ chip_id = sci_get_ana_chip_id() >> 16;
+ ver_id = sci_get_ana_chip_ver();
+
+ if (chip_id == 0x2723)
+ if (ver_id == AUDIO_2723_VER_E)
+ ucontrol->value.integer.value[0] = AUDIO_CODEC_2723E;
+ else if (ver_id == AUDIO_2723_VER_T)
+ ucontrol->value.integer.value[0] = AUDIO_CODEC_2723T;
+
+ sp_asoc_pr_info("%s, codec info = %d\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int sprd_codec_linein_mute_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret = 0;
+ int i = 0;
+
+ sp_asoc_pr_info("%s, 0x%08x\n",
+ __func__,(int)ucontrol->value.integer.value[0]);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+ mutex_lock(&sprd_codec->sprd_linein_mute_mutex);
+ sprd_codec->sprd_linein_mute = (u32) val;
+ if (sprd_codec->sprd_linein_set) {
+ _mixer_adc_linein_mute_nolock(codec, sprd_codec->sprd_linein_mute);
+ }
+ mutex_unlock(&sprd_codec->sprd_linein_mute_mutex);
+ return ret;
+}
+
+static int sprd_codec_linein_mute_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+
+ mutex_lock(&sprd_codec->sprd_linein_mute_mutex);
+ ucontrol->value.integer.value[0] = sprd_codec->sprd_linein_mute;
+ mutex_unlock(&sprd_codec->sprd_linein_mute_mutex);
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+ return 0;
+}
+static void _dacspkl_switch_nolock(struct snd_soc_codec *codec, int val)
+{
+ int dacrspkl = val & BIT(SPRD_CODEC_RIGHT);
+ int daclspkl = val & BIT(SPRD_CODEC_LEFT);
+ int r_on = dacrspkl ? 1:0;
+ int l_on = daclspkl ? 1:0;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+
+ sp_asoc_pr_info("%s,l:%d,r:%d\n",__func__,l_on,r_on);
+ if (sprd_codec->sprd_dacspkl_set & BIT(SPRD_CODEC_RIGHT)) {
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC6), BIT(DACR_AOL),
+ r_on << DACR_AOL);
+ }
+ if (sprd_codec->sprd_dacspkl_set & BIT(SPRD_CODEC_LEFT)) {
+ snd_soc_update_bits(codec, SOC_REG(ANA_CDC6), BIT(DACL_AOL),
+ l_on << DACL_AOL);
+ }
+}
+
+static int dacspkl_enable_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ unsigned int shift = mc ->shift;
+
+ sp_asoc_pr_info("%s, %s(%s)\n",
+ __func__,shift?"dacrspkl":"daclspkl",(int)ucontrol->value.integer.value[0] ? "on":"off");
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+ mutex_lock(&sprd_codec->sprd_dacspkl_mutex);
+ if(val){
+ sprd_codec->sprd_dacspkl_enable |= (1 << shift);
+ } else {
+ sprd_codec->sprd_dacspkl_enable &= ~(1 << shift);
+ }
+ sp_asoc_pr_info("%s,0x%x,0x%x\n",__func__,sprd_codec->sprd_dacspkl_enable,sprd_codec->sprd_dacspkl_set);
+ if (sprd_codec->sprd_dacspkl_set) {
+ _dacspkl_switch_nolock(codec, sprd_codec->sprd_dacspkl_enable);
+ }
+ mutex_unlock(&sprd_codec->sprd_dacspkl_mutex);
+ return 0;
+}
+
+static int dacspkl_enable_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ int shift = mc->shift;
+ unsigned int invert = mc->invert;
+ int mask = 1 << shift;
+
+ sp_asoc_pr_info("%s, 0x%08x,%s,shift:%d\n",
+ __func__,mask,mask&0x2 ? "dacrspkl":"daclspkl",shift);
+ mutex_lock(&sprd_codec->sprd_dacspkl_mutex);
+ ucontrol->value.integer.value[0] = (sprd_codec->sprd_dacspkl_enable & mask) >> shift;
+ mutex_unlock(&sprd_codec->sprd_dacspkl_mutex);
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+ return 0;
+}
+
+static int sprd_codec_inter_hp_pa_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret = 0;
+ struct sprd_codec_inter_hp_pa *p_setting =
+ &sprd_codec->inter_hp_pa.setting;
+
+ sp_asoc_pr_info("Config inter HP PA 0x%08x\n",
+ (int)ucontrol->value.integer.value[0]);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ if (invert)
+ val = max - val;
+ mutex_lock(&sprd_codec->inter_hp_pa_mutex);
+ sprd_codec->inter_hp_pa.value = (u32) val;
+ if (sprd_codec->inter_hp_pa.set) {
+ mutex_unlock(&sprd_codec->inter_hp_pa_mutex);
+ sprd_codec_hp_pa_lpw(codec, p_setting->class_g_low_power);
+ sprd_codec_hp_pa_mode(codec, p_setting->class_g_mode);
+ sprd_codec_hp_pa_osc(codec, p_setting->class_g_osc);
+ sprd_codec_hp_pa_cgcal_en(codec, p_setting->class_g_cgcal);
+ } else {
+ mutex_unlock(&sprd_codec->inter_hp_pa_mutex);
+ }
+ return ret;
+}
+
+static int sprd_codec_inter_hp_pa_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+
+ mutex_lock(&sprd_codec->inter_hp_pa_mutex);
+ ucontrol->value.integer.value[0] = sprd_codec->inter_hp_pa.value;
+ mutex_unlock(&sprd_codec->inter_hp_pa_mutex);
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static int sprd_codec_mic_bias_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = FUN_REG(mc->reg);
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret = 0;
+ int id = GET_MIC_BIAS_ID(reg);
+
+ val = (ucontrol->value.integer.value[0] & mask);
+
+ if (invert)
+ val = max - val;
+ if (sprd_codec->mic_bias[id] == val) {
+ return 0;
+ }
+
+ sp_asoc_pr_info("%s Switch %s\n", mic_bias_name[id], STR_ON_OFF(val));
+
+ sprd_codec->mic_bias[id] = val;
+ if (val) {
+ ret =
+ snd_soc_dapm_force_enable_pin(&codec->card->dapm,
+ mic_bias_name[id]);
+ } else {
+ ret =
+ snd_soc_dapm_disable_pin(&codec->card->dapm,
+ mic_bias_name[id]);
+ }
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync(&codec->card->dapm);
+
+ return ret;
+}
+
+static int sprd_codec_mic_bias_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_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = FUN_REG(mc->reg);
+ int max = mc->max;
+ unsigned int invert = mc->invert;
+ int id = GET_MIC_BIAS_ID(reg);
+
+ ucontrol->value.integer.value[0] = sprd_codec->mic_bias[id];
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -600, 75, 0);
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -3600, 300, 1);
+static const DECLARE_TLV_DB_SCALE(ear_tlv, -3600, 300, 1);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -2400, 300, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(auxmic_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(headmic_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(ailr_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_RANGE(clsg_1_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-3000, 300, 1),
+ 8, 46, TLV_DB_SCALE_ITEM(-850, 50, 0),
+);
+
+static const struct soc_enum codec_info_enum =
+ SOC_ENUM_SINGLE_EXT(SP_AUDIO_CODEC_NUM, codec_hw_info);
+
+static const DECLARE_TLV_DB_SCALE(clsg_2_tlv, -84, 12, 0);
+
+
+#define SPRD_CODEC_PGA(xname, xreg, tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, FUN_REG(xreg), 0, 15, 0, \
+ sprd_codec_vol_get, sprd_codec_vol_put, tlv_array)
+
+#define SPRD_CODEC_PGA_MAX(xname, xreg, max, tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, FUN_REG(xreg), 0, max, 0, \
+ sprd_codec_vol_get, sprd_codec_vol_put, tlv_array)
+
+#define SPRD_CODEC_MIC_BIAS(xname, xreg) \
+ SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, \
+ sprd_codec_mic_bias_get, sprd_codec_mic_bias_put)
+
+#define SPRD_CODEC_SWITCH(xname, xreg) \
+ SOC_SINGLE_EXT(xname, FUN_REG(xreg), 0, 1, 0, \
+ sprd_codec_switch_get, sprd_codec_switch_put)
+
+static const struct snd_kcontrol_new sprd_codec_snd_controls[] = {
+ SPRD_CODEC_PGA("SPKL Playback Volume", SPRD_CODEC_PGA_SPKL, spk_tlv),
+ SPRD_CODEC_PGA("SPKR Playback Volume", SPRD_CODEC_PGA_SPKR, spk_tlv),
+ SPRD_CODEC_PGA("HPL Playback Volume", SPRD_CODEC_PGA_HPL, hp_tlv),
+ SPRD_CODEC_PGA("HPR Playback Volume", SPRD_CODEC_PGA_HPR, hp_tlv),
+ SPRD_CODEC_PGA_MAX("HPL CG Playback Volume1", SPRD_CODEC_PGA_CG_HPL_1, 63,
+ clsg_1_tlv),
+ SPRD_CODEC_PGA_MAX("HPR CG Playback Volume1", SPRD_CODEC_PGA_CG_HPR_1, 63,
+ clsg_1_tlv),
+ SPRD_CODEC_PGA_MAX("HPL CG Playback Volume2", SPRD_CODEC_PGA_CG_HPL_2, 7,
+ clsg_2_tlv),
+ SPRD_CODEC_PGA_MAX("HPR CG Playback Volume2", SPRD_CODEC_PGA_CG_HPR_2, 7,
+ clsg_2_tlv),
+ SPRD_CODEC_PGA("EAR Playback Volume", SPRD_CODEC_PGA_EAR, ear_tlv),
+
+ SPRD_CODEC_PGA_MAX("ADCL Capture Volume", SPRD_CODEC_PGA_ADCL, 63,
+ adc_tlv),
+ SPRD_CODEC_PGA_MAX("ADCR Capture Volume", SPRD_CODEC_PGA_ADCR, 63,
+ adc_tlv),
+
+ SPRD_CODEC_PGA_MAX("DACL Playback Volume", SPRD_CODEC_PGA_DACL, 7,
+ dac_tlv),
+ SPRD_CODEC_PGA_MAX("DACR Playback Volume", SPRD_CODEC_PGA_DACR, 7,
+ dac_tlv),
+ SPRD_CODEC_PGA_MAX("MIC Boost", SPRD_CODEC_PGA_MIC, 3, mic_tlv),
+ SPRD_CODEC_PGA_MAX("AUXMIC Boost", SPRD_CODEC_PGA_AUXMIC, 3,
+ auxmic_tlv),
+ SPRD_CODEC_PGA_MAX("HEADMIC Boost", SPRD_CODEC_PGA_HEADMIC, 3,
+ headmic_tlv),
+ SPRD_CODEC_PGA_MAX("Linein Boost", SPRD_CODEC_PGA_AIL, 3, ailr_tlv),
+
+ SOC_SINGLE_EXT("Inter PA Config", 0, 0, INT_MAX, 0,
+ sprd_codec_inter_pa_get, sprd_codec_inter_pa_put),
+
+ SOC_SINGLE_EXT("Inter HP PA Config", 0, 0, INT_MAX, 0,
+ sprd_codec_inter_hp_pa_get, sprd_codec_inter_hp_pa_put),
+
+ SPRD_CODEC_SWITCH("DACL Switch",SPRD_CODEC_DACL),
+
+ SPRD_CODEC_SWITCH("DACR Switch",SPRD_CODEC_DACR),
+
+ SPRD_CODEC_SWITCH("HPLCGL Switch",SPRD_CODEC_HPLCGL),
+
+ SPRD_CODEC_SWITCH("HPRCGR Switch",SPRD_CODEC_HPRCGR),
+
+ SPRD_CODEC_MIC_BIAS("MIC Bias Switch", SPRD_CODEC_MIC_BIAS),
+
+ SPRD_CODEC_MIC_BIAS("AUXMIC Bias Switch", SPRD_CODEC_AUXMIC_BIAS),
+
+ SPRD_CODEC_MIC_BIAS("HEADMIC Bias Switch", SPRD_CODEC_HEADMIC_BIAS),
+
+ SOC_SINGLE_EXT("Linein Mute Switch", 0, 0, 1, 0,
+ sprd_codec_linein_mute_get, sprd_codec_linein_mute_put),
+
+ SOC_SINGLE_EXT("DACLSPKL Enable", 0, SPRD_CODEC_LEFT , 1, 0,
+ dacspkl_enable_get, dacspkl_enable_put),
+
+ SOC_SINGLE_EXT("DACRSPKL Enable", 0, SPRD_CODEC_RIGHT , 1, 0,
+ dacspkl_enable_get, dacspkl_enable_put),
+
+ SOC_ENUM_EXT("Aud Codec Info", codec_info_enum,
+ sprd_codec_info_get, sprd_codec_info_put),
+};
+
+static unsigned int sprd_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ /* Because snd_soc_update_bits reg is 16 bits short type,
+ so muse do following convert
+ */
+ if (IS_SPRD_CODEC_AP_RANG(reg | SPRD_CODEC_AP_BASE_HI)) {
+ reg |= SPRD_CODEC_AP_BASE_HI;
+ return arch_audio_codec_read(reg);
+ } else if (IS_SPRD_CODEC_DP_RANG(reg | SPRD_CODEC_DP_BASE_HI)) {
+ reg |= SPRD_CODEC_DP_BASE_HI;
+ return __raw_readl((void __iomem *)(reg - CODEC_DP_BASE + sprd_codec_dp_base));
+ } else if (IS_SPRD_CODEC_MIXER_RANG(FUN_REG(reg))) {
+ struct sprd_codec_priv *sprd_codec =
+ snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(reg));
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]);
+ return mixer->on;
+ } else if (IS_SPRD_CODEC_PGA_RANG(FUN_REG(reg))) {
+ } else if (IS_SPRD_CODEC_MIC_BIAS_RANG(FUN_REG(reg))) {
+ } else if (IS_SPRD_CODEC_SWITCH_RANG(FUN_REG(reg))) {
+ } else if (IS_SPRD_CODEC_ADC_LOOP_RANG(FUN_REG(reg))) {
+ struct sprd_codec_priv *sprd_codec =
+ snd_soc_codec_get_drvdata(codec);
+ int id = GET_ADC_LOOP_ID(FUN_REG(reg));
+ return sprd_codec->ad_da_loop_en[id];
+ } else
+ sp_asoc_pr_dbg("read the register is not codec's reg = 0x%x\n",
+ reg);
+ return 0;
+}
+
+static int sprd_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ int ret = 0;
+ if (IS_SPRD_CODEC_AP_RANG(reg | SPRD_CODEC_AP_BASE_HI)) {
+ reg |= SPRD_CODEC_AP_BASE_HI;
+ ret = arch_audio_codec_write(reg, val);
+ sp_asoc_pr_reg("A[0x%04x] W:[0x%08x] R:[0x%08x]\n",
+ (reg - CODEC_AP_BASE) & 0xFFFF, val, arch_audio_codec_read(reg));
+ return ret;
+ } else if (IS_SPRD_CODEC_DP_RANG(reg | SPRD_CODEC_DP_BASE_HI)) {
+ reg |= SPRD_CODEC_DP_BASE_HI;
+ __raw_writel(val, (void __iomem *)(reg - CODEC_DP_BASE + sprd_codec_dp_base));
+ sp_asoc_pr_reg("D[0x%04x] W:[0x%08x] R:[0x%08x]\n",
+ (reg - CODEC_DP_BASE) & 0xFFFF, val,
+ __raw_readl((void __iomem *)(reg - CODEC_DP_BASE + sprd_codec_dp_base)));
+ return ret;
+ } else if (IS_SPRD_CODEC_MIXER_RANG(FUN_REG(reg))) {
+ struct sprd_codec_priv *sprd_codec =
+ snd_soc_codec_get_drvdata(codec);
+ int id = GET_MIXER_ID(FUN_REG(reg));
+ struct sprd_codec_mixer *mixer = &(sprd_codec->mixer[id]);
+ mixer->on = val ? 1 : 0;
+ } else if (IS_SPRD_CODEC_PGA_RANG(FUN_REG(reg))) {
+ } else if (IS_SPRD_CODEC_MIC_BIAS_RANG(FUN_REG(reg))) {
+ } else if (IS_SPRD_CODEC_SWITCH_RANG(FUN_REG(reg))) {
+ struct sprd_codec_priv *sprd_codec =
+ snd_soc_codec_get_drvdata(codec);
+ int id = GET_SWITCH_ID(FUN_REG(reg));
+ struct sprd_codec_switch_op *switcher = &(sprd_codec->switcher[id]);
+ switcher->on = val ? 1 : 0;
+ } else
+ sp_asoc_pr_dbg("write the register is not codec's reg = 0x%x\n",
+ reg);
+ return ret;
+}
+
+static int sprd_codec_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int mask = 0x0F;
+ int shift = 0;
+ int rate = params_rate(params);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sprd_codec->da_sample_val = rate;
+ sp_asoc_pr_info("Playback rate is [%d]\n", rate);
+ sprd_codec_set_sample_rate(codec, rate, mask, shift);
+ } else {
+ sp_asoc_pr_info("Capture rate is [%d]\n", rate);
+ if (dai->id != SPRD_CODEC_IIS1_ID) {
+ sprd_codec->ad_sample_val = rate;
+ sprd_codec_set_ad_sample_rate(codec, rate, mask, shift);
+ } else {
+ sprd_codec->ad1_sample_val = rate;
+ sprd_codec_set_ad_sample_rate(codec, rate,
+ ADC1_SRC_N_MASK,
+ ADC1_SRC_N);
+ }
+ }
+
+ return 0;
+}
+
+static int sprd_codec_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+#if 0
+static void sprd_codec_dac_mute_irq_enable(struct snd_soc_codec *codec)
+{
+ int mask = BIT(DAC_MUTE_D);
+ snd_soc_update_bits(codec, SOC_REG(AUD_INT_CLR), mask, mask);
+ snd_soc_update_bits(codec, SOC_REG(AUD_INT_EN), mask, mask);
+}
+#endif
+
+static irqreturn_t sprd_codec_dp_irq(int irq, void *dev_id)
+{
+ int mask;
+ struct sprd_codec_priv *sprd_codec = dev_id;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ mask = snd_soc_read(codec, AUD_AUD_STS0);
+ sp_asoc_pr_dbg("dac mute irq mask = 0x%x\n", mask);
+ if (BIT(DAC_MUTE_D_MASK) & mask) {
+ mask = BIT(DAC_MUTE_D);
+ complete(&sprd_codec->completion_dac_mute);
+ }
+ if (BIT(DAC_MUTE_U_MASK) & mask) {
+ mask = BIT(DAC_MUTE_U);
+ }
+ snd_soc_update_bits(codec, SOC_REG(AUD_INT_EN), mask, 0);
+ return IRQ_HANDLED;
+}
+
+#if 0
+static int sprd_codec_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+ if (dai->id == SPRD_CODEC_IIS1_ID) /*codec_dai_1 has no playback */
+ return 0;
+
+ if (atomic_read(&sprd_codec->ldo_refcount) >= 1) {
+ sp_asoc_pr_dbg("mute %i\n", mute);
+
+ ret =
+ snd_soc_update_bits(codec, SOC_REG(AUD_DAC_CTL),
+ BIT(DAC_MUTE_START),
+ mute ? BIT(DAC_MUTE_START) : 0);
+#if 0
+ if (mute && ret) {
+ int dac_mute_complete;
+ struct sprd_codec_priv *sprd_codec =
+ snd_soc_codec_get_drvdata(codec);
+ sp_asoc_pr_dbg("Dac Mute IRQ enable\n");
+ sprd_codec_dac_mute_irq_enable(codec);
+ init_completion(&sprd_codec->completion_dac_mute);
+ dac_mute_complete =
+ wait_for_completion_timeout
+ (&sprd_codec->completion_dac_mute,
+ msecs_to_jiffies(SPRD_CODEC_DAC_MUTE_TIMEOUT));
+ sp_asoc_pr_dbg("Dac Mute Completion %d\n",
+ dac_mute_complete);
+ if (!dac_mute_complete) {
+ pr_err("Dac Mute Timeout\n");
+ }
+ }
+#endif
+
+ }
+
+ return ret;
+}
+#endif
+
+static struct snd_soc_dai_ops sprd_codec_dai_ops = {
+ .hw_params = sprd_codec_pcm_hw_params,
+ .hw_free = sprd_codec_pcm_hw_free,
+};
+
+#ifdef CONFIG_PM
+static int sprd_codec_soc_suspend(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static int sprd_codec_soc_resume(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+#else
+#define sprd_codec_soc_suspend NULL
+#define sprd_codec_soc_resume NULL
+#endif
+
+/*
+ * proc interface
+ */
+
+#ifdef CONFIG_PROC_FS
+static void sprd_codec_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct sprd_codec_priv *sprd_codec = entry->private_data;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ int reg;
+
+ snd_iprintf(buffer, "%s digital part\n", codec->name);
+ for (reg = SPRD_CODEC_DP_BASE; reg < SPRD_CODEC_DP_END; reg += 0x10) {
+ snd_iprintf(buffer, "0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (unsigned int)(reg - SPRD_CODEC_DP_BASE)
+ , snd_soc_read(codec, reg + 0x00)
+ , snd_soc_read(codec, reg + 0x04)
+ , snd_soc_read(codec, reg + 0x08)
+ , snd_soc_read(codec, reg + 0x0C)
+ );
+ }
+ snd_iprintf(buffer, "%s analog part\n", codec->name);
+ for (reg = SPRD_CODEC_AP_BASE; reg < SPRD_CODEC_AP_END; reg += 0x10) {
+ snd_iprintf(buffer, "0x%04x | 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ (unsigned int)(reg - SPRD_CODEC_AP_BASE)
+ , snd_soc_read(codec, reg + 0x00)
+ , snd_soc_read(codec, reg + 0x04)
+ , snd_soc_read(codec, reg + 0x08)
+ , snd_soc_read(codec, reg + 0x0C)
+ );
+ }
+}
+
+static void aud_glb_reg_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ #define N 30
+ int i = 0, j = 0;
+ glb_reg_dump_t *aud_glb_reg = NULL;
+ glb_reg_dump_t *reg_p = NULL;
+ aud_glb_reg = devm_kzalloc(entry->card->dev, sizeof(glb_reg_dump_t) * N, GFP_KERNEL);
+ if (NULL == aud_glb_reg) {
+ sp_asoc_pr_info("%, error, not enough memory!!!\n", __func__);
+ return;
+ }
+ reg_p = aud_glb_reg;
+ AUDIO_GLB_REG_DUMP_LIST(reg_p);
+
+ snd_iprintf(buffer, "audio global register dump\n");
+ for (i = 0; i < N; i++) {
+ for (j = 0; j < aud_glb_reg[i].count; j++)
+ if (aud_glb_reg[i].func) {
+ if (j == 0)
+ snd_iprintf(buffer, "%30s: 0x%08x 0x%08x\n",
+ aud_glb_reg[i].reg_name,
+ aud_glb_reg[i].reg + sizeof(aud_glb_reg[i].reg) * j,
+ aud_glb_reg[i].func((void *)(aud_glb_reg[i].reg + sizeof(aud_glb_reg[i].reg) * j))
+ );
+ else
+ snd_iprintf(buffer, "%27s+%2d: 0x%08x 0x%08x\n",
+ aud_glb_reg[i].reg_name,
+ j,
+ aud_glb_reg[i].reg + sizeof(aud_glb_reg[i].reg) * j,
+ aud_glb_reg[i].func((void *)(aud_glb_reg[i].reg + sizeof(aud_glb_reg[i].reg) * j))
+ );
+ }
+ }
+ kfree(aud_glb_reg);
+}
+
+static void sprd_codec_proc_init(struct sprd_codec_priv *sprd_codec)
+{
+ struct snd_info_entry *entry;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+
+ if (!snd_card_proc_new(codec->card->snd_card, "sprd-codec", &entry))
+ snd_info_set_text_ops(entry, sprd_codec, sprd_codec_proc_read);
+ if (!snd_card_proc_new(codec->card->snd_card, "aud-glb", &entry))
+ snd_info_set_text_ops(entry, sprd_codec, aud_glb_reg_read);
+}
+#else /* !CONFIG_PROC_FS */
+static inline void sprd_codec_proc_init(struct sprd_codec_priv *sprd_codec)
+{
+}
+#endif
+
+static void sprd_codec_power_changed(struct power_supply *psy)
+{
+ struct sprd_codec_priv *sprd_codec = 0;
+ sp_asoc_pr_dbg("%s %s\n", __func__, psy->name);
+#if 1
+ sprd_codec = container_of(psy, struct sprd_codec_priv, audio_ldo);
+ mutex_lock(&sprd_codec->inter_pa_mutex);
+ if (sprd_codec->inter_pa.set && sprd_codec->inter_pa.setting.is_LDO_mode
+ && sprd_codec->inter_pa.setting.is_auto_LDO_mode) {
+ sprd_codec_pa_ldo_auto(sprd_codec->codec, 0);
+ }
+ mutex_unlock(&sprd_codec->inter_pa_mutex);
+ if (atomic_read(&sprd_codec->ldo_refcount) >= 1) {
+ sprd_codec_vcom_ldo_auto(sprd_codec->codec, 0);
+ }
+#endif
+}
+
+static int sprd_codec_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ return -EINVAL;
+}
+
+static int sprd_codec_audio_ldo(struct sprd_codec_priv *sprd_codec)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = sprd_codec->codec;
+ sprd_codec->audio_ldo.name = "audio-ldo";
+ sprd_codec->audio_ldo.get_property = sprd_codec_power_get_property;
+ sprd_codec->audio_ldo.external_power_changed = sprd_codec_power_changed;
+#ifdef CONFIG_POWER_SUPPLY
+ ret = power_supply_register(codec->dev, &sprd_codec->audio_ldo);
+ if (ret) {
+ pr_err("ERR:Register power supply error!\n");
+ return -EFAULT;
+ }
+#endif
+ return ret;
+}
+
+#define SPRD_CODEC_PCM_RATES \
+ (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define SPRD_CODEC_PCM_AD_RATES \
+ (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SRC_SUPPORT_RATE | \
+ SNDRV_PCM_RATE_48000)
+
+/* PCM Playing and Recording default in full duplex mode */
+static struct snd_soc_dai_driver sprd_codec_dai[] = {
+ {
+ .name = "sprd-codec-i2s",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Main-Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_AD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+#ifdef CONFIG_SND_SOC_SPRD_VAUDIO
+ {
+ .name = "sprd-codec-vaudio",
+ .playback = {
+ .stream_name = "Playback-Vaudio",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Main-Capture-Vaudio",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_AD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+#endif
+ /*digital ad1 */
+ {
+ .id = SPRD_CODEC_IIS1_ID,
+ .name = "codec-i2s-ext",
+ .capture = {
+ .stream_name = "Ext-Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_AD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+#ifdef CONFIG_SND_SOC_SPRD_VAUDIO
+ /*digital ad1 */
+ {
+ .id = SPRD_CODEC_IIS1_ID,
+ .name = "codec-vaudio-ext",
+ .capture = {
+ .stream_name = "Ext-Capture-Vaudio",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_AD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+#endif
+ {
+ .name = "sprd-codec-fm",
+ .playback = {
+ .stream_name = "DFM-Output",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SPRD_CODEC_PCM_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sprd_codec_dai_ops,
+ },
+};
+
+static int sprd_codec_soc_probe(struct snd_soc_codec *codec)
+{
+ struct sprd_codec_priv *sprd_codec = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+#ifndef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+#ifdef CONFIG_INPUT_HEADSET_SPRD_SC2723
+ int hp_plug_state = get_hp_plug_state();
+#endif
+#endif
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ codec->dapm.idle_bias_off = 1;
+
+ sprd_codec->codec = codec;
+
+ sprd_codec_proc_init(sprd_codec);
+
+ sprd_codec_audio_ldo(sprd_codec);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ sprd_inter_headphone_pa_pre(sprd_codec, 1);
+#else
+#ifdef CONFIG_INPUT_HEADSET_SPRD_SC2723
+ sprd_inter_headphone_pa_pre(sprd_codec, hp_plug_state);
+#endif
+#endif
+ return ret;
+}
+
+/* power down chip */
+static int sprd_codec_soc_remove(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static int sprd_codec_power_regulator_init(struct sprd_codec_priv *sprd_codec,
+ struct device *dev)
+{
+ sprd_codec_power_get(dev, &sprd_codec->main_mic, "MICBIAS");
+ sprd_codec_power_get(dev, &sprd_codec->aux_mic, "AUXMICBIAS");
+ sprd_codec_power_get(dev, &sprd_codec->head_mic, "HEADMICBIAS");
+ sprd_codec_power_get(dev, &sprd_codec->cg_ldo, "CGLDO");
+
+ return 0;
+}
+
+static void sprd_codec_power_regulator_exit(struct sprd_codec_priv *sprd_codec)
+{
+ sprd_codec_power_put(&sprd_codec->main_mic);
+ sprd_codec_power_put(&sprd_codec->aux_mic);
+ sprd_codec_power_put(&sprd_codec->head_mic);
+ sprd_codec_power_put(&sprd_codec->cg_ldo);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_sprd_codec = {
+ .probe = sprd_codec_soc_probe,
+ .remove = sprd_codec_soc_remove,
+ .suspend = sprd_codec_soc_suspend,
+ .resume = sprd_codec_soc_resume,
+ .read = sprd_codec_read,
+ .write = sprd_codec_write,
+ .reg_word_size = sizeof(u16),
+ .reg_cache_step = 2,
+ .dapm_widgets = sprd_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sprd_codec_dapm_widgets),
+ .dapm_routes = sprd_codec_intercon,
+ .num_dapm_routes = ARRAY_SIZE(sprd_codec_intercon),
+ .controls = sprd_codec_snd_controls,
+ .num_controls = ARRAY_SIZE(sprd_codec_snd_controls),
+};
+
+static int sprd_codec_probe(struct platform_device *pdev)
+{
+ struct sprd_codec_priv *sprd_codec;
+ int ret;
+ struct device_node *node = pdev->dev.of_node;
+ u32 ana_chip_id;
+
+ sp_asoc_pr_dbg("%s\n", __func__);
+
+ arch_audio_codec_switch(AUDIO_TO_ARM_CTRL);
+
+ sprd_codec =
+ devm_kzalloc(&pdev->dev, sizeof(struct sprd_codec_priv),
+ GFP_KERNEL);
+ if (sprd_codec == NULL)
+ return -ENOMEM;
+
+ sprd_codec->da_sample_val = 44100; /*inital value for FM route */
+
+ if (node) {
+ u32 val;
+ u32 val_arr[2];
+ if (!of_property_read_u32(node, "sprd,hp_pa_ver", &val)) {
+ if (val < SPRD_CODEC_HP_PA_VER_MAX) {
+ sprd_codec->hp_ver = val;
+ sp_asoc_pr_dbg("Set HP PA Ver is %d!\n", val);
+ } else {
+ pr_err
+ ("ERR:This driver just support less %d version!\n",
+ SPRD_CODEC_HP_PA_VER_MAX);
+ return -EINVAL;
+ }
+ }
+ if (!of_property_read_u32(node, "sprd,ap_irq", &val)) {
+ sprd_codec->ap_irq = val;
+ sp_asoc_pr_dbg("Set AP IRQ is %d!\n", val);
+ } else {
+ pr_err("ERR:Must give me the AP IRQ!\n");
+ return -EINVAL;
+ }
+ if (!of_property_read_u32(node, "sprd,dp_irq", &val)) {
+ sprd_codec->dp_irq = val;
+ sp_asoc_pr_dbg("Set DP IRQ is %d!\n", val);
+ } else {
+ pr_err("ERR:Must give me the DP IRQ!\n");
+ return -EINVAL;
+ }
+ if (!of_property_read_u32(node, "sprd,def_da_fs", &val)) {
+ sprd_codec->da_sample_val = val;
+ sp_asoc_pr_dbg("Change DA default fs to %d!\n", val);
+ }
+ if (!of_property_read_u32_array(node, "sprd,reg_dp", &val_arr[0], 2)) {
+ sprd_codec_dp_base = (unsigned long)ioremap_nocache(val_arr[0], val_arr[1]);
+ if(!sprd_codec_dp_base) {
+ pr_err("ERR: cannot create iomap address for codec DP!\n");
+ return -EINVAL;
+ }
+ } else {
+ pr_err("ERR:Must give me the codec DP reg address!\n");
+ return -EINVAL;
+ }
+ } else {
+ sprd_codec->ap_irq = CODEC_AP_IRQ;
+ sprd_codec->dp_irq = CODEC_DP_IRQ;
+ sprd_codec_dp_base = (unsigned long)ioremap_nocache(CODEC_DP_BASE_DEFAULT, CODEC_DP_SIZE_DEFAULT);
+ if(!sprd_codec_dp_base) {
+ pr_err("ERR: cannot create iomap address for codec DP with default address!\n");
+ return -EINVAL;
+ }
+ }
+ ana_chip_id = sci_get_ana_chip_id() >> 16;
+ if (ana_chip_id == 0x2713)
+ sprd_codec->hp_ver = SPRD_CODEC_HP_PA_VER_1;
+ else if (ana_chip_id == 0x2711)
+ sprd_codec->hp_ver = SPRD_CODEC_HP_PA_VER_2;
+ else {
+ pr_info("ana_chip_id is 0x%x\n", (unsigned int)sci_get_ana_chip_id);
+ }
+ platform_set_drvdata(pdev, sprd_codec);
+
+ ret =
+ request_irq(sprd_codec->ap_irq, sprd_codec_ap_irq, 0,
+ "sprd_codec_ap", sprd_codec);
+ if (ret) {
+ pr_err("ERR:Request irq ap failed!\n");
+ goto err_irq;
+ }
+
+ INIT_DELAYED_WORK(&sprd_codec->ovp_delayed_work,
+ sprd_codec_ovp_delay_worker);
+
+ ret =
+ request_irq(sprd_codec->dp_irq, sprd_codec_dp_irq, 0,
+ "sprd_codec_dp", sprd_codec);
+ if (ret) {
+ pr_err("ERR:Request irq dp failed!\n");
+ goto dp_err_irq;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_sprd_codec,
+ sprd_codec_dai,
+ ARRAY_SIZE(sprd_codec_dai));
+ if (ret != 0) {
+ pr_err("ERR:Failed to register CODEC: %d\n", ret);
+ goto reg_err_irq;
+ }
+
+ sprd_codec_inter_pa_init(sprd_codec);
+ sprd_codec_inter_hp_pa_init(sprd_codec);
+ sprd_codec_linein_mute_init(sprd_codec);
+ sprd_codec_switch_init(sprd_codec);
+ sprd_codec_dacspkl_enable_init(sprd_codec);
+ sprd_codec_power_regulator_init(sprd_codec, &pdev->dev);
+
+ mutex_init(&sprd_codec->inter_pa_mutex);
+ mutex_init(&sprd_codec->inter_hp_pa_mutex);
+ spin_lock_init(&sprd_codec->sprd_codec_fun_lock);
+ spin_lock_init(&sprd_codec->sprd_codec_pa_sw_lock);
+
+ sprd_codec_vcom_ldo_cfg(sprd_codec, ldo_v_map, ARRAY_SIZE(ldo_v_map));
+ sprd_codec_pa_ldo_cfg(sprd_codec, ldo_v_map, ARRAY_SIZE(ldo_v_map));
+
+ sprd_codec->nb.notifier_call = hp_notifier_handler;
+#ifndef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+#ifdef CONFIG_INPUT_HEADSET_SPRD_SC2723
+ hp_register_notifier(&sprd_codec->nb);
+#endif
+#endif
+
+ return 0;
+reg_err_irq:
+ free_irq(sprd_codec->dp_irq, sprd_codec);
+dp_err_irq:
+ free_irq(sprd_codec->ap_irq, sprd_codec);
+err_irq:
+ iounmap(sprd_codec_dp_base);
+ return -EINVAL;
+}
+
+static int sprd_codec_remove(struct platform_device *pdev)
+{
+ struct sprd_codec_priv *sprd_codec = platform_get_drvdata(pdev);
+ sprd_codec_power_regulator_exit(sprd_codec);
+ free_irq(sprd_codec->ap_irq, sprd_codec);
+ free_irq(sprd_codec->dp_irq, sprd_codec);
+ iounmap(sprd_codec_dp_base);
+#ifndef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+#ifdef CONFIG_INPUT_HEADSET_SPRD_SC2723
+ hp_unregister_notifier(&sprd_codec->nb);
+#endif
+#endif
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id codec_of_match[] = {
+ {.compatible = "sprd,sprd-codec",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, codec_of_match);
+#endif
+
+static struct platform_driver sprd_codec_codec_driver = {
+ .driver = {
+ .name = "sprd-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(codec_of_match),
+ },
+ .probe = sprd_codec_probe,
+ .remove = sprd_codec_remove,
+};
+
+static int __init sprd_codec_driver_init(void)
+{
+ return platform_driver_register(&sprd_codec_codec_driver);
+}
+
+late_initcall(sprd_codec_driver_init);
+
+MODULE_DESCRIPTION("SPRD-CODEC ALSA SoC codec driver");
+MODULE_AUTHOR("Jian Chen <jian.chen@spreadtrum.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("codec:sprd-codec");
diff --git a/sound/soc/sprd/codec/sprd/v4/sprd-codec.h b/sound/soc/sprd/codec/sprd/v4/sprd-codec.h
new file mode 100644
index 00000000..44a00052
--- /dev/null
+++ b/sound/soc/sprd/codec/sprd/v4/sprd-codec.h
@@ -0,0 +1,593 @@
+/*
+ * sound/soc/sprd/codec/sprd/v4/sprd-codec.h
+ *
+ * SPRD-CODEC -- SpreadTrum Tiger intergrated codec.
+ *
+ * Copyright (C) 2013 SpreadTrum Ltd.
+ *
+ * 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 ork FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __SPRD_CODEC_H
+#define __SPRD_CODEC_H
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/globalregs.h>
+#include <soc/sprd/sprd-audio.h>
+#include <soc/sprd/adi.h>
+#include <asm/io.h>
+
+
+/* unit: ms */
+#define SPRD_CODEC_LDO_WAIT_TIME (5)
+#define SPRD_CODEC_LDO_VCM_TIME (2)
+#define SPRD_CODEC_DAC_MUTE_TIMEOUT (600)
+
+#define SPRD_CODEC_HP_POP_TIMEOUT (1000)
+
+#define SPRD_CODEC_INFO (AUDIO_CODEC_2723)
+
+#define SPRD_CODEC_RATE_8000 (10)
+#define SPRD_CODEC_RATE_9600 ( 9)
+#define SPRD_CODEC_RATE_11025 ( 8)
+#define SPRD_CODEC_RATE_12000 ( 7)
+#define SPRD_CODEC_RATE_16000 ( 6)
+#define SPRD_CODEC_RATE_22050 ( 5)
+#define SPRD_CODEC_RATE_24000 ( 4)
+#define SPRD_CODEC_RATE_32000 ( 3)
+#define SPRD_CODEC_RATE_44100 ( 2)
+#define SPRD_CODEC_RATE_48000 ( 1)
+#define SPRD_CODEC_RATE_96000 ( 0)
+
+/* AUD_TOP_CTL */
+#define DAC_EN_L (0)
+#define ADC_EN_L (1)
+#define DAC_EN_R (2)
+#define ADC_EN_R (3)
+#define ADC_SINC_SEL (8)
+#define ADC_SINC_SEL_MASK (0x3)
+#define ADC_DMIC_SEL (9)
+#define ADC1_EN_L (10)
+#define ADC1_EN_R (11)
+#define ADC1_SINC_SEL (14)
+#define ADC1_SINC_SEL_MASK (0x3)
+#define ADC1_DMIC1_SEL (15)
+
+/* AUD_DAC_CTL */
+#define DAC_MUTE_START (14)
+#define DAC_MUTE_EN (15)
+
+/* AUD_ADC_CTL */
+#define ADC_SRC_N (0)
+#define ADC_SRC_N_MASK (0x0f)
+#define ADC1_SRC_N (4)
+#define ADC1_SRC_N_MASK (0xf0)
+
+#define AUD_LOOP_TEST (0)
+#define LOOP_ADC_PATH_SEL (9)
+#define LOOP_PATH_SEL (1)
+#define LOOP_PATH_SEL_MASK (0x3)
+/* AUD_AUD_STS0 */
+#define DAC_MUTE_U_MASK (5)
+#define DAC_MUTE_D_MASK (4)
+#define DAC_MUTE_U_RAW (3)
+#define DAC_MUTE_D_RAW (2)
+#define DAC_MUTE_ST (0)
+#define DAC_MUTE_ST_MASK (0x3)
+
+/* AUD_INT_CLR */
+/* AUD_INT_EN */
+#define DAC_MUTE_U (1)
+#define DAC_MUTE_D (0)
+
+/*AUD_DMIC_CTL*/
+#define ADC_DMIC_CLK_MODE (0)
+#define ADC_DMIC_CLK_MODE_MASK (0x3)
+#define ADC_DMIC_LR_SEL (2)
+#define ADC1_DMIC_CLK_MODE (3)
+#define ADC1_DMIC_CLK_MODE_MASK (0x3)
+#define ADC1_DMIC_LR_SEL (5)
+#define ADC_DMIC_EN (6)
+#define ADC1_DMIC1_EN (7)
+
+/*DAC_SDM_DC_L*/
+#define DAC_SDM_DC_L (0)
+#define DAC_SDM_DC_L_MASK (0xffff)
+/*DAC_SDM_DC_H*/
+#define DAC_SDM_DC_R (0)
+#define DAC_SDM_DC_R_MASK (0xff)
+
+
+#define LDO_V_28 (0)
+#define LDO_V_29 (1)
+#define LDO_V_30 (2)
+#define LDO_V_31 (3)
+#define LDO_V_32 (4)
+#define LDO_V_33 (5)
+#define LDO_V_34 (6)
+#define LDO_V_36 (7)
+
+
+#define MIC_LDO_V_21 (0)
+#define MIC_LDO_V_19 (1)
+#define MIC_LDO_V_23 (2)
+#define MIC_LDO_V_25 (3)
+
+/* ANA_PMU0 */
+#define PA_SW_EN (15)
+#define PA_LDO_EN (14)
+#define PA_EN (13)
+#define OVP_LDO_EN (11)
+#define LDOCG_EN (10)
+#define VB_EN (9)
+#define VBO_EN (8)
+#define HEADMICBIAS_EN (7)
+#define HEADMIC_SLEEP_EN (6)
+
+/* ANA_PMU1 */
+#define BG_EN (15)
+#define BG_IBIAS_EN (14)
+#define VCM_EN (13)
+#define VCM_BUF_EN (12)
+#define SEL_VCMI (11)
+#define VCMI_FAST_EN (10)
+#define MICBIAS_EN (9)
+#define AUXMICBIAS_EN (8)
+#define LDOCG_EN_FAL_ILD (3)
+#define LDOCG_ISET_FAL (2)
+#define LDOCG_RAMP_EN (1)
+#define LDOVBO_FAST_EN (0)
+
+/* ANA_PMU2 */
+#define VCM_V (13)
+#define VCM_V_MASK (0x7)
+#define MICBIAS_V (11)
+#define MICBIAS_V_MASK (0x3)
+#define HEADMICBIAS_V (9)
+#define HEADMICBIAS_V_MASK (0x3)
+
+/* ANA_PMU3 */
+#define BG_I (14)
+#define BG_I_MASK (0x3)
+#define PA_AB_I (11)
+#define PA_AB_I_MASK (0x7)
+#define PA_LDO_V (8)
+#define PA_LDO_V_MASK (0x7)
+#define PA_LDO_V_28 (0)
+#define PA_LDO_V_29 (1)
+#define PA_LDO_V_30 (2)
+#define PA_LDO_V_31 (3)
+#define PA_LDO_V_32 (4)
+#define PA_LDO_V_33 (5)
+#define PA_LDO_V_34 (6)
+#define PA_LDO_V_36 (7)
+#define CG_LDO_V (0)
+#define CG_LDO_V_MASK (0xff)
+
+/* ANA_PMU4 */
+#define VB_CAL (8)
+#define VB_CAL_MASK (0x1F)
+#define VCM_CAL (5)
+#define VCM_CAL_MASK (0x7)
+#define LDO_PA_CAL (0)
+#define LDO_PA_CAL_MASK (0x1F)
+
+/* ANA_PMU5 */
+#define PA_OVP_V (13)
+#define PA_OVP_V_MASK (0x7)
+#define PA_OVP_542 (0)
+#define PA_OVP_527 (1)
+#define PA_OVP_512 (2)
+#define PA_OVP_496 (3)
+#define PA_OVP_481 (4)
+#define PA_OVP_465 (5)
+#define PA_OVP_450 (6)
+#define PA_OVP_435 (7)
+
+/* ANA_PMU6 */
+#define DRV_OCP_AOR_PD (15)
+#define DRV_OCP_AOL_PD (14)
+#define DRV_OCP_EAR_PD (13)
+#define DRV_OCP_HP_PD (12)
+
+/* ANA_CDC0 */
+#define ADC_CLK_EN (15)
+#define ADC_CLK_RST (14)
+#define ADC_CLK_F (12)
+#define ADC_CLK_F_MASK (0x3)
+#define DAC_CLK_EN (11)
+#define DAC_CLK_F (9)
+#define DAC_CLK_F_MASK (0x3)
+#define DRV_CLK_EN (8)
+
+/* ANA_CDC1 */
+#define ADCPGAL_BYP (14)
+#define ADCPGAL_BYP_MASK (0x3)
+#define ADCPGAL_EN (12)
+#define ADCPGAL_EN_MASK (0x3)
+#define ADCPGAR_BYP (10)
+#define ADCPGAR_BYP_MASK (0x3)
+#define ADCPGAR_EN (8)
+#define ADCPGAR_EN_MASK (0x3)
+#define ADC_IBUF_PD (7)
+#define ADC_VREF1P5 (6)
+#define ADCL_PD (5)
+#define ADCL_RST (4)
+#define ADCR_PD (3)
+#define ADCR_RST (2)
+#define FM_REC_EN (1)
+
+#define ADC_PGAL_BYP_SEL_MASK (0x3)
+#define ADC_PGAL_BYP_SEL_NORMAL_INPUT (0x0)
+#define ADC_PGAL_BYP_SEL_PGAL1_2_ADCL (0x1)
+#define ADC_PGAL_BYP_SEL_HEADMIC_2_ADCL (0x2)
+#define ADC_PGAL_BYP_SEL_ALL_DISCONNECT (0x3)
+
+#define ADC_PGAR_BYP_SEL_MASK (0x3)
+#define ADC_PGAR_BYP_SEL_NORMAL_INPUT (0x0)
+#define ADC_PGAR_BYP_SEL_PGAR1_2_ADCR (0x1)
+#define ADC_PGAR_BYP_SEL_HEADMIC_2_ADCR (0x2)
+#define ADC_PGAR_BYP_SEL_ALL_DISCONNECT (0x3)
+
+/* ANA_CDC2 */
+#define DACL_EN (15)
+#define DACR_EN (14)
+#define DACBUF_I_S (13)
+#define DACDC_RMV_EN (12)
+#define DACDC_RMV_S (10)
+#define DACDC_RMV_S_MASK (0x3)
+#define DRV_SOFT_EN (9)
+#define HPL_EN (8)
+#define HPR_EN (7)
+#define DIFF_EN (6)
+#define EAR_EN (5)
+#define AOL_EN (4)
+#define AOR_EN (3)
+
+/* ANA_CDC3 */
+#define CG_REF_EN (15)
+#define CG_CHP_EN (14)
+#define CG_HPL_EN (13)
+#define CG_HPR_EN (12)
+#define CG_HP_DIFF_EN (11)
+#define CG_CHP_CLK_S (10)
+#define CG_CHP_OSC (7)
+#define CG_CHP_OSC_MASK (0x7)
+#define CG_LPW (5)
+#define CG_LPW_MASK (0x3)
+#define CG_CHP_MODE (4)
+#define CG_HP_MODE (3)
+#define CG_HPCAL_EN (2)
+#define CG_HPCALSTP_EN (1)
+
+/* ANA_CDC4 */
+#define PA_D_EN (15)
+#define PA_DTRI_F (13)
+#define PA_DTRI_F_MASK (0x03)
+#define PA_DEMI_EN (12)
+#define PA_SWOCP_PD (11)
+#define PA_LDOOCP_PD (10)
+#define PA_SS_EN (9)
+#define PA_SS_RST (8)
+#define PA_SS_F (6)
+#define PA_SS_F_MASK (0x3)
+#define PA_SS_32K_EN (5)
+#define PA_SS_T (2)
+#define PA_SS_T_MASK (0x7)
+#define DRV_STOP_EN (1)
+
+/* ANA_CDC5 */
+#define MIC_ADCR (15)
+#define AUXMIC_ADCR (14)
+#define HEADMIC_ADCR (13)
+#define AIL_ADCR (12)
+#define AIR_ADCR (11)
+#define MIC_ADCL (10)
+#define AUXMIC_ADCL (9)
+#define HEADMIC_ADCL (8)
+#define AIL_ADCL (7)
+#define AIR_ADCL (6)
+
+/* ANA_CDC6 */
+#define ADCL_AOL (15)
+#define ADCR_AOL (14)
+#define DACL_AOL (13)
+#define DACR_AOL (12)
+#define ADCL_AOR (11)
+#define ADCR_AOR (10)
+#define DACL_AOR (9)
+#define DACR_AOR (8)
+
+/* ANA_CDC7 */
+#define ADCL_EAR (15)
+#define ADCR_EAR (14)
+#define DACL_EAR (13)
+#define DACR_EAR (12)
+#define HPL_CGL (11)
+#define HPR_CGR (10)
+#define ADCL_P_HPL (9)
+#define ADCR_P_HPL (8)
+#define DACL_P_HPL (7)
+#define DACR_P_HPL (6)
+#define ADCL_N_HPR (5)
+#define ADCR_P_HPR (4)
+#define DACL_N_HPR (3)
+#define DACR_P_HPR (2)
+
+/* ANA_CDC8 */
+#define ADC_MIC_PGA (14)
+#define ADC_MIC_PGA_MASK (0x3)
+#define ADC_AUXMIC_PGA (12)
+#define ADC_AUXMIC_PGA_MASK (0x3)
+#define ADC_HEADMIC_PGA (10)
+#define ADC_HEADMIC_PGA_MASK (0x3)
+#define ADC_AILR_PGA (8)
+#define ADC_AILR_PGA_MASK (0x3)
+
+/* ANA_CDC9 */
+#define ADC_PGA_L (4)
+#define ADC_PGA_L_MASK (0x3F)
+#define ADC_PGA_R (10)
+#define ADC_PGA_R_MASK (0x3F)
+
+
+/* ANA_CDC10 */
+#define DACL_G (13)
+#define DACL_G_MASK (0x7)
+#define DACR_G (10)
+#define DACR_G_MASK (0x7)
+
+#define DACL_G_MUTE (0x0)
+#define DACL_G_MINUS_3DB (0x1)
+#define DACL_G_MINUS_2_5DB (0x2)
+#define DACL_G_MINUS_2DB (0x3)
+#define DACL_G_MINUS_1_5DB (0x4)
+#define DACL_G_MINUS_1DB (0x5)
+#define DACL_G_MINUS_0_5DB (0x6)
+#define DACL_G_MINUS_0DB (0x7)
+
+#define DACR_G_MUTE (0x0)
+#define DACR_G_MINUS_3DB (0x1)
+#define DACR_G_MINUS_2_5DB (0x2)
+#define DACR_G_MINUS_2DB (0x3)
+#define DACR_G_MINUS_1_5DB (0x4)
+#define DACR_G_MINUS_1DB (0x5)
+#define DACR_G_MINUS_0_5DB (0x6)
+#define DACR_G_MINUS_0DB (0x7)
+
+
+
+/* ANA_CDC11 */
+#define AOL_G (12)
+#define AOL_G_MASK (0xF)
+#define AOR_G (8)
+#define AOR_G_MASK (0xF)
+
+/* ANA_CDC12 */
+#define EAR_G (12)
+#define EAR_G_MASK (0xF)
+#define HPL_G (8)
+#define HPL_G_MASK (0xF)
+#define HPR_G (4)
+#define HPR_G_MASK (0xF)
+
+/* ANA_CDC13 */
+#define CG_HPL_G_2 (7)
+#define CG_HPL_G_2_MASK (0x7)
+#define CG_HPL_G_1 (10)
+#define CG_HPL_G_1_MASK (0x3F)
+
+/* ANA_CDC14 */
+#define CG_HPR_G_2 (7)
+#define CG_HPR_G_2_MASK (0x7)
+#define CG_HPR_G_1 (10)
+#define CG_HPR_G_1_MASK (0x3F)
+
+
+/* ANA_CDC15 */
+#define AUD_NG_EN (15)
+#define AUD_NG_DA_EN (14)
+#define AUD_NG_PA_EN (13)
+#define AUD_NG_PA_EN (13)
+#define AUD_NG_CG_LPW_EN (12)
+#define AUD_CG_DRE_EN (11)
+#define AUD_CG_DRE_HYS_EN (10)
+#define AUD_CG_POP_EN (9)
+#define AUD_CG_POP_HYS_EN (8)
+
+/* ANA_CDC16 */
+#define HP_POP_CTL (6)
+#define HP_POP_CTL_MASK (0x3)
+#define HP_POP_CTL_DIS (0)
+#define HP_POP_CTL_UP (1)
+#define HP_POP_CTL_DOWN (2)
+#define HP_POP_CTL_HOLD (3)
+
+#define HP_POP_STEP (3)
+#define HP_POP_STEP_MASK (0x7)
+#define HP_POP_STEP_012 (0)
+#define HP_POP_STEP_025 (1)
+#define HP_POP_STEP_05 (2)
+#define HP_POP_STEP_1 (3)
+#define HP_POP_STEP_2 (4)
+#define HP_POP_STEP_4 (5)
+#define HP_POP_STEP_8 (6)
+#define HP_POP_STEP_16 (7)
+#define ADC_PGA_IBIAS_CTL_OFFSET (8)
+#define ADC_PGA_IBIAS_CTL_MASK (0x3)
+#define ADC_PGA_IBIAS_CTL_10 (0x0)
+#define ADC_PGA_IBIAS_CTL_08 (0x1)
+#define ADC_PGA_IBIAS_CTL_05 (0x2)
+#define ADC_PGA_IBIAS_CTL_04 (0x3)
+
+#define DAC_FILTER_IBIS_CTL_OFFSET (10)
+#define DAC_FILTER_IBIS_CTL_MASK (0x3)
+#define DAC_FILTER_IBIS_CTL_10 (0x0)
+#define DAC_FILTER_IBIS_CTL_075 (0x1)
+#define DAC_FILTER_IBIS_CTL_05 (0x2)
+#define DAC_FILTER_IBIS_CTL_3_PER_7 (0x3)
+
+#define RCV_IBIS_CTL_OFFSET (12)
+#define RCV_IBIS_CTL_MASK (0x3)
+#define RCV_IBIS_CTL_095 (0x0)
+#define RCV_IBIS_CTL_075 (0x1)
+#define RCV_IBIS_CTL_065 (0x2)
+#define RCV_IBIS_CTL_05 (0x3)
+
+#define HP_CLASSAB_IBIS_CTL_OFFSET (14)
+#define HP_CLASSAB_IBIS_CTL_MASK (0x3)
+#define HP_CLASSAB_IBIS_CTL_09 (0x0)
+#define HP_CLASSAB_IBIS_CTL_06 (0x1)
+#define HP_CLASSAB_IBIS_CTL_05 (0x2)
+#define HP_CLASSAB_IBIS_CTL_3_PER_7 (0x3)
+
+#define AUDIO_POP_CHGR_PD (2)
+#define AUDIO_POP_SOFTCHG_EN (1)
+
+
+/* ANA_CDC17 */
+
+/* ANA_CDC18 */
+#define CG_HPL_DCVI (8)
+#define CG_HPL_DCVI_MASK (0xFF)
+#define CG_HPR_DCVI (0)
+#define CG_HPR_DCVI_MASK (0xFF)
+
+/* ANA_HDT0 */
+#define AUD_V2ADC_EN (15)
+#define AUD_V2ADC_SEL (11)
+#define AUD_V2ADC_SEL_MASK (0xF)
+#define HIB_SBUT (1)
+#define HIB_SBUT_MASK (0xF)
+
+/* ANA_HDT1 */
+#define AUD_GND_DET_PU_PD (15)
+#define AUD_HEAD_L_INT_PU_PD (14)
+#define AUD_HEAD_APP_G (9)
+#define AUD_HEAD_APP_G_MASK (3)
+#define AUD_HEAD_APP_P (7)
+#define AUD_HEAD_APP_P_MASK (3)
+
+/* ANA_HDT2 */
+
+/* ANA_STS0 */
+#define HP_POP_FLG (7)////////////CHECK
+#define HP_POP_FLG_MASK (0x3)
+#define HP_POP_FLG_NEAR_CMP (3)
+#define OVP_FLAG (6)
+
+/* ANA_STS1 */
+
+/* ANA_STS2 */
+
+/* DANGL */
+#define AUDIO_DANGL (15)
+/* DANGR */
+#define AUDIO_DANGR (15)
+/* DRE */
+
+/* DIG_CFG0 */
+#define AUDIFA_DACL_EN (9)
+#define AUDIFA_ADCL_EN (10)
+#define AUDIFA_DACR_EN (11)
+#define AUDIFA_ADCR_EN (12)
+#define AUDIO_ADIE_LOOP_EN (14)
+
+/* DIG_CFG1 */
+
+/* DIG_CFG2 */
+
+/* DIG_CFG3 */
+#define AUD_A_INT_CLR (3)
+#define AUD_A_INT_CLR_MASK (0xFF)
+/* DIG_CFG4 */
+#define AUD_A_INT_EN (8)
+#define AUD_A_INT_EN_MASK (0xFF)
+/* DIG_STS0 */
+#define AUDIO_POP_IRQ (7)
+#define OVP_IRQ (6)
+#define OTP_IRQ (5)
+#define PA_OCP_IRQ (4)
+#define LOR_OCP_IRQ (3)
+#define LOL_OCP_IRQ (2)
+#define EAR_OCP_IRQ (1)
+#define HP_OCP_IRQ (0)
+
+/* DIG_STS1 */
+#define AUD_IRQ_MSK (8)
+#define AUD_IRQ_MSK_MASK (0xFF)
+
+
+#define SPRD_CODEC_DP_BASE (CODEC_DP_BASE)
+#define AUD_TOP_CTL (SPRD_CODEC_DP_BASE + 0x0000)
+#define AUD_AUD_CTR (SPRD_CODEC_DP_BASE + 0x0004)
+#define AUD_I2S_CTL (SPRD_CODEC_DP_BASE + 0x0008)
+#define AUD_DAC_CTL (SPRD_CODEC_DP_BASE + 0x000C)
+#define AUD_SDM_CTL0 (SPRD_CODEC_DP_BASE + 0x0010)
+#define AUD_SDM_CTL1 (SPRD_CODEC_DP_BASE + 0x0014)
+#define AUD_ADC_CTL (SPRD_CODEC_DP_BASE + 0x0018)
+#define AUD_LOOP_CTL (SPRD_CODEC_DP_BASE + 0x001C)
+#define AUD_AUD_STS0 (SPRD_CODEC_DP_BASE + 0x0020)
+#define AUD_INT_CLR (SPRD_CODEC_DP_BASE + 0x0024)
+#define AUD_INT_EN (SPRD_CODEC_DP_BASE + 0x0028)
+#define AUDIF_FIFO_CTL (SPRD_CODEC_DP_BASE + 0x002C)
+#define AUD_DMIC_CTL (SPRD_CODEC_DP_BASE + 0x0030)
+#define AUD_ADC1_I2S_CTL (SPRD_CODEC_DP_BASE + 0x0034)
+#define AUD_DAC_SDM_L (SPRD_CODEC_DP_BASE + 0x0038)
+#define AUD_DAC_SDM_H (SPRD_CODEC_DP_BASE + 0x003C)
+#define SPRD_CODEC_DP_END (SPRD_CODEC_DP_BASE + 0x0040)
+#define IS_SPRD_CODEC_DP_RANG(reg) (((reg) >= SPRD_CODEC_DP_BASE) && ((reg) < SPRD_CODEC_DP_END))
+#define SPRD_CODEC_AP_BASE (CODEC_AP_BASE)
+#define ANA_PMU0 (SPRD_CODEC_AP_BASE + 0x0000)
+#define ANA_PMU1 (SPRD_CODEC_AP_BASE + 0x0004)
+#define ANA_PMU2 (SPRD_CODEC_AP_BASE + 0x0008)
+#define ANA_PMU3 (SPRD_CODEC_AP_BASE + 0x000C)
+#define ANA_PMU4 (SPRD_CODEC_AP_BASE + 0x0010)
+#define ANA_PMU5 (SPRD_CODEC_AP_BASE + 0x0014)
+#define ANA_PMU6 (SPRD_CODEC_AP_BASE + 0x0018)
+#define ANA_CDC0 (SPRD_CODEC_AP_BASE + 0x001C)
+#define ANA_CDC1 (SPRD_CODEC_AP_BASE + 0x0020)
+#define ANA_CDC2 (SPRD_CODEC_AP_BASE + 0x0024)
+#define ANA_CDC3 (SPRD_CODEC_AP_BASE + 0x0028)
+#define ANA_CDC4 (SPRD_CODEC_AP_BASE + 0x002C)
+#define ANA_CDC5 (SPRD_CODEC_AP_BASE + 0x0030)
+#define ANA_CDC6 (SPRD_CODEC_AP_BASE + 0x0034)
+#define ANA_CDC7 (SPRD_CODEC_AP_BASE + 0x0038)
+#define ANA_CDC8 (SPRD_CODEC_AP_BASE + 0x003C)
+#define ANA_CDC9 (SPRD_CODEC_AP_BASE + 0x0040)
+#define ANA_CDC10 (SPRD_CODEC_AP_BASE + 0x0044)
+#define ANA_CDC11 (SPRD_CODEC_AP_BASE + 0x0048)
+#define ANA_CDC12 (SPRD_CODEC_AP_BASE + 0x004C)
+#define ANA_CDC13 (SPRD_CODEC_AP_BASE + 0x0050)
+#define ANA_CDC14 (SPRD_CODEC_AP_BASE + 0x0054)
+#define ANA_CDC15 (SPRD_CODEC_AP_BASE + 0x0058)
+#define ANA_CDC16 (SPRD_CODEC_AP_BASE + 0x005C)
+#define ANA_CDC17 (SPRD_CODEC_AP_BASE + 0x0060)
+#define ANA_CDC18 (SPRD_CODEC_AP_BASE + 0x0064)
+#define ANA_HDT0 (SPRD_CODEC_AP_BASE + 0x0068)
+#define ANA_HDT1 (SPRD_CODEC_AP_BASE + 0x006C)
+#define ANA_HDT2 (SPRD_CODEC_AP_BASE + 0x0070)
+#define ANA_STS0 (SPRD_CODEC_AP_BASE + 0x0074)
+#define ANA_STS1 (SPRD_CODEC_AP_BASE + 0x0078)
+#define ANA_STS2 (SPRD_CODEC_AP_BASE + 0x007C)
+#define DANGL (SPRD_CODEC_AP_BASE + 0x0080)
+#define DANGR (SPRD_CODEC_AP_BASE + 0x0084)
+#define DRE (SPRD_CODEC_AP_BASE + 0x0088)
+#define DIG_CFG0 (SPRD_CODEC_AP_BASE + 0x008C)
+#define DIG_CFG1 (SPRD_CODEC_AP_BASE + 0x0090)
+#define DIG_CFG2 (SPRD_CODEC_AP_BASE + 0x0094)
+#define DIG_CFG3 (SPRD_CODEC_AP_BASE + 0x0098)
+#define DIG_CFG4 (SPRD_CODEC_AP_BASE + 0x009C)
+#define DIG_STS0 (SPRD_CODEC_AP_BASE + 0x00A0)
+#define DIG_STS1 (SPRD_CODEC_AP_BASE + 0x00A4)
+#define SPRD_CODEC_AP_END (SPRD_CODEC_AP_BASE + 0x00A8)
+#define IS_SPRD_CODEC_AP_RANG(reg) (((reg) >= SPRD_CODEC_AP_BASE) && ((reg) < SPRD_CODEC_AP_END))
+
+#define SPRD_CODEC_IIS1_ID 111
+#endif /* __SPRD_CODEC_H */