summaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/tlv320adcx140.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/tlv320adcx140.c')
-rw-r--r--sound/soc/codecs/tlv320adcx140.c124
1 files changed, 87 insertions, 37 deletions
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index 35fe8ee5bce9..5cd50d841177 100644
--- a/sound/soc/codecs/tlv320adcx140.c
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
// TLV320ADCX140 Sound driver
-// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+// Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -37,6 +37,13 @@ struct adcx140_priv {
unsigned int slot_width;
};
+static const char * const gpo_config_names[] = {
+ "ti,gpo-config-1",
+ "ti,gpo-config-2",
+ "ti,gpo-config-3",
+ "ti,gpo-config-4",
+};
+
static const struct reg_default adcx140_reg_defaults[] = {
{ ADCX140_PAGE_SELECT, 0x00 },
{ ADCX140_SW_RESET, 0x00 },
@@ -60,10 +67,10 @@ static const struct reg_default adcx140_reg_defaults[] = {
{ ADCX140_PDMCLK_CFG, 0x40 },
{ ADCX140_PDM_CFG, 0x00 },
{ ADCX140_GPIO_CFG0, 0x22 },
+ { ADCX140_GPO_CFG0, 0x00 },
{ ADCX140_GPO_CFG1, 0x00 },
{ ADCX140_GPO_CFG2, 0x00 },
{ ADCX140_GPO_CFG3, 0x00 },
- { ADCX140_GPO_CFG4, 0x00 },
{ ADCX140_GPO_VAL, 0x00 },
{ ADCX140_GPIO_MON, 0x00 },
{ ADCX140_GPI_CFG0, 0x00 },
@@ -218,8 +225,8 @@ static const struct snd_kcontrol_new in4_resistor_controls[] = {
};
/* Analog/Digital Selection */
-static const char *adcx140_mic_sel_text[] = {"Analog", "Line In", "Digital"};
-static const char *adcx140_analog_sel_text[] = {"Analog", "Line In"};
+static const char * const adcx140_mic_sel_text[] = {"Analog", "Line In", "Digital"};
+static const char * const adcx140_analog_sel_text[] = {"Analog", "Line In"};
static SOC_ENUM_SINGLE_DECL(adcx140_mic1p_enum,
ADCX140_CH1_CFG0, 5,
@@ -313,6 +320,14 @@ static const struct snd_kcontrol_new adcx140_dapm_ch3_en_switch =
SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 5, 1, 0);
static const struct snd_kcontrol_new adcx140_dapm_ch4_en_switch =
SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 4, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch5_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 3, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch6_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 2, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch7_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 1, 1, 0);
+static const struct snd_kcontrol_new adcx140_dapm_ch8_en_switch =
+ SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 0, 1, 0);
static const struct snd_kcontrol_new adcx140_dapm_ch1_dre_en_switch =
SOC_DAPM_SINGLE("Switch", ADCX140_CH1_CFG0, 0, 1, 0);
@@ -406,6 +421,15 @@ static const struct snd_soc_dapm_widget adcx140_dapm_widgets[] = {
SND_SOC_DAPM_SWITCH("CH4_ASI_EN", SND_SOC_NOPM, 0, 0,
&adcx140_dapm_ch4_en_switch),
+ SND_SOC_DAPM_SWITCH("CH5_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch5_en_switch),
+ SND_SOC_DAPM_SWITCH("CH6_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch6_en_switch),
+ SND_SOC_DAPM_SWITCH("CH7_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch7_en_switch),
+ SND_SOC_DAPM_SWITCH("CH8_ASI_EN", SND_SOC_NOPM, 0, 0,
+ &adcx140_dapm_ch8_en_switch),
+
SND_SOC_DAPM_SWITCH("DRE_ENABLE", SND_SOC_NOPM, 0, 0,
&adcx140_dapm_dre_en_switch),
@@ -446,6 +470,11 @@ static const struct snd_soc_dapm_route adcx140_audio_map[] = {
{"CH3_ASI_EN", "Switch", "CH3_ADC"},
{"CH4_ASI_EN", "Switch", "CH4_ADC"},
+ {"CH5_ASI_EN", "Switch", "CH5_OUT"},
+ {"CH6_ASI_EN", "Switch", "CH6_OUT"},
+ {"CH7_ASI_EN", "Switch", "CH7_OUT"},
+ {"CH8_ASI_EN", "Switch", "CH8_OUT"},
+
{"Decimation Filter", "Linear Phase", "DRE_ENABLE"},
{"Decimation Filter", "Low Latency", "DRE_ENABLE"},
{"Decimation Filter", "Ultra-low Latency", "DRE_ENABLE"},
@@ -576,7 +605,7 @@ static int adcx140_reset(struct adcx140_priv *adcx140)
gpiod_direction_output(adcx140->gpio_reset, 1);
} else {
ret = regmap_write(adcx140->regmap, ADCX140_SW_RESET,
- ADCX140_RESET);
+ ADCX140_RESET);
}
/* 8.4.2: wait >= 10 ms after entering sleep mode. */
@@ -624,6 +653,8 @@ static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
u8 iface_reg1 = 0;
u8 iface_reg2 = 0;
+ int offset = 0;
+ int width = adcx140->slot_width;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -666,7 +697,10 @@ static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai,
iface_reg1 |= ADCX140_LEFT_JUST_BIT;
break;
case SND_SOC_DAIFMT_DSP_A:
+ offset += (adcx140->tdm_delay * width + 1);
+ break;
case SND_SOC_DAIFMT_DSP_B:
+ offset += adcx140->tdm_delay * width;
break;
default:
dev_err(component->dev, "Invalid DAI interface format\n");
@@ -683,6 +717,11 @@ static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai,
snd_soc_component_update_bits(component, ADCX140_MST_CFG0,
ADCX140_BCLK_FSYNC_MASTER, iface_reg2);
+ /* Configure data offset */
+ snd_soc_component_update_bits(component, ADCX140_ASI_CFG1,
+ ADCX140_TX_OFFSET_MASK, offset);
+
+
return 0;
}
@@ -694,11 +733,6 @@ static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
unsigned int lsb;
- if (tx_mask != rx_mask) {
- dev_err(component->dev, "tx and rx masks must be symmetric\n");
- return -EINVAL;
- }
-
/* TDM based on DSP mode requires slots to be adjacent */
lsb = __ffs(tx_mask);
if ((lsb + 1) != __fls(tx_mask)) {
@@ -723,36 +757,48 @@ static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
return 0;
}
-static int adcx140_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static const struct snd_soc_dai_ops adcx140_dai_ops = {
+ .hw_params = adcx140_hw_params,
+ .set_fmt = adcx140_set_dai_fmt,
+ .set_tdm_slot = adcx140_set_dai_tdm_slot,
+};
+
+static int adcx140_configure_gpo(struct adcx140_priv *adcx140)
{
- struct snd_soc_component *component = dai->component;
- struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
- int offset = 0;
- int width = adcx140->slot_width;
+ u32 gpo_outputs[ADCX140_NUM_GPOS];
+ u32 gpo_output_val = 0;
+ int ret;
+ int i;
- if (!width)
- width = substream->runtime->sample_bits;
+ for (i = 0; i < ADCX140_NUM_GPOS; i++) {
+ ret = device_property_read_u32_array(adcx140->dev,
+ gpo_config_names[i],
+ gpo_outputs,
+ ADCX140_NUM_GPO_CFGS);
+ if (ret)
+ continue;
- /* TDM slot selection only valid in DSP_A/_B mode */
- if (adcx140->dai_fmt == SND_SOC_DAIFMT_DSP_A)
- offset += (adcx140->tdm_delay * width + 1);
- else if (adcx140->dai_fmt == SND_SOC_DAIFMT_DSP_B)
- offset += adcx140->tdm_delay * width;
+ if (gpo_outputs[0] > ADCX140_GPO_CFG_MAX) {
+ dev_err(adcx140->dev, "GPO%d config out of range\n", i + 1);
+ return -EINVAL;
+ }
- /* Configure data offset */
- snd_soc_component_update_bits(component, ADCX140_ASI_CFG1,
- ADCX140_TX_OFFSET_MASK, offset);
+ if (gpo_outputs[1] > ADCX140_GPO_DRV_MAX) {
+ dev_err(adcx140->dev, "GPO%d drive out of range\n", i + 1);
+ return -EINVAL;
+ }
+
+ gpo_output_val = gpo_outputs[0] << ADCX140_GPO_SHIFT |
+ gpo_outputs[1];
+ ret = regmap_write(adcx140->regmap, ADCX140_GPO_CFG0 + i,
+ gpo_output_val);
+ if (ret)
+ return ret;
+ }
return 0;
-}
-static const struct snd_soc_dai_ops adcx140_dai_ops = {
- .hw_params = adcx140_hw_params,
- .set_fmt = adcx140_set_dai_fmt,
- .prepare = adcx140_prepare,
- .set_tdm_slot = adcx140_set_dai_tdm_slot,
-};
+}
static int adcx140_codec_probe(struct snd_soc_component *component)
{
@@ -792,6 +838,10 @@ static int adcx140_codec_probe(struct snd_soc_component *component)
bias_cfg = bias_source << ADCX140_MIC_BIAS_SHIFT | vref_source;
+ ret = adcx140_reset(adcx140);
+ if (ret)
+ goto out;
+
pdm_count = device_property_count_u32(adcx140->dev,
"ti,pdm-edge-select");
if (pdm_count <= ADCX140_NUM_PDM_EDGES && pdm_count > 0) {
@@ -835,11 +885,11 @@ static int adcx140_codec_probe(struct snd_soc_component *component)
return ret;
}
- ret = adcx140_reset(adcx140);
+ ret = adcx140_configure_gpo(adcx140);
if (ret)
goto out;
- if(adcx140->supply_areg == NULL)
+ if (adcx140->supply_areg == NULL)
sleep_cfg_val |= ADCX140_AREG_INTERNAL;
ret = regmap_write(adcx140->regmap, ADCX140_SLEEP_CFG, sleep_cfg_val);
@@ -940,8 +990,8 @@ static int adcx140_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(adcx140->supply_areg)) {
if (PTR_ERR(adcx140->supply_areg) == -EPROBE_DEFER)
return -EPROBE_DEFER;
- else
- adcx140->supply_areg = NULL;
+
+ adcx140->supply_areg = NULL;
} else {
ret = regulator_enable(adcx140->supply_areg);
if (ret) {