Skip to content

Commit

Permalink
ASoC: tlv320aic32x4: add support for TAS2505
Browse files Browse the repository at this point in the history
This adds support for TAS2505 and TAS2521 to the tlv320aic32x4 driver.

The TAS2505 seems to be a stripped down version of the TLV320AIC32X4 so
it makes sense to handle them in the same driver.

Signed-off-by: Claudius Heine <ch@denx.de>
Link: https://lore.kernel.org/r/20210617085230.1851503-3-ch@denx.de
Signed-off-by: Mark Brown <broonie@kernel.org>
  • Loading branch information
cmhe authored and broonie committed Jun 23, 2021
1 parent 688d47c commit b4525b6
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 1 deletion.
2 changes: 2 additions & 0 deletions sound/soc/codecs/tlv320aic32x4-i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@ static int aic32x4_i2c_remove(struct i2c_client *i2c)
static const struct i2c_device_id aic32x4_i2c_id[] = {
{ "tlv320aic32x4", (kernel_ulong_t)AIC32X4_TYPE_AIC32X4 },
{ "tlv320aic32x6", (kernel_ulong_t)AIC32X4_TYPE_AIC32X6 },
{ "tas2505", (kernel_ulong_t)AIC32X4_TYPE_TAS2505 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);

static const struct of_device_id aic32x4_of_id[] = {
{ .compatible = "ti,tlv320aic32x4", .data = (void *)AIC32X4_TYPE_AIC32X4 },
{ .compatible = "ti,tlv320aic32x6", .data = (void *)AIC32X4_TYPE_AIC32X6 },
{ .compatible = "ti,tas2505", .data = (void *)AIC32X4_TYPE_TAS2505 },
{ /* senitel */ }
};
MODULE_DEVICE_TABLE(of, aic32x4_of_id);
Expand Down
136 changes: 135 additions & 1 deletion sound/soc/codecs/tlv320aic32x4.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ static DECLARE_TLV_DB_SCALE(tlv_driver_gain, -600, 100, 0);
/* -12dB min, 0.5dB steps */
static DECLARE_TLV_DB_SCALE(tlv_adc_vol, -1200, 50, 0);

static DECLARE_TLV_DB_LINEAR(tlv_spk_vol, TLV_DB_GAIN_MUTE, 0);
static DECLARE_TLV_DB_SCALE(tlv_amp_vol, 0, 600, 1);

static const char * const lo_cm_text[] = {
"Full Chip", "1.65V",
};
Expand Down Expand Up @@ -1059,6 +1062,129 @@ static const struct snd_soc_component_driver soc_component_dev_aic32x4 = {
.non_legacy_dai_naming = 1,
};

static const struct snd_kcontrol_new aic32x4_tas2505_snd_controls[] = {
SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
AIC32X4_LDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm),
SOC_ENUM("DAC Playback PowerTune Switch", l_ptm_enum),
SOC_DOUBLE_R_S_TLV("HP Driver Playback Volume", AIC32X4_HPLGAIN,
AIC32X4_HPLGAIN, 0, -0x6, 0x1d, 5, 0,
tlv_driver_gain),
SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN,
AIC32X4_HPLGAIN, 6, 0x01, 1),

SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0),

SOC_SINGLE_RANGE_TLV("Speaker Driver Playback Volume", TAS2505_SPKVOL1,
0, 0, 117, 1, tlv_spk_vol),
SOC_SINGLE_TLV("Speaker Amplifier Playback Volume", TAS2505_SPKVOL2,
4, 5, 0, tlv_amp_vol),
};

static const struct snd_kcontrol_new hp_output_mixer_controls[] = {
SOC_DAPM_SINGLE("DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0),
};

static const struct snd_soc_dapm_widget aic32x4_tas2505_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", AIC32X4_DACSETUP, 7, 0),
SND_SOC_DAPM_MIXER("HP Output Mixer", SND_SOC_NOPM, 0, 0,
&hp_output_mixer_controls[0],
ARRAY_SIZE(hp_output_mixer_controls)),
SND_SOC_DAPM_PGA("HP Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0),

SND_SOC_DAPM_PGA("Speaker Driver", TAS2505_SPK, 1, 0, NULL, 0),

SND_SOC_DAPM_OUTPUT("HP"),
SND_SOC_DAPM_OUTPUT("Speaker"),
};

static const struct snd_soc_dapm_route aic32x4_tas2505_dapm_routes[] = {
/* Left Output */
{"HP Output Mixer", "DAC Switch", "DAC"},

{"HP Power", NULL, "HP Output Mixer"},
{"HP", NULL, "HP Power"},

{"Speaker Driver", NULL, "DAC"},
{"Speaker", NULL, "Speaker Driver"},
};

static struct snd_soc_dai_driver aic32x4_tas2505_dai = {
.name = "tas2505-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = AIC32X4_FORMATS,},
.ops = &aic32x4_ops,
.symmetric_rate = 1,
};

static int aic32x4_tas2505_component_probe(struct snd_soc_component *component)
{
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
u32 tmp_reg;
int ret;

struct clk_bulk_data clocks[] = {
{ .id = "codec_clkin" },
{ .id = "pll" },
{ .id = "bdiv" },
{ .id = "mdac" },
};

ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
if (ret)
return ret;

if (aic32x4->setup)
aic32x4_setup_gpios(component);

clk_set_parent(clocks[0].clk, clocks[1].clk);
clk_set_parent(clocks[2].clk, clocks[3].clk);

/* Power platform configuration */
if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE)
snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE);

