blob: de51ca963c2f97474552f33cbbadeaeb2f5a3d16 [file] [log] [blame]
/*
* Copyright (C) ST-Ericsson SA 2010
*
* 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:
*
* 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.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <linux/mfd/abx500.h>
#include <linux/bitops.h>
#include <asm/atomic.h>
#include "ab3550.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)); \
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)); \
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))
static enum enum_register outamp_indices[] = {
IDX_LINE1, IDX_LINE2, IDX_SPKR, IDX_EAR, IDX_AUXO1, IDX_AUXO2
};
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 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,
AUXOx_GAIN_SHIFT, AUXOx_GAIN_SHIFT
};
static const u8 outamp_gain_mask[] = {
LINEx_GAIN_MASK, LINEx_GAIN_MASK,
SPKR_GAIN_MASK, EAR_GAIN_MASK,
AUXOx_GAIN_MASK, AUXOx_GAIN_MASK
};
static const u8 outamp_pwr_shift[] = {
LINEx_PWR_SHIFT, LINEx_PWR_SHIFT,
SPKR_PWR_SHIFT, EAR_PWR_SHIFT,
AUXOx_PWR_SHIFT, AUXOx_PWR_SHIFT
};
static const u8 outamp_pwr_mask[] = {
LINEx_PWR_MASK, LINEx_PWR_MASK,
SPKR_PWR_MASK, EAR_PWR_MASK,
AUXOx_PWR_MASK, AUXOx_PWR_MASK
};
struct codec_dai_private {
atomic_t substream_count;
} privates[] = {
{ATOMIC_INIT(0)},
{ATOMIC_INIT(0)},
};
/* TODO: Add a clock use count.
Turn off the clock is there is no audio activity in the system.
*/
// Controls -----------------------------------------------------------------------------------------------------------------------
struct ab3550_control {
const char *name;
u8 value;
enum enum_register reg_idx;
u8 reg_mask;
u8 reg_shift;
bool is_enum;
bool changed;
};
struct ab3550_control ab3550_ctl[] = {
// 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 changed_controls[BIT_WORD(ARRAY_SIZE(ab3550_ctl)) + 1];
static const char *enum_rx2_select[] = {"I2S0", "I2S1"};
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", 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 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", 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),
/* 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),
/* 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),
/* RX Properties */
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 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", soc_enum_st1_hp_filter),
SOC_ENUM( "ST2 HP Filter", soc_enum_st2_hp_filter),
/* I2S Interface Properties */
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( "Voice Call", 0, 0, 1, 0),
SOC_SINGLE( "Commit", 0, 0, 1, 0)
};
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)
{
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;
}
static unsigned int get_control_index(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;
}
return i;
}
static unsigned int get_control_value_by_name(const char *name)
{
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;
int ret = 0;
// printk(KERN_DEBUG "%s invoked.\n", __func__);
// 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 :-) */
// msleep(1);
// ret = MASK_SET_REG(reg, mask, (curr += step) << shift);
// }
ret = MASK_SET_REG(reg, mask, target << shift);
return ret;
}
static void ab3550_print_registers(int stream_dir)
{
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 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 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)
{
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_toggle_output_adder(int idx_adder, u8 val)
{
int i;
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 void ab3550_toggle_ADC(int idx_ADC, u8 val)
{
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));
printk(KERN_DEBUG "DORIANO: ADC%d turned %s.\n", idx_ADC + 1, val == 0 ? "off" : "on");
}
static void ab3550_set_ADC_filter(int idx_ADC, u8 val)
{
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)
{
snd_soc_dapm_new_controls(codec, ab3550_dapm_widgets,
ARRAY_SIZE(ab3550_dapm_widgets));
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(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)
{
static const char *control_names[] = {
"DAC1 Routing", "DAC2 Routing", "DAC3 Routing",
"APGA1 Destination", "APGA2 Destination"
};
static const u8 outamp_0db_gain[] = {
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C
};
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_indices); i++) {
unsigned int connect = 0;
int j;
for (j = 0; j < ARRAY_SIZE(control_names); j++) {
if (get_control_value_by_name(control_names[j])
& 1 << i) {
connect |= 1 << j;
}
}
/* Ramp down the amplifier if
nothing is to be disconnected to it */
if (!connect) {
u8 val = 0;
reg = &(ab3550_registers[(int)outamp_indices[i]]);
GET_REG(reg->address, &val);
if (! (val & 1 << outamp_pwr_shift[i]))
continue;
gain_ramp(reg->address, 0,
outamp_gain_shift[i],
outamp_gain_mask[i]);
MASK_SET_REG(reg->address,
outamp_pwr_mask[i], 0);
continue;
}
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,
connect);
if (connect & 1 << 3) {
/* Connect APGA1 */
MASK_SET_REG(APGA1_ADDER,
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_indices) - i),
1 << (ARRAY_SIZE(outamp_indices) - i));
}
reg = &(ab3550_registers[(int)outamp_indices[i]]);
MASK_SET_REG(reg->address,
outamp_pwr_mask[i],
1 << outamp_pwr_shift[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_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);
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;
}
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;
}
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)
{
if (ctl >= ARRAY_SIZE(ab3550_ctl))
return -EINVAL;
if (strcmp(ab3550_ctl[ctl].name, "Commit") == 0) {
commit_all();
} 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)
{
int err = 0, i, n = ARRAY_SIZE(ab3550_snd_controls);
printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
for (i = 0; i < n; i++) {
/* Initialize the control indice */
if (! ab3550_ctl[i].is_enum) {
((struct soc_mixer_control *)
ab3550_snd_controls[i].private_value)->reg = i;
} else {
((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);
err = snd_ctl_add(codec->card,
snd_ctl_new1(&ab3550_snd_controls[i], codec));
if (err < 0) {
printk(KERN_DEBUG "%s failed to add control No.%d of %d.\n",
__func__, i, n);
return err;
}
}
/* initialize control values */
printk(KERN_DEBUG "%s: %s to return %d.\n", __FILE__, __func__, err);
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);
struct snd_soc_codec *codec;
int ret;
printk(KERN_INFO "%s called. pdev = %p.\n", __func__, pdev);
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
codec->name = "AB3550";
codec->owner = THIS_MODULE;
codec->dai = ab3550_codec_dai;
codec->num_dai = 2;
codec->read = ab3550_get_control_value;
codec->write = ab3550_set_control_value;
codec->reg_cache_size = ARRAY_SIZE(ab3550_ctl);
codec->reg_cache = kmemdup(ab3550_ctl, sizeof(ab3550_ctl), GFP_KERNEL);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
socdev->codec = codec;
mutex_init(&codec->mutex);
printk(KERN_DEBUG "%s: %s: snd_soc_new_pcms to be called.\n",
__FILE__, __func__);
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "%s: Failed to create a new card "
"and new PCMs. error %d\n", __func__, ret);
goto err1;
}
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "%s: failed to register card. error %d.\n",
__func__, ret);
goto err2;
}
/* Add controls */
if (ab3550_add_controls(socdev->codec) < 0)
goto err2;
ab3550_add_widgets(codec);
return 0;
err2:
snd_soc_free_pcms(socdev);
err1:
kfree(codec);
return ret;
}
static int ab3550_codec_remove(struct platform_device *pdev)
{
printk(KERN_DEBUG "%s : pdev=%p.\n", __func__, pdev);
return 0;
}
static int ab3550_codec_suspend(struct platform_device *pdev,
pm_message_t state)
{
printk(KERN_DEBUG "%s : pdev=%p.\n", __func__, pdev);
return 0;
}
static int ab3550_codec_resume(struct platform_device *pdev)
{
printk(KERN_DEBUG "%s : pdev=%p.\n", __func__, pdev);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_ab3550 = {
.probe = ab3550_codec_probe,
.remove = ab3550_codec_remove,
.suspend = ab3550_codec_suspend,
.resume = ab3550_codec_resume
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ab3550);
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;
}
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);
return ret;
}
static int ab3550_platform_suspend(struct platform_device *pdev,
pm_message_t state)
{
int ret;
printk(KERN_DEBUG "%s called.\n", __func__);
if ((ret = soc_codec_dev_ab3550.suspend(NULL, state)))
printk(KERN_ERR "%s failed with error code %d.\n",
__func__, ret);
return ret;
}
static int ab3550_platform_resume(struct platform_device *pdev)
{
int ret;
printk(KERN_DEBUG "%s called.\n", __func__);
if ((ret = soc_codec_dev_ab3550.resume(NULL)))
printk(KERN_ERR "%s failed with error code %d.\n",
__func__, ret);
return ret;
}
static struct platform_driver ab3550_platform_driver = {
.driver = {
.name = "ab3550-codec",
.owner = THIS_MODULE,
},
.probe = ab3550_platform_probe,
.remove = ab3550_platform_remove,
.suspend = ab3550_platform_suspend,
.resume = ab3550_platform_resume,
};
static int __devinit ab3550_init(void)
{
int i, ret1;
int ret2 = 0;
printk(KERN_DEBUG "%s called.\n", __func__);
// 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)
{
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);
module_exit(ab3550_exit);
MODULE_DESCRIPTION("AB3550 Codec driver");
MODULE_AUTHOR("Xie Xiaolei, xie.xiaolei@stericsson.com, www.stericsson.com");
MODULE_LICENSE("GPL");