Merge branch 'asoc-5.2' into asoc-next
authorMark Brown <broonie@kernel.org>
Mon, 6 May 2019 13:51:54 +0000 (22:51 +0900)
committerMark Brown <broonie@kernel.org>
Mon, 6 May 2019 13:51:54 +0000 (22:51 +0900)
1  2 
MAINTAINERS
sound/soc/codecs/Kconfig
sound/soc/codecs/da7219.c
sound/soc/codecs/hdmi-codec.c
sound/soc/soc-dapm.c
sound/soc/soc-pcm.c
sound/soc/stm/stm32_sai_sub.c

diff --combined MAINTAINERS
index 2c2fce72e694f6ccc2000974e9cba4f6fcafbf48,b6ed758ffd47ca69f74b715c5c09e7b3042a8535..8ebba1b540acb97e3f5f9dbeb40f772b9207ce86
@@@ -1893,15 -1893,14 +1893,15 @@@ T:   git git://git.kernel.org/pub/scm/lin
  ARM/NUVOTON NPCM ARCHITECTURE
  M:    Avi Fishman <avifishman70@gmail.com>
  M:    Tomer Maimon <tmaimon77@gmail.com>
 +M:    Tali Perry <tali.perry1@gmail.com>
  R:    Patrick Venture <venture@google.com>
  R:    Nancy Yuen <yuenn@google.com>
 -R:    Brendan Higgins <brendanhiggins@google.com>
 +R:    Benjamin Fair <benjaminfair@google.com>
  L:    openbmc@lists.ozlabs.org (moderated for non-subscribers)
  S:    Supported
  F:    arch/arm/mach-npcm/
  F:    arch/arm/boot/dts/nuvoton-npcm*
 -F:    include/dt-bindings/clock/nuvoton,npcm7xx-clks.h
 +F:    include/dt-bindings/clock/nuvoton,npcm7xx-clock.h
  F:    drivers/*/*npcm*
  F:    Documentation/devicetree/bindings/*/*npcm*
  F:    Documentation/devicetree/bindings/*/*/*npcm*
@@@ -3121,7 -3120,6 +3121,7 @@@ F:      drivers/cpufreq/bmips-cpufreq.
  BROADCOM BMIPS MIPS ARCHITECTURE
  M:    Kevin Cernekee <cernekee@gmail.com>
  M:    Florian Fainelli <f.fainelli@gmail.com>
 +L:    bcm-kernel-feedback-list@broadcom.com
  L:    linux-mips@vger.kernel.org
  T:    git git://github.com/broadcom/stblinux.git
  S:    Maintained
@@@ -3801,6 -3799,7 +3801,7 @@@ F:      drivers/clk/clk-lochnagar.
  F:    drivers/mfd/lochnagar-i2c.c
  F:    drivers/pinctrl/cirrus/pinctrl-lochnagar.c
  F:    drivers/regulator/lochnagar-regulator.c
+ F:    sound/soc/codecs/lochnagar-sc.c
  F:    include/dt-bindings/clk/lochnagar.h
  F:    include/dt-bindings/pinctrl/lochnagar.h
  F:    include/linux/mfd/lochnagar*
@@@ -3808,6 -3807,7 +3809,7 @@@ F:      Documentation/devicetree/bindings/mf
  F:    Documentation/devicetree/bindings/clock/cirrus,lochnagar.txt
  F:    Documentation/devicetree/bindings/pinctrl/cirrus,lochnagar.txt
  F:    Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt
+ F:    Documentation/devicetree/bindings/sound/cirrus,lochnagar.txt
  
  CISCO FCOE HBA DRIVER
  M:    Satish Kharat <satishkh@cisco.com>
@@@ -4131,7 -4131,7 +4133,7 @@@ F:      drivers/cpuidle/
  F:    include/linux/cpuidle.h
  
  CRAMFS FILESYSTEM
 -M:    Nicolas Pitre <nico@linaro.org>
 +M:    Nicolas Pitre <nico@fluxnic.net>
  S:    Maintained
  F:    Documentation/filesystems/cramfs.txt
  F:    fs/cramfs/
@@@ -5835,7 -5835,7 +5837,7 @@@ L:      netdev@vger.kernel.or
  S:    Maintained
  F:    Documentation/ABI/testing/sysfs-bus-mdio
  F:    Documentation/devicetree/bindings/net/mdio*
 -F:    Documentation/networking/phy.txt
 +F:    Documentation/networking/phy.rst
  F:    drivers/net/phy/
  F:    drivers/of/of_mdio.c
  F:    drivers/of/of_net.c
@@@ -6462,7 -6462,7 +6464,7 @@@ S:      Maintaine
  F:    drivers/media/radio/radio-gemtek*
  
  GENERIC GPIO I2C DRIVER
 -M:    Haavard Skinnemoen <hskinnemoen@gmail.com>
 +M:    Wolfram Sang <wsa+renesas@sang-engineering.com>
  S:    Supported
  F:    drivers/i2c/busses/i2c-gpio.c
  F:    include/linux/platform_data/i2c-gpio.h
@@@ -7334,6 -7334,7 +7336,6 @@@ F:      Documentation/devicetree/bindings/i3
  F:    Documentation/driver-api/i3c
  F:    drivers/i3c/
  F:    include/linux/i3c/
 -F:    include/dt-bindings/i3c/
  
  I3C DRIVER FOR SYNOPSYS DESIGNWARE
  M:    Vitor Soares <vitor.soares@synopsys.com>
@@@ -7516,7 -7517,7 +7518,7 @@@ F:      include/net/mac802154.
  F:    include/net/af_ieee802154.h
  F:    include/net/cfg802154.h
  F:    include/net/ieee802154_netdev.h
 -F:    Documentation/networking/ieee802154.txt
 +F:    Documentation/networking/ieee802154.rst
  
  IFE PROTOCOL
  M:    Yotam Gigi <yotam.gi@gmail.com>
@@@ -8708,7 -8709,6 +8710,7 @@@ F:      scripts/leaking_addresses.p
  LED SUBSYSTEM
  M:    Jacek Anaszewski <jacek.anaszewski@gmail.com>
  M:    Pavel Machek <pavel@ucw.cz>
 +R:    Dan Murphy <dmurphy@ti.com>
  L:    linux-leds@vger.kernel.org
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git
  S:    Maintained
