From: Codrin Ciubotariu codrin.ciubotariu@microchip.com
stable inclusion from linux-4.19.198 commit e462d5387ef2f542d8d2e7a55c169f98d28fd302
--------------------------------
[ 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: b543e467d1a9 ("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@microchi... Signed-off-by: Mark Brown broonie@kernel.org Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- sound/soc/atmel/atmel-i2s.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-)
diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c index d88c1d995036c..99cc731505769 100644 --- a/sound/soc/atmel/atmel-i2s.c +++ b/sound/soc/atmel/atmel-i2s.c @@ -211,6 +211,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) @@ -332,9 +333,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; @@ -413,7 +421,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, @@ -506,18 +514,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; }