ASoC: davinci-mcasp: When stopping TX/RX stop the AFIFO as the last step
[firefly-linux-kernel-4.4.55.git] / sound / soc / davinci / davinci-mcasp.c
index 68347b55f6e17ecd2aa900c875d22c45e4ab0bbe..002351f9fc40ed24f175efda785016ca0cd33a51 100644 (file)
 
 #define MCASP_MAX_AFIFO_DEPTH  64
 
+static u32 context_regs[] = {
+       DAVINCI_MCASP_TXFMCTL_REG,
+       DAVINCI_MCASP_RXFMCTL_REG,
+       DAVINCI_MCASP_TXFMT_REG,
+       DAVINCI_MCASP_RXFMT_REG,
+       DAVINCI_MCASP_ACLKXCTL_REG,
+       DAVINCI_MCASP_ACLKRCTL_REG,
+       DAVINCI_MCASP_AHCLKXCTL_REG,
+       DAVINCI_MCASP_AHCLKRCTL_REG,
+       DAVINCI_MCASP_PDIR_REG,
+       DAVINCI_MCASP_RXMASK_REG,
+       DAVINCI_MCASP_TXMASK_REG,
+       DAVINCI_MCASP_RXTDM_REG,
+       DAVINCI_MCASP_TXTDM_REG,
+};
+
 struct davinci_mcasp_context {
-       u32     txfmtctl;
-       u32     rxfmtctl;
-       u32     txfmt;
-       u32     rxfmt;
-       u32     aclkxctl;
-       u32     aclkrctl;
-       u32     pdir;
+       u32     config_regs[ARRAY_SIZE(context_regs)];
+       u32     afifo_regs[2]; /* for read/write fifo control registers */
+       u32     *xrsr_regs; /* for serializer configuration */
 };
 
 struct davinci_mcasp {
@@ -142,9 +154,9 @@ static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
 
 static void mcasp_start_rx(struct davinci_mcasp *mcasp)
 {
+       /* Start clocks */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
-
        /*
         * When ASYNC == 0 the transmit and receive sections operate
         * synchronously from the transmit clock and frame sync. We need to make
@@ -155,47 +167,36 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp)
                mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
        }
 
+       /* Activate serializer(s) */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
+       /* Release RX state machine */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+       /* Release Frame Sync generator */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
-
        if (mcasp_is_synchronous(mcasp))
                mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
 }
 
 static void mcasp_start_tx(struct davinci_mcasp *mcasp)
 {
-       u8 offset = 0, i;
        u32 cnt;
 
+       /* Start clocks */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+       /* Activate serializer(s) */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
 
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
-       for (i = 0; i < mcasp->num_serializer; i++) {
-               if (mcasp->serial_dir[i] == TX_MODE) {
-                       offset = i;
-                       break;
-               }
-       }
-
-       /* wait for TX ready */
+       /* wait for XDATA to be cleared */
        cnt = 0;
-       while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(offset)) &
-                TXSTATE) && (cnt < 100000))
+       while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) &
+                ~XRDATA) && (cnt < 100000))
                cnt++;
 
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
+       /* Release TX state machine */
+       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+       /* Release Frame Sync generator */
+       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
 }
 
 static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream)
@@ -232,6 +233,12 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
 
        mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
        mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+
+       if (mcasp->rxnumevt) {  /* disable FIFO */
+               u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+               mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+       }
 }
 
 static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
@@ -247,27 +254,22 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
 
        mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
        mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+
+       if (mcasp->txnumevt) {  /* disable FIFO */
+               u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+               mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+       }
 }
 
 static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream)
 {
-       u32 reg;
-
        mcasp->streams--;
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               if (mcasp->txnumevt) {  /* disable FIFO */
-                       reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-                       mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-               }
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
                mcasp_stop_tx(mcasp);
-       } else {
-               if (mcasp->rxnumevt) {  /* disable FIFO */
-                       reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-                       mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-               }
+       else
                mcasp_stop_rx(mcasp);
-       }
 }
 
 static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
@@ -874,14 +876,24 @@ static int davinci_mcasp_suspend(struct snd_soc_dai *dai)
 {
        struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
        struct davinci_mcasp_context *context = &mcasp->context;
+       u32 reg;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(context_regs); i++)
+               context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]);
+
+       if (mcasp->txnumevt) {
+               reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+               context->afifo_regs[0] = mcasp_get_reg(mcasp, reg);
+       }
+       if (mcasp->rxnumevt) {
+               reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+               context->afifo_regs[1] = mcasp_get_reg(mcasp, reg);
+       }
 
-       context->txfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG);
-       context->rxfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
-       context->txfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMT_REG);
-       context->rxfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMT_REG);
-       context->aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
-       context->aclkrctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG);
-       context->pdir = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG);
+       for (i = 0; i < mcasp->num_serializer; i++)
+               context->xrsr_regs[i] = mcasp_get_reg(mcasp,
+                                               DAVINCI_MCASP_XRSRCTL_REG(i));
 
        return 0;
 }
@@ -890,14 +902,24 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai)
 {
        struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
        struct davinci_mcasp_context *context = &mcasp->context;
+       u32 reg;
+       int i;
 
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, context->txfmtctl);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG, context->rxfmtctl);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMT_REG, context->txfmt);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMT_REG, context->rxfmt);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, context->aclkxctl);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, context->aclkrctl);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_PDIR_REG, context->pdir);
+       for (i = 0; i < ARRAY_SIZE(context_regs); i++)
+               mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]);
+
+       if (mcasp->txnumevt) {
+               reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+               mcasp_set_reg(mcasp, reg, context->afifo_regs[0]);
+       }
+       if (mcasp->rxnumevt) {
+               reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+               mcasp_set_reg(mcasp, reg, context->afifo_regs[1]);
+       }
+
+       for (i = 0; i < mcasp->num_serializer; i++)
+               mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+                             context->xrsr_regs[i]);
 
        return 0;
 }
@@ -1216,6 +1238,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        mcasp->op_mode = pdata->op_mode;
        mcasp->tdm_slots = pdata->tdm_slots;
        mcasp->num_serializer = pdata->num_serializer;
+#ifdef CONFIG_PM_SLEEP
+       mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev,
+                                       sizeof(u32) * mcasp->num_serializer,
+                                       GFP_KERNEL);
+#endif
        mcasp->serial_dir = pdata->serial_dir;
        mcasp->version = pdata->version;
        mcasp->txnumevt = pdata->txnumevt;