ASoC: fsl_ssi: Clean up helper functions of trigger()

The trigger() calls fsl_ssi_tx_config() and fsl_ssi_rx_config(),
and both of them jump to fsl_ssi_config(). And fsl_ssi_config()
later calls another fsl_ssi_rxtx_config().

However, the whole routine, especially fsl_ssi_config() function,
is too complicated because of the folowing reasons:
1) It has to handle the concern of the opposite stream.
2) It has to handle cases of offline configurations support.
3) It has to handle enable and disable operations while they're
   mostly different.

Since the enable and disable routines have more differences than
TX and RX rountines, this patch simplifies these helper functions
with the following changes:
- Changing to two helper functions of enable and disable instead
  of TX and RX.
- Removing fsl_ssi_rxtx_config() by separately integrating it to
  two newly introduced enable & disable functions.

Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
Tested-by: Caleb Crome <caleb@crome.org>
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Reviewed-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Nicolin Chen 2018-02-12 14:03:15 -08:00 committed by Mark Brown
parent 2e1327403b
commit 7d67bcb669
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
1 changed files with 124 additions and 136 deletions

View File

@ -382,31 +382,83 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
}
/**
* Enable or disable all rx/tx config flags at once
* Set SCR, SIER, STCR and SRCR registers with cached values in regvals
*
* Notes:
* 1) For offline_config SoCs, enable all necessary bits of both streams
* when 1st stream starts, even if the opposite stream will not start
* 2) It also clears FIFO before setting regvals; SOR is safe to set online
*/
static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable)
static void fsl_ssi_config_enable(struct fsl_ssi *ssi, bool tx)
{
struct regmap *regs = ssi->regs;
struct fsl_ssi_regvals *vals = ssi->regvals;
int dir = tx ? TX : RX;
u32 sier, srcr, stcr;
if (enable) {
regmap_update_bits(regs, REG_SSI_SIER,
vals[RX].sier | vals[TX].sier,
vals[RX].sier | vals[TX].sier);
regmap_update_bits(regs, REG_SSI_SRCR,
vals[RX].srcr | vals[TX].srcr,
vals[RX].srcr | vals[TX].srcr);
regmap_update_bits(regs, REG_SSI_STCR,
vals[RX].stcr | vals[TX].stcr,
vals[RX].stcr | vals[TX].stcr);
/* Clear dirty data in the FIFO; It also prevents channel slipping */
regmap_update_bits(ssi->regs, REG_SSI_SOR,
SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
/*
* On offline_config SoCs, SxCR and SIER are already configured when
* the previous stream started. So skip all SxCR and SIER settings
* to prevent online reconfigurations, then jump to set SCR directly
*/
if (ssi->soc->offline_config && ssi->streams)
goto enable_scr;
if (ssi->soc->offline_config) {
/*
* Online reconfiguration not supported, so enable all bits for
* both streams at once to avoid necessity of reconfigurations
*/
srcr = vals[RX].srcr | vals[TX].srcr;
stcr = vals[RX].stcr | vals[TX].stcr;
sier = vals[RX].sier | vals[TX].sier;
} else {
regmap_update_bits(regs, REG_SSI_SRCR,
vals[RX].srcr | vals[TX].srcr, 0);
regmap_update_bits(regs, REG_SSI_STCR,
vals[RX].stcr | vals[TX].stcr, 0);
regmap_update_bits(regs, REG_SSI_SIER,
vals[RX].sier | vals[TX].sier, 0);
/* Otherwise, only set bits for the current stream */
srcr = vals[dir].srcr;
stcr = vals[dir].stcr;
sier = vals[dir].sier;
}
/* Configure SRCR, STCR and SIER at once */
regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, srcr);
regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, stcr);
regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, sier);
enable_scr:
/*
* Start DMA before setting TE to avoid FIFO underrun
* which may cause a channel slip or a channel swap
*
* TODO: FIQ cases might also need this upon testing
*/
if (ssi->use_dma && tx) {
int try = 100;
u32 sfcsr;
/* Enable SSI first to send TX DMA request */
regmap_update_bits(ssi->regs, REG_SSI_SCR,
SSI_SCR_SSIEN, SSI_SCR_SSIEN);
/* Busy wait until TX FIFO not empty -- DMA working */
do {
regmap_read(ssi->regs, REG_SSI_SFCSR, &sfcsr);
if (SSI_SFCSR_TFCNT0(sfcsr))
break;
} while (--try);
/* FIFO still empty -- something might be wrong */
if (!SSI_SFCSR_TFCNT0(sfcsr))
dev_warn(ssi->dev, "Timeout waiting TX FIFO filling\n");
}
/* Enable all remaining bits in SCR */
regmap_update_bits(ssi->regs, REG_SSI_SCR,
vals[dir].scr, vals[dir].scr);
/* Log the enabled stream to the mask */
ssi->streams |= BIT(dir);
}
/**
@ -430,14 +482,17 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable)
((vals) & _ssi_xor_shared_bits(vals, avals, aactive))
/**
* Enable or disable SSI configuration.
* Unset SCR, SIER, STCR and SRCR registers with cached values in regvals
*
* Notes:
* 1) For offline_config SoCs, to avoid online reconfigurations, disable all
* bits of both streams at once when the last stream is abort to end
* 2) It also clears FIFO after unsetting regvals; SOR is safe to set online
*/
static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
struct fsl_ssi_regvals *vals)
static void fsl_ssi_config_disable(struct fsl_ssi *ssi, bool tx)
{
bool tx = &ssi->regvals[TX] == vals;
struct regmap *regs = ssi->regs;
struct fsl_ssi_regvals *avals;
struct fsl_ssi_regvals *vals, *avals;
u32 sier, srcr, stcr, scr;
int adir = tx ? RX : TX;
int dir = tx ? TX : RX;
bool aactive;
@ -445,52 +500,36 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
/* Check if the opposite stream is active */
aactive = ssi->streams & BIT(adir);
/* Get the opposite direction to keep its values untouched */
if (&ssi->regvals[RX] == vals)
avals = &ssi->regvals[TX];
else
avals = &ssi->regvals[RX];
vals = &ssi->regvals[dir];
if (!enable) {
/*
* To keep the other stream safe, exclude shared bits between
* both streams, and get safe bits to disable current stream
*/
u32 scr = ssi_excl_shared_bits(vals->scr, avals->scr, aactive);
/* Safely disable SCR register for the stream */
regmap_update_bits(regs, REG_SSI_SCR, scr, 0);
/* Log the disabled stream to the mask */
ssi->streams &= ~BIT(dir);
}
/* Get regvals of the opposite stream to keep opposite stream safe */
avals = &ssi->regvals[adir];
/*
* For cases where online configuration is not supported,
* 1) Enable all necessary bits of both streams when 1st stream starts
* even if the opposite stream will not start
* 2) Disable all remaining bits of both streams when last stream ends
* To keep the other stream safe, exclude shared bits between
* both streams, and get safe bits to disable current stream
*/
scr = ssi_excl_shared_bits(vals->scr, avals->scr, aactive);
/* Disable safe bits of SCR register for the current stream */
regmap_update_bits(ssi->regs, REG_SSI_SCR, scr, 0);
/* Log the disabled stream to the mask */
ssi->streams &= ~BIT(dir);
/*
* On offline_config SoCs, if the other stream is active, skip
* SxCR and SIER settings to prevent online reconfigurations
*/
if (ssi->soc->offline_config && aactive)
goto fifo_clear;
if (ssi->soc->offline_config) {
if ((enable && !ssi->streams) || (!enable && !aactive))
fsl_ssi_rxtx_config(ssi, enable);
goto config_done;
}
/* Online configure single direction while SSI is running */
if (enable) {
/* Clear FIFO to prevent dirty data or channel slipping */
regmap_update_bits(ssi->regs, REG_SSI_SOR,
SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
regmap_update_bits(regs, REG_SSI_SRCR, vals->srcr, vals->srcr);
regmap_update_bits(regs, REG_SSI_STCR, vals->stcr, vals->stcr);
regmap_update_bits(regs, REG_SSI_SIER, vals->sier, vals->sier);
/* Now there is only current stream active, disable all bits */
srcr = vals->srcr | avals->srcr;
stcr = vals->stcr | avals->stcr;
sier = vals->sier | avals->sier;
} else {
u32 sier;
u32 srcr;
u32 stcr;
/*
* To keep the other stream safe, exclude shared bits between
* both streams, and get safe bits to disable current stream
@ -498,57 +537,17 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
sier = ssi_excl_shared_bits(vals->sier, avals->sier, aactive);
srcr = ssi_excl_shared_bits(vals->srcr, avals->srcr, aactive);
stcr = ssi_excl_shared_bits(vals->stcr, avals->stcr, aactive);
/* Safely disable other control registers for the stream */
regmap_update_bits(regs, REG_SSI_SRCR, srcr, 0);
regmap_update_bits(regs, REG_SSI_STCR, stcr, 0);
regmap_update_bits(regs, REG_SSI_SIER, sier, 0);
/* Clear FIFO to prevent dirty data or channel slipping */
regmap_update_bits(ssi->regs, REG_SSI_SOR,
SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
}
config_done:
/* Enabling of subunits is done after configuration */
if (enable) {
/*
* Start DMA before setting TE to avoid FIFO underrun
* which may cause a channel slip or a channel swap
*
* TODO: FIQ cases might also need this upon testing
*/
if (ssi->use_dma && (vals->scr & SSI_SCR_TE)) {
int i;
int max_loop = 100;
/* Clear configurations of SRCR, STCR and SIER at once */
regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, 0);
regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, 0);
regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, 0);
/* Enable SSI first to send TX DMA request */
regmap_update_bits(regs, REG_SSI_SCR,
SSI_SCR_SSIEN, SSI_SCR_SSIEN);
/* Busy wait until TX FIFO not empty -- DMA working */
for (i = 0; i < max_loop; i++) {
u32 sfcsr;
regmap_read(regs, REG_SSI_SFCSR, &sfcsr);
if (SSI_SFCSR_TFCNT0(sfcsr))
break;
}
if (i == max_loop) {
dev_err(ssi->dev,
"Timeout waiting TX FIFO filling\n");
}
}
/* Enable all remaining bits */
regmap_update_bits(regs, REG_SSI_SCR, vals->scr, vals->scr);
/* Log the enabled stream to the mask */
ssi->streams |= BIT(dir);
}
}
static void fsl_ssi_rx_config(struct fsl_ssi *ssi, bool enable)
{
fsl_ssi_config(ssi, enable, &ssi->regvals[RX]);
fifo_clear:
/* Clear remaining data in the FIFO */
regmap_update_bits(ssi->regs, REG_SSI_SOR,
SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
}
static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi)
@ -564,21 +563,6 @@ static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi)
}
}
static void fsl_ssi_tx_config(struct fsl_ssi *ssi, bool enable)
{
/*
* SACCST might be modified via AC Link by a CODEC if it sends
* extra bits in their SLOTREQ requests, which'll accidentally
* send valid data to slots other than normal playback slots.
*
* To be safe, configure SACCST right before TX starts.
*/
if (enable && fsl_ssi_is_ac97(ssi))
fsl_ssi_tx_ac97_saccst_setup(ssi);
fsl_ssi_config(ssi, enable, &ssi->regvals[TX]);
}
/**
* Cache critical bits of SIER, SRCR, STCR and SCR to later set them safely
*/
@ -1087,24 +1071,28 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
fsl_ssi_tx_config(ssi, true);
else
fsl_ssi_rx_config(ssi, true);
/*
* SACCST might be modified via AC Link by a CODEC if it sends
* extra bits in their SLOTREQ requests, which'll accidentally
* send valid data to slots other than normal playback slots.
*
* To be safe, configure SACCST right before TX starts.
*/
if (tx && fsl_ssi_is_ac97(ssi))
fsl_ssi_tx_ac97_saccst_setup(ssi);
fsl_ssi_config_enable(ssi, tx);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
fsl_ssi_tx_config(ssi, false);
else
fsl_ssi_rx_config(ssi, false);
fsl_ssi_config_disable(ssi, tx);
break;
default: