]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
Merge remote-tracking branches 'asoc/topic/dwc', 'asoc/topic/es8328', 'asoc/topic...
authorMark Brown <broonie@kernel.org>
Mon, 26 Oct 2015 02:16:03 +0000 (11:16 +0900)
committerMark Brown <broonie@kernel.org>
Mon, 26 Oct 2015 02:16:03 +0000 (11:16 +0900)
Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
include/sound/designware_i2s.h
sound/soc/codecs/es8328.c
sound/soc/dwc/designware_i2s.c
sound/soc/fsl/fsl-asoc-card.c
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/fsl_spdif.c
sound/soc/fsl/fsl_ssi.c

index a96774c194c8be9f242c1db4ca0398283e10a203..ce55c0a6f7578ee192a207a6b247095d605072b8 100644 (file)
@@ -13,13 +13,15 @@ So having this generic sound card allows all Freescale SoC users to benefit
 from the simplification of a new card support and the capability of the wide
 sample rates support through ASRC.
 
-Note: The card is initially designed for those sound cards who use I2S and
-      PCM DAI formats. However, it'll be also possible to support those non
-      I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long
-      as the driver has been properly upgraded.
+Note: The card is initially designed for those sound cards who use AC'97, I2S
+      and PCM DAI formats. However, it'll be also possible to support those non
+      AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as
+      long as the driver has been properly upgraded.
 
 
 The compatible list for this generic sound card currently:
+ "fsl,imx-audio-ac97"
+
  "fsl,imx-audio-cs42888"
 
  "fsl,imx-audio-wm8962"
index 3a8fca9409a7a0c1fad472155854b26f0a122487..8966ba7c962951de6fd1f972c092266a38b823d2 100644 (file)
@@ -38,6 +38,8 @@ struct i2s_clk_config_data {
 struct i2s_platform_data {
        #define DWC_I2S_PLAY    (1 << 0)
        #define DWC_I2S_RECORD  (1 << 1)
+       #define DW_I2S_SLAVE    (1 << 2)
+       #define DW_I2S_MASTER   (1 << 3)
        unsigned int cap;
        int channel;
        u32 snd_fmts;
index 6a091016e0fc767c2465cfd5c8eaf822d09d2ea3..969e337dc17c131413e43f666edd5b299fc11544 100644 (file)
@@ -129,7 +129,7 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
-       int deemph = ucontrol->value.integer.value[0];
+       unsigned int deemph = ucontrol->value.integer.value[0];
        int ret;
 
        if (deemph > 1)
index ba34252b7bba4fdd8d1b7c58a313b490c14aa329..6e6a70c5c2bdba8349e4652e53a672c7fb40d74e 100644 (file)
@@ -282,23 +282,25 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
 
        config->sample_rate = params_rate(params);
 
-       if (dev->i2s_clk_cfg) {
-               ret = dev->i2s_clk_cfg(config);
-               if (ret < 0) {
-                       dev_err(dev->dev, "runtime audio clk config fail\n");
-                       return ret;
-               }
-       } else {
-               u32 bitclk = config->sample_rate * config->data_width * 2;
-
-               ret = clk_set_rate(dev->clk, bitclk);
-               if (ret) {
-                       dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
-                               ret);
-                       return ret;
+       if (dev->capability & DW_I2S_MASTER) {
+               if (dev->i2s_clk_cfg) {
+                       ret = dev->i2s_clk_cfg(config);
+                       if (ret < 0) {
+                               dev_err(dev->dev, "runtime audio clk config fail\n");
+                               return ret;
+                       }
+               } else {
+                       u32 bitclk = config->sample_rate *
+                                       config->data_width * 2;
+
+                       ret = clk_set_rate(dev->clk, bitclk);
+                       if (ret) {
+                               dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
+                                       ret);
+                               return ret;
+                       }
                }
        }
-
        return 0;
 }
 
@@ -348,12 +350,43 @@ static int dw_i2s_trigger(struct snd_pcm_substream *substream,
        return ret;
 }
 
+static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+       int ret = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               if (dev->capability & DW_I2S_SLAVE)
+                       ret = 0;
+               else
+                       ret = -EINVAL;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               if (dev->capability & DW_I2S_MASTER)
+                       ret = 0;
+               else
+                       ret = -EINVAL;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+       case SND_SOC_DAIFMT_CBS_CFM:
+               ret = -EINVAL;
+               break;
+       default:
+               dev_dbg(dev->dev, "dwc : Invalid master/slave format\n");
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
 static struct snd_soc_dai_ops dw_i2s_dai_ops = {
        .startup        = dw_i2s_startup,
        .shutdown       = dw_i2s_shutdown,
        .hw_params      = dw_i2s_hw_params,
        .prepare        = dw_i2s_prepare,
        .trigger        = dw_i2s_trigger,
+       .set_fmt        = dw_i2s_set_fmt,
 };
 
 static const struct snd_soc_component_driver dw_i2s_component = {
@@ -366,7 +399,8 @@ static int dw_i2s_suspend(struct snd_soc_dai *dai)
 {
        struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
 
-       clk_disable(dev->clk);
+       if (dev->capability & DW_I2S_MASTER)
+               clk_disable(dev->clk);
        return 0;
 }
 
@@ -374,7 +408,8 @@ static int dw_i2s_resume(struct snd_soc_dai *dai)
 {
        struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
 
-       clk_enable(dev->clk);
+       if (dev->capability & DW_I2S_MASTER)
+               clk_enable(dev->clk);
        return 0;
 }
 
@@ -452,6 +487,14 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
                dw_i2s_dai->capture.rates = rates;
        }
 
+       if (COMP1_MODE_EN(comp1)) {
+               dev_dbg(dev->dev, "designware: i2s master mode supported\n");
+               dev->capability |= DW_I2S_MASTER;
+       } else {
+               dev_dbg(dev->dev, "designware: i2s slave mode supported\n");
+               dev->capability |= DW_I2S_SLAVE;
+       }
+
        return 0;
 }
 
