From: Maruthi Srinivas Bayyavarapu Maruthi.Bayyavarapu@amd.com
ACP SRAM banks gets turned on when ACP is powered on. Not all banks are used for playback/capture. So, power on required banks during audio device open and power off during audio device close.
Signed-off-by: Maruthi Bayyavarapu maruthi.bayyavarapu@amd.com Reviewed-by: Alex Deucher alexander.deucher@amd.com Signed-off-by: Alex Deucher alexander.deucher@amd.com --- sound/soc/amd/acp-pcm-dma.c | 25 +++++++++++--- sound/soc/amd/acp.c | 81 ++++++++++++++++++++++++++++++++++++++++++++- sound/soc/amd/acp.h | 3 +- 3 files changed, 103 insertions(+), 6 deletions(-)
diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 07a26e5..daba64a 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -133,6 +133,7 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
static int acp_dma_open(struct snd_pcm_substream *substream) { + u32 bank; int ret = 0; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *prtd = substream->private_data; @@ -166,10 +167,17 @@ static int acp_dma_open(struct snd_pcm_substream *substream) if (!intr_data->play_stream && !intr_data->capture_stream) acp_enable_external_interrupts(adata->acp_mmio, 1);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { intr_data->play_stream = substream; - else + for (bank = 1; bank <= 4; bank++) + acp_turnonoff_lower_sram_bank(intr_data->acp_mmio, bank, + true); + } else { intr_data->capture_stream = substream; + for (bank = 5; bank <= 8; bank++) + acp_turnonoff_lower_sram_bank(intr_data->acp_mmio, bank, + true); + }
return 0; } @@ -201,6 +209,7 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, pg = virt_to_page(substream->dma_buffer.area);
if (pg != NULL) { + acp_turnonoff_lower_sram_bank(rtd->acp_mmio, 0, true); /* Save for runtime private data */ rtd->pg = pg; rtd->order = get_order(size); @@ -364,6 +373,7 @@ static int acp_dma_new(struct snd_soc_pcm_runtime *rtd)
static int acp_dma_close(struct snd_pcm_substream *substream) { + u32 bank; struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; struct snd_soc_pcm_runtime *prtd = substream->private_data; @@ -371,10 +381,17 @@ static int acp_dma_close(struct snd_pcm_substream *substream)
kfree(rtd);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { adata->play_stream = NULL; - else + for (bank = 1; bank <= 4; bank++) + acp_turnonoff_lower_sram_bank(adata->acp_mmio, bank, + false); + } else { adata->capture_stream = NULL; + for (bank = 5; bank <= 8; bank++) + acp_turnonoff_lower_sram_bank(adata->acp_mmio, bank, + false); + }
/* Disable ACP irq, when the current stream is being closed and * another stream is also not active. diff --git a/sound/soc/amd/acp.c b/sound/soc/amd/acp.c index 0d59be4..ed3f83f 100644 --- a/sound/soc/amd/acp.c +++ b/sound/soc/amd/acp.c @@ -462,10 +462,79 @@ int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num) return 0; }
+void acp_turnonoff_lower_sram_bank(void __iomem *acp_mmio, u16 bank, + bool turnon) +{ + u32 val; + + val = acp_reg_read(acp_mmio, mmACP_MEM_SHUT_DOWN_REQ_LO); + if (val & (1 << bank)) { + /* bank is in off state */ + if (turnon == true) + /* request to on */ + val &= ~(1 << bank); + else + /* request to off */ + return; + } else { + /* bank is in on state */ + if (turnon == false) + /* request to off */ + val |= 1 << bank; + else + /* request to on */ + return; + } + acp_reg_write(val, acp_mmio, + mmACP_MEM_SHUT_DOWN_REQ_LO); + /* If ACP_MEM_SHUT_DOWN_STS_LO is 0xFFFFFFFF, then + * shutdown sequence is complete. + */ + while (acp_reg_read(acp_mmio, + mmACP_MEM_SHUT_DOWN_STS_LO) + != 0xFFFFFFFF) + cpu_relax(); +} + +void acp_turnonoff_higher_sram_bank(void __iomem *acp_mmio, u16 bank, + bool turnon) +{ + u32 val; + + bank -= 32; + val = acp_reg_read(acp_mmio, mmACP_MEM_SHUT_DOWN_REQ_HI); + if (val & (1 << bank)) { + /* bank is in off state */ + if (turnon == true) + /* request to on */ + val &= ~(1 << bank); + else + /* request to off */ + return; + } else { + /* bank is in on state */ + if (turnon == false) + /* request to off */ + val |= 1 << bank; + else + /* request to on */ + return; + } + acp_reg_write(val, acp_mmio, + mmACP_MEM_SHUT_DOWN_REQ_HI); + /* If ACP_MEM_SHUT_DOWN_STS_LO is 0xFFFFFFFF, then + * shutdown sequence is complete. + */ + while (acp_reg_read(acp_mmio, + mmACP_MEM_SHUT_DOWN_STS_HI) + != 0x0000FFFF) + cpu_relax(); +} + /* Initialize and bring ACP hardware to default state. */ int acp_init(void __iomem *acp_mmio) { - u32 val; + u32 val, bank; u32 count;
/* Assert Soft reset of ACP */ @@ -527,6 +596,16 @@ int acp_init(void __iomem *acp_mmio) acp_reg_write(ACP_EXTERNAL_INTR_CNTL__DMAIOCMask_MASK, acp_mmio, mmACP_EXTERNAL_INTR_CNTL);
+ /* When ACP_TILE_P1 is turned on, all SRAM banks get turned on. + * Now, turn off all of them. This can't be done in 'poweron' of + * ACP pm domain, as this requires ACP to be initialized. + */ + for (bank = 1; bank < 32; bank++) + acp_turnonoff_lower_sram_bank(acp_mmio, bank, false); + + for (bank = 32; bank < 48; bank++) + acp_turnonoff_higher_sram_bank(acp_mmio, bank, false); + /* Designware I2S driver requries proper capabilities * from mmACP_I2SMICSP_COMP_PARAM_1 register. The register * reports playback and capture capabilities though the diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h index 53c30ea..a2f3762 100644 --- a/sound/soc/amd/acp.h +++ b/sound/soc/amd/acp.h @@ -135,5 +135,6 @@ extern void acp_enable_external_interrupts(void __iomem *acp_mmio, extern u32 acp_get_intr_flag(void __iomem *acp_mmio); extern u16 get_dscr_idx(void __iomem *acp_mmio, int direction); extern void acp_ext_stat_clear_dmaioc(void __iomem *acp_mmio, u8 ch_num); - +extern void acp_turnonoff_lower_sram_bank(void __iomem *acp_mmio, u16 bank, + bool turnon); #endif /*__ACP_HW_H */