]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge remote-tracking branches 'asoc/topic/dpcm', 'asoc/topic/dwc', 'asoc/topic/fsl...
authorMark Brown <broonie@kernel.org>
Wed, 23 Dec 2015 00:23:40 +0000 (00:23 +0000)
committerMark Brown <broonie@kernel.org>
Wed, 23 Dec 2015 00:23:40 +0000 (00:23 +0000)
1  2  3  4  5  6 
sound/soc/fsl/fsl-asoc-card.c
sound/soc/fsl/fsl_asrc.c
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_sai.c
sound/soc/soc-pcm.c

index c63d89da51f1daccf291d406e3b6ba477283a1ad,1b05d1c5d9fd50fe32eb9b65f4da1340db9170e5,1b05d1c5d9fd50fe32eb9b65f4da1340db9170e5,1b05d1c5d9fd50fe32eb9b65f4da1340db9170e5,6fb3aed91b447d96b88fe55fa036da2a2da0bdb3,1b05d1c5d9fd50fe32eb9b65f4da1340db9170e5..562b3bd22d9ac1f86de0a7d08fe64d8145445db8
@@@@@@@ -107,6 -107,6 -107,6 -107,6 -107,13 -107,6 +107,13 @@@@@@@ static const struct snd_soc_dapm_route 
        {"CPU-Capture",  NULL, "Capture"},
      };
      
++++ +static const struct snd_soc_dapm_route audio_map_ac97[] = {
++++ +  {"AC97 Playback",  NULL, "ASRC-Playback"},
++++ +  {"Playback",  NULL, "AC97 Playback"},
++++ +  {"ASRC-Capture",  NULL, "AC97 Capture"},
++++ +  {"AC97 Capture",  NULL, "Capture"},
++++ +};
++++ +
      /* Add all possible widgets into here without being redundant */
      static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
        SND_SOC_DAPM_LINE("Line Out Jack", NULL),
