Merge remote-tracking branches 'asoc/topic/ad193x', 'asoc/topic/alc5632', 'asoc/topic...
[pandora-kernel.git] / sound / soc / atmel / atmel_ssc_dai.c
index 99ff35e..fb0b7e8 100644 (file)
@@ -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);
 }
 
 
@@ -348,7 +350,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
        struct atmel_pcm_dma_params *dma_params;
        int dir, channels, bits;
        u32 tfmr, rfmr, tcmr, rcmr;
-       int start_event;
        int ret;
        int fslen, fslen_ext;
 
@@ -451,25 +452,10 @@ 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.
-                *
-                *  For single channel data, one sample is transferred
-                * on the falling edge of the LRC clock.
-                * For two channel data, one sample is
-                * transferred on both edges of the LRC clock.
-                */
-               start_event = ((channels == 1)
-                               ? SSC_START_FALLING_RF
-                               : SSC_START_EDGE_RF);
-
+               /* I2S format, CODEC supplies BCLK and LRC clocks. */
                rcmr =    SSC_BF(RCMR_PERIOD, 0)
                        | SSC_BF(RCMR_STTDLY, START_DELAY)
-                       | SSC_BF(RCMR_START, start_event)
+                       | 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 ?
@@ -478,14 +464,14 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
                rfmr =    SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
                        | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
                        | SSC_BF(RFMR_FSLEN, 0)
-                       | SSC_BF(RFMR_DATNB, 0)
+                       | 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, 0)
                        | SSC_BF(TCMR_STTDLY, START_DELAY)
-                       | SSC_BF(TCMR_START, start_event)
+                       | 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 ?
@@ -495,7 +481,55 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
                        | SSC_BF(TFMR_FSDEN, 0)
                        | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
                        | SSC_BF(TFMR_FSLEN, 0)
-                       | SSC_BF(TFMR_DATNB, 0)
+                       | 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_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));
@@ -512,7 +546,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
                rcmr =    SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
                        | SSC_BF(RCMR_STTDLY, 1)
                        | SSC_BF(RCMR_START, SSC_START_RISING_RF)
-                       | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+                       | SSC_BF(RCMR_CKI, SSC_CKI_FALLING)
                        | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
                        | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
 
@@ -527,7 +561,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
                tcmr =    SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
                        | SSC_BF(TCMR_STTDLY, 1)
                        | SSC_BF(TCMR_START, SSC_START_RISING_RF)
-                       | SSC_BF(TCMR_CKI, SSC_CKI_RISING)
+                       | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
                        | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
                        | SSC_BF(TCMR_CKS, SSC_CKS_DIV);
 
@@ -545,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.
@@ -556,7 +586,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
                rcmr =    SSC_BF(RCMR_PERIOD, 0)
                        | SSC_BF(RCMR_STTDLY, START_DELAY)
                        | SSC_BF(RCMR_START, SSC_START_RISING_RF)
-                       | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+                       | SSC_BF(RCMR_CKI, SSC_CKI_FALLING)
                        | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
                        | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
                                           SSC_CKS_PIN : SSC_CKS_CLOCK);
@@ -597,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);