diff options
Diffstat (limited to 'sound/soc/codecs/ab3550.c')
-rwxr-xr-x | sound/soc/codecs/ab3550.c | 1652 |
1 files changed, 1046 insertions, 606 deletions
diff --git a/sound/soc/codecs/ab3550.c b/sound/soc/codecs/ab3550.c index 5ee08672792..de51ca963c2 100755 --- a/sound/soc/codecs/ab3550.c +++ b/sound/soc/codecs/ab3550.c @@ -1,7 +1,9 @@ /* * Copyright (C) ST-Ericsson SA 2010 * - * Author: Xie Xiaolei <xie.xiaolei@etericsson.com> + * Author: Xie Xiaolei <xie.xiaolei@etericsson.com>, + * Ola Lilja ola.o.lilja@stericsson.com, + * Roger Nilsson roger.xr.nilsson@stericsson.com * for ST-Ericsson. * * License terms: @@ -25,34 +27,102 @@ #include <sound/soc-dapm.h> #include <linux/mfd/abx500.h> #include <linux/bitops.h> +#include <asm/atomic.h> #include "ab3550.h" -#include "../u8500/u8500_pcm.h" -#include "../u8500/u8500_msp_dai.h" #define I2C_BANK 0 static struct device *ab3550_dev = NULL; +// Registers ----------------------------------------------------------------------------------------------------------------------- + +struct ab3550_register { + const char* name; + u8 address; +}; + +struct ab3550_register ab3550_registers[] = { + {"IDX_MIC_BIAS1", 0x31}, + {"IDX_MIC_BIAS2", 0x32}, + {"IDX_MIC_BIAS2_VAD", 0x33}, + {"IDX_MIC1_GAIN", 0x34}, + {"IDX_MIC2_GAIN", 0x35}, + {"IDX_MIC1_INPUT_SELECT", 0x36}, + {"IDX_MIC2_INPUT_SELECT", 0x37}, + {"IDX_MIC1_VMID_SELECT", 0x38}, + {"IDX_MIC2_VMID_SELECT", 0x39}, + {"IDX_MIC2_TO_MIC1", 0x3A}, + {"IDX_ANALOG_LOOP_PGA1", 0x3B}, + {"IDX_ANALOG_LOOP_PGA2", 0x3C}, + {"IDX_APGA_VMID_SELECT", 0x3D}, + {"IDX_EAR", 0x3E}, + {"IDX_AUXO1", 0x3F}, + {"IDX_AUXO2", 0x40}, + {"IDX_AUXO_PWR_MODE", 0x41}, + {"IDX_OFFSET_CANCEL", 0x42}, + {"IDX_SPKR", 0x43}, + {"IDX_LINE1", 0x44}, + {"IDX_LINE2", 0x45}, + {"IDX_APGA1_ADDER", 0x46}, + {"IDX_APGA2_ADDER", 0x47}, + {"IDX_EAR_ADDER", 0x48}, + {"IDX_AUXO1_ADDER", 0x49}, + {"IDX_AUXO2_ADDER", 0x4A}, + {"IDX_SPKR_ADDER", 0x4B}, + {"IDX_LINE1_ADDER", 0x4C}, + {"IDX_LINE2_ADDER", 0x4D}, + {"IDX_EAR_TO_MIC2", 0x4E}, + {"IDX_SPKR_TO_MIC2", 0x4F}, + {"IDX_NEGATIVE_CHARGE_PUMP", 0x50}, + {"IDX_TX1", 0x51}, + {"IDX_TX2", 0x52}, + {"IDX_RX1", 0x53}, + {"IDX_RX2", 0x54}, + {"IDX_RX3", 0x55}, + {"IDX_TX_DIGITAL_PGA1", 0X56}, + {"IDX_TX_DIGITAL_PGA2", 0X57}, + {"IDX_RX1_DIGITAL_PGA", 0x58}, + {"IDX_RX2_DIGITAL_PGA", 0x59}, + {"IDX_RX3_DIGITAL_PGA", 0x5A}, + {"IDX_SIDETONE1_PGA", 0x5B}, + {"IDX_SIDETONE2_PGA", 0x5C}, + {"IDX_CLOCK", 0x5D}, + {"IDX_INTERFACE0", 0x5E}, + {"IDX_INTERFACE1", 0x60}, + {"IDX_INTERFACE0_DATA", 0x5F}, + {"IDX_INTERFACE1_DATA", 0x61}, + {"IDX_INTERFACE_LOOP", 0x62}, + {"IDX_INTERFACE_SWAP", 0x63} +}; + #define SET_REG(reg, val) abx500_set_register_interruptible( \ - ab3550_dev, I2C_BANK, (reg), (val)) + ab3550_dev, I2C_BANK, (reg), (val)); \ + printk(KERN_DEBUG "REG = 0x%02x, VAL = 0x%02x.\n", (reg), (val)) -#define MASK_SET_REG(reg, mask, val) \ - abx500_mask_and_set_register_interruptible( \ - ab3550_dev, I2C_BANK, (reg), (mask), (val)) +#define MASK_SET_REG(reg, mask, val) abx500_mask_and_set_register_interruptible( \ + ab3550_dev, I2C_BANK, (reg), (mask), (val)); \ + printk(KERN_DEBUG "REG = 0x%02x, MASK = 0x%02x, VAL = 0x%02x.\n", (reg), (mask), (val)) #define GET_REG(reg, val) abx500_get_register_interruptible( \ ab3550_dev, I2C_BANK, (reg), (val)) -#define AB3550_SUPPORTED_RATE (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +static enum enum_register outamp_indices[] = { + IDX_LINE1, IDX_LINE2, IDX_SPKR, IDX_EAR, IDX_AUXO1, IDX_AUXO2 +}; -#define AB3550_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S24_LE) +static enum enum_control outamp_gain_controls[] = { + IDX_LINE1_Gain, IDX_LINE2_Gain, IDX_SPKR_Gain, IDX_EAR_Gain, IDX_AUXO1_Gain, IDX_AUXO2_Gain +}; -static const u8 outamp_reg[] = { - LINE1, LINE2, SPKR, EAR, AUXO1, AUXO2 +static enum enum_register outamp_adder_indices[] = { + IDX_LINE1_ADDER, IDX_LINE2_ADDER, IDX_SPKR_ADDER, IDX_EAR_ADDER, IDX_AUXO1_ADDER, IDX_AUXO2_ADDER }; + +//static const char* outamp_name[] = { +// "LINE1", "LINE2", "SPKR", "EAR", "AUXO1", "AUXO2" +//}; + static const u8 outamp_gain_shift[] = { LINEx_GAIN_SHIFT, LINEx_GAIN_SHIFT, SPKR_GAIN_SHIFT, EAR_GAIN_SHIFT, @@ -74,402 +144,268 @@ static const u8 outamp_pwr_mask[] = { AUXOx_PWR_MASK, AUXOx_PWR_MASK }; -static int ab3550_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai* dai); -static int ab3550_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params, struct snd_soc_dai* dai); -static void ab3550_pcm_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai* dai); +struct codec_dai_private { + atomic_t substream_count; +} privates[] = { + {ATOMIC_INIT(0)}, + {ATOMIC_INIT(0)}, +}; -static int ab3550_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, - unsigned int freq, int dir); -static int ab3550_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt); +/* TODO: Add a clock use count. + Turn off the clock is there is no audio activity in the system. + */ -struct snd_soc_dai ab3550_codec_dai[] = { - { - .name = "ab3550_0", - .playback = { - .stream_name = "ab3550_0", - .channels_min = 2, - .channels_max = 2, - .rates = AB3550_SUPPORTED_RATE, - .formats = AB3550_SUPPORTED_FMT, - }, - .capture = { - .stream_name = "ab3550_0", - .channels_min = 2, - .channels_max = 2, - .rates = AB3550_SUPPORTED_RATE, - .formats = AB3550_SUPPORTED_FMT, - }, - .ops = { - .prepare = ab3550_pcm_prepare, - .hw_params = ab3550_pcm_hw_params, - .shutdown = ab3550_pcm_shutdown, - .set_sysclk = ab3550_set_dai_sysclk, - .set_fmt = ab3550_set_dai_fmt - }, - }, - { - .name = "ab3550_1", - .playback = { - .stream_name = "ab3550_1", - .channels_min = 2, - .channels_max = 2, - .rates = AB3550_SUPPORTED_RATE, - .formats = AB3550_SUPPORTED_FMT, - }, - .capture = { - .stream_name = "ab3550_1", - .channels_min = 2, - .channels_max = 2, - .rates = AB3550_SUPPORTED_RATE, - .formats = AB3550_SUPPORTED_FMT, - }, - .ops = { - .prepare = ab3550_pcm_prepare, - .hw_params = ab3550_pcm_hw_params, - .shutdown = ab3550_pcm_shutdown, - .set_sysclk = ab3550_set_dai_sysclk, - .set_fmt = ab3550_set_dai_fmt - }, - } -}; -EXPORT_SYMBOL_GPL(ab3550_codec_dai); + + +// Controls ----------------------------------------------------------------------------------------------------------------------- struct ab3550_control { const char *name; u8 value; - int is_enum; + enum enum_register reg_idx; + u8 reg_mask; + u8 reg_shift; + bool is_enum; + bool changed; }; -/* When AB3550 starts up, these registers are initialized to 0 */ struct ab3550_control ab3550_ctl[] = { - {"RX2 Select", 0, 1}, - {"DAC1 Routing", 0, 0}, - {"DAC2 Routing", 0, 0}, - {"DAC3 Routing", 0, 0}, - {"MIC1 Input Select", 0, 0}, - {"MIC2 Input Select", 0, 0}, - {"I2S0 TX", 0, 1}, - {"I2S1 TX", 0, 1}, - {"APGA1 Source", 0, 1}, - {"APGA2 Source", 0, 1}, - {"APGA1 Destination", 0, 0}, - {"APGA2 Destination", 0, 0}, - {"DAC1 Side Tone", 0, 1}, - {"DAC2 Side Tone", 0, 1}, - {"RX-DPGA1 Gain", 0x3D, 0}, - {"RX-DPGA2 Gain", 0x3D, 0}, - {"RX-DPGA3 Gain", 0x3D, 0}, - {"LINE1 Gain", 0x0A, 0}, - {"LINE2 Gain", 0x0A, 0}, - {"SPKR Gain", 0x0A, 0}, - {"EAR Gain", 0x0A, 0}, - {"AUXO1 Gain", 0x0A, 0}, - {"AUXO2 Gain", 0x0A, 0}, - {"MIC1 Gain", 0, 0}, - {"MIC2 Gain", 0, 0}, - {"TX-DPGA1 Gain", 0, 0}, - {"TX-DPGA2 Gain", 0, 0}, - {"ST-PGA1 Gain", 0, 0}, - {"ST-PGA2 Gain", 0, 0}, - {"APGA1 Gain", 0, 0}, - {"APGA2 Gain", 0, 0}, - {"DAC1 Power Mode", 0, 1}, - {"DAC2 Power Mode", 0, 1}, - {"DAC3 Power Mode", 0, 1}, - {"EAR Power Mode", 0, 1}, - {"AUXO Power Mode", 0, 1}, - {"LINE1 Inverse", 0, 0}, - {"LINE2 Inverse", 0, 0}, - {"AUXO1 Inverse", 0, 0}, - {"AUXO2 Inverse", 0, 0}, - {"AUXO1 Pulldown", 0, 0}, - {"AUXO2 Pulldown", 0, 0}, - {"VMID1", 0, 0}, - {"VMID2", 0, 0}, - {"MIC1-1 MBias", 0, 1}, - {"MIC1-2 MBias", 0, 1}, - {"MIC2-1 MBias", 0, 1}, - {"MIC2-2 MBias", 0, 1}, - {"MBIAS1 HiZ Option", 0, 1}, - {"MBIAS2 HiZ Option", 0, 1}, - {"MBIAS2 Output Voltage", 0, 1}, - {"MBIAS2 Internal Resistor", 0, 1}, - {"MIC1 Input Impedance", 0, 1}, - {"MIC2 Input Impedance", 0, 1}, - {"TX1 HP Filter", 0, 1}, - {"TX2 HP Filter", 0, 1}, - {"LINEIN1 Pre-charge", 0, 0}, - {"LINEIN2 Pre-charge", 0, 0}, - {"MIC1P1 Pre-charge", 0, 0}, - {"MIC1P2 Pre-charge", 0, 0}, - {"MIC1N1 Pre-charge", 0, 0}, - {"MIC1N2 Pre-charge", 0, 0}, - {"MIC2P1 Pre-charge", 0, 0}, - {"MIC2P2 Pre-charge", 0, 0}, - {"MIC2N1 Pre-charge", 0, 0}, - {"MIC2N2 Pre-charge", 0, 0}, - {"ST1 HP Filter", 0, 1}, - {"ST2 HP Filter", 0, 1}, - {"I2S0 Word Length", 0, 1}, - {"I2S1 Word Length", 0, 1}, - {"I2S0 Mode", 0, 1}, - {"I2S1 Mode", 0, 1}, - {"I2S0 Tri-state", 0, 1}, - {"I2S1 Tri-state", 0, 1}, - {"I2S0 Pulldown", 0, 1}, - {"I2S1 Pulldown", 0, 1}, - {"I2S0 Sample Rate", 0, 1}, - {"I2S1 Sample Rate", 0, 1}, - {"Interface Loop", 0, 0}, - {"Interface Swap", 0, 0}, - {"Commit", 0, 0}, +// name value reg_idx reg_mask reg_shift is_enum changed + {"RX2 Select", 0, IDX_RX2, RX2_IF_SELECT_MASK, RX2_IF_SELECT_SHIFT,true, false}, + {"DAC1 Routing", 0x05, IDX_RX1, DACx_PWR_MASK, DACx_PWR_SHIFT, false, false}, // DAC1 -> AUXO1, SPKR + {"DAC2 Routing", 0x06, IDX_RX2, DACx_PWR_MASK, DACx_PWR_SHIFT, false, false}, // DAC2 -> AUXO1, SPKR + {"DAC3 Routing", 0x00, IDX_RX3, DACx_PWR_MASK, DACx_PWR_SHIFT, false, false}, + {"MIC1 Input Select", 0x30, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"MIC2 Input Select", 0x00, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"I2S0 Input Select", 0x11, IDX_TX1, 0x00, 0x00, true, false}, + {"I2S1 Input Select", 0x22, IDX_TX2, 0x00, 0x00, true, false}, + {"APGA1 Source", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"APGA2 Source", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"APGA1 Destination", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"APGA2 Destination", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"DAC1 Side Tone", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"DAC2 Side Tone", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"RX-DPGA1 Gain", 0x3A, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"RX-DPGA2 Gain", 0x3A, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"RX-DPGA3 Gain", 0x00, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"LINE1 Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB + {"LINE2 Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB + {"SPKR Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB + {"EAR Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB + {"AUXO1 Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB + {"AUXO2 Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB + {"MIC1 Gain", 0x2, IDX_MIC1_GAIN, MICx_GAIN_MASK, MICx_GAIN_SHIFT, false, false}, + {"MIC2 Gain", 0x2, IDX_MIC2_GAIN, MICx_GAIN_MASK, MICx_GAIN_SHIFT, false, false}, + {"TX-DPGA1 Gain", 0x06, IDX_TX_DIGITAL_PGA1, TXDPGAx_MASK, TXDPGAx_SHIFT, false, false}, + {"TX-DPGA2 Gain", 0x06, IDX_TX_DIGITAL_PGA2, TXDPGAx_MASK, TXDPGAx_SHIFT, false, false}, + {"ST-PGA1 Gain", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"ST-PGA2 Gain", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"APGA1 Gain", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"APGA2 Gain", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"DAC1 Power Mode", 0x0, IDX_UNKNOWN, 0x00, 0x00, true, false}, // 100% + {"DAC2 Power Mode", 0x0, IDX_UNKNOWN, 0x00, 0x00, true, false}, // 100% + {"DAC3 Power Mode", 0x3, IDX_UNKNOWN, 0x00, 0x00, true, false}, // Do not use + {"EAR Power Mode", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"AUXO Power Mode", 0x4, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"LINE1 Inverse", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"LINE2 Inverse", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"AUXO1 Inverse", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"AUXO2 Inverse", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"AUXO1 Pulldown", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"AUXO2 Pulldown", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"VMID1", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"VMID2", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"MIC1 MBias", 0x01, IDX_UNKNOWN, 0x00, 0x00, true, false}, // MBias 1 On + {"MIC2 MBias", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"MBIAS1 HiZ Option", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"MBIAS2 HiZ Option", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"MBIAS2 Output Voltage", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"MBIAS2 Internal Resistor",0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"MIC1 Input Impedance", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"MIC2 Input Impedance", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"TX1 HP Filter", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"TX2 HP Filter", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"LINEIN1 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"LINEIN2 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"MIC1P1 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"MIC1P2 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"MIC1N1 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"MIC1N2 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"MIC2P1 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"MIC2P2 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"MIC2N1 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"MIC2N2 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"ST1 HP Filter", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"ST2 HP Filter", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"I2S0 Word Length", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"I2S1 Word Length", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"I2S0 Mode", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"I2S1 Mode", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"I2S0 Tri-state", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"I2S1 Tri-state", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"I2S0 Pulldown", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"I2S1 Pulldown", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"I2S0 Sample Rate", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"I2S1 Sample Rate", 0, IDX_UNKNOWN, 0x00, 0x00, true, false}, + {"Interface Loop", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"Interface Swap", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"Voice Call", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}, + {"Commit", 0, IDX_UNKNOWN, 0x00, 0x00, false, false} }; /* control No. 0 correpsonds to bit No. 0 in this array. If a control value has been changed, but not commited to the AB3550. Its corresponding bit is set. */ -static unsigned long control_changed[] = { - 0, 0, 0, 0 -}; +//static unsigned long changed_controls[BIT_WORD(ARRAY_SIZE(ab3550_ctl)) + 1]; static const char *enum_rx2_select[] = {"I2S0", "I2S1"}; -static const char *enum_i2s_tx[] = -{"TX1", "TX2", "tri-state", "mute"}; - -static const char *enum_apga1_source[] = -{"None", "LINEIN1", "MIC1", "MIC2"}; - -static const char *enum_apga2_source[] = -{"None", "LINEIN2", "MIC1", "MIC2"}; - -static const char *enum_dac1_side_tone[] = -{"None", "TX1", "TX2"}; - -static const char *enum_dac1_power_mode[] = -{"100%", "75%", "50%"}; - -static const char *enum_ear_power_mode[] = -{"100%", "75%"}; - -static const char *enum_auxo_power_mode[] = -{"100%", "75%", "50%", "25%"}; - -static const char *enum_mic_bias_connection[] = -{"MIC Bias 1", "MIC Bias 2"}; - -static const char *enum_mbias1_hiz_option[] = -{"GND", "HiZ"}; - -static const char *enum_mbias2_output_voltage[] = -{"2.0v", "2.2v"}; - -static const char *enum_mbias2_internal_resistor[] = -{"connected", "bypassed"}; - -static const char *enum_mic1_input_impedance[] = -{"12.5 kohm", "25 kohm", "50 kohm"}; - -static const char *enum_tx1_hp_filter[] = -{"HP3", "HP1", "bypass"}; - -static const char *enum_i2s_word_length[] = -{"16 bits", "24 bits"}; - -static const char *enum_i2s_mode[] = -{"Master Mode", "Slave Mode"}; - -static const char *enum_i2s_tristate[] = -{"Normal", "Tri-state"}; - -static const char *enum_i2s_pulldown[] = -{"disconnected", "connected"}; - -static const char *enum_i2s_sample_rate[] = -{"8 kHz", "16 kHz", "44.1 kHz", "48 kHz"}; - - -static struct soc_enum ab3550_enum[] = { - /* RX2 Select */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_rx2_select), enum_rx2_select), - /* I2S0 TX */ - SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_tx), enum_i2s_tx), - /* I2S1 TX */ - SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_tx), enum_i2s_tx), - /* APGA1 Source */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga1_source), - enum_apga1_source), - /* APGA2 Source */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga2_source), - enum_apga2_source), - /* DAC1 Side Tone */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_side_tone), - enum_dac1_side_tone), - /* DAC2 Side Tone */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_side_tone), - enum_dac1_side_tone), - /* DAC1 Power Mode */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_power_mode), - enum_dac1_power_mode), - /* DAC2 Power Mode */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_power_mode), - enum_dac1_power_mode), - /* DAC3 Power Mode */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_power_mode), - enum_dac1_power_mode), - /* EAR Power Mode */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_ear_power_mode), - enum_ear_power_mode), - /* AUXO Power Mode */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_auxo_power_mode), - enum_auxo_power_mode), - /* MIC Bias Connection */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection), - enum_mic_bias_connection), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection), - enum_mic_bias_connection), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection), - enum_mic_bias_connection), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection), - enum_mic_bias_connection), - /* MBIAS1 HiZ Option */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias1_hiz_option), - enum_mbias1_hiz_option), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias1_hiz_option), - enum_mbias1_hiz_option), - /* MBIAS2 Output voltage */ - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_output_voltage), - enum_mbias2_output_voltage), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_internal_resistor), - enum_mbias2_internal_resistor), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic1_input_impedance), - enum_mic1_input_impedance), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic1_input_impedance), - enum_mic1_input_impedance), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter), - enum_tx1_hp_filter), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter), - enum_tx1_hp_filter), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter), - enum_tx1_hp_filter), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter), - enum_tx1_hp_filter), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length), - enum_i2s_word_length), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length), - enum_i2s_word_length), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode), - enum_i2s_mode), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode), - enum_i2s_mode), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate), - enum_i2s_tristate), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate), - enum_i2s_tristate), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown), - enum_i2s_pulldown), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown), - enum_i2s_pulldown), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate), - enum_i2s_sample_rate), - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate), - enum_i2s_sample_rate), - -}; +static const char *enum_i2s_input_select[] = {"tri-state", "MIC1", "MIC2", "mute"}; +static const char *enum_apga1_source[] = {"None", "LINEIN1", "MIC1", "MIC2"}; +static const char *enum_apga2_source[] = {"None", "LINEIN2", "MIC1", "MIC2"}; +static const char *enum_dac_side_tone[] = {"None", "TX1", "TX2"}; +static const char *enum_dac_power_mode[] = {"100%", "75%", "50%"}; +static const char *enum_ear_power_mode[] = {"100%", "75%"}; +static const char *enum_auxo_power_mode[] = {"100%", "75%", "50%", "25%"}; +static const char *enum_mbias[] = {"Off", "On"}; +static const char *enum_mbias_hiz_option[] = {"GND", "HiZ"}; +static const char *enum_mbias2_output_voltage[] = {"2.0v", "2.2v"}; +static const char *enum_mbias2_internal_resistor[] = {"connected", "bypassed"}; +static const char *enum_mic_input_impedance[] = {"12.5 kohm", "25 kohm", "50 kohm"}; +static const char *enum_hp_filter[] = {"HP3", "HP1", "bypass"}; +static const char *enum_i2s_word_length[] = {"16 bits", "24 bits"}; +static const char *enum_i2s_mode[] = {"Master Mode", "Slave Mode"}; +static const char *enum_i2s_tristate[] = {"Normal", "Tri-state"}; +static const char *enum_i2s_pulldown[] = {"disconnected", "connected"}; +static const char *enum_i2s_sample_rate[] = {"8 kHz", "16 kHz", "44.1 kHz", "48 kHz"}; + +static struct soc_enum soc_enum_rx2_select = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_rx2_select), enum_rx2_select); // RX2 Select +static struct soc_enum soc_enum_i2s0_input_select = SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_input_select), enum_i2s_input_select); // I2S0 Input Select +static struct soc_enum soc_enum_i2s1_input_select = SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_input_select), enum_i2s_input_select); // I2S1 Input Select +static struct soc_enum soc_enum_apga1_source = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga1_source), enum_apga1_source); // APGA1 Source +static struct soc_enum soc_enum_apga2_source = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga2_source), enum_apga2_source); // APGA2 Source +static struct soc_enum soc_enum_dac1_side_tone = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_side_tone), enum_dac_side_tone); // DAC1 Side Tone +static struct soc_enum soc_enum_dac2_side_tone = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_side_tone), enum_dac_side_tone); // DAC2 Side Tone +static struct soc_enum soc_enum_dac1_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_power_mode), enum_dac_power_mode); // DAC1 Power Mode +static struct soc_enum soc_enum_dac2_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_power_mode), enum_dac_power_mode); // DAC2 Power Mode +static struct soc_enum soc_enum_dac3_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_power_mode), enum_dac_power_mode); // DAC3 Power Mode +static struct soc_enum soc_enum_ear_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_ear_power_mode), enum_ear_power_mode); // EAR Power Mode +static struct soc_enum soc_enum_auxo_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_auxo_power_mode), enum_auxo_power_mode); // AUXO Power Mode +static struct soc_enum soc_enum_mic1_mbias = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias), enum_mbias); // MIC Bias Connection +static struct soc_enum soc_enum_mic2_mbias = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias), enum_mbias); // MIC Bias Connection +static struct soc_enum soc_enum_mbias1_hiz_option = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias_hiz_option), enum_mbias_hiz_option); // MBIAS1 HiZ Option +static struct soc_enum soc_enum_mbias2_hiz_option = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias_hiz_option), enum_mbias_hiz_option); // MBIAS1 HiZ Option +static struct soc_enum soc_enum_mbias2_output_voltage = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_output_voltage), enum_mbias2_output_voltage); // MBIAS2 Output voltage +static struct soc_enum soc_enum_mbias2_internal_resistor = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_internal_resistor), enum_mbias2_internal_resistor); // +static struct soc_enum soc_enum_mic1_input_impedance = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_input_impedance), enum_mic_input_impedance); // +static struct soc_enum soc_enum_mic2_input_impedance = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_input_impedance), enum_mic_input_impedance); // +static struct soc_enum soc_enum_tx1_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); // +static struct soc_enum soc_enum_tx2_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); // +static struct soc_enum soc_enum_st1_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); // +static struct soc_enum soc_enum_st2_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); // +static struct soc_enum soc_enum_i2s0_word_length = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length), enum_i2s_word_length); // +static struct soc_enum soc_enum_i2s1_word_length = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length), enum_i2s_word_length); // +static struct soc_enum soc_enum_i2s0_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode), enum_i2s_mode); // +static struct soc_enum soc_enum_i2s1_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode), enum_i2s_mode); // +static struct soc_enum soc_enum_i2s0_tristate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate), enum_i2s_tristate); // +static struct soc_enum soc_enum_i2s1_tristate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate), enum_i2s_tristate); // +static struct soc_enum soc_enum_i2s0_pulldown = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown), enum_i2s_pulldown); // +static struct soc_enum soc_enum_i2s1_pulldown = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown), enum_i2s_pulldown); // +static struct soc_enum soc_enum_i2s0_sample_rate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate), enum_i2s_sample_rate); // +static struct soc_enum soc_enum_i2s1_sample_rate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate), enum_i2s_sample_rate); // static struct snd_kcontrol_new ab3550_snd_controls[] = { /* RX Routing */ - SOC_ENUM("RX2 Select", ab3550_enum[0]), - SOC_SINGLE("DAC1 Routing", 0, 0, 0x3f, 0), - SOC_SINGLE("DAC2 Routing", 0, 0, 0x3f, 0), - SOC_SINGLE("DAC3 Routing", 0, 0, 0x3f, 0), + SOC_ENUM( "RX2 Select", soc_enum_rx2_select), + SOC_SINGLE( "DAC1 Routing", 0, 0, 0x3f, 0), + SOC_SINGLE( "DAC2 Routing", 0, 0, 0x3f, 0), + SOC_SINGLE( "DAC3 Routing", 0, 0, 0x3f, 0), /* TX Routing */ - SOC_SINGLE("MIC1 Input Select", 0, 0, 0xff, 0), - SOC_SINGLE("MIC2 Input Select", 0, 0, 0x3f, 0), - SOC_ENUM("I2S0 TX", ab3550_enum[1]), - SOC_ENUM("I2S1 TX", ab3550_enum[2]), + SOC_SINGLE( "MIC1 Input Select", 0, 0, 0xff, 0), + SOC_SINGLE( "MIC2 Input Select", 0, 0, 0x3f, 0), + SOC_ENUM( "I2S0 Input Select", soc_enum_i2s0_input_select), + SOC_ENUM( "I2S1 Input Select", soc_enum_i2s1_input_select), /* Routing of Side Tone and Analop Loop */ - SOC_ENUM("APGA1 Source", ab3550_enum[3]), - SOC_ENUM("APGA2 Source", ab3550_enum[4]), - SOC_SINGLE("APGA1 Destination", 0, 0, 0x3f, 0), - SOC_SINGLE("APGA2 Destination", 0, 0, 0x3f, 0), - SOC_ENUM("DAC1 Side Tone", ab3550_enum[5]), - SOC_ENUM("DAC2 Side Tone", ab3550_enum[5]), + SOC_ENUM( "APGA1 Source", soc_enum_apga1_source), + SOC_ENUM( "APGA2 Source", soc_enum_apga2_source), + SOC_SINGLE( "APGA1 Destination", 0, 0, 0x3f, 0), + SOC_SINGLE( "APGA2 Destination", 0, 0, 0x3f, 0), + SOC_ENUM( "DAC1 Side Tone", soc_enum_dac1_side_tone), + SOC_ENUM( "DAC2 Side Tone", soc_enum_dac2_side_tone), /* RX Volume Control */ - SOC_SINGLE("RX-DPGA1 Gain", 0, 0, 66, 0), - SOC_SINGLE("RX-DPGA2 Gain", 0, 0, 66, 0), - SOC_SINGLE("RX-DPGA3 Gain", 0, 0, 66, 0), - SOC_SINGLE("LINE1 Gain", 0, 0, 10, 0), - SOC_SINGLE("LINE2 Gain", 0, 0, 10, 0), - SOC_SINGLE("SPKR Gain", 0, 0, 22, 0), - SOC_SINGLE("EAR Gain", 0, 0, 14, 0), - SOC_SINGLE("AUXO1 Gain", 0, 0, 12, 0), - SOC_SINGLE("AUXO2 Gain", 0, 0, 12, 0), + SOC_SINGLE( "RX-DPGA1 Gain", 0, 0, 66, 0), + SOC_SINGLE( "RX-DPGA2 Gain", 0, 0, 66, 0), + SOC_SINGLE( "RX-DPGA3 Gain", 0, 0, 66, 0), + SOC_SINGLE( "LINE1 Gain", 0, 0, 10, 0), + SOC_SINGLE( "LINE2 Gain", 0, 0, 10, 0), + SOC_SINGLE( "SPKR Gain", 0, 0, 22, 0), + SOC_SINGLE( "EAR Gain", 0, 0, 14, 0), + SOC_SINGLE( "AUXO1 Gain", 0, 0, 12, 0), + SOC_SINGLE( "AUXO2 Gain", 0, 0, 12, 0), /* TX Volume Control */ - SOC_SINGLE("MIC1 Gain", 0, 0, 10, 0), - SOC_SINGLE("MIC2 Gain", 0, 0, 10, 0), - SOC_SINGLE("TX-DPGA1 Gain", 0, 0, 15, 0), - SOC_SINGLE("TX-DPGA2 Gain", 0, 0, 15, 0), + SOC_SINGLE( "MIC1 Gain", 0, 0, 10, 0), + SOC_SINGLE( "MIC2 Gain", 0, 0, 10, 0), + SOC_SINGLE( "TX-DPGA1 Gain", 0, 0, 15, 0), + SOC_SINGLE( "TX-DPGA2 Gain", 0, 0, 15, 0), /* Volume Control of Side Tone and Analog Loop */ - SOC_SINGLE("ST-PGA1 Gain", 0, 0, 10, 0), - SOC_SINGLE("ST-PGA2 Gain", 0, 0, 10, 0), - SOC_SINGLE("APGA1 Gain", 0, 0, 28, 0), - SOC_SINGLE("APGA2 Gain", 0, 0, 28, 0), + SOC_SINGLE( "ST-PGA1 Gain", 0, 0, 10, 0), + SOC_SINGLE( "ST-PGA2 Gain", 0, 0, 10, 0), + SOC_SINGLE( "APGA1 Gain", 0, 0, 28, 0), + SOC_SINGLE( "APGA2 Gain", 0, 0, 28, 0), /* RX Properties */ - SOC_ENUM("DAC1 Power Mode", ab3550_enum[6]), - SOC_ENUM("DAC2 Power Mode", ab3550_enum[7]), - SOC_ENUM("DAC3 Power Mode", ab3550_enum[8]), - SOC_ENUM("EAR Power Mode", ab3550_enum[9]), - SOC_ENUM("AUXO Power Mode", ab3550_enum[10]), - SOC_SINGLE("LINE1 Inverse", 0, 0, 1, 0), - SOC_SINGLE("LINE2 Inverse", 0, 0, 1, 0), - SOC_SINGLE("AUXO1 Inverse", 0, 0, 1, 0), - SOC_SINGLE("AUXO2 Inverse", 0, 0, 1, 0), - SOC_SINGLE("AUXO1 Pulldown", 0, 0, 1, 0), - SOC_SINGLE("AUXO2 Pulldown", 0, 0, 1, 0), + SOC_ENUM( "DAC1 Power Mode", soc_enum_dac1_power_mode), + SOC_ENUM( "DAC2 Power Mode", soc_enum_dac2_power_mode), + SOC_ENUM( "DAC3 Power Mode", soc_enum_dac3_power_mode), + SOC_ENUM( "EAR Power Mode", soc_enum_ear_power_mode), + SOC_ENUM( "AUXO Power Mode", soc_enum_auxo_power_mode), + SOC_SINGLE( "LINE1 Inverse", 0, 0, 1, 0), + SOC_SINGLE( "LINE2 Inverse", 0, 0, 1, 0), + SOC_SINGLE( "AUXO1 Inverse", 0, 0, 1, 0), + SOC_SINGLE( "AUXO2 Inverse", 0, 0, 1, 0), + SOC_SINGLE( "AUXO1 Pulldown", 0, 0, 1, 0), + SOC_SINGLE( "AUXO2 Pulldown", 0, 0, 1, 0), /* TX Properties */ - SOC_SINGLE("VMID1", 0, 0, 0xff, 0), - SOC_SINGLE("VMID2", 0, 0, 0xff, 0), - SOC_ENUM("MIC1-1 MBias", ab3550_enum[11]), - SOC_ENUM("MIC1-2 MBias", ab3550_enum[12]), - SOC_ENUM("MIC2-1 MBias", ab3550_enum[13]), - SOC_ENUM("MIC2-2 MBias", ab3550_enum[14]), - SOC_ENUM("MBIAS1 HiZ Option", ab3550_enum[15]), - SOC_ENUM("MBIAS2 HiZ Option", ab3550_enum[16]), - SOC_ENUM("MBIAS2 Output Voltage", ab3550_enum[17]), - SOC_ENUM("MBIAS2 Internal Resistor", ab3550_enum[18]), - SOC_ENUM("MIC1 Input Impedance", ab3550_enum[19]), - SOC_ENUM("MIC2 Input Impedance", ab3550_enum[20]), - SOC_ENUM("TX1 HP Filter", ab3550_enum[21]), - SOC_ENUM("TX2 HP Filter", ab3550_enum[22]), - SOC_SINGLE("LINEIN1 Pre-charge", 0, 0, 100, 0), - SOC_SINGLE("LINEIN2 Pre-charge", 0, 0, 100, 0), - SOC_SINGLE("MIC1P1 Pre-charge", 0, 0, 100, 0), - SOC_SINGLE("MIC1P2 Pre-charge", 0, 0, 100, 0), - SOC_SINGLE("MIC1N1 Pre-charge", 0, 0, 100, 0), - SOC_SINGLE("MIC1N2 Pre-charge", 0, 0, 100, 0), - SOC_SINGLE("MIC2P1 Pre-charge", 0, 0, 100, 0), - SOC_SINGLE("MIC2P2 Pre-charge", 0, 0, 100, 0), - SOC_SINGLE("MIC2N1 Pre-charge", 0, 0, 100, 0), - SOC_SINGLE("MIC2N2 Pre-charge", 0, 0, 100, 0), + SOC_SINGLE( "VMID1", 0, 0, 0xff, 0), + SOC_SINGLE( "VMID2", 0, 0, 0xff, 0), + SOC_ENUM( "MIC1 MBias", soc_enum_mic1_mbias), + SOC_ENUM( "MIC2 MBias", soc_enum_mic2_mbias), + SOC_ENUM( "MBIAS1 HiZ Option", soc_enum_mbias1_hiz_option), + SOC_ENUM( "MBIAS2 HiZ Option", soc_enum_mbias2_hiz_option), + SOC_ENUM( "MBIAS2 Output Voltage", soc_enum_mbias2_output_voltage), + SOC_ENUM( "MBIAS2 Internal Resistor", soc_enum_mbias2_internal_resistor), + SOC_ENUM( "MIC1 Input Impedance", soc_enum_mic1_input_impedance), + SOC_ENUM( "MIC2 Input Impedance", soc_enum_mic2_input_impedance), + SOC_ENUM( "TX1 HP Filter", soc_enum_tx1_hp_filter), + SOC_ENUM( "TX2 HP Filter", soc_enum_tx2_hp_filter), + SOC_SINGLE( "LINEIN1 Pre-charge", 0, 0, 100, 0), + SOC_SINGLE( "LINEIN2 Pre-charge", 0, 0, 100, 0), + SOC_SINGLE( "MIC1P1 Pre-charge", 0, 0, 100, 0), + SOC_SINGLE( "MIC1P2 Pre-charge", 0, 0, 100, 0), + SOC_SINGLE( "MIC1N1 Pre-charge", 0, 0, 100, 0), + SOC_SINGLE( "MIC1N2 Pre-charge", 0, 0, 100, 0), + SOC_SINGLE( "MIC2P1 Pre-charge", 0, 0, 100, 0), + SOC_SINGLE( "MIC2P2 Pre-charge", 0, 0, 100, 0), + SOC_SINGLE( "MIC2N1 Pre-charge", 0, 0, 100, 0), + SOC_SINGLE( "MIC2N2 Pre-charge", 0, 0, 100, 0), /* Side Tone and Analog Loop Properties */ - SOC_ENUM("ST1 HP Filter", ab3550_enum[23]), - SOC_ENUM("ST2 HP Filter", ab3550_enum[24]), + SOC_ENUM( "ST1 HP Filter", soc_enum_st1_hp_filter), + SOC_ENUM( "ST2 HP Filter", soc_enum_st2_hp_filter), /* I2S Interface Properties */ - SOC_ENUM("I2S0 Word Length", ab3550_enum[25]), - SOC_ENUM("I2S1 Word Length", ab3550_enum[26]), - SOC_ENUM("I2S0 Mode", ab3550_enum[27]), - SOC_ENUM("I2S1 Mode", ab3550_enum[28]), - SOC_ENUM("I2S0 tri-state", ab3550_enum[29]), - SOC_ENUM("I2S1 tri-state", ab3550_enum[30]), - SOC_ENUM("I2S0 pulldown", ab3550_enum[31]), - SOC_ENUM("I2S1 pulldown", ab3550_enum[32]), - SOC_ENUM("I2S0 Sample Rate", ab3550_enum[33]), - SOC_ENUM("I2S1 Sample Rate", ab3550_enum[34]), - SOC_SINGLE("Interface Loop", 0, 0, 0x0f, 0), - SOC_SINGLE("Interface Swap", 0, 0, 0x1f, 0), + SOC_ENUM( "I2S0 Word Length", soc_enum_i2s0_word_length), + SOC_ENUM( "I2S1 Word Length", soc_enum_i2s1_word_length), + SOC_ENUM( "I2S0 Mode", soc_enum_i2s0_mode), + SOC_ENUM( "I2S1 Mode", soc_enum_i2s1_mode), + SOC_ENUM( "I2S0 tri-state", soc_enum_i2s0_tristate), + SOC_ENUM( "I2S1 tri-state", soc_enum_i2s1_tristate), + SOC_ENUM( "I2S0 pulldown", soc_enum_i2s0_pulldown), + SOC_ENUM( "I2S1 pulldown", soc_enum_i2s1_pulldown), + SOC_ENUM( "I2S0 Sample Rate", soc_enum_i2s0_sample_rate), + SOC_ENUM( "I2S1 Sample Rate", soc_enum_i2s1_sample_rate), + SOC_SINGLE( "Interface Loop", 0, 0, 0x0f, 0), + SOC_SINGLE( "Interface Swap", 0, 0, 0x1f, 0), /* Miscellaneous */ - SOC_SINGLE("Commit", 0, 0, 1, 0) + SOC_SINGLE( "Voice Call", 0, 0, 1, 0), + SOC_SINGLE( "Commit", 0, 0, 1, 0) }; static const struct snd_soc_dapm_widget ab3550_dapm_widgets[] = { @@ -478,11 +414,12 @@ static const struct snd_soc_dapm_widget ab3550_dapm_widgets[] = { static const struct snd_soc_dapm_route intercon[] = { }; -static unsigned int ab3550_get_control_value(struct snd_soc_codec *codec, - unsigned int ctl) +static unsigned int ab3550_get_control_value(struct snd_soc_codec *codec, unsigned int ctl) { if (ctl >= ARRAY_SIZE(ab3550_ctl)) return -EINVAL; + //printk(KERN_DEBUG "%s: returning value of control %s: 0x%02x.\n", + // __func__, ab3550_ctl[ctl].name, ab3550_ctl[ctl].value); return ab3550_ctl[ctl].value; } @@ -498,25 +435,27 @@ static unsigned int get_control_index(const char *name) static unsigned int get_control_value_by_name(const char *name) { - int i, n; - for(i = 0, n = ARRAY_SIZE(ab3550_ctl); i < n; i++) { - if (strcmp(ab3550_ctl[i].name, name) == 0) - break; - } + int i = get_control_index(name); return ab3550_ctl[i].value; } +static unsigned int get_control_changed_by_name(const char *name) +{ + int i = get_control_index(name); + return ab3550_ctl[i].changed; +} + static int gain_ramp(u8 reg, u8 target, int shift, u8 mask) { - u8 curr, step; +// u8 curr; int ret = 0; // printk(KERN_DEBUG "%s invoked.\n", __func__); - if ((ret = GET_REG(reg, &curr))) { - return ret; - } - if (curr == target) - return 0; +// if ((ret = GET_REG(reg, &curr))) { +// return ret; +// } +// if (curr == target) +// return 0; // step = curr < target ? 1 : -1; // while (!ret && curr != target) { /* At least 15ms are required, so I take 16ms :-) */ @@ -527,125 +466,79 @@ static int gain_ramp(u8 reg, u8 target, int shift, u8 mask) return ret; } -static int ab3550_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, - unsigned int freq, int dir) +static void ab3550_print_registers(int stream_dir) { - printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__); - return 0; + int i; + u8 val; + for (i = 0; i < ARRAY_SIZE(ab3550_registers); i++) { + GET_REG(ab3550_registers[i].address, &val); + printk(KERN_DEBUG "REGISTERS: %s = 0x%02x\n", ab3550_registers[i].name, val); + } } -static int ab3550_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) -{ - printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__); - return 0; +static void ab3550_toggle_output_amp(int idx_amp, u8 val) { + struct ab3550_register* reg = &(ab3550_registers[(int)outamp_indices[idx_amp]]); + MASK_SET_REG(reg->address, + outamp_pwr_mask[idx_amp], + val << outamp_pwr_shift[idx_amp]); + + printk(KERN_DEBUG "DORIANO: %s turned %s.\n", reg->name, val == 0 ? "off" : "on"); } -static int ab3550_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai* dai) +static void ab3550_toggle_mic_amp(int idx, u8 val) { + enum enum_register idx_reg_mic_amp[] = {IDX_MIC1_GAIN, IDX_MIC2_GAIN}; // PWR is in located in the gain regs. + struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_mic_amp[idx]]); + MASK_SET_REG(reg->address, + MICx_PWR_MASK, + val << MICx_PWR_SHIFT); + + printk(KERN_DEBUG "DORIANO: %s turned %s.\n", reg->name, val == 0 ? "off" : "on"); +} + +static void ab3550_toggle_DAC(int idx_DAC, u8 val) { - u8 val; - printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__); - MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 1 << CLOCK_ENABLE_SHIFT); - /* Turn on the master generator */ - MASK_SET_REG(INTERFACE0, MASTER_GENx_PWR_MASK, - 1 << MASTER_GENx_PWR_SHIFT); - GET_REG(CLOCK, &val); - printk(KERN_DEBUG "%s: CLOCK = %02x.\n", __func__, val); - GET_REG(INTERFACE0, &val); - printk(KERN_DEBUG "%s: INTERFACE0 = %02x.\n", __func__, val); - GET_REG(RX1, &val); - printk(KERN_DEBUG "%s: RX1 = %02x.\n", __func__, val); - GET_REG(RX2, &val); - printk(KERN_DEBUG "%s: RX2 = %02x.\n", __func__, val); - GET_REG(SPKR_ADDER, &val); - printk(KERN_DEBUG "%s: SPKR_ADDER = %02x.\n", __func__, val); - GET_REG(AUXO1_ADDER, &val); - printk(KERN_DEBUG "%s: AUXO1_ADDER = %02x.\n", __func__, val); - GET_REG(AUXO2_ADDER, &val); - printk(KERN_DEBUG "%s: AUXO2_ADDER = %02x.\n", __func__, val); - GET_REG(SPKR, &val); - printk(KERN_DEBUG "%s: SPKR = %02x.\n", __func__, val); - GET_REG(AUXO1, &val); - printk(KERN_DEBUG "%s: AUXO1 = %02x.\n", __func__, val); - GET_REG(AUXO2, &val); - printk(KERN_DEBUG "%s: AUXO2 = %02x.\n", __func__, val); - GET_REG(RX1_DIGITAL_PGA, &val); - printk(KERN_DEBUG "%s: RX1_DIGITAL_PGA = %02x.\n", __func__, val); - GET_REG(RX2_DIGITAL_PGA, &val); - printk(KERN_DEBUG "%s: RX2_DIGITAL_PGA = %02x.\n", __func__, val); - return 0; + enum enum_register idx_reg_DAC[] = {IDX_RX1, IDX_RX2, IDX_RX3}; + struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_DAC[idx_DAC]]); + MASK_SET_REG(reg->address, + DACx_PWR_MASK | RXx_PWR_MASK, + (val << RXx_PWR_SHIFT | val << DACx_PWR_SHIFT)); + + printk(KERN_DEBUG "DORIANO: DAC%d turned %s.\n", idx_DAC + 1, val == 0 ? "off" : "on"); } -static void ab3550_pcm_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai* dai) +static void ab3550_toggle_output_adder(int idx_adder, u8 val) { int i; - printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__); - MASK_SET_REG(INTERFACE0, MASTER_GENx_PWR_MASK, 0); - MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 0); - for (i = 0; i < ARRAY_SIZE(outamp_reg); i++) { - MASK_SET_REG(outamp_reg[i], outamp_pwr_mask[i], 0); - } + struct ab3550_register* reg = &(ab3550_registers[(int)outamp_adder_indices[idx_adder]]); + + MASK_SET_REG(reg->address, + DAC1_TO_ADDER_MASK | DAC2_TO_ADDER_MASK | DAC3_TO_ADDER_MASK, + val); + + for (i = 0; i < 3; i++) + printk(KERN_DEBUG "DORIANO: DAC%d -> %s: %s.\n", i + 1, reg->name, (val & 1 << i) ? "on" : "off"); } -static int set_up_speaker_playback(void) +static void ab3550_toggle_ADC(int idx_ADC, u8 val) { - printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__); - MASK_SET_REG(CLOCK, CLOCK_REF_SELECT_MASK, - 1 << CLOCK_REF_SELECT_SHIFT); + enum enum_register idx_reg_ADC[] = {IDX_TX1, IDX_TX2}; + struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_ADC[idx_ADC]]); + MASK_SET_REG(reg->address, + ADCx_PWR_MASK | TXx_PWR_MASK, + ( val << ADCx_PWR_SHIFT | val << TXx_PWR_SHIFT)); - /* Sample rate 48 kHz */ - SET_REG(INTERFACE0, 0x03); - printk(KERN_DEBUG "%s: interface 0 is setup", __func__); - - MASK_SET_REG(RX2, RX2_IF_SELECT_MASK, 0); - SET_REG(SPKR_ADDER, 1 << DAC1_TO_ADDER_SHIFT | - 1 << DAC2_TO_ADDER_SHIFT); -/* SET_REG(AUXO1_ADDER, 1 << DAC1_TO_ADDER_SHIFT); */ -/* SET_REG(AUXO2_ADDER, 1 << DAC2_TO_ADDER_SHIFT); */ - printk(KERN_DEBUG "%s: Routes to the speaker and the AUXO are setup.\n", - __func__); - - /* Mute the outamps first */ - MASK_SET_REG(RX1_DIGITAL_PGA, RXx_PGA_GAIN_MASK, 0); - MASK_SET_REG(RX2_DIGITAL_PGA, RXx_PGA_GAIN_MASK, 0); -/* MASK_SET_REG(AUXO1, AUXOx_GAIN_MASK, 0); */ -/* MASK_SET_REG(AUXO2, AUXOx_GAIN_MASK, 0); */ - MASK_SET_REG(SPKR, SPKR_GAIN_MASK, 0); - - MASK_SET_REG(RX1, RX1_PWR_MASK | DAC1_PWR_MASK | DAC1_PWR_MODE_MASK, - 1 << RX1_PWR_SHIFT | 1 << DAC1_PWR_SHIFT | - 1 << DAC1_PWR_MODE_SHIFT); - MASK_SET_REG(RX2, RX2_PWR_MASK | DAC2_PWR_MASK | DAC2_PWR_MODE_MASK, - 1 << RX2_PWR_SHIFT | 1 << DAC2_PWR_SHIFT | - 1 << DAC2_PWR_MODE_SHIFT); - printk(KERN_DEBUG "%s: DAC1, DAC2 and RX2 are now powered up.\n", - __func__); - - MASK_SET_REG(SPKR, SPKR_PWR_MASK, 1 << SPKR_PWR_SHIFT); -/* MASK_SET_REG(AUXO1, AUXOx_PWR_MASK, 1 << AUXOx_PWR_SHIFT); */ -/* MASK_SET_REG(AUXO2, AUXOx_PWR_MASK, 1 << AUXOx_PWR_SHIFT); */ - printk(KERN_DEBUG "%s: The speaker and the AUXO are now powered up.\n", - __func__); - - gain_ramp(RX1_DIGITAL_PGA, get_control_value_by_name("RX-DPGA1 Gain"), - RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK); - - gain_ramp(RX2_DIGITAL_PGA, get_control_value_by_name("RX-DPGA2 Gain"), - RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK); - gain_ramp(SPKR, get_control_value_by_name("SPKR Gain"), - SPKR_GAIN_SHIFT, SPKR_GAIN_MASK); -/* gain_ramp(AUXO1, get_control_value_by_name("AUXO1 Gain"), */ -/* AUXOx_GAIN_SHIFT, AUXOx_GAIN_MASK); */ -/* gain_ramp(AUXO2, get_control_value_by_name("AUXO1 Gain"), */ -/* AUXOx_GAIN_SHIFT, AUXOx_GAIN_MASK); */ - printk(KERN_DEBUG "%s: return 0.\n", __func__); - return 0; + printk(KERN_DEBUG "DORIANO: ADC%d turned %s.\n", idx_ADC + 1, val == 0 ? "off" : "on"); } -static int ab3550_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params, struct snd_soc_dai* dai) +static void ab3550_set_ADC_filter(int idx_ADC, u8 val) { - printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__); - set_up_speaker_playback(); - return 0; + enum enum_register idx_reg_ADC[] = {IDX_TX1, IDX_TX2}; + struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_ADC[idx_ADC]]); + MASK_SET_REG(reg->address, + TXx_HP_FILTER_MASK, + val << TXx_HP_FILTER_SHIFT); + + printk(KERN_DEBUG "DORIANO: ADC%d filter set to %s.\n", idx_ADC + 1, enum_hp_filter[val]); } static int ab3550_add_widgets(struct snd_soc_codec *codec) @@ -659,35 +552,150 @@ static int ab3550_add_widgets(struct snd_soc_codec *codec) return 0; } + + +static void ab3550_init_registers_startup(int ifSel) +{ + u8 val; + u8 iface = (ifSel == 0) ? INTERFACE0 : INTERFACE1; + + // Configure CLOCK register + val = 1 << CLOCK_REF_SELECT_SHIFT | // 32kHz RTC + 1 << CLOCK_ENABLE_SHIFT; // Enable clock + SET_REG(CLOCK, val); + + // Enable Master Generator I2Sx + MASK_SET_REG(iface, MASTER_GENx_PWR_MASK | I2Sx_TRISTATE_MASK | I2Sx_PULLDOWN_MASK, 1 << MASTER_GENx_PWR_SHIFT); + + // Configure INTERFACEx_DATA register + val = 1 << I2Sx_L_DATA_SHIFT |// TX1 to I2Sx_L + 1 << I2Sx_R_DATA_SHIFT; // TX1 to I2Sx_R + SET_REG(ifSel == 0 ? INTERFACE0_DATA : INTERFACE1_DATA, val); + + GET_REG(CLOCK, &val); + printk(KERN_DEBUG "AB: CLOCK = 0x%02x\n", val); + GET_REG(iface, &val); + printk(KERN_DEBUG "AB: INTERFACE%d = 0x%02x\n", ifSel, val); + GET_REG(ifSel == 0 ? INTERFACE0_DATA : INTERFACE1_DATA, &val); + printk(KERN_DEBUG "AB: INTERFACE%d_DATA = 0x%02x\n", ifSel, val); +} + +static void ab3550_init_registers_playback(int ifSel) { + + int i, routing_left, routing_right, idx_RX_left, idx_RX_right; + u8 val; + + // Get indices to active DACs + idx_RX_left = idx_RX_right = -1; + val = get_control_value_by_name("RX2 Select"); + if (ifSel == 0) { + if (!(val & RX2_IF_SELECT_MASK)) + idx_RX_right = 1; + idx_RX_left = 0; + } else { + if (val & RX2_IF_SELECT_MASK) + idx_RX_right = 1; + idx_RX_left = 2; + } +printk(KERN_DEBUG "DORIANO: idx_RX_left = %d, idx_RX_right = %d\n", idx_RX_left, idx_RX_right); + + // Toggle DACs + routing_left = ab3550_ctl[IDX_DAC1_Routing].value; +printk(KERN_DEBUG "DORIANO: routing_left = %d\n", routing_left); + if (routing_left) + ab3550_toggle_DAC(idx_RX_left, 1); + routing_right = 0; + if (idx_RX_right > -1) + { + routing_right = ab3550_ctl[IDX_DAC2_Routing].value; +printk(KERN_DEBUG "DORIANO: routing_right = %d\n", routing_right); + if (routing_right) + ab3550_toggle_DAC(idx_RX_right, 1); + } + + // Toggle adders + for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) { + val = 0; + val |= (routing_left & 1 << i) ? 1 << idx_RX_left : 0; + if (idx_RX_right > -1) + val |= (routing_right & 1 << i) ? 1 << idx_RX_right : 0; + ab3550_toggle_output_adder(i, val); + } + + /* TODO: DC cancellation */ + + // Toggle amplifiers + val = routing_left | routing_right; + for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) { +printk(KERN_DEBUG "DORIANO: val & 1 << i = %d\n", val & 1 << i); + if (val & 1 << i) + ab3550_toggle_output_amp(i, 1); + } +} + +static void ab3550_init_registers_capture(int ifSel) +{ + enum enum_control idx = (ifSel == 0) ? IDX_I2S0_Input_Select : IDX_I2S1_Input_Select; + +printk(KERN_DEBUG "DORIANO: ab3550_ctl[idx].value = 0x%02x\n", ab3550_ctl[idx].value); + + // Toggle ADC (RXx) and amplifiers (MICx_GAIN) + if ((ab3550_ctl[idx].value & 0x0F) < 3) { // value = 3 => mute + int idx_ADC = (ab3550_ctl[idx].value & 0x0F) - 1; // value = 0xX1 => MIC1, value = 0xX2 => MIC2 + ab3550_toggle_ADC(idx_ADC, 1); + ab3550_toggle_mic_amp(idx_ADC, 1); + } +} + + + +// Commit --------------------------------------------------------------------------------------------------------------- + +static void printCommit(int idx) { + printk(KERN_DEBUG "%s: %s committed to 0x%02x.\n", __func__, ab3550_ctl[idx].name, ab3550_ctl[idx].value); +} + +static bool commit_if_changed(enum enum_control idx_ctl) +{ + bool changed = false; + + if (ab3550_ctl[idx_ctl].changed) { + MASK_SET_REG(ab3550_registers[ab3550_ctl[idx_ctl].reg_idx].address, + ab3550_ctl[idx_ctl].reg_mask, + ab3550_ctl[idx_ctl].value << ab3550_ctl[idx_ctl].reg_shift); + ab3550_ctl[idx_ctl].changed = false; + changed = true; + + printCommit(idx_ctl); + } + + return changed; +} + static int commit_outamps_routing(void) { - unsigned long idx; - int adjust = 0; static const char *control_names[] = { "DAC1 Routing", "DAC2 Routing", "DAC3 Routing", "APGA1 Destination", "APGA2 Destination" }; - static const u8 outamp_adder_reg[] = { - LINE1_ADDER, LINE2_ADDER, SPKR_ADDER, - EAR_ADDER, AUXO1_ADDER, AUXO2_ADDER - }; + static const u8 outamp_0db_gain[] = { 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }; - for_each_bit(idx, control_changed, ARRAY_SIZE(ab3550_ctl)) { - if (strcmp(ab3550_ctl[idx].name, "DAC1 Routing") == 0 || - strcmp(ab3550_ctl[idx].name, "DAC2 Routing") == 0 || - strcmp(ab3550_ctl[idx].name, "DAC3 Routing") == 0 || - strcmp(ab3550_ctl[idx].name, "APGA1 Destination") == 0 || - strcmp(ab3550_ctl[idx].name, "APGA2 Destination") == 0 - ) { - adjust = 1; - } - } - if (adjust) { + struct ab3550_register* reg; + + printk(KERN_DEBUG "%s: Enter.\n", __func__); + + if ((get_control_changed_by_name("DAC1 Routing")) || + (get_control_changed_by_name("DAC2 Routing")) || + (get_control_changed_by_name("DAC3 Routing")) || + (get_control_changed_by_name("APGA1 Destination")) || + (get_control_changed_by_name("APGA2 Destination"))) { + int i; - for (i = 0; i < ARRAY_SIZE(outamp_reg); i++) { + + for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) { unsigned int connect = 0; int j; for (j = 0; j < ARRAY_SIZE(control_names); j++) { @@ -700,17 +708,19 @@ static int commit_outamps_routing(void) nothing is to be disconnected to it */ if (!connect) { u8 val = 0; - GET_REG(outamp_reg[i], &val); + reg = &(ab3550_registers[(int)outamp_indices[i]]); + GET_REG(reg->address, &val); if (! (val & 1 << outamp_pwr_shift[i])) continue; - gain_ramp(outamp_reg[i], 0, + gain_ramp(reg->address, 0, outamp_gain_shift[i], outamp_gain_mask[i]); - MASK_SET_REG(outamp_reg[i], + MASK_SET_REG(reg->address, outamp_pwr_mask[i], 0); continue; } - MASK_SET_REG(outamp_adder_reg[i], + reg = &(ab3550_registers[(int)outamp_adder_indices[i]]); + MASK_SET_REG(reg->address, DAC3_TO_ADDER_MASK | DAC2_TO_ADDER_MASK | DAC3_TO_ADDER_MASK, @@ -718,88 +728,270 @@ static int commit_outamps_routing(void) if (connect & 1 << 3) { /* Connect APGA1 */ MASK_SET_REG(APGA1_ADDER, - 1 << (ARRAY_SIZE(outamp_reg) - i), - 1 << (ARRAY_SIZE(outamp_reg) - i)); + 1 << (ARRAY_SIZE(outamp_indices) - i), + 1 << (ARRAY_SIZE(outamp_indices) - i)); } if (connect & 1 << 4) { /* Connect APGA2 */ MASK_SET_REG(APGA2_ADDER, - 1 << (ARRAY_SIZE(outamp_reg) - i), - 1 << (ARRAY_SIZE(outamp_reg) - i)); + 1 << (ARRAY_SIZE(outamp_indices) - i), + 1 << (ARRAY_SIZE(outamp_indices) - i)); } - MASK_SET_REG(outamp_reg[i], + reg = &(ab3550_registers[(int)outamp_indices[i]]); + MASK_SET_REG(reg->address, outamp_pwr_mask[i], 1 << outamp_pwr_shift[i]); - gain_ramp(outamp_reg[i], outamp_0db_gain[i], + gain_ramp(reg->address, outamp_0db_gain[i], outamp_gain_shift[i], outamp_gain_mask[i]); } + + for (i = 0; i < ARRAY_SIZE(control_names); i++) + ab3550_ctl[get_control_index(control_names[i])].changed = 0; } return 0; } -static int commit_gain(void) -{ - unsigned long idx; - for_each_bit(idx, control_changed, ARRAY_SIZE(ab3550_ctl)) { - if (strcmp(ab3550_ctl[idx].name, "LINE1 Gain") == 0 || - strcmp(ab3550_ctl[idx].name, "LINE2 Gain") == 0 || - strcmp(ab3550_ctl[idx].name, "SPKR Gain") == 0 || - strcmp(ab3550_ctl[idx].name, "EAR Gain") == 0 || - strcmp(ab3550_ctl[idx].name, "AUXO1 Gain") == 0 || - strcmp(ab3550_ctl[idx].name, "AUXO2 Gain") == 0 - ) { - /* Ramping is needed if powered up */ - int i = idx - get_control_index("LINE1 Gain"); - u8 val = GET_REG(outamp_reg[i], &val); - if (val & 1 << outamp_pwr_shift[i]) { - gain_ramp(outamp_reg[i], ab3550_ctl[idx].value, - outamp_gain_shift[i], - outamp_gain_mask[i]); - continue; - } - /* Othwise set directly */ - MASK_SET_REG(outamp_reg[i], outamp_gain_mask[i], +static int commit_rx_routing(void) +{ + int idx; + + printk(KERN_DEBUG "%s: Enter.\n", __func__); + + idx = get_control_index("RX2 Select"); + if (ab3550_ctl[idx].changed) { + u8 val = ab3550_ctl[idx].value; + if (val & RXx_PWR_MASK) + MASK_SET_REG(RX2, RXx_PWR_MASK, 0); + MASK_SET_REG(RX2, RX2_IF_SELECT_MASK, ab3550_ctl[idx].value); + if (val & RXx_PWR_MASK) + MASK_SET_REG(RX2, RXx_PWR_MASK, 1 << RXx_PWR_SHIFT); + ab3550_ctl[idx].changed = false; + + printCommit(idx); + } + + commit_outamps_routing(); + + return 0; +} + +static int commit_rx_gain(void) +{ + int i, idx_ctl; + //u8 val; + struct ab3550_register* reg; + + printk(KERN_DEBUG "%s: Enter.\n", __func__); + + for (i = 0; i < ARRAY_SIZE(outamp_gain_controls); i++) { + idx_ctl = (int)outamp_gain_controls[i]; + reg = &(ab3550_registers[ab3550_ctl[idx_ctl].reg_idx]); + if (ab3550_ctl[idx_ctl].changed) { + //val = GET_REG(reg->address, &val); + // if (ab3550_ctl[idx_ctl].value & 1 << outamp_pwr_shift[i]) // Ramping is needed if amp is powered up + // gain_ramp(outamp_reg[i], ab3550_ctl[idx_ctl].value, outamp_gain_shift[i], outamp_gain_mask[i]); + // else // Set directly if amp is not powered up + MASK_SET_REG(reg->address, outamp_gain_mask[i], ab3550_ctl[idx_ctl].value); + ab3550_ctl[idx_ctl].changed = false; + + printCommit(idx_ctl); + } + } + + idx_ctl = get_control_index("RX-DPGA1 Gain"); + if (ab3550_ctl[idx_ctl].changed) { + gain_ramp(RX1_DIGITAL_PGA, ab3550_ctl[idx_ctl].value, RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK); + ab3550_ctl[idx_ctl].changed = false; + + printCommit(idx_ctl); + + } + + idx_ctl = get_control_index("RX-DPGA2 Gain"); + if (ab3550_ctl[idx_ctl].changed) { + gain_ramp(RX2_DIGITAL_PGA, ab3550_ctl[idx_ctl].value, RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK); + ab3550_ctl[idx_ctl].changed = false; + + printCommit(idx_ctl); + } + + idx_ctl = get_control_index("RX-DPGA3 Gain"); + if (ab3550_ctl[idx_ctl].changed) { + gain_ramp(RX3_DIGITAL_PGA, ab3550_ctl[idx_ctl].value, RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK); + ab3550_ctl[idx_ctl].changed = false; + + printCommit(idx_ctl); + } + + return 0; +} + +static int commit_tx_routing(void) +{ + int idx; + + printk(KERN_DEBUG "%s: Enter.\n", __func__); + + idx = get_control_index("MIC1 Input Select"); + if (ab3550_ctl[idx].changed) { + MASK_SET_REG(MIC1_INPUT_SELECT, + MICxP1_SEL_MASK | MICxN1_SEL_MASK | MICxP2_SEL_MASK | MICxN2_SEL_MASK | LINEIN_SEL_MASK, + ab3550_ctl[idx].value); + MASK_SET_REG(MIC2_TO_MIC1, + MIC2P1_MIC1P_SEL_MASK | MIC2N1_MIC1N_SEL_MASK, + ab3550_ctl[idx].value >> 2); + ab3550_ctl[idx].changed = false; + + printCommit(idx); + } + + idx = get_control_index("MIC2 Input Select"); + if (ab3550_ctl[idx].changed) { + MASK_SET_REG(MIC2_INPUT_SELECT, + MICxP1_SEL_MASK | MICxN1_SEL_MASK | MICxP2_SEL_MASK | MICxN2_SEL_MASK | LINEIN_SEL_MASK, ab3550_ctl[idx].value); - } else if (strcmp(ab3550_ctl[idx].name, "RX-DPGA1 Gain") == 0) { - gain_ramp(RX1_DIGITAL_PGA, - get_control_value_by_name("RX-DPGA1 Gain"), - RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK); + ab3550_ctl[idx].changed = false; + + printCommit(idx); + } + + idx = get_control_index("I2S0 Input Select"); + if (ab3550_ctl[idx].changed) { + MASK_SET_REG(INTERFACE0_DATA, + I2Sx_L_DATA_MASK, + ab3550_ctl[idx].value << I2Sx_L_DATA_SHIFT); + MASK_SET_REG(INTERFACE0_DATA, + I2Sx_R_DATA_MASK, + ab3550_ctl[idx].value << I2Sx_R_DATA_SHIFT); + ab3550_ctl[idx].changed = false; + + printCommit(idx); + } + + idx = get_control_index("I2S1 Input Select"); + if (ab3550_ctl[idx].changed) { + MASK_SET_REG(INTERFACE1_DATA, + I2Sx_L_DATA_MASK, + ab3550_ctl[idx].value << I2Sx_L_DATA_SHIFT); + MASK_SET_REG(INTERFACE1_DATA, + I2Sx_R_DATA_MASK, + ab3550_ctl[idx].value << I2Sx_R_DATA_SHIFT); + ab3550_ctl[idx].changed = false; + + printCommit(idx); + } + + return 0; +} + +static int commit_tx_gain(void) +{ + printk(KERN_DEBUG "%s: Enter.\n", __func__); + + commit_if_changed(IDX_MIC1_Gain); + commit_if_changed(IDX_MIC2_Gain); + commit_if_changed(IDX_TX_DPGA1_Gain); + commit_if_changed(IDX_TX_DPGA2_Gain); + + return 0; +} + +static int commit_others(void) +{ + int idx; + + printk(KERN_DEBUG "%s: Enter.\n", __func__); + + idx = get_control_index("I2S0 Sample Rate"); + if (ab3550_ctl[idx].changed) { + // TODO? + ab3550_ctl[idx].changed = false; + } + + idx = get_control_index("I2S1 Sample Rate"); + if (ab3550_ctl[idx].changed) { + // TODO? + ab3550_ctl[idx].changed = false; + } + + idx = get_control_index("MIC1 MBias"); + if (ab3550_ctl[idx].changed) { + MASK_SET_REG(MIC_BIAS1, + MBIAS_PWR_MASK, + ab3550_ctl[idx].value << MBIAS_PWR_SHIFT); + ab3550_ctl[idx].changed = false; + } + + idx = get_control_index("MIC1 MBias"); + if (ab3550_ctl[idx].changed) { + MASK_SET_REG(MIC_BIAS2, + MBIAS_PWR_MASK, + ab3550_ctl[idx].value << MBIAS_PWR_SHIFT); + ab3550_ctl[idx].changed = false; + } - } else if (strcmp(ab3550_ctl[idx].name, "RX-DPGA2 Gain") == 0) { - gain_ramp(RX2_DIGITAL_PGA, - get_control_value_by_name("RX-DPGA2 Gain"), - RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK); + idx = get_control_index("TX1 HP Filter"); + if (ab3550_ctl[idx].changed) { + ab3550_set_ADC_filter(0, ab3550_ctl[idx].value); + ab3550_ctl[idx].changed = false; + } - } else if (strcmp(ab3550_ctl[idx].name, "RX-DPGA3 Gain") == 0) { - gain_ramp(RX3_DIGITAL_PGA, - get_control_value_by_name("RX-DPGA3 Gain"), - RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK); + idx = get_control_index("TX2 HP Filter"); + if (ab3550_ctl[idx].changed) { + ab3550_set_ADC_filter(1, ab3550_ctl[idx].value); + ab3550_ctl[idx].changed = false; + } + idx = get_control_index("Voice Call"); + if (ab3550_ctl[idx].changed) { + printk(KERN_DEBUG "%s: commiting 'Voice Call'.\n", __func__); + if (ab3550_ctl[idx].value) { + MASK_SET_REG(MIC_BIAS1, + MBIAS_PWR_MASK, + 1 << MBIAS_PWR_SHIFT); + MASK_SET_REG(MIC_BIAS2, + MBIAS_PWR_MASK, + 1 << MBIAS_PWR_SHIFT); + //turn_on_interface_tx(INTERFACE1); + MASK_SET_REG(INTERFACE1, + MASTER_GENx_PWR_MASK, + 1 << MASTER_GENx_PWR_SHIFT); + + printCommit(idx); } + ab3550_ctl[idx].changed = false; } + return 0; } +void commit_all(void) +{ + commit_rx_routing(); + commit_rx_gain(); + commit_tx_routing(); + commit_tx_gain(); + commit_others(); +} + static int ab3550_set_control_value(struct snd_soc_codec *codec, unsigned int ctl, unsigned int value) { - int ret = 0; - if (ctl >= ARRAY_SIZE(ab3550_ctl)) return -EINVAL; - if (ab3550_ctl[ctl].value == value) - return 0; - ab3550_ctl[ctl].value = value; - if (strcmp(ab3550_ctl[ctl].name, "Commit") != 0) - return 0; - /* Commit the changes */ - commit_outamps_routing(); - commit_gain(); - memset(control_changed, 0, sizeof(control_changed)); + if (strcmp(ab3550_ctl[ctl].name, "Commit") == 0) { + commit_all(); - return ret; + } else if (ab3550_ctl[ctl].value != value) { + printk(KERN_DEBUG "%s: Ctl %d changed from 0x%02x to 0x%02x\n", __func__, ctl, ab3550_ctl[ctl].value, value); + + ab3550_ctl[ctl].value = value; + ab3550_ctl[ctl].changed = true; + } + + return 0; } static int ab3550_add_controls(struct snd_soc_codec *codec) @@ -810,11 +1002,11 @@ static int ab3550_add_controls(struct snd_soc_codec *codec) for (i = 0; i < n; i++) { /* Initialize the control indice */ if (! ab3550_ctl[i].is_enum) { - ab3550_snd_controls[i].private_value |= i; + ((struct soc_mixer_control *) + ab3550_snd_controls[i].private_value)->reg = i; } else { - struct soc_enum *p = (struct soc_enum *) - ab3550_snd_controls[i].private_value; - p->reg = i; + ((struct soc_enum *) + ab3550_snd_controls[i].private_value)->reg = i; } printk(KERN_DEBUG "%s: %s.reg = %d\n", __func__, ab3550_ctl[i].name, i); @@ -831,6 +1023,232 @@ static int ab3550_add_controls(struct snd_soc_codec *codec) return err; } + + +// snd_soc_dai --------------------------------------------------------------------------------------------------------- + +static int ab3550_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai* dai) +{ + struct codec_dai_private *priv = (struct codec_dai_private*)dai->private_data; + + printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__); + /* TODO: consult the clock use count */ + + + // Configure registers on startup + if (atomic_read(&priv->substream_count) == 0) + ab3550_init_registers_startup(dai->id); + + atomic_inc(&priv->substream_count); + + // Configure registers for either playback or capture + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ab3550_init_registers_playback(dai->id); + else + ab3550_init_registers_capture(dai->id); + + ab3550_print_registers(substream->stream); + + return 0; +} + +static int ab3550_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, + struct snd_soc_dai* dai) +{ + + u8 val; + const char *reg_name; + + printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__); + + reg_name = dai->id == 0 ? "I2S0 Sample Rate" : "I2S1 Sample Rate"; + + switch(params_rate(hw_params)) + { + case 8000: + val=0; + break; + + case 16000: + val=1; + break; + + case 44100: + val=2; + break; + + case 48000: + val=3; + break; + + default: + return -EINVAL; + } + + ab3550_ctl[get_control_index(reg_name)].value = val; + + MASK_SET_REG(dai->id == 0 ? INTERFACE0 : INTERFACE1, + I2Sx_SR_MASK, + val); + + return 0; +} + +static void ab3550_pcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai* dai) +{ + int i; + u8 iface; + const char *mode_ctl_name; + printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__); + if (dai->id == 0) { + iface = INTERFACE0; + mode_ctl_name = "I2S0 Mode"; + } else { + iface = INTERFACE1; + mode_ctl_name = "I2S1 Mode"; + } + if (get_control_value_by_name(mode_ctl_name) == 0) { + struct codec_dai_private *priv = + (struct codec_dai_private *)dai->private_data; + if (atomic_dec_and_test(&priv->substream_count)) + MASK_SET_REG(iface, MASTER_GENx_PWR_MASK, 0); + } + /*TODO: consult the clock use count */ +/* MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 0); */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + struct ab3550_register* reg; + /* TODO: Turn off the outamps only used by this I2S */ + for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) { + reg = &(ab3550_registers[(int)outamp_indices[i]]); + MASK_SET_REG(reg->address, outamp_pwr_mask[i], 0); + } + } else { + /* TODO: Turn off the inactive components */ + } +} + +static int ab3550_set_dai_sysclk(struct snd_soc_dai* dai, int clk_id, + unsigned int freq, int dir) +{ + /* + u8 val; + const char *reg_name; + printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__); + switch(freq) { + case 8000: + val = 0; + break; + case 16000: + val = 1; + break; + case 44100: + val = 2; + break; + case 48000: + val = 3; + break; + default: + printk(KERN_ERR "%s: invalid frequency %u.\n", + __func__, freq); + return -EINVAL; + } + + reg_name = dai->id == 0 ? "I2S0 Sample Rate" : "I2S1 Sample Rate"; + ab3550_ctl[get_control_index(reg_name)].value = val; + return MASK_SET_REG(dai->id == 0 ? INTERFACE0 : INTERFACE1, + I2Sx_SR_MASK, + val << I2Sx_SR_SHIFT); + */ + return 0; +} + +static int ab3550_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + u8 iface = (codec_dai->id == 0) ? INTERFACE0 : INTERFACE1; + u8 val=0; + printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__); + + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) { + + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: + val |= 1 << I2Sx_MODE_SHIFT; + break; + + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: + break; + + default: + printk(KERN_WARNING "AB3550_dai: unsupported DAI format 0x%x\n",fmt); + return -EINVAL; + } + + return MASK_SET_REG(iface,I2Sx_MODE_MASK | I2Sx_WORDLENGTH_MASK, val); +} + +struct snd_soc_dai ab3550_codec_dai[] = { + { + .name = "ab3550_0", + .id = 0, + .playback = { + .stream_name = "ab3550_0", + .channels_min = 2, + .channels_max = 2, + .rates = AB3550_SUPPORTED_RATE, + .formats = AB3550_SUPPORTED_FMT, + }, + .capture = { + .stream_name = "ab3550_0", + .channels_min = 2, + .channels_max = 2, + .rates = AB3550_SUPPORTED_RATE, + .formats = AB3550_SUPPORTED_FMT, + }, + .ops = { + .prepare = ab3550_pcm_prepare, + .hw_params = ab3550_pcm_hw_params, + .shutdown = ab3550_pcm_shutdown, + .set_sysclk = ab3550_set_dai_sysclk, + .set_fmt = ab3550_set_dai_fmt + }, + .private_data = &privates[0] + }, + { + .name = "ab3550_1", + .id = 1, + .playback = { + .stream_name = "ab3550_1", + .channels_min = 2, + .channels_max = 2, + .rates = AB3550_SUPPORTED_RATE, + .formats = AB3550_SUPPORTED_FMT, + }, + .capture = { + .stream_name = "ab3550_1", + .channels_min = 2, + .channels_max = 2, + .rates = AB3550_SUPPORTED_RATE, + .formats = AB3550_SUPPORTED_FMT, + }, + .ops = { + .prepare = ab3550_pcm_prepare, + .hw_params = ab3550_pcm_hw_params, + .shutdown = ab3550_pcm_shutdown, + .set_sysclk = ab3550_set_dai_sysclk, + .set_fmt = ab3550_set_dai_fmt + }, + .private_data = &privates[1] + } +}; +EXPORT_SYMBOL_GPL(ab3550_codec_dai); + + + + + +// snd_soc_codec_device --------------------------------------------------------------------------------------------------- + static int ab3550_codec_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); @@ -843,7 +1261,6 @@ static int ab3550_codec_probe(struct platform_device *pdev) return -ENOMEM; codec->name = "AB3550"; codec->owner = THIS_MODULE; - /* TODO: add codec dai */ codec->dai = ab3550_codec_dai; codec->num_dai = 2; codec->read = ab3550_get_control_value; @@ -911,38 +1328,31 @@ struct snd_soc_codec_device soc_codec_dev_ab3550 = { EXPORT_SYMBOL_GPL(soc_codec_dev_ab3550); -/* struct snd_soc_dai ab3550_codec_dai = { */ -/* .name = "AB3550", */ -/* .playback = { */ -/* .stream_name = "Playback", */ -/* .channels_min = 1, */ -/* .channels_max = 2, */ -/* .rates = AB3550_RATES, */ -/* .formats = AB3550_FORMATS,}, */ -/* .capture = { */ -/* .stream_name = "Capture", */ -/* .channels_min = 1, */ -/* .channels_max = 2, */ -/* .rates = AB3550_RATES, */ -/* .formats = AB3550_FORMATS,}, */ -/* .ops = { */ -/* .prepare = ab3550_pcm_prepare, */ -/* .hw_params = ab3550_hw_params, */ -/* .shutdown = ab3550_shutdown, */ -/* }, */ -/* .dai_ops = { */ -/* .digital_mute = ab3550_mute, */ -/* .set_sysclk = ab3550_set_dai_sysclk, */ -/* .set_fmt = ab3550_set_dai_fmt, */ -/* } */ -/* }; */ -/* EXPORT_SYMBOL_GPL(ab3550_codec_dai); */ static int ab3550_platform_probe(struct platform_device *pdev) { int ret = 0; - + u8 reg, val; + unsigned long i; printk(KERN_DEBUG "%s invoked with pdev = %p.\n", __func__, pdev); ab3550_dev = &pdev->dev; + + /* Initialize the codec registers */ + for (reg = MIC_BIAS1; reg < INTERFACE_SWAP; reg++) { + SET_REG(reg, 0); + } + MASK_SET_REG(CLOCK, CLOCK_REF_SELECT_MASK, + 1 << CLOCK_REF_SELECT_SHIFT); + MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, + 1 << CLOCK_ENABLE_SHIFT); + val = GET_REG(CLOCK, &val); + printk(KERN_DEBUG "%s: CLOCK = 0x%02x.\n", __func__, val); + + // Init all registers from default control values + for (i = 0; i < ARRAY_SIZE(ab3550_ctl); i++) + if (ab3550_ctl[i].value) + ab3550_ctl[i].changed = true; + commit_all(); + return ret; } @@ -950,6 +1360,7 @@ static int ab3550_platform_remove(struct platform_device *pdev) { int ret; printk(KERN_DEBUG "%s called.\n", __func__); + MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 0); if ((ret = soc_codec_dev_ab3550.remove(NULL))) printk(KERN_ERR "%s failed with error code %d.\n", __func__, ret); @@ -991,18 +1402,47 @@ static struct platform_driver ab3550_platform_driver = { static int __devinit ab3550_init(void) { - int ret; + int i, ret1; + int ret2 = 0; + printk(KERN_DEBUG "%s called.\n", __func__); - ret= platform_driver_register(&ab3550_platform_driver); - printk(KERN_DEBUG "%s finished with error code %d.\n", - __func__, ret); - return ret; + + // Register codec platform driver. + ret1 = platform_driver_register(&ab3550_platform_driver); + if (ret1 < 0) { + printk(KERN_DEBUG "%s: Error %d: Failed to register codec platform driver.\n", __func__, ret1); + ret2 = -1; + } + + // Register codec-dai. + printk(KERN_DEBUG "%s: Register codec-dai.\n", __func__); + + for (i = 0; i < ARRAY_SIZE(ab3550_codec_dai); i++) { + printk(KERN_DEBUG "%s: Register codec-dai %d.\n", __func__, i); + ret1 = snd_soc_register_dai(&ab3550_codec_dai[i]); + if (ret1 < 0) { + printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to register codec-dai %d.\n", i); + ret2 = -1; + } + } + + return ret2; } static void ab3550_exit(void) { - printk(KERN_DEBUG "%s called.\n", __func__); + int i; + + printk(KERN_DEBUG "u8500_ab3550_init: Enter.\n"); + + // Register codec platform driver. + printk(KERN_DEBUG "%s: Un-register codec platform driver.\n", __func__); platform_driver_unregister(&ab3550_platform_driver); + + for (i = 0; i < ARRAY_SIZE(ab3550_codec_dai); i++) { + printk(KERN_DEBUG "%s: Un-register codec-dai %d.\n", __func__, i); + snd_soc_unregister_dai(&ab3550_codec_dai[i]); + } } module_init(ab3550_init); |