diff --git a/Documentation/devicetree/bindings/mfd/arizona.txt b/Documentation/devicetree/bindings/mfd/arizona.txt
index 18be0cb..4b3510a 100644
--- a/Documentation/devicetree/bindings/mfd/arizona.txt
+++ b/Documentation/devicetree/bindings/mfd/arizona.txt
@@ -44,6 +44,23 @@ Required properties:
 Optional properties:
 
   - wlf,reset : GPIO specifier for the GPIO controlling /RESET
+  - wlf,clk32k-src : set input source for codec 32kHz clock.
+      0 = default, 1 = MCLK1, 2 = MCLK2, 3 = None
+
+  - wlf,micd-ranges : Microphone detection level and key configuration, this
+    field can be of variable length but should always be a multiple of 2 cells
+    long, each two cell group represents one button configuration
+    The first cell is the maximum impedance for this button in ohms
+    The second cell the key that should be reported to the input layer
+  - wlf,micd-configs : Headset polarity configurations, the field can be of
+    variable length but should always be a multiple of 3 cells long, each two
+    cell group represents one polarity configration
+    The first cell is the accessory detection source as per the ACCDET_SRC bits
+    in the ACCESSORY_DETECT_MODE_1 register
+    The second cell represents the MICBIAS to be used as per the MICD_BIAS_SRC
+    bits in the MIC_DETECT_1 register
+    The third cell represents the value of the micd-pol-gpio pin, a non-zero
+    value indicates this should be on
 
   - wlf,gpio-defaults : A list of GPIO configuration register values. Defines
     for the appropriate values can found in <dt-bindings/mfd/arizona.txt>. If
@@ -51,6 +68,10 @@ Optional properties:
     a value that is out of range for a 16 bit register then the chip default
     will be used. If present exactly five values must be specified.
 
+  - wlf,dmic-ref : DMIC reference for each input, must contain four cells if
+    specified. 0 indicates MICVDD and is the default, 1,2,3 indicate the
+    respective MICBIAS.
+
   - wlf,inmode : A list of INn_MODE register values, where n is the number
     of input signals. Valid values are 0 (Differential), 1 (Single-ended) and
     2 (Digital Microphone). If absent, INn_MODE registers set to 0 by default.