@@@@@@@ -222,15 -222,12 -222,12 -222,12 -229,12 -222,12 +229,15 @@@@@@@ static int fsl_asoc_card_set_bias_level
                                        enum snd_soc_bias_level level)
      {
        struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
 -----  struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
 +++++  struct snd_soc_pcm_runtime *rtd;
 +++++  struct snd_soc_dai *codec_dai;
        struct codec_priv *codec_priv = &priv->codec_priv;
        struct device *dev = card->dev;
        unsigned int pll_out;
        int ret;
      
 +++++  rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 +++++  codec_dai = rtd->codec_dai;
        if (dapm->dev != codec_dai->dev)
                return 0;
      
@@@@@@@ -417,16 -414,14 -414,14 -414,14 -421,14 -414,14 +424,16 @@@@@@@ static int fsl_asoc_card_audmux_init(st
      static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
      {
        struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
 -----  struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
 +++++  struct snd_soc_pcm_runtime *rtd = list_first_entry(
 +++++                  &card->rtd_list, struct snd_soc_pcm_runtime, list);
 +++++  struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct codec_priv *codec_priv = &priv->codec_priv;
        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_soc_codec *codec = rtd->codec;
                struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
      
                /*
@@@@@@@ -579,7 -574,7 -574,7 -574,7 -581,8 -574,7 +586,8 @@@@@@@ static int fsl_asoc_card_probe(struct p
        priv->card.dev = &pdev->dev;
        priv->card.name = priv->name;
        priv->card.dai_link = priv->dai_link;
---- -  priv->card.dapm_routes = audio_map;
++++ +  priv->card.dapm_routes = fsl_asoc_card_is_ac97(priv) ?
++++ +                           audio_map_ac97 : audio_map;
        priv->card.late_probe = fsl_asoc_card_late_probe;
        priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
        priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
diff --combined sound/soc/fsl/fsl_asrc.c
index 9f087d4f73ed775bdfaf9bad8c562b26a657b47d,9f087d4f73ed775bdfaf9bad8c562b26a657b47d,9f087d4f73ed775bdfaf9bad8c562b26a657b47d,6d0636605ed25467e00ecc82f44b9f9cb5d28db1,7b811485a8e5e38674facb28c17836e622c3f2da,9f087d4f73ed775bdfaf9bad8c562b26a657b47d..dd1263b95dc78f5e882c541c14efddeb20e05ef6
        dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
      
      /* Sample rates are aligned with that defined in pcm.h file */
---- -static const u8 process_option[][8][2] = {
---- -  /* 32kHz 44.1kHz 48kHz   64kHz   88.2kHz 96kHz   176kHz  192kHz */
---- -  {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},      /* 5512Hz */
---- -  {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},      /* 8kHz */
---- -  {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},      /* 11025Hz */
---- -  {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},      /* 16kHz */
---- -  {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},      /* 22050Hz */
---- -  {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},},      /* 32kHz */
---- -  {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},},      /* 44.1kHz */
---- -  {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},},      /* 48kHz */
---- -  {{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},},      /* 64kHz */
---- -  {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},      /* 88.2kHz */
---- -  {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},      /* 96kHz */
---- -  {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},},      /* 176kHz */
---- -  {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},},      /* 192kHz */
++++ +static const u8 process_option[][12][2] = {
++++ +  /* 8kHz 11.025kHz 16kHz 22.05kHz 32kHz 44.1kHz 48kHz   64kHz   88.2kHz 96kHz   176kHz  192kHz */
++++ +  {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},      /* 5512Hz */
++++ +  {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},      /* 8kHz */
++++ +  {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},      /* 11025Hz */
++++ +  {{1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},      /* 16kHz */
++++ +  {{1, 2}, {1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},      /* 22050Hz */
++++ +  {{1, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},},      /* 32kHz */
++++ +  {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},},      /* 44.1kHz */
++++ +  {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},},      /* 48kHz */
++++ +  {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},},      /* 64kHz */
++++ +  {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},      /* 88.2kHz */
++++ +  {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},      /* 96kHz */
++++ +  {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},},      /* 176kHz */
++++ +  {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},},      /* 192kHz */
      };
      
      /* Corresponding to process_option */
@@@@@@@ -55,7 -55,7 -55,7 -55,7 -55,7 -55,7 +55,7 @@@@@@@ static int supported_input_rate[] = 
      };
      
      static int supported_asrc_rate[] = {
---- -  32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
++++ +  8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
      };
      
      /**
@@@@@@@ -286,6 -286,6 -286,6 -286,6 -286,13 -286,6 +286,13 @@@@@@@ static int fsl_asrc_config_pair(struct 
                return -EINVAL;
        }
      
++++ +  if ((outrate > 8000 && outrate < 30000) &&
++++ +      (outrate/inrate > 24 || inrate/outrate > 8)) {
++++ +          pair_err("exceed supported ratio range [1/24, 8] for \
++++ +                          inrate/outrate: %d/%d\n", inrate, outrate);
++++ +          return -EINVAL;
++++ +  }
++++ +
        /* Validate input and output clock sources */
        clk_index[IN] = clk_map[IN][config->inclk];
        clk_index[OUT] = clk_map[OUT][config->outclk];
