Tested-by: Chris Healy cphealy@gmail.com
This was tested on an i.MX6 platform with TLV320DAC3100 DAC. No non-existent capture devices show up any longer.
On Thu, Sep 22, 2016 at 1:06 PM, Nikita Yushchenko < nikita.yoush@cogentembedded.com> wrote:
tlv320dac31xx is a subset of tlv320aic31xx:
- it does not have MIC inputs and ADC, thus capture is not supported,
- it has analog inputs AIN1/AIN2 that can be mixed into output.
Although tlv320dac31xx does work with tlv320aic31xx driver, this setup does register non-existent widgets and non-existent capture stream. Thus userspace lists non-existent objects in user interfaces, an can access these, causing operations with device registers that are declared as "reserved" in tlv320dac31xx datasheet.
This patch fixes this situation by separating controls/widgets/routes into common, aic31xx-specific, and dac31xx-specific parts. Only parts that match actual hardware (as declared in "compatible" device tree property) are registered.
Signed-off-by: Nikita Yushchenko nikita.yoush@cogentembedded.com
sound/soc/codecs/tlv320aic31xx.c | 212 ++++++++++++++++++++++++++++--
sound/soc/codecs/tlv320aic31xx.h | 2 + 2 files changed, 158 insertions(+), 56 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/ tlv320aic31xx.c index 3c5e1df..fb7d648 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -273,10 +273,20 @@ static const DECLARE_TLV_DB_SCALE(sp_vol_tlv, -6350, 50, 0); /*
- controls to be exported to the user space
*/ -static const struct snd_kcontrol_new aic31xx_snd_controls[] = { +static const struct snd_kcontrol_new common31xx_snd_controls[] = { SOC_DOUBLE_R_S_TLV("DAC Playback Volume", AIC31XX_LDACVOL, AIC31XX_RDACVOL, 0, -127, 48, 7, 0, dac_vol_tlv),
SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,AIC31XX_HPRGAIN, 2, 1, 0),SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),+};
+static const struct snd_kcontrol_new aic31xx_snd_controls[] = { SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1, adc_fgain_tlv),
@@ -286,14 +296,6 @@ static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
SOC_SINGLE_TLV("Mic PGA Capture Volume", AIC31XX_MICPGA, 0, 119, 0, mic_pga_tlv),
SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,AIC31XX_HPRGAIN, 2, 1, 0),SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),};
static const struct snd_kcontrol_new aic311x_snd_controls[] = { @@ -397,17 +399,28 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w, return 0; }
-static const struct snd_kcontrol_new left_output_switches[] = { +static const struct snd_kcontrol_new aic31xx_left_output_switches[] = { SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0), SOC_DAPM_SINGLE("From MIC1LP", AIC31XX_DACMIXERROUTE, 5, 1, 0), SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 4, 1, 0), };
-static const struct snd_kcontrol_new right_output_switches[] = { +static const struct snd_kcontrol_new aic31xx_right_output_switches[] = { SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0), SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 1, 1, 0), };
+static const struct snd_kcontrol_new dac31xx_left_output_switches[] = {
SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),SOC_DAPM_SINGLE("From AIN1", AIC31XX_DACMIXERROUTE, 5, 1, 0),SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 4, 1, 0),+};
+static const struct snd_kcontrol_new dac31xx_right_output_switches[] = {
SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 1, 1, 0),+};
static const struct snd_kcontrol_new p_term_mic1lp = SOC_DAPM_ENUM("MIC1LP P-Terminal", mic1lp_p_enum);
@@ -457,7 +470,7 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, return 0; }
-static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = { +static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("DAC Left Input",@@ -473,14 +486,7 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = { AIC31XX_DACSETUP, 6, 0, aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Output Mixers */SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,left_output_switches,ARRAY_SIZE(left_output_switches)),SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,right_output_switches,ARRAY_SIZE(right_output_switches)),
/* HP */ SND_SOC_DAPM_SWITCH("HP Left", SND_SOC_NOPM, 0, 0, &aic31xx_dapm_hpl_switch), SND_SOC_DAPM_SWITCH("HP Right", SND_SOC_NOPM, 0, 0,@@ -494,10 +500,34 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = { NULL, 0, aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
/* ADC */SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU|
SND_SOC_DAPM_POST_PMD),
/* Mic Bias */SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),/* Outputs */SND_SOC_DAPM_OUTPUT("HPL"),SND_SOC_DAPM_OUTPUT("HPR"),+};
+static const struct snd_soc_dapm_widget dac31xx_dapm_widgets[] = {
/* Inputs */SND_SOC_DAPM_INPUT("AIN1"),SND_SOC_DAPM_INPUT("AIN2"),/* Output Mixers */SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,dac31xx_left_output_switches,ARRAY_SIZE(dac31xx_left_output_switches)),SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,dac31xx_right_output_switches,ARRAY_SIZE(dac31xx_right_output_switches)),+};
+static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
/* Inputs */SND_SOC_DAPM_INPUT("MIC1LP"),SND_SOC_DAPM_INPUT("MIC1RP"),SND_SOC_DAPM_INPUT("MIC1LM"), /* Input Selection to MIC_PGA */ SND_SOC_DAPM_MUX("MIC1LP P-Terminal", SND_SOC_NOPM, 0, 0,@@ -507,24 +537,25 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = { SND_SOC_DAPM_MUX("MIC1LM P-Terminal", SND_SOC_NOPM, 0, 0, &p_term_mic1lm),
/* ADC */SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU|
SND_SOC_DAPM_POST_PMD),SND_SOC_DAPM_MUX("MIC1LM M-Terminal", SND_SOC_NOPM, 0, 0, &m_term_mic1lm),/* Enabling & Disabling MIC Gain Ctl */ SND_SOC_DAPM_PGA("MIC_GAIN_CTL", AIC31XX_MICPGA, 7, 1, NULL, 0),
/* Mic Bias */SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),/* Outputs */SND_SOC_DAPM_OUTPUT("HPL"),SND_SOC_DAPM_OUTPUT("HPR"),/* Inputs */SND_SOC_DAPM_INPUT("MIC1LP"),SND_SOC_DAPM_INPUT("MIC1RP"),SND_SOC_DAPM_INPUT("MIC1LM"),
/* Output Mixers */SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,aic31xx_left_output_switches,ARRAY_SIZE(aic31xx_left_output_switches)),SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,aic31xx_right_output_switches,ARRAY_SIZE(aic31xx_right_output_switches)),};
static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = { @@ -554,7 +585,7 @@ static const struct snd_soc_dapm_widget aic310x_dapm_widgets[] = { };
static const struct snd_soc_dapm_route -aic31xx_audio_map[] = { +common31xx_audio_map[] = { /* DAC Input Routing */ {"DAC Left Input", "Left Data", "DAC IN"}, {"DAC Left Input", "Right Data", "DAC IN"}, @@ -565,6 +596,31 @@ aic31xx_audio_map[] = { {"DAC Left", NULL, "DAC Left Input"}, {"DAC Right", NULL, "DAC Right Input"},
/* HPL path */{"HP Left", "Switch", "Output Left"},{"HPL Driver", NULL, "HP Left"},{"HPL", NULL, "HPL Driver"},/* HPR path */{"HP Right", "Switch", "Output Right"},{"HPR Driver", NULL, "HP Right"},{"HPR", NULL, "HPR Driver"},+};
+static const struct snd_soc_dapm_route +dac31xx_audio_map[] = {
/* Left Output */{"Output Left", "From Left DAC", "DAC Left"},{"Output Left", "From AIN1", "AIN1"},{"Output Left", "From AIN2", "AIN2"},/* Right Output */{"Output Right", "From Right DAC", "DAC Right"},{"Output Right", "From AIN2", "AIN2"},+};
+static const struct snd_soc_dapm_route +aic31xx_audio_map[] = { /* Mic input */ {"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"}, {"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"}, @@ -595,16 +651,6 @@ aic31xx_audio_map[] = { /* Right Output */ {"Output Right", "From Right DAC", "DAC Right"}, {"Output Right", "From MIC1RP", "MIC1RP"},
/* HPL path */{"HP Left", "Switch", "Output Left"},{"HPL Driver", NULL, "HP Left"},{"HPL", NULL, "HPL Driver"},/* HPR path */{"HP Right", "Switch", "Output Right"},{"HPR Driver", NULL, "HP Right"},{"HPR", NULL, "HPR Driver"},};
static const struct snd_soc_dapm_route @@ -633,6 +679,13 @@ static int aic31xx_add_controls(struct snd_soc_codec *codec) int ret = 0; struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
if (!(aic31xx->pdata.codec_type & DAC31XX_BIT))ret = snd_soc_add_codec_controls(codec, aic31xx_snd_controls,ARRAY_SIZE(aic31xx_snd_controls));if (ret)return ret;if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) ret = snd_soc_add_codec_controls( codec, aic311x_snd_controls,@@ -651,6 +704,30 @@ static int aic31xx_add_widgets(struct snd_soc_codec *codec) struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); int ret = 0;
if (aic31xx->pdata.codec_type & DAC31XX_BIT) {ret = snd_soc_dapm_new_controls(dapm, dac31xx_dapm_widgets,ARRAY_SIZE(dac31xx_dapm_widgets));if (ret)return ret;ret = snd_soc_dapm_add_routes(dapm, dac31xx_audio_map,ARRAY_SIZE(dac31xx_audio_map));
if (ret)return ret;} else {ret = snd_soc_dapm_new_controls(dapm, aic31xx_dapm_widgets,ARRAY_SIZE(aic31xx_dapm_widgets));if (ret)return ret;ret = snd_soc_dapm_add_routes(dapm, aic31xx_audio_map,ARRAY_SIZE(aic31xx_audio_map));
if (ret)return ret;}if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) { ret = snd_soc_dapm_new_controls( dapm, aic311x_dapm_widgets,@@ -1114,12 +1191,12 @@ static struct snd_soc_codec_driver soc_codec_driver_aic31xx = { .set_bias_level = aic31xx_set_bias_level, .suspend_bias_off = true,
.controls = aic31xx_snd_controls,.num_controls = ARRAY_SIZE(aic31xx_snd_controls),.dapm_widgets = aic31xx_dapm_widgets,.num_dapm_widgets = ARRAY_SIZE(aic31xx_dapm_widgets),.dapm_routes = aic31xx_audio_map,.num_dapm_routes = ARRAY_SIZE(aic31xx_audio_map),
.controls = common31xx_snd_controls,.num_controls = ARRAY_SIZE(common31xx_snd_controls),.dapm_widgets = common31xx_dapm_widgets,.num_dapm_widgets = ARRAY_SIZE(common31xx_dapm_widgets),.dapm_routes = common31xx_audio_map,.num_dapm_routes = ARRAY_SIZE(common31xx_audio_map),};
static const struct snd_soc_dai_ops aic31xx_dai_ops = { @@ -1129,6 +1206,21 @@ static const struct snd_soc_dai_ops aic31xx_dai_ops = { .digital_mute = aic31xx_dac_mute, };
+static struct snd_soc_dai_driver dac31xx_dai_driver[] = {
{.name = "tlv32dac31xx-hifi",.playback = {.stream_name = "Playback",.channels_min = 1,.channels_max = 2,.rates = AIC31XX_RATES,.formats = AIC31XX_FORMATS,},.ops = &aic31xx_dai_ops,.symmetric_rates = 1,}+};
static struct snd_soc_dai_driver aic31xx_dai_driver[] = { { .name = "tlv320aic31xx-hifi", @@ -1259,9 +1351,16 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, if (ret) return ret;
return snd_soc_register_codec(&i2c->dev,&soc_codec_driver_aic31xx,
aic31xx_dai_driver,ARRAY_SIZE(aic31xx_dai_driver));
if (aic31xx->pdata.codec_type & DAC31XX_BIT)return snd_soc_register_codec(&i2c->dev,&soc_codec_driver_aic31xx,dac31xx_dai_driver,ARRAY_SIZE(dac31xx_dai_driver));elsereturn snd_soc_register_codec(&i2c->dev,&soc_codec_driver_aic31xx,aic31xx_dai_driver,ARRAY_SIZE(aic31xx_dai_driver));}
static int aic31xx_i2c_remove(struct i2c_client *i2c) @@ -1277,6 +1376,7 @@ static const struct i2c_device_id aic31xx_i2c_id[] = { { "tlv320aic3110", AIC3110 }, { "tlv320aic3120", AIC3120 }, { "tlv320aic3111", AIC3111 },
{ "tlv320dac3100", DAC3100 }, { }}; MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id); diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/ tlv320aic31xx.h index ac9b146..5acd5b6 100644 --- a/sound/soc/codecs/tlv320aic31xx.h +++ b/sound/soc/codecs/tlv320aic31xx.h @@ -24,12 +24,14 @@
#define AIC31XX_STEREO_CLASS_D_BIT 0x1 #define AIC31XX_MINIDSP_BIT 0x2 +#define DAC31XX_BIT 0x4
enum aic31xx_type { AIC3100 = 0, AIC3110 = AIC31XX_STEREO_CLASS_D_BIT, AIC3120 = AIC31XX_MINIDSP_BIT, AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT),
DAC3100 = DAC31XX_BIT,};
struct aic31xx_pdata {
2.1.4