@@ -87,6 +108,19 @@ codec: wm5102@1a {
 	gpio-controller;
 	#gpio-cells = <2>;
 
+	wlf,micd-ranges = <
+		11 0x100
+		28 0x101
+		54 0x102
+		100 0x103
+		186 0x104
+		430 0x105
+	>;
+	wlf,micd-configs = <
+		0x1 1 0
+		0x0 2 1
+	>;
+
 	wlf,gpio-defaults = <
 		ARIZONA_GP_FN_TXLRCLK
 		ARIZONA_GP_DEFAULT
@@ -94,4 +128,6 @@ codec: wm5102@1a {
 		ARIZONA_GP_DEFAULT
 		ARIZONA_GP_DEFAULT
 	>;
+
+	wlf,dmic-ref = <0 0 1 0>;
 };
diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile
index 7e740c8..459c720 100644
--- a/arch/arm/boot/dts/overlays/Makefile
+++ b/arch/arm/boot/dts/overlays/Makefile
@@ -66,6 +66,7 @@ dtbo-$(RPI_DT_OVERLAYS) += pwm-2chan.dtbo
 dtbo-$(RPI_DT_OVERLAYS) += qca7000.dtbo
 dtbo-$(RPI_DT_OVERLAYS) += raspidac3.dtbo
 dtbo-$(RPI_DT_OVERLAYS) += rpi-backlight.dtbo
+dtbo-$(RPI_DT_OVERLAYS) += rpi-cirrus-wm5102.dtbo
 dtbo-$(RPI_DT_OVERLAYS) += rpi-dac.dtbo
 dtbo-$(RPI_DT_OVERLAYS) += rpi-display.dtbo
 dtbo-$(RPI_DT_OVERLAYS) += rpi-ft5406.dtbo
diff --git a/arch/arm/boot/dts/overlays/rpi-cirrus-wm5102-overlay.dts b/arch/arm/boot/dts/overlays/rpi-cirrus-wm5102-overlay.dts
new file mode 100644
index 0000000..4de09b1
--- /dev/null
+++ b/arch/arm/boot/dts/overlays/rpi-cirrus-wm5102-overlay.dts
@@ -0,0 +1,138 @@
+// Definitions for Cirrus audio card
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2708";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			aliases {
+				ldo0 = &ldo0;
+				ldo1 = &ldo1;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&gpio>;
+		__overlay__ {
+			wlf_pins: wlf_pins {
+				brcm,pins = <17 22 27 8>;
+				brcm,function = <1 1 0 1>;
+			};
+		};
+	};
+
+	fragment@3 {
+		target-path = "/soc";
+		__overlay__ {
+
+			ldo1: ldo1 {
+				compatible = "regulator-fixed";
+				regulator-name = "DC_5V";
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5000000>;
+				enable-active-high;
+				regulator-always-on;
+			};
+
+			ldo0: ldo0 {
+				compatible = "regulator-fixed";
+				regulator-name = "DC_1V8";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				enable-active-high;
+				regulator-always-on;
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			spidev@0{
+				status = "disabled";
+			};
+
+			spidev@1{
+				status = "disabled";
+			};
+
+			wm5102@1{
+				compatible = "wlf,wm5102";
+				reg = <1>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <500000>;
+
+				interrupt-parent = <&gpio>;
+				interrupts = <27 8>;
+
+				LDOVDD-supply = <&ldo0>;
+				AVDD-supply = <&ldo0>;
+				DBVDD1-supply = <&ldo0>;
+				DBVDD2-supply = <&ldo0>;
+				DBVDD3-supply = <&ldo0>;
+				CPVDD-supply = <&ldo0>;
+				SPKVDDL-supply = <&ldo1>;
+				SPKVDDR-supply = <&ldo1>;
+
+				wlf,reset = <&gpio 17 0>;
+				wlf,ldoena = <&gpio 22 0>;
+				wlf,gpio-defaults = <
+					0x000fffff
+					0x000fffff
+					0x000fffff
+					0x000fffff
+					0x000fffff
+				>;
+				wlf,micd-configs = <0 1 0>;
+				wlf,dmic-ref = <0 2 0 0>;
+				wlf,clk32k-src = <3>;
+				wlf,inmode = <0 2 1 0>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@5 {
+		target = <&i2c1>;
+		__overlay__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			wm8804@3b {
+				#sound-dai-cells = <0>;
+				compatible = "wlf,wm8804";
+				reg = <0x3b>;
+				status = "okay";
+				PVDD-supply = <&ldo0>;
+				DVDD-supply = <&ldo0>;
+				wlf,reset-gpio = <&gpio 8 0>;
+			};
+		};
+	};
+
+	fragment@6 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "wlf,rpi-wm5102";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig
index 9a73179..2463de2 100644
--- a/arch/arm/configs/bcm2709_defconfig
+++ b/arch/arm/configs/bcm2709_defconfig
@@ -653,6 +653,9 @@ CONFIG_STMPE_SPI=y
 CONFIG_MFD_ARIZONA_I2C=m
 CONFIG_MFD_ARIZONA_SPI=m
 CONFIG_MFD_WM5102=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=m
+CONFIG_REGULATOR_ARIZONA=m
 CONFIG_MEDIA_SUPPORT=m
 CONFIG_MEDIA_CAMERA_SUPPORT=y
 CONFIG_MEDIA_ANALOG_TV_SUPPORT=y
@@ -860,6 +863,7 @@ CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
 CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
 CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m
 CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m
+CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP=m
 CONFIG_SND_BCM2708_SOC_RPI_DAC=m
 CONFIG_SND_BCM2708_SOC_RPI_PROTO=m
 CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC=m
diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig
index 41c549b..bd6b102 100644
--- a/arch/arm/configs/bcmrpi_defconfig
+++ b/arch/arm/configs/bcmrpi_defconfig
@@ -645,6 +645,9 @@ CONFIG_STMPE_SPI=y
 CONFIG_MFD_ARIZONA_I2C=m
 CONFIG_MFD_ARIZONA_SPI=m
 CONFIG_MFD_WM5102=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=m
+CONFIG_REGULATOR_ARIZONA=m
 CONFIG_MEDIA_SUPPORT=m
 CONFIG_MEDIA_CAMERA_SUPPORT=y
 CONFIG_MEDIA_ANALOG_TV_SUPPORT=y
@@ -852,6 +855,7 @@ CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
 CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
 CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m
 CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m
+CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP=m
 CONFIG_SND_BCM2708_SOC_RPI_DAC=m
 CONFIG_SND_BCM2708_SOC_RPI_PROTO=m
 CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC=m
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index d474732..a899b57 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -16,6 +16,7 @@
 #include <linux/interrupt.h>
 #include <linux/mfd/core.h>
 #include <linux/module.h>
+#include <linux/notifier.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
@@ -460,6 +461,20 @@ static int wm5102_clear_write_sequencer(struct arizona *arizona)
 	return 0;
 }
 
+static int arizona_dcvdd_notify(struct notifier_block *nb,
+				unsigned long action, void *data)
+{
+	struct arizona *arizona = container_of(nb, struct arizona,
+					       dcvdd_notifier);
+
+	dev_dbg(arizona->dev, "DCVDD notify %lx\n", action);
+
+	if (action & REGULATOR_EVENT_DISABLE)
+		msleep(20);
+
+	return NOTIFY_DONE;
+}
+
 #ifdef CONFIG_PM
 static int arizona_isolate_dcvdd(struct arizona *arizona)
 {
@@ -800,6 +815,154 @@ int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop,
 }
 EXPORT_SYMBOL_GPL(arizona_of_get_named_gpio);
 
+static int arizona_of_get_u32_num_groups(struct arizona *arizona,
+					const char *prop,
+					int group_size)
+{
+	int len_prop;
+	int num_groups;
+
+	if (!of_get_property(arizona->dev->of_node, prop, &len_prop))
+		return -EINVAL;
+
+	num_groups =  len_prop / (group_size * sizeof(u32));
+
+	if (num_groups * group_size * sizeof(u32) != len_prop) {
+		dev_err(arizona->dev,
+			"DT property %s is malformed: %d\n",
+			prop, -EOVERFLOW);
+		return -EOVERFLOW;
+	}
+
+	return num_groups;
+}
+
+static int arizona_of_get_micd_ranges(struct arizona *arizona,
+				      const char *prop)
+{
+	int nranges;
+	int i, j;
+	int ret = 0;
+	u32 value;
+	struct arizona_micd_range *micd_ranges;
+
+	nranges = arizona_of_get_u32_num_groups(arizona, prop, 2);
+	if (nranges < 0)
+		return nranges;
+
+	micd_ranges = devm_kzalloc(arizona->dev,
+				   nranges * sizeof(struct arizona_micd_range),
+				   GFP_KERNEL);
+
+	for (i = 0, j = 0; i < nranges; ++i) {
+		ret = of_property_read_u32_index(arizona->dev->of_node,
+						 prop, j++, &value);
+		if (ret < 0)
+			goto error;
+		micd_ranges[i].max = value;
+
+		ret = of_property_read_u32_index(arizona->dev->of_node,
+						 prop, j++, &value);
+		if (ret < 0)
+			goto error;
+		micd_ranges[i].key = value;
+	}
+
+	arizona->pdata.micd_ranges = micd_ranges;
+	arizona->pdata.num_micd_ranges = nranges;
+
+	return ret;
+
+error:
+	devm_kfree(arizona->dev, micd_ranges);
+	dev_err(arizona->dev, "DT property %s is malformed: %d\n", prop, ret);
+	return ret;
+}
+
+static int arizona_of_get_micd_configs(struct arizona *arizona,
+				       const char *prop)
+{
+	int nconfigs;
+	int i, j;
+	int ret = 0;
+	u32 value;
+	struct arizona_micd_config *micd_configs;
+
+	nconfigs = arizona_of_get_u32_num_groups(arizona, prop, 3);
+	if (nconfigs < 0)
+		return nconfigs;
+
+	micd_configs = devm_kzalloc(arizona->dev,
+				    nconfigs *
+				    sizeof(struct arizona_micd_config),
+				    GFP_KERNEL);
+
+	for (i = 0, j = 0; i < nconfigs; ++i) {
+		ret = of_property_read_u32_index(arizona->dev->of_node,
+						 prop, j++, &value);
+		if (ret < 0)
+			goto error;
+		micd_configs[i].src = value;
+
+		ret = of_property_read_u32_index(arizona->dev->of_node,
+						 prop, j++, &value);
+		if (ret < 0)
+			goto error;
+		micd_configs[i].bias = value;
+
+		ret = of_property_read_u32_index(arizona->dev->of_node,
+						 prop, j++, &value);
+		if (ret < 0)
+			goto error;
+		micd_configs[i].gpio = value;
+	}
+
+	arizona->pdata.micd_configs = micd_configs;
+	arizona->pdata.num_micd_configs = nconfigs;
+
+	return ret;
+
+error:
+	devm_kfree(arizona->dev, micd_configs);
+	dev_err(arizona->dev, "DT property %s is malformed: %d\n", prop, ret);
+	return ret;
+}
+
+static int arizona_of_read_u32_array(struct arizona *arizona,
+				     const char *prop, bool mandatory,
+				     u32 *data, size_t num)
+{
+	int ret;
+
+	ret = of_property_read_u32_array(arizona->dev->of_node, prop,
+					 data, num);
+
+	if (ret >= 0)
+		return 0;
+
+	switch (ret) {
+	case -EINVAL:
+		if (mandatory)
+			dev_err(arizona->dev,
+				"Mandatory DT property %s is missing\n",
+				prop);
+		break;
+	default:
+		dev_err(arizona->dev,
+			"DT property %s is malformed: %d\n",
+			prop, ret);
+	}
+
+	return ret;
+}
+
+static int arizona_of_read_u32(struct arizona *arizona,
+			       const char* prop, bool mandatory,
+			       u32 *data)
+{
+	return arizona_of_read_u32_array(arizona, prop, mandatory, data, 1);
+}
+
 static int arizona_of_get_core_pdata(struct arizona *arizona)
 {
 	struct arizona_pdata *pdata = &arizona->pdata;
@@ -833,6 +996,15 @@ static int arizona_of_get_core_pdata(struct arizona *arizona)
 			ret);
 	}
 
+	arizona_of_read_u32(arizona, "wlf,clk32k-src", false,
+				&pdata->clk32k_src);
+
+	arizona_of_get_micd_ranges(arizona, "wlf,micd-ranges");
+	arizona_of_get_micd_configs(arizona, "wlf,micd-configs");
+
+	arizona_of_read_u32_array(arizona, "wlf,dmic-ref", false,
+				  pdata->dmic_ref, ARRAY_SIZE(pdata->dmic_ref));
+
 	of_property_for_each_u32(arizona->dev->of_node, "wlf,inmode", prop,
 				 cur, val) {
 		if (count == ARRAY_SIZE(pdata->inmode))
@@ -852,6 +1024,9 @@ static int arizona_of_get_core_pdata(struct arizona *arizona)
 		count++;
 	}
 
+	arizona_of_read_u32(arizona, "wlf,irq_gpio", false,
+				&pdata->irq_gpio);
+
 	return 0;
 }
 
@@ -1029,6 +1204,14 @@ int arizona_dev_init(struct arizona *arizona)
 		goto err_early;
 	}
 
+	arizona->dcvdd_notifier.notifier_call = arizona_dcvdd_notify;
+	ret = regulator_register_notifier(arizona->dcvdd,
+					  &arizona->dcvdd_notifier);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register DCVDD notifier %d\n", ret);
+		goto err_dcvdd;
+	}
+
 	if (arizona->pdata.reset) {
 		/* Start out with /RESET low to put the chip into reset */
 		ret = devm_gpio_request_one(arizona->dev, arizona->pdata.reset,
@@ -1036,16 +1219,19 @@ int arizona_dev_init(struct arizona *arizona)
 					    "arizona /RESET");
 		if (ret != 0) {
 			dev_err(dev, "Failed to request /RESET: %d\n", ret);
-			goto err_dcvdd;
+			goto err_notifier;
 		}
 	}
 
+	/* Ensure period of reset asserted before we apply the supplies */
+	msleep(20);
+
 	ret = regulator_bulk_enable(arizona->num_core_supplies,
 				    arizona->core_supplies);
 	if (ret != 0) {
 		dev_err(dev, "Failed to enable core supplies: %d\n",
 			ret);
-		goto err_dcvdd;
+		goto err_notifier;
 	}
 
 	ret = regulator_enable(arizona->dcvdd);
@@ -1420,6 +1606,8 @@ err_reset:
 err_enable:
 	regulator_bulk_disable(arizona->num_core_supplies,
 			       arizona->core_supplies);
+err_notifier:
+	regulator_unregister_notifier(arizona->dcvdd, &arizona->dcvdd_notifier);
 err_dcvdd:
 	regulator_put(arizona->dcvdd);
 err_early:
@@ -1433,6 +1621,7 @@ int arizona_dev_exit(struct arizona *arizona)
 	pm_runtime_disable(arizona->dev);
 
 	regulator_disable(arizona->dcvdd);
+	regulator_unregister_notifier(arizona->dcvdd, &arizona->dcvdd_notifier);
 	regulator_put(arizona->dcvdd);
 
 	mfd_remove_devices(arizona->dev);
diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c
index f7c88ff..19fdcd4 100644
--- a/drivers/regulator/arizona-ldo1.c
+++ b/drivers/regulator/arizona-ldo1.c
@@ -288,6 +288,7 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
 	}
 
 	config.ena_gpio = arizona->pdata.ldoena;
+	config.ena_gpio_flags = GPIOF_OUT_INIT_LOW;
 
 	if (arizona->pdata.ldo1)
 		config.init_data = arizona->pdata.ldo1;
diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h
index 79e607e..39a23eb 100644
--- a/include/linux/mfd/arizona/core.h
+++ b/include/linux/mfd/arizona/core.h
@@ -119,6 +119,7 @@ struct arizona {
 	int num_core_supplies;
 	struct regulator_bulk_data core_supplies[ARIZONA_MAX_CORE_SUPPLIES];
 	struct regulator *dcvdd;
+	struct notifier_block dcvdd_notifier;
 	bool has_fully_powered_off;
 
 	struct arizona_pdata pdata;
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 95a937e..8262719 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -383,10 +383,11 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
 void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card);
 int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
-			 const struct snd_soc_pcm_stream *params,
+			 struct snd_soc_pcm_stream *params,
 			 unsigned int num_params,
 			 struct snd_soc_dapm_widget *source,
-			 struct snd_soc_dapm_widget *sink);
+			 struct snd_soc_dapm_widget *sink,
+			 void *priv);
 
 /* dapm path setup */
 int snd_soc_dapm_new_widgets(struct snd_soc_card *card);
