linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] ASoC: AMD: Fix simultaneous playback and capture on different channel
@ 2018-09-10  8:06 Akshu Agrawal
  2018-09-10  8:06 ` [PATCH 2/2] ASoC: AMD: Ensure reset bit is cleared before configuring Akshu Agrawal
  2018-09-10 11:38 ` [PATCH 1/2] ASoC: AMD: Fix simultaneous playback and capture on different channel Mark Brown
  0 siblings, 2 replies; 5+ messages in thread
From: Akshu Agrawal @ 2018-09-10  8:06 UTC (permalink / raw)
  Cc: djkurtz, akshu.agrawal, Alexander.Deucher, Liam Girdwood,
	Mark Brown, Jaroslav Kysela, Takashi Iwai, Mukunda, Vijendar,
	Kuninori Morimoto, Wei Yongjun,
	moderated list:SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEM...,
	open list

If capture and playback are started on different channel (I2S/BT)
there is a possibilty that channel information passed from machine driver
is overwritten before the configuration is done in dma driver.
Example:
113.597588: cz_max_startup: ---playback sets BT channel
113.597694: cz_dmic1_startup: ---capture sets I2S channel
113.597979: acp_dma_hw_params: ---configures capture for I2S channel
113.598114: acp_dma_hw_params: ---configures playback for I2S channel

This is fixed by having lock between startup and prepare. This ensures
no other codec startup gets called between a codec's startup(where channel
info is set) and hw_params(where channel info is read).

Signed-off-by: Akshu Agrawal <akshu.agrawal@amd.com>
---
 sound/soc/amd/acp-da7219-max98357a.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
index 3879ccc..b98ffbc 100644
--- a/sound/soc/amd/acp-da7219-max98357a.c
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -47,6 +47,7 @@
 
 static struct snd_soc_jack cz_jack;
 static struct clk *da7219_dai_clk;
+static struct mutex instance_lock;
 extern int bt_uart_enable;
 
 static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
@@ -150,6 +151,7 @@ static int cz_da7219_startup(struct snd_pcm_substream *substream)
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 				   &constraints_rates);
 
+	mutex_lock(&instance_lock);
 	machine->i2s_instance = I2S_SP_INSTANCE;
 	machine->capture_channel = CAP_CHANNEL1;
 	return da7219_clk_enable(substream);
@@ -160,6 +162,12 @@ static void cz_da7219_shutdown(struct snd_pcm_substream *substream)
 	da7219_clk_disable();
 }
 
+static int cz_da7219_prepare(struct snd_pcm_substream *substream)
+{
+	mutex_unlock(&instance_lock);
+	return 0;
+}
+
 static int cz_max_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
@@ -177,6 +185,7 @@ static int cz_max_startup(struct snd_pcm_substream *substream)
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 				   &constraints_rates);
 
+	mutex_lock(&instance_lock);
 	machine->i2s_instance = I2S_BT_INSTANCE;
 	return da7219_clk_enable(substream);
 }
@@ -186,6 +195,12 @@ static void cz_max_shutdown(struct snd_pcm_substream *substream)
 	da7219_clk_disable();
 }
 
+static int cz_max_prepare(struct snd_pcm_substream *substream)
+{
+	mutex_unlock(&instance_lock);
+	return 0;
+}
+
 static int cz_dmic0_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
@@ -203,6 +218,7 @@ static int cz_dmic0_startup(struct snd_pcm_substream *substream)
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 				   &constraints_rates);
 
+	mutex_lock(&instance_lock);
 	machine->i2s_instance = I2S_BT_INSTANCE;
 	return da7219_clk_enable(substream);
 }
@@ -224,6 +240,7 @@ static int cz_dmic1_startup(struct snd_pcm_substream *substream)
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 				   &constraints_rates);
 
+	mutex_lock(&instance_lock);
 	machine->i2s_instance = I2S_SP_INSTANCE;
 	machine->capture_channel = CAP_CHANNEL0;
 	return da7219_clk_enable(substream);
@@ -234,24 +251,34 @@ static void cz_dmic_shutdown(struct snd_pcm_substream *substream)
 	da7219_clk_disable();
 }
 
