These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / sound / soc / fsl / fsl_ssi.c
index 0d48804..95d2392 100644 (file)
@@ -111,12 +111,75 @@ struct fsl_ssi_rxtx_reg_val {
        struct fsl_ssi_reg_val rx;
        struct fsl_ssi_reg_val tx;
 };
+
+static const struct reg_default fsl_ssi_reg_defaults[] = {
+       {0x10, 0x00000000},
+       {0x18, 0x00003003},
+       {0x1c, 0x00000200},
+       {0x20, 0x00000200},
+       {0x24, 0x00040000},
+       {0x28, 0x00040000},
+       {0x38, 0x00000000},
+       {0x48, 0x00000000},
+       {0x4c, 0x00000000},
+       {0x54, 0x00000000},
+       {0x58, 0x00000000},
+};
+
+static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CCSR_SSI_SACCEN:
+       case CCSR_SSI_SACCDIS:
+               return false;
+       default:
+               return true;
+       }
+}
+
+static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CCSR_SSI_STX0:
+       case CCSR_SSI_STX1:
+       case CCSR_SSI_SRX0:
+       case CCSR_SSI_SRX1:
+       case CCSR_SSI_SISR:
+       case CCSR_SSI_SFCSR:
+       case CCSR_SSI_SACADD:
+       case CCSR_SSI_SACDAT:
+       case CCSR_SSI_SATAG:
+       case CCSR_SSI_SACCST:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CCSR_SSI_SRX0:
+       case CCSR_SSI_SRX1:
+       case CCSR_SSI_SACCST:
+               return false;
+       default:
+               return true;
+       }
+}
+
 static const struct regmap_config fsl_ssi_regconfig = {
        .max_register = CCSR_SSI_SACCDIS,
        .reg_bits = 32,
        .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),
+       .readable_reg = fsl_ssi_readable_reg,
+       .volatile_reg = fsl_ssi_volatile_reg,
+       .writeable_reg = fsl_ssi_writeable_reg,
+       .cache_type = REGCACHE_RBTREE,
 };
 
 struct fsl_ssi_soc_data {
@@ -156,7 +219,7 @@ struct fsl_ssi_soc_data {
  *
  * @dbg_stats: Debugging statistics
  *
- * @soc: SoC specifc data
+ * @soc: SoC specific data
  */
 struct fsl_ssi_private {
        struct regmap *regs;
@@ -176,6 +239,9 @@ struct fsl_ssi_private {
        unsigned int baudclk_streams;
        unsigned int bitclk_freq;
 
+       /*regcache for SFCSR*/
+       u32 regcache_sfcsr;
+
        /* DMA params */
        struct snd_dmaengine_dai_dma_data dma_params_tx;
        struct snd_dmaengine_dai_dma_data dma_params_rx;
@@ -249,7 +315,8 @@ MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
 
 static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private)
 {
-       return !!(ssi_private->dai_fmt & SND_SOC_DAIFMT_AC97);
+       return (ssi_private->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
+               SND_SOC_DAIFMT_AC97;
 }
 
 static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private)
@@ -633,7 +700,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
                sub *= 100000;
                do_div(sub, freq);
 
-               if (sub < savesub) {
+               if (sub < savesub && !(i == 0 && psr == 0 && div2 == 0)) {
                        baudrate = tmprate;
                        savesub = sub;
                        pm = i;
@@ -900,14 +967,16 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
                scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
                break;
        default:
-               return -EINVAL;
+               if (!fsl_ssi_is_ac97(ssi_private))
+                       return -EINVAL;
        }
 
        stcr |= strcr;
        srcr |= strcr;
 
-       if (ssi_private->cpu_dai_drv.symmetric_rates) {
-               /* Need to clear RXDIR when using SYNC mode */
+       if (ssi_private->cpu_dai_drv.symmetric_rates
+                       || fsl_ssi_is_ac97(ssi_private)) {
+               /* Need to clear RXDIR when using SYNC or AC97 mode */
                srcr &= ~CCSR_SSI_SRCR_RXDIR;
                scr |= CCSR_SSI_SCR_SYN;
        }
@@ -945,7 +1014,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
                                CCSR_SSI_SCR_TCH_EN);
        }
 
-       if (fmt & SND_SOC_DAIFMT_AC97)
+       if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97)
                fsl_ssi_setup_ac97(ssi_private);
 
        return 0;
@@ -1101,6 +1170,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
 
 static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
        .bus_control = true,
+       .probe = fsl_ssi_dai_probe,
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 2,
@@ -1127,10 +1197,17 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
        struct regmap *regs = fsl_ac97_data->regs;
        unsigned int lreg;
        unsigned int lval;
+       int ret;
 
        if (reg > 0x7f)
                return;
 
+       ret = clk_prepare_enable(fsl_ac97_data->clk);
+       if (ret) {
+               pr_err("ac97 write clk_prepare_enable failed: %d\n",
+                       ret);
+               return;
+       }
 
        lreg = reg <<  12;
        regmap_write(regs, CCSR_SSI_SACADD, lreg);
@@ -1141,6 +1218,8 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
        regmap_update_bits(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_RDWR_MASK,
                        CCSR_SSI_SACNT_WR);
        udelay(100);
+
+       clk_disable_unprepare(fsl_ac97_data->clk);
 }
 
 static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
@@ -1151,6 +1230,14 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
        unsigned short val = -1;
        u32 reg_val;
        unsigned int lreg;
+       int ret;
+
+       ret = clk_prepare_enable(fsl_ac97_data->clk);
+       if (ret) {
+               pr_err("ac97 read clk_prepare_enable failed: %d\n",
+                       ret);
+               return -1;
+       }
 
        lreg = (reg & 0x7f) <<  12;
        regmap_write(regs, CCSR_SSI_SACADD, lreg);
@@ -1162,6 +1249,8 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
        regmap_read(regs, CCSR_SSI_SACDAT, &reg_val);
        val = (reg_val >> 4) & 0xffff;
 
+       clk_disable_unprepare(fsl_ac97_data->clk);
+
        return val;
 }
 
