Merge remote-tracking branch 'asoc/topic/mtk' into asoc-next
[cascardo/linux.git] / sound / soc / fsl / fsl_ssi.c
index 40dfd8a..632ecc0 100644 (file)
@@ -112,20 +112,6 @@ struct fsl_ssi_rxtx_reg_val {
        struct fsl_ssi_reg_val tx;
 };
 
-static const struct reg_default fsl_ssi_reg_defaults[] = {
-       {CCSR_SSI_SCR,     0x00000000},
-       {CCSR_SSI_SIER,    0x00003003},
-       {CCSR_SSI_STCR,    0x00000200},
-       {CCSR_SSI_SRCR,    0x00000200},
-       {CCSR_SSI_STCCR,   0x00040000},
-       {CCSR_SSI_SRCCR,   0x00040000},
-       {CCSR_SSI_SACNT,   0x00000000},
-       {CCSR_SSI_STMSK,   0x00000000},
-       {CCSR_SSI_SRMSK,   0x00000000},
-       {CCSR_SSI_SACCEN,  0x00000000},
-       {CCSR_SSI_SACCDIS, 0x00000000},
-};
-
 static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -151,6 +137,7 @@ static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
        case CCSR_SSI_SACDAT:
        case CCSR_SSI_SATAG:
        case CCSR_SSI_SACCST:
+       case CCSR_SSI_SOR:
                return true;
        default:
                return false;
@@ -190,8 +177,7 @@ static const struct regmap_config fsl_ssi_regconfig = {
        .val_bits = 32,
        .reg_stride = 4,
        .val_format_endian = REGMAP_ENDIAN_NATIVE,
-       .reg_defaults = fsl_ssi_reg_defaults,
-       .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults),
+       .num_reg_defaults_raw = CCSR_SSI_SACCDIS / sizeof(uint32_t) + 1,
        .readable_reg = fsl_ssi_readable_reg,
        .volatile_reg = fsl_ssi_volatile_reg,
        .precious_reg = fsl_ssi_precious_reg,
@@ -201,6 +187,7 @@ static const struct regmap_config fsl_ssi_regconfig = {
 
 struct fsl_ssi_soc_data {
        bool imx;
+       bool imx21regs; /* imx21-class SSI - no SACC{ST,EN,DIS} regs */
        bool offline_config;
        u32 sisr_write_mask;
 };
@@ -275,6 +262,7 @@ struct fsl_ssi_private {
        struct fsl_ssi_dbg dbg_stats;
 
        const struct fsl_ssi_soc_data *soc;
+       struct device *dev;
 };
 
 /*
@@ -303,6 +291,7 @@ static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = {
 
 static struct fsl_ssi_soc_data fsl_ssi_imx21 = {
        .imx = true,
+       .imx21regs = true,
        .offline_config = true,
        .sisr_write_mask = 0,
 };
@@ -412,6 +401,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
        }
 }
 
+/*
+ * Clear RX or TX FIFO to remove samples from the previous
+ * stream session which may be still present in the FIFO and
+ * may introduce bad samples and/or channel slipping.
+ *
+ * Note: The SOR is not documented in recent IMX datasheet, but
+ * is described in IMX51 reference manual at section 56.3.3.15.
+ */
+static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private,
+               bool is_rx)
+{
+       if (is_rx) {
+               regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
+                       CCSR_SSI_SOR_RX_CLR, CCSR_SSI_SOR_RX_CLR);
+       } else {
+               regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
+                       CCSR_SSI_SOR_TX_CLR, CCSR_SSI_SOR_TX_CLR);
+       }
+}
+
 /*
  * Calculate the bits that have to be disabled for the current stream that is
  * getting disabled. This keeps the bits enabled that are necessary for the
@@ -487,9 +496,11 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
         * (online configuration)
         */
        if (enable) {
-               regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
+               fsl_ssi_fifo_clear(ssi_private, vals->scr & CCSR_SSI_SCR_RE);
+
                regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr);
                regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr);
