Browse Source

ENGR00307835-3 ASoC: fsl: implement the ESAI xrun handler.

When esai xrun happened, there is possibility of channel swap. So ESAI
need to be reset.

Signed-off-by: Shengjiu Wang <b02247@freescale.com>
(cherry picked from commit 440fbeee5b)
isee-imx_3.14.28.y
Shengjiu Wang 7 years ago
committed by Nitin Garg
parent
commit
963e1bef81
2 changed files with 152 additions and 1 deletions
  1. +111
    -0
      sound/soc/fsl/fsl_esai.c
  2. +41
    -1
      sound/soc/fsl/imx-pcm-dma.c

+ 111
- 0
sound/soc/fsl/fsl_esai.c View File

@ -49,6 +49,7 @@
struct fsl_esai {
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_pcm_substream *substream[2];
struct platform_device *pdev;
struct regmap *regmap;
struct clk *coreclk;
@ -497,6 +498,8 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream,
ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2));
}
esai_priv->substream[substream->stream] = substream;
return 0;
err_fsysclk:
@ -560,6 +563,8 @@ static void fsl_esai_shutdown(struct snd_pcm_substream *substream,
{
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
esai_priv->substream[substream->stream] = NULL;
if (!IS_ERR(esai_priv->fsysclk))
clk_disable_unprepare(esai_priv->fsysclk);
if (!IS_ERR(esai_priv->extalclk))
@ -726,6 +731,107 @@ static struct regmap_config fsl_esai_regmap_config = {
.writeable_reg = fsl_esai_writeable_reg,
};
static bool fsl_esai_check_xrun(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(cpu_dai);
u32 saisr;
regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr);
return saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE) ;
}
static int stop_lock_stream(struct snd_pcm_substream *substream)
{
if (substream) {
snd_pcm_stream_lock_irq(substream);
if (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING)
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
}
return 0;
}
static int start_unlock_stream(struct snd_pcm_substream *substream)
{
if (substream) {
if (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING)
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
snd_pcm_stream_unlock_irq(substream);
}
return 0;
}
/*
*Here is ESAI underrun reset step:
*1. Read "TUE" and got TUE=1
*2. stop DMA.
*3. stop ESAI TX section.
*4. Set the transmitter section individual reset "TPR=1"
*5. Reset the ESAI Transmit FIFO (set ESAI_TFCR[1]=1).
*6. Config the control registers ESAI_TCCR and ESAI_TCR.config the Transmit FIFO register.
*7. clear "TPR"
*8. read "TUE"
*9. Prefill ESAI TX FIFO.
*10.Start DMA.
*11 Enable the ESAI
*/
static void fsl_esai_reset(struct snd_pcm_substream *substream, bool stop)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(cpu_dai);
u32 saisr;
if (stop) {
stop_lock_stream(esai_priv->substream[0]);
stop_lock_stream(esai_priv->substream[1]);
}
regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,
ESAI_ECR_ESAIEN | ESAI_ECR_ERST);
regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,
ESAI_ECR_ESAIEN);
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);
regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, ESAI_PRRC_PDC_MASK, 0);
regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, ESAI_PCRC_PC_MASK, 0);
/*
* Add fifo reset here, because the regcache_sync will write one more data to ETDR.
* Which will cause channel shift.
*/
regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR);
regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR);
regcache_mark_dirty(esai_priv->regmap);
regcache_sync(esai_priv->regmap);
regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR_MASK, 0);
regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR_MASK, 0);
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, ESAI_xCR_xPR_MASK, 0);
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, ESAI_xCR_xPR_MASK, 0);
regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr);
if (stop) {
start_unlock_stream(esai_priv->substream[1]);
start_unlock_stream(esai_priv->substream[0]);
}
}
static int fsl_esai_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@ -817,6 +923,11 @@ static int fsl_esai_probe(struct platform_device *pdev)
esai_priv->dma_params_tx.addr = res->start + REG_ESAI_ETDR;
esai_priv->dma_params_rx.addr = res->start + REG_ESAI_ERDR;
esai_priv->dma_params_tx.check_xrun = fsl_esai_check_xrun;
esai_priv->dma_params_rx.check_xrun = fsl_esai_check_xrun;
esai_priv->dma_params_tx.device_reset = fsl_esai_reset;
esai_priv->dma_params_rx.device_reset = fsl_esai_reset;
esai_priv->synchronous =
of_property_read_bool(np, "fsl,esai-synchronous");


+ 41
- 1
sound/soc/fsl/imx-pcm-dma.c View File

@ -48,9 +48,49 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
.fifo_size = 0,
};
static void imx_pcm_dma_complete(void *arg)
{
struct snd_pcm_substream *substream = arg;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct dmaengine_pcm_runtime_data *prtd = substream->runtime->private_data;
struct snd_dmaengine_dai_dma_data *dma_data;
prtd->pos += snd_pcm_lib_period_bytes(substream);
if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream))
prtd->pos = 0;
snd_pcm_period_elapsed(substream);
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
if (dma_data->check_xrun && dma_data->check_xrun(substream))
dma_data->device_reset(substream, 1);
}
static int imx_pcm_dma_prepare_slave_config(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_dmaengine_dai_dma_data *dma_data;
struct dmaengine_pcm_runtime_data *prtd = substream->runtime->private_data;
int ret;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
prtd->callback = imx_pcm_dma_complete;
ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
if (ret)
return ret;
snd_dmaengine_pcm_set_config_from_dai_data(substream, dma_data,
slave_config);
return 0;
}
static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = {
.pcm_hardware = &imx_pcm_hardware,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
.prepare_slave_config = imx_pcm_dma_prepare_slave_config,
.compat_filter_fn = filter,
.prealloc_buffer_size = IMX_DEFAULT_DMABUF_SIZE,
};


Loading…
Cancel
Save