@@@@@@@ -447,7 -447,7 -447,7 -447,7 -454,7 -447,7 +454,7 @@@@@@@ static int fsl_asrc_dai_hw_params(struc
                                  struct snd_soc_dai *dai)
      {
        struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
--- --  int width = snd_pcm_format_width(params_format(params));
+++ ++  int width = params_width(params);
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct fsl_asrc_pair *pair = runtime->private_data;
        unsigned int channels = params_channels(params);
@@@@@@@ -859,6 -859,6 -859,6 -859,6 -866,10 -859,6 +866,10 @@@@@@@ static int fsl_asrc_probe(struct platfo
                return PTR_ERR(asrc_priv->ipg_clk);
        }
      
++++ +  asrc_priv->spba_clk = devm_clk_get(&pdev->dev, "spba");
++++ +  if (IS_ERR(asrc_priv->spba_clk))
++++ +          dev_warn(&pdev->dev, "failed to get spba clock\n");
++++ +
        for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
                sprintf(tmp, "asrck_%x", i);
                asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
@@@@@@@ -939,6 -939,6 -939,6 -939,6 -950,11 -939,6 +950,11 @@@@@@@ static int fsl_asrc_runtime_resume(stru
        ret = clk_prepare_enable(asrc_priv->ipg_clk);
        if (ret)
                goto disable_mem_clk;
++++ +  if (!IS_ERR(asrc_priv->spba_clk)) {
++++ +          ret = clk_prepare_enable(asrc_priv->spba_clk);
++++ +          if (ret)
++++ +                  goto disable_ipg_clk;
++++ +  }
        for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
                ret = clk_prepare_enable(asrc_priv->asrck_clk[i]);
                if (ret)
      disable_asrck_clk:
        for (i--; i >= 0; i--)
                clk_disable_unprepare(asrc_priv->asrck_clk[i]);
++++ +  if (!IS_ERR(asrc_priv->spba_clk))
++++ +          clk_disable_unprepare(asrc_priv->spba_clk);
++++ +disable_ipg_clk:
        clk_disable_unprepare(asrc_priv->ipg_clk);
      disable_mem_clk:
        clk_disable_unprepare(asrc_priv->mem_clk);
@@@@@@@ -963,6 -963,6 -963,6 -963,6 -982,8 -963,6 +982,8 @@@@@@@ static int fsl_asrc_runtime_suspend(str
      
        for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
                clk_disable_unprepare(asrc_priv->asrck_clk[i]);
++++ +  if (!IS_ERR(asrc_priv->spba_clk))
++++ +          clk_disable_unprepare(asrc_priv->spba_clk);
        clk_disable_unprepare(asrc_priv->ipg_clk);
        clk_disable_unprepare(asrc_priv->mem_clk);
      
diff --combined sound/soc/fsl/fsl_esai.c
index 59f234e51971e2abfe7597dc7ad783a5972fbc76,59f234e51971e2abfe7597dc7ad783a5972fbc76,59f234e51971e2abfe7597dc7ad783a5972fbc76,45d4319b2079d1bf83c7bcfada850061809b0396,59f234e51971e2abfe7597dc7ad783a5972fbc76,6746f76a8c7fb38dc9c903184f014a3a54e73ebd..26a90e12ede44d613e5fa6094c0144aac279a12d
       * @coreclk: clock source to access register
       * @extalclk: esai clock source to derive HCK, SCK and FS
       * @fsysclk: system clock source to derive HCK, SCK and FS
+++++  * @spbaclk: SPBA clock (optional, depending on SoC design)
       * @fifo_depth: depth of tx/rx FIFO
       * @slot_width: width of each DAI slot
       * @slots: number of slots
@@@@@@@ -54,6 -54,6 -54,6 -54,6 -54,6 -55,7 +55,7 @@@@@@@ struct fsl_esai 
        struct clk *coreclk;
        struct clk *extalclk;
        struct clk *fsysclk;
+++++   struct clk *spbaclk;
        u32 fifo_depth;
        u32 slot_width;
        u32 slots;
@@@@@@@ -469,6 -469,6 -469,6 -469,6 -469,6 -471,11 +471,11 @@@@@@@ static int fsl_esai_startup(struct snd_
        ret = clk_prepare_enable(esai_priv->coreclk);
        if (ret)
                return ret;
+++++   if (!IS_ERR(esai_priv->spbaclk)) {
+++++           ret = clk_prepare_enable(esai_priv->spbaclk);
+++++           if (ret)
+++++                   goto err_spbaclk;
+++++   }
        if (!IS_ERR(esai_priv->extalclk)) {
                ret = clk_prepare_enable(esai_priv->extalclk);
                if (ret)
@@@@@@@ -499,6 -499,6 -499,6 -499,6 -499,6 -506,9 +506,9 @@@@@@@ err_fsysclk
        if (!IS_ERR(esai_priv->extalclk))
                clk_disable_unprepare(esai_priv->extalclk);
      err_extalck:
+++++   if (!IS_ERR(esai_priv->spbaclk))
+++++           clk_disable_unprepare(esai_priv->spbaclk);
+++++ err_spbaclk:
        clk_disable_unprepare(esai_priv->coreclk);
      
        return ret;
@@@@@@@ -510,7 -510,7 -510,7 -510,7 -510,7 -520,7 +520,7 @@@@@@@ static int fsl_esai_hw_params(struct sn
      {
        struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
        bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
--- --  u32 width = snd_pcm_format_width(params_format(params));
+++ ++  u32 width = params_width(params);
        u32 channels = params_channels(params);
        u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
        u32 slot_width = width;
@@@@@@@ -564,6 -564,6 -564,6 -564,6 -564,6 -574,8 +574,8 @@@@@@@ static void fsl_esai_shutdown(struct sn
                clk_disable_unprepare(esai_priv->fsysclk);
        if (!IS_ERR(esai_priv->extalclk))
                clk_disable_unprepare(esai_priv->extalclk);
+++++   if (!IS_ERR(esai_priv->spbaclk))
+++++           clk_disable_unprepare(esai_priv->spbaclk);
        clk_disable_unprepare(esai_priv->coreclk);
      }
      
@@@@@@@ -653,21 -653,21 -653,21 -653,28 -653,21 -665,21 +665,28 @@@@@@@ static const struct snd_soc_component_d
      };
      
      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},
+++ ++  {REG_ESAI_ETDR,  0x00000000},
+++ ++  {REG_ESAI_ECR,   0x00000000},
+++ ++  {REG_ESAI_TFCR,  0x00000000},
+++ ++  {REG_ESAI_RFCR,  0x00000000},
+++ ++  {REG_ESAI_TX0,   0x00000000},
+++ ++  {REG_ESAI_TX1,   0x00000000},
+++ ++  {REG_ESAI_TX2,   0x00000000},
+++ ++  {REG_ESAI_TX3,   0x00000000},
+++ ++  {REG_ESAI_TX4,   0x00000000},
+++ ++  {REG_ESAI_TX5,   0x00000000},
+++ ++  {REG_ESAI_TSR,   0x00000000},
+++ ++  {REG_ESAI_SAICR, 0x00000000},
+++ ++  {REG_ESAI_TCR,   0x00000000},
+++ ++  {REG_ESAI_TCCR,  0x00000000},
+++ ++  {REG_ESAI_RCR,   0x00000000},
+++ ++  {REG_ESAI_RCCR,  0x00000000},
+++ ++  {REG_ESAI_TSMA,  0x0000ffff},
+++ ++  {REG_ESAI_TSMB,  0x0000ffff},
+++ ++  {REG_ESAI_RSMA,  0x0000ffff},
+++ ++  {REG_ESAI_RSMB,  0x0000ffff},
+++ ++  {REG_ESAI_PRRC,  0x00000000},
+++ ++  {REG_ESAI_PCRC,  0x00000000},
      };
      
      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:
@@@@@@@ -819,6 -819,6 -819,6 -819,6 -819,6 -831,11 +831,11 @@@@@@@ static int fsl_esai_probe(struct platfo
                dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n",
                                PTR_ERR(esai_priv->fsysclk));
      
+++++   esai_priv->spbaclk = devm_clk_get(&pdev->dev, "spba");
+++++   if (IS_ERR(esai_priv->spbaclk))
+++++           dev_warn(&pdev->dev, "failed to get spba clock: %ld\n",
+++++                           PTR_ERR(esai_priv->spbaclk));
+++++ 
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
diff --combined sound/soc/fsl/fsl_sai.c
index 08b460ba06efc28906fe1094e9e68b7c4e1e66cf,a4435f5e3be910447f9168b4708d19140f3c1f4f,a4435f5e3be910447f9168b4708d19140f3c1f4f,3da278313591d397c2ece80199779a7b86bfbb01,a4435f5e3be910447f9168b4708d19140f3c1f4f,a4435f5e3be910447f9168b4708d19140f3c1f4f..fef264d27fd3378d726a77209647ccc8098ec6a0
                return IRQ_HANDLED;
      }
      
+++ ++static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+++ ++                          u32 rx_mask, int slots, int slot_width)
+++ ++{
+++ ++  struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+++ ++
+++ ++  sai->slots = slots;
+++ ++  sai->slot_width = slot_width;
+++ ++
+++ ++  return 0;
+++ ++}
+++ ++
      static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
                int clk_id, unsigned int freq, int fsl_dir)
      {
@@@@@@@ -354,13 -354,13 -354,13 -365,25 -354,13 -354,13 +365,25 @@@@@@@ static int fsl_sai_set_bclk(struct snd_
                return -EINVAL;
        }
      
--- --  if ((tx && sai->synchronous[TX]) || (!tx && !sai->synchronous[RX])) {
+++ ++  /*
+++ ++   * 1) For Asynchronous mode, we must set RCR2 register for capture, and
+++ ++   *    set TCR2 register for playback.
+++ ++   * 2) For Tx sync with Rx clock, we must set RCR2 register for playback
+++ ++   *    and capture.
+++ ++   * 3) For Rx sync with Tx clock, we must set TCR2 register for playback
+++ ++   *    and capture.
+++ ++   * 4) For Tx and Rx are both Synchronous with another SAI, we just
+++ ++   *    ignore it.
+++ ++   */
+++ ++  if ((sai->synchronous[TX] && !sai->synchronous[RX]) ||
+++ ++      (!tx && !sai->synchronous[RX])) {
                regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
                                   FSL_SAI_CR2_MSEL_MASK,
                                   FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
                regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
                                   FSL_SAI_CR2_DIV_MASK, savediv - 1);
--- --  } else {
+++ ++  } else if ((sai->synchronous[RX] && !sai->synchronous[TX]) ||
+++ ++             (tx && !sai->synchronous[TX])) {
                regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
                                   FSL_SAI_CR2_MSEL_MASK,
                                   FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
@@@@@@@ -381,13 -381,13 -381,13 -404,21 -381,13 -381,13 +404,21 @@@@@@@ static int fsl_sai_hw_params(struct snd
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
        bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        unsigned int channels = params_channels(params);
--- --  u32 word_width = snd_pcm_format_width(params_format(params));
+++ ++  u32 word_width = params_width(params);
        u32 val_cr4 = 0, val_cr5 = 0;
+++ ++  u32 slots = (channels == 1) ? 2 : channels;
+++ ++  u32 slot_width = word_width;
        int ret;
      
+++ ++  if (sai->slots)
+++ ++          slots = sai->slots;
+++ ++
+++ ++  if (sai->slot_width)
+++ ++          slot_width = sai->slot_width;
+++ ++
        if (!sai->is_slave_mode) {
                ret = fsl_sai_set_bclk(cpu_dai, tx,
--- --                  2 * word_width * params_rate(params));
+++ ++                          slots * slot_width * params_rate(params));
                if (ret)
                        return ret;
      
      
                        sai->mclk_streams |= BIT(substream->stream);
                }
--- --
        }
      
        if (!sai->is_dsp_mode)
--- --          val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
+++ ++          val_cr4 |= FSL_SAI_CR4_SYWD(slot_width);
      
--- --  val_cr5 |= FSL_SAI_CR5_WNW(word_width);
--- --  val_cr5 |= FSL_SAI_CR5_W0W(word_width);
+++ ++  val_cr5 |= FSL_SAI_CR5_WNW(slot_width);
+++ ++  val_cr5 |= FSL_SAI_CR5_W0W(slot_width);
      
        if (sai->is_lsb_first)
                val_cr5 |= FSL_SAI_CR5_FBT(0);
        else
                val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
      
--- --  val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
+++ ++  val_cr4 |= FSL_SAI_CR4_FRSZ(slots);
+++ ++
+++ ++  /*
+++ ++   * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will
+++ ++   * generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4),
+++ ++   * RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync
+++ ++   * error.
+++ ++   */
+++ ++
+++ ++  if (!sai->is_slave_mode) {
+++ ++          if (!sai->synchronous[TX] && sai->synchronous[RX] && !tx) {
+++ ++                  regmap_update_bits(sai->regmap, FSL_SAI_TCR4,
+++ ++                          FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
+++ ++                          val_cr4);
+++ ++                  regmap_update_bits(sai->regmap, FSL_SAI_TCR5,
+++ ++                          FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
+++ ++                          FSL_SAI_CR5_FBT_MASK, val_cr5);
+++ ++                  regmap_write(sai->regmap, FSL_SAI_TMR,
+++ ++                          ~0UL - ((1 << channels) - 1));
+++ ++          } else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) {
+++ ++                  regmap_update_bits(sai->regmap, FSL_SAI_RCR4,
+++ ++                          FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
+++ ++                          val_cr4);
+++ ++                  regmap_update_bits(sai->regmap, FSL_SAI_RCR5,
+++ ++                          FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
+++ ++                          FSL_SAI_CR5_FBT_MASK, val_cr5);
+++ ++                  regmap_write(sai->regmap, FSL_SAI_RMR,
+++ ++                          ~0UL - ((1 << channels) - 1));
+++ ++          }
+++ ++  }
      
        regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
                           FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
@@@@@@@ -454,8 -454,7 -454,7 -513,7 -454,7 -454,7 +513,8 @@@@@@@ static int fsl_sai_trigger(struct snd_p
         * Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx.
         * Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx.
         */
 -----  regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, 0);
 +++++  regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
 +++++                     sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0);
        regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
                           sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0);
      
                                           FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
                        regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
                                           FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
 +++++
 +++++                  /*
 +++++                   * For sai master mode, after several open/close sai,
 +++++                   * there will be no frame clock, and can't recover
 +++++                   * anymore. Add software reset to fix this issue.
 +++++                   * This is a hardware bug, and will be fix in the
 +++++                   * next sai version.
 +++++                   */
 +++++                  if (!sai->is_slave_mode) {
 +++++                          /* Software Reset for both Tx and Rx */
 +++++                          regmap_write(sai->regmap,
 +++++                                       FSL_SAI_TCSR, FSL_SAI_CSR_SR);
 +++++                          regmap_write(sai->regmap,
 +++++                                       FSL_SAI_RCSR, FSL_SAI_CSR_SR);
 +++++                          /* Clear SR bit to finish the reset */
 +++++                          regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
 +++++                          regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
 +++++                  }
                }
                break;
        default:
