Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 98c5fb1f authored by Peter Ujfalusi's avatar Peter Ujfalusi Committed by Mark Brown
Browse files

ASoC: twl6040: Add digital mute support



To reduce pop noise during playback stream start and stop the codec needs
to have the digital_mute callback implemented.
The codec need to be muted before the CPU dai has been stopped (McPDM).
Stopping the McPDM will generate a pop on the codec since no signal on the
PDM bus means full negative amplitude.
By managing the mute/unmute state of the outputs we can decrease the amount
of pop noise when playback starts or stops.

Signed-off-by: default avatarPeter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: default avatarMark Brown <broonie@linaro.org>
parent 68897497
Loading
Loading
Loading
Loading
+90 −1
Original line number Diff line number Diff line
@@ -75,6 +75,8 @@ struct twl6040_data {
	int pll_power_mode;
	int hs_power_mode;
	int hs_power_mode_locked;
	bool dl1_unmuted;
	bool dl2_unmuted;
	unsigned int clk_in;
	unsigned int sysclk;
	struct twl6040_jack_data hs_jack;
@@ -228,6 +230,25 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
	return value;
}

static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec,
				    unsigned int reg)
{
	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);

	switch (reg) {
	case TWL6040_REG_HSLCTL:
	case TWL6040_REG_HSRCTL:
	case TWL6040_REG_EARCTL:
		/* DL1 path */
		return priv->dl1_unmuted;
	case TWL6040_REG_HFLCTL:
	case TWL6040_REG_HFRCTL:
		return priv->dl2_unmuted;
	default:
		return 1;
	};
}

/*
 * write to the twl6040 register space
 */
@@ -240,7 +261,8 @@ static int twl6040_write(struct snd_soc_codec *codec,
		return -EIO;

	twl6040_write_reg_cache(codec, reg, value);
	if (likely(reg < TWL6040_REG_SW_SHADOW))
	if (likely(reg < TWL6040_REG_SW_SHADOW) &&
	    twl6040_is_path_unmuted(codec, reg))
		return twl6040_reg_write(twl6040, reg, value);
	else
		return 0;
@@ -1034,11 +1056,78 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
	return 0;
}

static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id id,
			     int mute)
{
	struct twl6040 *twl6040 = codec->control_data;
	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
	int hslctl, hsrctl, earctl;
	int hflctl, hfrctl;

	switch (id) {
	case TWL6040_DAI_DL1:
		hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL);
		hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL);
		earctl = twl6040_read_reg_cache(codec, TWL6040_REG_EARCTL);

		if (mute) {
			/* Power down drivers and DACs */
			earctl &= ~0x01;
			hslctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
			hsrctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);

		}

		twl6040_reg_write(twl6040, TWL6040_REG_EARCTL, earctl);
		twl6040_reg_write(twl6040, TWL6040_REG_HSLCTL, hslctl);
		twl6040_reg_write(twl6040, TWL6040_REG_HSRCTL, hsrctl);
		priv->dl1_unmuted = !mute;
		break;
	case TWL6040_DAI_DL2:
		hflctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFLCTL);
		hfrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFRCTL);

		if (mute) {
			/* Power down drivers and DACs */
			hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
				    TWL6040_HFDRVENA);
			hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
				    TWL6040_HFDRVENA);
		}

		twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl);
		twl6040_reg_write(twl6040, TWL6040_REG_HFRCTL, hfrctl);
		priv->dl2_unmuted = !mute;
		break;
	default:
		break;
	};
}

static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute)
{
	switch (dai->id) {
	case TWL6040_DAI_LEGACY:
		twl6040_mute_path(dai->codec, TWL6040_DAI_DL1, mute);
		twl6040_mute_path(dai->codec, TWL6040_DAI_DL2, mute);
		break;
	case TWL6040_DAI_DL1:
	case TWL6040_DAI_DL2:
		twl6040_mute_path(dai->codec, dai->id, mute);
		break;
	default:
		break;
	}

	return 0;
}

static const struct snd_soc_dai_ops twl6040_dai_ops = {
	.startup	= twl6040_startup,
	.hw_params	= twl6040_hw_params,
	.prepare	= twl6040_prepare,
	.set_sysclk	= twl6040_set_dai_sysclk,
	.digital_mute	= twl6040_digital_mute,
};

static struct snd_soc_dai_driver twl6040_dai[] = {