@@ -538,6 +581,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
        struct resource *res;
        int ret;
        struct snd_soc_dai_driver *dw_i2s_dai;
+       const char *clk_id;
 
        dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
        if (!dev) {
@@ -559,32 +603,35 @@ static int dw_i2s_probe(struct platform_device *pdev)
                return PTR_ERR(dev->i2s_base);
 
        dev->dev = &pdev->dev;
+
        if (pdata) {
+               dev->capability = pdata->cap;
+               clk_id = NULL;
                ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
-               if (ret < 0)
-                       return ret;
+       } else {
+               clk_id = "i2sclk";
+               ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+       }
+       if (ret < 0)
+               return ret;
 
-               dev->capability = pdata->cap;
-               dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
-               if (!dev->i2s_clk_cfg) {
-                       dev_err(&pdev->dev, "no clock configure method\n");
-                       return -ENODEV;
+       if (dev->capability & DW_I2S_MASTER) {
+               if (pdata) {
+                       dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
+                       if (!dev->i2s_clk_cfg) {
+                               dev_err(&pdev->dev, "no clock configure method\n");
+                               return -ENODEV;
+                       }
                }
+               dev->clk = devm_clk_get(&pdev->dev, clk_id);
 
-               dev->clk = devm_clk_get(&pdev->dev, NULL);
-       } else {
-               ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+               if (IS_ERR(dev->clk))
+                       return PTR_ERR(dev->clk);
+
+               ret = clk_prepare_enable(dev->clk);
                if (ret < 0)
                        return ret;
-
-               dev->clk = devm_clk_get(&pdev->dev, "i2sclk");
        }
-       if (IS_ERR(dev->clk))
-               return PTR_ERR(dev->clk);
-
-       ret = clk_prepare_enable(dev->clk);
-       if (ret < 0)
-               return ret;
 
        dev_set_drvdata(&pdev->dev, dev);
        ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component,
@@ -606,7 +653,8 @@ static int dw_i2s_probe(struct platform_device *pdev)
        return 0;
 
 err_clk_disable:
-       clk_disable_unprepare(dev->clk);
+       if (dev->capability & DW_I2S_MASTER)
+               clk_disable_unprepare(dev->clk);
        return ret;
 }
 
@@ -614,7 +662,8 @@ static int dw_i2s_remove(struct platform_device *pdev)
 {
        struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
 
-       clk_disable_unprepare(dev->clk);
+       if (dev->capability & DW_I2S_MASTER)
+               clk_disable_unprepare(dev->clk);
 
        return 0;
 }
index 0901d5e20df2823c8cd19393007ed0ed946caf7f..1b05d1c5d9fd50fe32eb9b65f4da1340db9170e5 100644 (file)
@@ -14,6 +14,9 @@
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/of_platform.h>
+#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
+#include <sound/ac97_codec.h>
+#endif
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 
@@ -115,6 +118,11 @@ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
        SND_SOC_DAPM_MIC("DMIC", NULL),
 };
 
