[alsa-devel] [PATCH 0/2] ASoC: davinci-mcasp: Improved suspend/resume support
Hi,
With this series the McASP will continue to work after deep sleep during audio activity. Without these changes the ongoing audio will not resume and it will time out, but streams started after resume were worked correctly.
Regards, Peter --- Peter Ujfalusi (2): ASoC: davinci-mcasp: Convert the context save/restore to use array ASoC: davinvi-mcasp: Proper suspend/resume support while audio is active
sound/soc/davinci/davinci-mcasp.c | 79 ++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 21 deletions(-)
Instead of individual values use an array to store the registers need to be saved on suspend and restored on resume. It is going to be easier to add more registers to save and restore.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/davinci/davinci-mcasp.c | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-)
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 68347b55f6e1..ccf2f3920a20 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -42,14 +42,18 @@
#define MCASP_MAX_AFIFO_DEPTH 64
+static u32 context_regs[] = { + DAVINCI_MCASP_TXFMCTL_REG, + DAVINCI_MCASP_RXFMCTL_REG, + DAVINCI_MCASP_TXFMT_REG, + DAVINCI_MCASP_RXFMT_REG, + DAVINCI_MCASP_ACLKXCTL_REG, + DAVINCI_MCASP_ACLKRCTL_REG, + DAVINCI_MCASP_PDIR_REG, +}; + struct davinci_mcasp_context { - u32 txfmtctl; - u32 rxfmtctl; - u32 txfmt; - u32 rxfmt; - u32 aclkxctl; - u32 aclkrctl; - u32 pdir; + u32 config_regs[ARRAY_SIZE(context_regs)]; };
struct davinci_mcasp { @@ -874,14 +878,10 @@ static int davinci_mcasp_suspend(struct snd_soc_dai *dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); struct davinci_mcasp_context *context = &mcasp->context; + int i;
- context->txfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG); - context->rxfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); - context->txfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMT_REG); - context->rxfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMT_REG); - context->aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG); - context->aclkrctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG); - context->pdir = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG); + for (i = 0; i < ARRAY_SIZE(context_regs); i++) + context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]);
return 0; } @@ -890,14 +890,10 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); struct davinci_mcasp_context *context = &mcasp->context; + int i;
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, context->txfmtctl); - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG, context->rxfmtctl); - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMT_REG, context->txfmt); - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMT_REG, context->rxfmt); - mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, context->aclkxctl); - mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, context->aclkrctl); - mcasp_set_reg(mcasp, DAVINCI_MCASP_PDIR_REG, context->pdir); + for (i = 0; i < ARRAY_SIZE(context_regs); i++) + mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]);
return 0; }
If the board is sent to suspend (deep sleep) the McASP context will be lost. In case when suspend happens during active audio we need to save and restore more registers, which was configured during hw_param times as well. We need to add more config registers, AFIFO control registers and we also need to save and restore the serializer configuration as well. Since the number of serializers depends on the SoC we need to allocate the memory for it based on the num_serializer for the given McASP instance.
With this patch the ongoing stream will resume after resuming from deep sleep.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/davinci/davinci-mcasp.c | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+)
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index ccf2f3920a20..0eed9b1b24e1 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -49,11 +49,19 @@ static u32 context_regs[] = { DAVINCI_MCASP_RXFMT_REG, DAVINCI_MCASP_ACLKXCTL_REG, DAVINCI_MCASP_ACLKRCTL_REG, + DAVINCI_MCASP_AHCLKXCTL_REG, + DAVINCI_MCASP_AHCLKRCTL_REG, DAVINCI_MCASP_PDIR_REG, + DAVINCI_MCASP_RXMASK_REG, + DAVINCI_MCASP_TXMASK_REG, + DAVINCI_MCASP_RXTDM_REG, + DAVINCI_MCASP_TXTDM_REG, };
struct davinci_mcasp_context { u32 config_regs[ARRAY_SIZE(context_regs)]; + u32 afifo_regs[2]; /* for read/write fifo control registers */ + u32 *xrsr_regs; /* for serializer configuration */ };
struct davinci_mcasp { @@ -878,11 +886,25 @@ static int davinci_mcasp_suspend(struct snd_soc_dai *dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); struct davinci_mcasp_context *context = &mcasp->context; + u32 reg; int i;
for (i = 0; i < ARRAY_SIZE(context_regs); i++) context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]);
+ if (mcasp->txnumevt) { + reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + context->afifo_regs[0] = mcasp_get_reg(mcasp, reg); + } + if (mcasp->rxnumevt) { + reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + context->afifo_regs[1] = mcasp_get_reg(mcasp, reg); + } + + for (i = 0; i < mcasp->num_serializer; i++) + context->xrsr_regs[i] = mcasp_get_reg(mcasp, + DAVINCI_MCASP_XRSRCTL_REG(i)); + return 0; }
@@ -890,11 +912,25 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); struct davinci_mcasp_context *context = &mcasp->context; + u32 reg; int i;
for (i = 0; i < ARRAY_SIZE(context_regs); i++) mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]);
+ if (mcasp->txnumevt) { + reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + mcasp_set_reg(mcasp, reg, context->afifo_regs[0]); + } + if (mcasp->rxnumevt) { + reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + mcasp_set_reg(mcasp, reg, context->afifo_regs[1]); + } + + for (i = 0; i < mcasp->num_serializer; i++) + mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + context->xrsr_regs[i]); + return 0; } #else @@ -1212,6 +1248,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->op_mode = pdata->op_mode; mcasp->tdm_slots = pdata->tdm_slots; mcasp->num_serializer = pdata->num_serializer; +#ifdef CONFIG_PM_SLEEP + mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev, + sizeof(u32) * mcasp->num_serializer, + GFP_KERNEL); +#endif mcasp->serial_dir = pdata->serial_dir; mcasp->version = pdata->version; mcasp->txnumevt = pdata->txnumevt;
On Wed, Oct 01, 2014 at 04:02:10PM +0300, Peter Ujfalusi wrote:
Hi,
With this series the McASP will continue to work after deep sleep during audio activity. Without these changes the ongoing audio will not resume and it will time out, but streams started after resume were worked correctly.
Applied both, thanks. Might be worth converting to regmap and using the cache code?
On 10/01/2014 07:02 PM, Mark Brown wrote:
On Wed, Oct 01, 2014 at 04:02:10PM +0300, Peter Ujfalusi wrote:
Hi,
With this series the McASP will continue to work after deep sleep during audio activity. Without these changes the ongoing audio will not resume and it will time out, but streams started after resume were worked correctly.
Applied both, thanks. Might be worth converting to regmap and using the cache code?
Yes, it is in my to-do list. One thing I'm a bit puzzled currently is on how to handle the serializer registers. Depending on the SoC the McASP is integrated in we can have different number of serializers, also in some SoC different McASP ports have different number of serializers. I do not want to have separate regmap struct for all of these (which would most probably means different compatible flag in DT). I also want to avoid wiring in some max limit of the serializer registers. But probably I need to do this at the end.
On Thu, Oct 02, 2014 at 10:21:31AM +0300, Peter Ujfalusi wrote:
On 10/01/2014 07:02 PM, Mark Brown wrote:
Applied both, thanks. Might be worth converting to regmap and using the cache code?
Yes, it is in my to-do list. One thing I'm a bit puzzled currently is on how to handle the serializer registers. Depending on the SoC the McASP is integrated in we can have different number of serializers, also in some SoC different McASP ports have different number of serializers. I do not want to have separate regmap struct for all of these (which would most probably means different compatible flag in DT). I also want to avoid wiring in some max limit of the serializer registers. But probably I need to do this at the end.
This sort of thing is why the access stuff is done with functions and get the device passed in, though max_register is still just a number. I don't know if that'll particularly help or not.
participants (2)
-
Mark Brown
-
Peter Ujfalusi