Merge series "refine and clean code for synchronous mode" from Shengjiu Wang <shengjiu.wang@nxp.com>:

refine and clean code for synchronous mode

Shengjiu Wang (3):
  ASoC: fsl_sai: Refine enable/disable TE/RE sequence in trigger()
  ASoC: fsl_sai: Drop TMR/RMR settings for synchronous mode
  ASoC: fsl_sai: Replace synchronous check with fsl_sai_dir_is_synced

changes in v3:
- Add reviewed-by Nicolin
- refine the commit log.
- Add one more patch #3

changes in v2:
- Split the commit
- refine the sequence in trigger stop

 sound/soc/fsl/fsl_sai.c | 173 +++++++++++++++++++++++-----------------
 1 file changed, 102 insertions(+), 71 deletions(-)

--
2.27.0
This commit is contained in:
Mark Brown 2020-08-18 14:52:59 +01:00
commit bf66f140d6
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View File

@ -37,6 +37,24 @@ static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
.list = fsl_sai_rates,
};
/**
* fsl_sai_dir_is_synced - Check if stream is synced by the opposite stream
*
* SAI supports synchronous mode using bit/frame clocks of either Transmitter's
* or Receiver's for both streams. This function is used to check if clocks of
* the stream's are synced by the opposite stream.
*
* @sai: SAI context
* @dir: stream direction
*/
static inline bool fsl_sai_dir_is_synced(struct fsl_sai *sai, int dir)
{
int adir = (dir == TX) ? RX : TX;
/* current dir in async mode while opposite dir in sync mode */
return !sai->synchronous[dir] && sai->synchronous[adir];
}
static irqreturn_t fsl_sai_isr(int irq, void *devid)
{
struct fsl_sai *sai = (struct fsl_sai *)devid;
@ -332,6 +350,8 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
unsigned int ofs = sai->soc_data->reg_offset;
unsigned long clk_rate;
u32 savediv = 0, ratio, savesub = freq;
int adir = tx ? RX : TX;
int dir = tx ? TX : RX;
u32 id;
int ret = 0;
@ -390,19 +410,17 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
* 4) For Tx and Rx are both Synchronous with another SAI, we just
* ignore it.
*/
if ((sai->synchronous[TX] && !sai->synchronous[RX]) ||
(!tx && !sai->synchronous[RX])) {
regmap_update_bits(sai->regmap, FSL_SAI_RCR2(ofs),
if (fsl_sai_dir_is_synced(sai, adir)) {
regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs),
FSL_SAI_CR2_MSEL_MASK,
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
regmap_update_bits(sai->regmap, FSL_SAI_RCR2(ofs),
regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs),
FSL_SAI_CR2_DIV_MASK, savediv - 1);
} else if ((sai->synchronous[RX] && !sai->synchronous[TX]) ||
(tx && !sai->synchronous[TX])) {
regmap_update_bits(sai->regmap, FSL_SAI_TCR2(ofs),
} else if (!sai->synchronous[dir]) {
regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
FSL_SAI_CR2_MSEL_MASK,
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
regmap_update_bits(sai->regmap, FSL_SAI_TCR2(ofs),
regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
FSL_SAI_CR2_DIV_MASK, savediv - 1);
}
@ -424,6 +442,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
u32 val_cr4 = 0, val_cr5 = 0;
u32 slots = (channels == 1) ? 2 : channels;
u32 slot_width = word_width;
int adir = tx ? RX : TX;
int ret;
if (sai->slots)
@ -470,30 +489,16 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
/*
* For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will
* generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4),
* RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync
* error.
* RCR5(TCR5) for playback(capture), or there will be sync error.
*/
if (!sai->is_slave_mode) {
if (!sai->synchronous[TX] && sai->synchronous[RX] && !tx) {
regmap_update_bits(sai->regmap, FSL_SAI_TCR4(ofs),
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
val_cr4);
regmap_update_bits(sai->regmap, FSL_SAI_TCR5(ofs),
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
FSL_SAI_CR5_FBT_MASK, val_cr5);
regmap_write(sai->regmap, FSL_SAI_TMR,
~0UL - ((1 << channels) - 1));
} else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) {
regmap_update_bits(sai->regmap, FSL_SAI_RCR4(ofs),
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
val_cr4);
regmap_update_bits(sai->regmap, FSL_SAI_RCR5(ofs),
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
FSL_SAI_CR5_FBT_MASK, val_cr5);
regmap_write(sai->regmap, FSL_SAI_RMR,
~0UL - ((1 << channels) - 1));
}
if (!sai->is_slave_mode && fsl_sai_dir_is_synced(sai, adir)) {
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs),
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
val_cr4);
regmap_update_bits(sai->regmap, FSL_SAI_xCR5(!tx, ofs),
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
FSL_SAI_CR5_FBT_MASK, val_cr5);
}
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
@ -522,6 +527,38 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
return 0;
}
static void fsl_sai_config_disable(struct fsl_sai *sai, int dir)
{
unsigned int ofs = sai->soc_data->reg_offset;
bool tx = dir == TX;
u32 xcsr, count = 100;
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_TERE, 0);
/* TERE will remain set till the end of current frame */
do {
udelay(10);
regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr);
} while (--count && xcsr & FSL_SAI_CSR_TERE);
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
/*
* For sai master mode, after several open/close sai,
* there will be no frame clock, and can't recover
* anymore. Add software reset to fix this issue.
* This is a hardware bug, and will be fix in the
* next sai version.
*/
if (!sai->is_slave_mode) {
/* Software Reset */
regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR);
/* Clear SR bit to finish the reset */
regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), 0);
}
}
static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *cpu_dai)
@ -530,7 +567,9 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
unsigned int ofs = sai->soc_data->reg_offset;
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
u32 xcsr, count = 100;
int adir = tx ? RX : TX;
int dir = tx ? TX : RX;
u32 xcsr;
/*
* Asynchronous mode: Clear SYNC for both Tx and Rx.
@ -553,10 +592,22 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs),
FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs),
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
/*
* Enable the opposite direction for synchronous mode
* 1. Tx sync with Rx: only set RE for Rx; set TE & RE for Tx
* 2. Rx sync with Tx: only set TE for Tx; set RE & TE for Rx
*
* RM recommends to enable RE after TE for case 1 and to enable
* TE after RE for case 2, but we here may not always guarantee
* that happens: "arecord 1.wav; aplay 2.wav" in case 1 enables
* TE after RE, which is against what RM recommends but should
* be safe to do, judging by years of testing results.
*/
if (fsl_sai_dir_is_synced(sai, adir))
regmap_update_bits(sai->regmap, FSL_SAI_xCSR((!tx), ofs),
FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS);
@ -571,43 +622,23 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
/* Check if the opposite FRDE is also disabled */
regmap_read(sai->regmap, FSL_SAI_xCSR(!tx, ofs), &xcsr);
if (!(xcsr & FSL_SAI_CSR_FRDE)) {
/* Disable both directions and reset their FIFOs */
regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs),
FSL_SAI_CSR_TERE, 0);
regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs),
FSL_SAI_CSR_TERE, 0);
/* TERE will remain set till the end of current frame */
do {
udelay(10);
regmap_read(sai->regmap,
FSL_SAI_xCSR(tx, ofs), &xcsr);
} while (--count && xcsr & FSL_SAI_CSR_TERE);
/*
* If opposite stream provides clocks for synchronous mode and
* it is inactive, disable it before disabling the current one
*/
if (fsl_sai_dir_is_synced(sai, adir) && !(xcsr & FSL_SAI_CSR_FRDE))
fsl_sai_config_disable(sai, adir);
regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs),
FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs),
FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
/*
* Disable current stream if either of:
* 1. current stream doesn't provide clocks for synchronous mode
* 2. current stream provides clocks for synchronous mode but no
* more stream is active.
*/
if (!fsl_sai_dir_is_synced(sai, dir) || !(xcsr & FSL_SAI_CSR_FRDE))
fsl_sai_config_disable(sai, dir);
/*
* For sai master mode, after several open/close sai,
* there will be no frame clock, and can't recover
* anymore. Add software reset to fix this issue.
* This is a hardware bug, and will be fix in the
* next sai version.
*/
if (!sai->is_slave_mode) {
/* Software Reset for both Tx and Rx */
regmap_write(sai->regmap, FSL_SAI_TCSR(ofs),
FSL_SAI_CSR_SR);
regmap_write(sai->regmap, FSL_SAI_RCSR(ofs),
FSL_SAI_CSR_SR);
/* Clear SR bit to finish the reset */
regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0);
regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0);
}
}
break;
default:
return -EINVAL;