ASoC: atmel-i2s: Fix usage of capture and playback at the same time

[ Upstream commit 3b7961a326f8a7e03f54a19f02fedae8d488b80f ]

For both capture and playback streams to work at the same time, only the
needed values from a register need to be updated. Also, clocks should be
enabled only when the first stream is started and stopped when there is no
running stream.

Fixes: b543e467d1 ("ASoC: atmel-i2s: add driver for the new Atmel I2S controller")
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
Link: https://lore.kernel.org/r/20210618150741.401739-2-codrin.ciubotariu@microchip.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Codrin Ciubotariu 2021-06-18 18:07:41 +03:00 committed by Greg Kroah-Hartman
parent af497961ab
commit e30e636447

View File

@ -200,6 +200,7 @@ struct atmel_i2s_dev {
unsigned int fmt;
const struct atmel_i2s_gck_param *gck_param;
const struct atmel_i2s_caps *caps;
int clk_use_no;
};
static irqreturn_t atmel_i2s_interrupt(int irq, void *dev_id)
@ -321,9 +322,16 @@ static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
{
struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
unsigned int mr = 0;
unsigned int mr = 0, mr_mask;
int ret;
mr_mask = ATMEL_I2SC_MR_FORMAT_MASK | ATMEL_I2SC_MR_MODE_MASK |
ATMEL_I2SC_MR_DATALENGTH_MASK;
if (is_playback)
mr_mask |= ATMEL_I2SC_MR_TXMONO;
else
mr_mask |= ATMEL_I2SC_MR_RXMONO;
switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
mr |= ATMEL_I2SC_MR_FORMAT_I2S;
@ -402,7 +410,7 @@ static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
return regmap_write(dev->regmap, ATMEL_I2SC_MR, mr);
return regmap_update_bits(dev->regmap, ATMEL_I2SC_MR, mr_mask, mr);
}
static int atmel_i2s_switch_mck_generator(struct atmel_i2s_dev *dev,
@ -495,18 +503,28 @@ static int atmel_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
is_master = (mr & ATMEL_I2SC_MR_MODE_MASK) == ATMEL_I2SC_MR_MODE_MASTER;
/* If master starts, enable the audio clock. */
if (is_master && mck_enabled)
err = atmel_i2s_switch_mck_generator(dev, true);
if (err)
return err;
if (is_master && mck_enabled) {
if (!dev->clk_use_no) {
err = atmel_i2s_switch_mck_generator(dev, true);
if (err)
return err;
}
dev->clk_use_no++;
}
err = regmap_write(dev->regmap, ATMEL_I2SC_CR, cr);
if (err)
return err;
/* If master stops, disable the audio clock. */
if (is_master && !mck_enabled)
err = atmel_i2s_switch_mck_generator(dev, false);
if (is_master && !mck_enabled) {
if (dev->clk_use_no == 1) {
err = atmel_i2s_switch_mck_generator(dev, false);
if (err)
return err;
}
dev->clk_use_no--;
}
return err;
}