+static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv)
+{
+       return priv->dai_fmt == SND_SOC_DAIFMT_AC97;
+}
+
 static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
                                   struct snd_pcm_hw_params *params)
 {
@@ -133,7 +141,9 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
         * set_bias_level(), bypass the remaining settings in hw_params().
         * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS.
         */
-       if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM)
+       if ((priv->card.set_bias_level &&
+            priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) ||
+           fsl_asoc_card_is_ac97(priv))
                return 0;
 
        /* Specific configurations of DAIs starts from here */
@@ -300,7 +310,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
        ext_port--;
 
        /*
-        * Use asynchronous mode (6 wires) for all cases.
+        * Use asynchronous mode (6 wires) for all cases except AC97.
         * If only 4 wires are needed, just set SSI into
         * synchronous mode and enable 4 PADs in IOMUX.
         */
@@ -346,15 +356,30 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
                           IMX_AUDMUX_V2_PTCR_TCLKDIR;
                break;
        default:
-               return -EINVAL;
+               if (!fsl_asoc_card_is_ac97(priv))
+                       return -EINVAL;
+       }
+
+       if (fsl_asoc_card_is_ac97(priv)) {
+               int_ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+                          IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+                          IMX_AUDMUX_V2_PTCR_TCLKDIR;
+               ext_ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+                          IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
+                          IMX_AUDMUX_V2_PTCR_TFSDIR;
        }
 
        /* Asynchronous mode can not be set along with RCLKDIR */