@@@ -10146,7 -10146,7 +10148,7 @@@ F:   drivers/spi/spi-at91-usart.
  F:    Documentation/devicetree/bindings/mfd/atmel-usart.txt
  
  MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
 -M:    Woojung Huh <Woojung.Huh@microchip.com>
 +M:    Woojung Huh <woojung.huh@microchip.com>
  M:    Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
  L:    netdev@vger.kernel.org
  S:    Maintained
@@@ -12176,7 -12176,6 +12178,7 @@@ F:   arch/*/kernel/*/*/perf_event*.
  F:    arch/*/include/asm/perf_event.h
  F:    arch/*/kernel/perf_callchain.c
  F:    arch/*/events/*
 +F:    arch/*/events/*/*
  F:    tools/perf/
  
  PERSONALITY HANDLING
@@@ -13984,7 -13983,7 +13986,7 @@@ F:   drivers/media/rc/serial_ir.
  SFC NETWORK DRIVER
  M:    Solarflare linux maintainers <linux-net-drivers@solarflare.com>
  M:    Edward Cree <ecree@solarflare.com>
 -M:    Bert Kenward <bkenward@solarflare.com>
 +M:    Martin Habets <mhabets@solarflare.com>
  L:    netdev@vger.kernel.org
  S:    Supported
  F:    drivers/net/ethernet/sfc/
@@@ -16511,7 -16510,7 +16513,7 @@@ F:   drivers/char/virtio_console.
  F:    include/linux/virtio_console.h
  F:    include/uapi/linux/virtio_console.h
  
 -VIRTIO CORE, NET AND BLOCK DRIVERS
 +VIRTIO CORE AND NET DRIVERS
  M:    "Michael S. Tsirkin" <mst@redhat.com>
  M:    Jason Wang <jasowang@redhat.com>
  L:    virtualization@lists.linux-foundation.org
@@@ -16526,19 -16525,6 +16528,19 @@@ F: include/uapi/linux/virtio_*.
  F:    drivers/crypto/virtio/
  F:    mm/balloon_compaction.c
  
 +VIRTIO BLOCK AND SCSI DRIVERS
 +M:    "Michael S. Tsirkin" <mst@redhat.com>
 +M:    Jason Wang <jasowang@redhat.com>
 +R:    Paolo Bonzini <pbonzini@redhat.com>
 +R:    Stefan Hajnoczi <stefanha@redhat.com>
 +L:    virtualization@lists.linux-foundation.org
 +S:    Maintained
 +F:    drivers/block/virtio_blk.c
 +F:    drivers/scsi/virtio_scsi.c
 +F:    include/uapi/linux/virtio_blk.h
 +F:    include/uapi/linux/virtio_scsi.h
 +F:    drivers/vhost/scsi.c
 +
  VIRTIO CRYPTO DRIVER
  M:    Gonglei <arei.gonglei@huawei.com>
  L:    virtualization@lists.linux-foundation.org
diff --combined sound/soc/codecs/Kconfig
index 667fc1d59e189f599e580654c10719b96b7e74a0,9981d40ef45b187e0b6b8ea61cfaf51a97da31f7..8f577258080bdbe2d582ff6776abcd5972346f5a
@@@ -94,6 -94,7 +94,7 @@@ config SND_SOC_ALL_CODEC
        select SND_SOC_JZ4725B_CODEC
        select SND_SOC_LM4857 if I2C
        select SND_SOC_LM49453 if I2C
+       select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR
        select SND_SOC_MAX98088 if I2C
        select SND_SOC_MAX98090 if I2C
        select SND_SOC_MAX98095 if I2C
        select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
        select SND_SOC_TLV320AIC26 if SPI_MASTER
        select SND_SOC_TLV320AIC31XX if I2C
-       select SND_SOC_TLV320AIC32X4_I2C if I2C
-       select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER
+       select SND_SOC_TLV320AIC32X4_I2C if I2C && COMMON_CLK
+       select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER && COMMON_CLK
        select SND_SOC_TLV320AIC3X if I2C
        select SND_SOC_TPA6130A2 if I2C
        select SND_SOC_TLV320DAC33 if I2C
@@@ -688,6 -689,13 +689,13 @@@ config SND_SOC_ISABELL
  config SND_SOC_LM49453
        tristate
  
+ config SND_SOC_LOCHNAGAR_SC
+       tristate "Lochnagar Sound Card"
+       depends on MFD_LOCHNAGAR
+       help
+         This driver support the sound card functionality of the Cirrus
+         Logic Lochnagar audio development board.
  config SND_SOC_MAX98088
        tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec"
        depends on I2C
@@@ -1097,15 -1105,18 +1105,18 @@@ config SND_SOC_TLV320AIC31X
  
  config SND_SOC_TLV320AIC32X4
        tristate
+       depends on COMMON_CLK
  
  config SND_SOC_TLV320AIC32X4_I2C
        tristate "Texas Instruments TLV320AIC32x4 audio CODECs - I2C"
        depends on I2C
+       depends on COMMON_CLK
        select SND_SOC_TLV320AIC32X4
  
  config SND_SOC_TLV320AIC32X4_SPI
        tristate "Texas Instruments TLV320AIC32x4 audio CODECs - SPI"
        depends on SPI_MASTER
+       depends on COMMON_CLK
        select SND_SOC_TLV320AIC32X4
  
  config SND_SOC_TLV320AIC3X
@@@ -1151,7 -1162,6 +1162,7 @@@ config SND_SOC_WCD933
        tristate "WCD9335 Codec"
        depends on SLIMBUS
        select REGMAP_SLIMBUS
 +      select REGMAP_IRQ
        help
          The WCD9335 is a standalone Hi-Fi audio CODEC IC, supports
          Qualcomm Technologies, Inc. (QTI) multimedia solutions,
index 9f6970eed6f69a5f9db7daf27a25d997b9c4ff51,206d01c6eb7edd33847ce95cd99e4b06a8ee1ab2..cf2948c00262abfbb4aeaca0d67ebabfc0988041
@@@ -797,6 -797,7 +797,7 @@@ static int da7219_dai_event(struct snd_
  {
        struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
        struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+       struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
        u8 pll_ctrl, pll_status;
        int i = 0, ret;
        bool srm_lock = false;
        case SND_SOC_DAPM_PRE_PMU:
                if (da7219->master) {
                        /* Enable DAI clks for master mode */
-                       if (da7219->dai_clks) {
-                               ret = clk_prepare_enable(da7219->dai_clks);
+                       if (bclk) {
+                               ret = clk_prepare_enable(bclk);
                                if (ret) {
                                        dev_err(component->dev,
-                                               "Failed to enable dai_clks\n");
+                                               "Failed to enable DAI clks\n");
                                        return ret;
                                }
                        } else {
  
                /* Disable DAI clks if in master mode */
                if (da7219->master) {
-                       if (da7219->dai_clks)
-                               clk_disable_unprepare(da7219->dai_clks);
+                       if (bclk)
+                               clk_disable_unprepare(bclk);
                        else
                                snd_soc_component_update_bits(component,
                                                              DA7219_DAI_CLK_MODE,
@@@ -1385,17 -1386,50 +1386,50 @@@ static int da7219_set_dai_fmt(struct sn
        return 0;
  }
  
+ static int da7219_set_bclks_per_wclk(struct snd_soc_component *component,
+                                    unsigned long factor)
+ {
+       u8 bclks_per_wclk;
+       switch (factor) {
+       case 32:
+               bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+               break;
+       case 64:
+               bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
+               break;
+       case 128:
+               bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
+               break;
+       case 256:
+               bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
+               break;
+       default:
+               return -EINVAL;
+       }
+       snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
+                                     DA7219_DAI_BCLKS_PER_WCLK_MASK,
+                                     bclks_per_wclk);
+       return 0;
+ }
  static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
                                   unsigned int tx_mask, unsigned int rx_mask,
                                   int slots, int slot_width)
  {
        struct snd_soc_component *component = dai->component;
        struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+       struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
+       struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
        unsigned int ch_mask;
-       u8 dai_bclks_per_wclk, slot_offset;
+       unsigned long sr, bclk_rate;
+       u8 slot_offset;
        u16 offset;
        __le16 dai_offset;
        u32 frame_size;
+       int ret;
  
        /* No channels enabled so disable TDM */
        if (!tx_mask) {
         */
        if (da7219->master) {
                frame_size = slots * slot_width;
-               switch (frame_size) {
-               case 32:
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
-                       break;
-               case 64:
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
-                       break;
-               case 128:
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
-                       break;
-               case 256:
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
-                       break;
-               default:
-                       dev_err(component->dev, "Invalid frame size %d\n",
-                               frame_size);
-                       return -EINVAL;
-               }
  
-               snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
-                               DA7219_DAI_BCLKS_PER_WCLK_MASK,
-                               dai_bclks_per_wclk);
+               if (bclk) {
+                       sr = clk_get_rate(wclk);
+                       bclk_rate = sr * frame_size;
+                       ret = clk_set_rate(bclk, bclk_rate);
+                       if (ret) {
+                               dev_err(component->dev,
+                                       "Failed to set TDM BCLK rate %lu: %d\n",
+                                       bclk_rate, ret);
+                               return ret;
+                       }
+               } else {
+                       ret = da7219_set_bclks_per_wclk(component, frame_size);
+                       if (ret) {
+                               dev_err(component->dev,
+                                       "Failed to set TDM BCLKs per WCLK %d: %d\n",
+                                       frame_size, ret);
+                               return ret;
+                       }
+               }
        }
  
        dai_offset = cpu_to_le16(offset);
        return 0;
  }
  
- static int da7219_hw_params(struct snd_pcm_substream *substream,
-                           struct snd_pcm_hw_params *params,
-                           struct snd_soc_dai *dai)
+ static int da7219_set_sr(struct snd_soc_component *component,
+                        unsigned long rate)
  {
-       struct snd_soc_component *component = dai->component;
-       struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
-       u8 dai_ctrl = 0, dai_bclks_per_wclk = 0, fs;
-       unsigned int channels;
-       int word_len = params_width(params);
-       int frame_size;
-       switch (word_len) {
-       case 16:
-               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
-               break;
-       case 20:
-               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
-               break;
-       case 24:
-               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
-               break;
-       case 32:
-               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
-               break;
-       default:
-               return -EINVAL;
-       }
+       u8 fs;
  
-       channels = params_channels(params);
-       if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
-               dev_err(component->dev,
-                       "Invalid number of channels, only 1 to %d supported\n",
-                       DA7219_DAI_CH_NUM_MAX);
-               return -EINVAL;
-       }
-       dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
-       switch (params_rate(params)) {
+       switch (rate) {
        case 8000:
                fs = DA7219_SR_8000;
                break;
                return -EINVAL;
        }
  
+       snd_soc_component_write(component, DA7219_SR, fs);
+       return 0;
+ }
+ static int da7219_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+ {
+       struct snd_soc_component *component = dai->component;
+       struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+       struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
+       struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
+       u8 dai_ctrl = 0;
+       unsigned int channels;
+       unsigned long sr, bclk_rate;
+       int word_len = params_width(params);
+       int frame_size, ret;
+       switch (word_len) {
+       case 16:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
+               break;
+       case 20:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
+               break;
+       case 24:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
+               break;
+       case 32:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
+               break;
+       default:
+               return -EINVAL;
+       }
+       channels = params_channels(params);
+       if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
+               dev_err(component->dev,
+                       "Invalid number of channels, only 1 to %d supported\n",
+                       DA7219_DAI_CH_NUM_MAX);
+               return -EINVAL;
+       }
+       dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+       sr = params_rate(params);
+       if (da7219->master && wclk) {
+               ret = clk_set_rate(wclk, sr);
+               if (ret) {
+                       dev_err(component->dev,
+                               "Failed to set WCLK SR %lu: %d\n", sr, ret);
+                       return ret;
+               }
+       } else {
+               ret = da7219_set_sr(component, sr);
+               if (ret) {
+                       dev_err(component->dev,
+                               "Failed to set SR %lu: %d\n", sr, ret);
+                       return ret;
+               }
+       }
        /*
         * If we're master, then we have a limited set of BCLK rates we
         * support. For slave mode this isn't the case and the codec can detect
         * the BCLK rate automatically.
         */
        if (da7219->master && !da7219->tdm_en) {
-               frame_size = word_len * 2;
-               if (frame_size <= 32)
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+               if ((word_len * DA7219_DAI_CH_NUM_MAX) <= 32)
+                       frame_size = 32;
                else
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
+                       frame_size = 64;
+               if (bclk) {
+                       bclk_rate = frame_size * sr;
+                       /*
+                        * Rounding the rate here avoids failure trying to set a
+                        * new rate on an already enabled bclk. In that
+                        * instance this will just set the same rate as is
+                        * currently in use, and so should continue without
+                        * problem, as long as the BCLK rate is suitable for the
+                        * desired frame size.
+                        */
+                       bclk_rate = clk_round_rate(bclk, bclk_rate);
+                       if ((bclk_rate / sr) < frame_size) {
+                               dev_err(component->dev,
+                                       "BCLK rate mismatch against frame size");
+                               return -EINVAL;
+                       }
  
-               snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
-                                             DA7219_DAI_BCLKS_PER_WCLK_MASK,
-                                             dai_bclks_per_wclk);
+                       ret = clk_set_rate(bclk, bclk_rate);
+                       if (ret) {
+                               dev_err(component->dev,
+                                       "Failed to set BCLK rate %lu: %d\n",
+                                       bclk_rate, ret);
+                               return ret;
+                       }
+               } else {
+                       ret = da7219_set_bclks_per_wclk(component, frame_size);
+                       if (ret) {
+                               dev_err(component->dev,
+                                       "Failed to set BCLKs per WCLK %d: %d\n",
+                                       frame_size, ret);
+                               return ret;
+                       }
+               }
        }
  
        snd_soc_component_update_bits(component, DA7219_DAI_CTRL,
                            DA7219_DAI_WORD_LENGTH_MASK |
                            DA7219_DAI_CH_NUM_MASK,
                            dai_ctrl);
-       snd_soc_component_write(component, DA7219_SR, fs);
  
        return 0;
  }
@@@ -1583,26 -1673,20 +1673,26 @@@ static const struct snd_soc_dai_ops da7
  #define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
  
 +#define DA7219_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
 +                    SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
 +                    SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
 +                    SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
 +                    SNDRV_PCM_RATE_96000)
 +
  static struct snd_soc_dai_driver da7219_dai = {
        .name = "da7219-hifi",
        .playback = {
                .stream_name = "Playback",
                .channels_min = 1,
                .channels_max = DA7219_DAI_CH_NUM_MAX,
 -              .rates = SNDRV_PCM_RATE_8000_96000,
 +              .rates = DA7219_RATES,
                .formats = DA7219_FORMATS,
        },
        .capture = {
                .stream_name = "Capture",
                .channels_min = 1,
                .channels_max = DA7219_DAI_CH_NUM_MAX,
 -              .rates = SNDRV_PCM_RATE_8000_96000,
 +              .rates = DA7219_RATES,
                .formats = DA7219_FORMATS,
        },
        .ops = &da7219_dai_ops,
@@@ -1678,11 -1762,14 +1768,14 @@@ static struct da7219_pdata *da7219_fw_t
  
        pdata->wakeup_source = device_property_read_bool(dev, "wakeup-source");
  
-       pdata->dai_clks_name = "da7219-dai-clks";
-       if (device_property_read_string(dev, "clock-output-names",
-                                       &pdata->dai_clks_name))
-               dev_warn(dev, "Using default clk name: %s\n",
-                        pdata->dai_clks_name);
+       pdata->dai_clk_names[DA7219_DAI_WCLK_IDX] = "da7219-dai-wclk";
+       pdata->dai_clk_names[DA7219_DAI_BCLK_IDX] = "da7219-dai-bclk";
+       if (device_property_read_string_array(dev, "clock-output-names",
+                                             pdata->dai_clk_names,
+                                             DA7219_DAI_NUM_CLKS) < 0)
+               dev_warn(dev, "Using default DAI clk names: %s, %s\n",
+                        pdata->dai_clk_names[DA7219_DAI_WCLK_IDX],
+                        pdata->dai_clk_names[DA7219_DAI_BCLK_IDX]);
  
        if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0)
                pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32);
@@@ -1799,12 -1886,16 +1892,16 @@@ static int da7219_handle_supplies(struc
  }
  
  #ifdef CONFIG_COMMON_CLK
- static int da7219_dai_clks_prepare(struct clk_hw *hw)
+ static int da7219_wclk_prepare(struct clk_hw *hw)
  {
        struct da7219_priv *da7219 =
-               container_of(hw, struct da7219_priv, dai_clks_hw);
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
        struct snd_soc_component *component = da7219->component;
  
+       if (!da7219->master)
+               return -EINVAL;
        snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
                                      DA7219_DAI_CLK_EN_MASK,
                                      DA7219_DAI_CLK_EN_MASK);
        return 0;
  }
  
- static void da7219_dai_clks_unprepare(struct clk_hw *hw)
+ static void da7219_wclk_unprepare(struct clk_hw *hw)
  {
        struct da7219_priv *da7219 =
-               container_of(hw, struct da7219_priv, dai_clks_hw);
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
        struct snd_soc_component *component = da7219->component;
  
+       if (!da7219->master)
+               return;
        snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
                                      DA7219_DAI_CLK_EN_MASK, 0);
  }
  
- static int da7219_dai_clks_is_prepared(struct clk_hw *hw)
+ static int da7219_wclk_is_prepared(struct clk_hw *hw)
  {
        struct da7219_priv *da7219 =
-               container_of(hw, struct da7219_priv, dai_clks_hw);
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
        struct snd_soc_component *component = da7219->component;
        u8 clk_reg;
  
+       if (!da7219->master)
+               return -EINVAL;
        clk_reg = snd_soc_component_read32(component, DA7219_DAI_CLK_MODE);
  
        return !!(clk_reg & DA7219_DAI_CLK_EN_MASK);
  }
  
- static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw,
-                                                unsigned long parent_rate)
+ static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw,
+                                            unsigned long parent_rate)
  {
        struct da7219_priv *da7219 =
-               container_of(hw, struct da7219_priv, dai_clks_hw);
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
        struct snd_soc_component *component = da7219->component;
        u8 fs = snd_soc_component_read32(component, DA7219_SR);
  
        }
  }
  