@@@@@@@ -569,6 -550,6 -550,6 -609,7 -550,6 -550,6 +628,7 @@@@@@@ static void fsl_sai_shutdown(struct snd
      static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
        .set_sysclk     = fsl_sai_set_dai_sysclk,
        .set_fmt        = fsl_sai_set_dai_fmt,
+++ ++  .set_tdm_slot   = fsl_sai_set_dai_tdm_slot,
        .hw_params      = fsl_sai_hw_params,
        .hw_free        = fsl_sai_hw_free,
        .trigger        = fsl_sai_trigger,
@@@@@@@ -627,6 -608,6 -608,6 -668,22 -608,6 -608,6 +687,22 @@@@@@@ static const struct snd_soc_component_d
        .name           = "fsl-sai",
      };
      
+++ ++static struct reg_default fsl_sai_reg_defaults[] = {
+++ ++  {FSL_SAI_TCR1, 0},
+++ ++  {FSL_SAI_TCR2, 0},
+++ ++  {FSL_SAI_TCR3, 0},
+++ ++  {FSL_SAI_TCR4, 0},
+++ ++  {FSL_SAI_TCR5, 0},
+++ ++  {FSL_SAI_TDR,  0},
+++ ++  {FSL_SAI_TMR,  0},
+++ ++  {FSL_SAI_RCR1, 0},
+++ ++  {FSL_SAI_RCR2, 0},
+++ ++  {FSL_SAI_RCR3, 0},
+++ ++  {FSL_SAI_RCR4, 0},
+++ ++  {FSL_SAI_RCR5, 0},
+++ ++  {FSL_SAI_RMR,  0},
+++ ++};
+++ ++
      static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
      {
        switch (reg) {
@@@@@@@ -660,13 -641,13 -641,13 -717,11 -641,13 -641,13 +736,11 @@@@@@@ static bool fsl_sai_volatile_reg(struc
        case FSL_SAI_RCSR:
        case FSL_SAI_TFR:
        case FSL_SAI_RFR:
--- --  case FSL_SAI_TDR:
        case FSL_SAI_RDR:
                return true;
        default:
                return false;
        }
--- --
      }
      
      static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
@@@@@@@ -699,6 -680,6 -680,6 -754,8 -680,6 -680,6 +773,8 @@@@@@@ static const struct regmap_config fsl_s
        .val_bits = 32,
      
        .max_register = FSL_SAI_RMR,
+++ ++  .reg_defaults = fsl_sai_reg_defaults,
+++ ++  .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults),
        .readable_reg = fsl_sai_readable_reg,
        .volatile_reg = fsl_sai_volatile_reg,
        .writeable_reg = fsl_sai_writeable_reg,