tmp_reg = (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) ?
AIC32X4_LDOCTLEN : 0;
snd_soc_component_write(component, AIC32X4_LDOCTL, tmp_reg);

tmp_reg = snd_soc_component_read(component, AIC32X4_CMMODE);
if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36)
tmp_reg |= AIC32X4_LDOIN_18_36;
if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED)
tmp_reg |= AIC32X4_LDOIN2HP;
snd_soc_component_write(component, AIC32X4_CMMODE, tmp_reg);

/*
* Enable the fast charging feature and ensure the needed 40ms ellapsed
* before using the analog circuits.
*/
snd_soc_component_write(component, TAS2505_REFPOWERUP,
AIC32X4_REFPOWERUP_40MS);
msleep(40);

return 0;
}

static const struct snd_soc_component_driver soc_component_dev_aic32x4_tas2505 = {
.probe = aic32x4_tas2505_component_probe,
.set_bias_level = aic32x4_set_bias_level,
.controls = aic32x4_tas2505_snd_controls,
.num_controls = ARRAY_SIZE(aic32x4_tas2505_snd_controls),
.dapm_widgets = aic32x4_tas2505_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(aic32x4_tas2505_dapm_widgets),
.dapm_routes = aic32x4_tas2505_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(aic32x4_tas2505_dapm_routes),
.suspend_bias_off = 1,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};

static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
struct device_node *np)
{
Expand Down Expand Up @@ -1250,8 +1376,16 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
if (ret)
goto err_disable_regulators;

ret = devm_snd_soc_register_component(dev,
switch (aic32x4->type) {
case AIC32X4_TYPE_TAS2505:
ret = devm_snd_soc_register_component(dev,
&soc_component_dev_aic32x4_tas2505, &aic32x4_tas2505_dai, 1);
break;
default:
ret = devm_snd_soc_register_component(dev,
&soc_component_dev_aic32x4, &aic32x4_dai, 1);
}

if (ret) {
dev_err(dev, "Failed to register component\n");
goto err_disable_regulators;
Expand Down
5 changes: 5 additions & 0 deletions sound/soc/codecs/tlv320aic32x4.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct regmap_config;
enum aic32x4_type {
AIC32X4_TYPE_AIC32X4 = 0,
AIC32X4_TYPE_AIC32X6,
AIC32X4_TYPE_TAS2505,
};

extern const struct regmap_config aic32x4_regmap_config;
Expand Down Expand Up @@ -93,6 +94,9 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name);
#define AIC32X4_LOLGAIN AIC32X4_REG(1, 18)
#define AIC32X4_LORGAIN AIC32X4_REG(1, 19)
#define AIC32X4_HEADSTART AIC32X4_REG(1, 20)
#define TAS2505_SPK AIC32X4_REG(1, 45)
#define TAS2505_SPKVOL1 AIC32X4_REG(1, 46)
#define TAS2505_SPKVOL2 AIC32X4_REG(1, 48)
#define AIC32X4_MICBIAS AIC32X4_REG(1, 51)
#define AIC32X4_LMICPGAPIN AIC32X4_REG(1, 52)
#define AIC32X4_LMICPGANIN AIC32X4_REG(1, 54)
Expand All @@ -101,6 +105,7 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name);
#define AIC32X4_FLOATINGINPUT AIC32X4_REG(1, 58)
#define AIC32X4_LMICPGAVOL AIC32X4_REG(1, 59)
#define AIC32X4_RMICPGAVOL AIC32X4_REG(1, 60)
#define TAS2505_REFPOWERUP AIC32X4_REG(1, 122)
#define AIC32X4_REFPOWERUP AIC32X4_REG(1, 123)

/* Bits, masks, and shifts */
Expand Down

0 comments on commit b4525b6

Please sign in to comment.