diff options
author | Xie Xiaolei <xie.xiaolei@stericsson.com> | 2011-01-13 10:11:24 +0100 |
---|---|---|
committer | Robert Marklund <robert.marklund@stericsson.com> | 2011-05-13 10:40:16 +0200 |
commit | 6efe705e3ef2f14c85e1775d97c131870b0297bb (patch) | |
tree | e18475dd0ce1c6b3cc7d8030dedf02dfd801e863 /sound | |
parent | 04ad2f1747f36b3733be2545d7f3477b6435d0b3 (diff) |
sound/soc: Add the AB5500 codec driver and correct the MSP internal clock frequency.
ST-Ericsson ID: WP 259100
Signed-off-by: Xie Xiaolei <xie.xiaolei@stericsson.com>
Change-Id: Ia2ecfb380d9f59f0ef588888745269160e914d9e
Change-Id: I77ebfb16d0c22c62695650ff20bd7087e04ca8f9
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/11424
Reviewed-by: Ola LILJA2 <ola.o.lilja@stericsson.com>
Tested-by: Xie XIAOLEI <xie.xiaolei@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/Kconfig | 6 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/ab5500.c | 1632 | ||||
-rw-r--r-- | sound/soc/codecs/ab5500.h | 399 | ||||
-rw-r--r-- | sound/soc/ux500/Kconfig | 10 | ||||
-rw-r--r-- | sound/soc/ux500/Makefile | 28 | ||||
-rw-r--r-- | sound/soc/ux500/u5500.c | 119 | ||||
-rw-r--r-- | sound/soc/ux500/ux500_ab5500.c | 57 | ||||
-rw-r--r-- | sound/soc/ux500/ux500_ab5500.h | 24 | ||||
-rw-r--r-- | sound/soc/ux500/ux500_msp_dai.c | 2 | ||||
-rw-r--r-- | sound/soc/ux500/ux500_msp_dai.h | 7 | ||||
-rw-r--r-- | sound/soc/ux500/ux500_pcm.c | 4 |
12 files changed, 2277 insertions, 13 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0aee62c984f..8301a761bff 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -85,9 +85,10 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9712 if SND_SOC_AC97_BUS select SND_SOC_WM9713 if SND_SOC_AC97_BUS select SND_SOC_AB3550 + select SND_SOC_AB5500 select SND_SOC_CG29XX select SND_SOC_AV8100 - help + help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine driver. Selecting this option will allow these drivers to be built @@ -331,6 +332,9 @@ config SND_SOC_WM9713 config SND_SOC_AB3550 tristate +config SND_SOC_AB5500 + tristate + config SND_SOC_CG29XX tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 6357cf959a1..0d064c54d35 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -1,5 +1,6 @@ snd-soc-88pm860x-objs := 88pm860x-codec.o snd-soc-ab3550-objs := ab3550.o +snd-soc-ab5500-objs := ab5500.o snd-soc-ac97-objs := ac97.o snd-soc-ad1836-objs := ad1836.o snd-soc-ad193x-objs := ad193x.o @@ -81,6 +82,7 @@ snd-soc-wm2000-objs := wm2000.o snd-soc-wm9090-objs := wm9090.o obj-$(CONFIG_SND_SOC_AB3550) += snd-soc-ab3550.o +obj-$(CONFIG_SND_SOC_AB5500) += snd-soc-ab5500.o obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o obj-$(CONFIG_SND_SOC_AB3550) += snd-soc-ab3550.o obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o diff --git a/sound/soc/codecs/ab5500.c b/sound/soc/codecs/ab5500.c new file mode 100644 index 00000000000..e5f0ecce3a0 --- /dev/null +++ b/sound/soc/codecs/ab5500.c @@ -0,0 +1,1632 @@ +/* + * 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 <linux/mfd/abx500.h> +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/mutex.h> +#include <asm/atomic.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 "ab5500.h" + +#define AB5500_CODEC_DEBUG 0 + +/* codec private data */ +struct ab5500_codec_dai_data { +}; + +static struct device *ab5500_dev; + +static u8 virtual_regs[] = { + 0, 0, 0, 0, 0 +}; + +#define set_reg(reg, val) mask_set_reg((reg), 0xff, (val)) + +static void mask_set_reg(u8 reg, u8 mask, u8 val) +{ + u8 newval = mask & val; + u8 oldval, diff; + + if (!ab5500_dev) { + pr_err("%s: The AB5500 codec driver not initialized.\n", + __func__); + return; + } + if (reg < AB5500_FIRST_REG) + return; + if (reg <= AB5500_LAST_REG) { + abx500_mask_and_set_register_interruptible( + ab5500_dev, AB5500_BANK_AUDIO_HEADSETUSB, + reg, mask, val); + return; + } + if (reg - AB5500_LAST_REG - 1 >= ARRAY_SIZE(virtual_regs)) + return; + + /* treatment of virtual registers follows */ + oldval = virtual_regs[reg - AB5500_LAST_REG - 1]; + diff = (val ^ oldval) & mask; + if (!diff) + return; + + switch (reg) { + case AB5500_VIRTUAL_REG3: + if ((diff & (1 << SPKR1_PWR_SHIFT))) { + if ((val & (1 << SPKR1_PWR_SHIFT)) == 0) + mask_set_reg(SPKR1, SPKRx_PWR_MASK, 0); + else + switch (oldval & SPKR1_MODE_MASK) { + case 0: + mask_set_reg(SPKR1, SPKRx_PWR_MASK, + SPKRx_PWR_VBR_VALUE); + break; + case 1: + mask_set_reg(SPKR1, SPKRx_PWR_MASK, + SPKRx_PWR_CLS_D_VALUE); + break; + case 2: + mask_set_reg(SPKR1, SPKRx_PWR_MASK, + SPKRx_PWR_CLS_AB_VALUE); + break; + } + } + if ((diff & (1 << SPKR2_PWR_SHIFT))) { + if ((val & (1 << SPKR2_PWR_SHIFT)) == 0) + mask_set_reg(SPKR2, SPKRx_PWR_MASK, 0); + else + switch (oldval & SPKR2_MODE_MASK) { + case 0: + mask_set_reg(SPKR2, SPKRx_PWR_MASK, + SPKRx_PWR_VBR_VALUE); + break; + case 1: + mask_set_reg(SPKR2, SPKRx_PWR_MASK, + SPKRx_PWR_CLS_D_VALUE); + break; + } + } + + break; + case AB5500_VIRTUAL_REG4: + ; + /* configure PWMCTRL_SPKR1, PWMCTRL_SPKR2, etc. */ + } + virtual_regs[reg - AB5500_LAST_REG - 1] &= ~mask; + virtual_regs[reg - AB5500_LAST_REG - 1] |= newval; +} + +static u8 read_reg(u8 reg) +{ + if (!ab5500_dev) { + pr_err("%s: The AB5500 codec driver not initialized.\n", + __func__); + return 0; + } + if (reg < AB5500_FIRST_REG) + return 0; + else if (reg <= AB5500_LAST_REG) { + u8 val; + abx500_get_register_interruptible( + ab5500_dev, AB5500_BANK_AUDIO_HEADSETUSB, reg, &val); + return val; + } else if (reg - AB5500_LAST_REG - 1 < ARRAY_SIZE(virtual_regs)) + return virtual_regs[reg - AB5500_LAST_REG - 1]; + dev_warn(ab5500_dev, "%s: out-of-scope reigster %u.\n", + __func__, reg); + return 0; +} + +/* Components that can be powered up/down */ +enum enum_widget { + widget_ear = 0, + + widget_auxo1, + widget_auxo2, + widget_auxo3, + widget_auxo4, + + widget_spkr1, + widget_spkr2, + widget_spkr1_adder, + widget_spkr2_adder, + + widget_pwm_spkr1, + widget_pwm_spkr2, + + widget_pwm_spkr1n, + widget_pwm_spkr1p, + widget_pwm_spkr2n, + widget_pwm_spkr2p, + + widget_line1, + widget_line2, + + widget_dac1, + widget_dac2, + widget_dac3, + + widget_rx1, + widget_rx2, + widget_rx3, + + widget_mic1, + widget_mic2, + + widget_micbias1, + widget_micbias2, + + widget_apga1, + widget_apga2, + + widget_tx1, + widget_tx2, + + widget_adc1, + widget_adc2, + + widget_if0_dld_l, + widget_if0_dld_r, + widget_if0_uld_l, + widget_if0_uld_r, + widget_if1_dld_l, + widget_if1_dld_r, + widget_if1_uld_l, + widget_if1_uld_r, + + widget_mic1p1, + widget_mic1n1, + widget_mic1p2, + widget_mic1n2, + + widget_mic2p1, + widget_mic2n1, + widget_mic2p2, + widget_mic2n2, + + widget_clock, + + number_of_widgets +}; + +#if AB5500_CODEC_DEBUG +static const char *widget_names[] = { + "EAR", "AUXO1", "AUXO2", "AUXO3", "AUXO4", + "SPKR1", "SPKR2", "SPKR1_ADDER", "SPKR2_ADDER", + "PWM_SPKR1", "PWM_SPKR2", + "PWM_SPKR1N", "PWM_SPKR1P", + "PWM_SPKR2N", "PWM_SPKR2P", + "LINE1", "LINE2", + "DAC1", "DAC2", "DAC3", + "RX1", "RX2", "RX3", + "MIC1", "MIC2", + "MIC-BIAS1", "MIC-BIAS2", + "APGA1", "APGA2", + "TX1", "TX2", + "ADC1", "ADC2", + "IF0-DLD-L", "IF0-DLD-R", "IF0-ULD-L", "IF0-ULD-R", + "IF1-DLD-L", "IF1-DLD-R", "IF1-ULD-L", "IF1-ULD-R", + "MIC1P1", "MIC1N1", "MIC1P2", "MIC1N2", + "MIC2P1", "MIC2N1", "MIC2P2", "MIC2N2", + "CLOCK" +}; +#endif + +struct widget_pm { + enum enum_widget widget; + u8 reg; + u8 shift; + + unsigned long source_list[BIT_WORD(number_of_widgets) + 1]; + unsigned long sink_list[BIT_WORD(number_of_widgets) + 1]; +}; + +static struct widget_pm widget_pm_array[] = { + {.widget = widget_ear, .reg = EAR_PWR, .shift = EAR_PWR_SHIFT}, + + {.widget = widget_auxo1, .reg = AUXO1, .shift = AUXOx_PWR_SHIFT}, + {.widget = widget_auxo2, .reg = AUXO2, .shift = AUXOx_PWR_SHIFT}, + {.widget = widget_auxo3, .reg = AUXO3, .shift = AUXOx_PWR_SHIFT}, + {.widget = widget_auxo4, .reg = AUXO4, .shift = AUXOx_PWR_SHIFT}, + + {.widget = widget_spkr1, .reg = AB5500_VIRTUAL_REG3, + .shift = SPKR1_PWR_SHIFT}, + {.widget = widget_spkr2, .reg = AB5500_VIRTUAL_REG3, + .shift = SPKR2_PWR_SHIFT}, + + {.widget = widget_spkr1_adder, .reg = AB5500_VIRTUAL_REG3, + .shift = SPKR1_ADDER_PWR_SHIFT}, + {.widget = widget_spkr2_adder, .reg = AB5500_VIRTUAL_REG3, + .shift = SPKR2_ADDER_PWR_SHIFT}, + + {.widget = widget_pwm_spkr1, .reg = AB5500_VIRTUAL_REG4, + .shift = PWM_SPKR1_PWR_SHIFT}, + {.widget = widget_pwm_spkr2, .reg = AB5500_VIRTUAL_REG4, + .shift = PWM_SPKR2_PWR_SHIFT}, + + {.widget = widget_pwm_spkr1n, .reg = AB5500_VIRTUAL_REG4, + .shift = PWM_SPKR1N_PWR_SHIFT}, + {.widget = widget_pwm_spkr1p, .reg = AB5500_VIRTUAL_REG4, + .shift = PWM_SPKR1P_PWR_SHIFT}, + + {.widget = widget_pwm_spkr2n, .reg = AB5500_VIRTUAL_REG4, + .shift = PWM_SPKR2N_PWR_SHIFT}, + {.widget = widget_pwm_spkr2p, .reg = AB5500_VIRTUAL_REG4, + .shift = PWM_SPKR2P_PWR_SHIFT}, + + + {.widget = widget_line1, .reg = LINE1, .shift = LINEx_PWR_SHIFT}, + {.widget = widget_line2, .reg = LINE2, .shift = LINEx_PWR_SHIFT}, + + {.widget = widget_dac1, .reg = RX1, .shift = DACx_PWR_SHIFT}, + {.widget = widget_dac2, .reg = RX2, .shift = DACx_PWR_SHIFT}, + {.widget = widget_dac3, .reg = RX3, .shift = DACx_PWR_SHIFT}, + + {.widget = widget_rx1, .reg = RX1, .shift = RXx_PWR_SHIFT}, + {.widget = widget_rx2, .reg = RX2, .shift = RXx_PWR_SHIFT}, + {.widget = widget_rx3, .reg = RX3, .shift = RXx_PWR_SHIFT}, + + {.widget = widget_mic1, .reg = MIC1_GAIN, .shift = MICx_PWR_SHIFT}, + {.widget = widget_mic2, .reg = MIC2_GAIN, .shift = MICx_PWR_SHIFT}, + + {.widget = widget_micbias1, .reg = MIC_BIAS1, + .shift = MBIASx_PWR_SHIFT}, + {.widget = widget_micbias2, .reg = MIC_BIAS2, + .shift = MBIASx_PWR_SHIFT}, + + {.widget = widget_apga1, .reg = ANALOG_LOOP_PGA1, + .shift = APGAx_PWR_SHIFT}, + {.widget = widget_apga2, .reg = ANALOG_LOOP_PGA2, + .shift = APGAx_PWR_SHIFT}, + + {.widget = widget_tx1, .reg = TX1, .shift = TXx_PWR_SHIFT}, + {.widget = widget_tx2, .reg = TX2, .shift = TXx_PWR_SHIFT}, + + {.widget = widget_adc1, .reg = TX1, .shift = ADCx_PWR_SHIFT}, + {.widget = widget_adc2, .reg = TX2, .shift = ADCx_PWR_SHIFT}, + + {.widget = widget_if0_dld_l, .reg = AB5500_VIRTUAL_REG1, + .shift = IF0_DLD_L_PW_SHIFT}, + {.widget = widget_if0_dld_r, .reg = AB5500_VIRTUAL_REG1, + .shift = IF0_DLD_R_PW_SHIFT}, + {.widget = widget_if0_uld_l, .reg = AB5500_VIRTUAL_REG1, + .shift = IF0_ULD_L_PW_SHIFT}, + {.widget = widget_if0_uld_r, .reg = AB5500_VIRTUAL_REG1, + .shift = IF0_ULD_R_PW_SHIFT}, + + {.widget = widget_if1_dld_l, .reg = AB5500_VIRTUAL_REG1, + .shift = IF1_DLD_L_PW_SHIFT}, + {.widget = widget_if1_dld_r, .reg = AB5500_VIRTUAL_REG1, + .shift = IF1_DLD_R_PW_SHIFT}, + {.widget = widget_if1_uld_l, .reg = AB5500_VIRTUAL_REG1, + .shift = IF1_ULD_L_PW_SHIFT}, + {.widget = widget_if1_uld_r, .reg = AB5500_VIRTUAL_REG1, + .shift = IF1_ULD_R_PW_SHIFT}, + + {.widget = widget_mic1p1, .reg = AB5500_VIRTUAL_REG2, + .shift = MIC1P1_PW_SHIFT}, + {.widget = widget_mic1n1, .reg = AB5500_VIRTUAL_REG2, + .shift = MIC1N1_PW_SHIFT}, + {.widget = widget_mic1p2, .reg = AB5500_VIRTUAL_REG2, + .shift = MIC1P2_PW_SHIFT}, + {.widget = widget_mic1n2, .reg = AB5500_VIRTUAL_REG2, + .shift = MIC1N2_PW_SHIFT}, + + {.widget = widget_mic2p1, .reg = AB5500_VIRTUAL_REG2, + .shift = MIC2P1_PW_SHIFT}, + {.widget = widget_mic2n1, .reg = AB5500_VIRTUAL_REG2, + .shift = MIC2N1_PW_SHIFT}, + {.widget = widget_mic2p2, .reg = AB5500_VIRTUAL_REG2, + .shift = MIC2P2_PW_SHIFT}, + {.widget = widget_mic2n2, .reg = AB5500_VIRTUAL_REG2, + .shift = MIC2N2_PW_SHIFT}, + + {.widget = widget_clock, .reg = CLOCK, .shift = CLOCK_ENABLE_SHIFT}, +}; + +DEFINE_MUTEX(ab5500_pm_mutex); + +static struct { + enum enum_widget stack[number_of_widgets]; + int p; +} pm_stack; + +#define pm_stack_as_bitmap ({ \ + unsigned long bitmap[BIT_WORD(number_of_widgets) + 1]; \ + int i; \ + memset(bitmap, 0, sizeof(bitmap)); \ + for (i = 0; i < pm_stack.p; i++) { \ + set_bit(pm_stack.stack[i], bitmap); \ + } \ + bitmap; \ + }) + +/* These are only meant to meet the obligations of DAPM */ +static const struct snd_soc_dapm_widget ab5500_dapm_widgets[] = { +}; + +static const struct snd_soc_dapm_route intercon[] = { +}; + + +struct codec_dai_private { + unsigned active_flags; +} privates[] = { + {0}, + {0} +}; + +static const char *enum_rx_input_select[] = { + "Mute", "TX1", "TX2", "I2S0_DLD_L", + "I2S0_DLD_R", "I2S1_DLD_L", "I2S1_DLD_R" +}; + +static const char *enum_i2s_uld_select[] = { + "Mute", "TX1", "TX2", "I2S0_DLD_L", + "I2S0_DLD_R", "I2S1_DLD_L", "I2S1_DLD_R", "tri-state" +}; +static const char *enum_apga1_source[] = {"LINEIN1", "MIC1", "MIC2", "None"}; +static const char *enum_apga2_source[] = {"LINEIN2", "MIC1", "MIC2", "None"}; +static const char *enum_rx_side_tone[] = {"TX1", "TX2"}; +static const char *enum_dac_power_mode[] = {"100%", "75%", "55%"}; +static const char *enum_ear_power_mode[] = {"100%", "70%", "50%"}; +static const char *enum_auxo_power_mode[] = { + "100%", "67%", "50%", "25%", "auto" +}; +static const char *enum_onoff[] = {"Off", "On"}; +static const char *enum_mbias_pdn_imp[] = {"GND", "HiZ"}; +static const char *enum_mbias2_out_v[] = {"2.0v", "2.2v"}; +static const char *enum_mic_in_imp[] = { + "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_optional_resistor[] = {"disconnected", "connected"}; +static const char *enum_i2s_sample_rate[] = { + "8 kHz", "16 kHz", "44.1 kHz", "48 kHz" +}; +static const char *enum_tx1_input_select[] = { + "ADC1", "DIGMIC1", "DIGMIC2" +}; +static const char *enum_tx2_input_select[] = { + "ADC2", "DIGMIC1", "DIGMIC2" +}; +static const char *enum_signal_inversion[] = {"normal", "inverted"}; +static const char *enum_spkr1_mode[] = { + "Vibra PWM", "class D amplifier", "class AB amplifier" +}; +static const char *enum_spkr2_mode[] = { + "Vibra PWM", "class D amplifier", +}; +static const char *enum_pwm_pol[] = { + "GND", "VDD" +}; +/* RX1 Input Select */ +static struct soc_enum soc_enum_rx1_in_sel = + SOC_ENUM_SINGLE(RX1, RXx_DATA_SHIFT, + ARRAY_SIZE(enum_rx_input_select), + enum_rx_input_select); + +/* RX2 Input Select */ +static struct soc_enum soc_enum_rx2_in_sel = + SOC_ENUM_SINGLE(RX2, RXx_DATA_SHIFT, + ARRAY_SIZE(enum_rx_input_select), + enum_rx_input_select); +/* RX3 Input Select */ +static struct soc_enum soc_enum_rx3_in_sel = + SOC_ENUM_SINGLE(RX3, RXx_DATA_SHIFT, + ARRAY_SIZE(enum_rx_input_select), + enum_rx_input_select); +/* TX1 Input Select */ +static struct soc_enum soc_enum_tx1_in_sel = + SOC_ENUM_SINGLE(TX1, TXx_MUX_SHIFT, + ARRAY_SIZE(enum_tx1_input_select), + enum_tx1_input_select); +/* TX2 Input Select */ +static struct soc_enum soc_enum_tx2_in_sel = + SOC_ENUM_SINGLE(TX2, TXx_MUX_SHIFT, + ARRAY_SIZE(enum_tx2_input_select), + enum_tx2_input_select); + +/* I2S0 ULD Select */ +static struct soc_enum soc_enum_i2s0_input_select = + SOC_ENUM_DOUBLE(INTERFACE0_ULD, 0, 4, + ARRAY_SIZE(enum_i2s_uld_select), + enum_i2s_uld_select); +/* I2S1 ULD Select */ +static struct soc_enum soc_enum_i2s1_input_select = + SOC_ENUM_DOUBLE(INTERFACE1_ULD, 0, 4, + ARRAY_SIZE(enum_i2s_uld_select), + enum_i2s_uld_select); + +/* APGA1 Source */ +static struct soc_enum soc_enum_apga1_source = + SOC_ENUM_SINGLE(ANALOG_LOOP_PGA1, APGAx_MUX_SHIFT, + ARRAY_SIZE(enum_apga1_source), + enum_apga1_source); + +/* APGA2 Source */ +static struct soc_enum soc_enum_apga2_source = + SOC_ENUM_SINGLE(ANALOG_LOOP_PGA2, APGAx_MUX_SHIFT, + ARRAY_SIZE(enum_apga2_source), + enum_apga2_source); + +static struct soc_enum soc_enum_apga1_enable = + SOC_ENUM_SINGLE(ANALOG_LOOP_PGA1, APGAx_PWR_SHIFT, + ARRAY_SIZE(enum_onoff), enum_onoff); + +static struct soc_enum soc_enum_apga2_enable = + SOC_ENUM_SINGLE(ANALOG_LOOP_PGA2, APGAx_PWR_SHIFT, + ARRAY_SIZE(enum_onoff), enum_onoff); + +/* RX1 Side Tone */ +static struct soc_enum soc_enum_dac1_side_tone = + SOC_ENUM_SINGLE(ST1_PGA, STx_MUX_SHIFT, + ARRAY_SIZE(enum_rx_side_tone), + enum_rx_side_tone); + +/* RX2 Side Tone */ +static struct soc_enum soc_enum_dac2_side_tone = + SOC_ENUM_SINGLE(ST2_PGA, STx_MUX_SHIFT, + ARRAY_SIZE(enum_rx_side_tone), + enum_rx_side_tone); + +/* DAC1 Power Mode */ +static struct soc_enum soc_enum_dac1_power_mode = + SOC_ENUM_SINGLE(RX1, DACx_PWR_MODE_SHIFT, + ARRAY_SIZE(enum_dac_power_mode), + enum_dac_power_mode); + +/* DAC2 Power Mode */ +static struct soc_enum soc_enum_dac2_power_mode = + SOC_ENUM_SINGLE(RX2, DACx_PWR_MODE_SHIFT, + ARRAY_SIZE(enum_dac_power_mode), + enum_dac_power_mode); + +/* DAC3 Power Mode */ +static struct soc_enum soc_enum_dac3_power_mode = + SOC_ENUM_SINGLE(RX3, DACx_PWR_MODE_SHIFT, + ARRAY_SIZE(enum_dac_power_mode), + enum_dac_power_mode); + +/* EAR Power Mode */ +static struct soc_enum soc_enum_ear_power_mode = + SOC_ENUM_SINGLE(EAR_PWR, EAR_PWR_MODE_SHIFT, + ARRAY_SIZE(enum_ear_power_mode), + enum_ear_power_mode); + +/* AUXO12 Power Mode */ +static struct soc_enum soc_enum_auxo12_power_mode = + SOC_ENUM_SINGLE(AUXO12_PWR_MODE, AUXOxy_PWR_MODE_SHIFT, + ARRAY_SIZE(enum_auxo_power_mode), + enum_auxo_power_mode); + +/* AUXO34 Power Mode */ +static struct soc_enum soc_enum_auxo34_power_mode = + SOC_ENUM_SINGLE(AUXO34_PWR_MODE, AUXOxy_PWR_MODE_SHIFT, + ARRAY_SIZE(enum_auxo_power_mode), + enum_auxo_power_mode); + +/* MBIAS1 PDN Impedance */ +static struct soc_enum soc_enum_mbias1_pdn_imp = + SOC_ENUM_SINGLE(MIC_BIAS1, MBIASx_PDN_IMP_SHIFT, + ARRAY_SIZE(enum_mbias_pdn_imp), + enum_mbias_pdn_imp); + +/* MBIAS2 PDN Impedance */ +static struct soc_enum soc_enum_mbias2_pdn_imp = + SOC_ENUM_SINGLE(MIC_BIAS2, MBIASx_PDN_IMP_SHIFT, + ARRAY_SIZE(enum_mbias_pdn_imp), + enum_mbias_pdn_imp); + +/* MBIAS2 Output voltage */ +static struct soc_enum soc_enum_mbias2_out_v = + SOC_ENUM_SINGLE(MIC_BIAS2, MBIAS2_OUT_V_SHIFT, + ARRAY_SIZE(enum_mbias2_out_v), + enum_mbias2_out_v); + +static struct soc_enum soc_enum_mbias2_int_r = + SOC_ENUM_SINGLE(MIC_BIAS2_VAD, MBIAS2_R_INT_SHIFT, + ARRAY_SIZE(enum_optional_resistor), + enum_optional_resistor); + +static struct soc_enum soc_enum_mic1_in_imp = + SOC_ENUM_SINGLE(MIC1_GAIN, MICx_IN_IMP_SHIFT, + ARRAY_SIZE(enum_mic_in_imp), + enum_mic_in_imp); + +static struct soc_enum soc_enum_mic2_in_imp = + SOC_ENUM_SINGLE(MIC2_GAIN, MICx_IN_IMP_SHIFT, + ARRAY_SIZE(enum_mic_in_imp), + enum_mic_in_imp); + +static struct soc_enum soc_enum_tx1_hp_filter = + SOC_ENUM_SINGLE(TX1, TXx_HP_FILTER_SHIFT, + ARRAY_SIZE(enum_hp_filter), + enum_hp_filter); + +static struct soc_enum soc_enum_tx2_hp_filter = + SOC_ENUM_SINGLE(TX2, TXx_HP_FILTER_SHIFT, + ARRAY_SIZE(enum_hp_filter), + enum_hp_filter); + +static struct soc_enum soc_enum_st1_hp_filter = + SOC_ENUM_SINGLE(ST1_PGA, STx_HP_FILTER_SHIFT, + ARRAY_SIZE(enum_hp_filter), + enum_hp_filter); + +static struct soc_enum soc_enum_st2_hp_filter = + SOC_ENUM_SINGLE(ST2_PGA, STx_HP_FILTER_SHIFT, + ARRAY_SIZE(enum_hp_filter), + enum_hp_filter); + +static struct soc_enum soc_enum_i2s0_word_length = + SOC_ENUM_SINGLE(INTERFACE0, I2Sx_WORDLENGTH_SHIFT, + ARRAY_SIZE(enum_i2s_word_length), + enum_i2s_word_length); + +static struct soc_enum soc_enum_i2s1_word_length = + SOC_ENUM_SINGLE(INTERFACE1, I2Sx_WORDLENGTH_SHIFT, + ARRAY_SIZE(enum_i2s_word_length), + enum_i2s_word_length); + +static struct soc_enum soc_enum_i2s0_mode = + SOC_ENUM_SINGLE(INTERFACE0, I2Sx_MODE_SHIFT, + ARRAY_SIZE(enum_i2s_mode), + enum_i2s_mode); + +static struct soc_enum soc_enum_i2s1_mode = + SOC_ENUM_SINGLE(INTERFACE1, I2Sx_MODE_SHIFT, + ARRAY_SIZE(enum_i2s_mode), + enum_i2s_mode); + +static struct soc_enum soc_enum_i2s0_tristate = + SOC_ENUM_SINGLE(INTERFACE0, I2Sx_TRISTATE_SHIFT, + ARRAY_SIZE(enum_i2s_tristate), + enum_i2s_tristate); + +static struct soc_enum soc_enum_i2s1_tristate = + SOC_ENUM_SINGLE(INTERFACE1, I2Sx_TRISTATE_SHIFT, + ARRAY_SIZE(enum_i2s_tristate), + enum_i2s_tristate); + +static struct soc_enum soc_enum_i2s0_pulldown_resistor = + SOC_ENUM_SINGLE(INTERFACE0, I2Sx_PULLDOWN_SHIFT, + ARRAY_SIZE(enum_optional_resistor), + enum_optional_resistor); + +static struct soc_enum soc_enum_i2s1_pulldown_resistor = + SOC_ENUM_SINGLE(INTERFACE1, I2Sx_PULLDOWN_SHIFT, + ARRAY_SIZE(enum_optional_resistor), + enum_optional_resistor); + +static struct soc_enum soc_enum_i2s0_sample_rate = + SOC_ENUM_SINGLE(INTERFACE0, I2Sx_SR_SHIFT, + ARRAY_SIZE(enum_i2s_sample_rate), + enum_i2s_sample_rate); + +static struct soc_enum soc_enum_i2s1_sample_rate = + SOC_ENUM_SINGLE(INTERFACE1, I2Sx_SR_SHIFT, + ARRAY_SIZE(enum_i2s_sample_rate), + enum_i2s_sample_rate); + +static struct soc_enum soc_enum_line1_inversion = + SOC_ENUM_SINGLE(LINE1, LINEx_INV_SHIFT, + ARRAY_SIZE(enum_signal_inversion), + enum_signal_inversion); + +static struct soc_enum soc_enum_line2_inversion = + SOC_ENUM_SINGLE(LINE2, LINEx_INV_SHIFT, + ARRAY_SIZE(enum_signal_inversion), + enum_signal_inversion); + +static struct soc_enum soc_enum_auxo1_inversion = + SOC_ENUM_SINGLE(AUXO1, AUXOx_INV_SHIFT, + ARRAY_SIZE(enum_signal_inversion), + enum_signal_inversion); + +static struct soc_enum soc_enum_auxo2_inversion = + SOC_ENUM_SINGLE(AUXO2, AUXOx_INV_SHIFT, + ARRAY_SIZE(enum_signal_inversion), + enum_signal_inversion); + +static struct soc_enum soc_enum_auxo3_inversion = + SOC_ENUM_SINGLE(AUXO3, AUXOx_INV_SHIFT, + ARRAY_SIZE(enum_signal_inversion), + enum_signal_inversion); + +static struct soc_enum soc_enum_auxo4_inversion = + SOC_ENUM_SINGLE(AUXO4, AUXOx_INV_SHIFT, + ARRAY_SIZE(enum_signal_inversion), + enum_signal_inversion); + +static struct soc_enum soc_enum_auxo1_pulldown_resistor = + SOC_ENUM_SINGLE(AUXO1, AUXOx_PULLDOWN_SHIFT, + ARRAY_SIZE(enum_optional_resistor), + enum_optional_resistor); + +static struct soc_enum soc_enum_auxo2_pulldown_resistor = + SOC_ENUM_SINGLE(AUXO2, AUXOx_PULLDOWN_SHIFT, + ARRAY_SIZE(enum_optional_resistor), + enum_optional_resistor); + +static struct soc_enum soc_enum_spkr1_mode = + SOC_ENUM_SINGLE(AB5500_VIRTUAL_REG3, SPKR1_MODE_SHIFT, + ARRAY_SIZE(enum_spkr1_mode), + enum_spkr1_mode); + +static struct soc_enum soc_enum_spkr2_mode = + SOC_ENUM_SINGLE(AB5500_VIRTUAL_REG3, SPKR2_MODE_SHIFT, + ARRAY_SIZE(enum_spkr2_mode), + enum_spkr2_mode); + +static struct soc_enum soc_enum_pwm_spkr1n_pol = + SOC_ENUM_SINGLE(PWMCTRL_SPKR1, PWMCTRL_SPKRx_N1_POL_SHIFT, + ARRAY_SIZE(enum_pwm_pol), enum_pwm_pol); + +static struct soc_enum soc_enum_pwm_spkr1p_pol = + SOC_ENUM_SINGLE(PWMCTRL_SPKR1, PWMCTRL_SPKRx_P1_POL_SHIFT, + ARRAY_SIZE(enum_pwm_pol), enum_pwm_pol); + +static struct soc_enum soc_enum_pwm_spkr2n_pol = + SOC_ENUM_SINGLE(PWMCTRL_SPKR2, PWMCTRL_SPKRx_N1_POL_SHIFT, + ARRAY_SIZE(enum_pwm_pol), enum_pwm_pol); + +static struct soc_enum soc_enum_pwm_spkr2p_pol = + SOC_ENUM_SINGLE(PWMCTRL_SPKR2, PWMCTRL_SPKRx_P1_POL_SHIFT, + ARRAY_SIZE(enum_pwm_pol), enum_pwm_pol); + +static struct snd_kcontrol_new ab5500_snd_controls[] = { + /* RX Routing */ + SOC_ENUM("RX1 Input Select", soc_enum_rx1_in_sel), + SOC_ENUM("RX2 Input Select", soc_enum_rx2_in_sel), + SOC_ENUM("RX3 Input Select", soc_enum_rx3_in_sel), + SOC_SINGLE("LINE1 Adder", LINE1_ADDER, 0, 0x1F, 0), + SOC_SINGLE("LINE2 Adder", LINE2_ADDER, 0, 0x1F, 0), + SOC_SINGLE("EAR Adder", EAR_ADDER, 0, 0x1F, 0), + SOC_SINGLE("SPKR1 Adder", SPKR1_ADDER, 0, 0x1F, 0), + SOC_SINGLE("SPKR2 Adder", SPKR2_ADDER, 0, 0x1F, 0), + SOC_SINGLE("AUXO1 Adder", AUXO1_ADDER, 0, 0x1F, 0), + SOC_SINGLE("AUXO2 Adder", AUXO2_ADDER, 0, 0x1F, 0), + SOC_SINGLE("AUXO3 Adder", AUXO3_ADDER, 0, 0x1F, 0), + SOC_SINGLE("AUXO4 Adder", AUXO4_ADDER, 0, 0x1F, 0), + SOC_SINGLE("SPKR1 PWM Select", AB5500_VIRTUAL_REG5, 0, 0x03, 0), + SOC_SINGLE("SPKR2 PWM Select", AB5500_VIRTUAL_REG5, 2, 0x0C, 0), + /* TX Routing */ + SOC_ENUM("TX1 Input Select", soc_enum_tx1_in_sel), + SOC_ENUM("TX2 Input Select", soc_enum_tx2_in_sel), + SOC_SINGLE("MIC1 Input Select", MIC1_INPUT_SELECT, 0, 0xff, 0), + SOC_SINGLE("MIC2 Input Select", MIC2_INPUT_SELECT, 0, 0xff, 0), + SOC_SINGLE("MIC2 to MIC1", MIC2_TO_MIC1, 0, 0x03, 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_ENUM("APGA1 Enable", soc_enum_apga1_enable), + SOC_ENUM("APGA2 Enable", soc_enum_apga2_enable), + 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", RX1_DPGA, 0, 0x43, 0), + SOC_SINGLE("RX-DPGA2 Gain", RX1_DPGA, 0, 0x43, 0), + SOC_SINGLE("RX-DPGA3 Gain", RX3_DPGA, 0, 0x43, 0), + SOC_SINGLE("LINE1 Gain", LINE1, LINEx_GAIN_SHIFT, 0x0a, 0), + SOC_SINGLE("LINE2 Gain", LINE2, LINEx_GAIN_SHIFT, 0x0a, 0), + SOC_SINGLE("SPKR1 Gain", SPKR1, SPKRx_GAIN_SHIFT, 0x16, 0), + SOC_SINGLE("SPKR2 Gain", SPKR2, SPKRx_GAIN_SHIFT, 0x16, 0), + SOC_SINGLE("EAR Gain", EAR_GAIN, EAR_GAIN_SHIFT, 0x12, 0), + SOC_SINGLE("AUXO1 Gain", AUXO1, AUXOx_GAIN_SHIFT, 0x0c, 0), + SOC_SINGLE("AUXO2 Gain", AUXO2, AUXOx_GAIN_SHIFT, 0x0c, 0), + SOC_SINGLE("AUXO3 Gain", AUXO3, AUXOx_GAIN_SHIFT, 0x0c, 0), + SOC_SINGLE("AUXO4 Gain", AUXO4, AUXOx_GAIN_SHIFT, 0x0c, 0), + /* TX Volume Control */ + SOC_SINGLE("MIC1 Gain", MIC1_GAIN, MICx_GAIN_SHIFT, 0x0a, 0), + SOC_SINGLE("MIC2 Gain", MIC2_GAIN, MICx_GAIN_SHIFT, 0x0a, 0), + SOC_SINGLE("TX-DPGA1 Gain", TX_DPGA1, TX_DPGAx_SHIFT, 0x0f, 0), + SOC_SINGLE("TX-DPGA2 Gain", TX_DPGA2, TX_DPGAx_SHIFT, 0x0f, 0), + /* Volume Control of Side Tone and Analog Loop */ + SOC_SINGLE("ST-PGA1 Gain", ST1_PGA, STx_PGA_SHIFT, 0x0a, 0), + SOC_SINGLE("ST-PGA2 Gain", ST2_PGA, STx_PGA_SHIFT, 0x0a, 0), + SOC_SINGLE("APGA1 Gain", ANALOG_LOOP_PGA1, APGAx_GAIN_SHIFT, 0x1d, 0), + SOC_SINGLE("APGA2 Gain", ANALOG_LOOP_PGA2, APGAx_GAIN_SHIFT, 0x1d, 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("AUXO12 Power Mode", soc_enum_auxo12_power_mode), + SOC_ENUM("AUXO34 Power Mode", soc_enum_auxo34_power_mode), + SOC_ENUM("LINE1 Inversion", soc_enum_line1_inversion), + SOC_ENUM("LINE2 Inversion", soc_enum_line2_inversion), + SOC_ENUM("AUXO1 Inversion", soc_enum_auxo1_inversion), + SOC_ENUM("AUXO2 Inversion", soc_enum_auxo2_inversion), + SOC_ENUM("AUXO3 Inversion", soc_enum_auxo3_inversion), + SOC_ENUM("AUXO4 Inversion", soc_enum_auxo4_inversion), + SOC_ENUM("AUXO1 Pulldown Resistor", soc_enum_auxo1_pulldown_resistor), + SOC_ENUM("AUXO2 Pulldown Resistor", soc_enum_auxo2_pulldown_resistor), + SOC_ENUM("SPKR1 Mode", soc_enum_spkr1_mode), + SOC_ENUM("SPKR2 Mode", soc_enum_spkr2_mode), + SOC_ENUM("PWM SPKR1N POL", soc_enum_pwm_spkr1n_pol), + SOC_ENUM("PWM SPKR1P POL", soc_enum_pwm_spkr1p_pol), + SOC_ENUM("PWM SPKR2N POL", soc_enum_pwm_spkr2n_pol), + SOC_ENUM("PWM SPKR2P POL", soc_enum_pwm_spkr2p_pol), + /* TX Properties */ + SOC_SINGLE("MIC1 VMID", MIC1_VMID_SELECT, 0, 0x3f, 0), + SOC_SINGLE("MIC2 VMID", MIC2_VMID_SELECT, 0, 0x3f, 0), + SOC_ENUM("MBIAS1 PDN Impedance", soc_enum_mbias1_pdn_imp), + SOC_ENUM("MBIAS2 PDN Impedance", soc_enum_mbias2_pdn_imp), + SOC_ENUM("MBIAS2 Output Voltage", soc_enum_mbias2_out_v), + SOC_ENUM("MBIAS2 Internal Resistor", soc_enum_mbias2_int_r), + SOC_ENUM("MIC1 Input Impedance", soc_enum_mic1_in_imp), + SOC_ENUM("MIC2 Input Impedance", soc_enum_mic2_in_imp), + SOC_ENUM("TX1 HP Filter", soc_enum_tx1_hp_filter), + SOC_ENUM("TX2 HP Filter", soc_enum_tx2_hp_filter), + /* 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 Resistor", soc_enum_i2s0_pulldown_resistor), + SOC_ENUM("I2S1 Pulldown Resistor", soc_enum_i2s1_pulldown_resistor), + SOC_ENUM("I2S0 Sample Rate", soc_enum_i2s0_sample_rate), + SOC_ENUM("I2S1 Sample Rate", soc_enum_i2s1_sample_rate), + SOC_SINGLE("Interface Swap", INTERFACE_SWAP, 0, 0x03, 0), + /* Miscellaneous */ + SOC_SINGLE("Negative Charge Pump", NEG_CHARGE_PUMP, 0, 0x03, 0) +}; + +enum enum_power { + POWER_OFF = 0, + POWER_ON = 1 +}; + +enum enum_link { + UNLINK = 0, + LINK = 1 +}; + +static enum enum_power get_widget_power_status(enum enum_widget widget) +{ + u8 val; + + if (widget >= number_of_widgets) + return POWER_OFF; + val = read_reg(widget_pm_array[widget].reg); + if (val & (1 << widget_pm_array[widget].shift)) + return POWER_ON; + else + return POWER_OFF; +} + +static int count_powered_neighbors(const unsigned long *neighbors) +{ + unsigned long i; + int n = 0; + for_each_set_bit(i, neighbors, number_of_widgets) { + if (get_widget_power_status(i) == POWER_ON) + n++; + } + return n; +} + +static int has_powered_neighbors(const unsigned long *neighbors) +{ + unsigned int i; + for_each_set_bit(i, neighbors, number_of_widgets) { + if (get_widget_power_status(i) == POWER_ON) + return 1; + } + return 0; +} + + +static int has_stacked_neighbors(const unsigned long *neighbors) +{ + unsigned long *stack_map = pm_stack_as_bitmap; + return bitmap_intersects(stack_map, neighbors, number_of_widgets); +} + +static void power_widget_unlocked(enum enum_power onoff, + enum enum_widget widget) +{ + enum enum_widget w; + int done; + + if (widget >= number_of_widgets) + return; + if (get_widget_power_status(widget) == onoff) + return; + for (w = widget, done = 0; !done;) { + unsigned long i; + unsigned long *srcs = widget_pm_array[w].source_list; + unsigned long *sinks = widget_pm_array[w].sink_list; + if (onoff == POWER_ON && + !bitmap_empty(srcs, number_of_widgets) && + !has_powered_neighbors(srcs)) { + pm_stack.stack[pm_stack.p++] = w; + for_each_set_bit(i, srcs, number_of_widgets) { + pm_stack.stack[pm_stack.p++] = i; + } + w = pm_stack.stack[--pm_stack.p]; + continue; + } else if (onoff == POWER_OFF && + has_powered_neighbors(sinks)) { + int n = 0; + pm_stack.stack[pm_stack.p++] = w; + for_each_set_bit(i, sinks, number_of_widgets) { + if (count_powered_neighbors( + widget_pm_array[i].source_list) + == 1 && + get_widget_power_status(i) == POWER_ON) { + pm_stack.stack[pm_stack.p++] = i; + n++; + } + } + if (n) { + w = pm_stack.stack[--pm_stack.p]; + continue; + } else + --pm_stack.p; + } + mask_set_reg(widget_pm_array[w].reg, + 1 << widget_pm_array[w].shift, + onoff == POWER_ON ? 0xff : 0); + if (onoff == POWER_ON && + !bitmap_empty(sinks, number_of_widgets) && + !has_powered_neighbors(sinks) && + !has_stacked_neighbors(sinks)) { + for_each_set_bit(i, sinks, number_of_widgets) { + pm_stack.stack[pm_stack.p++] = i; + } + w = pm_stack.stack[--pm_stack.p]; + continue; + } else if (onoff == POWER_OFF) { + for_each_set_bit(i, srcs, number_of_widgets) { + if (!has_powered_neighbors( + widget_pm_array[i].sink_list) + && get_widget_power_status(i) == POWER_ON + && !test_bit(i, pm_stack_as_bitmap)) { + pm_stack.stack[pm_stack.p++] = i; + } + } + } + if (pm_stack.p > 0) + w = pm_stack.stack[--pm_stack.p]; + else + done = 1; + } +} + +static void power_widget_locked(enum enum_power onoff, + enum enum_widget widget) +{ + if (mutex_lock_interruptible(&ab5500_pm_mutex)) { + dev_warn(ab5500_dev, + "%s: Signal received while waiting on the PM mutex.\n", + __func__); + return; + } + power_widget_unlocked(onoff, widget); + mutex_unlock(&ab5500_pm_mutex); +} + +#if AB5500_CODEC_DEBUG +static void dump_registers(const char *where, ...) +{ + va_list ap; + va_start(ap, where); + do { + short reg = va_arg(ap, int); + if (reg < 0) + break; + dev_info(ab5500_dev, "%s from %s> 0x%02X : 0x%02X.\n", + __func__, where, reg, read_reg(reg)); + } while (1); + va_end(ap); +} +#endif + +/** + * update the link two widgets. + * @op: 1 - connect; 0 - disconnect + * @src: source of the connection + * @sink: sink of the connection + */ +static int update_widgets_link(enum enum_link op, enum enum_widget src, + enum enum_widget sink, + u8 reg, u8 mask, u8 newval) +{ + int ret = 0; + + if (mutex_lock_interruptible(&ab5500_pm_mutex)) { + dev_warn(ab5500_dev, "%s: A signal is received while waiting on" + " the PM mutex.\n", __func__); + return -EINTR; + } + + switch (op << 2 | test_bit(sink, widget_pm_array[src].sink_list) << 1 | + test_bit(src, widget_pm_array[sink].source_list)) { + case 3: /* UNLINK, sink in sink_list, src in source_list */ + case 4: /* LINK, sink not in sink_list, src not in source_list */ + break; + default: + ret = -EINVAL; + goto end; + } + switch (((int)op) << 2 | get_widget_power_status(src) << 1 | + get_widget_power_status(sink)) { + case 3: /* op = 0, src on, sink on */ + if (count_powered_neighbors(widget_pm_array[sink].source_list) + == 1) + power_widget_unlocked(POWER_OFF, sink); + mask_set_reg(reg, mask, newval); + break; + case 6: /* op = 1, src on, sink off */ + mask_set_reg(reg, mask, newval); + power_widget_unlocked(POWER_ON, sink); + break; + default: + /* op = 0, src off, sink off */ + /* op = 0, src off, sink on */ + /* op = 0, src on, sink off */ + /* op = 1, src off, sink off */ + /* op = 1, src off, sink on */ + /* op = 1, src on, sink on */ + mask_set_reg(reg, mask, newval); + } + change_bit(sink, widget_pm_array[src].sink_list); + change_bit(src, widget_pm_array[sink].source_list); +end: + mutex_unlock(&ab5500_pm_mutex); + return ret; +}; + +static enum enum_widget apga_source_translate(u8 reg_value) +{ + switch (reg_value) { + case 1: + return widget_mic1; + case 2: + return widget_mic2; + default: + return number_of_widgets; + } +} + +static enum enum_widget adder_sink_translate(u8 reg) +{ + switch (reg) { + case EAR_ADDER: + return widget_ear; + case AUXO1_ADDER: + return widget_auxo1; + case AUXO2_ADDER: + return widget_auxo2; + case AUXO3_ADDER: + return widget_auxo3; + case AUXO4_ADDER: + return widget_auxo4; + case SPKR1_ADDER: + return widget_spkr1; + case SPKR2_ADDER: + return widget_spkr2; + case LINE1_ADDER: + return widget_line1; + case LINE2_ADDER: + return widget_line2; + default: + return number_of_widgets; + } +} + +static int ab5500_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, ab5500_dapm_widgets, + ARRAY_SIZE(ab5500_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +static void power_for_playback(enum enum_power onoff, int ifsel) +{ + if (mutex_lock_interruptible(&ab5500_pm_mutex)) { + dev_warn(ab5500_dev, + "%s: Signal received while waiting on the PM mutex.\n", + __func__); + return; + } + power_widget_unlocked(onoff, ifsel == 0 ? + widget_if0_dld_l : widget_if1_dld_l); + power_widget_unlocked(onoff, ifsel == 0 ? + widget_if0_dld_r : widget_if1_dld_r); + mutex_unlock(&ab5500_pm_mutex); +} + +static void power_for_capture(enum enum_power onoff, int ifsel) +{ + if (mutex_lock_interruptible(&ab5500_pm_mutex)) { + dev_warn(ab5500_dev, + "%s: Signal received while waiting on the PM mutex.\n", + __func__); + return; + } + power_widget_unlocked(onoff, ifsel == 0 ? + widget_if0_uld_l : widget_if1_uld_l); + power_widget_unlocked(onoff, ifsel == 0 ? + widget_if0_uld_r : widget_if1_uld_r); + mutex_unlock(&ab5500_pm_mutex); +} + +static int ab5500_add_controls(struct snd_soc_codec *codec) +{ + int err = 0, i, n = ARRAY_SIZE(ab5500_snd_controls); + + for (i = 0; i < n; i++) { + err = snd_ctl_add(codec->card->snd_card, snd_ctl_new1( + &ab5500_snd_controls[i], codec)); + if (err < 0) { + pr_err("%s failed to add control No.%d of %d.\n", + __func__, i, n); + return err; + } + } + return err; +} + +static int ab5500_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + dai->playback_active : dai->capture_active) { + dev_err(dai->dev, "A %s stream is already active.\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + "playback" : "capture"); + return -EBUSY; + } + return 0; +} + +static int ab5500_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, + struct snd_soc_dai *dai) +{ + u8 val; + u8 reg = dai->id == 0 ? INTERFACE0 : INTERFACE1; + + if (!ab5500_dev) { + pr_err("%s: The AB5500 codec driver not initialized.\n", + __func__); + return -EAGAIN; + } + switch (params_rate(hw_params)) { + case 8000: + val = I2Sx_SR_8000Hz; + break; + case 16000: + val = I2Sx_SR_16000Hz; + break; + case 44100: + val = I2Sx_SR_44100Hz; + break; + case 48000: + val = I2Sx_SR_48000Hz; + break; + default: + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + !dai->capture_active : !dai->playback_active) { + + mask_set_reg(reg, I2Sx_SR_MASK, val << I2Sx_SR_SHIFT); + if ((read_reg(reg) & I2Sx_MODE_MASK) == 0) { + mask_set_reg(reg, MASTER_GENx_PWR_MASK, + 1 << MASTER_GENx_PWR_SHIFT); + } + } + return 0; +} + +static int ab5500_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* Configure registers for either playback or capture */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + power_for_playback(POWER_ON, dai->id); + else + power_for_capture(POWER_ON, dai->id); + return 0; +} + +static void ab5500_pcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + u8 iface = dai->id == 0 ? INTERFACE0 : INTERFACE1; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + power_for_playback(POWER_OFF, dai->id); + else + power_for_capture(POWER_OFF, dai->id); + if (!dai->playback_active && !dai->capture_active && + (read_reg(iface) & I2Sx_MODE_MASK) == 0) + mask_set_reg(iface, MASTER_GENx_PWR_MASK, 0); +} + +static int ab5500_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + return 0; +} + +static int ab5500_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + u8 iface = (codec_dai->id == 0) ? INTERFACE0 : INTERFACE1; + u8 val = 0; + + 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: + dev_warn(ab5500_dev, "AB5500_dai: unsupported DAI format " + "0x%x\n", fmt); + return -EINVAL; + } + if (codec_dai->playback_active && codec_dai->capture_active) { + if ((read_reg(iface) & I2Sx_MODE_MASK) == val) + return 0; + else { + dev_err(ab5500_dev, + "%s: DAI format set differently " + "by an existing stream.\n", __func__); + return -EINVAL; + } + } + mask_set_reg(iface, I2Sx_MODE_MASK, val); + return 0; +} + +struct snd_soc_dai_driver ab5500_dai_drv[] = { + { + .name = "ab5500-codec-dai.0", + .id = 0, + .playback = { + .stream_name = "ab5500.0 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AB5500_SUPPORTED_RATE, + .formats = AB5500_SUPPORTED_FMT, + }, + .capture = { + .stream_name = "ab5500.0 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AB5500_SUPPORTED_RATE, + .formats = AB5500_SUPPORTED_FMT, + }, + .ops = (struct snd_soc_dai_ops[]) { + { + .startup = ab5500_pcm_startup, + .prepare = ab5500_pcm_prepare, + .hw_params = ab5500_pcm_hw_params, + .shutdown = ab5500_pcm_shutdown, + .set_sysclk = ab5500_set_dai_sysclk, + .set_fmt = ab5500_set_dai_fmt, + } + }, + .symmetric_rates = 1, + }, + { + .name = "ab5500-codec-dai.1", + .id = 1, + .playback = { + .stream_name = "ab5500.1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AB5500_SUPPORTED_RATE, + .formats = AB5500_SUPPORTED_FMT, + }, + .capture = { + .stream_name = "ab5500.1 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AB5500_SUPPORTED_RATE, + .formats = AB5500_SUPPORTED_FMT, + }, + .ops = (struct snd_soc_dai_ops[]) { + { + .startup = ab5500_pcm_startup, + .prepare = ab5500_pcm_prepare, + .hw_params = ab5500_pcm_hw_params, + .shutdown = ab5500_pcm_shutdown, + .set_sysclk = ab5500_set_dai_sysclk, + .set_fmt = ab5500_set_dai_fmt, + } + }, + .symmetric_rates = 1, + } +}; + +static int ab5500_codec_probe(struct snd_soc_codec *codec) +{ + int ret = ab5500_add_controls(codec); + if (ret < 0) + return ret; + ab5500_add_widgets(codec); + return 0; +} + +static int ab5500_codec_remove(struct snd_soc_codec *codec) +{ + snd_soc_dapm_free(codec); + return 0; +} + +#ifdef CONFIG_PM +static int ab5500_codec_suspend(struct snd_soc_codec *codec, + pm_message_t state) +{ + mask_set_reg(CLOCK, CLOCK_ENABLE_MASK, 0); + return 0; +} + +static int ab5500_codec_resume(struct snd_soc_codec *codec) +{ + mask_set_reg(CLOCK, CLOCK_ENABLE_MASK, 0xff); + return 0; +} +#else +#define ab5500_codec_resume NULL +#define ab5500_codec_suspend NULL +#endif + +/** + This function is only called by the SOC framework to + set registers associated to the mixer controls. +*/ +static int ab5500_codec_write_reg(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + if (reg < MIC_BIAS1 || reg > INTERFACE_SWAP) + return -EINVAL; + switch (reg) { + u8 diff, oldval; + case ANALOG_LOOP_PGA1: + case ANALOG_LOOP_PGA2: { + enum enum_widget apga = reg == ANALOG_LOOP_PGA1 ? + widget_apga1 : widget_apga2; + + oldval = read_reg(reg); + diff = value ^ oldval; + + /* + * The APGA is to be turned on/off. The power bit and the + * other bits in the same register won't be changed at the + * same time since they belong to different controls. + */ + if (diff & (1 << APGAx_PWR_SHIFT)) { + power_widget_locked(value >> APGAx_PWR_SHIFT & 1, + apga); + } else if (diff & APGAx_MUX_MASK) { + enum enum_widget old_source = + apga_source_translate(oldval); + enum enum_widget new_source = + apga_source_translate(value); + update_widgets_link(UNLINK, old_source, apga, + reg, APGAx_MUX_MASK, 0); + update_widgets_link(LINK, new_source, apga, + reg, APGAx_MUX_MASK, value); + } else { + set_reg(reg, value); + } + break; + } + + case EAR_ADDER: + case AUXO1_ADDER: + case AUXO2_ADDER: + case AUXO3_ADDER: + case AUXO4_ADDER: + case SPKR1_ADDER: + case SPKR2_ADDER: + case LINE1_ADDER: + case LINE2_ADDER: { + int i; + enum enum_widget widgets[] = { + widget_dac1, widget_dac2, widget_dac3, + widget_apga1, widget_apga2 + }; + oldval = read_reg(reg); + diff = value ^ oldval; + for (i = 0; diff; i++) { + if (!(diff & 1 << i)) + continue; + diff ^= 1 << i; + update_widgets_link(value >> i & 1, widgets[i], + adder_sink_translate(reg), + reg, 1 << i, value); + } + break; + } + case AB5500_VIRTUAL_REG3: + oldval = read_reg(reg); + diff = value ^ oldval; + /* + * The following changes won't take place in the same call, + * since they are arranged into different mixer controls. + */ + + /* changed between the two amplifier modes */ + if (hweight8(diff & SPKR1_MODE_MASK) == 2) { + set_reg(reg, value); + break; + } + + if (diff & SPKR1_MODE_MASK) { + update_widgets_link( + UNLINK, + (oldval & SPKR1_MODE_MASK) == 0 ? + widget_pwm_spkr1 : widget_spkr1_adder, + widget_spkr1, + reg, SPKR1_MODE_MASK, value); + update_widgets_link( + LINK, + (value & SPKR1_MODE_MASK) == 0 ? + widget_pwm_spkr1 : widget_spkr1_adder, + widget_spkr1, + DUMMY_REG, 0, 0); + + } + if (diff & SPKR2_MODE_MASK) { + update_widgets_link( + UNLINK, + (oldval & SPKR2_MODE_MASK) == 0 ? + widget_pwm_spkr2 : widget_spkr2_adder, + widget_spkr2, + reg, SPKR2_MODE_MASK, value); + update_widgets_link( + LINK, + (value & SPKR2_MODE_MASK) == 0 ? + widget_pwm_spkr2 : widget_spkr2_adder, + widget_spkr2, + DUMMY_REG, 0, 0); + + } + break; + + case AB5500_VIRTUAL_REG4: + /* configure PWMCTRL_SPKR1, PWMCTRL_SPKR2, etc. */ + break; + default: + set_reg(reg, value); + } + return 0; +} + +static unsigned int ab5500_codec_read_reg(struct snd_soc_codec *codec, + unsigned int reg) +{ + return read_reg(reg); +} + + +static struct snd_soc_codec_driver ab5500_codec_drv = { + .probe = ab5500_codec_probe, + .remove = ab5500_codec_remove, + .suspend = ab5500_codec_suspend, + .resume = ab5500_codec_resume, + .read = ab5500_codec_read_reg, + .write = ab5500_codec_write_reg, +}; EXPORT_SYMBOL_GPL(ab5500_codec_drv); + +static inline void init_playback_route(void) +{ + /* if0_dld_l -> rx1 -> dac1 -> auxo1 */ + update_widgets_link(LINK, widget_if0_dld_l, widget_rx1, + RX1, RXx_DATA_MASK, 0x03 << RXx_DATA_SHIFT); + update_widgets_link(LINK, widget_rx1, widget_dac1, 0, 0, 0); + update_widgets_link(LINK, widget_dac1, widget_auxo1, + AUXO1_ADDER, DAC1_TO_X_MASK, 0xff); + + /* if0_dld_r -> rx2 -> dac2 -> auxo2 */ + update_widgets_link(LINK, widget_if0_dld_r, widget_rx2, + RX2, RXx_DATA_MASK, 0x04 << RXx_DATA_SHIFT); + update_widgets_link(LINK, widget_rx2, widget_dac2, 0, 0, 0); + update_widgets_link(LINK, widget_dac2, widget_auxo2, + AUXO2_ADDER, DAC2_TO_X_MASK, 0xff); + +} + +static inline void init_capture_route(void) +{ + /* mic bias - > mic2 inputs */ + update_widgets_link(LINK, widget_micbias1, widget_mic2p2, + 0, 0, 0); + update_widgets_link(LINK, widget_micbias1, widget_mic2n2, + 0, 0, 0); + update_widgets_link(LINK, widget_micbias2, widget_mic2p2, + 0, 0, 0); + update_widgets_link(LINK, widget_micbias2, widget_mic2n2, + 0, 0, 0); + + + /* mic2 inputs -> mic2 */ + update_widgets_link(LINK, widget_mic2p2, widget_mic2, + MIC2_INPUT_SELECT, MICxP2_SEL_MASK, 0xff); + update_widgets_link(LINK, widget_mic2n2, widget_mic2, + MIC2_INPUT_SELECT, MICxN2_SEL_MASK, 0xff); + + /* mic2 -> adc2 -> tx2 */ + update_widgets_link(LINK, widget_mic2, widget_adc2, + 0, 0, 0); + update_widgets_link(LINK, widget_adc2, widget_tx2, + TX2, TXx_MUX_MASK, 0); + + /* tx2 -> if0_uld_l & if0_uld_r */ + update_widgets_link(LINK, widget_tx2, widget_if0_uld_l, + INTERFACE0_ULD, I2Sx_ULD_L_MASK, + 0x02 << I2Sx_ULD_L_SHIFT); + update_widgets_link(LINK, widget_tx2, widget_if0_uld_r, + INTERFACE0_ULD, I2Sx_ULD_R_MASK, + 0x02 << I2Sx_ULD_R_SHIFT); + + /* mic2 -> apga2 */ + update_widgets_link(LINK, widget_mic2, widget_apga2, + ANALOG_LOOP_PGA2, APGAx_MUX_MIC2_MASK, + 1 << APGAx_MUX_MIC2_SHIFT); + + /* apga2 -> auxo1 & auxo2 */ + update_widgets_link(LINK, widget_apga2, widget_auxo1, + AUXO1_ADDER, APGA2_TO_X_MASK, + 1 << APGA2_TO_X_SHIFT); + update_widgets_link(LINK, widget_apga2, widget_auxo2, + AUXO2_ADDER, APGA2_TO_X_MASK, + 1 << APGA2_TO_X_SHIFT); +} + +static inline void init_playback_gain(void) +{ + /* 0x43, 0x0C: pure gain values */ + mask_set_reg(RX1_DPGA, RXx_DPGA_MASK, + 0x43 << RXx_DPGA_SHIFT); + mask_set_reg(RX2_DPGA, RXx_DPGA_MASK, + 0x43 << RXx_DPGA_SHIFT); + mask_set_reg(AUXO1, AUXOx_GAIN_MASK, 0x0C << AUXOx_GAIN_SHIFT); + mask_set_reg(AUXO2, AUXOx_GAIN_MASK, 0x0C << AUXOx_GAIN_SHIFT); +} + +static inline void init_capture_gain(void) +{ + /* 0x06, 0x0f: pure gain values */ + mask_set_reg(MIC1_GAIN, MICx_GAIN_MASK, 0x06 << MICx_GAIN_SHIFT); + mask_set_reg(TX_DPGA1, TX_DPGAx_MASK, 0x0f << TX_DPGAx_SHIFT); +} + +static int __devinit ab5500_platform_probe(struct platform_device *pdev) +{ + int ret; + u8 reg; + struct ab5500_codec_dai_data *codec_drvdata; + + ab5500_dev = &pdev->dev; + codec_drvdata = kzalloc(sizeof(struct ab5500_codec_dai_data), + GFP_KERNEL); + if (codec_drvdata == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, codec_drvdata); + ret = snd_soc_register_codec(ab5500_dev, &ab5500_codec_drv, + ab5500_dai_drv, + ARRAY_SIZE(ab5500_dai_drv)); + if (ret < 0) { + dev_err(ab5500_dev, "%s: Failed to register codec. " + "Error %d.\n", __func__, ret); + snd_soc_unregister_codec(ab5500_dev); + kfree(codec_drvdata); + } + /* Initialize the codec registers */ + for (reg = AB5500_FIRST_REG; reg <= AB5500_LAST_REG; reg++) + set_reg(reg, 0); + + mask_set_reg(CLOCK, CLOCK_REF_SELECT_MASK | CLOCK_ENABLE_MASK, + 1 << CLOCK_REF_SELECT_SHIFT | 1 << CLOCK_ENABLE_SHIFT); + init_playback_route(); + init_playback_gain(); + init_capture_route(); + init_capture_gain(); + memset(&pm_stack, 0, sizeof(pm_stack)); + return ret; +} + +static int __devexit ab5500_platform_remove(struct platform_device *pdev) +{ + mask_set_reg(CLOCK, CLOCK_ENABLE_MASK, 0); + snd_soc_unregister_codec(ab5500_dev); + kfree(platform_get_drvdata(pdev)); + ab5500_dev = NULL; + return 0; +} + +static int ab5500_platform_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +static int ab5500_platform_resume(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver ab5500_platform_driver = { + .driver = { + .name = "ab5500-codec", + .owner = THIS_MODULE, + }, + .probe = ab5500_platform_probe, + .remove = ab5500_platform_remove, + .suspend = ab5500_platform_suspend, + .resume = ab5500_platform_resume, +}; + +static int __devinit ab5500_init(void) +{ + int ret; + + /* Register codec platform driver. */ + ret = platform_driver_register(&ab5500_platform_driver); + if (ret) { + pr_err("%s: Error %d: Failed to register codec platform " + "driver.\n", __func__, ret); + } + return ret; +} + +static void __devexit ab5500_exit(void) +{ + platform_driver_unregister(&ab5500_platform_driver); +} + +module_init(ab5500_init); +module_exit(ab5500_exit); + +MODULE_DESCRIPTION("AB5500 Codec driver"); +MODULE_AUTHOR("Xie Xiaolei <xie.xiaolei@stericsson.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ab5500.h b/sound/soc/codecs/ab5500.h new file mode 100644 index 00000000000..a401280f3cc --- /dev/null +++ b/sound/soc/codecs/ab5500.h @@ -0,0 +1,399 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Xie Xiaolei <xie.xiaolei@etericsson.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. + */ +#ifndef AB5500_CODEC_REGISTERS_H +#define AB5500_CODEC_REGISTERS_H + +#define AB5500_SUPPORTED_RATE (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +#define AB5500_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + + +/* MIC BIAS */ + +#define MIC_BIAS1 0x00 +#define MIC_BIAS2 0x01 +#define MBIAS2_OUT_V_MASK 0x04 +#define MBIAS2_OUT_V_SHIFT 2 +#define MBIASx_PWR_MASK 0x02 +#define MBIASx_PWR_SHIFT 1 +#define MBIASx_PDN_IMP_MASK 0x01 +#define MBIASx_PDN_IMP_SHIFT 0 + +#define MIC_BIAS2_VAD 0x02 +#define MBIAS2_R_INT_MASK 0x01 +#define MBIAS2_R_INT_SHIFT 0 + +/* MIC */ +#define MIC1_GAIN 0x03 +#define MIC2_GAIN 0x04 +#define MICx_GAIN_MASK 0xF0 +#define MICx_GAIN_SHIFT 4 +#define MICx_IN_IMP_MASK 0x0C +#define MICx_IN_IMP_SHIFT 2 +#define MICx_PWR_MASK 0x01 +#define MICx_PWR_SHIFT 0 + +#define MIC1_INPUT_SELECT 0x05 +#define MIC2_INPUT_SELECT 0x06 +#define MICxP1_SEL_MASK 0x80 +#define MICxP1_SEL_SHIFT 7 +#define MICxN1_SEL_MASK 0x40 +#define MICxN1_SEL_SHIFT 6 +#define MICxP2_SEL_MASK 0x20 +#define MICxP2_SEL_SHIFT 5 +#define MICxN2_SEL_MASK 0x10 +#define MICxN2_SEL_SHIFT 4 +#define LINEIN_SEL_MASK 0x03 +#define LINEIN_SEL_SHIFT 0 + +#define MIC1_VMID_SELECT 0x07 +#define MIC2_VMID_SELECT 0x08 +#define VMIDx_ENABLE_MASK 0xC0 +#define VMIDx_ENABLE_SHIFT 6 +#define VMIDx_LINEIN1_N_MASK 0x20 +#define VMIDx_LINEIN1_N_SHIFT 5 +#define VMIDx_LINEIN2_N_MASK 0x10 +#define VMIDx_LINEIN2_N_SHIFT 4 +#define VMIDx_MICxP1_MASK 0x08 +#define VMIDx_MICxP1_SHIFT 3 +#define VMIDx_MICxP2_MASK 0x04 +#define VMIDx_MICxP2_SHIFT 2 +#define VMIDx_MICxN1_MASK 0x02 +#define VMIDx_MICxN1_SHIFT 1 +#define VMIDx_MICxN2_MASK 0x01 +#define VMIDx_MICxN2_SHIFT 0 + +#define MIC2_TO_MIC1 0x09 +#define MIC2_TO_MIC1_MASK 0x03 +#define MIC2_TO_MIC1_SHIFT 0 + +/* Analog Loop */ +#define ANALOG_LOOP_PGA1 0x0A +#define ANALOG_LOOP_PGA2 0x0B +#define APGAx_GAIN_MASK 0xF8 +#define APGAx_GAIN_SHIFT 3 +#define APGAx_PWR_MASK 0x04 +#define APGAx_PWR_SHIFT 2 +#define APGAx_MUX_MASK 0x03 +#define APGAx_MUX_SHIFT 0 +#define APGAx_MUX_MIC1_MASK 0x01 +#define APGAx_MUX_MIC1_SHIFT 0 +#define APGAx_MUX_MIC2_MASK 0x02 +#define APGAx_MUX_MIC2_SHIFT 1 + +#define APGA_VMID_SELECT 0x0C +#define VMID_APGA1_ENABLE_MASK 0xC0 +#define VMID_APGA1_ENABLE_SHIFT 6 +#define VMID_APGA1_LINEIN1_MASK 0x20 +#define VMID_APGA1_LINEIN1_SHIFT 5 +#define VMID_APGA2_ENABLE_MASK 0x0C +#define VMID_APGA2_ENABLE_SHIFT 2 +#define VMID_APGA2_LINEIN2_MASK 0x02 +#define VMID_APGA2_LINEIN2_SHIFT 1 + +/* Output Amplifiers */ +#define EAR_PWR 0x0D +#define EAR_PWR_MODE_MASK 0xC0 +#define EAR_PWR_MODE_SHIFT 6 +#define EAR_PWR_VMID_MASK 0x30 +#define EAR_PWR_VMID_SHIFT 4 +#define EAR_PWR_MASK 0x01 +#define EAR_PWR_SHIFT 0 + +#define EAR_GAIN 0x0E +#define EAR_GAIN_MASK 0x1F +#define EAR_GAIN_SHIFT 0 + +#define AUXO1 0x0F +#define AUXO2 0x10 +#define AUXO3 0x11 +#define AUXO4 0x12 +#define AUXOx_PWR_MASK 0x80 +#define AUXOx_PWR_SHIFT 7 +#define AUXOx_INV_MASK 0x40 +#define AUXOx_INV_SHIFT 6 +#define AUXOx_PULLDOWN_MASK 0x20 +#define AUXOx_PULLDOWN_SHIFT 5 +#define AUXOx_GAIN_MASK 0x0F +#define AUXOx_GAIN_SHIFT 0 + +#define AUXO12_PWR_MODE 0x13 +#define AUXO34_PWR_MODE 0x14 +#define AUXOxy_PWR_MODE_MASK 0x07 +#define AUXOxy_PWR_MODE_SHIFT 0 + +#define NEG_CHARGE_PUMP 0x15 +#define NEG_CHARGE_PUMP_MODE_MASK 0x02 +#define NEG_CHARGE_PUMP_MODE_SHIFT 1 +#define NEG_CHARGE_PUMP_PWR_MASK 0x01 +#define NEG_CHARGE_PUMP_PWR_SHIFT 0 + +#define ENV_THR 0x16 +#define ENV_THR_HIGH_MASK 0xF0 +#define ENV_THR_HIGH_SHIFT 4 +#define ENV_THR_LOW_MASK 0x0F +#define ENV_THR_LOW_SHIFT 0 + +#define ENV_DECAY_TIME 0x17 +#define ENV_DECAY_TIME_CP_LV_MASK 0x20 +#define ENV_DECAY_TIME_CP_LV_SHIFT 5 +#define ENV_DECAY_TIME_DET_CP_MASK 0x10 +#define ENV_DECAY_TIME_DET_CP_SHIFT 4 +#define ENV_DECAY_TIME_MASK 0x0F +#define ENV_DECAY_TIME_SHIFT 0 + +#define DC_CANCEL 0x18 +#define DC_CANCEL_SPKR2_MASK 0x10 +#define DC_CANCEL_SPKR2_SHIFT 4 +#define DC_CANCEL_SPKR1_MASK 0x08 +#define DC_CANCEL_SPKR1_SHIFT 3 +#define DC_CANCEL_AUXO34_MASK 0x04 +#define DC_CANCEL_AUXO34_SHIFT 2 +#define DC_CANCEL_AUXO12_MASK 0x02 +#define DC_CANCEL_AUXO12_SHIFT 1 +#define DC_CANCEL_OFFSET_CLOCK_MASK 0x01 +#define DC_CANCEL_OFFSET_CLOCK_SHIFT 0 + +#define SPKR1 0x19 +#define SPKR2 0x1A +#define SPKRx_PWR_MASK 0xC0 +#define SPKRx_PWR_SHIFT 6 +#define SPKRx_PWR_VBR_VALUE 0x40 +#define SPKRx_PWR_CLS_D_VALUE 0x80 +#define SPKRx_PWR_CLS_AB_VALUE 0xC0 +#define SPKR1_VMID_MASK 0x20 +#define SPKR1_VMID_SHIFT 5 +#define SPKRx_GAIN_MASK 0x1F +#define SPKRx_GAIN_SHIFT 0 + +#define SPKR_OVCR 0x1B +#define SPKR_OVCR_PROT2_MASK 0x80 +#define SPKR_OVCR_PROT2_SHIFT 7 +#define SPKR_OVCR_TRIM2_MASK 0x70 +#define SPKR_OVCR_TRIM2_SHIFT 4 +#define SPKR_OVCR_PROT1_MASK 0x08 +#define SPKR_OVCR_PROT1_SHIFT 3 +#define SPKR_OVCR_TRIM1_MASK 0x07 +#define SPKR_OVCR_TRIM1_SHIFT 0 + +#define PWMCTRL_SPKR1 0x1C +#define PWMCTRL_SPKR2 0x1F +#define PWMCTRL_SPKRx_N1_POL_MASK 0x80 +#define PWMCTRL_SPKRx_N1_POL_SHIFT 7 +#define PWMCTRL_SPKRx_P1_POL_MASK 0x40 +#define PWMCTRL_SPKRx_P1_POL_SHIFT 6 +#define PWMCTRL_SPKRx_MASK 0x04 +#define PWMCTRL_SPKRx_SHIFT 2 +#define PWMCTRL_SPKRxN_MASK 0x02 +#define PWMCTRL_SPKRxN_SHIFT 1 +#define PWMCTRL_SPKRxP_MASK 0x01 +#define PWMCTRL_SPKRxP_SHIFT 0 + +#define PWM_SPKR1N 0x1D +#define PWM_SPKR2N 0x20 +#define PWM_SPKR1P 0x1E +#define PWM_SPKR2P 0x21 +#define PWM_SPKRxy_DUT_CYC_MASK 0xFF +#define PWM_SPKRxy_DUT_CYC_SHIFT 0 + +#define SPKR1_CLK_DIV 0x22 +#define SPKR2_CLK_DIV 0x23 +#define SPKRx_CLK_DIV_MASK 0x3F +#define SPKRx_CLK_DIV_SHIFT 0 + +#define LINE1 0x24 +#define LINE2 0x25 +#define LINEx_PWR_MASK 0x80 +#define LINEx_PWR_SHIFT 7 +#define LINEx_INV_MASK 0x40 +#define LINEx_INV_SHIFT 6 +#define LINEx_TO_USB_MASK 0x20 +#define LINEx_TO_USB_SHIFT 5 +#define LINEx_VMID_BUFF_MASK 0x10 +#define LINEx_VMID_BUFF_SHIFT 4 +#define LINEx_GAIN_MASK 0x0F +#define LINEx_GAIN_SHIFT 0 + +#define USB_AUDIO 0x26 +#define USB_AUDIO_MIC_MUX_MASK 0x03 +#define USB_AUDIO_MIC_MUX_SHIFT 0 + +#define EAR_ADDER 0x28 +#define AUXO1_ADDER 0x29 +#define AUXO2_ADDER 0x2A +#define AUXO3_ADDER 0x2B +#define AUXO4_ADDER 0x2C +#define SPKR1_ADDER 0x2D +#define SPKR2_ADDER 0x2E +#define LINE1_ADDER 0x2F +#define LINE2_ADDER 0x30 +#define APGA2_TO_X_MASK 0x10 +#define APGA2_TO_X_SHIFT 4 +#define APGA1_TO_X_MASK 0x08 +#define APGA1_TO_X_SHIFT 3 +#define DAC3_TO_X_MASK 0x04 +#define DAC3_TO_X_SHIFT 2 +#define DAC2_TO_X_MASK 0x02 +#define DAC2_TO_X_SHIFT 1 +#define DAC1_TO_X_MASK 0x01 +#define DAC1_TO_X_SHIFT 0 + +#define EAR_TO_MIC2 0x31 +#define SPKR1_TO_MIC2 0x32 +#define SPKR2_TO_MIC2 0x33 +#define EAR_TO_MIC2_MASK 0x01 +#define EAR_TO_MIC2_SHIFT 0 + +#define ADC_LOW_PWR 0x35 +#define ADC_LOW_PWR_MASK 0x01 +#define ADC_LOW_PWR_SHIFT 0 + +#define TX1 0x36 +#define TX2 0x37 +#define TXx_MUX_MASK 0x60 +#define TXx_MUX_SHIFT 6 +#define TXx_FS_MASK 0x10 +#define TXx_FS_SHIFT 4 +#define TXx_HP_FILTER_MASK 0x0C +#define TXx_HP_FILTER_SHIFT 2 +#define TXx_PWR_MASK 0x02 +#define TXx_PWR_SHIFT 1 +#define ADCx_PWR_MASK 0x01 +#define ADCx_PWR_SHIFT 0 + +#define RX1 0x38 +#define RX2 0x39 +#define RX3 0x3A +#define RXx_DATA_MASK 0x70 +#define RXx_DATA_SHIFT 4 +#define RXx_PWR_MASK 0x08 +#define RXx_PWR_SHIFT 3 +#define DACx_PWR_MASK 0x04 +#define DACx_PWR_SHIFT 2 +#define DACx_PWR_MODE_MASK 0x03 +#define DACx_PWR_MODE_SHIFT 0 + +#define TX_DPGA1 0x3B +#define TX_DPGA2 0x3C +#define TX_DPGAx_MASK 0x0F +#define TX_DPGAx_SHIFT 0 + +#define RX1_DPGA 0x3D +#define RX2_DPGA 0x3E +#define RX3_DPGA 0x3F +#define RXx_DPGA_MASK 0x7F +#define RXx_DPGA_SHIFT 0 + +#define ST1_PGA 0x40 +#define ST2_PGA 0x41 +#define STx_HP_FILTER_MASK 0x60 +#define STx_HP_FILTER_SHIFT 6 +#define STx_MUX_MASK 0x10 +#define STx_MUX_SHIFT 4 +#define STx_PGA_MASK 0x0F +#define STx_PGA_SHIFT 0 + +#define CLOCK 0x42 +#define CLOCK_REF_SELECT_MASK 0x02 +#define CLOCK_REF_SELECT_SHIFT 1 +#define CLOCK_ENABLE_MASK 0x01 +#define CLOCK_ENABLE_SHIFT 0 + +#define INTERFACE0 0x43 +#define INTERFACE1 0x45 +#define I2Sx_WORDLENGTH_MASK 0x40 +#define I2Sx_WORDLENGTH_SHIFT 6 +#define MASTER_GENx_PWR_MASK 0x20 +#define MASTER_GENx_PWR_SHIFT 5 +#define I2Sx_MODE_MASK 0x10 +#define I2Sx_MODE_SHIFT 4 +#define I2Sx_TRISTATE_MASK 0x08 +#define I2Sx_TRISTATE_SHIFT 3 +#define I2Sx_PULLDOWN_MASK 0x04 +#define I2Sx_PULLDOWN_SHIFT 2 +#define I2Sx_SR_MASK 0x03 +#define I2Sx_SR_SHIFT 0 +#define I2Sx_SR_8000Hz 0 +#define I2Sx_SR_16000Hz 1 +#define I2Sx_SR_44100Hz 2 +#define I2Sx_SR_48000Hz 3 + +#define INTERFACE0_ULD 0x44 +#define INTERFACE1_ULD 0x46 +#define I2Sx_ULD_R_MASK 0x70 +#define I2Sx_ULD_R_SHIFT 4 +#define I2Sx_ULD_L_MASK 0x07 +#define I2Sx_ULD_L_SHIFT 0 + +#define INTERFACE_SWAP 0x47 +#define IO_SWAP0_MASK 0x02 +#define IO_SWAP0_SHIFT 1 +#define IO_SWAP1_MASK 0x01 +#define IO_SWAP1_SHIFT 0 + +#define AB5500_FIRST_REG MIC_BIAS1 +#define AB5500_LAST_REG INTERFACE_SWAP + +#define AB5500_VIRTUAL_REG1 (AB5500_LAST_REG + 1) +#define IF0_DLD_L_PW_SHIFT 0 +#define IF0_DLD_R_PW_SHIFT 1 +#define IF0_ULD_L_PW_SHIFT 2 +#define IF0_ULD_R_PW_SHIFT 3 +#define IF1_DLD_L_PW_SHIFT 4 +#define IF1_DLD_R_PW_SHIFT 5 +#define IF1_ULD_L_PW_SHIFT 6 +#define IF1_ULD_R_PW_SHIFT 7 + +#define AB5500_VIRTUAL_REG2 (AB5500_LAST_REG + 2) +#define MIC1P1_PW_SHIFT 0 +#define MIC1N1_PW_SHIFT 1 +#define MIC1P2_PW_SHIFT 2 +#define MIC1N2_PW_SHIFT 3 +#define MIC2P1_PW_SHIFT 4 +#define MIC2N1_PW_SHIFT 5 +#define MIC2P2_PW_SHIFT 6 +#define MIC2N2_PW_SHIFT 7 + +#define AB5500_VIRTUAL_REG3 (AB5500_LAST_REG + 3) +#define SPKR1_MODE_MASK 0x03 +#define SPKR1_MODE_SHIFT 0 +#define SPKR1_MODE_VBR_VALUE 0 +#define SPKR1_MODE_CLS_D_VALUE 1 +#define SPKR1_MODE_CLS_AB_VALUE 2 +#define SPKR1_ADDER_PWR_SHIFT 2 +#define SPKR1_PWR_SHIFT 3 +#define SPKR2_MODE_MASK 0x10 +#define SPKR2_MODE_SHIFT 4 +#define SPKR2_MODE_VBR_VALUE 0 +#define SPKR2_MODE_CLS_D_VALUE 1 +#define SPKR2_ADDER_PWR_SHIFT 5 +#define SPKR2_PWR_SHIFT 6 + +#define AB5500_VIRTUAL_REG4 (AB5500_LAST_REG + 4) +#define PWM_SPKR1_PWR_SHIFT 0 +#define PWM_SPKR2_PWR_SHIFT 1 +#define PWM_SPKR1N_PWR_SHIFT 2 +#define PWM_SPKR1P_PWR_SHIFT 3 +#define PWM_SPKR2N_PWR_SHIFT 4 +#define PWM_SPKR2P_PWR_SHIFT 5 + +#define AB5500_VIRTUAL_REG5 (AB5500_LAST_REG + 5) +#define PWM_SPKR1N_SEL_SHIFT 0 +#define PWM_SPKR1P_SEL_SHIFT 1 +#define PWM_SPKR2N_SEL_SHIFT 2 +#define PWM_SPKR2P_SEL_SHIFT 3 + +#define DUMMY_REG 0xff +#endif diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig index 2b1327240fe..a5cdf4358b5 100644 --- a/sound/soc/ux500/Kconfig +++ b/sound/soc/ux500/Kconfig @@ -15,7 +15,15 @@ config SND_SOC_UX500_AB3550 select SND_SOC_AB3550 default n help - Say Y if you want to include AB3550 codec (Petronella MSA). + Say Y if you want to include the AB3550 codec. + +config SND_SOC_UX500_AB5500 + bool "Codec - AB5500" + depends on SND_SOC_UX500 && (UX500_SOC_DB8500 || UX500_SOC_DB5500) && AB5500_CORE + select SND_SOC_AB5500 + default n + help + Say Y if you want to include the AB5500 codec. config SND_SOC_UX500_CG29XX bool "Codec - CG29xx" diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile index e7917509681..84e642bf193 100644 --- a/sound/soc/ux500/Makefile +++ b/sound/soc/ux500/Makefile @@ -7,27 +7,37 @@ CFLAGS_ux500_av8100.o := -DDEBUG endif ifdef CONFIG_SND_SOC_UX500_AB3550 -snd-soc-ux500-av8100-objs := ux500_ab3550.o +snd-soc-ux500-AB3550-objs := ux500_ab3550.o obj-$(CONFIG_SND_SOC_UX500_AB3550) += ux500_ab3550.o endif +ifdef CONFIG_SND_SOC_UX500_AB5500 +snd-soc-ux500-AB5500-objs := ux500_ab5500.o +obj-$(CONFIG_SND_SOC_UX500_AB5500) += ux500_ab5500.o +endif + ifdef CONFIG_SND_SOC_UX500_AV8100 snd-soc-ux500-av8100-objs := ux500_av8100.o obj-$(CONFIG_SND_SOC_UX500_AV8100) += ux500_av8100.o endif ifdef CONFIG_SND_SOC_UX500_CG29XX -snd-soc-ux500-av8100-objs := ux500_cg29xx.o +snd-soc-ux500-cg29xx-objs := ux500_cg29xx.o obj-$(CONFIG_SND_SOC_UX500_CG29XX) += ux500_cg29xx.o endif -#ifdef UX500_SOC_DB8500 -snd-soc-u8500-objs := u8500.o -obj-y += ux500_pcm.o ux500_msp_dai.o snd-soc-u8500.o -#endif +ifdef CONFIG_UX500_SOC_DB8500 +CFLAGS_ux500_msp_dai.o += -DSTE_PLATFORM_U8500 +CFLAGS_ux500_pcm.o += -DSTE_PLATFORM_U8500 +snd-soc-u8500-objs := ux500_pcm.o ux500_msp_dai.o u8500.o +obj-$(CONFIG_UX500_SOC_DB8500) += snd-soc-u8500.o +endif -ifdef UX500_SOC_DB5500 -snd-soc-u8500-objs := u5500.o -obj-$(UX500_SOC_DB5500) += ux500_pcm.o ux500_msp_dai.o snd-soc-u5500.o +ifdef CONFIG_UX500_SOC_DB5500 +CFLAGS_ux500_msp_dai.o += -DSTE_PLATFORM_U5500 +CFLAGS_ux500_pcm.o += -DSTE_PLATFORM_U5500 +snd-soc-u5500-objs := ux500_pcm.o ux500_msp_dai.o u5500.o +obj-$(CONFIG_UX500_SOC_DB5500) += snd-soc-u5500.o endif + diff --git a/sound/soc/ux500/u5500.c b/sound/soc/ux500/u5500.c new file mode 100644 index 00000000000..1993f5dfe0c --- /dev/null +++ b/sound/soc/ux500/u5500.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Xie Xiaolei (xie.xiaolei@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/io.h> +#include <linux/spi/spi.h> +#include <sound/soc.h> +#include <sound/initval.h> + +#include "ux500_pcm.h" +#include "ux500_msp_dai.h" + +#ifdef CONFIG_SND_SOC_UX500_AB5500 +#include "ux500_ab5500.h" +#endif + +static struct platform_device *u5500_platform_dev; + +/* Create dummy devices for platform drivers */ + +static struct platform_device ux500_pcm = { + .name = "ux500-pcm", + .id = 0, + .dev = { + .platform_data = NULL, + }, +}; + +/* Define the whole U5500 soundcard, linking platform to the codec-drivers */ +struct snd_soc_dai_link u5500_dai_links[] = { +#ifdef CONFIG_SND_SOC_UX500_AB5500 + { + .name = "ab5500_0", + .stream_name = "ab5500_0", + .cpu_dai_name = "i2s.0", + .codec_dai_name = "ab5500-codec-dai.0", + .platform_name = "ux500-pcm.0", + .codec_name = "ab5500-codec.9", + .init = NULL, + .ops = (struct snd_soc_ops[]) { + { + .startup = ux500_ab5500_startup, + .shutdown = ux500_ab5500_shutdown, + .hw_params = ux500_ab5500_hw_params, + } + } + }, + { + .name = "ab5500_1", + .stream_name = "ab5500_1", + .cpu_dai_name = "i2s.1", + .codec_dai_name = "ab5500-codec-dai.1", + .platform_name = "ux500-pcm.0", + .codec_name = "ab5500-codec.9", + .init = NULL, + .ops = (struct snd_soc_ops[]) { + { + .startup = ux500_ab5500_startup, + .shutdown = ux500_ab5500_shutdown, + .hw_params = ux500_ab5500_hw_params, + } + } + }, +#endif +}; + +static struct snd_soc_card u5500_drvdata = { + .name = "U5500-card", + .probe = NULL, + .dai_link = u5500_dai_links, + .num_links = ARRAY_SIZE(u5500_dai_links), +}; + +static int __init u5500_soc_init(void) +{ + int ret; + + pr_debug("%s: Enter.\n", __func__); + + platform_device_register(&ux500_pcm); + + u5500_platform_dev = platform_device_alloc("soc-audio", -1); + if (!u5500_platform_dev) + return -ENOMEM; + + platform_set_drvdata(u5500_platform_dev, &u5500_drvdata); + u5500_drvdata.dev = &u5500_platform_dev->dev; + + ret = platform_device_add(u5500_platform_dev); + if (ret) { + pr_err("%s: Error: Failed to add platform device (%s).\n", + __func__, + u5500_drvdata.name); + platform_device_put(u5500_platform_dev); + } + + return ret; +} + +static void __exit u5500_soc_exit(void) +{ + pr_debug("%s: Enter.\n", __func__); + + platform_device_unregister(u5500_platform_dev); +} + +module_init(u5500_soc_init); +module_exit(u5500_soc_exit); + +MODULE_LICENSE("GPLv2"); diff --git a/sound/soc/ux500/ux500_ab5500.c b/sound/soc/ux500/ux500_ab5500.c new file mode 100644 index 00000000000..89ecc19dfdc --- /dev/null +++ b/sound/soc/ux500/ux500_ab5500.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: 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 <sound/soc.h> +#include "../codecs/ab5500.h" + +int ux500_ab5500_startup(struct snd_pcm_substream *substream) +{ + printk(KERN_DEBUG "%s: Enter.\n", __func__); + + return 0; +} + +void ux500_ab5500_shutdown(struct snd_pcm_substream *substream) +{ + printk(KERN_DEBUG "%s: Enter.\n", __func__); +} + +int ux500_ab5500_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) { + printk(KERN_DEBUG "%s: snd_soc_dai_set_fmt failed with %d.\n", + __func__, + ret); + return ret; + } + + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) { + printk(KERN_DEBUG "%s: snd_soc_dai_set_fmt failed with %d.\n", + __func__, + ret); + return ret; + } + + return ret; +} diff --git a/sound/soc/ux500/ux500_ab5500.h b/sound/soc/ux500/ux500_ab5500.h new file mode 100644 index 00000000000..977851bbce6 --- /dev/null +++ b/sound/soc/ux500/ux500_ab5500.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Xie Xiaolei (xie.xiaolei@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. + */ + +#ifndef UX500_AB5500_H +#define UX500_AB5500_H + +int ux500_ab5500_startup(struct snd_pcm_substream *substream); + +void ux500_ab5500_shutdown(struct snd_pcm_substream *substream); + +int ux500_ab5500_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); + +#endif diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index b0d327f9ba6..96d4cf9f6f9 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -472,6 +472,8 @@ static void ux500_msp_dai_compile_msp_config(struct snd_pcm_substream *substream msp_config->work_mode = MSP_DMA_MODE; msp_config->frame_freq = rate; + printk(KERN_INFO "%s: input_clock_freq = %u, frame_freq = %u.\n", + __func__, msp_config->input_clock_freq, msp_config->frame_freq); /* To avoid division by zero in I2S-driver (i2s_setup) */ prot_desc->total_clocks_for_one_frame = 1; diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h index 84130f5d624..2b2f3776efc 100644 --- a/sound/soc/ux500/ux500_msp_dai.h +++ b/sound/soc/ux500/ux500_msp_dai.h @@ -34,7 +34,12 @@ #define FRAME_PER_8_SLOTS 138 #define FRAME_PER_16_SLOTS 277 -#define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000; +#ifdef STE_PLATFORM_U5500 +#define UX500_MSP_INTERNAL_CLOCK_FREQ 13000000 +#else +#define UX500_MSP_INTERNAL_CLOCK_FREQ 38400000 +#endif + #define UX500_MSP_MIN_CHANNELS 1 #define UX500_MSP_MAX_CHANNELS 8 diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index f8c92b59da2..2655e257ac1 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -105,7 +105,9 @@ void ux500_pcm_dma_eot_handler(void *data) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *dai = rtd->cpu_dai; - pr_err("%s: MSP %d (%s): Enter.\n", __func__, dai->id, stream_str(substream)); + pr_debug("%s: MSP %d (%s): Enter.\n", + __func__, + dai->id, stream_str(substream)); if (substream) { runtime = substream->runtime; |