-       ret = imx_audmux_v2_configure_port(int_port, 0,
-                                          IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
-       if (ret) {
-               dev_err(dev, "audmux internal port setup failed\n");
-               return ret;
+       if (!fsl_asoc_card_is_ac97(priv)) {
+               unsigned int pdcr =
+                               IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port);
+
+               ret = imx_audmux_v2_configure_port(int_port, 0,
+                                                  pdcr);
+               if (ret) {
+                       dev_err(dev, "audmux internal port setup failed\n");
+                       return ret;
+               }
        }
 
        ret = imx_audmux_v2_configure_port(int_port, int_ptcr,
@@ -364,11 +389,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
                return ret;
        }
 
-       ret = imx_audmux_v2_configure_port(ext_port, 0,
-                                          IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
-       if (ret) {
-               dev_err(dev, "audmux external port setup failed\n");
-               return ret;
+       if (!fsl_asoc_card_is_ac97(priv)) {
+               unsigned int pdcr =
+                               IMX_AUDMUX_V2_PDCR_RXDSEL(int_port);
+
+               ret = imx_audmux_v2_configure_port(ext_port, 0,
+                                                  pdcr);
+               if (ret) {
+                       dev_err(dev, "audmux external port setup failed\n");
+                       return ret;
+               }
        }
 
        ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr,
@@ -389,6 +419,23 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
        struct device *dev = card->dev;
        int ret;
 
+       if (fsl_asoc_card_is_ac97(priv)) {
+#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
+               struct snd_soc_codec *codec = card->rtd[0].codec;
+               struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+               /*
+                * Use slots 3/4 for S/PDIF so SSI won't try to enable
+                * other slots and send some samples there
+                * due to SLOTREQ bits for S/PDIF received from codec
+                */
+               snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+                                    AC97_EA_SPSA_SLOT_MASK, AC97_EA_SPSA_3_4);
+#endif
+
+               return 0;
+       }
+
        ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
                                     codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
        if (ret) {
@@ -407,7 +454,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
        struct platform_device *cpu_pdev;
        struct fsl_asoc_card_priv *priv;
        struct i2c_client *codec_dev;
-       struct clk *codec_clk;
        const char *codec_dai_name;
        u32 width;
        int ret;
@@ -420,9 +466,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
        /* Give a chance to old DT binding */
        if (!cpu_np)
                cpu_np = of_parse_phandle(np, "ssi-controller", 0);
-       codec_np = of_parse_phandle(np, "audio-codec", 0);
-       if (!cpu_np || !codec_np) {
-               dev_err(&pdev->dev, "phandle missing or invalid\n");
+       if (!cpu_np) {
+               dev_err(&pdev->dev, "CPU phandle missing or invalid\n");
                ret = -EINVAL;
                goto fail;
        }
@@ -434,22 +479,24 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       codec_dev = of_find_i2c_device_by_node(codec_np);
-       if (!codec_dev) {
-               dev_err(&pdev->dev, "failed to find codec platform device\n");
-               ret = -EINVAL;
-               goto fail;
-       }
+       codec_np = of_parse_phandle(np, "audio-codec", 0);
+       if (codec_np)
+               codec_dev = of_find_i2c_device_by_node(codec_np);
+       else
+               codec_dev = NULL;
 
        asrc_np = of_parse_phandle(np, "audio-asrc", 0);
        if (asrc_np)
                asrc_pdev = of_find_device_by_node(asrc_np);
 
        /* Get the MCLK rate only, and leave it controlled by CODEC drivers */
-       codec_clk = clk_get(&codec_dev->dev, NULL);
-       if (!IS_ERR(codec_clk)) {
-               priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
-               clk_put(codec_clk);
+       if (codec_dev) {
+               struct clk *codec_clk = clk_get(&codec_dev->dev, NULL);
+
+               if (!IS_ERR(codec_clk)) {
+                       priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
+                       clk_put(codec_clk);
+               }
        }
 
        /* Default sample rate and format, will be updated in hw_params() */
@@ -486,12 +533,22 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
                priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO;
                priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO;
                priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+       } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) {
+               codec_dai_name = "ac97-hifi";
+               priv->card.set_bias_level = NULL;
+               priv->dai_fmt = SND_SOC_DAIFMT_AC97;
        } else {
                dev_err(&pdev->dev, "unknown Device Tree compatible\n");
                ret = -EINVAL;
                goto asrc_fail;
        }
 
+       if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) {
+               dev_err(&pdev->dev, "failed to find codec device\n");
+               ret = -EINVAL;
+               goto asrc_fail;
+       }
+
        /* Common settings for corresponding Freescale CPU DAI driver */
        if (strstr(cpu_np->name, "ssi")) {
                /* Only SSI needs to configure AUDMUX */
@@ -508,7 +565,9 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
                priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
        }
 
-       sprintf(priv->name, "%s-audio", codec_dev->name);
+       snprintf(priv->name, sizeof(priv->name), "%s-audio",
+                fsl_asoc_card_is_ac97(priv) ? "ac97" :
+                codec_dev->name);
 
        /* Initialize sound card */
        priv->pdev = pdev;
@@ -532,8 +591,26 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
 
        /* Normal DAI Link */
        priv->dai_link[0].cpu_of_node = cpu_np;
-       priv->dai_link[0].codec_of_node = codec_np;
        priv->dai_link[0].codec_dai_name = codec_dai_name;
+
+       if (!fsl_asoc_card_is_ac97(priv))
+               priv->dai_link[0].codec_of_node = codec_np;
+       else {
+               u32 idx;
+
+               ret = of_property_read_u32(cpu_np, "cell-index", &idx);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "cannot get CPU index property\n");
+                       goto asrc_fail;
+               }
+
+               priv->dai_link[0].codec_name =
+                               devm_kasprintf(&pdev->dev, GFP_KERNEL,
+                                              "ac97-codec.%u",
+                                              (unsigned int)idx);
+       }
+
        priv->dai_link[0].platform_of_node = cpu_np;
        priv->dai_link[0].dai_fmt = priv->dai_fmt;
        priv->card.num_links = 1;
