[ARM] tegra: stingray: cpcap-audio: integrate Motorola's driver
authorIliyan Malchev <malchev@google.com>
Fri, 3 Sep 2010 17:12:10 +0000 (10:12 -0700)
committerColin Cross <ccross@android.com>
Wed, 6 Oct 2010 23:50:52 +0000 (16:50 -0700)
This reverts commit 02e784126d67e77c039123fed3f2ece48b6559f9.

arch/arm/mach-tegra/board-stingray.c
arch/arm/mach-tegra/include/mach/cpcap_audio.h
drivers/mfd/Makefile
drivers/mfd/cpcap-audio-core.c [new file with mode: 0644]
drivers/mfd/cpcap-audio.c [deleted file]
drivers/mfd/tegra-cpcap-audio.c [new file with mode: 0644]
include/linux/cpcap_audio.h

index 8c501fef27733f491aeaf30398587a7f76faaf17..e3df31d80653daaba02acdbfbab711e6849a2484 100644 (file)
@@ -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 = {
index 1f6804dbaba7eb880a887db3de69f1d4facf7326..85f740be57b7a29bc0ff3485586040a1bbaab2a6 100644 (file)
@@ -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>
 #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_*/
index ece21b1a98a2b4dfbc7e90971be81434600f85e3..a28fc814aea6832796205ba52147e3a602eee29e 100644 (file)
@@ -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 (file)
index 0000000..8c725a0
--- /dev/null
@@ -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/cpcap-audio.c
deleted file mode 100644 (file)
index d335083..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-/* drivers/mfd/cpcap-audio.c
- *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- *      Iliyan Malchev <malchev@google.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/spi/cpcap-regbits.h>
-#include <linux/spi/cpcap.h>
-#include <linux/regulator/consumer.h>
-#include <linux/cpcap_audio.h>
-#include <linux/spi/cpcap.h>
-#include <linux/mutex.h>
-#include <linux/fs.h>
-#include <linux/err.h>
-#include <linux/gpio.h>
-#include <linux/miscdevice.h>
-#include <linux/cpcap_audio.h>
-#include <linux/uaccess.h>
-
-#include <mach/cpcap_audio.h>
-
-static struct cpcap_device *cpcap;
-static struct cpcap_audio_platform_data *pdata;
-static unsigned current_output = CPCAP_AUDIO_OUT_SPEAKER;
-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__);
-       volume &= 0xF;
-       volume = volume << 12 | volume << 8;
-       return cpcap_regacc_write(cpcap, CPCAP_REG_RXVC, volume, 0xFF00);
-}
-
-
-static int cpcap_set_mic_volume(struct cpcap_device *cpcap, unsigned volume)
-{
-       pr_info("%s\n", __func__);
-       volume &= 0x1F;
-       /* set the same volume for mic1 and mic2 */
-       volume = volume << 5 | volume;
-       return cpcap_regacc_write(cpcap, CPCAP_REG_TXMP, volume, 0x3FF);
-}
-
-static int cpcap_audio_ctl_open(struct inode *inode, struct file *file)
-{
-       return 0;
-}
-
-static int cpcap_audio_ctl_release(struct inode *inode, struct file *file)
-{
-       return 0;
-}
-
-static DEFINE_MUTEX(cpcap_lock);
-
-static long cpcap_audio_ctl_ioctl(struct file *file, unsigned int cmd,
-                       unsigned long arg)
-{
-       int rc = 0;
-       struct cpcap_audio_output out;
-
-       mutex_lock(&cpcap_lock);
-
-       switch (cmd) {
-       case CPCAP_AUDIO_OUT_SET_OUTPUT:
-               if (copy_from_user(&out, (const void __user *)arg,
-                               sizeof(out))) {
-                       rc = -EFAULT;
-                       goto done;
-               }
-               if (out.id > CPCAP_AUDIO_OUT_MAX) {
-                       pr_err("%s: invalid audio-output selector %d\n",
-                               __func__, out.id);
-                       rc = -EINVAL;
-                       goto done;
-               }
-               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);
-                       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);
-                       break;
-               }
-               current_output = out.id;
-               break;
-       case CPCAP_AUDIO_OUT_GET_OUTPUT:
-               if (copy_to_user((void __user *)arg, &current_output,
-                                       sizeof(unsigned int)))
-                       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);
-                       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);
-                       break;
-               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);
-                       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);
-                       break;
-               }
-               current_input = arg;
-               break;
-       case CPCAP_AUDIO_IN_GET_INPUT:
-               if (copy_to_user((void __user *)arg, &current_input,
-                                       sizeof(unsigned int)))
-                       rc = -EFAULT;
-               break;
-       case CPCAP_AUDIO_OUT_SET_VOLUME:
-               if (arg > CPCAP_AUDIO_OUT_VOL_MAX) {
-                       pr_err("%s: invalid audio volume %ld\n",
-                               __func__, arg);
-                       rc = -EINVAL;
-                       goto done;
-               }
-               rc = cpcap_set_volume(cpcap, (unsigned)arg);
-               if (rc < 0) {
-                       pr_err("%s: could not set audio volume to %ld: %d\n",
-                               __func__, arg, rc);
-                       goto done;
-               }
-               current_volume = arg;
-               break;
-       case CPCAP_AUDIO_IN_SET_VOLUME:
-               if (arg > CPCAP_AUDIO_IN_VOL_MAX) {
-                       pr_err("%s: invalid audio-input volume %ld\n",
-                               __func__, arg);
-                       rc = -EINVAL;
-                       goto done;
-               }
-               rc = cpcap_set_mic_volume(cpcap, (unsigned)arg);
-               if (rc < 0) {
-                       pr_err("%s: could not set audio-input"\
-                               " volume to %ld: %d\n", __func__, arg, rc);
-                       goto done;
-               }
-               current_in_volume = arg;
-               break;
-       case CPCAP_AUDIO_OUT_GET_VOLUME:
-               if (copy_to_user((void __user *)arg, &current_volume,
-                                       sizeof(unsigned int))) {
-                       rc = -EFAULT;
-                       goto done;
-               }
-               break;
-       case CPCAP_AUDIO_IN_GET_VOLUME:
-               if (copy_to_user((void __user *)arg, &current_in_volume,
-                                       sizeof(unsigned int))) {
-                       rc = -EFAULT;
-                       goto done;
-               }
-               break;
-       }
-
-done:
-       mutex_unlock(&cpcap_lock);
-       return rc;
-}
-
-static const struct file_operations cpcap_audio_ctl_fops = {
-       .open = cpcap_audio_ctl_open,
-       .release = cpcap_audio_ctl_release,
-       .unlocked_ioctl = cpcap_audio_ctl_ioctl,
-};
-
-static struct miscdevice cpcap_audio_ctl = {
-       .name = "audio_ctl",
-       .minor = MISC_DYNAMIC_MINOR,
-       .fops = &cpcap_audio_ctl_fops,
-};
-
-static int cpcap_audio_probe(struct platform_device *pdev)
-{
-       int rc;
-       struct regulator *audio_reg;
-
-       pr_info("%s\n", __func__);
-
-       cpcap = platform_get_drvdata(pdev);
-       BUG_ON(!cpcap);
-
-       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 (rc) {
-                       pr_err("%s: could not get speaker GPIO %d: %d\n",
-                               __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 (rc) {
-                       pr_err("%s: could not get headset GPIO %d: %d\n",
-                               __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);
-
-       rc = misc_register(&cpcap_audio_ctl);
-       if (rc < 0) {
-               pr_err("%s: failed to register misc device: %d\n", __func__,
-                               rc);
-               goto fail3;
-       }
-
-       return rc;
-
-fail3:
-       if (pdata->headset->gpio >= 0)
-               gpio_free(pdata->headset->gpio);
-fail2:
-       if (pdata->speaker->gpio >= 0)
-               gpio_free(pdata->speaker->gpio);
-fail1:
-       regulator_disable(audio_reg);
-fail:
-       regulator_put(audio_reg);
-       return rc;
-}
-
-static struct platform_driver cpcap_audio_driver = {
-       .probe = cpcap_audio_probe,
-       .driver = {
-               .name = "cpcap_audio",
-               .owner = THIS_MODULE,
-       },
-};
-
-static int __init cpcap_audio_init(void)
-{
-       return cpcap_driver_register(&cpcap_audio_driver);
-}
-
-module_init(cpcap_audio_init);
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tegra-cpcap-audio.c b/drivers/mfd/tegra-cpcap-audio.c
new file mode 100644 (file)
index 0000000..5a78d14
--- /dev/null
@@ -0,0 +1,356 @@
+/* drivers/mfd/cpcap-audio.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *      Iliyan Malchev <malchev@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/spi/cpcap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/cpcap_audio.h>
+#include <linux/spi/cpcap.h>
+#include <linux/mutex.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/cpcap_audio.h>
+#include <linux/uaccess.h>
+
+#include <mach/cpcap_audio.h>
+
+static struct cpcap_device *cpcap;
+static struct cpcap_audio_platform_data *pdata;
+static unsigned current_output = CPCAP_AUDIO_OUT_SPEAKER;
+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_set_volume(struct cpcap_device *cpcap, unsigned volume)
+{
+       pr_info("%s\n", __func__);
+       volume &= 0xF;
+       volume = volume << 12 | volume << 8;
+       return cpcap_regacc_write(cpcap, CPCAP_REG_RXVC, volume, 0xFF00);
+}
+
+
+static int cpcap_set_mic_volume(struct cpcap_device *cpcap, unsigned volume)
+{
+       pr_info("%s\n", __func__);
+       volume &= 0x1F;
+       /* set the same volume for mic1 and mic2 */
+       volume = volume << 5 | volume;
+       return cpcap_regacc_write(cpcap, CPCAP_REG_TXMP, volume, 0x3FF);
+}
+
+static int cpcap_audio_ctl_open(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static int cpcap_audio_ctl_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static DEFINE_MUTEX(cpcap_lock);
+
+static long cpcap_audio_ctl_ioctl(struct file *file, unsigned int cmd,
+                       unsigned long arg)
+{
+       int rc = 0;
+       struct cpcap_audio_stream in, out;
+
+       mutex_lock(&cpcap_lock);
+
+       switch (cmd) {
+       case CPCAP_AUDIO_OUT_SET_OUTPUT:
+               if (copy_from_user(&out, (const void __user *)arg,
+                               sizeof(out))) {
+                       rc = -EFAULT;
+                       goto done;
+               }
+               if (out.id > CPCAP_AUDIO_OUT_MAX) {
+                       pr_err("%s: invalid audio-output selector %d\n",
+                               __func__, out.id);
+                       rc = -EINVAL;
+                       goto done;
+               }
+               switch (out.id) {
+               case CPCAP_AUDIO_OUT_SPEAKER:
+                       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 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;
+               break;
+       case CPCAP_AUDIO_OUT_GET_OUTPUT:
+               if (copy_to_user((void __user *)arg, &current_output,
+                                       sizeof(unsigned int)))
+                       rc = -EFAULT;
+               break;
+       case CPCAP_AUDIO_IN_SET_INPUT:
+               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;
+               }
+
+               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 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 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;
+               break;
+       case CPCAP_AUDIO_IN_GET_INPUT:
+               if (copy_to_user((void __user *)arg, &current_input,
+                                       sizeof(unsigned int)))
+                       rc = -EFAULT;
+               break;
+       case CPCAP_AUDIO_OUT_SET_VOLUME:
+               if (arg > CPCAP_AUDIO_OUT_VOL_MAX) {
+                       pr_err("%s: invalid audio volume %ld\n",
+                               __func__, arg);
+                       rc = -EINVAL;
+                       goto done;
+               }
+               rc = cpcap_set_volume(cpcap, (unsigned)arg);
+               if (rc < 0) {
+                       pr_err("%s: could not set audio volume to %ld: %d\n",
+                               __func__, arg, rc);
+                       goto done;
+               }
+               current_volume = arg;
+               break;
+       case CPCAP_AUDIO_IN_SET_VOLUME:
+               if (arg > CPCAP_AUDIO_IN_VOL_MAX) {
+                       pr_err("%s: invalid audio-input volume %ld\n",
+                               __func__, arg);
+                       rc = -EINVAL;
+                       goto done;
+               }
+               rc = cpcap_set_mic_volume(cpcap, (unsigned)arg);
+               if (rc < 0) {
+                       pr_err("%s: could not set audio-input"\
+                               " volume to %ld: %d\n", __func__, arg, rc);
+                       goto done;
+               }
+               current_in_volume = arg;
+               break;
+       case CPCAP_AUDIO_OUT_GET_VOLUME:
+               if (copy_to_user((void __user *)arg, &current_volume,
+                                       sizeof(unsigned int))) {
+                       rc = -EFAULT;
+                       goto done;
+               }
+               break;
+       case CPCAP_AUDIO_IN_GET_VOLUME:
+               if (copy_to_user((void __user *)arg, &current_in_volume,
+                                       sizeof(unsigned int))) {
+                       rc = -EFAULT;
+                       goto done;
+               }
+               break;
+       }
+
+done:
+       mutex_unlock(&cpcap_lock);
+       return rc;
+}
+
+static const struct file_operations cpcap_audio_ctl_fops = {
+       .open = cpcap_audio_ctl_open,
+       .release = cpcap_audio_ctl_release,
+       .unlocked_ioctl = cpcap_audio_ctl_ioctl,
+};
+
+static struct miscdevice cpcap_audio_ctl = {
+       .name = "audio_ctl",
+       .minor = MISC_DYNAMIC_MINOR,
+       .fops = &cpcap_audio_ctl_fops,
+};
+
+static int cpcap_audio_probe(struct platform_device *pdev)
+{
+       int rc;
+
+       pr_info("%s\n", __func__);
+
+       cpcap = platform_get_drvdata(pdev);
+       BUG_ON(!cpcap);
+
+       pdata = pdev->dev.platform_data;
+       BUG_ON(!pdata);
+
+       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);
+                       goto fail1;
+               }
+       }
+
+       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);
+                       goto fail2;
+               }
+       }
+
+       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) {
+               pr_err("%s: failed to register misc device: %d\n", __func__,
+                               rc);
+               goto fail3;
+       }
+
+       return rc;
+
+fail3:
+       if (pdata->headset_gpio >= 0)
+               gpio_free(pdata->headset_gpio);
+fail2:
+       if (pdata->headset_gpio >= 0)
+               tegra_gpio_disable(pdata->headset_gpio);
+       if (pdata->speaker_gpio >= 0)
+               gpio_free(pdata->speaker_gpio);
+fail1:
+       if (pdata->speaker_gpio >= 0)
+               tegra_gpio_disable(pdata->speaker_gpio);
+       return rc;
+}
+
+static struct platform_driver cpcap_audio_driver = {
+       .probe = cpcap_audio_probe,
+       .driver = {
+               .name = "cpcap_audio",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init tegra_cpcap_audio_init(void)
+{
+       return cpcap_driver_register(&cpcap_audio_driver);
+}
+
+module_init(tegra_cpcap_audio_init);
+MODULE_LICENSE("GPL");
index 50097231b276df7df35dea857d8ce21b4689322e..3d3766e03858e2bd466ccb4a16d20eb0246ebd2a 100644 (file)
 
 #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