From 79e9ed9b2d09a06d0ff96485f1f800cdc1699828 Mon Sep 17 00:00:00 2001
From: Iliyan Malchev <malchev@google.com>
Date: Fri, 3 Sep 2010 10:12:10 -0700
Subject: [PATCH] [ARM] tegra: stingray: cpcap-audio: integrate Motorola's
 driver

This reverts commit 02e784126d67e77c039123fed3f2ece48b6559f9.
---
 arch/arm/mach-tegra/board-stingray.c          |  104 +-
 .../arm/mach-tegra/include/mach/cpcap_audio.h |  218 +++-
 drivers/mfd/Makefile                          |    3 +-
 drivers/mfd/cpcap-audio-core.c                | 1126 +++++++++++++++++
 .../{cpcap-audio.c => tegra-cpcap-audio.c}    |  231 ++--
 include/linux/cpcap_audio.h                   |   23 +-
 6 files changed, 1490 insertions(+), 215 deletions(-)
 create mode 100644 drivers/mfd/cpcap-audio-core.c
 rename drivers/mfd/{cpcap-audio.c => tegra-cpcap-audio.c} (57%)

diff --git a/arch/arm/mach-tegra/board-stingray.c b/arch/arm/mach-tegra/board-stingray.c
index 8c501fef2773..e3df31d80653 100644
--- a/arch/arm/mach-tegra/board-stingray.c
+++ b/arch/arm/mach-tegra/board-stingray.c
@@ -229,88 +229,38 @@ static struct platform_device cpcap_otg = {
 	.num_resources = ARRAY_SIZE(cpcap_otg_resources),
 };
 