diff --combined sound/soc/soc-pcm.c
index bbeaa87a36f4a6472059f1f311ddd802b611065b,37de8af91f13e03ce0ab0901357de07ad4e178eb,c86dc96e8986f39cd08bcf7e92c63f49459d50f2,c86dc96e8986f39cd08bcf7e92c63f49459d50f2,c86dc96e8986f39cd08bcf7e92c63f49459d50f2,c86dc96e8986f39cd08bcf7e92c63f49459d50f2..2a2ca2209656d70d4c3cb06176ac7e8d7096ebd6
@@@@@@@ -1213,10 -1213,11 -1213,11 -1213,11 -1213,11 -1213,11 +1213,10 @@@@@@@ static struct snd_soc_pcm_runtime *dpcm
                struct snd_soc_dapm_widget *widget, int stream)
      {
        struct snd_soc_pcm_runtime *be;
 -----  int i, j;
 +++++  int i;
      
        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
 -----          for (i = 0; i < card->num_links; i++) {
 -----                  be = &card->rtd[i];
 +++++          list_for_each_entry(be, &card->rtd_list, list) {
      
                        if (!be->dai_link->no_pcm)
                                continue;
                        if (be->cpu_dai->playback_widget == widget)
                                return be;
      
 -----                  for (j = 0; j < be->num_codecs; j++) {
 -----                          struct snd_soc_dai *dai = be->codec_dais[j];
 +++++                  for (i = 0; i < be->num_codecs; i++) {
 +++++                          struct snd_soc_dai *dai = be->codec_dais[i];
                                if (dai->playback_widget == widget)
                                        return be;
                        }
                }
        } else {
      
 -----          for (i = 0; i < card->num_links; i++) {
 -----                  be = &card->rtd[i];
 +++++          list_for_each_entry(be, &card->rtd_list, list) {
      
                        if (!be->dai_link->no_pcm)
                                continue;
                        if (be->cpu_dai->capture_widget == widget)
                                return be;
      
 -----                  for (j = 0; j < be->num_codecs; j++) {
 -----                          struct snd_soc_dai *dai = be->codec_dais[j];
 +++++                  for (i = 0; i < be->num_codecs; i++) {
 +++++                          struct snd_soc_dai *dai = be->codec_dais[i];
                                if (dai->capture_widget == widget)
                                        return be;
                        }
@@@@@@@ -1614,6 -1616,56 -1616,6 -1616,6 -1616,6 -1616,6 +1614,56 @@@@@@@ static void dpcm_set_fe_update_state(st
        snd_pcm_stream_unlock_irq(substream);
      }
      
+ ++++static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
+ ++++                         int stream)
+ ++++{
+ ++++  struct snd_soc_dpcm *dpcm;
+ ++++  struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
+ ++++  struct snd_soc_dai *fe_cpu_dai = fe->cpu_dai;
+ ++++  int err;
+ ++++
+ ++++  /* apply symmetry for FE */
+ ++++  if (soc_pcm_has_symmetry(fe_substream))
+ ++++          fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+ ++++
+ ++++  /* Symmetry only applies if we've got an active stream. */
+ ++++  if (fe_cpu_dai->active) {
+ ++++          err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai);
+ ++++          if (err < 0)
+ ++++                  return err;
+ ++++  }
+ ++++
+ ++++  /* apply symmetry for BE */
+ ++++  list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ ++++          struct snd_soc_pcm_runtime *be = dpcm->be;
+ ++++          struct snd_pcm_substream *be_substream =
+ ++++                  snd_soc_dpcm_get_substream(be, stream);
+ ++++          struct snd_soc_pcm_runtime *rtd = be_substream->private_data;
+ ++++          int i;
+ ++++
+ ++++          if (soc_pcm_has_symmetry(be_substream))
+ ++++                  be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+ ++++
+ ++++          /* Symmetry only applies if we've got an active stream. */
+ ++++          if (rtd->cpu_dai->active) {
+ ++++                  err = soc_pcm_apply_symmetry(be_substream, rtd->cpu_dai);
+ ++++                  if (err < 0)
+ ++++                          return err;
+ ++++          }
+ ++++
+ ++++          for (i = 0; i < rtd->num_codecs; i++) {
+ ++++                  if (rtd->codec_dais[i]->active) {
+ ++++                          err = soc_pcm_apply_symmetry(be_substream,
+ ++++                                                       rtd->codec_dais[i]);
+ ++++                          if (err < 0)
+ ++++                                  return err;
+ ++++                  }
+ ++++          }
+ ++++  }
+ ++++
+ ++++  return 0;
+ ++++}
+ ++++
      static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
      {
        struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
        dpcm_set_fe_runtime(fe_substream);
        snd_pcm_limit_hw_rates(runtime);
      
+ ++++  ret = dpcm_apply_symmetry(fe_substream, stream);
+ ++++  if (ret < 0) {
+ ++++          dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n",
+ ++++                  ret);
+ ++++          goto unwind;
+ ++++  }
+ ++++
        dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
        return 0;
      
@@@@@@@ -2113,7 -2172,8 -2115,7 -2115,7 -2115,7 -2115,7 +2170,8 @@@@@@@ int dpcm_be_dai_prepare(struct snd_soc_
                        continue;
      
                if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
- ----              (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ ++++              (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
+ ++++              (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
                        continue;
      
                dev_dbg(be->dev, "ASoC: prepare BE %s\n",
@@@@@@@ -2341,12 -2401,12 -2343,12 -2343,12 -2343,12 -2343,12 +2399,12 @@@@@@@ static int dpcm_run_old_update(struct s
       */
      int soc_dpcm_runtime_update(struct snd_soc_card *card)
      {
 -----  int i, old, new, paths;
 +++++  struct snd_soc_pcm_runtime *fe;
 +++++  int old, new, paths;
      
        mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
 -----  for (i = 0; i < card->num_rtd; i++) {
 +++++  list_for_each_entry(fe, &card->rtd_list, list) {
                struct snd_soc_dapm_widget_list *list;
 -----          struct snd_soc_pcm_runtime *fe = &card->rtd[i];
      
                /* make sure link is FE */
                if (!fe->dai_link->dynamic)