@@ -544,6 +621,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
                priv->dai_link[1].platform_of_node = asrc_np;
                priv->dai_link[2].codec_dai_name = codec_dai_name;
                priv->dai_link[2].codec_of_node = codec_np;
+               priv->dai_link[2].codec_name =
+                               priv->dai_link[0].codec_name;
                priv->dai_link[2].cpu_of_node = cpu_np;
                priv->dai_link[2].dai_fmt = priv->dai_fmt;
                priv->card.num_links = 3;
@@ -579,14 +658,15 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
 
 asrc_fail:
        of_node_put(asrc_np);
-fail:
        of_node_put(codec_np);
+fail:
        of_node_put(cpu_np);
 
        return ret;
 }
 
 static const struct of_device_id fsl_asoc_card_dt_ids[] = {
+       { .compatible = "fsl,imx-audio-ac97", },
        { .compatible = "fsl,imx-audio-cs42888", },
        { .compatible = "fsl,imx-audio-sgtl5000", },
        { .compatible = "fsl,imx-audio-wm8962", },
index 837979ea5c922e58cfcf1044c9217cfdc2d01745..59f234e51971e2abfe7597dc7ad783a5972fbc76 100644 (file)
@@ -652,6 +652,24 @@ static const struct snd_soc_component_driver fsl_esai_component = {
        .name           = "fsl-esai",
 };
 
+static const struct reg_default fsl_esai_reg_defaults[] = {
+       {0x8,  0x00000000},
+       {0x10, 0x00000000},
+       {0x18, 0x00000000},
+       {0x98, 0x00000000},
+       {0xd0, 0x00000000},
+       {0xd4, 0x00000000},
+       {0xd8, 0x00000000},
+       {0xdc, 0x00000000},
+       {0xe0, 0x00000000},
+       {0xe4, 0x0000ffff},
+       {0xe8, 0x0000ffff},
+       {0xec, 0x0000ffff},
+       {0xf0, 0x0000ffff},
+       {0xf8, 0x00000000},
+       {0xfc, 0x00000000},
+};
+
 static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -684,6 +702,31 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
        }
 }
 
+static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case REG_ESAI_ETDR:
+       case REG_ESAI_ERDR:
+       case REG_ESAI_ESR:
+       case REG_ESAI_TFSR:
+       case REG_ESAI_RFSR:
+       case REG_ESAI_TX0:
+       case REG_ESAI_TX1:
+       case REG_ESAI_TX2:
+       case REG_ESAI_TX3:
+       case REG_ESAI_TX4:
+       case REG_ESAI_TX5:
+       case REG_ESAI_RX0:
+       case REG_ESAI_RX1:
+       case REG_ESAI_RX2:
+       case REG_ESAI_RX3:
+       case REG_ESAI_SAISR:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -721,8 +764,12 @@ static const struct regmap_config fsl_esai_regmap_config = {
        .val_bits = 32,
 
        .max_register = REG_ESAI_PCRC,
+       .reg_defaults = fsl_esai_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(fsl_esai_reg_defaults),
        .readable_reg = fsl_esai_readable_reg,
+       .volatile_reg = fsl_esai_volatile_reg,
        .writeable_reg = fsl_esai_writeable_reg,
+       .cache_type = REGCACHE_RBTREE,
 };
 
 static int fsl_esai_probe(struct platform_device *pdev)
