aboutsummaryrefslogtreecommitdiffstats
path: root/main/linux-rpi/rpi-cirrus-4.4.y-20160327.patch
diff options
context:
space:
mode:
Diffstat (limited to 'main/linux-rpi/rpi-cirrus-4.4.y-20160327.patch')
-rw-r--r--main/linux-rpi/rpi-cirrus-4.4.y-20160327.patch1533
1 files changed, 1533 insertions, 0 deletions
diff --git a/main/linux-rpi/rpi-cirrus-4.4.y-20160327.patch b/main/linux-rpi/rpi-cirrus-4.4.y-20160327.patch
new file mode 100644
index 0000000000..d430efa34d
--- /dev/null
+++ b/main/linux-rpi/rpi-cirrus-4.4.y-20160327.patch
@@ -0,0 +1,1533 @@
+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 4c3db73..b38e598 100644
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -52,6 +52,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..3cb63a5
+--- /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 = <&sound>;
++ __overlay__ {
++ compatible = "wlf,rpi-wm5102";
++ i2s-controller = <&i2s>;
++ status = "okay";
++ };
++ };
++
++ fragment@2 {
++ target = <&i2s>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@3 {
++ target = <&gpio>;
++ __overlay__ {
++ wlf_pins: wlf_pins {
++ brcm,pins = <17 22 27 8>;
++ brcm,function = <1 1 0 1>;
++ };
++ };
++ };
++
++ fragment@4 {
++ 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@5 {
++ 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@6 {
++ 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>;
++ };
++ };
++ };
++};
+diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig
+index b63632d..ee84684 100644
+--- a/arch/arm/configs/bcm2709_defconfig
++++ b/arch/arm/configs/bcm2709_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
+@@ -853,6 +856,7 @@ CONFIG_SND_BCM2708_SOC_RPI_DAC=m
+ CONFIG_SND_BCM2708_SOC_RPI_PROTO=m
+ CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m
+ CONFIG_SND_BCM2708_SOC_RASPIDAC3=m
++CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP=m
+ CONFIG_SND_SOC_ADAU1701=m
+ CONFIG_SND_SOC_WM8804_I2C=m
+ CONFIG_SND_SIMPLE_CARD=m
+diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig
+index e720c74..b7d8ad8 100644
+--- a/arch/arm/configs/bcmrpi_defconfig
++++ b/arch/arm/configs/bcmrpi_defconfig
+@@ -637,6 +637,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
+@@ -845,6 +848,7 @@ CONFIG_SND_BCM2708_SOC_RPI_DAC=m
+ CONFIG_SND_BCM2708_SOC_RPI_PROTO=m
+ CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m
+ CONFIG_SND_BCM2708_SOC_RASPIDAC3=m
++CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP=m
+ CONFIG_SND_SOC_ADAU1701=m
+ CONFIG_SND_SOC_WM8804_I2C=m
+ CONFIG_SND_SIMPLE_CARD=m
+diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
+index 985019b..77be442 100644
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -144,12 +144,6 @@ struct bcm2835_desc {
+ */
+ #define MAX_LITE_TRANSFER (SZ_64K - 4)
+
+-/*
+- * Transfers larger than 32k cause issues with the bcm2708-i2s driver,
+- * so limit transfer size to 32k as bcm2708-dmaengine did.
+- */
+-#define MAX_CYCLIC_LITE_TRANSFER SZ_32K
+-
+ static inline struct bcm2835_dmadev *to_bcm2835_dma_dev(struct dma_device *d)
+ {
+ return container_of(d, struct bcm2835_dmadev, ddev);
+@@ -385,6 +379,15 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
+ unsigned int frame, max_size;
+ int i;
+
++ if (!buf_len || !period_len)
++ return NULL;
++
++ if (buf_len % period_len) {
++ dev_err(chan->device->dev,
++ "Buffer length should be a multiple of period\n");
++ return NULL;
++ }
++
+ /* Grab configuration */
+ if (!is_slave_direction(direction)) {
+ dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+@@ -410,6 +413,18 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
+ return NULL;
+ }
+
++ if (c->ch >= 8) /* LITE channel */
++ max_size = MAX_LITE_TRANSFER;
++ else
++ max_size = MAX_NORMAL_TRANSFER;
++
++ if (period_len > max_size) {
++ dev_err(chan->device->dev,
++ "Period length %d larger than maximum %d\n",
++ period_len, max_size);
++ return NULL;
++ }
++
+ /* Now allocate and setup the descriptor. */
+ d = kzalloc(sizeof(*d), GFP_NOWAIT);
+ if (!d)
+@@ -417,12 +432,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
+
+ d->c = c;
+ d->dir = direction;
+- if (c->ch >= 8) /* LITE channel */
+- max_size = MAX_CYCLIC_LITE_TRANSFER;
+- else
+- max_size = MAX_NORMAL_TRANSFER;
+- period_len = min(period_len, max_size);
+- d->frames = (buf_len - 1) / (period_len + 1);
++ d->frames = buf_len / period_len;
++ d->size = buf_len;
+
+ d->cb_list = kcalloc(d->frames, sizeof(*d->cb_list), GFP_KERNEL);
+ if (!d->cb_list) {
+@@ -470,12 +481,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
+ BCM2835_DMA_PER_MAP(c->dreq);
+
+ /* Length of a frame */
+- if (frame != d->frames - 1)
+- control_block->length = period_len;
+- else
+- control_block->length = buf_len - (d->frames - 1) *
+- period_len;
+- d->size += control_block->length;
++ control_block->length = period_len;
+
+ /*
+ * Next block is the next frame.
+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 1a3f826..020cfb1 100644
+--- a/sound/soc/bcm/Kconfig
++++ b/sound/soc/bcm/Kconfig
+@@ -64,3 +64,11 @@ config SND_BCM2708_SOC_RASPIDAC3
+ select SND_SOC_TPA6130A2
+ help
+ Say Y or M if you want to add support for RaspiDAC Rev.3x.
++
++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
+diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile
+index b21e11e..4cb717f 100644
+--- a/sound/soc/bcm/Makefile
++++ b/sound/soc/bcm/Makefile
+@@ -12,6 +12,7 @@ snd-soc-rpi-dac-objs := rpi-dac.o
+ snd-soc-rpi-proto-objs := rpi-proto.o
+ snd-soc-iqaudio-dac-objs := iqaudio-dac.o
+ snd-soc-raspidac3-objs := raspidac3.o
++snd-soc-rpi-wsp-objs := rpi-cirrus-sound-pi.o
+
+ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o
+ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o
+@@ -21,3 +22,4 @@ 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
+ obj-$(CONFIG_SND_BCM2708_SOC_RASPIDAC3) += snd-soc-raspidac3.o
++obj-$(CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP) += snd-soc-rpi-wsp.o
+diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c
+index 04c1d13..815509b 100644
+--- a/sound/soc/bcm/bcm2835-i2s.c
++++ b/sound/soc/bcm/bcm2835-i2s.c
+@@ -806,16 +806,16 @@ static struct snd_pcm_hardware bcm2835_pcm_hardware = {
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 32,
+- .period_bytes_max = 64 * PAGE_SIZE,
++ .period_bytes_max = SZ_64K - 4,
+ .periods_min = 2,
+ .periods_max = 255,
+- .buffer_bytes_max = 128 * PAGE_SIZE,
++ .buffer_bytes_max = SZ_512K,
+ };
+
+ static const struct snd_dmaengine_pcm_config bcm2835_dmaengine_pcm_config = {
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .pcm_hardware = &bcm2835_pcm_hardware,
+- .prealloc_buffer_size = 256 * PAGE_SIZE,
++ .prealloc_buffer_size = SZ_1M,
+ };
+
+ static int bcm2835_i2s_probe(struct platform_device *pdev)
+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 93b4008..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;
+@@ -2093,9 +2096,9 @@ static int arizona_enable_fll(struct arizona_fll *fll)
+ /* Facilitate smooth refclk across the transition */
+ regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
+ ARIZONA_FLL1_GAIN_MASK, 0);
+- regmap_update_bits_async(fll->arizona->regmap, fll->base + 1,
+- ARIZONA_FLL1_FREERUN,
+- ARIZONA_FLL1_FREERUN);
++ regmap_update_bits(fll->arizona->regmap, fll->base + 1,
++ ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
++ udelay(32);
+ }
+
+ /*
+@@ -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 416514f..92832eb 100644
+--- a/sound/soc/soc-dapm.c
++++ b/sound/soc/soc-dapm.c
+@@ -3443,11 +3443,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]) ||
+@@ -3465,6 +3466,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;
+@@ -3594,10 +3605,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;
+@@ -3699,6 +3711,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)