diff options
Diffstat (limited to 'sound/soc/codecs/tlv320adcx140.c')
| -rw-r--r-- | sound/soc/codecs/tlv320adcx140.c | 417 |
1 files changed, 322 insertions, 95 deletions
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 35fe8ee5bce9..443cf59cb71a 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> @@ -12,7 +12,6 @@ #include <linux/regulator/consumer.h> #include <linux/acpi.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -30,13 +29,20 @@ struct adcx140_priv { struct regmap *regmap; struct device *dev; - int micbias_vg; + bool micbias_vg; + bool phase_calib_on; unsigned int dai_fmt; - unsigned int tdm_delay; 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 +66,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 }, @@ -154,7 +160,7 @@ static const struct regmap_config adcx140_i2c_regmap = { }; /* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */ -static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10000, 50, 0); +static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10050, 50, 0); /* ADC gain. From 0 to 42 dB in 1 dB steps */ static DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); @@ -218,8 +224,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 +319,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); @@ -397,6 +411,16 @@ static const struct snd_soc_dapm_widget adcx140_dapm_widgets[] = { SND_SOC_DAPM_ADC("CH3_ADC", "CH3 Capture", ADCX140_IN_CH_EN, 5, 0), SND_SOC_DAPM_ADC("CH4_ADC", "CH4 Capture", ADCX140_IN_CH_EN, 4, 0), + SND_SOC_DAPM_ADC("CH1_DIG", "CH1 Capture", ADCX140_IN_CH_EN, 7, 0), + SND_SOC_DAPM_ADC("CH2_DIG", "CH2 Capture", ADCX140_IN_CH_EN, 6, 0), + SND_SOC_DAPM_ADC("CH3_DIG", "CH3 Capture", ADCX140_IN_CH_EN, 5, 0), + SND_SOC_DAPM_ADC("CH4_DIG", "CH4 Capture", ADCX140_IN_CH_EN, 4, 0), + SND_SOC_DAPM_ADC("CH5_DIG", "CH5 Capture", ADCX140_IN_CH_EN, 3, 0), + SND_SOC_DAPM_ADC("CH6_DIG", "CH6 Capture", ADCX140_IN_CH_EN, 2, 0), + SND_SOC_DAPM_ADC("CH7_DIG", "CH7 Capture", ADCX140_IN_CH_EN, 1, 0), + SND_SOC_DAPM_ADC("CH8_DIG", "CH8 Capture", ADCX140_IN_CH_EN, 0, 0), + + SND_SOC_DAPM_SWITCH("CH1_ASI_EN", SND_SOC_NOPM, 0, 0, &adcx140_dapm_ch1_en_switch), SND_SOC_DAPM_SWITCH("CH2_ASI_EN", SND_SOC_NOPM, 0, 0, @@ -406,6 +430,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 +479,20 @@ static const struct snd_soc_dapm_route adcx140_audio_map[] = { {"CH3_ASI_EN", "Switch", "CH3_ADC"}, {"CH4_ASI_EN", "Switch", "CH4_ADC"}, + {"CH1_ASI_EN", "Switch", "CH1_DIG"}, + {"CH2_ASI_EN", "Switch", "CH2_DIG"}, + {"CH3_ASI_EN", "Switch", "CH3_DIG"}, + {"CH4_ASI_EN", "Switch", "CH4_DIG"}, + {"CH5_ASI_EN", "Switch", "CH5_DIG"}, + {"CH6_ASI_EN", "Switch", "CH6_DIG"}, + {"CH7_ASI_EN", "Switch", "CH7_DIG"}, + {"CH8_ASI_EN", "Switch", "CH8_DIG"}, + + {"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"}, @@ -512,6 +559,15 @@ static const struct snd_soc_dapm_route adcx140_audio_map[] = { {"PDM Clk Div Select", "705.6 kHz", "MIC1P Input Mux"}, {"PDM Clk Div Select", "5.6448 MHz", "MIC1P Input Mux"}, + {"MIC1P Input Mux", NULL, "CH1_DIG"}, + {"MIC1M Input Mux", NULL, "CH2_DIG"}, + {"MIC2P Input Mux", NULL, "CH3_DIG"}, + {"MIC2M Input Mux", NULL, "CH4_DIG"}, + {"MIC3P Input Mux", NULL, "CH5_DIG"}, + {"MIC3M Input Mux", NULL, "CH6_DIG"}, + {"MIC4P Input Mux", NULL, "CH7_DIG"}, + {"MIC4M Input Mux", NULL, "CH8_DIG"}, + {"MIC1 Analog Mux", "Line In", "MIC1P"}, {"MIC2 Analog Mux", "Line In", "MIC2P"}, {"MIC3 Analog Mux", "Line In", "MIC3P"}, @@ -525,8 +581,61 @@ static const struct snd_soc_dapm_route adcx140_audio_map[] = { {"MIC3M Input Mux", "Analog", "MIC3M"}, {"MIC4P Input Mux", "Analog", "MIC4P"}, {"MIC4M Input Mux", "Analog", "MIC4M"}, + + {"MIC1P Input Mux", "Digital", "MIC1P"}, + {"MIC1M Input Mux", "Digital", "MIC1M"}, + {"MIC2P Input Mux", "Digital", "MIC2P"}, + {"MIC2M Input Mux", "Digital", "MIC2M"}, + {"MIC3P Input Mux", "Digital", "MIC3P"}, + {"MIC3M Input Mux", "Digital", "MIC3M"}, + {"MIC4P Input Mux", "Digital", "MIC4P"}, + {"MIC4M Input Mux", "Digital", "MIC4M"}, }; +#define ADCX140_PHASE_CALIB_SWITCH(xname) {\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .info = adcx140_phase_calib_info, \ + .get = adcx140_phase_calib_get, \ + .put = adcx140_phase_calib_put} + +static int adcx140_phase_calib_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int adcx140_phase_calib_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(codec); + + value->value.integer.value[0] = adcx140->phase_calib_on ? 1 : 0; + + + return 0; +} + +static int adcx140_phase_calib_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(codec); + + bool v = value->value.integer.value[0] ? true : false; + + if (adcx140->phase_calib_on != v) { + adcx140->phase_calib_on = v; + return 1; + } + return 0; +} + static const struct snd_kcontrol_new adcx140_snd_controls[] = { SOC_SINGLE_TLV("Analog CH1 Mic Gain Volume", ADCX140_CH1_CFG1, 2, 42, 0, adc_tlv), @@ -563,6 +672,7 @@ static const struct snd_kcontrol_new adcx140_snd_controls[] = { 0, 0xff, 0, dig_vol_tlv), SOC_SINGLE_TLV("Digital CH8 Out Volume", ADCX140_CH8_CFG2, 0, 0xff, 0, dig_vol_tlv), + ADCX140_PHASE_CALIB_SWITCH("Phase Calibration Switch"), }; static int adcx140_reset(struct adcx140_priv *adcx140) @@ -576,7 +686,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. */ @@ -585,11 +695,36 @@ static int adcx140_reset(struct adcx140_priv *adcx140) return ret; } +static void adcx140_pwr_ctrl(struct adcx140_priv *adcx140, bool power_state) +{ + int pwr_ctrl = 0; + int ret = 0; + struct snd_soc_component *component = adcx140->component; + + if (power_state) + pwr_ctrl = ADCX140_PWR_CFG_ADC_PDZ | ADCX140_PWR_CFG_PLL_PDZ; + + if (adcx140->micbias_vg && power_state) + pwr_ctrl |= ADCX140_PWR_CFG_BIAS_PDZ; + + if (pwr_ctrl) { + ret = regmap_write(adcx140->regmap, ADCX140_PHASE_CALIB, + adcx140->phase_calib_on ? 0x00 : 0x40); + if (ret) + dev_err(component->dev, "%s: register write error %d\n", + __func__, ret); + } + + regmap_update_bits(adcx140->regmap, ADCX140_PWR_CFG, + ADCX140_PWR_CTRL_MSK, pwr_ctrl); +} + static int adcx140_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); u8 data = 0; switch (params_width(params)) { @@ -611,9 +746,13 @@ static int adcx140_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + adcx140_pwr_ctrl(adcx140, false); + snd_soc_component_update_bits(component, ADCX140_ASI_CFG0, ADCX140_WORD_LEN_MSK, data); + adcx140_pwr_ctrl(adcx140, true); + return 0; } @@ -624,36 +763,18 @@ 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; + bool inverted_bclk = false; /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: iface_reg2 |= ADCX140_BCLK_FSYNC_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFS: default: - dev_err(component->dev, "Invalid DAI master/slave interface\n"); - return -EINVAL; - } - - /* signal polarity */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_IF: - iface_reg1 |= ADCX140_FSYNCINV_BIT; - break; - case SND_SOC_DAIFMT_IB_IF: - iface_reg1 |= ADCX140_BCLKINV_BIT | ADCX140_FSYNCINV_BIT; - break; - case SND_SOC_DAIFMT_IB_NF: - iface_reg1 |= ADCX140_BCLKINV_BIT; - break; - case SND_SOC_DAIFMT_NB_NF: - break; - default: - dev_err(component->dev, "Invalid DAI clock signal polarity\n"); + dev_err(component->dev, "Invalid DAI clock provider\n"); return -EINVAL; } @@ -666,15 +787,40 @@ 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 = 1; + inverted_bclk = true; + break; case SND_SOC_DAIFMT_DSP_B: + inverted_bclk = true; break; default: dev_err(component->dev, "Invalid DAI interface format\n"); return -EINVAL; } + /* signal polarity */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_IB_IF: + inverted_bclk = !inverted_bclk; + break; + case SND_SOC_DAIFMT_NB_IF: + iface_reg1 |= ADCX140_FSYNCINV_BIT; + break; + case SND_SOC_DAIFMT_NB_NF: + break; + default: + dev_err(component->dev, "Invalid DAI clock signal polarity\n"); + return -EINVAL; + } + + if (inverted_bclk) + iface_reg1 |= ADCX140_BCLKINV_BIT; + adcx140->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + adcx140_pwr_ctrl(adcx140, false); + snd_soc_component_update_bits(component, ADCX140_ASI_CFG0, ADCX140_FSYNCINV_BIT | ADCX140_BCLKINV_BIT | @@ -683,6 +829,12 @@ 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); + + adcx140_pwr_ctrl(adcx140, true); + return 0; } @@ -692,17 +844,13 @@ static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai, { struct snd_soc_component *component = codec_dai->component; 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)) { - dev_err(component->dev, "Invalid mask, slots must be adjacent\n"); + /* + * The chip itself supports arbitrary masks, but the driver currently + * only supports adjacent slots beginning at the first slot. + */ + if (tx_mask != GENMASK(__fls(tx_mask), 0)) { + dev_err(component->dev, "Only lower adjacent slots are supported\n"); return -EINVAL; } @@ -717,42 +865,89 @@ static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai, return -EINVAL; } - adcx140->tdm_delay = lsb; adcx140->slot_width = slot_width; 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_configure_gpio(struct adcx140_priv *adcx140) +{ + int gpio_count = 0; + u32 gpio_outputs[ADCX140_NUM_GPIO_CFGS]; + u32 gpio_output_val = 0; + int ret; + + gpio_count = device_property_count_u32(adcx140->dev, + "ti,gpio-config"); + if (gpio_count <= 0) + return 0; + + if (gpio_count != ADCX140_NUM_GPIO_CFGS) + return -EINVAL; + + ret = device_property_read_u32_array(adcx140->dev, "ti,gpio-config", + gpio_outputs, gpio_count); + if (ret) + return ret; + + if (gpio_outputs[0] > ADCX140_GPIO_CFG_MAX) { + dev_err(adcx140->dev, "GPIO config out of range\n"); + return -EINVAL; + } + + if (gpio_outputs[1] > ADCX140_GPIO_DRV_MAX) { + dev_err(adcx140->dev, "GPIO drive out of range\n"); + return -EINVAL; + } + + gpio_output_val = gpio_outputs[0] << ADCX140_GPIO_SHIFT + | gpio_outputs[1]; + + return regmap_write(adcx140->regmap, ADCX140_GPIO_CFG0, gpio_output_val); +} static int adcx140_codec_probe(struct snd_soc_component *component) { @@ -769,15 +964,15 @@ static int adcx140_codec_probe(struct snd_soc_component *component) u32 gpi_input_val = 0; int i; int ret; + bool tx_high_z; ret = device_property_read_u32(adcx140->dev, "ti,mic-bias-source", &bias_source); - if (ret) + if (ret || bias_source > ADCX140_MIC_BIAS_VAL_AVDD) { bias_source = ADCX140_MIC_BIAS_VAL_VREF; - - if (bias_source > ADCX140_MIC_BIAS_VAL_AVDD) { - dev_err(adcx140->dev, "Mic Bias source value is invalid\n"); - return -EINVAL; + adcx140->micbias_vg = false; + } else { + adcx140->micbias_vg = true; } ret = device_property_read_u32(adcx140->dev, "ti,vref-source", @@ -792,6 +987,22 @@ 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; + + if (adcx140->supply_areg == NULL) + sleep_cfg_val |= ADCX140_AREG_INTERNAL; + + ret = regmap_write(adcx140->regmap, ADCX140_SLEEP_CFG, sleep_cfg_val); + if (ret) { + dev_err(adcx140->dev, "setting sleep config failed %d\n", ret); + goto out; + } + + /* 8.4.3: Wait >= 1ms after entering active mode. */ + usleep_range(1000, 100000); + pdm_count = device_property_count_u32(adcx140->dev, "ti,pdm-edge-select"); if (pdm_count <= ADCX140_NUM_PDM_EDGES && pdm_count > 0) { @@ -835,27 +1046,31 @@ static int adcx140_codec_probe(struct snd_soc_component *component) return ret; } - ret = adcx140_reset(adcx140); + ret = adcx140_configure_gpio(adcx140); if (ret) - goto out; - - if(adcx140->supply_areg == NULL) - sleep_cfg_val |= ADCX140_AREG_INTERNAL; + return ret; - ret = regmap_write(adcx140->regmap, ADCX140_SLEEP_CFG, sleep_cfg_val); - if (ret) { - dev_err(adcx140->dev, "setting sleep config failed %d\n", ret); + ret = adcx140_configure_gpo(adcx140); + if (ret) goto out; - } - - /* 8.4.3: Wait >= 1ms after entering active mode. */ - usleep_range(1000, 100000); ret = regmap_update_bits(adcx140->regmap, ADCX140_BIAS_CFG, ADCX140_MIC_BIAS_VAL_MSK | ADCX140_MIC_BIAS_VREF_MSK, bias_cfg); if (ret) dev_err(adcx140->dev, "setting MIC bias failed %d\n", ret); + + tx_high_z = device_property_read_bool(adcx140->dev, "ti,asi-tx-drive"); + if (tx_high_z) { + ret = regmap_update_bits(adcx140->regmap, ADCX140_ASI_CFG0, + ADCX140_TX_FILL, ADCX140_TX_FILL); + if (ret) { + dev_err(adcx140->dev, "Setting Tx drive failed %d\n", ret); + goto out; + } + } + + adcx140_pwr_ctrl(adcx140, true); out: return ret; } @@ -864,21 +1079,19 @@ static int adcx140_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); - int pwr_cfg = 0; switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_STANDBY: - pwr_cfg = ADCX140_PWR_CFG_BIAS_PDZ | ADCX140_PWR_CFG_PLL_PDZ | - ADCX140_PWR_CFG_ADC_PDZ; + adcx140_pwr_ctrl(adcx140, true); break; case SND_SOC_BIAS_OFF: - pwr_cfg = 0x0; + adcx140_pwr_ctrl(adcx140, false); break; } - return regmap_write(adcx140->regmap, ADCX140_PWR_CFG, pwr_cfg); + return 0; } static const struct snd_soc_component_driver soc_codec_driver_adcx140 = { @@ -894,7 +1107,6 @@ static const struct snd_soc_component_driver soc_codec_driver_adcx140 = { .idle_bias_on = 0, .use_pmdown_time = 1, .endianness = 1, - .non_legacy_dai_naming = 1, }; static struct snd_soc_dai_driver adcx140_dai_driver[] = { @@ -908,10 +1120,11 @@ static struct snd_soc_dai_driver adcx140_dai_driver[] = { .formats = ADCX140_FORMATS, }, .ops = &adcx140_dai_ops, - .symmetric_rates = 1, + .symmetric_rate = 1, } }; +#ifdef CONFIG_OF static const struct of_device_id tlv320adcx140_of_match[] = { { .compatible = "ti,tlv320adc3140" }, { .compatible = "ti,tlv320adc5140" }, @@ -919,9 +1132,16 @@ static const struct of_device_id tlv320adcx140_of_match[] = { {}, }; MODULE_DEVICE_TABLE(of, tlv320adcx140_of_match); +#endif + +static void adcx140_disable_regulator(void *arg) +{ + struct adcx140_priv *adcx140 = arg; + + regulator_disable(adcx140->supply_areg); +} -static int adcx140_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int adcx140_i2c_probe(struct i2c_client *i2c) { struct adcx140_priv *adcx140; int ret; @@ -930,6 +1150,9 @@ static int adcx140_i2c_probe(struct i2c_client *i2c, if (!adcx140) return -ENOMEM; + adcx140->phase_calib_on = false; + adcx140->dev = &i2c->dev; + adcx140->gpio_reset = devm_gpiod_get_optional(adcx140->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(adcx140->gpio_reset)) @@ -940,14 +1163,18 @@ 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) { dev_err(adcx140->dev, "Failed to enable areg\n"); return ret; } + + ret = devm_add_action_or_reset(&i2c->dev, adcx140_disable_regulator, adcx140); + if (ret) + return ret; } adcx140->regmap = devm_regmap_init_i2c(i2c, &adcx140_i2c_regmap); @@ -957,7 +1184,7 @@ static int adcx140_i2c_probe(struct i2c_client *i2c, ret); return ret; } - adcx140->dev = &i2c->dev; + i2c_set_clientdata(i2c, adcx140); return devm_snd_soc_register_component(&i2c->dev, |