-#define CPCAP_REG(r, v, m) { .reg = (r), .val = (v), .mask = (m) }
-#define CPCAP_REG_SLAVE(r, v, m, s) { .reg = (r), .val = (v), \
-					.mask = (m), .slave_or = (s) }
-
-static const struct cpcap_audio_config_table speaker_config_table[] = {
-	CPCAP_REG(CPCAP_REG_VAUDIOC, 0x0007, 0x77),		/* 512 */
-	CPCAP_REG(CPCAP_REG_CC, 0x8E93, 0xFEDF),		/* 513 */
-	CPCAP_REG(CPCAP_REG_CDI, 0x1E42, 0xBFFF),		/* 514 */
-	CPCAP_REG(CPCAP_REG_SDAC, 0x0079, 0xFFF),		/* 515 */
-	CPCAP_REG_SLAVE(CPCAP_REG_SDACDI, 0x003E, 0x3FFF, 1),	/* 516 */
-	CPCAP_REG(CPCAP_REG_RXOA, 0x0218, 0x07FF),		/* 519 */
-	CPCAP_REG(CPCAP_REG_RXVC, 0x0028, 0x003C),		/* 520 */
-	CPCAP_REG(CPCAP_REG_RXCOA, 0x0618, 0x07FF),		/* 521 */
-	CPCAP_REG(CPCAP_REG_RXSDOA, 0x1818, 0x1FFF),		/* 522 */
-};
-
-static const struct cpcap_audio_config_table headset_config_table[] = {
-	CPCAP_REG(CPCAP_REG_VAUDIOC, 0x0007, 0x0077),		/* 512 */
-	CPCAP_REG(CPCAP_REG_CC, 0x8000, 0xFEDF),		/* 513 */
-	CPCAP_REG(CPCAP_REG_CDI, 0x8607, 0xBFFF),		/* 514 */
-	CPCAP_REG(CPCAP_REG_SDAC, 0x0079, 0xFFF),		/* 515 */
-	CPCAP_REG_SLAVE(CPCAP_REG_SDACDI, 0x003E, 0x3FFF, 1),	/* 516 */
-	CPCAP_REG(CPCAP_REG_RXOA, 0x0262, 0x07FF),		/* 519 */
-	CPCAP_REG(CPCAP_REG_RXVC, 0x0030, 0x003C),		/* 520 */
-	CPCAP_REG(CPCAP_REG_RXCOA, 0x0000, 0x07FF),		/* 521 */
-	CPCAP_REG(CPCAP_REG_RXSDOA, 0x1862, 0x1FFF),		/* 522 */
-};
-
-static const struct cpcap_audio_config_table mic1_config_table[] = {
-	CPCAP_REG(CPCAP_REG_VAUDIOC, 0x0035, 0x77),		/* 512 */
-	CPCAP_REG(CPCAP_REG_CC, 0x8F11, 0xFE11),		/* 513 */
-	CPCAP_REG(CPCAP_REG_CDI, 0x9E42, 0xBFFF),		/* 514 */
-	CPCAP_REG_SLAVE(CPCAP_REG_SDACDI, 0x003E, 0x3FFF, 1),	/* 516 */
-	CPCAP_REG(CPCAP_REG_TXI, 0x1CC6, 0xFFFF),		/* 517 */
-};
-
-static const struct cpcap_audio_config_table mic2_config_table[] = {
-	CPCAP_REG(CPCAP_REG_VAUDIOC, 0x0007, 0x77),
-	CPCAP_REG(CPCAP_REG_CC, 0x8FB3, 0xFEDF),
-	CPCAP_REG(CPCAP_REG_CDI, 0x1E40, 0xBFFF),
-	CPCAP_REG_SLAVE(CPCAP_REG_SDACDI, 0x007E, 0x3FFF, 1),
-	CPCAP_REG(CPCAP_REG_TXI, 0x0CC6, 0xFFFF),
-};
-
-#undef CPCAP_REG
-#undef CPCAP_REG_SLAVE
-
-static struct cpcap_audio_path speaker = {
-	.name = "speaker",
-	.gpio = TEGRA_GPIO_PR3,
-	.table = speaker_config_table,
-	.table_len = ARRAY_SIZE(speaker_config_table)
-};
-
-static const struct cpcap_audio_path headset = {
-	.name = "headset",
-	.gpio = TEGRA_GPIO_PS7,
-	.table = headset_config_table,
-	.table_len = ARRAY_SIZE(headset_config_table)
-};
-
-static const struct cpcap_audio_path mic1 = {
-	.name = "mic1",
-	.gpio = -1,
-	.table = mic1_config_table,
-	.table_len = ARRAY_SIZE(mic1_config_table),
-};
-
-static const struct cpcap_audio_path mic2 = {
-	.name = "mic2",
-	.gpio = -1,
-	.table = mic2_config_table,
-	.table_len = ARRAY_SIZE(mic2_config_table),
+static struct cpcap_audio_state stingray_cpcap_audio_state = {
+	NULL,
+	CPCAP_AUDIO_MODE_NORMAL,
+	CPCAP_AUDIO_CODEC_OFF,
+	CPCAP_AUDIO_CODEC_RATE_8000_HZ,
+	CPCAP_AUDIO_CODEC_MUTE,
+	CPCAP_AUDIO_STDAC_OFF,
+	CPCAP_AUDIO_STDAC_RATE_44100_HZ,
+	CPCAP_AUDIO_STDAC_MUTE,
+	CPCAP_AUDIO_ANALOG_SOURCE_OFF,
+	CPCAP_AUDIO_OUT_NONE,
+	CPCAP_AUDIO_OUT_NONE,
+	CPCAP_AUDIO_OUT_LINEOUT,
+	CPCAP_AUDIO_OUT_LINEOUT,
+	CPCAP_AUDIO_OUT_NONE,
+	CPCAP_AUDIO_OUT_NONE,
+	CPCAP_AUDIO_BALANCE_NEUTRAL,
+	CPCAP_AUDIO_BALANCE_NEUTRAL,
+	CPCAP_AUDIO_BALANCE_NEUTRAL,
+	7,                      /*default output gain */
+	CPCAP_AUDIO_IN_NONE,
+	31,                     /*default input_gain */
+	CPCAP_AUDIO_RAT_NONE
 };
 
 /* CPCAP is i2s master; tegra_audio_pdata.master == false */
 static struct cpcap_audio_platform_data cpcap_audio_pdata = {
 	.master = true,
-	.speaker = &speaker,
-	.headset = &headset,
-	.mic1 = &mic1,
-	.mic2 = &mic2,
+	.regulator = "vaudio",
+	.state = &stingray_cpcap_audio_state,
+	.speaker_gpio = TEGRA_GPIO_PR3,
+	.headset_gpio = TEGRA_GPIO_PS7,
 };
 
 static struct platform_device cpcap_audio_device = {
diff --git a/arch/arm/mach-tegra/include/mach/cpcap_audio.h b/arch/arm/mach-tegra/include/mach/cpcap_audio.h
index 1f6804dbaba7..85f740be57b7 100644
--- a/arch/arm/mach-tegra/include/mach/cpcap_audio.h
+++ b/arch/arm/mach-tegra/include/mach/cpcap_audio.h
@@ -2,6 +2,7 @@
  * arch/arm/mach-tegra/include/mach/cpcap_audio.h
  *
  * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2007 - 2010 Motorola, Inc.
  *
  * Author:
  *	Iliyan Malchev <malchev@google.com>
@@ -23,27 +24,216 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/platform_device.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/spi/cpcap.h>
 
-struct cpcap_audio_config_table {
-	int reg;
-	int val;
-	int mask;
-	int slave_or;
+enum {
+	CPCAP_AUDIO_MODE_NORMAL, /* mode of normal audio operation */
+	CPCAP_AUDIO_MODE_DAI, /* CPCAP_AUDIO is configured for DAI testing */
+	CPCAP_AUDIO_MODE_DAI_DOWNLINK = CPCAP_AUDIO_MODE_DAI,
+	CPCAP_AUDIO_MODE_DAI_UPLINK,
+	CPCAP_AUDIO_MODE_TTY /* CPCAP_AUDIO is configured for TTY */
 };
 
-struct cpcap_audio_path {
-	const char *name;
-	int gpio;
-	const struct cpcap_audio_config_table *table;
-	int table_len;
+enum {
+	CPCAP_AUDIO_CODEC_OFF, /* codec is powered down */
+	CPCAP_AUDIO_CODEC_CLOCK_ONLY, /* codec powered down, clocks running */
+	CPCAP_AUDIO_CODEC_ON, /* codec is completely operational */
+	CPCAP_AUDIO_CODEC_LOOPBACK /* xcap in (analog->digital->analog) mode */
+};
+
+enum {
+	CPCAP_AUDIO_CODEC_RATE_8000_HZ,
+		/* codec is running at 8Khz sample rate */
+	CPCAP_AUDIO_CODEC_RATE_11025_HZ,
+		/* codec is running at 11.025Khz sample rate */
+	CPCAP_AUDIO_CODEC_RATE_12000_HZ,
+		/* codec is running at 12Khz sample rate */
+	CPCAP_AUDIO_CODEC_RATE_16000_HZ,
+		/* codec is running at 16Khz sample rate */
+	CPCAP_AUDIO_CODEC_RATE_22050_HZ,
+		/* codec is running at 22.05Khz sample rate */
+	CPCAP_AUDIO_CODEC_RATE_24000_HZ,
+		/* codec is running at 24Khz sample rate */
+	CPCAP_AUDIO_CODEC_RATE_32000_HZ,
+		/* codec is running at 32Khz sample rate */
+	CPCAP_AUDIO_CODEC_RATE_44100_HZ,
+		/* codec is running at 44.1Khz sample rate */
+	CPCAP_AUDIO_CODEC_RATE_48000_HZ,
+		/* codec is running at 48Khz sample rate */
+};
+
+enum {
+	CPCAP_AUDIO_CODEC_UNMUTE, /* codec is unmuted */
+	CPCAP_AUDIO_CODEC_MUTE, /* codec is muted */
+	CPCAP_AUDIO_CODEC_BYPASS_LOOP
+				/* codec is bypassed
+				 * (analog-only loopback mode) */
+};
+
+enum {
+	CPCAP_AUDIO_STDAC_OFF,
+		/* stereo dac is powered down */
+	CPCAP_AUDIO_STDAC_CLOCK_ONLY,
+		/* stereo dac is powered down, but clocks are activated */
+	CPCAP_AUDIO_STDAC_ON
+		/* stereo dac is completely operational */
+};
+
+enum {
+		/* THESE MUST CORRESPOND TO XCPCAP_AUDIO SETTINGS */
+	CPCAP_AUDIO_STDAC_RATE_8000_HZ,
+		/* stereo dac set for 8Khz sample rate */
+	CPCAP_AUDIO_STDAC_RATE_11025_HZ,
+		/* stereo dac set for 11.025Khz sample rate */
+	CPCAP_AUDIO_STDAC_RATE_12000_HZ,
+		/* stereo dac set for 12Khz sample rate */
+	CPCAP_AUDIO_STDAC_RATE_16000_HZ,
+		/* stereo dac set for 16Khz sample rate */
+	CPCAP_AUDIO_STDAC_RATE_22050_HZ,
+		/* stereo dac set for 22.05Khz sample rate */
+	CPCAP_AUDIO_STDAC_RATE_24000_HZ,
+		/* stereo dac set for 24Khz sample rate */
+	CPCAP_AUDIO_STDAC_RATE_32000_HZ,
+		/* stereo dac set for 32Khz sample rate */
+	CPCAP_AUDIO_STDAC_RATE_44100_HZ,
+		/* stereo dac set for 44.1Khz sample rate */
+	CPCAP_AUDIO_STDAC_RATE_48000_HZ
+		/* stereo dac set for 48Khz sample rate */
+};
+
+enum {
+	CPCAP_AUDIO_STDAC_UNMUTE, /* stereo dac is unmuted */
+	CPCAP_AUDIO_STDAC_MUTE /* stereo dac is muted */
+};
+
+enum {
+	CPCAP_AUDIO_ANALOG_SOURCE_OFF,
+		/* Analog PGA input is disabled */
+	CPCAP_AUDIO_ANALOG_SOURCE_R,
+		/* Right analog PGA input is enabled */
+	CPCAP_AUDIO_ANALOG_SOURCE_L,
+		/* Left analog PGA input is enabled */
+	CPCAP_AUDIO_ANALOG_SOURCE_STEREO
+		/* Both analog PGA inputs are enabled */
+};
+
+enum {
+	CPCAP_AUDIO_OUT_NONE,
+		/* No audio output selected */
+	CPCAP_AUDIO_OUT_HANDSET,
+		/* handset (earpiece) speaker */
+	CPCAP_AUDIO_OUT_LOUDSPEAKER,
+		/* loudspeaker (speakerphone) */
+	CPCAP_AUDIO_OUT_LINEAR_VIBRATOR,
+		/* linear vibrator, if equipped */
+	CPCAP_AUDIO_OUT_MONO_HEADSET,
+		/* mono (R channel) x.5mm headset */
+	CPCAP_AUDIO_OUT_STEREO_HEADSET,
+		/* stereo x.5mm headset */
+	CPCAP_AUDIO_OUT_EXT_BUS_MONO,
+		/* accessory bus mono output(EMU) */
+	CPCAP_AUDIO_OUT_EMU_MONO,
+	CPCAP_AUDIO_OUT_EXT_BUS_STEREO,
+		/* accessory bus stereo output (EMU only) */
+	CPCAP_AUDIO_OUT_EMU_STEREO,
+	CPCAP_AUDIO_OUT_LINEOUT,
+	CPCAP_AUDIO_OUT_BT_MONO,
+	CPCAP_AUDIO_OUT_NUM_OF_PATHS
+		/* Max number of audio output paths */
+};
+
+enum {
+	CPCAP_AUDIO_IN_NONE,
+		/* No audio input selected */
+	CPCAP_AUDIO_IN_HANDSET,
+		/* handset (internal) microphone */
+	CPCAP_AUDIO_IN_AUX_INTERNAL,
+		/* Auxiliary (second) internal mic */
+	CPCAP_AUDIO_IN_DUAL_INTERNAL,
+		/* both internal microphones are connected */
+	CPCAP_AUDIO_IN_HEADSET,
+		/* Audio <- x.5mm headset microphone */
+	CPCAP_AUDIO_IN_EXT_BUS,
+		/* Audio <- accessory bus analog input (EMU) */
+	CPCAP_AUDIO_IN_EMU = CPCAP_AUDIO_IN_EXT_BUS,
+	CPCAP_AUDIO_IN_HEADSET_BIAS_ONLY,
+		/* 3.5mm headset control when no mic is selected */
+	CPCAP_AUDIO_IN_DUAL_EXTERNAL,
+		/* Recording from external source */
+	CPCAP_AUDIO_IN_BT_MONO,
+	CPCAP_AUDIO_IN_NUM_OF_PATHS
+		/* Max number of audio input paths */
+};
+
+enum {
+		/* Defines the audio path type */
+	CPCAP_AUDIO_AUDIO_IN_PATH,
+		/* Audio input path refers to CPCAP_AUDIO_MIC_TYPE */
+	CPCAP_AUDIO_AUDIO_OUT_PATH
+		/* Audio output path refers to CPCAP_AUDIO_SPEAKER_TYPE */
+};
+
+enum {
+	CPCAP_AUDIO_BALANCE_NEUTRAL,/* audio routed normally */
+	CPCAP_AUDIO_BALANCE_R_ONLY, /* audio routed to left channel only */
+	CPCAP_AUDIO_BALANCE_L_ONLY /* audio routed to right channel only */
+};
+
+enum {
+	CPCAP_AUDIO_RAT_NONE, /* Not in a call mode */
+	CPCAP_AUDIO_RAT_2G,   /* In 2G call mode */
+	CPCAP_AUDIO_RAT_3G,   /* In 3G call mode */
+	CPCAP_AUDIO_RAT_CDMA  /* In CDMA call mode */
+};
+
+/* Clock multipliers for A2LA register */
+enum {
+	CPCAP_AUDIO_A2_CLOCK_MASK  = CPCAP_BIT_A2_CLK2 | CPCAP_BIT_A2_CLK1 |
+							 CPCAP_BIT_A2_CLK0,
+	CPCAP_AUDIO_A2_CLOCK_15_36 = CPCAP_BIT_A2_CLK0,
+	CPCAP_AUDIO_A2_CLOCK_16_80 = CPCAP_BIT_A2_CLK1,
+	CPCAP_AUDIO_A2_CLOCK_19_20 = CPCAP_BIT_A2_CLK1 | CPCAP_BIT_A2_CLK0,
+	CPCAP_AUDIO_A2_CLOCK_26_00 = CPCAP_BIT_A2_CLK2,
+	CPCAP_AUDIO_A2_CLOCK_33_60 = CPCAP_BIT_A2_CLK2 | CPCAP_BIT_A2_CLK0,
+	CPCAP_AUDIO_A2_CLOCK_38_40 = CPCAP_BIT_A2_CLK2 | CPCAP_BIT_A2_CLK1
+};
+
+struct cpcap_audio_state {
+	struct cpcap_device *cpcap;
+	int mode;
+	int codec_mode;
+	int codec_rate;
+	int codec_mute;
+	int stdac_mode;
+	int stdac_rate;
+	int stdac_mute;
+	int analog_source;
+	int codec_primary_speaker;
+	int codec_secondary_speaker;
+	int stdac_primary_speaker;
+	int stdac_secondary_speaker;
+	int ext_primary_speaker;
+	int ext_secondary_speaker;
+	int codec_primary_balance;
+	int stdac_primary_balance;
+	int ext_primary_balance;
+	unsigned int output_gain;
+	int microphone;
+	unsigned int input_gain;
+	int rat_type;
 };
 
 struct cpcap_audio_platform_data {
 	bool master;
-	const struct cpcap_audio_path *speaker;
-	const struct cpcap_audio_path *headset;
-	const struct cpcap_audio_path *mic1;
-	const struct cpcap_audio_path *mic2;
+	const char *regulator;
+	struct cpcap_audio_state *state;
+	int speaker_gpio;
+	int headset_gpio;
 };
 
+int cpcap_audio_init(struct cpcap_audio_state *state, const char *regulator);
+void cpcap_audio_register_dump(struct cpcap_audio_state *state);
+void cpcap_audio_set_audio_state(struct cpcap_audio_state *state);
+
 #endif/*__ARCH_ARM_MACH_TEGRA_CPCAP_AUDIO_H_*/
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index ece21b1a98a2..a28fc814aea6 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -85,6 +85,7 @@ cpcap-objs			:= cpcap-core.o \
 				   cpcap-adc.o \
 				   cpcap-uc.o \
 				   cpcap-3mm5.o \
-				   cpcap-audio.o
+				   tegra-cpcap-audio.o \
+				   cpcap-audio-core.o
 
 obj-$(CONFIG_MFD_CPCAP)		+= cpcap.o
diff --git a/drivers/mfd/cpcap-audio-core.c b/drivers/mfd/cpcap-audio-core.c
new file mode 100644
index 000000000000..8c725a00fe79
--- /dev/null
+++ b/drivers/mfd/cpcap-audio-core.c
@@ -0,0 +1,1126 @@
+/*
+ * Copyright (C) 2007 - 2009 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/spi/cpcap.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/cpcap_audio.h>
+
+#define SLEEP_ACTIVATE_POWER_DELAY_MS	2
+#define CLOCK_TREE_RESET_DELAY_MS	1
+
+#define CPCAP_AUDIO_SPI_READBACK	1
+
+#define E(args...)  pr_err("cpcap-audio: " args)
+
+static struct cpcap_audio_state current_state = {
+	.cpcap				= NULL,
+	.mode				= CPCAP_AUDIO_MODE_NORMAL,
+
+	.codec_mode			= CPCAP_AUDIO_CODEC_OFF,
+	.codec_rate			= CPCAP_AUDIO_CODEC_RATE_8000_HZ,
+	.codec_mute			= CPCAP_AUDIO_CODEC_MUTE,
+
+	.stdac_mode			= CPCAP_AUDIO_STDAC_OFF,
+	.stdac_rate			= CPCAP_AUDIO_STDAC_RATE_8000_HZ,
+	.stdac_mute			= CPCAP_AUDIO_STDAC_MUTE,
+
+	.analog_source			= CPCAP_AUDIO_ANALOG_SOURCE_OFF,
+
+	.codec_primary_speaker		= CPCAP_AUDIO_OUT_NONE,
+	.codec_secondary_speaker	= CPCAP_AUDIO_OUT_NONE,
+
+	.stdac_primary_speaker		= CPCAP_AUDIO_OUT_NONE,
+	.stdac_secondary_speaker	= CPCAP_AUDIO_OUT_NONE,
+
+	.ext_primary_speaker		= CPCAP_AUDIO_OUT_NONE,
+	.ext_secondary_speaker		= CPCAP_AUDIO_OUT_NONE,
+
+	.codec_primary_balance		= CPCAP_AUDIO_BALANCE_NEUTRAL,
+	.stdac_primary_balance		= CPCAP_AUDIO_BALANCE_NEUTRAL,
+	.ext_primary_balance		= CPCAP_AUDIO_BALANCE_NEUTRAL,
+
+	.output_gain			= 0,
+	.microphone			= CPCAP_AUDIO_IN_NONE,
+	.input_gain			= 0,
+	.rat_type			= CPCAP_AUDIO_RAT_NONE
+};
+
+/* Define regulator to turn on the audio portion of cpcap */
+struct regulator *audio_reg;
+
+static inline bool is_mic_stereo(int microphone)
+{
+	return microphone == CPCAP_AUDIO_IN_DUAL_INTERNAL ||
+		microphone == CPCAP_AUDIO_IN_DUAL_EXTERNAL;
+}
+
+static inline bool is_codec_changed(struct cpcap_audio_state *state,
+					struct cpcap_audio_state *prev)
+{
+	return state->codec_mode != prev->codec_mode ||
+		state->codec_rate != prev->codec_rate ||
+		state->rat_type != prev->rat_type ||
+		state->microphone != prev->microphone;
+}
+
+static inline bool is_stdac_changed(struct cpcap_audio_state *state,
+					struct cpcap_audio_state *prev)
+{
+	return state->stdac_mode != prev->stdac_mode ||
+		state->rat_type != prev->rat_type ||
+		state->stdac_rate != prev->stdac_rate;
+}
+
+static inline bool is_output_bt_only(struct cpcap_audio_state *state)
+{
+	if (state->codec_primary_speaker == CPCAP_AUDIO_OUT_BT_MONO &&
+			state->codec_secondary_speaker == CPCAP_AUDIO_OUT_NONE)
+		return true;
+
+	if (state->stdac_primary_speaker == CPCAP_AUDIO_OUT_BT_MONO &&
+			state->stdac_secondary_speaker == CPCAP_AUDIO_OUT_NONE)
+		return true;
+
+	if (state->ext_primary_speaker == CPCAP_AUDIO_OUT_BT_MONO &&
+			state->ext_secondary_speaker == CPCAP_AUDIO_OUT_NONE)
+		return true;
+
+	return false;
+}
+
+static inline bool is_output_headset(struct cpcap_audio_state *state)
+{
+	if (state->codec_primary_speaker == CPCAP_AUDIO_OUT_STEREO_HEADSET ||
+			state->codec_primary_speaker ==
+				CPCAP_AUDIO_OUT_MONO_HEADSET ||
+			state->codec_secondary_speaker ==
+				CPCAP_AUDIO_OUT_STEREO_HEADSET ||
+			state->codec_secondary_speaker ==
+				CPCAP_AUDIO_OUT_MONO_HEADSET)
+		return true;
+
+	if (state->stdac_primary_speaker == CPCAP_AUDIO_OUT_STEREO_HEADSET ||
+			state->stdac_primary_speaker ==
+				CPCAP_AUDIO_OUT_MONO_HEADSET ||
+			state->stdac_secondary_speaker ==
+				CPCAP_AUDIO_OUT_STEREO_HEADSET ||
+			state->stdac_secondary_speaker ==
+				CPCAP_AUDIO_OUT_MONO_HEADSET)
+		return true;
+
+	if (state->ext_primary_speaker == CPCAP_AUDIO_OUT_STEREO_HEADSET ||
+			state->ext_primary_speaker ==
+				CPCAP_AUDIO_OUT_MONO_HEADSET ||
+			state->ext_secondary_speaker ==
+				CPCAP_AUDIO_OUT_STEREO_HEADSET ||
+			state->ext_secondary_speaker ==
+				CPCAP_AUDIO_OUT_MONO_HEADSET)
+		return true;
+
+	return false;
+}
+
+static inline bool is_speaker_turning_off(struct cpcap_audio_state *state,
+					struct cpcap_audio_state *prev)
+{
+	return (prev->codec_primary_speaker != CPCAP_AUDIO_OUT_NONE &&
+			state->codec_primary_speaker ==
+				CPCAP_AUDIO_OUT_NONE) ||
+		(prev->codec_secondary_speaker != CPCAP_AUDIO_OUT_NONE &&
+			state->codec_secondary_speaker ==
+				CPCAP_AUDIO_OUT_NONE) ||
+		(prev->stdac_primary_speaker != CPCAP_AUDIO_OUT_NONE &&
+			state->stdac_primary_speaker ==
+				CPCAP_AUDIO_OUT_NONE) ||
+		(prev->stdac_secondary_speaker != CPCAP_AUDIO_OUT_NONE &&
+			state->stdac_secondary_speaker ==
+				CPCAP_AUDIO_OUT_NONE) ||
+		(prev->ext_primary_speaker != CPCAP_AUDIO_OUT_NONE &&
+			state->ext_primary_speaker ==
+				CPCAP_AUDIO_OUT_NONE) ||
+		(prev->ext_secondary_speaker != CPCAP_AUDIO_OUT_NONE &&
+			state->ext_secondary_speaker ==
+				CPCAP_AUDIO_OUT_NONE);
+}
+
+static inline bool is_output_changed(struct cpcap_audio_state *state,
+			struct cpcap_audio_state *prev)
+{
+	if (state->codec_primary_speaker != prev->codec_primary_speaker ||
+			state->codec_primary_balance !=
+				prev->codec_primary_balance ||
+			state->codec_secondary_speaker !=
+				prev->codec_secondary_speaker)
+		return true;
+
+	if (state->stdac_primary_speaker != prev->stdac_primary_speaker ||
+			state->stdac_primary_balance !=
+				prev->stdac_primary_balance ||
+			state->stdac_secondary_speaker !=
+				prev->stdac_secondary_speaker)
+		return true;
+
+	if (state->ext_primary_speaker != prev->ext_primary_speaker ||
+			state->ext_primary_balance !=
+				prev->ext_primary_balance ||
+			state->ext_secondary_speaker !=
+				prev->ext_secondary_speaker)
+		return true;
+
+	return false;
+}
+
+/* this is only true for audio registers, but those are the only ones we use */
+#define CPCAP_REG_FOR_POWERIC_REG(a) ((a) + (0x200 - CPCAP_REG_VAUDIOC))
+
+static void logged_cpcap_write(struct cpcap_device *cpcap, unsigned int reg,
+			unsigned short int value, unsigned short int mask)
+{
+	if (mask != 0) {
+		int ret_val = 0;
+		pr_debug("%s: audio: reg %u, value 0x%x,mask 0x%x\n", __func__,
+			CPCAP_REG_FOR_POWERIC_REG(reg), value, mask);
+		ret_val = cpcap_regacc_write(cpcap, reg, value, mask);
+		if (ret_val != 0)
+			E("%s: w %04x m %04x -> r %u failed: %d\n", __func__,
+				value, mask, reg, ret_val);
+#if CPCAP_AUDIO_SPI_READBACK
+		ret_val = cpcap_regacc_read(cpcap, reg, &value);
+		if (ret_val == 0)
+			pr_debug("%s: audio verify: reg %u: value 0x%x\n",
+				__func__,
+				CPCAP_REG_FOR_POWERIC_REG(reg), value);
+		else
+			E("%s: audio verify: reg %u FAILED\n", __func__,
+				CPCAP_REG_FOR_POWERIC_REG(reg));
+#endif
+	}
+}
+
+static unsigned short int cpcap_audio_get_codec_output_amp_switches(
+						int speaker, int balance)
+{
+	unsigned short int value = CPCAP_BIT_PGA_CDC_EN;
+
+	pr_debug("%s() called with speaker = %d\n", __func__,
+			  speaker);
+
+	switch (speaker) {
+	case CPCAP_AUDIO_OUT_HANDSET:
+		value |= CPCAP_BIT_A1_EAR_CDC_SW;
+		break;
+
+	case CPCAP_AUDIO_OUT_LOUDSPEAKER:
+		value |= CPCAP_BIT_A2_LDSP_L_CDC_SW;
+		break;
+
+	case CPCAP_AUDIO_OUT_MONO_HEADSET:
+	case CPCAP_AUDIO_OUT_STEREO_HEADSET:
+		if (balance != CPCAP_AUDIO_BALANCE_L_ONLY)
+			value |= CPCAP_BIT_ARIGHT_HS_CDC_SW;
+		if (balance != CPCAP_AUDIO_BALANCE_R_ONLY)
+			value |= CPCAP_BIT_ALEFT_HS_CDC_SW;
+		break;
+
+	case CPCAP_AUDIO_OUT_LINEOUT:
+		value |= CPCAP_BIT_A4_LINEOUT_R_CDC_SW |
+			CPCAP_BIT_A4_LINEOUT_L_CDC_SW;
+		break;
+
+	case CPCAP_AUDIO_OUT_BT_MONO:
+	default:
+		value = 0;
+		break;
+	}
+
+	pr_debug("Exiting %s() with return value = %d\n", __func__,
+			  value);
+	return value;
+}
+
+static unsigned short int cpcap_audio_get_stdac_output_amp_switches(
+						int speaker, int balance)
+{
+	unsigned short int value = CPCAP_BIT_PGA_DAC_EN;
+
+	pr_debug("%s() called with speaker = %d\n", __func__,
+			  speaker);
+
+	switch (speaker) {
+	case CPCAP_AUDIO_OUT_HANDSET:
+		value |= CPCAP_BIT_A1_EAR_DAC_SW;
+		break;
+
+	case CPCAP_AUDIO_OUT_MONO_HEADSET:
+	case CPCAP_AUDIO_OUT_STEREO_HEADSET:
+		if (balance != CPCAP_AUDIO_BALANCE_R_ONLY)
+			value |= CPCAP_BIT_ALEFT_HS_DAC_SW;
+		if (balance != CPCAP_AUDIO_BALANCE_L_ONLY)
+			value |= CPCAP_BIT_ARIGHT_HS_DAC_SW;
+		break;
+
+	case CPCAP_AUDIO_OUT_LOUDSPEAKER:
+		value |= CPCAP_BIT_A2_LDSP_L_DAC_SW | CPCAP_BIT_MONO_DAC0 |
+			CPCAP_BIT_MONO_DAC1;
+		break;
+
+	case CPCAP_AUDIO_OUT_LINEOUT:
+		value |= CPCAP_BIT_A4_LINEOUT_R_DAC_SW |
+			CPCAP_BIT_A4_LINEOUT_L_DAC_SW;
+		break;
+
+	case CPCAP_AUDIO_OUT_BT_MONO:
+	default:
+		value = 0;
+		break;
+	}
+
+	pr_debug("Exiting %s() with return value = %d\n", __func__,
+			  value);
+	return value;
+}
+
+static unsigned short int cpcap_audio_get_ext_output_amp_switches(
+						int speaker, int balance)
+{
+	unsigned short int value = 0;
+	pr_debug("%s() called with speaker %d\n", __func__,
+								speaker);
+	switch (speaker) {
+	case CPCAP_AUDIO_OUT_HANDSET:
+		value = CPCAP_BIT_A1_EAR_EXT_SW | CPCAP_BIT_PGA_EXT_R_EN;
+		break;
+
+	case CPCAP_AUDIO_OUT_MONO_HEADSET:
+	case CPCAP_AUDIO_OUT_STEREO_HEADSET:
+		if (balance != CPCAP_AUDIO_BALANCE_L_ONLY)
+			value = CPCAP_BIT_ARIGHT_HS_EXT_SW |
+				CPCAP_BIT_PGA_EXT_R_EN;
+		if (balance != CPCAP_AUDIO_BALANCE_R_ONLY)
+			value |= CPCAP_BIT_ALEFT_HS_EXT_SW |
+				CPCAP_BIT_PGA_EXT_L_EN;
+		break;
+
+	case CPCAP_AUDIO_OUT_LOUDSPEAKER:
+		value = CPCAP_BIT_A2_LDSP_L_EXT_SW | CPCAP_BIT_PGA_EXT_L_EN;
+		break;
+
+	case CPCAP_AUDIO_OUT_LINEOUT:
+		value = CPCAP_BIT_A4_LINEOUT_R_EXT_SW |
+			CPCAP_BIT_A4_LINEOUT_L_EXT_SW |
+			CPCAP_BIT_PGA_EXT_L_EN | CPCAP_BIT_PGA_EXT_R_EN;
+		break;
+
+	case CPCAP_AUDIO_OUT_BT_MONO:
+	default:
+		value = 0;
+		break;
+	}
+
+	pr_debug("Exiting %s() with return value = %d\n", __func__,
+			  value);
+	return value;
+}
+
+static void cpcap_audio_set_output_amp_switches(struct cpcap_audio_state *state)
+{
+	static unsigned int codec_prev_settings;
+	static unsigned int stdac_prev_settings;
+	static unsigned int ext_prev_settings;
+
+	struct cpcap_regacc reg_changes;
+	unsigned short int value1 = 0, value2 = 0;
+
+	/* First set codec output amp switches */
+	value1 = cpcap_audio_get_codec_output_amp_switches(state->
+			codec_primary_speaker, state->codec_primary_balance);
+	value2 = cpcap_audio_get_codec_output_amp_switches(state->
+			codec_secondary_speaker, state->codec_primary_balance);
+
+	reg_changes.mask = value1 | value2 | codec_prev_settings;
+	reg_changes.value = value1 | value2;
+	codec_prev_settings = reg_changes.value;
+
+	logged_cpcap_write(state->cpcap, CPCAP_REG_RXCOA, reg_changes.value,
+							reg_changes.mask);
+
+	/* Second Stdac switches */
+	value1 = cpcap_audio_get_stdac_output_amp_switches(state->
+			stdac_primary_speaker, state->stdac_primary_balance);
+	value2 = cpcap_audio_get_stdac_output_amp_switches(state->
+			stdac_secondary_speaker, state->stdac_primary_balance);
+
+	reg_changes.mask = value1 | value2 | stdac_prev_settings;
+	reg_changes.value = value1 | value2;
+
+	if ((state->stdac_primary_speaker == CPCAP_AUDIO_OUT_STEREO_HEADSET &&
+		state->stdac_secondary_speaker == CPCAP_AUDIO_OUT_LOUDSPEAKER)
+		|| (state->stdac_primary_speaker == CPCAP_AUDIO_OUT_LOUDSPEAKER
+		&& state->stdac_secondary_speaker ==
+						CPCAP_AUDIO_OUT_STEREO_HEADSET))
+		reg_changes.value &= ~(CPCAP_BIT_MONO_DAC0 |
+					CPCAP_BIT_MONO_DAC1);
+
+	stdac_prev_settings = reg_changes.value;
+
+	logged_cpcap_write(state->cpcap, CPCAP_REG_RXSDOA, reg_changes.value,
+							reg_changes.mask);
+
+	/* Last External source switches */
+	value1 =
+	    cpcap_audio_get_ext_output_amp_switches(state->
+				ext_primary_speaker,
+				state->ext_primary_balance);
+	value2 =
+	    cpcap_audio_get_ext_output_amp_switches(state->
+				ext_secondary_speaker,
+				state->ext_primary_balance);
+
+	reg_changes.mask = value1 | value2 | ext_prev_settings;
+	reg_changes.value = value1 | value2;
+	ext_prev_settings = reg_changes.value;
+
+	logged_cpcap_write(state->cpcap, CPCAP_REG_RXEPOA,
+			reg_changes.value, reg_changes.mask);
+}
+
+static bool cpcap_audio_set_bits_for_speaker(int speaker, int balance,
+						unsigned short int *message)
+{
+	pr_debug("%s() called with speaker = %d\n", __func__,
+			  speaker);
+
+	/* Get the data required to enable each possible path */
+	switch (speaker) {
+	case CPCAP_AUDIO_OUT_HANDSET:
+		(*message) |= CPCAP_BIT_A1_EAR_EN;
+		break;
+
+	case CPCAP_AUDIO_OUT_MONO_HEADSET:
+	case CPCAP_AUDIO_OUT_STEREO_HEADSET:
+		if (balance != CPCAP_AUDIO_BALANCE_R_ONLY)
+			(*message) |= CPCAP_BIT_HS_L_EN;
+		if (balance != CPCAP_AUDIO_BALANCE_L_ONLY)
+			(*message) |= CPCAP_BIT_HS_R_EN;
+		break;
+
+	case CPCAP_AUDIO_OUT_LOUDSPEAKER:
+		(*message) |= CPCAP_BIT_A2_LDSP_L_EN;
+		break;
+
+	case CPCAP_AUDIO_OUT_LINEOUT:
+		(*message) |= CPCAP_BIT_A4_LINEOUT_R_EN |
+				CPCAP_BIT_A4_LINEOUT_L_EN;
+		break;
+
+	case CPCAP_AUDIO_OUT_BT_MONO:
+	default:
+		(*message) |= 0;
+		break;
+	}
+
+	return false; /* There is no external loudspeaker on this product */
+}
+
+static void cpcap_audio_configure_aud_mute(struct cpcap_audio_state *state,
+				struct cpcap_audio_state *prev)
+{
+	struct cpcap_regacc reg_changes = { 0 };
+	unsigned short int value1 = 0, value2 = 0;
+
+	if (state->codec_mute != prev->codec_mute) {
+		value1 = cpcap_audio_get_codec_output_amp_switches(
+				prev->codec_primary_speaker,
+				prev->codec_primary_balance);
+
+		value2 = cpcap_audio_get_codec_output_amp_switches(
+				prev->codec_secondary_speaker,
+				prev->codec_primary_balance);
+
+		reg_changes.mask = value1 | value2 | CPCAP_BIT_CDC_SW;
+
+		if (state->codec_mute == CPCAP_AUDIO_CODEC_UNMUTE)
+			reg_changes.value = reg_changes.mask;
+
+		logged_cpcap_write(state->cpcap, CPCAP_REG_RXCOA,
+					reg_changes.value, reg_changes.mask);
+	}
+
+	if (state->stdac_mute != prev->stdac_mute) {
+		value1 = cpcap_audio_get_stdac_output_amp_switches(
+				prev->stdac_primary_speaker,
+				prev->stdac_primary_balance);
+
+		value2 = cpcap_audio_get_stdac_output_amp_switches(
+				prev->stdac_secondary_speaker,
+				prev->stdac_primary_balance);
+
+		reg_changes.mask = value1 | value2 | CPCAP_BIT_ST_DAC_SW;
+
+		if (state->stdac_mute == CPCAP_AUDIO_STDAC_UNMUTE)
+			reg_changes.value = reg_changes.mask;
+
+		logged_cpcap_write(state->cpcap, CPCAP_REG_RXSDOA,
+					reg_changes.value, reg_changes.mask);
+	}
+}
+
+static void cpcap_audio_configure_codec(struct cpcap_audio_state *state,
+				struct cpcap_audio_state *prev)
+{
+	unsigned int temp_codec_rate = state->codec_rate;
+	struct cpcap_regacc cdai_changes = { 0 };
+	struct cpcap_regacc codec_changes = { 0 };
+	int codec_freq_config = 0;
+
+	const unsigned int CODEC_FREQ_MASK = CPCAP_BIT_CDC_CLK0
+		| CPCAP_BIT_CDC_CLK1 | CPCAP_BIT_CDC_CLK2;
+	const unsigned int CODEC_RESET_FREQ_MASK = CODEC_FREQ_MASK
+		| CPCAP_BIT_CDC_CLOCK_TREE_RESET;
+
+	static unsigned int prev_codec_data = 0x0, prev_cdai_data = 0x0;
+
+	if (!is_codec_changed(state, prev))
+		return;
+
+	if (state->rat_type == CPCAP_AUDIO_RAT_CDMA)
+		codec_freq_config = (CPCAP_BIT_CDC_CLK0
+				| CPCAP_BIT_CDC_CLK1) ; /* 19.2Mhz */
+	else
+		codec_freq_config = CPCAP_BIT_CDC_CLK2 ; /* 26Mhz */
+
+	/* If a codec is already in use, reset codec to initial state */
+	if (prev->codec_mode != CPCAP_AUDIO_CODEC_OFF) {
+		codec_changes.mask = prev_codec_data
+			| CPCAP_BIT_DF_RESET
+			| CPCAP_BIT_CDC_CLOCK_TREE_RESET;
+
+		logged_cpcap_write(state->cpcap, CPCAP_REG_CC,
+			codec_changes.value, codec_changes.mask);
+
+		prev_codec_data = 0;
+		prev->codec_mode = CPCAP_AUDIO_CODEC_OFF;
+	}
+
+	temp_codec_rate &= 0x0000000F;
+	temp_codec_rate = temp_codec_rate << 9;
+
+	switch (state->codec_mode) {
+	case CPCAP_AUDIO_CODEC_LOOPBACK:
+	case CPCAP_AUDIO_CODEC_ON:
+		if (state->codec_primary_speaker !=
+			CPCAP_AUDIO_OUT_NONE) {
+			codec_changes.value |= CPCAP_BIT_CDC_EN_RX;
+		}
+
+		/* Turning on the input HPF */
+		if (state->microphone != CPCAP_AUDIO_IN_NONE)
+			codec_changes.value |= CPCAP_BIT_AUDIHPF_0 |
+						CPCAP_BIT_AUDIHPF_1;
+
+#if 1
+		if (state->microphone != CPCAP_AUDIO_IN_NONE) {
+			codec_changes.value |= CPCAP_BIT_MIC1_CDC_EN;
+			codec_changes.value |= CPCAP_BIT_MIC2_CDC_EN;
+		}
+#else
+		if (state->microphone != CPCAP_AUDIO_IN_AUX_INTERNAL &&
+			state->microphone != CPCAP_AUDIO_IN_NONE)
+			codec_changes.value |= CPCAP_BIT_MIC1_CDC_EN |
+						CPCAP_BIT_MIC2_CDC_EN;
+
+		if (state->microphone == CPCAP_AUDIO_IN_AUX_INTERNAL ||
+			is_mic_stereo(state->microphone))
+			codec_changes.value |= CPCAP_BIT_MIC2_CDC_EN;
+#endif
+
+	/* falling through intentionally */
+	case CPCAP_AUDIO_CODEC_CLOCK_ONLY:
+		codec_changes.value |=
+			(codec_freq_config | temp_codec_rate |
+			CPCAP_BIT_DF_RESET);
+		cdai_changes.value |= CPCAP_BIT_CDC_CLK_EN;
+		break;
+
+	case CPCAP_AUDIO_CODEC_OFF:
+		cdai_changes.value |= CPCAP_BIT_SMB_CDC;
+		break;
+
+	default:
+		break;
+	}
+
+	/* Multimedia uses CLK_IN0, incall uses CLK_IN1 */
+	if (state->rat_type != CPCAP_AUDIO_RAT_NONE)
+		cdai_changes.value |= CPCAP_BIT_CLK_IN_SEL;
+
+	cdai_changes.value |= CPCAP_BIT_CDC_PLL_SEL | CPCAP_BIT_CLK_INV;
+	cdai_changes.value |= CPCAP_BIT_DIG_AUD_IN;
+
+	/* Setting I2S mode */
+	cdai_changes.value |= CPCAP_BIT_CDC_DIG_AUD_FS0 |
+			CPCAP_BIT_CDC_DIG_AUD_FS1 |
+			CPCAP_BIT_MIC2_TIMESLOT0;
+
+	/* OK, now start paranoid codec sequence */
+	/* FIRST, make sure the frequency config is right... */
+	logged_cpcap_write(state->cpcap, CPCAP_REG_CC,
+				codec_freq_config, CODEC_FREQ_MASK);
+
+	/* Next, write the CDAI if it's changed */
+	if (prev_cdai_data != cdai_changes.value) {
+		cdai_changes.mask = cdai_changes.value
+			| prev_cdai_data;
+		prev_cdai_data = cdai_changes.value;
+
+		logged_cpcap_write(state->cpcap, CPCAP_REG_CDI,
+				cdai_changes.value, cdai_changes.mask);
+
+		/* Clock tree change -- reset and wait */
+		codec_freq_config |= CPCAP_BIT_CDC_CLOCK_TREE_RESET;
+
+		logged_cpcap_write(state->cpcap, CPCAP_REG_CC,
+			codec_freq_config, CODEC_RESET_FREQ_MASK);
+
+		/* Wait for clock tree reset to complete */
+		mdelay(CLOCK_TREE_RESET_DELAY_MS);
+	}
+
+	/* Clear old settings */
+	codec_changes.mask = codec_changes.value | prev_codec_data;
+	prev_codec_data    = codec_changes.value;
+
+	logged_cpcap_write(state->cpcap, CPCAP_REG_CC,
+			codec_changes.value, codec_changes.mask);
+}
+
+static void cpcap_audio_configure_stdac(struct cpcap_audio_state *state,
+				struct cpcap_audio_state *prev)
+{
+	const unsigned int SDAC_FREQ_MASK = CPCAP_BIT_ST_DAC_CLK0
+			| CPCAP_BIT_ST_DAC_CLK1 | CPCAP_BIT_ST_DAC_CLK2;
+	const unsigned int SDAC_RESET_FREQ_MASK = SDAC_FREQ_MASK
+					| CPCAP_BIT_ST_CLOCK_TREE_RESET;
+	static unsigned int prev_stdac_data, prev_sdai_data;
+
+	if (is_stdac_changed(state, prev)) {
+		unsigned int temp_stdac_rate = state->stdac_rate;
+		struct cpcap_regacc sdai_changes = { 0 };
+		struct cpcap_regacc stdac_changes = { 0 };
+
+		int stdac_freq_config = 0;
+		if (state->rat_type == CPCAP_AUDIO_RAT_CDMA)
+			stdac_freq_config = (CPCAP_BIT_ST_DAC_CLK0
+					| CPCAP_BIT_ST_DAC_CLK1) ; /*19.2Mhz*/
+		else
+			stdac_freq_config = CPCAP_BIT_ST_DAC_CLK2 ; /* 26Mhz */
+
+		/* We need to turn off stdac before changing its settings */
+		if (prev->stdac_mode != CPCAP_AUDIO_STDAC_OFF) {
+			stdac_changes.mask = prev_stdac_data |
+					CPCAP_BIT_DF_RESET_ST_DAC |
+					CPCAP_BIT_ST_CLOCK_TREE_RESET;
+
+			logged_cpcap_write(state->cpcap, CPCAP_REG_SDAC,
+				stdac_changes.value, stdac_changes.mask);
+
+			prev_stdac_data = 0;
+			prev->stdac_mode = CPCAP_AUDIO_STDAC_OFF;
+		}
+
+		temp_stdac_rate &= 0x0000000F;
+		temp_stdac_rate = temp_stdac_rate << 4;
+
+		switch (state->stdac_mode) {
+		case CPCAP_AUDIO_STDAC_ON:
+			stdac_changes.value |= CPCAP_BIT_ST_DAC_EN;
+		/* falling through intentionally */
+		case CPCAP_AUDIO_STDAC_CLOCK_ONLY:
+			stdac_changes.value |= temp_stdac_rate |
+				CPCAP_BIT_DF_RESET_ST_DAC | stdac_freq_config;
+			sdai_changes.value |= CPCAP_BIT_ST_CLK_EN;
+			break;
+
+		case CPCAP_AUDIO_STDAC_OFF:
+		default:
+			break;
+		}
+
+		if (state->rat_type != CPCAP_AUDIO_RAT_NONE)
+			sdai_changes.value |= CPCAP_BIT_ST_DAC_CLK_IN_SEL;
+		/* begin everest change */
+		/*
+		sdai_changes.value |= CPCAP_BIT_ST_DIG_AUD_FS0 |
+			CPCAP_BIT_DIG_AUD_IN_ST_DAC | CPCAP_BIT_ST_L_TIMESLOT0;
+		*/
+		/* I2S Mode, ignore timeslots, invert bit clock */
+		sdai_changes.value |= CPCAP_BIT_ST_DIG_AUD_FS0 |
+			CPCAP_BIT_DIG_AUD_IN_ST_DAC |
+			CPCAP_BIT_ST_DIG_AUD_FS1 | CPCAP_BIT_ST_CLK_INV;
+		/* end everest change */
+
+		logged_cpcap_write(state->cpcap, CPCAP_REG_SDAC,
+				stdac_freq_config, SDAC_FREQ_MASK);
+
+		/* Next, write the SDACDI if it's changed */
+		if (prev_sdai_data != sdai_changes.value) {
+			sdai_changes.mask = sdai_changes.value
+						| prev_sdai_data;
+			prev_sdai_data = sdai_changes.value;
+
+			logged_cpcap_write(state->cpcap, CPCAP_REG_SDACDI,
+					sdai_changes.value, sdai_changes.mask);
+
+			/* Clock tree change -- reset and wait */
+			stdac_freq_config |= CPCAP_BIT_ST_CLOCK_TREE_RESET;
+
+			logged_cpcap_write(state->cpcap, CPCAP_REG_SDAC,
+				stdac_freq_config, SDAC_RESET_FREQ_MASK);
+
+			/* Wait for clock tree reset to complete */
+			mdelay(CLOCK_TREE_RESET_DELAY_MS);
+		}
+
+		/* Clear old settings */
+		stdac_changes.mask = stdac_changes.value | prev_stdac_data;
+		prev_stdac_data = stdac_changes.value;
+
+		logged_cpcap_write(state->cpcap, CPCAP_REG_SDAC,
+			stdac_changes.value, stdac_changes.mask);
+	}
+}
+
+static void cpcap_audio_configure_analog_source(
+	struct cpcap_audio_state *state,
+	struct cpcap_audio_state *prev)
+{
+	if (state->analog_source != prev->analog_source) {
+		struct cpcap_regacc ext_changes = { 0 };
+		static unsigned int prev_ext_data;
+		switch (state->analog_source) {
+		case CPCAP_AUDIO_ANALOG_SOURCE_STEREO:
+			ext_changes.value |= CPCAP_BIT_MONO_EXT0 |
+				CPCAP_BIT_PGA_IN_R_SW | CPCAP_BIT_PGA_IN_L_SW;
+			break;
+		case CPCAP_AUDIO_ANALOG_SOURCE_L:
+			ext_changes.value |= CPCAP_BIT_MONO_EXT1 |
+						CPCAP_BIT_PGA_IN_L_SW;
+			break;
+		case CPCAP_AUDIO_ANALOG_SOURCE_R:
+			ext_changes.value |= CPCAP_BIT_MONO_EXT1 |
+						CPCAP_BIT_PGA_IN_R_SW;
+			break;
+		default:
+			break;
+		}
+
+		ext_changes.mask = ext_changes.value | prev_ext_data;
+
+		prev_ext_data = ext_changes.value;
+
+		logged_cpcap_write(state->cpcap, CPCAP_REG_RXEPOA,
+				ext_changes.value, ext_changes.mask);
+	}
+}
+
+static void cpcap_audio_configure_input_gains(
+	struct cpcap_audio_state *state,
+	struct cpcap_audio_state *prev)
+{
+	if (state->input_gain != prev->input_gain) {
+		struct cpcap_regacc reg_changes = { 0 };
+		unsigned int temp_input_gain = state->input_gain & 0x0000001F;
+
+		reg_changes.value |= ((temp_input_gain << 5) | temp_input_gain);
+
+		reg_changes.mask = 0x3FF;
+
+		logged_cpcap_write(state->cpcap, CPCAP_REG_TXMP,
+				reg_changes.value, reg_changes.mask);
+	}
+}
+
+static void cpcap_audio_configure_output_gains(
+	struct cpcap_audio_state *state,
+	struct cpcap_audio_state *prev)
+{
+	if (state->output_gain != prev->output_gain) {
+		struct cpcap_regacc reg_changes = { 0 };
+		unsigned int temp_output_gain = state->output_gain & 0x0000000F;
+
+		reg_changes.value |=
+		    ((temp_output_gain << 2) | (temp_output_gain << 8) |
+		     (temp_output_gain << 12));
+
+		reg_changes.mask = 0xFF3C;
+
+		logged_cpcap_write(state->cpcap, CPCAP_REG_RXVC,
+				reg_changes.value, reg_changes.mask);
+	}
+}
+
+static void cpcap_audio_configure_output(
+	struct cpcap_audio_state *state,
+	struct cpcap_audio_state *prev)
+{
+	static unsigned int prev_aud_out_data;
+
+	bool activate_ext_loudspeaker = false;
+	struct cpcap_regacc reg_changes = { 0 };
+
+	if (!is_output_changed(prev, state) &&
+			!is_codec_changed(prev, state) &&
+			!is_stdac_changed(prev, state))
+		return;
+
+	cpcap_audio_set_output_amp_switches(state);
+
+	activate_ext_loudspeaker = cpcap_audio_set_bits_for_speaker(
+					state->codec_primary_speaker,
+					 state->codec_primary_balance,
+					 &(reg_changes.value));
+
+	activate_ext_loudspeaker = activate_ext_loudspeaker ||
+				cpcap_audio_set_bits_for_speaker(
+					state->codec_secondary_speaker,
+					 CPCAP_AUDIO_BALANCE_NEUTRAL,
+					 &(reg_changes.value));
+
+	activate_ext_loudspeaker = activate_ext_loudspeaker ||
+				cpcap_audio_set_bits_for_speaker(
+					state->stdac_primary_speaker,
+					 state->stdac_primary_balance,
+					 &(reg_changes.value));
+
+	activate_ext_loudspeaker = activate_ext_loudspeaker ||
+				cpcap_audio_set_bits_for_speaker(
+					state->stdac_secondary_speaker,
+					 CPCAP_AUDIO_BALANCE_NEUTRAL,
+					 &(reg_changes.value));
+
+	activate_ext_loudspeaker = activate_ext_loudspeaker ||
+				cpcap_audio_set_bits_for_speaker(
+					state->ext_primary_speaker,
+					 state->ext_primary_balance,
+					 &(reg_changes.value));
+
+	activate_ext_loudspeaker = activate_ext_loudspeaker ||
+				cpcap_audio_set_bits_for_speaker(
+					state->ext_secondary_speaker,
+					 CPCAP_AUDIO_BALANCE_NEUTRAL,
+					 &(reg_changes.value));
+
+	reg_changes.mask = reg_changes.value | prev_aud_out_data;
+
+	prev_aud_out_data = reg_changes.value;
+
+	/* Sleep for 300ms if we are getting into a call to allow the switch to
+	 * settle.  If we don't do this, it causes a loud pop at the beginning
+	 * of the call.
+	 */
+	if (state->rat_type == CPCAP_AUDIO_RAT_CDMA &&
+			state->ext_primary_speaker != CPCAP_AUDIO_OUT_NONE &&
+			prev->ext_primary_speaker == CPCAP_AUDIO_OUT_NONE)
+		msleep(300);
+
+	logged_cpcap_write(state->cpcap, CPCAP_REG_RXOA,
+				reg_changes.value, reg_changes.mask);
+}
+
+static inline bool codec_loopback_changed(struct cpcap_audio_state *new,
+			struct cpcap_audio_state *old)
+{
+	return (new->codec_mode != old->codec_mode) &&
+		(new->codec_mode == CPCAP_AUDIO_CODEC_LOOPBACK ||
+		 old->codec_mode == CPCAP_AUDIO_CODEC_LOOPBACK);
+}
+
+static void cpcap_audio_configure_input(struct cpcap_audio_state *state,
+			struct cpcap_audio_state *prev)
+{
+	static unsigned int prev_input_data = 0x0;
+	struct cpcap_regacc reg_changes = { 0 };
+
+	if (state->microphone == prev->microphone &&
+			!codec_loopback_changed(state, prev))
+		return;
+
+	if (state->codec_mode == CPCAP_AUDIO_CODEC_LOOPBACK)
+		reg_changes.value |= CPCAP_BIT_DLM;
+
+	if (prev->microphone == CPCAP_AUDIO_IN_HEADSET)
+		logged_cpcap_write(state->cpcap, CPCAP_REG_GPIO4,
+						0, CPCAP_BIT_GPIO4DRV);
+
+	switch (state->microphone) {
+	case CPCAP_AUDIO_IN_HANDSET:
+		pr_debug("%s: handset\n", __func__);
+		reg_changes.value |= CPCAP_BIT_MB_ON1R
+			| CPCAP_BIT_MIC1_MUX | CPCAP_BIT_MIC1_PGA_EN;
+		break;
+
+	case CPCAP_AUDIO_IN_HEADSET:
+		pr_debug("%s: headset\n", __func__);
+		reg_changes.value |= CPCAP_BIT_HS_MIC_MUX
+			| CPCAP_BIT_MIC1_PGA_EN;
+		if (state->rat_type == CPCAP_AUDIO_RAT_CDMA)
+			logged_cpcap_write(state->cpcap, CPCAP_REG_GPIO4,
+				CPCAP_BIT_GPIO4DRV, CPCAP_BIT_GPIO4DRV);
+		break;
+
+	case CPCAP_AUDIO_IN_EXT_BUS:
+		reg_changes.value |=  CPCAP_BIT_EMU_MIC_MUX
+			| CPCAP_BIT_MIC1_PGA_EN;
+		break;
+
+	case CPCAP_AUDIO_IN_AUX_INTERNAL:
+		reg_changes.value |= CPCAP_BIT_MB_ON1L
+			| CPCAP_BIT_MIC2_MUX | CPCAP_BIT_MIC2_PGA_EN;
+		break;
+
+	case CPCAP_AUDIO_IN_DUAL_INTERNAL:
+		reg_changes.value |= CPCAP_BIT_MB_ON1R
+			| CPCAP_BIT_MIC1_MUX | CPCAP_BIT_MIC1_PGA_EN
+			| CPCAP_BIT_MB_ON1L | CPCAP_BIT_MIC2_MUX
+			| CPCAP_BIT_MIC2_PGA_EN;
+		break;
+
+	case CPCAP_AUDIO_IN_DUAL_EXTERNAL:
+		reg_changes.value |= CPCAP_BIT_RX_R_ENCODE
+			| CPCAP_BIT_RX_L_ENCODE;
+		break;
+
+	case CPCAP_AUDIO_IN_BT_MONO:
+	default:
+		reg_changes.value = 0;
+		break;
+	}
+
+	reg_changes.mask = reg_changes.value | prev_input_data;
+	prev_input_data = reg_changes.value;
+
+	logged_cpcap_write(state->cpcap, CPCAP_REG_TXI,
+				reg_changes.value, reg_changes.mask);
+}
+
+static void cpcap_audio_configure_power(int power)
+{
+	static int previous_power = -1;
+
+	pr_debug("%s() called with power= %d\n", __func__, power);
+
+	if (power == previous_power)
+		return;
+
+	if (IS_ERR_OR_NULL(audio_reg)) {
+		E("audio_reg not valid for regulator setup\n");
+		return;
+	}
+
+	if (power) {
+		pr_info("%s: regulator -> enable\n", __func__);
+		regulator_enable(audio_reg);
+		regulator_set_mode(audio_reg, REGULATOR_MODE_NORMAL);
+		mdelay(SLEEP_ACTIVATE_POWER_DELAY_MS);
+	} else {
+		pr_info("%s: regulator -> standby\n", __func__);
+		regulator_set_mode(audio_reg, REGULATOR_MODE_STANDBY);
+		regulator_disable(audio_reg);
+	}
+
+	previous_power = power;
+}
+
+void cpcap_audio_register_dump(struct cpcap_audio_state *state)
+{
+	unsigned short reg_val = 0;
+
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_VAUDIOC, &reg_val);
+	printk(KERN_INFO "0x200[512] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_CC, &reg_val);
+	printk(KERN_INFO "0x201[513] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_CDI, &reg_val);
+	printk(KERN_INFO "0x202[514] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_SDAC, &reg_val);
+	printk(KERN_INFO "0x203[515] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_SDACDI, &reg_val);
+	printk(KERN_INFO "0x204[516] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_TXI, &reg_val);
+	printk(KERN_INFO "0x205[517] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_TXMP, &reg_val);
+	printk(KERN_INFO "0x206[518] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_RXOA, &reg_val);
+	printk(KERN_INFO "0x207[519] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_RXVC, &reg_val);
+	printk(KERN_INFO "0x208[520] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_RXCOA, &reg_val);
+	printk(KERN_INFO "0x209[521] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_RXSDOA, &reg_val);
+	printk(KERN_INFO "0x20A[522] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_RXEPOA, &reg_val);
+	printk(KERN_INFO "0x20B[523] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_RXLL, &reg_val);
+	printk(KERN_INFO "0x20C[524] = %x\n", reg_val);
+	cpcap_regacc_read(state->cpcap, CPCAP_REG_A2LA, &reg_val);
+	printk(KERN_INFO "0x20D[525] = %x\n", reg_val);
+}
+
+static inline bool should_power_on(struct cpcap_audio_state *state)
+{
+	if (state->codec_mode != CPCAP_AUDIO_CODEC_OFF &&
+			state->codec_mode != CPCAP_AUDIO_CODEC_CLOCK_ONLY)
+		return true;
+
+	if (state->stdac_mode != CPCAP_AUDIO_STDAC_OFF)
+		return true;
+
+	if (state->codec_primary_speaker != CPCAP_AUDIO_OUT_NONE &&
+			state->codec_primary_speaker !=
+				CPCAP_AUDIO_OUT_BT_MONO)
+		return true;
+
+	if (state->stdac_primary_speaker != CPCAP_AUDIO_OUT_NONE)
+		return true;
+
+	if (state->ext_primary_speaker != CPCAP_AUDIO_OUT_NONE)
+		return true;
+
+	if (state->microphone != CPCAP_AUDIO_IN_NONE &&
+			state->microphone != CPCAP_AUDIO_IN_BT_MONO)
+		return true;
+
+	return false;
+}
+
+void cpcap_audio_set_audio_state(struct cpcap_audio_state *state)
+{
+	bool power_on;
+	struct cpcap_audio_state *prev = &current_state;
+
+	if (state->codec_mute == CPCAP_AUDIO_CODEC_BYPASS_LOOP)
+		state->codec_mode = CPCAP_AUDIO_CODEC_ON;
+
+	if (state->codec_mode == CPCAP_AUDIO_CODEC_OFF ||
+			state->codec_mode == CPCAP_AUDIO_CODEC_CLOCK_ONLY ||
+			state->rat_type == CPCAP_AUDIO_RAT_CDMA)
+		state->codec_mute = CPCAP_AUDIO_CODEC_MUTE;
+	else
+		state->codec_mute = CPCAP_AUDIO_CODEC_UNMUTE;
+
+	if (state->stdac_mode != CPCAP_AUDIO_STDAC_ON)
+		state->stdac_mute = CPCAP_AUDIO_STDAC_MUTE;
+	else
+		state->stdac_mute = CPCAP_AUDIO_STDAC_UNMUTE;
+
+	if (state->stdac_mode == CPCAP_AUDIO_STDAC_CLOCK_ONLY)
+		state->stdac_mode = CPCAP_AUDIO_STDAC_ON;
+
+	power_on = should_power_on(state);
+
+	if (power_on)
+		cpcap_audio_configure_power(1);
+
+	if (is_speaker_turning_off(state, prev))
+		cpcap_audio_configure_output(state, prev);
+
+	if (is_codec_changed(state, prev) || is_stdac_changed(state, prev)) {
+		int codec_mute = state->codec_mute;
+		int stdac_mute = state->stdac_mute;
+
+		state->codec_mute = CPCAP_AUDIO_CODEC_MUTE;
+		state->stdac_mute = CPCAP_AUDIO_STDAC_MUTE;
+
+		cpcap_audio_configure_aud_mute(state, prev);
+
+		prev->codec_mute = state->codec_mute;
+		prev->stdac_mute = state->stdac_mute;
+
+		state->codec_mute = codec_mute;
+		state->stdac_mute = stdac_mute;
+
+		cpcap_audio_configure_codec(state, prev);
+		cpcap_audio_configure_stdac(state, prev);
+	}
+
+	cpcap_audio_configure_analog_source(state, prev);
+
+	cpcap_audio_configure_input(state, prev);
+
+	cpcap_audio_configure_input_gains(state, prev);
+
+	cpcap_audio_configure_output(state, prev);
+
+	cpcap_audio_configure_output_gains(state, prev);
+
+	cpcap_audio_configure_aud_mute(state, prev);
+
+	if (!power_on)
+		cpcap_audio_configure_power(0);
+
+	current_state = *state;
+}
+
+int cpcap_audio_init(struct cpcap_audio_state *state, const char *regulator)
+{
+	logged_cpcap_write(state->cpcap, CPCAP_REG_CC, 0, 0xFFFF);
+	logged_cpcap_write(state->cpcap, CPCAP_REG_CDI, 0, 0xBFFF);
+	logged_cpcap_write(state->cpcap, CPCAP_REG_SDAC, 0, 0xFFF);
+	logged_cpcap_write(state->cpcap, CPCAP_REG_SDACDI, 0, 0x3FFF);
+	logged_cpcap_write(state->cpcap, CPCAP_REG_TXI, 0, 0xFDF);
+	logged_cpcap_write(state->cpcap, CPCAP_REG_TXMP, 0, 0xFFF);
+	logged_cpcap_write(state->cpcap, CPCAP_REG_RXOA, 0, 0x1FF);
+	/* logged_cpcap_write(state->cpcap, CPCAP_REG_RXVC, 0, 0xFFF); */
+	logged_cpcap_write(state->cpcap, CPCAP_REG_RXCOA, 0, 0x7FF);
+	logged_cpcap_write(state->cpcap, CPCAP_REG_RXSDOA, 0, 0x1FFF);
+	logged_cpcap_write(state->cpcap, CPCAP_REG_RXEPOA, 0, 0x7FFF);
+
+	/* Use free running clock for amplifiers */
+	logged_cpcap_write(state->cpcap, CPCAP_REG_A2LA,
+		CPCAP_BIT_A2_FREE_RUN,
+		CPCAP_BIT_A2_FREE_RUN);
+
+	logged_cpcap_write(state->cpcap, CPCAP_REG_GPIO4,
+			   CPCAP_BIT_GPIO4DIR, CPCAP_BIT_GPIO4DIR);
+
+	audio_reg = regulator_get(NULL, regulator);
+
+	if (IS_ERR(audio_reg)) {
+		E("could not get regulator for audio\n");
+		return PTR_ERR(audio_reg);
+	}
+
+	return 0;
+}
+
+int cpcap_set_volume(struct cpcap_device *cpcap, unsigned volume)
+{
+	volume &= 0xF;
+	volume = volume << 12 | volume << 8;
+	return cpcap_regacc_write(cpcap, CPCAP_REG_RXVC, volume, 0xFF00);
+}
diff --git a/drivers/mfd/cpcap-audio.c b/drivers/mfd/tegra-cpcap-audio.c
similarity index 57%
rename from drivers/mfd/cpcap-audio.c
rename to drivers/mfd/tegra-cpcap-audio.c
index d335083899bf..5a78d1483575 100644
--- a/drivers/mfd/cpcap-audio.c
+++ b/drivers/mfd/tegra-cpcap-audio.c
@@ -40,58 +40,6 @@ static unsigned current_input  = -1U; /* none */
 static unsigned current_volume = CPCAP_AUDIO_OUT_VOL_MAX;
 static unsigned current_in_volume = CPCAP_AUDIO_IN_VOL_MAX;
 
-static int cpcap_audio_set(const struct cpcap_audio_path *path, bool on)
-{
-	int len, rc;
-	const struct cpcap_audio_config_table *entry;
-
-	pr_info("%s: %s %s\n", __func__, path->name, on ? "on" : "off");
-
-	if (!path) {
-		pr_info("%s: no path\n", __func__);
-		return -ENOSYS;
-	}
-
-	if (path->gpio >= 0) {
-		pr_info("%s: %s: enable gpio %d\n", __func__,
-			path->name, path->gpio);
-		rc = gpio_direction_output(path->gpio, on);
-		if (rc)
-			pr_err("%s: could not set gpio %d to %d\n", __func__,
-				path->gpio, on);
-	}
-
-	if (!on)
-		return 0;
-	if (!path->table) {
-		pr_info("%s: no config table for path %s\n", __func__,
-				path->name);
-		return -ENOSYS;
-	}
-
-	entry = path->table;
-	len = path->table_len;
-	while (len--) {
-		u16 val = entry->val | (pdata->master ? 0 : entry->slave_or);
-		int rc = cpcap_regacc_write(cpcap,
-				entry->reg,
-				val,
-				entry->mask);
-		if (rc) {
-			pr_err("%s: cpcap_regacc_write %d %x/%x %x failed: %d\n",
-				__func__,
-				entry->reg,
-				entry->val,
-				entry->slave_or,
-				entry->mask, rc);
-			rc = -EIO;
-		}
-		entry++;
-	}
-
-	return 0;
-}
-
 static int cpcap_set_volume(struct cpcap_device *cpcap, unsigned volume)
 {
 	pr_info("%s\n", __func__);
@@ -126,7 +74,7 @@ static long cpcap_audio_ctl_ioctl(struct file *file, unsigned int cmd,
 			unsigned long arg)
 {
 	int rc = 0;
-	struct cpcap_audio_output out;
+	struct cpcap_audio_stream in, out;
 
 	mutex_lock(&cpcap_lock);
 
@@ -145,16 +93,62 @@ static long cpcap_audio_ctl_ioctl(struct file *file, unsigned int cmd,
 		}
 		switch (out.id) {
 		case CPCAP_AUDIO_OUT_SPEAKER:
-			pr_info("%s: setting output path to %s\n", __func__,
-					pdata->speaker->name);
-			cpcap_audio_set(pdata->headset, 0);
-			cpcap_audio_set(pdata->speaker, out.on);
+			pr_info("%s: setting output path to speaker\n",
+					__func__);
+			pdata->state->stdac_primary_speaker =
+					CPCAP_AUDIO_OUT_NONE;
+			cpcap_audio_set_audio_state(pdata->state);
+			if (!out.on) {
+				if (pdata->speaker_gpio >= 0)
+					gpio_direction_output(
+						pdata->speaker_gpio, 0);
+				break;
+			}
+
+			pr_info("%s: enable speaker\n", __func__);
+
+			pdata->state->stdac_primary_speaker =
+					CPCAP_AUDIO_OUT_LOUDSPEAKER;
+			gpio_direction_output(pdata->headset_gpio, 0);
+			gpio_direction_output(pdata->speaker_gpio, 1);
 			break;
 		case CPCAP_AUDIO_OUT_HEADSET:
-			pr_info("%s: setting output path to %s\n", __func__,
-					pdata->headset->name);
-			cpcap_audio_set(pdata->speaker, 0);
-			cpcap_audio_set(pdata->headset, out.on);
+			pr_info("%s: setting output path to headset\n",
+					__func__);
+			pdata->state->stdac_primary_speaker =
+					CPCAP_AUDIO_OUT_NONE;
+			cpcap_audio_set_audio_state(pdata->state);
+			if (!out.on) {
+				if (pdata->headset_gpio >= 0)
+					gpio_direction_output(
+						pdata->headset_gpio, 0);
+				break;
+			}
+			pdata->state->stdac_primary_speaker =
+					CPCAP_AUDIO_OUT_STEREO_HEADSET;
+			cpcap_audio_set_audio_state(pdata->state);
+			gpio_direction_output(pdata->speaker_gpio, 0);
+			gpio_direction_output(pdata->headset_gpio, 1);
+			break;
+		case CPCAP_AUDIO_OUT_HEADSET_AND_SPEAKER:
+			pr_info("%s: setting output path to "
+					"headset + speaker\n", __func__);
+			pdata->state->stdac_primary_speaker =
+					CPCAP_AUDIO_OUT_NONE;
+			cpcap_audio_set_audio_state(pdata->state);
+			if (!out.on) {
+				if (pdata->headset_gpio >= 0)
+					gpio_direction_output(
+						pdata->headset_gpio, 0);
+					gpio_direction_output(
+						pdata->speaker_gpio, 0);
+				break;
+			}
+			pdata->state->stdac_primary_speaker =
+					CPCAP_AUDIO_OUT_STEREO_HEADSET;
+			cpcap_audio_set_audio_state(pdata->state);
+			gpio_direction_output(pdata->speaker_gpio, 1);
+			gpio_direction_output(pdata->headset_gpio, 1);
 			break;
 		}
 		current_output = out.id;
@@ -165,29 +159,50 @@ static long cpcap_audio_ctl_ioctl(struct file *file, unsigned int cmd,
 			rc = -EFAULT;
 		break;
 	case CPCAP_AUDIO_IN_SET_INPUT:
-		if (arg > CPCAP_AUDIO_IN_MAX && arg != -1UL) {
-			pr_err("%s: invalid audio input selector %ld\n",
-				__func__, arg);
+		if (copy_from_user(&in, (const void __user *)arg,
+				sizeof(in))) {
+			rc = -EFAULT;
+			goto done;
+		}
+
+		if (in.id > CPCAP_AUDIO_IN_MAX) {
+			pr_err("%s: invalid audio input selector %d\n",
+				__func__, in.id);
 			rc = -EINVAL;
 			goto done;
 		}
-		switch (arg) {
-		case -1UL:
-			pr_info("%s: turning off input path\n", __func__);
-			cpcap_audio_set(pdata->mic1, 0);
-			cpcap_audio_set(pdata->mic2, 0);
+
+		pr_info("%s: muting current input before switch\n", __func__);
+
+		pdata->state->microphone = CPCAP_AUDIO_IN_NONE;
+		pdata->state->codec_mute = CPCAP_AUDIO_CODEC_MUTE;
+		pdata->state->stdac_mute = CPCAP_AUDIO_STDAC_MUTE;
+		pdata->state->codec_mode = CPCAP_AUDIO_CODEC_OFF;
+		pdata->state->stdac_mode = CPCAP_AUDIO_STDAC_OFF;
+		cpcap_audio_set_audio_state(pdata->state);
+
+		if (!in.on)
 			break;
+
+		pdata->state->codec_mute = CPCAP_AUDIO_CODEC_UNMUTE;
+		pdata->state->stdac_mute = CPCAP_AUDIO_STDAC_UNMUTE;
+		pdata->state->codec_mode = CPCAP_AUDIO_CODEC_ON;
+		pdata->state->stdac_mode = CPCAP_AUDIO_STDAC_ON;
+
+		switch (in.id) {
 		case CPCAP_AUDIO_IN_MIC1:
-			pr_info("%s: setting input path to %s\n", __func__,
-					pdata->mic1->name);
-			cpcap_audio_set(pdata->mic2, 0);
-			cpcap_audio_set(pdata->mic1, 1);
+			pr_info("%s: setting input path to on-board mic\n",
+					__func__);
+			pdata->state->microphone = CPCAP_AUDIO_IN_HANDSET;
+			cpcap_audio_set_audio_state(pdata->state);
+			cpcap_audio_register_dump(pdata->state);
 			break;
 		case CPCAP_AUDIO_IN_MIC2:
-			pr_info("%s: setting input path to %s\n", __func__,
-					pdata->mic2->name);
-			cpcap_audio_set(pdata->mic1, 0);
-			cpcap_audio_set(pdata->mic2, 1);
+			pr_info("%s: setting input path to headset mic\n",
+					__func__);
+			pdata->state->microphone = CPCAP_AUDIO_IN_HEADSET;
+			cpcap_audio_set_audio_state(pdata->state);
+			cpcap_audio_register_dump(pdata->state);
 			break;
 		}
 		current_input = arg;
@@ -263,7 +278,6 @@ static struct miscdevice cpcap_audio_ctl = {
 static int cpcap_audio_probe(struct platform_device *pdev)
 {
 	int rc;
-	struct regulator *audio_reg;
 
 	pr_info("%s\n", __func__);
 
@@ -273,44 +287,34 @@ static int cpcap_audio_probe(struct platform_device *pdev)
 	pdata = pdev->dev.platform_data;
 	BUG_ON(!pdata);
 
-	audio_reg = regulator_get(NULL, "vaudio");
-	if (IS_ERR(audio_reg)) {
-		rc = PTR_ERR(audio_reg);
-		pr_err("%s: could not get vaudio regulator: %d\n", __func__,
-			rc);
-		return rc;
-	}
-
-	rc = regulator_enable(audio_reg);
-	if (rc) {
-		pr_err("%s: failed to enable vaudio regulator: %d\n", __func__,
-			rc);
-		goto fail;
-	}
-
-	if (pdata->speaker->gpio >= 0) {
-		tegra_gpio_enable(pdata->speaker->gpio);
-		rc = gpio_request(pdata->speaker->gpio, pdata->speaker->name);
+	if (pdata->speaker_gpio >= 0) {
+		tegra_gpio_enable(pdata->speaker_gpio);
+		rc = gpio_request(pdata->speaker_gpio, "speaker");
 		if (rc) {
 			pr_err("%s: could not get speaker GPIO %d: %d\n",
-				__func__, pdata->speaker->gpio, rc);
+				__func__, pdata->speaker_gpio, rc);
 			goto fail1;
 		}
 	}
 
-	if (pdata->headset->gpio >= 0) {
-		tegra_gpio_enable(pdata->headset->gpio);
-		rc = gpio_request(pdata->headset->gpio, pdata->headset->name);
+	if (pdata->headset_gpio >= 0) {
+		tegra_gpio_enable(pdata->headset_gpio);
+		rc = gpio_request(pdata->headset_gpio, "headset");
 		if (rc) {
 			pr_err("%s: could not get headset GPIO %d: %d\n",
-				__func__, pdata->headset->gpio, rc);
+				__func__, pdata->headset_gpio, rc);
 			goto fail2;
 		}
 	}
 
-	cpcap_audio_set(pdata->speaker, 1);
-	cpcap_set_volume(cpcap, current_volume);
-	cpcap_set_mic_volume(cpcap, current_in_volume);
+	pdata->state->cpcap = cpcap;
+	if (cpcap_audio_init(pdata->state, pdata->regulator))
+		goto fail3;
+	cpcap_audio_register_dump(pdata->state);
+
+	pdata->state->stdac_mode = CPCAP_AUDIO_STDAC_ON;
+	cpcap_audio_set_audio_state(pdata->state);
+	cpcap_audio_register_dump(pdata->state);
 
 	rc = misc_register(&cpcap_audio_ctl);
 	if (rc < 0) {
@@ -322,15 +326,16 @@ static int cpcap_audio_probe(struct platform_device *pdev)
 	return rc;
 
 fail3:
-	if (pdata->headset->gpio >= 0)
-		gpio_free(pdata->headset->gpio);
+	if (pdata->headset_gpio >= 0)
+		gpio_free(pdata->headset_gpio);
 fail2:
-	if (pdata->speaker->gpio >= 0)
-		gpio_free(pdata->speaker->gpio);
+	if (pdata->headset_gpio >= 0)
+		tegra_gpio_disable(pdata->headset_gpio);
+	if (pdata->speaker_gpio >= 0)
+		gpio_free(pdata->speaker_gpio);
 fail1:
-	regulator_disable(audio_reg);
-fail:
-	regulator_put(audio_reg);
+	if (pdata->speaker_gpio >= 0)
+		tegra_gpio_disable(pdata->speaker_gpio);
 	return rc;
 }
 
@@ -342,10 +347,10 @@ static struct platform_driver cpcap_audio_driver = {
 	},
 };
 
-static int __init cpcap_audio_init(void)
+static int __init tegra_cpcap_audio_init(void)
 {
 	return cpcap_driver_register(&cpcap_audio_driver);
 }
 
-module_init(cpcap_audio_init);
+module_init(tegra_cpcap_audio_init);
 MODULE_LICENSE("GPL");
diff --git a/include/linux/cpcap_audio.h b/include/linux/cpcap_audio.h
index 50097231b276..3d3766e03858 100644
--- a/include/linux/cpcap_audio.h
+++ b/include/linux/cpcap_audio.h
@@ -23,17 +23,18 @@
 
 #define CPCAP_AUDIO_MAGIC 'c'
 
-#define CPCAP_AUDIO_OUT_SPEAKER		0
-#define CPCAP_AUDIO_OUT_HEADSET		1
-#define CPCAP_AUDIO_OUT_MAX		1
-
-struct cpcap_audio_output {
-	int id; /* e.g., CPCAP_AUDIO_OUT_SPEAKER */
-	int on;
+#define CPCAP_AUDIO_OUT_SPEAKER			0
+#define CPCAP_AUDIO_OUT_HEADSET			1
+#define CPCAP_AUDIO_OUT_HEADSET_AND_SPEAKER	2
+#define CPCAP_AUDIO_OUT_MAX			2
+
+struct cpcap_audio_stream {
+	unsigned id; /* e.g., CPCAP_AUDIO_OUT_SPEAKER or CPCAP_AUDIO_IN_MIC1 */
+	int on; /* enable/disable for output, unmute/mute for input */
 };
 
 #define CPCAP_AUDIO_OUT_SET_OUTPUT _IOW(CPCAP_AUDIO_MAGIC, 0, \
-			struct cpcap_audio_output *)
+			const struct cpcap_audio_stream *)
 
 #define CPCAP_AUDIO_OUT_VOL_MIN 0
 #define CPCAP_AUDIO_OUT_VOL_MAX 15
@@ -49,9 +50,11 @@ struct cpcap_audio_output {
 #define CPCAP_AUDIO_IN_MIC2		1
 #define CPCAP_AUDIO_IN_MAX		1
 
-#define CPCAP_AUDIO_IN_SET_INPUT   _IOW(CPCAP_AUDIO_MAGIC, 4, unsigned int)
+#define CPCAP_AUDIO_IN_SET_INPUT   _IOW(CPCAP_AUDIO_MAGIC, 4, \
+			const struct cpcap_audio_stream *)
 
-#define CPCAP_AUDIO_IN_GET_INPUT   _IOR(CPCAP_AUDIO_MAGIC, 5, unsigned int *)
+#define CPCAP_AUDIO_IN_GET_INPUT   _IOR(CPCAP_AUDIO_MAGIC, 5, \
+			struct cpcap_audio_stream *)
 
 #define CPCAP_AUDIO_IN_VOL_MIN 0
 #define CPCAP_AUDIO_IN_VOL_MAX 31
-- 
2.34.1