@@ -853,10 +900,51 @@ static const struct of_device_id fsl_esai_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int fsl_esai_suspend(struct device *dev)
+{
+       struct fsl_esai *esai = dev_get_drvdata(dev);
+
+       regcache_cache_only(esai->regmap, true);
+       regcache_mark_dirty(esai->regmap);
+
+       return 0;
+}
+
+static int fsl_esai_resume(struct device *dev)
+{
+       struct fsl_esai *esai = dev_get_drvdata(dev);
+       int ret;
+
+       regcache_cache_only(esai->regmap, false);
+
+       /* FIFO reset for safety */
+       regmap_update_bits(esai->regmap, REG_ESAI_TFCR,
+                          ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+       regmap_update_bits(esai->regmap, REG_ESAI_RFCR,
+                          ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+
+       ret = regcache_sync(esai->regmap);
+       if (ret)
+               return ret;
+
+       /* FIFO reset done */
+       regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0);
+       regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0);
+
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_esai_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(fsl_esai_suspend, fsl_esai_resume)
+};
+
 static struct platform_driver fsl_esai_driver = {
        .probe = fsl_esai_probe,
        .driver = {
                .name = "fsl-esai-dai",
+               .pm = &fsl_esai_pm_ops,
                .of_match_table = fsl_esai_dt_ids,
        },
 };
index 9366b5a42e1defb9f9d274d142ef9d1fef25104c..a4435f5e3be910447f9168b4708d19140f3c1f4f 100644 (file)
 #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
                       FSL_SAI_CSR_FEIE)
 
-static u32 fsl_sai_rates[] = {
+static const unsigned int fsl_sai_rates[] = {
        8000, 11025, 12000, 16000, 22050,
        24000, 32000, 44100, 48000, 64000,
        88200, 96000, 176400, 192000
 };
 
-static struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
+static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
        .count = ARRAY_SIZE(fsl_sai_rates),
        .list = fsl_sai_rates,
 };
@@ -637,6 +637,8 @@ static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
 static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
+       case FSL_SAI_TCSR:
+       case FSL_SAI_RCSR:
        case FSL_SAI_TFR:
        case FSL_SAI_RFR:
        case FSL_SAI_TDR:
@@ -681,6 +683,7 @@ static const struct regmap_config fsl_sai_regmap_config = {
        .readable_reg = fsl_sai_readable_reg,
        .volatile_reg = fsl_sai_volatile_reg,
        .writeable_reg = fsl_sai_writeable_reg,
+       .cache_type = REGCACHE_FLAT,
 };
 
 static int fsl_sai_probe(struct platform_device *pdev)
@@ -803,10 +806,40 @@ static const struct of_device_id fsl_sai_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, fsl_sai_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int fsl_sai_suspend(struct device *dev)
+{
+       struct fsl_sai *sai = dev_get_drvdata(dev);
+
+       regcache_cache_only(sai->regmap, true);
+       regcache_mark_dirty(sai->regmap);
+
+       return 0;
+}
+
+static int fsl_sai_resume(struct device *dev)
+{
+       struct fsl_sai *sai = dev_get_drvdata(dev);
+
+       regcache_cache_only(sai->regmap, false);
+       regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
+       regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
+       msleep(1);
+       regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
+       regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+       return regcache_sync(sai->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_sai_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume)
+};
+
 static struct platform_driver fsl_sai_driver = {
        .probe = fsl_sai_probe,
        .driver = {
                .name = "fsl-sai",
+               .pm = &fsl_sai_pm_ops,
                .of_match_table = fsl_sai_ids,
        },
 };
index ab729f2426fe3ce18ca19b3adedf0e3fe946b3b3..3d59bb6719f2b6fea0d7ed7137940bac88d99d6b 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
@@ -997,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)
 {
@@ -1022,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) {
@@ -1047,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,
@@ -1271,6 +1311,38 @@ static int fsl_spdif_probe(struct platform_device *pdev)
        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", },
@@ -1282,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,
 };
index 37c5cd4d0e59038ca72c8520bb350de60e42a278..95d2392303eb449bdcf5375872c9edd21aae36c7 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 {
@@ -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;
@@ -1514,10 +1580,46 @@ static int fsl_ssi_remove(struct platform_device *pdev)
        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,