/*
 *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]);
	}
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
0
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;
}