| From 0cea3133bf28b3f9ba79eea3d4ddc1cb225588fe Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 18 Jun 2021 18:07:41 +0300 |
| Subject: ASoC: atmel-i2s: Fix usage of capture and playback at the same time |
| |
| From: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> |
| |
| [ 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@microchip.com |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| 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 bbe2b638abb5..d870f56c44cf 100644 |
| --- a/sound/soc/atmel/atmel-i2s.c |
| +++ b/sound/soc/atmel/atmel-i2s.c |
| @@ -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; |
| } |
| -- |
| 2.30.2 |
| |