From 79e9ed9b2d09a06d0ff96485f1f800cdc1699828 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev 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 @@ -23,27 +24,216 @@ #include #include #include +#include +#include -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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, ®_val); + printk(KERN_INFO "0x200[512] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_CC, ®_val); + printk(KERN_INFO "0x201[513] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_CDI, ®_val); + printk(KERN_INFO "0x202[514] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_SDAC, ®_val); + printk(KERN_INFO "0x203[515] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_SDACDI, ®_val); + printk(KERN_INFO "0x204[516] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_TXI, ®_val); + printk(KERN_INFO "0x205[517] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_TXMP, ®_val); + printk(KERN_INFO "0x206[518] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_RXOA, ®_val); + printk(KERN_INFO "0x207[519] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_RXVC, ®_val); + printk(KERN_INFO "0x208[520] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_RXCOA, ®_val); + printk(KERN_INFO "0x209[521] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_RXSDOA, ®_val); + printk(KERN_INFO "0x20A[522] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_RXEPOA, ®_val); + printk(KERN_INFO "0x20B[523] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_RXLL, ®_val); + printk(KERN_INFO "0x20C[524] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_A2LA, ®_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 = ¤t_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