summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-08-31 22:14:51 +0100
committerMark Brown <broonie@kernel.org>2026-08-31 22:14:51 +0100
commit05bbe220993f6d9f85fa831ea2fe6b607fbc529e (patch)
tree4fe92caf61c2ee29eceac2064dc912f3ba26a618
parentfbf2c660bac863f0ac372b677ee55bf775c94594 (diff)
parent711178754287db3fd0f7accff3c2a7575f8873b7 (diff)
downloadlinux-next-05bbe220993f6d9f85fa831ea2fe6b607fbc529e.tar.gz
linux-next-05bbe220993f6d9f85fa831ea2fe6b607fbc529e.zip
ASoC: ab8500: Fix codec reset, routing and DAI setup
Linus Walleij <linusw@kernel.org> says: Repair independent correctness problems in the AB8500 codec driver: reset sequencing, AB8505 digital microphone GPIO setup, the capture power graph, serial format setup, and TDM slot programming. Remove the sidetone apply control which became destructive when its coefficient controls were removed. Link: https://patch.msgid.link/20260831-ab8500-codec-fixes-v1-0-f85024e717e3@kernel.org
-rw-r--r--sound/soc/codecs/ab8500-codec.c682
1 files changed, 273 insertions, 409 deletions
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index 2cf96cbdd294..e1a0e35836e6 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -14,17 +14,14 @@
* for ST-Ericsson.
*/
-#include <linux/cleanup.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
-#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
-#include <linux/mutex.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500-sysctrl.h>
@@ -54,32 +51,9 @@
/* Macrocell register definitions */
#define AB8500_GPIO_DIR4_REG 0x13 /* Bank AB8500_MISC */
-/* Nr of FIR/IIR-coeff banks in ANC-block */
-#define AB8500_NR_OF_ANC_COEFF_BANKS 2
-
-/* Minimum duration to keep ANC IIR Init bit high or
-low before proceeding with the configuration sequence */
-#define AB8500_ANC_SM_DELAY 2000
-
-/* Sidetone states */
-static const char * const enum_sid_state[] = {
- "Unconfigured",
- "Apply FIR",
- "FIR is configured",
-};
-enum sid_state {
- SID_UNCONFIGURED = 0,
- SID_APPLY_FIR = 1,
- SID_FIR_CONFIGURED = 2,
-};
-
/* Private data for AB8500 device-driver */
struct ab8500_codec_drvdata {
struct regmap *regmap;
- struct mutex ctrl_lock;
-
- /* Sidetone */
- enum sid_state sid_status;
};
static inline const char *amic_micbias_str(enum amic_micbias micbias)
@@ -259,7 +233,7 @@ static const struct snd_kcontrol_new dapm_anc_in_select[] = {
/* ANC - Enable/Disable */
static const struct snd_kcontrol_new dapm_anc_enable[] = {
SOC_DAPM_SINGLE("Switch", AB8500_ANCCONF1,
- AB8500_ANCCONF1_ENANC, 0, 0),
+ AB8500_ANCCONF1_ENANC, 1, 0),
};
/* ANC to Earpiece - Mute */
@@ -341,12 +315,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
/* DA/AD */
- SND_SOC_DAPM_INPUT("ADC Input"),
- SND_SOC_DAPM_ADC("ADC", "ab8500_0c", SND_SOC_NOPM, 0, 0),
-
- SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_OUTPUT("DAC Output"),
-
SND_SOC_DAPM_AIF_IN("DA_IN1", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("DA_IN2", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("DA_IN3", NULL, 0, SND_SOC_NOPM, 0, 0),
@@ -538,9 +506,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("AD3 Channel Volume",
SND_SOC_NOPM, 0, 0,
NULL, 0),
- SND_SOC_DAPM_MIXER("AD3 Enable",
- AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34, 0,
- NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AD34 Enable", AB8500_ADPATHENA,
+ AB8500_ADPATHENA_ENAD34, 0, NULL, 0),
/* Mic 2 */
@@ -599,9 +566,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
SND_SOC_NOPM, 0, 0,
NULL, 0),
- SND_SOC_DAPM_MIXER("AD12 Enable",
- AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD12, 0,
- NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AD12 Enable", AB8500_ADPATHENA,
+ AB8500_ADPATHENA_ENAD12, 0, NULL, 0),
/* HD Capture path */
@@ -615,12 +581,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("AD6 Channel Volume",
SND_SOC_NOPM, 0, 0,
NULL, 0),
- SND_SOC_DAPM_MIXER("AD57 Enable",
- AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0,
- NULL, 0),
- SND_SOC_DAPM_MIXER("AD68 Enable",
- AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0,
- NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AD5768 Enable", AB8500_ADPATHENA,
+ AB8500_ADPATHENA_ENAD5768, 0, NULL, 0),
/* Digital Microphone path */
@@ -652,15 +614,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("AD4 Channel Volume",
SND_SOC_NOPM, 0, 0,
NULL, 0),
- SND_SOC_DAPM_MIXER("AD4 Enable",
- AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34,
- 0, NULL, 0),
-
/* Acoustical Noise Cancellation path */
- SND_SOC_DAPM_INPUT("ANC Configure Input"),
- SND_SOC_DAPM_OUTPUT("ANC Configure Output"),
-
SND_SOC_DAPM_MUX("ANC Source",
SND_SOC_NOPM, 0, 0,
dapm_anc_in_select),
@@ -703,24 +658,13 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
{"Main Supply", NULL, "Audio Power"},
{"Main Supply", NULL, "Audio Analog Power"},
- {"DAC", NULL, "ab8500_0p"},
- {"DAC", NULL, "Main Supply"},
- {"ADC", NULL, "ab8500_0c"},
- {"ADC", NULL, "Main Supply"},
-
- /* ANC Configure */
- {"ANC Configure Input", NULL, "Main Supply"},
- {"ANC Configure Output", NULL, "ANC Configure Input"},
-
- /* AD/DA */
- {"ADC", NULL, "ADC Input"},
- {"DAC Output", NULL, "DAC"},
-
/* Powerup charge pump if DA1/2 is in use */
{"DA_IN1", NULL, "ab8500_0p"},
+ {"DA_IN1", NULL, "Main Supply"},
{"DA_IN1", NULL, "Charge Pump"},
{"DA_IN2", NULL, "ab8500_0p"},
+ {"DA_IN2", NULL, "Main Supply"},
{"DA_IN2", NULL, "Charge Pump"},
/* Headset path */
@@ -755,8 +699,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
/* HF or LineOut path */
{"DA_IN3", NULL, "ab8500_0p"},
+ {"DA_IN3", NULL, "Main Supply"},
{"DA3 Channel Volume", NULL, "DA_IN3"},
{"DA_IN4", NULL, "ab8500_0p"},
+ {"DA_IN4", NULL, "Main Supply"},
{"DA4 Channel Volume", NULL, "DA_IN4"},
{"Speaker Left Source", "Audio Path", "DA3 Channel Volume"},
@@ -814,8 +760,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
/* Vibrator path */
{"DA_IN5", NULL, "ab8500_0p"},
+ {"DA_IN5", NULL, "Main Supply"},
{"DA5 Channel Volume", NULL, "DA_IN5"},
{"DA_IN6", NULL, "ab8500_0p"},
+ {"DA_IN6", NULL, "Main Supply"},
{"DA6 Channel Volume", NULL, "DA_IN6"},
{"VIB1 DAC", NULL, "DA5 Channel Volume"},
@@ -857,13 +805,15 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
{"AD1 Channel Volume", NULL, "AD1 Source Select"},
{"AD2 Channel Volume", NULL, "AD2 Source Select"},
- {"AD12 Enable", NULL, "AD1 Channel Volume"},
- {"AD12 Enable", NULL, "AD2 Channel Volume"},
+ {"AD1 Channel Volume", NULL, "AD12 Enable"},
+ {"AD2 Channel Volume", NULL, "AD12 Enable"},
- {"AD_OUT1", NULL, "ab8500_0c"},
- {"AD_OUT1", NULL, "AD12 Enable"},
- {"AD_OUT2", NULL, "ab8500_0c"},
- {"AD_OUT2", NULL, "AD12 Enable"},
+ {"ab8500_0c", NULL, "AD_OUT1"},
+ {"AD_OUT1", NULL, "Main Supply"},
+ {"AD_OUT1", NULL, "AD1 Channel Volume"},
+ {"ab8500_0c", NULL, "AD_OUT2"},
+ {"AD_OUT2", NULL, "Main Supply"},
+ {"AD_OUT2", NULL, "AD2 Channel Volume"},
/* Mic 1 */
@@ -880,11 +830,11 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
{"AD3 Source Select", "Mic 1", "MIC1 ADC"},
{"AD3 Channel Volume", NULL, "AD3 Source Select"},
+ {"AD3 Channel Volume", NULL, "AD34 Enable"},
- {"AD3 Enable", NULL, "AD3 Channel Volume"},
-
- {"AD_OUT3", NULL, "ab8500_0c"},
- {"AD_OUT3", NULL, "AD3 Enable"},
+ {"ab8500_0c", NULL, "AD_OUT3"},
+ {"AD_OUT3", NULL, "Main Supply"},
+ {"AD_OUT3", NULL, "AD3 Channel Volume"},
/* HD Capture path */
@@ -893,14 +843,15 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
{"AD5 Channel Volume", NULL, "AD5 Source Select"},
{"AD6 Channel Volume", NULL, "AD6 Source Select"},
+ {"AD5 Channel Volume", NULL, "AD5768 Enable"},
+ {"AD6 Channel Volume", NULL, "AD5768 Enable"},
- {"AD57 Enable", NULL, "AD5 Channel Volume"},
- {"AD68 Enable", NULL, "AD6 Channel Volume"},
-
- {"AD_OUT57", NULL, "ab8500_0c"},
- {"AD_OUT57", NULL, "AD57 Enable"},
- {"AD_OUT68", NULL, "ab8500_0c"},
- {"AD_OUT68", NULL, "AD68 Enable"},
+ {"ab8500_0c", NULL, "AD_OUT57"},
+ {"AD_OUT57", NULL, "Main Supply"},
+ {"AD_OUT57", NULL, "AD5 Channel Volume"},
+ {"ab8500_0c", NULL, "AD_OUT68"},
+ {"AD_OUT68", NULL, "Main Supply"},
+ {"AD_OUT68", NULL, "AD6 Channel Volume"},
/* Digital Microphone path */
@@ -911,17 +862,25 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
{"DMic 5", NULL, "V-DMIC"},
{"DMic 6", NULL, "V-DMIC"},
- {"AD1 Source Select", NULL, "DMic 1"},
- {"AD2 Source Select", NULL, "DMic 2"},
- {"AD3 Source Select", NULL, "DMic 3"},
- {"AD5 Source Select", NULL, "DMic 5"},
- {"AD6 Source Select", NULL, "DMic 6"},
+ {"DMIC1", NULL, "DMic 1"},
+ {"DMIC2", NULL, "DMic 2"},
+ {"DMIC3", NULL, "DMic 3"},
+ {"DMIC4", NULL, "DMic 4"},
+ {"DMIC5", NULL, "DMic 5"},
+ {"DMIC6", NULL, "DMic 6"},
- {"AD4 Channel Volume", NULL, "DMic 4"},
- {"AD4 Enable", NULL, "AD4 Channel Volume"},
+ {"AD1 Source Select", "DMic 1", "DMIC1"},
+ {"AD2 Source Select", "DMic 2", "DMIC2"},
+ {"AD3 Source Select", "DMic 3", "DMIC3"},
+ {"AD5 Source Select", "DMic 5", "DMIC5"},
+ {"AD6 Source Select", "DMic 6", "DMIC6"},
- {"AD_OUT4", NULL, "ab8500_0c"},
- {"AD_OUT4", NULL, "AD4 Enable"},
+ {"AD4 Channel Volume", NULL, "DMIC4"},
+ {"AD4 Channel Volume", NULL, "AD34 Enable"},
+
+ {"ab8500_0c", NULL, "AD_OUT4"},
+ {"AD_OUT4", NULL, "Main Supply"},
+ {"AD_OUT4", NULL, "AD4 Channel Volume"},
/* LineIn Bypass path */
@@ -946,13 +905,13 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
/* Sidetone Filter path */
- {"Sidetone Left Source", "LineIn Left", "AD12 Enable"},
- {"Sidetone Left Source", "LineIn Right", "AD12 Enable"},
- {"Sidetone Left Source", "Mic 1", "AD3 Enable"},
+ {"Sidetone Left Source", "LineIn Left", "AD1 Channel Volume"},
+ {"Sidetone Left Source", "LineIn Right", "AD2 Channel Volume"},
+ {"Sidetone Left Source", "Mic 1", "AD3 Channel Volume"},
{"Sidetone Left Source", "Headset Left", "DA_IN1"},
- {"Sidetone Right Source", "LineIn Right", "AD12 Enable"},
- {"Sidetone Right Source", "Mic 1", "AD3 Enable"},
- {"Sidetone Right Source", "DMic 4", "AD4 Enable"},
+ {"Sidetone Right Source", "LineIn Right", "AD2 Channel Volume"},
+ {"Sidetone Right Source", "Mic 1", "AD3 Channel Volume"},
+ {"Sidetone Right Source", "DMic 4", "AD4 Channel Volume"},
{"Sidetone Right Source", "Headset Right", "DA_IN2"},
{"STFIR1 Control", NULL, "Sidetone Left Source"},
@@ -981,75 +940,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = {
};
/*
- * Control-events
- */
-
-static int sid_status_control_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
-
- guard(mutex)(&drvdata->ctrl_lock);
- ucontrol->value.enumerated.item[0] = drvdata->sid_status;
-
- return 0;
-}
-
-/* Write sidetone FIR-coefficients configuration sequence */
-static int sid_status_control_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
- unsigned int param, sidconf;
- int status = 1;
-
- dev_dbg(component->dev, "%s: Enter\n", __func__);
-
- if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) {
- dev_err(component->dev,
- "%s: ERROR: This control supports '%s' only!\n",
- __func__, enum_sid_state[SID_APPLY_FIR]);
- return -EIO;
- }
-
- guard(mutex)(&drvdata->ctrl_lock);
-
- sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF);
- if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) {
- if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) {
- dev_err(component->dev, "%s: Sidetone busy while off!\n",
- __func__);
- status = -EPERM;
- } else {
- status = -EBUSY;
- }
- dev_dbg(component->dev, "%s: Exit\n", __func__);
- return status;
- }
-
- snd_soc_component_write(component, AB8500_SIDFIRADR, 0);
-
- for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) {
- snd_soc_component_write(component, AB8500_SIDFIRCOEF1, 0);
- snd_soc_component_write(component, AB8500_SIDFIRCOEF2, 0);
- }
-
- snd_soc_component_update_bits(component, AB8500_SIDFIRADR,
- BIT(AB8500_SIDFIRADR_FIRSIDSET),
- BIT(AB8500_SIDFIRADR_FIRSIDSET));
- snd_soc_component_update_bits(component, AB8500_SIDFIRADR,
- BIT(AB8500_SIDFIRADR_FIRSIDSET), 0);
-
- drvdata->sid_status = SID_FIR_CONFIGURED;
-
- dev_dbg(component->dev, "%s: Exit\n", __func__);
-
- return status;
-}
-
-/*
* Controls - Non-DAPM ASoC
*/
@@ -1332,9 +1222,6 @@ static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast,
AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOMAST_SHIFT,
enum_slavemaster);
-/* Sidetone */
-static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state);
-
/* ANC */
static struct snd_kcontrol_new ab8500_ctrls[] = {
@@ -1625,8 +1512,6 @@ static struct snd_kcontrol_new ab8500_ctrls[] = {
AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0),
/* Sidetone */
- SOC_ENUM_EXT("Sidetone Status", soc_enum_sidstate,
- sid_status_control_get, sid_status_control_put),
SOC_SINGLE_STROBE("Sidetone Reset",
AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0),
};
@@ -1638,23 +1523,25 @@ static struct snd_kcontrol_new ab8500_ctrls[] = {
static int ab8500_audio_init_audioblock(struct snd_soc_component *component)
{
int status;
+ u8 mask = AB8500_STW4500CTRL3_CLK32KOUT2DIS |
+ AB8500_STW4500CTRL3_RESETAUDN;
dev_dbg(component->dev, "%s: Enter.\n", __func__);
- /* Reset audio-registers and disable 32kHz-clock output 2 */
- status = ab8500_sysctrl_write(AB8500_STW4500CTRL3,
- AB8500_STW4500CTRL3_CLK32KOUT2DIS |
- AB8500_STW4500CTRL3_RESETAUDN,
- AB8500_STW4500CTRL3_RESETAUDN);
+ /* Reset the audio registers and disable the unused 32 kHz output. */
+ status = ab8500_sysctrl_write(AB8500_STW4500CTRL3, mask,
+ AB8500_STW4500CTRL3_CLK32KOUT2DIS);
if (status < 0)
return status;
- return 0;
+ return ab8500_sysctrl_write(AB8500_STW4500CTRL3, mask, mask);
}
static int ab8500_audio_setup_mics(struct snd_soc_component *component,
struct amic_settings *amics)
{
+ struct device *dev = component->dev;
+ struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
u8 value8;
unsigned int value;
@@ -1663,20 +1550,21 @@ static int ab8500_audio_setup_mics(struct snd_soc_component *component,
dev_dbg(component->dev, "%s: Enter.\n", __func__);
- /* Set DMic-clocks to outputs */
- status = abx500_get_register_interruptible(component->dev, AB8500_MISC,
- AB8500_GPIO_DIR4_REG,
- &value8);
- if (status < 0)
- return status;
- value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT |
- GPIO31_DIR_OUTPUT;
- status = abx500_set_register_interruptible(component->dev,
- AB8500_MISC,
- AB8500_GPIO_DIR4_REG,
- value);
- if (status < 0)
- return status;
+ /* Set DMic-clocks to outputs; these GPIOs do not exist on AB8505. */
+ if (!is_ab8505(ab8500)) {
+ status = abx500_get_register_interruptible(dev, AB8500_MISC,
+ AB8500_GPIO_DIR4_REG,
+ &value8);
+ if (status < 0)
+ return status;
+ value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT |
+ GPIO31_DIR_OUTPUT;
+ status = abx500_set_register_interruptible(dev, AB8500_MISC,
+ AB8500_GPIO_DIR4_REG,
+ value);
+ if (status < 0)
+ return status;
+ }
/* Attach regulators to AMic DAPM-paths */
dev_dbg(component->dev, "%s: Mic 1a regulator: %s\n", __func__,
@@ -1745,149 +1633,91 @@ static int ab8500_audio_set_ear_cmv(struct snd_soc_component *component,
return 0;
}
-static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai,
- unsigned int delay)
-{
- unsigned int mask, val;
- struct snd_soc_component *component = dai->component;
-
- mask = BIT(AB8500_DIGIFCONF2_IF0DEL);
- val = 0;
-
- switch (delay) {
- case 0:
- break;
- case 1:
- val |= BIT(AB8500_DIGIFCONF2_IF0DEL);
- break;
- default:
- dev_err(dai->component->dev,
- "%s: ERROR: Unsupported bit-delay (0x%x)!\n",
- __func__, delay);
- return -EINVAL;
- }
-
- dev_dbg(dai->component->dev, "%s: IF0 Bit-delay: %d bits.\n",
- __func__, delay);
- snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
-
- return 0;
-}
-
-/* Gates clocking according format mask */
-static int ab8500_codec_set_dai_clock_gate(struct snd_soc_component *component,
- unsigned int fmt)
-{
- unsigned int mask;
- unsigned int val;
-
- mask = BIT(AB8500_DIGIFCONF1_ENMASTGEN) |
- BIT(AB8500_DIGIFCONF1_ENFSBITCLK0);
-
- val = BIT(AB8500_DIGIFCONF1_ENMASTGEN);
-
- switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
- case SND_SOC_DAIFMT_CONT: /* continuous clock */
- dev_dbg(component->dev, "%s: IF0 Clock is continuous.\n",
- __func__);
- val |= BIT(AB8500_DIGIFCONF1_ENFSBITCLK0);
- break;
- case SND_SOC_DAIFMT_GATED: /* clock is gated */
- dev_dbg(component->dev, "%s: IF0 Clock is gated.\n",
- __func__);
- break;
- default:
- dev_err(component->dev,
- "%s: ERROR: Unsupported clock mask (0x%x)!\n",
- __func__, fmt & SND_SOC_DAIFMT_CLOCK_MASK);
- return -EINVAL;
- }
-
- snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val);
-
- return 0;
-}
-
static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- unsigned int mask;
- unsigned int val;
struct snd_soc_component *component = dai->component;
- int status;
+ unsigned int conf1_mask, conf1_val = 0;
+ unsigned int conf2_mask, conf2_val = 0;
+ unsigned int conf3_mask, conf3_val = 0;
+ bool provider = false;
+ int ret;
dev_dbg(component->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt);
- mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) |
+ conf3_mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) |
BIT(AB8500_DIGIFCONF3_IF1CLKTOIF0CLK) |
BIT(AB8500_DIGIFCONF3_IF0BFIFOEN) |
BIT(AB8500_DIGIFCONF3_IF0MASTER);
- val = 0;
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_CBP_CFP:
- dev_dbg(dai->component->dev,
+ dev_dbg(component->dev,
"%s: IF0 Master-mode: AB8500 provider.\n", __func__);
- val |= BIT(AB8500_DIGIFCONF3_IF0MASTER);
+ conf3_val |= BIT(AB8500_DIGIFCONF3_IF0MASTER);
+ provider = true;
break;
case SND_SOC_DAIFMT_CBC_CFC:
- dev_dbg(dai->component->dev,
+ dev_dbg(component->dev,
"%s: IF0 Master-mode: AB8500 consumer.\n", __func__);
break;
case SND_SOC_DAIFMT_CBC_CFP:
case SND_SOC_DAIFMT_CBP_CFC:
- dev_err(dai->component->dev,
+ dev_err(component->dev,
"%s: ERROR: The device is either a provider or a consumer.\n",
__func__);
fallthrough;
default:
- dev_err(dai->component->dev,
- "%s: ERROR: Unsupporter clocking mask 0x%x\n",
+ dev_err(component->dev,
+ "%s: ERROR: Unsupported clocking mask 0x%x\n",
__func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
return -EINVAL;
}
- snd_soc_component_update_bits(component, AB8500_DIGIFCONF3, mask, val);
-
- /* Set clock gating */
- status = ab8500_codec_set_dai_clock_gate(component, fmt);
- if (status) {
- dev_err(dai->component->dev,
- "%s: ERROR: Failed to set clock gate (%d).\n",
- __func__, status);
- return status;
+ conf1_mask = BIT(AB8500_DIGIFCONF1_ENMASTGEN) |
+ BIT(AB8500_DIGIFCONF1_ENFSBITCLK0);
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+ case SND_SOC_DAIFMT_CONT:
+ if (provider)
+ conf1_val = conf1_mask;
+ break;
+ case SND_SOC_DAIFMT_GATED:
+ if (provider)
+ conf1_val = BIT(AB8500_DIGIFCONF1_ENMASTGEN);
+ break;
+ default:
+ dev_err(component->dev, "%s: Unsupported clock mask 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_CLOCK_MASK);
+ return -EINVAL;
}
- /* Setting data transfer format */
-
- mask = BIT(AB8500_DIGIFCONF2_IF0FORMAT0) |
- BIT(AB8500_DIGIFCONF2_IF0FORMAT1) |
- BIT(AB8500_DIGIFCONF2_FSYNC0P) |
- BIT(AB8500_DIGIFCONF2_BITCLK0P);
- val = 0;
+ conf2_mask = BIT(AB8500_DIGIFCONF2_IF0FORMAT0) |
+ BIT(AB8500_DIGIFCONF2_IF0FORMAT1) |
+ BIT(AB8500_DIGIFCONF2_IF0DEL) |
+ BIT(AB8500_DIGIFCONF2_FSYNC0P) |
+ BIT(AB8500_DIGIFCONF2_BITCLK0P);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: /* I2S mode */
- dev_dbg(dai->component->dev, "%s: IF0 Protocol: I2S\n", __func__);
- val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT1);
- ab8500_audio_set_bit_delay(dai, 0);
+ dev_dbg(component->dev, "%s: IF0 Protocol: I2S\n", __func__);
+ conf2_val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT1) |
+ BIT(AB8500_DIGIFCONF2_IF0DEL);
break;
case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */
- dev_dbg(dai->component->dev,
+ dev_dbg(component->dev,
"%s: IF0 Protocol: DSP A (TDM)\n", __func__);
- val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0);
- ab8500_audio_set_bit_delay(dai, 1);
+ conf2_val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0) |
+ BIT(AB8500_DIGIFCONF2_IF0DEL);
break;
case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */
- dev_dbg(dai->component->dev,
+ dev_dbg(component->dev,
"%s: IF0 Protocol: DSP B (TDM)\n", __func__);
- val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0);
- ab8500_audio_set_bit_delay(dai, 0);
+ conf2_val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0);
break;
default:
- dev_err(dai->component->dev,
+ dev_err(component->dev,
"%s: ERROR: Unsupported format (0x%x)!\n",
__func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
@@ -1895,39 +1725,50 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
- dev_dbg(dai->component->dev,
+ dev_dbg(component->dev,
"%s: IF0: Normal bit clock, normal frame\n",
__func__);
break;
case SND_SOC_DAIFMT_NB_IF: /* normal BCLK + inv FRM */
- dev_dbg(dai->component->dev,
+ dev_dbg(component->dev,
"%s: IF0: Normal bit clock, inverted frame\n",
__func__);
- val |= BIT(AB8500_DIGIFCONF2_FSYNC0P);
+ conf2_val |= BIT(AB8500_DIGIFCONF2_FSYNC0P);
break;
case SND_SOC_DAIFMT_IB_NF: /* invert BCLK + nor FRM */
- dev_dbg(dai->component->dev,
+ dev_dbg(component->dev,
"%s: IF0: Inverted bit clock, normal frame\n",
__func__);
- val |= BIT(AB8500_DIGIFCONF2_BITCLK0P);
+ conf2_val |= BIT(AB8500_DIGIFCONF2_BITCLK0P);
break;
case SND_SOC_DAIFMT_IB_IF: /* invert BCLK + FRM */
- dev_dbg(dai->component->dev,
+ dev_dbg(component->dev,
"%s: IF0: Inverted bit clock, inverted frame\n",
__func__);
- val |= BIT(AB8500_DIGIFCONF2_FSYNC0P);
- val |= BIT(AB8500_DIGIFCONF2_BITCLK0P);
+ conf2_val |= BIT(AB8500_DIGIFCONF2_FSYNC0P) |
+ BIT(AB8500_DIGIFCONF2_BITCLK0P);
break;
default:
- dev_err(dai->component->dev,
+ dev_err(component->dev,
"%s: ERROR: Unsupported INV mask 0x%x\n",
__func__, fmt & SND_SOC_DAIFMT_INV_MASK);
return -EINVAL;
}
- snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
+ ret = snd_soc_component_update_bits(component, AB8500_DIGIFCONF3,
+ conf3_mask, conf3_val);
+ if (ret < 0)
+ return ret;
- return 0;
+ ret = snd_soc_component_update_bits(component, AB8500_DIGIFCONF1,
+ conf1_mask, conf1_val);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, AB8500_DIGIFCONF2,
+ conf2_mask, conf2_val);
+
+ return ret < 0 ? ret : 0;
}
static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
@@ -1935,23 +1776,27 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
int slots, int slot_width)
{
struct snd_soc_component *component = dai->component;
- unsigned int val, mask, slot, slots_active;
+ unsigned int active_mask, clock_ratio, slot, value, ad_out, reg;
+ unsigned int tx_active, rx_active;
+ unsigned int conf1_val, conf2_val;
+ unsigned int mask;
+ int channel, ret;
mask = BIT(AB8500_DIGIFCONF2_IF0WL0) |
BIT(AB8500_DIGIFCONF2_IF0WL1);
- val = 0;
+ conf2_val = 0;
switch (slot_width) {
case 16:
break;
case 20:
- val |= BIT(AB8500_DIGIFCONF2_IF0WL0);
+ conf2_val |= BIT(AB8500_DIGIFCONF2_IF0WL0);
break;
case 24:
- val |= BIT(AB8500_DIGIFCONF2_IF0WL1);
+ conf2_val |= BIT(AB8500_DIGIFCONF2_IF0WL1);
break;
case 32:
- val |= BIT(AB8500_DIGIFCONF2_IF0WL1) |
+ conf2_val |= BIT(AB8500_DIGIFCONF2_IF0WL1) |
BIT(AB8500_DIGIFCONF2_IF0WL0);
break;
default:
@@ -1960,27 +1805,11 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
return -EINVAL;
}
- dev_dbg(dai->component->dev, "%s: IF0 slot-width: %d bits.\n",
- __func__, slot_width);
- snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
-
- /* Setup TDM clocking according to slot count */
- dev_dbg(dai->component->dev, "%s: Slots, total: %d\n", __func__, slots);
- mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
- BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
switch (slots) {
case 2:
- val = AB8500_MASK_NONE;
- break;
case 4:
- val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0);
- break;
case 8:
- val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
- break;
case 16:
- val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
- BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
break;
default:
dev_err(dai->component->dev,
@@ -1988,92 +1817,134 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
__func__, slots);
return -EINVAL;
}
- snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val);
-
- /* Setup TDM DA according to active tx slots */
- if (tx_mask & ~0xff)
- return -EINVAL;
-
- mask = AB8500_DASLOTCONFX_SLTODAX_MASK;
- tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET;
- slots_active = hweight32(tx_mask);
-
- dev_dbg(dai->component->dev, "%s: Slots, active, TX: %d\n", __func__,
- slots_active);
-
- switch (slots_active) {
- case 0:
+ clock_ratio = slots * slot_width;
+ switch (clock_ratio) {
+ case 32:
+ conf1_val = 0;
break;
- case 1:
- slot = ffs(tx_mask);
- snd_soc_component_update_bits(component, AB8500_DASLOTCONF1, mask, slot);
- snd_soc_component_update_bits(component, AB8500_DASLOTCONF3, mask, slot);
- snd_soc_component_update_bits(component, AB8500_DASLOTCONF2, mask, slot);
- snd_soc_component_update_bits(component, AB8500_DASLOTCONF4, mask, slot);
+ case 64:
+ conf1_val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0);
break;
- case 2:
- slot = ffs(tx_mask);
- snd_soc_component_update_bits(component, AB8500_DASLOTCONF1, mask, slot);
- snd_soc_component_update_bits(component, AB8500_DASLOTCONF3, mask, slot);
- slot = fls(tx_mask);
- snd_soc_component_update_bits(component, AB8500_DASLOTCONF2, mask, slot);
- snd_soc_component_update_bits(component, AB8500_DASLOTCONF4, mask, slot);
+ case 128:
+ conf1_val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
break;
- case 8:
- dev_dbg(dai->component->dev,
- "%s: In 8-channel mode DA-from-slot mapping is set manually.",
- __func__);
+ case 256:
+ conf1_val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
+ BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
break;
default:
- dev_err(dai->component->dev,
- "%s: Unsupported number of active TX-slots (%d)!\n",
- __func__, slots_active);
+ dev_err(component->dev, "%s: Unsupported BCLK ratio (%u)!\n",
+ __func__, clock_ratio);
return -EINVAL;
}
- /* Setup TDM AD according to active RX-slots */
+ active_mask = GENMASK(min(slots, 8) - 1, 0);
+ if ((tx_mask | rx_mask) & ~active_mask) {
+ dev_err(component->dev, "%s: Slot mask exceeds slot count\n",
+ __func__);
+ return -EINVAL;
+ }
- if (rx_mask & ~0xff)
+ tx_active = hweight32(tx_mask);
+ rx_active = hweight32(rx_mask);
+ if (tx_active != 0 && tx_active != 1 && tx_active != 2 &&
+ tx_active != 8) {
+ dev_err(component->dev, "%s: Unsupported active TX slots (%u)!\n",
+ __func__, tx_active);
return -EINVAL;
+ }
+ if (rx_active != 0 && rx_active != 1 && rx_active != 2 &&
+ rx_active != 8) {
+ dev_err(component->dev, "%s: Unsupported active RX slots (%u)!\n",
+ __func__, rx_active);
+ return -EINVAL;
+ }
- rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET;
- slots_active = hweight32(rx_mask);
+ dev_dbg(component->dev,
+ "%s: %d slots of %d bits, TX active: %u, RX active: %u\n",
+ __func__, slots, slot_width, tx_active, rx_active);
- dev_dbg(dai->component->dev, "%s: Slots, active, RX: %d\n", __func__,
- slots_active);
+ ret = snd_soc_component_update_bits(component, AB8500_DIGIFCONF2,
+ mask, conf2_val);
+ if (ret < 0)
+ return ret;
- switch (slots_active) {
- case 0:
- break;
- case 1:
- slot = ffs(rx_mask);
- snd_soc_component_update_bits(component, AB8500_ADSLOTSEL(slot),
- AB8500_MASK_SLOT(slot),
- AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
- break;
- case 2:
- slot = ffs(rx_mask);
- snd_soc_component_update_bits(component,
- AB8500_ADSLOTSEL(slot),
- AB8500_MASK_SLOT(slot),
- AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
- slot = fls(rx_mask);
- snd_soc_component_update_bits(component,
- AB8500_ADSLOTSEL(slot),
- AB8500_MASK_SLOT(slot),
- AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT2, slot));
- break;
- case 8:
- dev_dbg(dai->component->dev,
- "%s: In 8-channel mode AD-to-slot mapping is set manually.",
- __func__);
- break;
- default:
- dev_err(dai->component->dev,
- "%s: Unsupported number of active RX-slots (%d)!\n",
- __func__, slots_active);
- return -EINVAL;
+ mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
+ BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
+ ret = snd_soc_component_update_bits(component, AB8500_DIGIFCONF1,
+ mask, conf1_val);
+ if (ret < 0)
+ return ret;
+
+ mask = AB8500_DASLOTCONFX_SLTODAX_MASK;
+ if (tx_active == 1 || tx_active == 2) {
+ slot = __ffs(tx_mask) + AB8500_DA_DATA0_OFFSET;
+ reg = AB8500_DASLOTCONF1;
+ ret = snd_soc_component_update_bits(component, reg, mask, slot);
+ if (ret < 0)
+ return ret;
+ reg = AB8500_DASLOTCONF3;
+ ret = snd_soc_component_update_bits(component, reg, mask, slot);
+ if (ret < 0)
+ return ret;
+
+ if (tx_active == 2)
+ slot = __fls(tx_mask) + AB8500_DA_DATA0_OFFSET;
+ reg = AB8500_DASLOTCONF2;
+ ret = snd_soc_component_update_bits(component, reg, mask, slot);
+ if (ret < 0)
+ return ret;
+ reg = AB8500_DASLOTCONF4;
+ ret = snd_soc_component_update_bits(component, reg, mask, slot);
+ if (ret < 0)
+ return ret;
+ } else if (tx_active == 8) {
+ channel = 0;
+ for (slot = 0; slot < 8; slot++) {
+ if (!(tx_mask & BIT(slot)))
+ continue;
+ reg = AB8500_DASLOTCONF1 + channel++;
+ value = slot + AB8500_DA_DATA0_OFFSET;
+ ret = snd_soc_component_update_bits(component, reg, mask, value);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ if (rx_active == 1 || rx_active == 2) {
+ slot = __ffs(rx_mask) + AB8500_AD_DATA0_OFFSET;
+ value = AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3,
+ slot);
+ reg = AB8500_ADSLOTSEL(slot);
+ mask = AB8500_MASK_SLOT(slot);
+ ret = snd_soc_component_update_bits(component, reg, mask, value);
+ if (ret < 0)
+ return ret;
+
+ if (rx_active == 2) {
+ slot = __fls(rx_mask) + AB8500_AD_DATA0_OFFSET;
+ value = AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT2,
+ slot);
+ reg = AB8500_ADSLOTSEL(slot);
+ mask = AB8500_MASK_SLOT(slot);
+ ret = snd_soc_component_update_bits(component, reg, mask, value);
+ if (ret < 0)
+ return ret;
+ }
+ } else if (rx_active == 8) {
+ channel = 0;
+ for (slot = 0; slot < 8; slot++) {
+ if (!(rx_mask & BIT(slot)))
+ continue;
+ ad_out = AB8500_AD_OUT1 + channel++;
+ value = AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(ad_out, slot);
+ reg = AB8500_ADSLOTSEL(slot);
+ mask = AB8500_MASK_SLOT(slot);
+ ret = snd_soc_component_update_bits(component, reg, mask, value);
+ if (ret < 0)
+ return ret;
+ }
}
return 0;
@@ -2170,10 +2041,8 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,
static int ab8500_codec_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct device *dev = component->dev;
struct device_node *np = dev->of_node;
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev);
struct ab8500_codec_platform_data codec_pdata;
int status;
@@ -2181,6 +2050,13 @@ static int ab8500_codec_probe(struct snd_soc_component *component)
ab8500_codec_of_probe(dev, np, &codec_pdata);
+ status = ab8500_audio_init_audioblock(component);
+ if (status < 0) {
+ dev_err(dev, "%s: failed to init audio-block (%d)!\n",
+ __func__, status);
+ return status;
+ }
+
status = ab8500_audio_setup_mics(component, &codec_pdata.amics);
if (status < 0) {
pr_err("%s: Failed to setup mics (%d)!\n", __func__, status);
@@ -2193,23 +2069,12 @@ static int ab8500_codec_probe(struct snd_soc_component *component)
return status;
}
- status = ab8500_audio_init_audioblock(component);
- if (status < 0) {
- dev_err(dev, "%s: failed to init audio-block (%d)!\n",
- __func__, status);
- return status;
- }
-
/* Override HW-defaults */
snd_soc_component_write(component, AB8500_ANACONF5,
BIT(AB8500_ANACONF5_HSAUTOEN));
snd_soc_component_write(component, AB8500_SHORTCIRCONF,
BIT(AB8500_SHORTCIRCONF_HSZCDDIS));
- snd_soc_dapm_disable_pin(dapm, "ANC Configure Input");
-
- mutex_init(&drvdata->ctrl_lock);
-
return status;
}
@@ -2238,7 +2103,6 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
- drvdata->sid_status = SID_UNCONFIGURED;
dev_set_drvdata(&pdev->dev, drvdata);
drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev,