diff options
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.patch | 1533 |
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) |