diff options
Diffstat (limited to 'sound/soc/atmel/atmel_ssc_dai.c')
-rw-r--r-- | sound/soc/atmel/atmel_ssc_dai.c | 108 |
1 files changed, 71 insertions, 37 deletions
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 35e44e463cfe..fb0b7e8b08ff 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -204,6 +204,13 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n", ssc_readl(ssc_p->ssc->regs, SR)); + /* Enable PMC peripheral clock for this SSC */ + pr_debug("atmel_ssc_dai: Starting clock\n"); + clk_enable(ssc_p->ssc->clk); + + /* Reset the SSC to keep it at a clean status */ + ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dir = 0; dir_mask = SSC_DIR_MASK_PLAYBACK; @@ -250,11 +257,6 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, dma_params = ssc_p->dma_params[dir]; if (dma_params != NULL) { - ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable); - pr_debug("atmel_ssc_shutdown: %s disabled SSC_SR=0x%08x\n", - (dir ? "receive" : "transmit"), - ssc_readl(ssc_p->ssc->regs, SR)); - dma_params->ssc = NULL; dma_params->substream = NULL; ssc_p->dma_params[dir] = NULL; @@ -266,10 +268,6 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, ssc_p->dir_mask &= ~dir_mask; if (!ssc_p->dir_mask) { if (ssc_p->initialized) { - /* Shutdown the SSC clock. */ - pr_debug("atmel_ssc_dai: Stopping clock\n"); - clk_disable(ssc_p->ssc->clk); - free_irq(ssc_p->ssc->irq, ssc_p); ssc_p->initialized = 0; } @@ -280,6 +278,10 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0; } spin_unlock_irq(&ssc_p->lock); + + /* Shutdown the SSC clock. */ + pr_debug("atmel_ssc_dai: Stopping clock\n"); + clk_disable(ssc_p->ssc->clk); } @@ -450,13 +452,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, break; case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: - /* - * I2S format, CODEC supplies BCLK and LRC clocks. - * - * The SSC transmit clock is obtained from the BCLK signal on - * on the TK line, and the SSC receive clock is - * generated from the transmit clock. - */ + /* I2S format, CODEC supplies BCLK and LRC clocks. */ rcmr = SSC_BF(RCMR_PERIOD, 0) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_FALLING_RF) @@ -491,6 +487,54 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(TFMR_DATLEN, (bits - 1)); break; + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFS: + /* I2S format, CODEC supplies BCLK, SSC supplies LRCLK. */ + if (bits > 16 && !ssc->pdata->has_fslen_ext) { + dev_err(dai->dev, + "sample size %d is too large for SSC device\n", + bits); + return -EINVAL; + } + + fslen_ext = (bits - 1) / 16; + fslen = (bits - 1) % 16; + + rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) + | SSC_BF(RCMR_STTDLY, START_DELAY) + | SSC_BF(RCMR_START, SSC_START_FALLING_RF) + | SSC_BF(RCMR_CKI, SSC_CKI_RISING) + | SSC_BF(RCMR_CKO, SSC_CKO_NONE) + | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? + SSC_CKS_PIN : SSC_CKS_CLOCK); + + rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext) + | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) + | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) + | SSC_BF(RFMR_FSLEN, fslen) + | SSC_BF(RFMR_DATNB, (channels - 1)) + | SSC_BIT(RFMR_MSBF) + | SSC_BF(RFMR_LOOP, 0) + | SSC_BF(RFMR_DATLEN, (bits - 1)); + + tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) + | SSC_BF(TCMR_STTDLY, START_DELAY) + | SSC_BF(TCMR_START, SSC_START_FALLING_RF) + | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) + | SSC_BF(TCMR_CKO, SSC_CKO_NONE) + | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ? + SSC_CKS_CLOCK : SSC_CKS_PIN); + + tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext) + | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_NEGATIVE) + | SSC_BF(TFMR_FSDEN, 0) + | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) + | SSC_BF(TFMR_FSLEN, fslen) + | SSC_BF(TFMR_DATNB, (channels - 1)) + | SSC_BIT(TFMR_MSBF) + | SSC_BF(TFMR_DATDEF, 0) + | SSC_BF(TFMR_DATLEN, (bits - 1)); + break; + case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: /* * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks. @@ -535,10 +579,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, /* * DSP/PCM Mode A format, CODEC supplies BCLK and LRC clocks. * - * The SSC transmit clock is obtained from the BCLK signal on - * on the TK line, and the SSC receive clock is - * generated from the transmit clock. - * * Data is transferred on first BCLK after LRC pulse rising * edge.If stereo, the right channel data is contiguous with * the left channel data. @@ -587,23 +627,17 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, rcmr, rfmr, tcmr, tfmr); if (!ssc_p->initialized) { - - /* Enable PMC peripheral clock for this SSC */ - pr_debug("atmel_ssc_dai: Starting clock\n"); - clk_enable(ssc_p->ssc->clk); - - /* Reset the SSC and its PDC registers */ - ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); - - ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0); - - ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0); - ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0); + if (!ssc_p->ssc->pdata->use_dma) { + ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0); + + ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0); + ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0); + } ret = request_irq(ssc_p->ssc->irq, atmel_ssc_interrupt, 0, ssc_p->name, ssc_p); |