+static int cz_dmic_prepare(struct snd_pcm_substream *substream)
+{
+	mutex_unlock(&instance_lock);
+	return 0;
+}
+
 static const struct snd_soc_ops cz_da7219_cap_ops = {
 	.startup = cz_da7219_startup,
 	.shutdown = cz_da7219_shutdown,
+	.prepare = cz_da7219_prepare,
 };
 
 static const struct snd_soc_ops cz_max_play_ops = {
 	.startup = cz_max_startup,
 	.shutdown = cz_max_shutdown,
+	.prepare = cz_max_prepare,
 };
 
 static const struct snd_soc_ops cz_dmic0_cap_ops = {
 	.startup = cz_dmic0_startup,
 	.shutdown = cz_dmic_shutdown,
+	.prepare = cz_dmic_prepare,
 };
 
 static const struct snd_soc_ops cz_dmic1_cap_ops = {
 	.startup = cz_dmic1_startup,
 	.shutdown = cz_dmic_shutdown,
+	.prepare = cz_dmic_prepare,
 };
 
 static struct snd_soc_dai_link cz_dai_7219_98357[] = {
@@ -409,6 +436,7 @@ static int cz_probe(struct platform_device *pdev)
 	card = &cz_card;
 	cz_card.dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
+	mutex_init(&instance_lock);
 	snd_soc_card_set_drvdata(card, machine);
 	ret = devm_snd_soc_register_card(&pdev->dev, &cz_card);
 	if (ret) {
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] ASoC: AMD: Ensure reset bit is cleared before configuring
  2018-09-10  8:06 [PATCH 1/2] ASoC: AMD: Fix simultaneous playback and capture on different channel Akshu Agrawal
@ 2018-09-10  8:06 ` Akshu Agrawal
  2018-09-10 11:35   ` Applied "ASoC: AMD: Ensure reset bit is cleared before configuring" to the asoc tree Mark Brown
  2018-09-10 11:38 ` [PATCH 1/2] ASoC: AMD: Fix simultaneous playback and capture on different channel Mark Brown
  1 sibling, 1 reply; 5+ messages in thread
From: Akshu Agrawal @ 2018-09-10  8:06 UTC (permalink / raw)
  Cc: djkurtz, akshu.agrawal, Alexander.Deucher, Liam Girdwood,
	Mark Brown, Jaroslav Kysela, Takashi Iwai, Mukunda, Vijendar,
	Alex Deucher,
	moderated list:SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEM...,
	open list

HW register descriptions says:
"DMA Channel Reset...Software must confirm that this bit is
cleared before reprogramming any of the channel configuration registers."
There could be cases where dma stop errored out leaving dma channel
in reset state. We need to ensure that before the start of another dma,
channel is out of the reset state.

Signed-off-by: Akshu Agrawal <akshu.agrawal@amd.com>
---
 sound/soc/amd/acp-pcm-dma.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c
index e359938..77b265b 100644
--- a/sound/soc/amd/acp-pcm-dma.c
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -16,6 +16,7 @@
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/sizes.h>
 #include <linux/pm_runtime.h>
 
@@ -184,6 +185,24 @@ static void config_dma_descriptor_in_sram(void __iomem *acp_mmio,
 	acp_reg_write(descr_info->xfer_val, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
 }
 
+static void pre_config_reset(void __iomem *acp_mmio, u16 ch_num)
+{
+	u32 dma_ctrl;
+	int ret;
+
+	/* clear the reset bit */
+	dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+	dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRst_MASK;
+	acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+	/* check the reset bit before programming configuration registers */
+	ret = readl_poll_timeout(acp_mmio + ((mmACP_DMA_CNTL_0 + ch_num) * 4),
+				 dma_ctrl,
+				 !(dma_ctrl & ACP_DMA_CNTL_0__DMAChRst_MASK),
+				 100, ACP_DMA_RESET_TIME);
+	if (ret < 0)
+		pr_err("Failed to clear reset of channel : %d\n", ch_num);
+}
+
 /*
  * Initialize the DMA descriptor information for transfer between
  * system memory <-> ACP SRAM
@@ -236,6 +255,7 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
 		config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
 					      &dmadscr[i]);
 	}
+	pre_config_reset(acp_mmio, ch);
 	config_acp_dma_channel(acp_mmio, ch,
 			       dma_dscr_idx - 1,
 			       NUM_DSCRS_PER_CHANNEL,
@@ -275,6 +295,7 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, u32 size,
 		config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
 					      &dmadscr[i]);
 	}
+	pre_config_reset(acp_mmio, ch);
 	/* Configure the DMA channel with the above descriptore */
 	config_acp_dma_channel(acp_mmio, ch, dma_dscr_idx - 1,
 			       NUM_DSCRS_PER_CHANNEL,
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Applied "ASoC: AMD: Ensure reset bit is cleared before configuring" to the asoc tree
  2018-09-10  8:06 ` [PATCH 2/2] ASoC: AMD: Ensure reset bit is cleared before configuring Akshu Agrawal
@ 2018-09-10 11:35   ` Mark Brown
  0 siblings, 0 replies; 5+ messages in thread
From: Mark Brown @ 2018-09-10 11:35 UTC (permalink / raw)
  To: Akshu Agrawal
  Cc: Mark Brown,
	moderated list:SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEM...,
	open list, Takashi Iwai, Liam Girdwood, djkurtz, Mark Brown,
	Mukunda,,
	Vijendar, Alex Deucher, akshu.agrawal, alsa-devel

The patch

   ASoC: AMD: Ensure reset bit is cleared before configuring

has been applied to the asoc tree at

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

From 2a665dba016d5493c7d826fec82b0cb643b30d42 Mon Sep 17 00:00:00 2001
From: Akshu Agrawal <akshu.agrawal@amd.com>
Date: Mon, 10 Sep 2018 13:36:30 +0530
Subject: [PATCH] ASoC: AMD: Ensure reset bit is cleared before configuring

HW register descriptions says:
"DMA Channel Reset...Software must confirm that this bit is
cleared before reprogramming any of the channel configuration registers."
There could be cases where dma stop errored out leaving dma channel
in reset state. We need to ensure that before the start of another dma,
channel is out of the reset state.

Signed-off-by: Akshu Agrawal <akshu.agrawal@amd.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 sound/soc/amd/acp-pcm-dma.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c
index e359938e3d7e..77b265bd0505 100644
--- a/sound/soc/amd/acp-pcm-dma.c
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -16,6 +16,7 @@
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/sizes.h>
 #include <linux/pm_runtime.h>
 
@@ -184,6 +185,24 @@ static void config_dma_descriptor_in_sram(void __iomem *acp_mmio,
 	acp_reg_write(descr_info->xfer_val, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
 }
 
+static void pre_config_reset(void __iomem *acp_mmio, u16 ch_num)
+{
+	u32 dma_ctrl;
+	int ret;
+
+	/* clear the reset bit */
+	dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+	dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRst_MASK;
+	acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+	/* check the reset bit before programming configuration registers */
+	ret = readl_poll_timeout(acp_mmio + ((mmACP_DMA_CNTL_0 + ch_num) * 4),
+				 dma_ctrl,
+				 !(dma_ctrl & ACP_DMA_CNTL_0__DMAChRst_MASK),
+				 100, ACP_DMA_RESET_TIME);
+	if (ret < 0)
+		pr_err("Failed to clear reset of channel : %d\n", ch_num);
+}
+
 /*
  * Initialize the DMA descriptor information for transfer between
  * system memory <-> ACP SRAM
@@ -236,6 +255,7 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
 		config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
 					      &dmadscr[i]);
 	}
+	pre_config_reset(acp_mmio, ch);
 	config_acp_dma_channel(acp_mmio, ch,
 			       dma_dscr_idx - 1,
 			       NUM_DSCRS_PER_CHANNEL,
@@ -275,6 +295,7 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, u32 size,
 		config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
 					      &dmadscr[i]);
 	}
+	pre_config_reset(acp_mmio, ch);
 	/* Configure the DMA channel with the above descriptore */
 	config_acp_dma_channel(acp_mmio, ch, dma_dscr_idx - 1,
 			       NUM_DSCRS_PER_CHANNEL,
-- 
2.19.0.rc1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] ASoC: AMD: Fix simultaneous playback and capture on different channel
  2018-09-10  8:06 [PATCH 1/2] ASoC: AMD: Fix simultaneous playback and capture on different channel Akshu Agrawal
  2018-09-10  8:06 ` [PATCH 2/2] ASoC: AMD: Ensure reset bit is cleared before configuring Akshu Agrawal
@ 2018-09-10 11:38 ` Mark Brown
  2018-09-10 17:21   ` Agrawal, Akshu
  1 sibling, 1 reply; 5+ messages in thread
From: Mark Brown @ 2018-09-10 11:38 UTC (permalink / raw)
  To: Akshu Agrawal
  Cc: djkurtz, Alexander.Deucher, Liam Girdwood, Jaroslav Kysela,
	Takashi Iwai, Mukunda, Vijendar, Kuninori Morimoto, Wei Yongjun,
	moderated list:SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEM...,
	open list

[-- Attachment #1: Type: text/plain, Size: 1126 bytes --]

On Mon, Sep 10, 2018 at 01:36:29PM +0530, Akshu Agrawal wrote:
> If capture and playback are started on different channel (I2S/BT)
> there is a possibilty that channel information passed from machine driver
> is overwritten before the configuration is done in dma driver.
> Example:
> 113.597588: cz_max_startup: ---playback sets BT channel
> 113.597694: cz_dmic1_startup: ---capture sets I2S channel
> 113.597979: acp_dma_hw_params: ---configures capture for I2S channel
> 113.598114: acp_dma_hw_params: ---configures playback for I2S channel
> 
> This is fixed by having lock between startup and prepare. This ensures
> no other codec startup gets called between a codec's startup(where channel
> info is set) and hw_params(where channel info is read).

This isn't viable - the driver will deadlock if the application hits an
error and never gets to startup, or if the application tries to
simultaneously configure two channels (ie, do all the prepares and then
all the parameter configuration and then startup).  The DMA driver needs
to remember the configurations for the different channels separately.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] ASoC: AMD: Fix simultaneous playback and capture on different channel
  2018-09-10 11:38 ` [PATCH 1/2] ASoC: AMD: Fix simultaneous playback and capture on different channel Mark Brown
@ 2018-09-10 17:21   ` Agrawal, Akshu
  0 siblings, 0 replies; 5+ messages in thread