@@ -553,7 +554,7 @@ struct snd_soc_dapm_widget {
 
 	void *priv;				/* widget specific data */
 	struct regulator *regulator;		/* attached regulator */
-	const struct snd_soc_pcm_stream *params; /* params for dai links */
+	struct snd_soc_pcm_stream *params; /* params for dai links */
 	unsigned int num_params; /* number of params for dai links */
 	unsigned int params_select; /* currently selected param for dai link */
 
diff --git a/include/sound/soc.h b/include/sound/soc.h
index fb955e6..bbdc05d 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -989,7 +989,9 @@ struct snd_soc_dai_link {
 	struct device_node *platform_of_node;
 	int be_id;	/* optional ID for machine driver BE identification */
 
-	const struct snd_soc_pcm_stream *params;
+	struct snd_soc_pcm_stream *params;
+	/* optional params re-writing for dai links */
+	int (*params_fixup)(struct snd_soc_dapm_widget *w, int event);
 	unsigned int num_params;
 
 	unsigned int dai_fmt;           /* format to set on init */
diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
index 8873fd8a..a727a79 100644
--- a/sound/soc/bcm/Kconfig
+++ b/sound/soc/bcm/Kconfig
@@ -36,6 +36,14 @@ config SND_BCM2708_SOC_HIFIBERRY_AMP
         help
          Say Y or M if you want to add support for the HifiBerry Amp amplifier board.
 
+config SND_BCM2708_SOC_RPI_CODEC_WSP
+	tristate "Support for Cirrus sound pi"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_WM5102
+	select SND_SOC_WM8804
+	help
+	  Say Y or M if you want to add support for Cirrus sound pi
+
 config SND_BCM2708_SOC_RPI_DAC
         tristate "Support for RPi-DAC"
         depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile
index 17f70f9..b7b5a7b 100644
--- a/sound/soc/bcm/Makefile
+++ b/sound/soc/bcm/Makefile
@@ -13,6 +13,7 @@ snd-soc-justboom-dac-objs := justboom-dac.o
 snd-soc-justboom-digi-objs := justboom-digi.o
 snd-soc-rpi-dac-objs := rpi-dac.o
 snd-soc-rpi-proto-objs := rpi-proto.o
+snd-soc-rpi-wsp-objs := rpi-cirrus-sound-pi.o
 snd-soc-iqaudio-dac-objs := iqaudio-dac.o
 snd-soc-iqaudio-digi-objs := iqaudio_digi.o
 snd-soc-raspidac3-objs := raspidac3.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o
 obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) += snd-soc-hifiberry-amp.o
 obj-$(CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC) += snd-soc-justboom-dac.o
 obj-$(CONFIG_SND_BCM2708_SOC_JUSTBOOM_DIGI) += snd-soc-justboom-digi.o
+obj-$(CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP) += snd-soc-rpi-wsp.o
 obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o
 obj-$(CONFIG_SND_BCM2708_SOC_RPI_PROTO) += snd-soc-rpi-proto.o
 obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o
diff --git a/sound/soc/bcm/rpi-cirrus-sound-pi.c b/sound/soc/bcm/rpi-cirrus-sound-pi.c
new file mode 100644
index 0000000..3abfff1
--- /dev/null
+++ b/sound/soc/bcm/rpi-cirrus-sound-pi.c
@@ -0,0 +1,638 @@
+/*
+ * ASoC machine driver for Cirrus Audio Card (with a WM5102 and WM8804 codecs )
+ * connected to a Raspberry Pi
+ *
+ * Copyright 2015 Cirrus Logic Inc.
+ *
+ * Author:	Nikesh Oswal, <Nikesh.Oswal@wolfsonmicro.com>
+ * Partly based on sound/soc/bcm/iqaudio-dac.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/wm5102.h"
+#include "../codecs/wm8804.h"
+
+#define WM8804_CLKOUT_HZ 12000000
+
+#define RPI_WLF_SR 44100
+#define WM5102_MAX_SYSCLK_1 49152000 /*max sysclk for 4K family*/
+#define WM5102_MAX_SYSCLK_2 45158400 /*max sysclk for 11.025K family*/
+
+#define DAI_WM5102 0
+#define DAI_WM8804 1
+
+static struct snd_soc_card snd_rpi_wsp;
+
+struct wm5102_machine_priv {
+	int wm8804_sr;
+	int wm5102_sr;
+	int sync_path_enable;
+	int fll1_freq; /* negative means RefClock in spdif rx case */
+	/* mutex for synchronzing FLL1 access with DAPM */
+	struct mutex fll1_mutex;
+};
+
+int spdif_rx_enable_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_card *card = &snd_rpi_wsp;
+	struct wm5102_machine_priv *priv = snd_soc_card_get_drvdata(card);
+	struct snd_soc_pcm_runtime *wm5102_rtd;
+	struct snd_soc_codec *wm5102_codec;
+	int ret = 0;
+	int clk_freq;
+	int sr = priv->wm8804_sr;
+
+	dev_dbg(card->dev, "spdif_rx event %d\n", event);
+
+	wm5102_rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_WM5102].name);
+	if (!wm5102_rtd) {
+		dev_warn(card->dev, "spdif_rx_enable_event: couldn't get WM5102 rtd\n");
+		return -EFAULT;
+	}
+	wm5102_codec = wm5102_rtd->codec;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		mutex_lock(&priv->fll1_mutex);
+
+		dev_dbg(wm5102_codec->dev,
+			"spdif_rx: changing FLL1 to use Ref Clock\n");
+
+		/* Enable sync path in case of SPDIF capture use case */
+		clk_freq = (sr % 4000 == 0) ? WM5102_MAX_SYSCLK_1 : WM5102_MAX_SYSCLK_2;
+
+		/*reset FLL1*/
+		snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1_REFCLK,
+					ARIZONA_FLL_SRC_NONE, 0, 0);
+		snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1,
+					ARIZONA_FLL_SRC_NONE, 0, 0);
+
+		ret = snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1_REFCLK,
+					    ARIZONA_CLK_SRC_MCLK1,
+					    WM8804_CLKOUT_HZ,
+					    clk_freq);
+		if (ret != 0) {
+			dev_err(wm5102_codec->dev, "Failed to enable FLL1 with Ref Clock Loop: %d\n", ret);
+			mutex_unlock(&priv->fll1_mutex);
+			return ret;
+		}
+
+		ret = snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1,
+					    ARIZONA_CLK_SRC_AIF2BCLK,
+					    sr * 64, clk_freq);
+		if (!ret)
+			/* set to negative to indicate we're doing spdif rx */
+			priv->fll1_freq = -clk_freq;
+
+		mutex_unlock(&priv->fll1_mutex);
+
+		if (ret != 0) {
+			dev_err(wm5102_codec->dev, "Failed to enable FLL1  Sync Clock Loop: %d\n", ret);
+			return ret;
+		}
+		priv->sync_path_enable = 1;
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		priv->sync_path_enable = 0;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct snd_kcontrol_new rpi_wsp_controls[] = {
+	SOC_DAPM_PIN_SWITCH("DMIC"),
+	SOC_DAPM_PIN_SWITCH("Headset Mic"),
+	SOC_DAPM_PIN_SWITCH("SPDIF Out"),
+	SOC_DAPM_PIN_SWITCH("SPDIF In"),
+	SOC_DAPM_PIN_SWITCH("Line Input"),
+};
+
+const struct snd_soc_dapm_widget rpi_wsp_dapm_widgets[] = {
+	SND_SOC_DAPM_MIC("DMIC", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Line Input", NULL),
+	SND_SOC_DAPM_INPUT("dummy SPDIF in"),
+	SND_SOC_DAPM_PGA_E("dummy SPDIFRX", SND_SOC_NOPM, 0, 0,NULL, 0,
+			spdif_rx_enable_event,
+			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+const struct snd_soc_dapm_route rpi_wsp_dapm_routes[] = {
+	{ "IN1L", NULL, "Headset Mic" },
+	{ "IN1R", NULL, "Headset Mic" },
+	{ "Headset Mic", NULL, "MICBIAS1" },
+
+	{ "IN2L", NULL, "DMIC" },
+	{ "IN2R", NULL, "DMIC" },
+	{ "DMIC", NULL, "MICBIAS2" },
+
+	{ "IN3L", NULL, "Line Input" },
+	{ "IN3R", NULL, "Line Input" },
+	{ "Line Input", NULL, "MICBIAS3" },
+
+	/* Dummy routes to check whether SPDIF RX is enabled or not */
+	{"dummy SPDIFRX", NULL, "dummy SPDIF in"},
+	{"AIFTX", NULL, "dummy SPDIFRX"},
+};
+
+static int rpi_set_bias_level(struct snd_soc_card *card,
+				struct snd_soc_dapm_context *dapm,
+				enum snd_soc_bias_level level)
+{
+	struct snd_soc_pcm_runtime *wm5102_rtd;
+	struct snd_soc_codec *wm5102_codec;
+	struct snd_soc_dai *wm5102_codec_dai;
+	struct wm5102_machine_priv *priv = snd_soc_card_get_drvdata(card);
+
+	int ret;
+	int sr = priv->wm5102_sr;
+	int clk_freq = (sr % 4000 == 0) ? WM5102_MAX_SYSCLK_1 : WM5102_MAX_SYSCLK_2;
+
+	wm5102_rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_WM5102].name);
+	if (!wm5102_rtd) {
+		dev_warn(card->dev, "rpi_set_bias_level: couldn't get WM5102 rtd\n");
+		return -EFAULT;
+	}
+	wm5102_codec = wm5102_rtd->codec;
+	wm5102_codec_dai = wm5102_rtd->codec_dai;
+
+	if (dapm->dev != wm5102_codec_dai->dev)
+		return 0;
+
+	dev_dbg(wm5102_codec->dev, "change bias level from %d to %d, sync=%d\n",
+		dapm->bias_level, level, priv->sync_path_enable);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		/* no need to check current level, it can only be PREPARE */
+		if (!priv->sync_path_enable) {
+			mutex_lock(&priv->fll1_mutex);
+
+			dev_dbg(wm5102_codec->dev,
+				"bias_on: changing FLL1 from %d to %d\n",
+				priv->fll1_freq, clk_freq);
+
+			ret = snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1,
+						    ARIZONA_CLK_SRC_MCLK1,
+						    WM8804_CLKOUT_HZ,
+						    clk_freq);
+			if (!ret)
+				priv->fll1_freq = clk_freq;
+
+			mutex_unlock(&priv->fll1_mutex);
+
+			if (ret != 0) {
+				dev_err(wm5102_codec->dev, "Failed to enable FLL1: %d\n", ret);
+				return ret;
+			}
+		}
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
+			break;
+
+		mutex_lock(&priv->fll1_mutex);
+
+		dev_dbg(wm5102_codec->dev,
+			"bias_standby: changing FLL1 from %d to off\n",
+			priv->fll1_freq);
+
+		ret = snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1,
+			ARIZONA_FLL_SRC_NONE, 0, 0);
+		if (ret)
+			 dev_warn(wm5102_codec->dev, "set_bias_level: Failed to stop FLL1: %d\n", ret);
+
+		ret = snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1_REFCLK,
+			ARIZONA_FLL_SRC_NONE, 0, 0);
+		if (ret)
+			 dev_warn(wm5102_codec->dev, "set_bias_level: Failed to stop FLL1_REFCLK: %d\n", ret);
+
+		priv->fll1_freq = 0;
+
+		mutex_unlock(&priv->fll1_mutex);
+
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int snd_rpi_wsp_config_5102_clks(
+	struct wm5102_machine_priv *priv,
+	struct snd_soc_codec *wm5102_codec, int sr)
+{
+	int ret;
+	int clk_freq = (sr % 4000 == 0) ? WM5102_MAX_SYSCLK_1 : WM5102_MAX_SYSCLK_2;
+
+	/*
+	 * Manually set up FLL1 if it's configured to another rate but only
+	 * if we are not using spdif rx (fll1_freq negative).
+	 * This is necessary if delayed DAPM powerdown hasn't stopped
+	 * the FLL before.
+	 */
+	if ((priv->fll1_freq > 0) && (priv->fll1_freq != clk_freq)) {
+		mutex_lock(&priv->fll1_mutex);
+
+		dev_dbg(wm5102_codec->dev,
+			"config_5102_clks: changing FLL1 from %d to %d\n",
+			priv->fll1_freq, clk_freq);
+
+		/*reset FLL1*/
+		snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1,
+			ARIZONA_FLL_SRC_NONE, 0, 0);
+
+		ret = snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1,
+						ARIZONA_CLK_SRC_MCLK1,
+						WM8804_CLKOUT_HZ,
+						clk_freq);
+		if (!ret)
+			priv->fll1_freq = clk_freq;
+
+		mutex_unlock(&priv->fll1_mutex);
+
+		if (ret) {
+			dev_err(wm5102_codec->dev, "Failed to set FLL1: %d\n", ret);
+			return ret;
+		}
+
+	}
+
+	ret = snd_soc_codec_set_sysclk(wm5102_codec,
+			ARIZONA_CLK_SYSCLK,
+			ARIZONA_CLK_SRC_FLL1,
+			clk_freq,
+			SND_SOC_CLOCK_IN);
+	if (ret != 0) {
+		dev_err(wm5102_codec->dev, "Failed to set AYNCCLK: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+ }
+
+static int snd_rpi_wsp_config_8804_clks(struct snd_soc_codec *wm8804_codec,
+	struct snd_soc_dai *wm8804_dai, int sr)
+ {
+	int ret;
+
+	/*Set OSC(12MHz) to CLK2 freq*/
+	/*Based on MCLKDIV it will be 128fs (MCLKDIV=1) or 256fs mode (MCLKDIV=0)*/
+	/*BCLK will be MCLK/2  (MCLKDIV=1) or MCLK/4  (MCLKDIV=0) so BCLK is 64fs always*/
+	ret = snd_soc_dai_set_pll(wm8804_dai, 0, 0, WM8804_CLKOUT_HZ, sr * 256);
+	if (ret != 0) {
+		dev_err(wm8804_codec->dev, "Failed to set OSC to CLK2 frequency: %d\n", ret);
+		return ret;
+	}
+
+	/*Set MCLK as PLL Output*/
+	ret = snd_soc_dai_set_sysclk(wm8804_dai, WM8804_TX_CLKSRC_PLL, sr * 256, 0);
+	if (ret != 0) {
+		dev_err(wm8804_codec->dev, "Failed to set MCLK as PLL Output: %d\n", ret);
+		return ret;
+	}
+
+	/*Fix MCLKDIV=0 for 256fs to avoid any issues switching between TX and RX. RX always expects 256fs*/
+	ret = snd_soc_dai_set_clkdiv(wm8804_dai, WM8804_MCLK_DIV, 0 );
+	if (ret != 0) {
+		dev_err(wm8804_codec->dev, "Failed to set MCLK_DIV to 256fs: %d\n", ret);
+		return ret;
+	}
+
+	/*Set CLKOUT as OSC Frequency*/
+	ret = snd_soc_dai_set_sysclk(wm8804_dai, WM8804_CLKOUT_SRC_OSCCLK, WM8804_CLKOUT_HZ, 0);
+	if (ret != 0) {
+		dev_err(wm8804_codec->dev, "Failed to set CLKOUT as OSC Frequency: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int snd_rpi_wsp_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *card = rtd->card;
+	struct snd_soc_pcm_runtime *wm5102_rtd, *wm8804_rtd;
+	struct snd_soc_codec *wm5102_codec, *wm8804_codec;
+	struct snd_soc_dai *wm8804_codec_dai, *bcm_i2s_dai = rtd->cpu_dai;
+	struct wm5102_machine_priv *priv = snd_soc_card_get_drvdata(card);
+	int ret, capture_stream_opened,playback_stream_opened;
+	unsigned int bclkratio, tx_mask, rx_mask;
+	int width, num_slots=1;
+
+	wm5102_rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_WM5102].name);
+	if (!wm5102_rtd) {
+		dev_warn(card->dev, "snd_rpi_wsp_hw_params: couldn't get WM5102 rtd\n");
+		return -EFAULT;
+	}
+	wm8804_rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_WM8804].name);
+	if (!wm8804_rtd) {
+		dev_warn(card->dev, "snd_rpi_wsp_hw_params: couldn't get WM8804 rtd\n");
+		return -EFAULT;
+	}
+	wm5102_codec = wm5102_rtd->codec;
+	wm8804_codec = wm8804_rtd->codec;
+	wm8804_codec_dai = wm8804_rtd->codec_dai;
+
+	bclkratio = 2 * snd_pcm_format_physical_width(params_format(params));
+
+	ret = snd_soc_dai_set_bclk_ratio(bcm_i2s_dai, bclkratio);
+	if (ret < 0) {
+		dev_err(wm5102_codec->dev, "set_bclk_ratio failed: %d\n", ret);
+		return ret;
+	}
+
+	/*8804 supports sample rates from 32k only*/
+	/*Setting <32k raises error from 8804 driver while setting the clock*/
+	if(params_rate(params) >= 32000)
+	{
+		ret = snd_rpi_wsp_config_8804_clks(wm8804_codec, wm8804_codec_dai,
+						params_rate(params));
+
+		if (ret != 0) {
+			dev_err(wm8804_codec->dev, "snd_rpi_wsp_config_8804_clks failed: %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	capture_stream_opened =
+		substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_opened;
+	playback_stream_opened =
+		substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_opened;
+
+	priv->wm5102_sr =  params_rate(params);
+
+	ret = snd_rpi_wsp_config_5102_clks(priv, wm5102_codec,  params_rate(params));
+	if (ret != 0) {
+		dev_err(wm5102_codec->dev, "snd_rpi_wsp_config_5102_clks failed: %d\n", ret);
+		return ret;
+	}
+
+	width = snd_pcm_format_physical_width(params_format(params));
+
+	if (capture_stream_opened) {
+		tx_mask = 0;
+		rx_mask = 1;
+	}
+	if (playback_stream_opened) {
+		tx_mask = 1;
+		rx_mask = 0;
+	}
+	ret = snd_soc_dai_set_tdm_slot(rtd->codec_dai, tx_mask, rx_mask, num_slots, width);
+	if (ret < 0)
+		return ret;
+
+	priv->wm8804_sr =  params_rate(params);
+
+	return 0;
+}
+
+static int dai_link2_params_fixup(struct snd_soc_dapm_widget *w, int event)
+{
+	struct snd_soc_card *card = &snd_rpi_wsp;
+	struct wm5102_machine_priv *priv = snd_soc_card_get_drvdata(card);
+	struct snd_soc_pcm_stream *config = w->params;
+
+	if (event == SND_SOC_DAPM_PRE_PMU) {
+		config->rate_min = priv->wm8804_sr;
+		config->rate_max = priv->wm8804_sr;
+	} else if (event == SND_SOC_DAPM_PRE_PMD) {
+		config->rate_min = RPI_WLF_SR;
+		config->rate_max = RPI_WLF_SR;
+	}
+
+	return 0;
+}
+
+static int snd_rpi_wsp_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec *wm5102_codec = rtd->codec;
+	int ret,playback_stream_opened,capture_stream_opened;
+
+	playback_stream_opened = substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_opened;
+
+	capture_stream_opened = substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_opened;
+
+	if((playback_stream_opened + capture_stream_opened) == 1){
+
+		ret = snd_soc_codec_set_sysclk(wm5102_codec,
+			ARIZONA_CLK_SYSCLK,
+			ARIZONA_CLK_SRC_FLL1,
+			0,
+			SND_SOC_CLOCK_IN);
+
+		if (ret != 0) {
+		dev_err(wm5102_codec->dev, "Failed to set SYSCLK to Zero: %d\n", ret);
+		return ret;
+		}
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops snd_rpi_wsp_ops = {
+	.hw_params = snd_rpi_wsp_hw_params,
+	.hw_free = snd_rpi_wsp_hw_free,
+};
+
+static struct snd_soc_pcm_stream dai_link2_params = {
+	.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	.rate_min = RPI_WLF_SR,
+	.rate_max = RPI_WLF_SR,
+	.channels_min = 2,
+	.channels_max = 2,
+};
+
+static struct snd_soc_dai_link snd_rpi_wsp_dai[] = {
+	{
+		.name		= "WM5102",
+		.stream_name	= "WM5102 AiFi",
+		.cpu_dai_name	= "bcm2708-i2s.0",
+		.codec_dai_name	= "wm5102-aif1",
+		.platform_name	= "bcm2708-i2s.0",
+		.codec_name	= "wm5102-codec",
+		.dai_fmt	= SND_SOC_DAIFMT_I2S
+					| SND_SOC_DAIFMT_NB_NF
+					| SND_SOC_DAIFMT_CBM_CFM,
+		.ops		= &snd_rpi_wsp_ops,
+	},
+	{
+		.name = "WM5102 SPDIF",
+		.stream_name = "SPDIF Tx/Rx",
+		.cpu_dai_name = "wm5102-aif2",
+		.codec_dai_name = "wm8804-spdif",
+		.codec_name = "wm8804.1-003b",
+		.dai_fmt = SND_SOC_DAIFMT_I2S
+			| SND_SOC_DAIFMT_NB_NF
+			| SND_SOC_DAIFMT_CBM_CFM,
+		.ignore_suspend = 1,
+		.params = &dai_link2_params,
+		.params_fixup = dai_link2_params_fixup,
+	},
+};
+
+static int snd_rpi_wsp_late_probe(struct snd_soc_card *card)
+{
+	struct wm5102_machine_priv *priv = snd_soc_card_get_drvdata(card);
+	struct snd_soc_pcm_runtime *wm5102_rtd, *wm8804_rtd;
+	struct snd_soc_codec *wm5102_codec, *wm8804_codec;
+	struct snd_soc_dai *wm5102_codec_dai, *wm8804_codec_dai, *wm8804_cpu_dai;
+	int ret;
+
+	wm5102_rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_WM5102].name);
+	if (!wm5102_rtd) {
+		dev_warn(card->dev, "snd_rpi_wsp_late_probe: couldn't get WM5102 rtd\n");
+		return -EFAULT;
+	}
+	wm8804_rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_WM8804].name);
+	if (!wm8804_rtd) {
+		dev_warn(card->dev, "snd_rpi_wsp_late_probe: couldn't get WM8804 rtd\n");
+		return -EFAULT;
+	}
+	wm5102_codec = wm5102_rtd->codec;
+	wm5102_codec_dai = wm5102_rtd->codec_dai;
+	wm8804_codec = wm8804_rtd->codec;
+	wm8804_codec_dai = wm8804_rtd->codec_dai;
+	wm8804_cpu_dai = wm8804_rtd->cpu_dai;
+
+	priv->wm8804_sr = RPI_WLF_SR;
+	priv->wm5102_sr = RPI_WLF_SR;
+	priv->sync_path_enable = 0;
+
+	ret = snd_soc_codec_set_sysclk(wm5102_codec, ARIZONA_CLK_SYSCLK, ARIZONA_CLK_SRC_FLL1,
+					0, SND_SOC_CLOCK_IN);
+	if (ret != 0) {
+		dev_err(wm5102_codec->dev, "Failed to set SYSCLK to Zero: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_rpi_wsp_config_8804_clks(wm8804_codec, wm8804_codec_dai, RPI_WLF_SR);
+
+	if (ret != 0) {
+		dev_err(wm8804_codec->dev, "snd_rpi_wsp_config_8804_clks failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_sysclk(wm5102_codec_dai,  ARIZONA_CLK_SYSCLK, 0, 0);
+	if (ret != 0) {
+		dev_err(wm5102_codec_dai->dev, "Failed to set codec dai clk domain: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_sysclk(wm8804_cpu_dai, ARIZONA_CLK_SYSCLK, 0, 0);
+	if (ret != 0) {
+		dev_err(wm8804_cpu_dai->dev, "Failed to set codec dai clk domain: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_wsp = {
+	.name		= "snd_rpi_wsp",
+	.owner		= THIS_MODULE,
+	.dai_link	= snd_rpi_wsp_dai,
+	.num_links	= ARRAY_SIZE(snd_rpi_wsp_dai),
+	.late_probe = snd_rpi_wsp_late_probe,
+	.controls = rpi_wsp_controls,
+	.num_controls = ARRAY_SIZE(rpi_wsp_controls),
+	.dapm_widgets = rpi_wsp_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(rpi_wsp_dapm_widgets),
+	.dapm_routes = rpi_wsp_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(rpi_wsp_dapm_routes),
+	.set_bias_level = rpi_set_bias_level,
+};
+
+static int snd_rpi_wsp_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct wm5102_machine_priv *wm5102;
+
+	wm5102 = kzalloc(sizeof *wm5102, GFP_KERNEL);
+	if (!wm5102)
+		return -ENOMEM;
+
+	wm5102->fll1_freq = 0;
+	mutex_init(&wm5102->fll1_mutex);
+
+	snd_soc_card_set_drvdata(&snd_rpi_wsp, wm5102);
+
+	if (pdev->dev.of_node) {
+	    struct device_node *i2s_node;
+	    struct snd_soc_dai_link *dai = &snd_rpi_wsp_dai[DAI_WM5102];
+	    i2s_node = of_parse_phandle(pdev->dev.of_node,
+					"i2s-controller", 0);
+
+	    if (i2s_node) {
+		dai->cpu_dai_name = NULL;
+		dai->cpu_of_node = i2s_node;
+		dai->platform_name = NULL;
+		dai->platform_of_node = i2s_node;
+	    }
+	}
+
+	snd_rpi_wsp.dev = &pdev->dev;
+	ret = snd_soc_register_card(&snd_rpi_wsp);
+	if (ret) {
+		if (ret == -EPROBE_DEFER)
+			dev_dbg(&pdev->dev, "register card requested probe deferral\n");
+		else
+			dev_err(&pdev->dev, "Failed to register card: %d\n", ret);
+
+		kfree(wm5102);
+	}
+
+	return ret;
+}
+
+static int snd_rpi_wsp_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &snd_rpi_wsp;
+	struct wm5102_machine_priv *wm5102 = snd_soc_card_get_drvdata(card);
+
+	snd_soc_unregister_card(&snd_rpi_wsp);
+	kfree(wm5102);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id snd_rpi_wsp_of_match[] = {
+		{ .compatible = "wlf,rpi-wm5102", },
+		{},
+};
+MODULE_DEVICE_TABLE(of, snd_rpi_wsp_of_match);
+#endif /* CONFIG_OF */
+
+static struct platform_driver snd_rpi_wsp_driver = {
+	.driver = {
+		.name   = "snd-rpi-wsp",
+		.owner  = THIS_MODULE,
+		.of_match_table = of_match_ptr(snd_rpi_wsp_of_match),
+	},
+	.probe	  = snd_rpi_wsp_probe,
+	.remove	 = snd_rpi_wsp_remove,
+};
+
+module_platform_driver(snd_rpi_wsp_driver);
+
+MODULE_AUTHOR("Nikesh Oswal");
+MODULE_AUTHOR("Liu Xin");
+MODULE_DESCRIPTION("ASoC Driver for Raspberry Pi connected to Cirrus sound pi");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 9653352..1be8d2e 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -1095,7 +1095,7 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
 	unsigned int reg;
 	unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK;
 	unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT;
-	unsigned int *clk;
+	int *clk;
 
 	switch (clk_id) {
 	case ARIZONA_CLK_SYSCLK:
@@ -1375,6 +1375,9 @@ static int arizona_startup(struct snd_pcm_substream *substream,
 	const struct snd_pcm_hw_constraint_list *constraint;
 	unsigned int base_rate;
 
+	if (!substream->runtime)
+		return 0;
+
 	switch (dai_priv->clk) {
 	case ARIZONA_CLK_SYSCLK:
 		base_rate = priv->sysclk;
@@ -2180,6 +2183,8 @@ static void arizona_disable_fll(struct arizona_fll *fll)
 {
 	struct arizona *arizona = fll->arizona;
 	bool change;
+	int i;
+	unsigned int val;
 
 	regmap_update_bits_async(arizona->regmap, fll->base + 1,
 				 ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
@@ -2190,6 +2195,25 @@ static void arizona_disable_fll(struct arizona_fll *fll)
 	regmap_update_bits_async(arizona->regmap, fll->base + 1,
 				 ARIZONA_FLL1_FREERUN, 0);
 
+	arizona_fll_dbg(fll, "Waiting for FLL disable...\n");
+	val = 0;
+	for (i = 0; i < 15; i++) {
+		if (i < 5)
+			usleep_range(200, 400);
+		else
+			msleep(20);
+
+		regmap_read(arizona->regmap,
+			    ARIZONA_INTERRUPT_RAW_STATUS_5,
+			    &val);
+		if (!(val & (ARIZONA_FLL1_CLOCK_OK_STS << (fll->id - 1))))
+			break;
+	}
+	if (i == 15)
+		arizona_fll_warn(fll, "Timed out waiting for disable\n");
+	else
+		arizona_fll_dbg(fll, "FLL disabled (%d polls)\n", i);
+
 	if (change)
 		pm_runtime_put_autosuspend(arizona->dev);
 }
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index a1305f8..31f36e6 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1295,7 +1295,7 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
 	if (play_w && capture_w) {
 		ret = snd_soc_dapm_new_pcm(card, dai_link->params,
 					   dai_link->num_params, capture_w,
-					   play_w);
+					   play_w, dai_link);
 		if (ret != 0) {
 			dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
 				play_w->name, capture_w->name, ret);
@@ -1308,7 +1308,7 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
 	if (play_w && capture_w) {
 		ret = snd_soc_dapm_new_pcm(card, dai_link->params,
 					   dai_link->num_params, capture_w,
-					   play_w);
+					   play_w, dai_link);
 		if (ret != 0) {
 			dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
 				play_w->name, capture_w->name, ret);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index afb70a5..104888d 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -3450,11 +3450,12 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
 {
 	struct snd_soc_dapm_path *source_p, *sink_p;
 	struct snd_soc_dai *source, *sink;
-	const struct snd_soc_pcm_stream *config = w->params + w->params_select;
+	struct snd_soc_pcm_stream *config = w->params + w->params_select;
+	struct snd_soc_dai_link *dai_link = w->priv;
 	struct snd_pcm_substream substream;
 	struct snd_pcm_hw_params *params = NULL;
 	u64 fmt;
-	int ret;
+	int ret = 0;
 
 	if (WARN_ON(!config) ||
 	    WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) ||
@@ -3472,6 +3473,16 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
 	source = source_p->source->priv;
 	sink = sink_p->sink->priv;
 
+	if (dai_link && dai_link->params_fixup) {
+		ret = dai_link->params_fixup(w, event);
+		if (ret < 0) {
+			dev_err(w->dapm->dev,
+				"ASoC: params_fixup for dai link widget failed %d\n",
+				ret);
+			goto out;
+		}
+	}
+
 	/* Be a little careful as we don't want to overflow the mask array */
 	if (config->formats) {
 		fmt = ffs(config->formats) - 1;
@@ -3601,10 +3612,11 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol,
 }
 
 int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
-			 const struct snd_soc_pcm_stream *params,
+			 struct snd_soc_pcm_stream *params,
 			 unsigned int num_params,
 			 struct snd_soc_dapm_widget *source,
-			 struct snd_soc_dapm_widget *sink)
+			 struct snd_soc_dapm_widget *sink,
+			 void *priv)
 {
 	struct snd_soc_dapm_widget template;
 	struct snd_soc_dapm_widget *w;
@@ -3706,6 +3718,7 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
 
 	w->params = params;
 	w->num_params = num_params;
+	w->priv = priv;
 
 	ret = snd_soc_dapm_add_path(&card->dapm, source, w, NULL, NULL);
 	if (ret)
