Discussion:
[PATCH 0/2] ASoC: davinci-mcasp: Improved suspend/resume support
Peter Ujfalusi
2014-10-01 13:02:10 UTC
Permalink
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(-)
--
2.1.0
Peter Ujfalusi
2014-10-01 13:02:11 UTC
Permalink
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 <***@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;
}
--
2.1.0
Peter Ujfalusi
2014-10-01 13:02:12 UTC
Permalink
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 <***@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;
--
2.1.0
Mark Brown
2014-10-01 16:02:40 UTC
Permalink
Post by Peter Ujfalusi
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?
Peter Ujfalusi
2014-10-02 07:21:31 UTC
Permalink
Post by Mark Brown
Post by Peter Ujfalusi
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.
--
Péter
Mark Brown
2014-10-02 10:56:52 UTC
Permalink
Post by Peter Ujfalusi
Post by Mark Brown
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.

Loading...