- static const struct clk_ops da7219_dai_clks_ops = {
-       .prepare = da7219_dai_clks_prepare,
-       .unprepare = da7219_dai_clks_unprepare,
-       .is_prepared = da7219_dai_clks_is_prepared,
-       .recalc_rate = da7219_dai_clks_recalc_rate,
+ static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
+                                  unsigned long *parent_rate)
+ {
+       struct da7219_priv *da7219 =
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+       if (!da7219->master)
+               return -EINVAL;
+       if (rate < 11025)
+               return 8000;
+       else if (rate < 12000)
+               return 11025;
+       else if (rate < 16000)
+               return 12000;
+       else if (rate < 22050)
+               return 16000;
+       else if (rate < 24000)
+               return 22050;
+       else if (rate < 32000)
+               return 24000;
+       else if (rate < 44100)
+               return 32000;
+       else if (rate < 48000)
+               return 44100;
+       else if (rate < 88200)
+               return 48000;
+       else if (rate < 96000)
+               return 88200;
+       else
+               return 96000;
+ }
+ static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate)
+ {
+       struct da7219_priv *da7219 =
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+       struct snd_soc_component *component = da7219->component;
+       if (!da7219->master)
+               return -EINVAL;
+       return da7219_set_sr(component, rate);
+ }
+ static unsigned long da7219_bclk_recalc_rate(struct clk_hw *hw,
+                                            unsigned long parent_rate)
+ {
+       struct da7219_priv *da7219 =
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+       struct snd_soc_component *component = da7219->component;
+       u8 bclks_per_wclk = snd_soc_component_read32(component,
+                                                    DA7219_DAI_CLK_MODE);
+       switch (bclks_per_wclk & DA7219_DAI_BCLKS_PER_WCLK_MASK) {
+       case DA7219_DAI_BCLKS_PER_WCLK_32:
+               return parent_rate * 32;
+       case DA7219_DAI_BCLKS_PER_WCLK_64:
+               return parent_rate * 64;
+       case DA7219_DAI_BCLKS_PER_WCLK_128:
+               return parent_rate * 128;
+       case DA7219_DAI_BCLKS_PER_WCLK_256:
+               return parent_rate * 256;
+       default:
+               return 0;
+       }
+ }
+ static unsigned long da7219_bclk_get_factor(unsigned long rate,
+                                           unsigned long parent_rate)
+ {
+       unsigned long factor;
+       factor = rate / parent_rate;
+       if (factor < 64)
+               return 32;
+       else if (factor < 128)
+               return 64;
+       else if (factor < 256)
+               return 128;
+       else
+               return 256;
+ }
+ static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
+                                  unsigned long *parent_rate)
+ {
+       struct da7219_priv *da7219 =
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+       unsigned long factor;
+       if (!*parent_rate || !da7219->master)
+               return -EINVAL;
+       /*
+        * We don't allow changing the parent rate as some BCLK rates can be
+        * derived from multiple parent WCLK rates (BCLK rates are set as a
+        * multiplier of WCLK in HW). We just do some rounding down based on the
+        * parent WCLK rate set and find the appropriate multiplier of BCLK to
+        * get the rounded down BCLK value.
+        */
+       factor = da7219_bclk_get_factor(rate, *parent_rate);
+       return *parent_rate * factor;
+ }
+ static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate)
+ {
+       struct da7219_priv *da7219 =
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+       struct snd_soc_component *component = da7219->component;
+       unsigned long factor;
+       if (!da7219->master)
+               return -EINVAL;
+       factor = da7219_bclk_get_factor(rate, parent_rate);
+       return da7219_set_bclks_per_wclk(component, factor);
+ }
+ static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = {
+       [DA7219_DAI_WCLK_IDX] = {
+               .prepare = da7219_wclk_prepare,
+               .unprepare = da7219_wclk_unprepare,
+               .is_prepared = da7219_wclk_is_prepared,
+               .recalc_rate = da7219_wclk_recalc_rate,
+               .round_rate = da7219_wclk_round_rate,
+               .set_rate = da7219_wclk_set_rate,
+       },
+       [DA7219_DAI_BCLK_IDX] = {
+               .recalc_rate = da7219_bclk_recalc_rate,
+               .round_rate = da7219_bclk_round_rate,
+               .set_rate = da7219_bclk_set_rate,
+       },
  };
  
  static int da7219_register_dai_clks(struct snd_soc_component *component)
        struct device *dev = component->dev;
        struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
        struct da7219_pdata *pdata = da7219->pdata;
-       struct clk_init_data init = {};
-       struct clk *dai_clks;
-       struct clk_lookup *dai_clks_lookup;
        const char *parent_name;
+       int i, ret;
  
-       if (da7219->mclk) {
-               parent_name = __clk_get_name(da7219->mclk);
-               init.parent_names = &parent_name;
-               init.num_parents = 1;
-       } else {
-               init.parent_names = NULL;
-               init.num_parents = 0;
-       }
+       for (i = 0; i < DA7219_DAI_NUM_CLKS; ++i) {
+               struct clk_init_data init = {};
+               struct clk *dai_clk;
+               struct clk_lookup *dai_clk_lookup;
+               struct clk_hw *dai_clk_hw = &da7219->dai_clks_hw[i];
  
-       init.name = pdata->dai_clks_name;
-       init.ops = &da7219_dai_clks_ops;
-       init.flags = CLK_GET_RATE_NOCACHE;
-       da7219->dai_clks_hw.init = &init;
+               switch (i) {
+               case DA7219_DAI_WCLK_IDX:
+                       /*
+                        * If we can, make MCLK the parent of WCLK to ensure
+                        * it's enabled as required.
+                        */
+                       if (da7219->mclk) {
+                               parent_name = __clk_get_name(da7219->mclk);
+                               init.parent_names = &parent_name;
+                               init.num_parents = 1;
+                       } else {
+                               init.parent_names = NULL;
+                               init.num_parents = 0;
+                       }
+                       break;
+               case DA7219_DAI_BCLK_IDX:
+                       /* Make WCLK the parent of BCLK */
+                       parent_name = __clk_get_name(da7219->dai_clks[DA7219_DAI_WCLK_IDX]);
+                       init.parent_names = &parent_name;
+                       init.num_parents = 1;
+                       break;
+               default:
+                       dev_err(dev, "Invalid clock index\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
  
-       dai_clks = devm_clk_register(dev, &da7219->dai_clks_hw);
-       if (IS_ERR(dai_clks)) {
-               dev_warn(dev, "Failed to register DAI clocks: %ld\n",
-                        PTR_ERR(dai_clks));
-               return PTR_ERR(dai_clks);
-       }
-       da7219->dai_clks = dai_clks;
+               init.name = pdata->dai_clk_names[i];
+               init.ops = &da7219_dai_clk_ops[i];
+               init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+               dai_clk_hw->init = &init;
+               dai_clk = devm_clk_register(dev, dai_clk_hw);
+               if (IS_ERR(dai_clk)) {
+                       dev_warn(dev, "Failed to register %s: %ld\n",
+                                init.name, PTR_ERR(dai_clk));
+                       ret = PTR_ERR(dai_clk);
+                       goto err;
+               }
+               da7219->dai_clks[i] = dai_clk;
  
-       /* If we're using DT, then register as provider accordingly */
-       if (dev->of_node) {
-               devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
-                                           &da7219->dai_clks_hw);
-       } else {
-               dai_clks_lookup = clkdev_create(dai_clks, pdata->dai_clks_name,
-                                               "%s", dev_name(dev));
-               if (!dai_clks_lookup)
-                       return -ENOMEM;
-               else
-                       da7219->dai_clks_lookup = dai_clks_lookup;
+               /* If we're using DT, then register as provider accordingly */
+               if (dev->of_node) {
+                       devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+                                                   dai_clk_hw);
+               } else {
+                       dai_clk_lookup = clkdev_create(dai_clk, init.name,
+                                                      "%s", dev_name(dev));
+                       if (!dai_clk_lookup) {
+                               ret = -ENOMEM;
+                               goto err;
+                       } else {
+                               da7219->dai_clks_lookup[i] = dai_clk_lookup;
+                       }
+               }
        }
  
        return 0;
+ err:
+       do {
+               if (da7219->dai_clks_lookup[i])
+                       clkdev_drop(da7219->dai_clks_lookup[i]);
+       } while (i-- > 0);
+       return ret;
  }
  #else
  static inline int da7219_register_dai_clks(struct snd_soc_component *component)
@@@ -2086,12 -2357,15 +2363,15 @@@ err_disable_reg
  static void da7219_remove(struct snd_soc_component *component)
  {
        struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+       int i;
  
        da7219_aad_exit(component);
  
  #ifdef CONFIG_COMMON_CLK
-       if (da7219->dai_clks_lookup)
-               clkdev_drop(da7219->dai_clks_lookup);
+       for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) {
+               if (da7219->dai_clks_lookup[i])
+                       clkdev_drop(da7219->dai_clks_lookup[i]);
+       }
  #endif
  
        /* Supplies */
index fb2f0ac1f16f303e1b999441754826d7391b180c,ef6d6959ecc591de7b4a24b15d1c51bfb596c244..39caf19abb0bcd49e3cf5bc652e00712e688a292
@@@ -439,12 -439,8 +439,12 @@@ static int hdmi_codec_startup(struct sn
                if (!ret) {
                        ret = snd_pcm_hw_constraint_eld(substream->runtime,
                                                        hcp->eld);
 -                      if (ret)
 +                      if (ret) {
 +                              mutex_lock(&hcp->current_stream_lock);
 +                              hcp->current_stream = NULL;
 +                              mutex_unlock(&hcp->current_stream_lock);
                                return ret;
 +                      }
                }
                /* Select chmap supported */
                hdmi_codec_eld_chmap(hcp);
@@@ -496,10 -492,6 +496,6 @@@ static int hdmi_codec_hw_params(struct 
                return ret;
        }
  
-       ret = hdmi_codec_new_stream(substream, dai);
-       if (ret)
-               return ret;
        hdmi_audio_infoframe_init(&hp.cea);
        hp.cea.channels = params_channels(params);
        hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
@@@ -761,7 -753,7 +757,7 @@@ static int hdmi_codec_probe(struct plat
        dev_dbg(dev, "%s()\n", __func__);
  
        if (!hcd) {
-               dev_err(dev, "%s: No plalform data\n", __func__);
+               dev_err(dev, "%s: No platform data\n", __func__);
                return -EINVAL;
        }
  
diff --combined sound/soc/soc-dapm.c
index 29cdfbf4c8886dc8fdb25396d05854d5b99bc95c,65ee0bb5dd0bd223ed1357f0afda80304f5dc75b..81a7a12196ffed084305126b14b31deca9f4be8e
@@@ -883,7 -883,6 +883,7 @@@ static int dapm_create_or_share_kcontro
                        case snd_soc_dapm_switch:
                        case snd_soc_dapm_mixer:
                        case snd_soc_dapm_pga:
 +                      case snd_soc_dapm_effect:
                        case snd_soc_dapm_out_drv:
                                wname_in_long_name = true;
                                kcname_in_long_name = true;
@@@ -2371,7 -2370,6 +2371,7 @@@ static ssize_t dapm_widget_show_compone
                case snd_soc_dapm_dac:
                case snd_soc_dapm_adc:
                case snd_soc_dapm_pga:
 +              case snd_soc_dapm_effect:
                case snd_soc_dapm_out_drv:
                case snd_soc_dapm_mixer:
                case snd_soc_dapm_mixer_named_ctl:
@@@ -3199,7 -3197,6 +3199,7 @@@ int snd_soc_dapm_new_widgets(struct snd
                        dapm_new_mux(w);
                        break;
                case snd_soc_dapm_pga:
 +              case snd_soc_dapm_effect:
                case snd_soc_dapm_out_drv:
                        dapm_new_pga(w);
                        break;
@@@ -4052,7 -4049,7 +4052,7 @@@ snd_soc_dapm_new_dai(struct snd_soc_car
        struct snd_soc_dapm_widget template;
        struct snd_soc_dapm_widget *w;
        const char **w_param_text;
-       unsigned long private_value;
+       unsigned long private_value = 0;
        char *link_name;
        int ret;
  
diff --combined sound/soc/soc-pcm.c
index 69ea962de58564c2170905c26e083eb49f72917f,74695355c1f8dfa0a6c293a82a020323d93fadb3..0a4f60c7a188cd6c81122f5dbe5d413921df897e
@@@ -43,8 -43,8 +43,8 @@@ static bool snd_soc_dai_stream_valid(st
        else
                codec_stream = &dai->driver->capture;
  
 -      /* If the codec specifies any rate at all, it supports the stream. */
 -      return codec_stream->rates;
 +      /* If the codec specifies any channels at all, it supports the stream */
 +      return codec_stream->channels_min;
  }
  
  /**
@@@ -518,10 -518,8 +518,10 @@@ static int soc_pcm_open(struct snd_pcm_
                        continue;
  
                if (component->driver->module_get_upon_open &&
 -                  !try_module_get(component->dev->driver->owner))
 -                      return -ENODEV;
 +                  !try_module_get(component->dev->driver->owner)) {
 +                      ret = -ENODEV;
 +                      goto module_err;
 +              }
  
                ret = component->driver->ops->open(substream);
                if (ret < 0) {
@@@ -638,7 -636,7 +638,7 @@@ codec_dai_err
  
  component_err:
        soc_pcm_components_close(substream, component);
 -
 +module_err:
        if (cpu_dai->driver->ops->shutdown)
                cpu_dai->driver->ops->shutdown(substream, cpu_dai);
  out:
@@@ -1033,9 -1031,6 +1033,9 @@@ interface_err
  
  codec_err:
        for_each_rtd_codec_dai_rollback(rtd, i, codec_dai) {
 +              if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
 +                      continue;
 +
                if (codec_dai->driver->ops->hw_free)
                        codec_dai->driver->ops->hw_free(substream, codec_dai);
                codec_dai->rate = 0;
@@@ -1093,9 -1088,6 +1093,9 @@@ static int soc_pcm_hw_free(struct snd_p
  
        /* now free hw params for the DAIs  */
        for_each_rtd_codec_dai(rtd, i, codec_dai) {
 +              if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
 +                      continue;
 +
                if (codec_dai->driver->ops->hw_free)
                        codec_dai->driver->ops->hw_free(substream, codec_dai);
        }
@@@ -2172,6 -2164,10 +2172,10 @@@ int dpcm_be_dai_hw_params(struct snd_so
                        }
                }
  
+               /* copy the fixed-up hw params for BE dai */
+               memcpy(&be->dpcm[stream].hw_params, &dpcm->hw_params,
+                      sizeof(struct snd_pcm_hw_params));
                /* only allow hw_params() if no connected FEs are running */
                if (!snd_soc_dpcm_can_be_params(fe, be, stream))
                        continue;
index d7045aa520de56eb42d108a14e92aee810d15f08,e3b021c9e8d0fbc5515e0755c407306faeed9ac1..2a74ce7c9440f56ddbcbb968a751a88efb0d4454
@@@ -70,7 -70,6 +70,7 @@@
  #define SAI_IEC60958_STATUS_BYTES     24
  
  #define SAI_MCLK_NAME_LEN             32
 +#define SAI_RATE_11K                  11025
  
  /**
   * struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
@@@ -110,7 -109,7 +110,7 @@@ struct stm32_sai_sub_data 
        struct regmap *regmap;
        const struct regmap_config *regmap_config;
        struct snd_dmaengine_dai_dma_data dma_params;
-       struct snd_soc_dai_driver *cpu_dai_drv;
+       struct snd_soc_dai_driver cpu_dai_drv;
        struct snd_soc_dai *cpu_dai;
        struct snd_pcm_substream *substream;
        struct stm32_sai_data *pdata;
@@@ -169,6 -168,7 +169,7 @@@ static bool stm32_sai_sub_volatile_reg(
  {
        switch (reg) {
        case STM_SAI_DR_REGX:
+       case STM_SAI_SR_REGX:
                return true;
        default:
                return false;
@@@ -183,7 -183,6 +184,6 @@@ static bool stm32_sai_sub_writeable_reg
        case STM_SAI_FRCR_REGX:
        case STM_SAI_SLOTR_REGX:
        case STM_SAI_IMR_REGX:
-       case STM_SAI_SR_REGX:
        case STM_SAI_CLRFR_REGX:
        case STM_SAI_DR_REGX:
        case STM_SAI_PDMCR_REGX:
@@@ -203,6 -202,7 +203,7 @@@ static const struct regmap_config stm32
        .volatile_reg = stm32_sai_sub_volatile_reg,
        .writeable_reg = stm32_sai_sub_writeable_reg,
        .fast_io = true,
+       .cache_type = REGCACHE_FLAT,
  };
  
  static const struct regmap_config stm32_sai_sub_regmap_config_h7 = {
        .volatile_reg = stm32_sai_sub_volatile_reg,
        .writeable_reg = stm32_sai_sub_writeable_reg,
        .fast_io = true,
+       .cache_type = REGCACHE_FLAT,
  };
  
  static int snd_pcm_iec958_info(struct snd_kcontrol *kcontrol,
@@@ -310,25 -311,6 +312,25 @@@ static int stm32_sai_set_clk_div(struc
        return ret;
  }
  
 +static int stm32_sai_set_parent_clock(struct stm32_sai_sub_data *sai,
 +                                    unsigned int rate)
 +{
 +      struct platform_device *pdev = sai->pdev;
 +      struct clk *parent_clk = sai->pdata->clk_x8k;
 +      int ret;
 +
 +      if (!(rate % SAI_RATE_11K))
 +              parent_clk = sai->pdata->clk_x11k;
 +
 +      ret = clk_set_parent(sai->sai_ck, parent_clk);
 +      if (ret)
 +              dev_err(&pdev->dev, " Error %d setting sai_ck parent clock. %s",
 +                      ret, ret == -EBUSY ?
 +                      "Active stream rates conflict\n" : "\n");
 +
 +      return ret;
 +}
 +
  static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate,
                                      unsigned long *prate)
  {
@@@ -461,8 -443,8 +463,8 @@@ static irqreturn_t stm32_sai_isr(int ir
        if (!flags)
                return IRQ_NONE;
  
-       regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK,
-                          SAI_XCLRFR_MASK);
+       regmap_write_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK,
+                         SAI_XCLRFR_MASK);
  
        if (!sai->substream) {
                dev_err(&pdev->dev, "Device stopped. Spurious IRQ 0x%x\n", sr);
@@@ -510,29 -492,25 +512,29 @@@ static int stm32_sai_set_sysclk(struct 
        struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
        int ret;
  
 -      if (dir == SND_SOC_CLOCK_OUT) {
 +      if (dir == SND_SOC_CLOCK_OUT && sai->sai_mclk) {
                ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
                                         SAI_XCR1_NODIV,
                                         (unsigned int)~SAI_XCR1_NODIV);
                if (ret < 0)
                        return ret;
  
 -              dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
 -              sai->mclk_rate = freq;
 +              /* If master clock is used, set parent clock now */
 +              ret = stm32_sai_set_parent_clock(sai, freq);
 +              if (ret)
 +                      return ret;
  
 -              if (sai->sai_mclk) {
 -                      ret = clk_set_rate_exclusive(sai->sai_mclk,
 -                                                   sai->mclk_rate);
 -                      if (ret) {
 -                              dev_err(cpu_dai->dev,
 -                                      "Could not set mclk rate\n");
 -                              return ret;
 -                      }
 +              ret = clk_set_rate_exclusive(sai->sai_mclk, freq);
 +              if (ret) {
 +                      dev_err(cpu_dai->dev,
 +                              ret == -EBUSY ?
 +                              "Active streams have incompatible rates" :
 +                              "Could not set mclk rate\n");
 +                      return ret;
                }
 +
 +              dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
 +              sai->mclk_rate = freq;
        }
  
        return 0;
@@@ -728,9 -706,8 +730,8 @@@ static int stm32_sai_startup(struct snd
        }
  
        /* Enable ITs */
-       regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX,
-                          SAI_XCLRFR_MASK, SAI_XCLRFR_MASK);
+       regmap_write_bits(sai->regmap, STM_SAI_CLRFR_REGX,
+                         SAI_XCLRFR_MASK, SAI_XCLRFR_MASK);
  
        imr = SAI_XIMR_OVRUDRIE;
        if (STM_SAI_IS_CAPTURE(sai)) {
@@@ -762,10 -739,10 +763,10 @@@ static int stm32_sai_set_config(struct 
         * SAI fifo threshold is set to half fifo, to keep enough space
         * for DMA incoming bursts.
         */
-       regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX,
-                          SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK,
-                          SAI_XCR2_FFLUSH |
-                          SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF));
+       regmap_write_bits(sai->regmap, STM_SAI_CR2_REGX,
+                         SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK,
+                         SAI_XCR2_FFLUSH |
+                         SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF));
  
        /* DS bits in CR1 not set for SPDIF (size forced to 24 bits).*/
        if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) {
@@@ -940,13 -917,11 +941,13 @@@ static int stm32_sai_configure_clock(st
        int div = 0, cr1 = 0;
        int sai_clk_rate, mclk_ratio, den;
        unsigned int rate = params_rate(params);
 +      int ret;
  
 -      if (!(rate % 11025))
 -              clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k);
 -      else
 -              clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k);
 +      if (!sai->sai_mclk) {
 +              ret = stm32_sai_set_parent_clock(sai, rate);
 +              if (ret)
 +                      return ret;
 +      }
        sai_clk_rate = clk_get_rate(sai->sai_ck);
  
        if (STM_SAI_IS_F4(sai->pdata)) {
@@@ -1105,13 -1080,9 +1106,13 @@@ static void stm32_sai_shutdown(struct s
        regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV,
                           SAI_XCR1_NODIV);
  
 -      clk_disable_unprepare(sai->sai_ck);
 +      /* Release mclk rate only if rate was actually set */
 +      if (sai->mclk_rate) {
 +              clk_rate_exclusive_put(sai->sai_mclk);
 +              sai->mclk_rate = 0;
 +      }
  
 -      clk_rate_exclusive_put(sai->sai_mclk);
 +      clk_disable_unprepare(sai->sai_ck);
  
        spin_lock_irqsave(&sai->irq_lock, flags);
        sai->substream = NULL;
@@@ -1233,8 -1204,7 +1234,7 @@@ static const struct snd_pcm_hardware st
        .periods_max = 8,
  };
  
