ssp: Keep SSP connected with DMA in pause state

When there is not enough time to wait for FIFO empty and we care about
data continuity - like in pause state - then SSP should be kept connected
with DMA in pause state.
Stop function has been updated to take action also on channels in
COMP_STATE_PAUSED state. After receiving COMP_TRIGGER_STOP,
component state should be set to COMP_STATE_PREPARE,
what is consistent with sof-docs.
Introduced changes have positive impact on glitches after
pause/resume sequence.

Signed-off-by: Karol Trzcinski <karolx.trzcinski@linux.intel.com>
This commit is contained in:
Karol Trzcinski 2020-02-06 10:19:36 +01:00 committed by Liam Girdwood
parent 93cf630d9d
commit 6cd8acc16a
4 changed files with 64 additions and 28 deletions

View File

@ -626,7 +626,7 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd)
case COMP_TRIGGER_STOP:
comp_info(dev, "dai_comp_trigger(), PAUSE/STOP");
ret = dma_stop(dd->chan);
dai_trigger(dd->dai, COMP_TRIGGER_STOP, dev->direction);
dai_trigger(dd->dai, cmd, dev->direction);
break;
default:
break;

View File

@ -543,33 +543,43 @@ static void ssp_stop(struct dai *dai, int direction)
/* stop Rx if neeed */
if (direction == DAI_DIR_CAPTURE &&
ssp->state[SOF_IPC_STREAM_CAPTURE] == COMP_STATE_ACTIVE) {
ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_PREPARE) {
ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0);
ssp_empty_rx_fifo(dai);
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PAUSED;
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE;
dai_info(dai, "ssp_stop(), RX stop");
}
/* stop Tx if needed */
if (direction == DAI_DIR_PLAYBACK &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] == COMP_STATE_ACTIVE) {
ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_PREPARE) {
ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0);
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PAUSED;
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE;
dai_info(dai, "ssp_stop(), TX stop");
}
/* disable SSP port if no users */
if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) {
if (ssp->state[SOF_IPC_STREAM_CAPTURE] == COMP_STATE_PREPARE &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] == COMP_STATE_PREPARE) {
ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0);
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE;
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE;
dai_info(dai, "ssp_stop(), SSP port disabled");
}
spin_unlock(&dai->lock);
}
static void ssp_pause(struct dai *dai, int direction)
{
struct ssp_pdata *ssp = dai_get_drvdata(dai);
if (direction == SOF_IPC_STREAM_CAPTURE)
dai_info(dai, "ssp_pause(), RX");
else
dai_info(dai, "ssp_pause(), TX");
ssp->state[direction] = COMP_STATE_PAUSED;
}
static int ssp_trigger(struct dai *dai, int cmd, int direction)
{
struct ssp_pdata *ssp = dai_get_drvdata(dai);
@ -588,9 +598,11 @@ static int ssp_trigger(struct dai *dai, int cmd, int direction)
ssp_start(dai, direction);
break;
case COMP_TRIGGER_STOP:
case COMP_TRIGGER_PAUSE:
ssp_stop(dai, direction);
break;
case COMP_TRIGGER_PAUSE:
ssp_pause(dai, direction);
break;
case COMP_TRIGGER_RESUME:
ssp_context_restore(dai);
break;

View File

@ -700,36 +700,46 @@ static void ssp_stop(struct dai *dai, int direction)
/* stop Rx if neeed */
if (direction == DAI_DIR_CAPTURE &&
ssp->state[SOF_IPC_STREAM_CAPTURE] == COMP_STATE_ACTIVE) {
ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_PREPARE) {
ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0);
ssp_update_bits(dai, SSRSA, SSRSA_RXEN, 0);
ssp_empty_rx_fifo(dai);
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PAUSED;
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE;
dai_info(dai, "ssp_stop(), RX stop");
}
/* stop Tx if needed */
if (direction == DAI_DIR_PLAYBACK &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] == COMP_STATE_ACTIVE) {
ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_PREPARE) {
ssp_empty_tx_fifo(dai);
ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0);
ssp_update_bits(dai, SSTSA, SSTSA_TXEN, 0);
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PAUSED;
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE;
dai_info(dai, "ssp_stop(), TX stop");
}
/* disable SSP port if no users */
if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) {
if (ssp->state[SOF_IPC_STREAM_CAPTURE] == COMP_STATE_PREPARE &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] == COMP_STATE_PREPARE) {
ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0);
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE;
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE;
dai_info(dai, "ssp_stop(), SSP port disabled");
}
spin_unlock(&dai->lock);
}
static void ssp_pause(struct dai *dai, int direction)
{
struct ssp_pdata *ssp = dai_get_drvdata(dai);
if (direction == SOF_IPC_STREAM_CAPTURE)
dai_info(dai, "ssp_pause(), RX");
else
dai_info(dai, "ssp_pause(), TX");
ssp->state[direction] = COMP_STATE_PAUSED;
}
static int ssp_trigger(struct dai *dai, int cmd, int direction)
{
struct ssp_pdata *ssp = dai_get_drvdata(dai);
@ -748,9 +758,11 @@ static int ssp_trigger(struct dai *dai, int cmd, int direction)
ssp_start(dai, direction);
break;
case COMP_TRIGGER_STOP:
case COMP_TRIGGER_PAUSE:
ssp_stop(dai, direction);
break;
case COMP_TRIGGER_PAUSE:
ssp_pause(dai, direction);
break;
case COMP_TRIGGER_RESUME:
ssp_context_restore(dai);
break;

View File

@ -459,34 +459,44 @@ static void ssp_stop(struct dai *dai, int direction)
/* stop Rx if neeed */
if (direction == DAI_DIR_CAPTURE &&
ssp->state[SOF_IPC_STREAM_CAPTURE] == COMP_STATE_ACTIVE) {
ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_PREPARE) {
ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0);
ssp_update_bits(dai, SSCR0, SSCR0_RIM, SSCR0_RIM);
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PAUSED;
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE;
dai_info(dai, "ssp_stop(), RX stop");
}
/* stop Tx if needed */
if (direction == DAI_DIR_PLAYBACK &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] == COMP_STATE_ACTIVE) {
ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_PREPARE) {
ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0);
ssp_update_bits(dai, SSCR0, SSCR0_TIM, SSCR0_TIM);
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PAUSED;
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE;
dai_info(dai, "ssp_stop(), TX stop");
}
/* disable SSP port if no users */
if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) {
if (ssp->state[SOF_IPC_STREAM_CAPTURE] == COMP_STATE_PREPARE &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] == COMP_STATE_PREPARE) {
ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0);
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE;
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE;
dai_info(dai, "ssp_stop(), SSP port disabled");
}
spin_unlock(&dai->lock);
}
static void ssp_pause(struct dai *dai, int direction)
{
struct ssp_pdata *ssp = dai_get_drvdata(dai);
if (direction == SOF_IPC_STREAM_CAPTURE)
dai_info(dai, "ssp_pause(), RX");
else
dai_info(dai, "ssp_pause(), TX");
ssp->state[direction] = COMP_STATE_PAUSED;
}
static int ssp_trigger(struct dai *dai, int cmd, int direction)
{
struct ssp_pdata *ssp = dai_get_drvdata(dai);
@ -505,9 +515,11 @@ static int ssp_trigger(struct dai *dai, int cmd, int direction)
ssp_start(dai, direction);
break;
case COMP_TRIGGER_STOP:
case COMP_TRIGGER_PAUSE:
ssp_stop(dai, direction);
break;
case COMP_TRIGGER_PAUSE:
ssp_pause(dai, direction);
break;
case COMP_TRIGGER_RESUME:
ssp_context_restore(dai);
break;