linux-stable/sound/soc/codecs/adau1781.c
Mark Brown 3841ff1bac
ASoC: adau1781: Update to use maple tree register cache
The maple tree register cache is based on a much more modern data structure
than the rbtree cache and makes optimisation choices which are probably
more appropriate for modern systems than those made by the rbtree cache. In
v6.5 it has also acquired the ability to generate multi-register writes in
sync operations, bringing performance up to parity with the rbtree cache
there.

Update the adau1781 driver to use the more modern data structure.

Reviewed-by: Nuno Sa <nuno.sa@analog.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20230713-asoc-ad-maple-v1-7-7d2f35d42b5f@kernel.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2023-07-13 16:20:15 +01:00

507 lines
15 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for ADAU1381/ADAU1781 codec
*
* Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/platform_data/adau17x1.h>
#include "adau17x1.h"
#include "adau1781.h"
#define ADAU1781_DMIC_BEEP_CTRL 0x4008
#define ADAU1781_LEFT_PGA 0x400e
#define ADAU1781_RIGHT_PGA 0x400f
#define ADAU1781_LEFT_PLAYBACK_MIXER 0x401c
#define ADAU1781_RIGHT_PLAYBACK_MIXER 0x401e
#define ADAU1781_MONO_PLAYBACK_MIXER 0x401f
#define ADAU1781_LEFT_LINEOUT 0x4025
#define ADAU1781_RIGHT_LINEOUT 0x4026
#define ADAU1781_SPEAKER 0x4027
#define ADAU1781_BEEP_ZC 0x4028
#define ADAU1781_DEJITTER 0x4032
#define ADAU1781_DIG_PWDN0 0x4080
#define ADAU1781_DIG_PWDN1 0x4081
#define ADAU1781_INPUT_DIFFERNTIAL BIT(3)
#define ADAU1381_FIRMWARE "adau1381.bin"
#define ADAU1781_FIRMWARE "adau1781.bin"
static const struct reg_default adau1781_reg_defaults[] = {
{ ADAU1781_DMIC_BEEP_CTRL, 0x00 },
{ ADAU1781_LEFT_PGA, 0xc7 },
{ ADAU1781_RIGHT_PGA, 0xc7 },
{ ADAU1781_LEFT_PLAYBACK_MIXER, 0x00 },
{ ADAU1781_RIGHT_PLAYBACK_MIXER, 0x00 },
{ ADAU1781_MONO_PLAYBACK_MIXER, 0x00 },
{ ADAU1781_LEFT_LINEOUT, 0x00 },
{ ADAU1781_RIGHT_LINEOUT, 0x00 },
{ ADAU1781_SPEAKER, 0x00 },
{ ADAU1781_BEEP_ZC, 0x19 },
{ ADAU1781_DEJITTER, 0x60 },
{ ADAU1781_DIG_PWDN1, 0x0c },
{ ADAU1781_DIG_PWDN1, 0x00 },
{ ADAU17X1_CLOCK_CONTROL, 0x00 },
{ ADAU17X1_PLL_CONTROL, 0x00 },
{ ADAU17X1_REC_POWER_MGMT, 0x00 },
{ ADAU17X1_MICBIAS, 0x04 },
{ ADAU17X1_SERIAL_PORT0, 0x00 },
{ ADAU17X1_SERIAL_PORT1, 0x00 },
{ ADAU17X1_CONVERTER0, 0x00 },
{ ADAU17X1_CONVERTER1, 0x00 },
{ ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 },
{ ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 },
{ ADAU17X1_ADC_CONTROL, 0x00 },
{ ADAU17X1_PLAY_POWER_MGMT, 0x00 },
{ ADAU17X1_DAC_CONTROL0, 0x00 },
{ ADAU17X1_DAC_CONTROL1, 0x00 },
{ ADAU17X1_DAC_CONTROL2, 0x00 },
{ ADAU17X1_SERIAL_PORT_PAD, 0x00 },
{ ADAU17X1_CONTROL_PORT_PAD0, 0x00 },
{ ADAU17X1_CONTROL_PORT_PAD1, 0x00 },
{ ADAU17X1_DSP_SAMPLING_RATE, 0x01 },
{ ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 },
{ ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 },
{ ADAU17X1_DSP_ENABLE, 0x00 },
{ ADAU17X1_DSP_RUN, 0x00 },
{ ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 },
};
static const DECLARE_TLV_DB_SCALE(adau1781_speaker_tlv, 0, 200, 0);
static const DECLARE_TLV_DB_RANGE(adau1781_pga_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
4, 4, TLV_DB_SCALE_ITEM(1700, 0, 0),
5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
);
static const DECLARE_TLV_DB_RANGE(adau1781_beep_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
4, 4, TLV_DB_SCALE_ITEM(-2300, 0, 0),
5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
);
static const DECLARE_TLV_DB_SCALE(adau1781_sidetone_tlv, -1800, 300, 1);
static const char * const adau1781_speaker_bias_select_text[] = {
"Normal operation", "Power saving", "Enhanced performance",
};
static const char * const adau1781_bias_select_text[] = {
"Normal operation", "Extreme power saving", "Power saving",
"Enhanced performance",
};
static SOC_ENUM_SINGLE_DECL(adau1781_adc_bias_enum,
ADAU17X1_REC_POWER_MGMT, 3, adau1781_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_speaker_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 6, adau1781_speaker_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_dac_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 4, adau1781_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_playback_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 2, adau1781_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_capture_bias_enum,
ADAU17X1_REC_POWER_MGMT, 1, adau1781_bias_select_text);
static const struct snd_kcontrol_new adau1781_controls[] = {
SOC_SINGLE_TLV("Beep Capture Volume", ADAU1781_DMIC_BEEP_CTRL, 0, 7, 0,
adau1781_beep_tlv),
SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAU1781_LEFT_PGA,
ADAU1781_RIGHT_PGA, 5, 7, 0, adau1781_pga_tlv),
SOC_DOUBLE_R("PGA Capture Switch", ADAU1781_LEFT_PGA,
ADAU1781_RIGHT_PGA, 1, 1, 0),
SOC_DOUBLE_R("Lineout Playback Switch", ADAU1781_LEFT_LINEOUT,
ADAU1781_RIGHT_LINEOUT, 1, 1, 0),
SOC_SINGLE("Beep ZC Switch", ADAU1781_BEEP_ZC, 0, 1, 0),
SOC_SINGLE("Mono Playback Switch", ADAU1781_MONO_PLAYBACK_MIXER,
0, 1, 0),
SOC_SINGLE_TLV("Mono Playback Volume", ADAU1781_SPEAKER, 6, 3, 0,
adau1781_speaker_tlv),
SOC_ENUM("ADC Bias", adau1781_adc_bias_enum),
SOC_ENUM("DAC Bias", adau1781_dac_bias_enum),
SOC_ENUM("Capture Bias", adau1781_capture_bias_enum),
SOC_ENUM("Playback Bias", adau1781_playback_bias_enum),
SOC_ENUM("Speaker Bias", adau1781_speaker_bias_enum),
};
static const struct snd_kcontrol_new adau1781_beep_mixer_controls[] = {
SOC_DAPM_SINGLE("Beep Capture Switch", ADAU1781_DMIC_BEEP_CTRL,
3, 1, 0),
};
static const struct snd_kcontrol_new adau1781_left_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Switch",
ADAU1781_LEFT_PLAYBACK_MIXER, 5, 1, 0),
SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
};
static const struct snd_kcontrol_new adau1781_right_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Switch",
ADAU1781_RIGHT_PLAYBACK_MIXER, 6, 1, 0),
SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
};
static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Left Switch",
ADAU1781_MONO_PLAYBACK_MIXER, 7, 1, 0),
SOC_DAPM_SINGLE_AUTODISABLE("Right Switch",
ADAU1781_MONO_PLAYBACK_MIXER, 6, 1, 0),
SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
ADAU1781_MONO_PLAYBACK_MIXER, 2, 8, 0, adau1781_sidetone_tlv),
};
static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct adau *adau = snd_soc_component_get_drvdata(component);
/* After any power changes have been made the dejitter circuit
* has to be reinitialized. */
regmap_write(adau->regmap, ADAU1781_DEJITTER, 0);
if (!adau->master)
regmap_write(adau->regmap, ADAU1781_DEJITTER, 5);
return 0;
}
static const struct snd_soc_dapm_widget adau1781_dapm_widgets[] = {
SND_SOC_DAPM_PGA("Left PGA", ADAU1781_LEFT_PGA, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right PGA", ADAU1781_RIGHT_PGA, 0, 0, NULL, 0),
SND_SOC_DAPM_OUT_DRV("Speaker", ADAU1781_SPEAKER, 0, 0, NULL, 0),
SOC_MIXER_NAMED_CTL_ARRAY("Beep Mixer", ADAU17X1_MICBIAS, 4, 0,
adau1781_beep_mixer_controls),
SOC_MIXER_ARRAY("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
adau1781_left_mixer_controls),
SOC_MIXER_ARRAY("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
adau1781_right_mixer_controls),
SOC_MIXER_ARRAY("Mono Mixer", SND_SOC_NOPM, 0, 0,
adau1781_mono_mixer_controls),
SND_SOC_DAPM_SUPPLY("Serial Input Routing", ADAU1781_DIG_PWDN0,
2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Serial Output Routing", ADAU1781_DIG_PWDN0,
3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Clock Domain Transfer", ADAU1781_DIG_PWDN0,
5, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Serial Ports", ADAU1781_DIG_PWDN0, 4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC Engine", ADAU1781_DIG_PWDN0, 7, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC Engine", ADAU1781_DIG_PWDN1, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Digital Mic", ADAU1781_DIG_PWDN1, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Sound Engine", ADAU1781_DIG_PWDN0, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, ADAU1781_DIG_PWDN0, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Zero Crossing Detector", ADAU1781_DIG_PWDN1, 2, 0,
NULL, 0),
SND_SOC_DAPM_POST("Dejitter fixup", adau1781_dejitter_fixup),
SND_SOC_DAPM_INPUT("BEEP"),
SND_SOC_DAPM_OUTPUT("AOUTL"),
SND_SOC_DAPM_OUTPUT("AOUTR"),
SND_SOC_DAPM_OUTPUT("SP"),
SND_SOC_DAPM_INPUT("LMIC"),
SND_SOC_DAPM_INPUT("RMIC"),
};
static const struct snd_soc_dapm_route adau1781_dapm_routes[] = {
{ "Left Lineout Mixer", NULL, "Left Playback Enable" },
{ "Right Lineout Mixer", NULL, "Right Playback Enable" },
{ "Left Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
{ "Left Lineout Mixer", "Switch", "Left DAC" },
{ "Right Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
{ "Right Lineout Mixer", "Switch", "Right DAC" },
{ "Mono Mixer", "Beep Playback Volume", "Beep Mixer" },
{ "Mono Mixer", "Right Switch", "Right DAC" },
{ "Mono Mixer", "Left Switch", "Left DAC" },
{ "Speaker", NULL, "Mono Mixer" },
{ "Mono Mixer", NULL, "SYSCLK" },
{ "Left Lineout Mixer", NULL, "SYSCLK" },
{ "Left Lineout Mixer", NULL, "SYSCLK" },
{ "Beep Mixer", "Beep Capture Switch", "BEEP" },
{ "Beep Mixer", NULL, "Zero Crossing Detector" },
{ "Left DAC", NULL, "DAC Engine" },
{ "Right DAC", NULL, "DAC Engine" },
{ "Sound Engine", NULL, "SYSCLK" },
{ "DSP", NULL, "Sound Engine" },
{ "Left Decimator", NULL, "ADC Engine" },
{ "Right Decimator", NULL, "ADC Engine" },
{ "AIFCLK", NULL, "SYSCLK" },
{ "Playback", NULL, "Serial Input Routing" },
{ "Playback", NULL, "Serial Ports" },
{ "Playback", NULL, "Clock Domain Transfer" },
{ "Capture", NULL, "Serial Output Routing" },
{ "Capture", NULL, "Serial Ports" },
{ "Capture", NULL, "Clock Domain Transfer" },
{ "AOUTL", NULL, "Left Lineout Mixer" },
{ "AOUTR", NULL, "Right Lineout Mixer" },
{ "SP", NULL, "Speaker" },
};
static const struct snd_soc_dapm_route adau1781_adc_dapm_routes[] = {
{ "Left PGA", NULL, "LMIC" },
{ "Right PGA", NULL, "RMIC" },
{ "Left Decimator", NULL, "Left PGA" },
{ "Right Decimator", NULL, "Right PGA" },
};
static const char * const adau1781_dmic_select_text[] = {
"DMIC1", "DMIC2",
};
static SOC_ENUM_SINGLE_VIRT_DECL(adau1781_dmic_select_enum,
adau1781_dmic_select_text);
static const struct snd_kcontrol_new adau1781_dmic_mux =
SOC_DAPM_ENUM("DMIC Select", adau1781_dmic_select_enum);
static const struct snd_soc_dapm_widget adau1781_dmic_dapm_widgets[] = {
SND_SOC_DAPM_MUX("DMIC Select", SND_SOC_NOPM, 0, 0, &adau1781_dmic_mux),
SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1781_DMIC_BEEP_CTRL, 4, 0),
SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1781_DMIC_BEEP_CTRL, 5, 0),
};
static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = {
{ "DMIC1", NULL, "LMIC" },
{ "DMIC2", NULL, "RMIC" },
{ "DMIC1", NULL, "Digital Mic" },
{ "DMIC2", NULL, "Digital Mic" },
{ "DMIC Select", "DMIC1", "DMIC1" },
{ "DMIC Select", "DMIC2", "DMIC2" },
{ "Left Decimator", NULL, "DMIC Select" },
{ "Right Decimator", NULL, "DMIC Select" },
};
static int adau1781_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct adau *adau = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
/* Precharge */
regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0x8, 0x8);
break;
case SND_SOC_BIAS_OFF:
regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0xc, 0x0);
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
break;
}
return 0;
}
static bool adau1781_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case ADAU1781_DMIC_BEEP_CTRL:
case ADAU1781_LEFT_PGA:
case ADAU1781_RIGHT_PGA:
case ADAU1781_LEFT_PLAYBACK_MIXER:
case ADAU1781_RIGHT_PLAYBACK_MIXER:
case ADAU1781_MONO_PLAYBACK_MIXER:
case ADAU1781_LEFT_LINEOUT:
case ADAU1781_RIGHT_LINEOUT:
case ADAU1781_SPEAKER:
case ADAU1781_BEEP_ZC:
case ADAU1781_DEJITTER:
case ADAU1781_DIG_PWDN0:
case ADAU1781_DIG_PWDN1:
return true;
default:
break;
}
return adau17x1_readable_register(dev, reg);
}
static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
bool differential)
{
unsigned int val;
if (differential)
val = ADAU1781_INPUT_DIFFERNTIAL;
else
val = 0;
return regmap_update_bits(adau->regmap, reg,
ADAU1781_INPUT_DIFFERNTIAL, val);
}
static int adau1781_component_probe(struct snd_soc_component *component)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
struct adau1781_platform_data *pdata = dev_get_platdata(component->dev);
struct adau *adau = snd_soc_component_get_drvdata(component);
int ret;
ret = adau17x1_add_widgets(component);
if (ret)
return ret;
if (pdata) {
ret = adau1781_set_input_mode(adau, ADAU1781_LEFT_PGA,
pdata->left_input_differential);
if (ret)
return ret;
ret = adau1781_set_input_mode(adau, ADAU1781_RIGHT_PGA,
pdata->right_input_differential);
if (ret)
return ret;
}
if (pdata && pdata->use_dmic) {
ret = snd_soc_dapm_new_controls(dapm,
adau1781_dmic_dapm_widgets,
ARRAY_SIZE(adau1781_dmic_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(dapm, adau1781_dmic_dapm_routes,
ARRAY_SIZE(adau1781_dmic_dapm_routes));
if (ret)
return ret;
} else {
ret = snd_soc_dapm_add_routes(dapm, adau1781_adc_dapm_routes,
ARRAY_SIZE(adau1781_adc_dapm_routes));
if (ret)
return ret;
}
ret = adau17x1_add_routes(component);
if (ret < 0)
return ret;
return 0;
}
static const struct snd_soc_component_driver adau1781_component_driver = {
.probe = adau1781_component_probe,
.resume = adau17x1_resume,
.set_bias_level = adau1781_set_bias_level,
.controls = adau1781_controls,
.num_controls = ARRAY_SIZE(adau1781_controls),
.dapm_widgets = adau1781_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(adau1781_dapm_widgets),
.dapm_routes = adau1781_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1781_dapm_routes),
.suspend_bias_off = 1,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
};
#define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver adau1781_dai_driver = {
.name = "adau-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1781_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1781_FORMATS,
},
.ops = &adau17x1_dai_ops,
};
const struct regmap_config adau1781_regmap_config = {
.val_bits = 8,
.reg_bits = 16,
.max_register = 0x40f8,
.reg_defaults = adau1781_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1781_reg_defaults),
.readable_reg = adau1781_readable_register,
.volatile_reg = adau17x1_volatile_register,
.precious_reg = adau17x1_precious_register,
.cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(adau1781_regmap_config);
int adau1781_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev))
{
const char *firmware_name;
int ret;
switch (type) {
case ADAU1381:
firmware_name = ADAU1381_FIRMWARE;
break;
case ADAU1781:
firmware_name = ADAU1781_FIRMWARE;
break;
default:
return -EINVAL;
}
ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name);
if (ret)
return ret;
return devm_snd_soc_register_component(dev, &adau1781_component_driver,
&adau1781_dai_driver, 1);
}
EXPORT_SYMBOL_GPL(adau1781_probe);
MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");