Skip to content

Commit

Permalink
ASoC: pcm512x: Implement the digital_mute interface
Browse files Browse the repository at this point in the history
Clicks and pops of various volumes can be produced while the device is
opened, closed, put into and taken out of standby, or reconfigured.
Fix this, by implementing the digital_mute interface, so that the
output is muted during such operations.

Signed-off-by: Dimitris Papavasiliou <dpapavas@gmail.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
  • Loading branch information
Dimitris Papavasiliou authored and broonie committed Dec 13, 2018
1 parent 165be3c commit 3500f1c
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 2 deletions.
121 changes: 119 additions & 2 deletions sound/soc/codecs/pcm512x.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ struct pcm512x_priv {
unsigned long overclock_pll;
unsigned long overclock_dac;
unsigned long overclock_dsp;
int mute;
struct mutex mutex;
};

/*
Expand Down Expand Up @@ -384,15 +386,77 @@ static const struct soc_enum pcm512x_veds =
SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4,
pcm512x_ramp_step_text);

static int pcm512x_update_mute(struct pcm512x_priv *pcm512x)
{
return regmap_update_bits(
pcm512x->regmap, PCM512x_MUTE, PCM512x_RQML | PCM512x_RQMR,
(!!(pcm512x->mute & 0x5) << PCM512x_RQML_SHIFT)
| (!!(pcm512x->mute & 0x3) << PCM512x_RQMR_SHIFT));
}

static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);

mutex_lock(&pcm512x->mutex);
ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4);
ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2);
mutex_unlock(&pcm512x->mutex);

return 0;
}

static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
int ret, changed = 0;

mutex_lock(&pcm512x->mutex);

if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) {
pcm512x->mute ^= 0x4;
changed = 1;
}
if ((pcm512x->mute & 0x2) == (ucontrol->value.integer.value[1] << 1)) {
pcm512x->mute ^= 0x2;
changed = 1;
}

if (changed) {
ret = pcm512x_update_mute(pcm512x);
if (ret != 0) {
dev_err(component->dev,
"Failed to update digital mute: %d\n", ret);
mutex_unlock(&pcm512x->mutex);
return ret;
}
}

mutex_unlock(&pcm512x->mutex);

return changed;
}

static const struct snd_kcontrol_new pcm512x_controls[] = {
SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2,
PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL,
PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv),
SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST,
PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv),
SOC_DOUBLE("Digital Playback Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
PCM512x_RQMR_SHIFT, 1, 1),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Playback Switch",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_ctl_boolean_stereo_info,
.get = pcm512x_digital_playback_switch_get,
.put = pcm512x_digital_playback_switch_put
},

SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1),
SOC_ENUM("DSP Program", pcm512x_dsp_program),
Expand Down Expand Up @@ -1319,10 +1383,61 @@ static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}

static int pcm512x_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_component *component = dai->component;
struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
int ret;
unsigned int mute_det;

mutex_lock(&pcm512x->mutex);

if (mute) {
pcm512x->mute |= 0x1;
ret = regmap_update_bits(pcm512x->regmap, PCM512x_MUTE,
PCM512x_RQML | PCM512x_RQMR,
PCM512x_RQML | PCM512x_RQMR);
if (ret != 0) {
dev_err(component->dev,
"Failed to set digital mute: %d\n", ret);
mutex_unlock(&pcm512x->mutex);
return ret;
}

regmap_read_poll_timeout(pcm512x->regmap,
PCM512x_ANALOG_MUTE_DET,
mute_det, (mute_det & 0x3) == 0,
200, 10000);

mutex_unlock(&pcm512x->mutex);
} else {
pcm512x->mute &= ~0x1;
ret = pcm512x_update_mute(pcm512x);
if (ret != 0) {
dev_err(component->dev,
"Failed to update digital mute: %d\n", ret);
mutex_unlock(&pcm512x->mutex);
return ret;
}

regmap_read_poll_timeout(pcm512x->regmap,
PCM512x_ANALOG_MUTE_DET,
mute_det,
(mute_det & 0x3)
== ((~pcm512x->mute >> 1) & 0x3),
200, 10000);
}

mutex_unlock(&pcm512x->mutex);

return 0;
}

static const struct snd_soc_dai_ops pcm512x_dai_ops = {
.startup = pcm512x_dai_startup,
.hw_params = pcm512x_hw_params,
.set_fmt = pcm512x_set_fmt,
.digital_mute = pcm512x_digital_mute,
};

static struct snd_soc_dai_driver pcm512x_dai = {
Expand Down Expand Up @@ -1388,6 +1503,8 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
if (!pcm512x)
return -ENOMEM;

mutex_init(&pcm512x->mutex);

dev_set_drvdata(dev, pcm512x);
pcm512x->regmap = regmap;

Expand Down
2 changes: 2 additions & 0 deletions sound/soc/codecs/pcm512x.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@
#define PCM512x_RQST_SHIFT 4

/* Page 0, Register 3 - mute */
#define PCM512x_RQMR (1 << 0)
#define PCM512x_RQMR_SHIFT 0
#define PCM512x_RQML (1 << 4)
#define PCM512x_RQML_SHIFT 4

/* Page 0, Register 4 - PLL */
Expand Down

0 comments on commit 3500f1c

Please sign in to comment.