ASoC: adau1761: Add ADAU1761-as-ADAU1361 compatibility mode

During probe, determine if the chip is in fact an ADAU1761
even though an ADAU1361 is specified, and perform additional
operations to enable the ADAU1761 to behave as an ADAU1361,
i.e. disregarding the DSP and setting up routing and PM
transparently.

This enables either chip to be mounted when an ADAU1361 is specified.

Signed-off-by: Ricard Wanderlof <ricardw@axis.com>
Link: https://lore.kernel.org/r/alpine.DEB.2.21.2204281841290.5574@lnxricardw1.se.axis.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Ricard Wanderlof 2022-04-28 18:46:35 +02:00 committed by Mark Brown
parent c8220e8721
commit c7b9239583
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
3 changed files with 99 additions and 8 deletions

View File

@ -556,8 +556,6 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Left DAC", NULL, "Interpolator Resync Clock" }, { "Left DAC", NULL, "Interpolator Resync Clock" },
{ "Right DAC", NULL, "Interpolator Resync Clock" }, { "Right DAC", NULL, "Interpolator Resync Clock" },
{ "DSP", NULL, "Digital Clock 0" },
{ "Slew Clock", NULL, "Digital Clock 0" }, { "Slew Clock", NULL, "Digital Clock 0" },
{ "Right Playback Mixer", NULL, "Slew Clock" }, { "Right Playback Mixer", NULL, "Slew Clock" },
{ "Left Playback Mixer", NULL, "Slew Clock" }, { "Left Playback Mixer", NULL, "Slew Clock" },
@ -569,6 +567,56 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Digital Clock 1", NULL, "SYSCLK" }, { "Digital Clock 1", NULL, "SYSCLK" },
}; };
static const struct snd_soc_dapm_route adau1761_dapm_dsp_routes[] = {
{ "DSP", NULL, "Digital Clock 0" },
};
static int adau1761_compatibility_probe(struct device *dev)
{
struct adau *adau = dev_get_drvdata(dev);
struct regmap *regmap = adau->regmap;
int val, ret = 0;
/* Only consider compatibility mode when ADAU1361 was specified. */
if (adau->type != ADAU1361)
return 0;
regcache_cache_bypass(regmap, true);
/*
* This will enable the core clock and bypass the PLL,
* so that we can access the registers for probing purposes
* (without having to set up the PLL).
*/
regmap_write(regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
/*
* ADAU17X1_SERIAL_SAMPLING_RATE doesn't exist in non-DSP chips;
* reading it results in zero at all times, and write is a no-op.
* Use this register to probe for ADAU1761.
*/
regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 1);
ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val);
if (ret)
goto exit;
if (val != 1)
goto exit;
regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 0);
ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val);
if (ret)
goto exit;
if (val != 0)
goto exit;
adau->type = ADAU1761_AS_1361;
exit:
/* Disable core clock after probing. */
regmap_write(regmap, ADAU17X1_CLOCK_CONTROL, 0);
regcache_cache_bypass(regmap, false);
return ret;
}
static int adau1761_set_bias_level(struct snd_soc_component *component, static int adau1761_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level) enum snd_soc_bias_level level)
{ {
@ -823,7 +871,11 @@ static int adau1761_component_probe(struct snd_soc_component *component)
if (ret) if (ret)
return ret; return ret;
if (adau->type == ADAU1761) { /*
* If we've got an ADAU1761, or an ADAU1761 operating as an
* ADAU1361, we need these non-DSP related DAPM widgets and routes.
*/
if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361) {
ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets, ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets,
ARRAY_SIZE(adau1761_dapm_widgets)); ARRAY_SIZE(adau1761_dapm_widgets));
if (ret) if (ret)
@ -834,7 +886,29 @@ static int adau1761_component_probe(struct snd_soc_component *component)
if (ret) if (ret)
return ret; return ret;
} }
/*
* These routes are DSP related and only used when we have a
* bona fide ADAU1761.
*/
if (adau->type == ADAU1761) {
ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_dsp_routes,
ARRAY_SIZE(adau1761_dapm_dsp_routes));
if (ret)
return ret;
}
/*
* In the ADAU1761, by default, the AIF is routed to the DSP, whereas
* for the ADAU1361, the AIF is permanently routed to the ADC and DAC.
* Thus, if we have an ADAU1761 masquerading as an ADAU1361,
* we need to explicitly route the AIF to the ADC and DAC.
* For the ADAU1761, this is normally done by set_tdm_slot, but this
* function is not necessarily called during stream setup, so set up
* the compatible AIF routings here from the start.
*/
if (adau->type == ADAU1761_AS_1361) {
regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE, 0x01);
regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x01);
}
ret = adau17x1_add_routes(component); ret = adau17x1_add_routes(component);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -919,6 +993,10 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
if (ret) if (ret)
return ret; return ret;
ret = adau1761_compatibility_probe(dev);
if (ret)
return ret;
/* Enable cache only mode as we could miss writes before bias level /* Enable cache only mode as we could miss writes before bias level
* reaches standby and the core clock is enabled */ * reaches standby and the core clock is enabled */
regcache_cache_only(regmap, true); regcache_cache_only(regmap, true);

View File

@ -334,6 +334,17 @@ static bool adau17x1_has_dsp(struct adau *adau)
} }
} }
/* Chip has a DSP but we're pretending it doesn't. */
static bool adau17x1_has_disused_dsp(struct adau *adau)
{
switch (adau->type) {
case ADAU1761_AS_1361:
return true;
default:
return false;
}
}
static bool adau17x1_has_safeload(struct adau *adau) static bool adau17x1_has_safeload(struct adau *adau)
{ {
switch (adau->type) { switch (adau->type) {
@ -516,10 +527,11 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0, regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
ADAU17X1_CONVERTER0_CONVSR_MASK, div); ADAU17X1_CONVERTER0_CONVSR_MASK, div);
if (adau17x1_has_dsp(adau)) {
if (adau17x1_has_dsp(adau) || adau17x1_has_disused_dsp(adau))
regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div); regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div);
if (adau17x1_has_dsp(adau))
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div); regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
}
if (adau->sigmadsp) { if (adau->sigmadsp) {
ret = adau17x1_setup_firmware(component, params_rate(params)); ret = adau17x1_setup_firmware(component, params_rate(params));
@ -663,7 +675,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
switch (slot_width * slots) { switch (slot_width * slots) {
case 32: case 32:
if (adau->type == ADAU1761) if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361)
return -EINVAL; return -EINVAL;
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32; ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32;
@ -738,7 +750,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1, regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1); ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
if (!adau17x1_has_dsp(adau)) if (!adau17x1_has_dsp(adau) && !adau17x1_has_disused_dsp(adau))
return 0; return 0;
if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) { if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) {

View File

@ -10,6 +10,7 @@
enum adau17x1_type { enum adau17x1_type {
ADAU1361, ADAU1361,
ADAU1761, ADAU1761,
ADAU1761_AS_1361,
ADAU1381, ADAU1381,
ADAU1781, ADAU1781,
}; };