/* *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_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { int ret; struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); /* * Some platforms might use the same bit to gate all three or two of * clocks, so keep all clocks open/close at the same time for safety */ ret = clk_prepare_enable(esai_priv->coreclk); if (ret) return ret; if (!IS_ERR(esai_priv->extalclk)) { ret = clk_prepare_enable(esai_priv->extalclk); if (ret) goto err_extalck; } if (!IS_ERR(esai_priv->fsysclk)) { ret = clk_prepare_enable(esai_priv->fsysclk); if (ret) goto err_fsysclk; } if (!dai->active) { /* Reset Port C */ 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)); /* Set synchronous mode */ regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR, ESAI_SAICR_SYNC, esai_priv->synchronous ? ESAI_SAICR_SYNC : 0); /* Set a default slot number -- 2 */ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2)); regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2)); } return 0; err_fsysclk: if (!IS_ERR(esai_priv->extalclk)) clk_disable_unprepare(esai_priv->extalclk); err_extalck: clk_disable_unprepare(esai_priv->coreclk); return ret; }
static int fsl_esai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; u32 width = params_width(params); u32 channels = params_channels(params); u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); u32 slot_width = width; u32 bclk, mask, val; int ret; /* Override slot_width if being specifically set */ if (esai_priv->slot_width) slot_width = esai_priv->slot_width; bclk = params_rate(params) * slot_width * esai_priv->slots; ret = fsl_esai_set_bclk(dai, tx, bclk); if (ret) return ret; /* Use Normal mode to support monaural audio */ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ? ESAI_xCR_xMOD_NETWORK : 0); regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR); mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK | (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK); val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) | (tx ? ESAI_xFCR_TE(pins) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(pins)); regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val); mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0); val = ESAI_xCR_xSWS(slot_width, width) | (tx ? ESAI_xCR_PADC : 0); regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val); /* Remove ESAI personal reset by configuring ESAI_PCRC and ESAI_PRRC */ 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)); return 0; }