These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / sound / soc / fsl / fsl_spdif.c
index 91eb3ae..3d59bb6 100644 (file)
@@ -108,6 +108,8 @@ struct fsl_spdif_priv {
        struct clk *sysclk;
        struct snd_dmaengine_dai_dma_data dma_params_tx;
        struct snd_dmaengine_dai_dma_data dma_params_rx;
+       /* regcache for SRPC */
+       u32 regcache_srpc;
 };
 
 /* DPLL locked and lock loss interrupt handler */
@@ -300,6 +302,8 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
        struct regmap *regmap = spdif_priv->regmap;
        u32 val, cycle = 1000;
 
+       regcache_cache_bypass(regmap, true);
+
        regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
 
        /*
@@ -310,6 +314,10 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
                regmap_read(regmap, REG_SPDIF_SCR, &val);
        } while ((val & SCR_SOFT_RESET) && cycle--);
 
+       regcache_cache_bypass(regmap, false);
+       regcache_mark_dirty(regmap);
+       regcache_sync(regmap);
+
        if (cycle)
                return 0;
        else
@@ -417,11 +425,9 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
        if (clk != STC_TXCLK_SPDIF_ROOT)
                goto clk_set_bypass;
 
-       /*
-        * The S/PDIF block needs a clock of 64 * fs * txclk_df.
-        * So request 64 * fs * (txclk_df + 1) to get rounded.
-        */
-       ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1));
+       /* The S/PDIF block needs a clock of 64 * fs * txclk_df */
+       ret = clk_set_rate(spdif_priv->txclk[rate],
+                          64 * sample_rate * txclk_df);
        if (ret) {
                dev_err(&pdev->dev, "failed to set tx clock rate\n");
                return ret;
@@ -456,7 +462,8 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
        struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
        struct platform_device *pdev = spdif_priv->pdev;
        struct regmap *regmap = spdif_priv->regmap;
-       u32 scr, mask, i;
+       u32 scr, mask;
+       int i;
        int ret;
 
        /* Reset module and interrupts only for first initialization */
@@ -484,13 +491,18 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
                mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
                        SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
                        SCR_TXFIFO_FSEL_MASK;
-               for (i = 0; i < SPDIF_TXRATE_MAX; i++)
-                       clk_prepare_enable(spdif_priv->txclk[i]);
+               for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+                       ret = clk_prepare_enable(spdif_priv->txclk[i]);
+                       if (ret)
+                               goto disable_txclk;
+               }
        } else {
                scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
                mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
                        SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
-               clk_prepare_enable(spdif_priv->rxclk);
+               ret = clk_prepare_enable(spdif_priv->rxclk);
+               if (ret)
+                       goto err;
        }
        regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
 
@@ -499,6 +511,9 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
 
        return 0;
 
+disable_txclk:
+       for (i--; i >= 0; i--)
+               clk_disable_unprepare(spdif_priv->txclk[i]);
 err:
        clk_disable_unprepare(spdif_priv->coreclk);
 
@@ -709,7 +724,7 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol,
        return ret;
 }
 
-/* Q-subcode infomation. The byte size is SPDIF_UBITS_SIZE/8 */
+/* Q-subcode information. The byte size is SPDIF_UBITS_SIZE/8 */
 static int fsl_spdif_qinfo(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_info *uinfo)
 {
@@ -741,7 +756,7 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
        return ret;
 }
 
-/* Valid bit infomation */
+/* Valid bit information */
 static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_info *uinfo)
 {
@@ -769,7 +784,7 @@ static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-/* DPLL lock infomation */
+/* DPLL lock information */
 static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_info *uinfo)
 {
@@ -990,6 +1005,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = {
 };
 
 /* FSL SPDIF REGMAP */
+static const struct reg_default fsl_spdif_reg_defaults[] = {
+       {0x0,  0x00000400},
+       {0x4,  0x00000000},
+       {0xc,  0x00000000},
+       {0x34, 0x00000000},
+       {0x38, 0x00000000},
+       {0x50, 0x00020f00},
+};
 
 static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
 {
@@ -1015,6 +1038,26 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
        }
 }
 
+static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case REG_SPDIF_SRPC:
+       case REG_SPDIF_SIS:
+       case REG_SPDIF_SRL:
+       case REG_SPDIF_SRR:
+       case REG_SPDIF_SRCSH:
+       case REG_SPDIF_SRCSL:
+       case REG_SPDIF_SRU:
+       case REG_SPDIF_SRQ:
+       case REG_SPDIF_STL:
+       case REG_SPDIF_STR:
+       case REG_SPDIF_SRFM:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -1040,8 +1083,12 @@ static const struct regmap_config fsl_spdif_regmap_config = {
        .val_bits = 32,
 
        .max_register = REG_SPDIF_STC,
+       .reg_defaults = fsl_spdif_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(fsl_spdif_reg_defaults),
        .readable_reg = fsl_spdif_readable_reg,
+       .volatile_reg = fsl_spdif_volatile_reg,
        .writeable_reg = fsl_spdif_writeable_reg,
+       .cache_type = REGCACHE_RBTREE,
 };
 
 static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
@@ -1060,7 +1107,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
 
        for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
                for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
-                       rate_ideal = rate[index] * (txclk_df + 1) * 64;
+                       rate_ideal = rate[index] * txclk_df * 64;
                        if (round)
                                rate_actual = clk_round_rate(clk, rate_ideal);
                        else
@@ -1257,13 +1304,45 @@ static int fsl_spdif_probe(struct platform_device *pdev)
                return ret;
        }
 
-       ret = imx_pcm_dma_init(pdev);
+       ret = imx_pcm_dma_init(pdev, IMX_SPDIF_DMABUF_SIZE);
        if (ret)
                dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret);
 
        return ret;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int fsl_spdif_suspend(struct device *dev)
+{
+       struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+
+       regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,
+                       &spdif_priv->regcache_srpc);
+
+       regcache_cache_only(spdif_priv->regmap, true);
+       regcache_mark_dirty(spdif_priv->regmap);
+
+       return 0;
+}
+
+static int fsl_spdif_resume(struct device *dev)
+{
+       struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+
+       regcache_cache_only(spdif_priv->regmap, false);
+
+       regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC,
+                       SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
+                       spdif_priv->regcache_srpc);
+
+       return regcache_sync(spdif_priv->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_spdif_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume)
+};
+
 static const struct of_device_id fsl_spdif_dt_ids[] = {
        { .compatible = "fsl,imx35-spdif", },
        { .compatible = "fsl,vf610-spdif", },
@@ -1275,6 +1354,7 @@ static struct platform_driver fsl_spdif_driver = {
        .driver = {
                .name = "fsl-spdif-dai",
                .of_match_table = fsl_spdif_dt_ids,
+               .pm = &fsl_spdif_pm,
        },
        .probe = fsl_spdif_probe,
 };