From: Agrawal, Akshu @ 2018-09-10 17:21 UTC (permalink / raw)
  To: Mark Brown
  Cc: djkurtz, Alexander.Deucher, Liam Girdwood, Jaroslav Kysela,
	Takashi Iwai, Mukunda, Vijendar, Kuninori Morimoto, Wei Yongjun,
	moderated list:SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEM...,
	open list



On 9/10/2018 5:08 PM, Mark Brown wrote:
> On Mon, Sep 10, 2018 at 01:36:29PM +0530, Akshu Agrawal wrote:
>> If capture and playback are started on different channel (I2S/BT)
>> there is a possibilty that channel information passed from machine driver
>> is overwritten before the configuration is done in dma driver.
>> Example:
>> 113.597588: cz_max_startup: ---playback sets BT channel
>> 113.597694: cz_dmic1_startup: ---capture sets I2S channel
>> 113.597979: acp_dma_hw_params: ---configures capture for I2S channel
>> 113.598114: acp_dma_hw_params: ---configures playback for I2S channel
>>
>> This is fixed by having lock between startup and prepare. This ensures
>> no other codec startup gets called between a codec's startup(where channel
>> info is set) and hw_params(where channel info is read).
> 
> This isn't viable - the driver will deadlock if the application hits an
> error and never gets to startup, or if the application tries to
> simultaneously configure two channels (ie, do all the prepares and then
> all the parameter configuration and then startup).

We can avoid deadlock by having another mutex_unlock in the shutdown
call of each of codec's ops. Wouldn't in all possible termination
scenarios, it will cleanup and exit via shutdown callback?

Having said that I think there is a better approach to this, is by
having 2 separate instance variable for playback and capture for passing
instance info from machine driver to dma driver. Respective codec in
machine driver will set the capture/playback instance. dma driver on the
basis of substream->stream can read the correct one. No fear of deadlock
in this.

Thanks,
Akshu

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2018-09-10 17:22 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-10  8:06 [PATCH 1/2] ASoC: AMD: Fix simultaneous playback and capture on different channel Akshu Agrawal
2018-09-10  8:06 ` [PATCH 2/2] ASoC: AMD: Ensure reset bit is cleared before configuring Akshu Agrawal
2018-09-10 11:35   ` Applied "ASoC: AMD: Ensure reset bit is cleared before configuring" to the asoc tree Mark Brown
2018-09-10 11:38 ` [PATCH 1/2] ASoC: AMD: Fix simultaneous playback and capture on different channel Mark Brown
2018-09-10 17:21   ` Agrawal, Akshu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).