@@ -1210,7 +1299,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
                }
        }
 
-       /* For those SLAVE implementations, we ingore non-baudclk cases
+       /* For those SLAVE implementations, we ignore non-baudclk cases
         * and, instead, abandon MASTER mode that needs baud clock.
         */
        ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
@@ -1257,7 +1346,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
                if (ret)
                        goto error_pcm;
        } else {
-               ret = imx_pcm_dma_init(pdev);
+               ret = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE);
                if (ret)
                        goto error_pcm;
        }
@@ -1292,13 +1381,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        void __iomem *iomem;
        char name[64];
 
-       /* SSIs that are not connected on the board should have a
-        *      status = "disabled"
-        * property in their device tree nodes.
-        */
-       if (!of_device_is_available(np))
-               return -ENODEV;
-
        of_id = of_match_device(fsl_ssi_ids, &pdev->dev);
        if (!of_id || !of_id->data)
                return -EINVAL;
@@ -1327,7 +1409,11 @@ static int fsl_ssi_probe(struct platform_device *pdev)
 
                fsl_ac97_data = ssi_private;
 
-               snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
+               ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
+               if (ret) {
+                       dev_err(&pdev->dev, "could not set AC'97 ops\n");
+                       return ret;
+               }
        } else {
                /* Initialize this copy of the CPU DAI driver structure */
                memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
@@ -1364,7 +1450,9 @@ static int fsl_ssi_probe(struct platform_device *pdev)
 
        /* Are the RX and the TX clocks locked? */
        if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) {
-               ssi_private->cpu_dai_drv.symmetric_rates = 1;
+               if (!fsl_ssi_is_ac97(ssi_private))
+                       ssi_private->cpu_dai_drv.symmetric_rates = 1;
+
                ssi_private->cpu_dai_drv.symmetric_channels = 1;
                ssi_private->cpu_dai_drv.symmetric_samplebits = 1;
        }
@@ -1441,6 +1529,27 @@ done:
                _fsl_ssi_set_dai_fmt(&pdev->dev, ssi_private,
                                     ssi_private->dai_fmt);
 
+       if (fsl_ssi_is_ac97(ssi_private)) {
+               u32 ssi_idx;
+
+               ret = of_property_read_u32(np, "cell-index", &ssi_idx);
+               if (ret) {
+                       dev_err(&pdev->dev, "cannot get SSI index property\n");
+                       goto error_sound_card;
+               }
+
+               ssi_private->pdev =
+                       platform_device_register_data(NULL,
+                                       "ac97-codec", ssi_idx, NULL, 0);
+               if (IS_ERR(ssi_private->pdev)) {
+                       ret = PTR_ERR(ssi_private->pdev);
+                       dev_err(&pdev->dev,
+                               "failed to register AC97 codec platform: %d\n",
+                               ret);
+                       goto error_sound_card;
+               }
+       }
+
        return 0;
 
 error_sound_card:
@@ -1465,13 +1574,52 @@ static int fsl_ssi_remove(struct platform_device *pdev)
        if (ssi_private->soc->imx)
                fsl_ssi_imx_clean(pdev, ssi_private);
 
+       if (fsl_ssi_is_ac97(ssi_private))
+               snd_soc_set_ac97_ops(NULL);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_ssi_suspend(struct device *dev)
+{
+       struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
+       struct regmap *regs = ssi_private->regs;
+
+       regmap_read(regs, CCSR_SSI_SFCSR,
+                       &ssi_private->regcache_sfcsr);
+
+       regcache_cache_only(regs, true);
+       regcache_mark_dirty(regs);
+
        return 0;
 }
 
+static int fsl_ssi_resume(struct device *dev)
+{
+       struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
+       struct regmap *regs = ssi_private->regs;
+
+       regcache_cache_only(regs, false);
+
+       regmap_update_bits(regs, CCSR_SSI_SFCSR,
+                       CCSR_SSI_SFCSR_RFWM1_MASK | CCSR_SSI_SFCSR_TFWM1_MASK |
+                       CCSR_SSI_SFCSR_RFWM0_MASK | CCSR_SSI_SFCSR_TFWM0_MASK,
+                       ssi_private->regcache_sfcsr);
+
+       return regcache_sync(regs);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_ssi_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
+};
+
 static struct platform_driver fsl_ssi_driver = {
        .driver = {
                .name = "fsl-ssi-dai",
                .of_match_table = fsl_ssi_ids,
+               .pm = &fsl_ssi_pm,
        },
        .probe = fsl_ssi_probe,
        .remove = fsl_ssi_remove,