+               regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
        } else {
                u32 sier;
                u32 srcr;
@@ -519,8 +530,40 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
 
 config_done:
        /* Enabling of subunits is done after configuration */
-       if (enable)
+       if (enable) {
+               if (ssi_private->use_dma && (vals->scr & CCSR_SSI_SCR_TE)) {
+                       /*
+                        * Be sure the Tx FIFO is filled when TE is set.
+                        * Otherwise, there are some chances to start the
+                        * playback with some void samples inserted first,
+                        * generating a channel slip.
+                        *
+                        * First, SSIEN must be set, to let the FIFO be filled.
+                        *
+                        * Notes:
+                        * - Limit this fix to the DMA case until FIQ cases can
+                        *   be tested.
+                        * - Limit the length of the busy loop to not lock the
+                        *   system too long, even if 1-2 loops are sufficient
+                        *   in general.
+                        */
+                       int i;
+                       int max_loop = 100;
+                       regmap_update_bits(regs, CCSR_SSI_SCR,
+                                       CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN);
+                       for (i = 0; i < max_loop; i++) {
+                               u32 sfcsr;
+                               regmap_read(regs, CCSR_SSI_SFCSR, &sfcsr);
+                               if (CCSR_SSI_SFCSR_TFCNT0(sfcsr))
+                                       break;
+                       }
+                       if (i == max_loop) {
+                               dev_err(ssi_private->dev,
+                                       "Timeout waiting TX FIFO filling\n");
+                       }
+               }
                regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr);
+       }
 }
 
 
@@ -586,8 +629,12 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
         */
        regmap_write(regs, CCSR_SSI_SACNT,
                        CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV);
-       regmap_write(regs, CCSR_SSI_SACCDIS, 0xff);
-       regmap_write(regs, CCSR_SSI_SACCEN, 0x300);
+
+       /* no SACC{ST,EN,DIS} regs on imx21-class SSI */
+       if (!ssi_private->soc->imx21regs) {
+               regmap_write(regs, CCSR_SSI_SACCDIS, 0xff);
+               regmap_write(regs, CCSR_SSI_SACCEN, 0x300);
+       }
 
        /*
         * Enable SSI, Transmit and Receive. AC97 has to communicate with the
@@ -679,6 +726,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
        if (IS_ERR(ssi_private->baudclk))
                return -EINVAL;
 
+       /*
+        * Hardware limitation: The bclk rate must be
+        * never greater than 1/5 IPG clock rate
+        */
+       if (freq * 5 > clk_get_rate(ssi_private->clk)) {
+               dev_err(cpu_dai->dev, "bitclk > ipgclk/5\n");
+               return -EINVAL;
+       }
+
        baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream));
 
        /* It should be already enough to divide clock by setting pm alone */
@@ -695,13 +751,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
                else
                        clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
 
-               /*
-                * Hardware limitation: The bclk rate must be
-                * never greater than 1/5 IPG clock rate
-                */
-               if (clkrate * 5 > clk_get_rate(ssi_private->clk))
-                       continue;
-
                clkrate /= factor;
                afreq = clkrate / (i + 1);
 
@@ -1167,14 +1216,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = {
        .playback = {
                .stream_name = "CPU-Playback",
                .channels_min = 1,
-               .channels_max = 2,
+               .channels_max = 32,
                .rates = FSLSSI_I2S_RATES,
                .formats = FSLSSI_I2S_FORMATS,
        },
        .capture = {
                .stream_name = "CPU-Capture",
                .channels_min = 1,
-               .channels_max = 2,
+               .channels_max = 32,
                .rates = FSLSSI_I2S_RATES,
                .formats = FSLSSI_I2S_FORMATS,
        },
@@ -1397,6 +1446,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        struct resource *res;
        void __iomem *iomem;
        char name[64];
+       struct regmap_config regconfig = fsl_ssi_regconfig;
 
        of_id = of_match_device(fsl_ssi_ids, &pdev->dev);
        if (!of_id || !of_id->data)
@@ -1410,6 +1460,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        }
 
        ssi_private->soc = of_id->data;
+       ssi_private->dev = &pdev->dev;
 
        sprop = of_get_property(np, "fsl,mode", NULL);
        if (sprop) {
@@ -1444,15 +1495,25 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                return PTR_ERR(iomem);
        ssi_private->ssi_phys = res->start;
 
+       if (ssi_private->soc->imx21regs) {
+               /*
+                * According to datasheet imx21-class SSI
+                * don't have SACC{ST,EN,DIS} regs.
+                */
+               regconfig.max_register = CCSR_SSI_SRMSK;
+               regconfig.num_reg_defaults_raw =
+                       CCSR_SSI_SRMSK / sizeof(uint32_t) + 1;
+       }
+
        ret = of_property_match_string(np, "clock-names", "ipg");
        if (ret < 0) {
                ssi_private->has_ipg_clk_name = false;
                ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem,
-                       &fsl_ssi_regconfig);
+                       &regconfig);
        } else {
                ssi_private->has_ipg_clk_name = true;
                ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev,
-                       "ipg", iomem, &fsl_ssi_regconfig);
+                       "ipg", iomem, &regconfig);
        }
        if (IS_ERR(ssi_private->regs)) {
                dev_err(&pdev->dev, "Failed to init register map\n");