- static struct snd_soc_dai_driver stm32_sai_playback_dai[] = {
- {
+ static struct snd_soc_dai_driver stm32_sai_playback_dai = {
                .probe = stm32_sai_dai_probe,
                .pcm_new = stm32_sai_pcm_new,
                .id = 1, /* avoid call to fmt_single_name() */
                                SNDRV_PCM_FMTBIT_S32_LE,
                },
                .ops = &stm32_sai_pcm_dai_ops,
-       }
  };
  
- static struct snd_soc_dai_driver stm32_sai_capture_dai[] = {
- {
+ static struct snd_soc_dai_driver stm32_sai_capture_dai = {
                .probe = stm32_sai_dai_probe,
                .id = 1, /* avoid call to fmt_single_name() */
                .capture = {
                                SNDRV_PCM_FMTBIT_S32_LE,
                },
                .ops = &stm32_sai_pcm_dai_ops,
-       }
  };
  
  static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = {
@@@ -1440,29 -1407,6 +1437,6 @@@ static int stm32_sai_sub_parse_of(struc
        return 0;
  }
  
- static int stm32_sai_sub_dais_init(struct platform_device *pdev,
-                                  struct stm32_sai_sub_data *sai)
- {
-       sai->cpu_dai_drv = devm_kzalloc(&pdev->dev,
-                                       sizeof(struct snd_soc_dai_driver),
-                                       GFP_KERNEL);
-       if (!sai->cpu_dai_drv)
-               return -ENOMEM;
-       if (STM_SAI_IS_PLAYBACK(sai)) {
-               memcpy(sai->cpu_dai_drv, &stm32_sai_playback_dai,
-                      sizeof(stm32_sai_playback_dai));
-               sai->cpu_dai_drv->playback.stream_name = sai->cpu_dai_drv->name;
-       } else {
-               memcpy(sai->cpu_dai_drv, &stm32_sai_capture_dai,
-                      sizeof(stm32_sai_capture_dai));
-               sai->cpu_dai_drv->capture.stream_name = sai->cpu_dai_drv->name;
-       }
-       sai->cpu_dai_drv->name = dev_name(&pdev->dev);
-       return 0;
- }
  static int stm32_sai_sub_probe(struct platform_device *pdev)
  {
        struct stm32_sai_sub_data *sai;
        if (ret)
                return ret;
  
-       ret = stm32_sai_sub_dais_init(pdev, sai);
-       if (ret)
-               return ret;
+       if (STM_SAI_IS_PLAYBACK(sai))
+               sai->cpu_dai_drv = stm32_sai_playback_dai;
+       else
+               sai->cpu_dai_drv = stm32_sai_capture_dai;
+       sai->cpu_dai_drv.name = dev_name(&pdev->dev);
  
        ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr,
                               IRQF_SHARED, dev_name(&pdev->dev), sai);
        }
  
        ret = devm_snd_soc_register_component(&pdev->dev, &stm32_component,
-                                             sai->cpu_dai_drv, 1);
+                                             &sai->cpu_dai_drv, 1);
        if (ret)
                return ret;
  
        return 0;
  }
  
+ #ifdef CONFIG_PM_SLEEP
+ static int stm32_sai_sub_suspend(struct device *dev)
+ {
+       struct stm32_sai_sub_data *sai = dev_get_drvdata(dev);
+       regcache_cache_only(sai->regmap, true);
+       regcache_mark_dirty(sai->regmap);
+       return 0;
+ }
+ static int stm32_sai_sub_resume(struct device *dev)
+ {
+       struct stm32_sai_sub_data *sai = dev_get_drvdata(dev);
+       regcache_cache_only(sai->regmap, false);
+       return regcache_sync(sai->regmap);
+ }
+ #endif /* CONFIG_PM_SLEEP */
+ static const struct dev_pm_ops stm32_sai_sub_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_sub_suspend, stm32_sai_sub_resume)
+ };
  static struct platform_driver stm32_sai_sub_driver = {
        .driver = {
                .name = "st,stm32-sai-sub",
                .of_match_table = stm32_sai_sub_ids,
+               .pm = &stm32_sai_sub_pm_ops,
        },
        .probe = stm32_sai_sub_probe,
  };