diff options
Diffstat (limited to 'main/linux-rpi/rpi-cirrus-4.1.x-20151025.patch')
-rw-r--r-- | main/linux-rpi/rpi-cirrus-4.1.x-20151025.patch | 2048 |
1 files changed, 2048 insertions, 0 deletions
diff --git a/main/linux-rpi/rpi-cirrus-4.1.x-20151025.patch b/main/linux-rpi/rpi-cirrus-4.1.x-20151025.patch new file mode 100644 index 0000000000..f29b381efc --- /dev/null +++ b/main/linux-rpi/rpi-cirrus-4.1.x-20151025.patch @@ -0,0 +1,2048 @@ +https://github.com/HiassofT/rpi-linux/commits/cirrus-4.1.y + +diff --git a/Documentation/devicetree/bindings/mfd/arizona.txt b/Documentation/devicetree/bindings/mfd/arizona.txt +index 7665aa9..ec7979e 100644 +--- a/Documentation/devicetree/bindings/mfd/arizona.txt ++++ b/Documentation/devicetree/bindings/mfd/arizona.txt +@@ -42,6 +42,23 @@ Optional properties: + + - wlf,reset : GPIO specifier for the GPIO controlling /RESET + - wlf,ldoena : GPIO specifier for the GPIO controlling LDOENA ++ - 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 +@@ -49,6 +66,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. +@@ -85,6 +106,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 +@@ -92,4 +126,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 f4b2817..756c08d 100644 +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -41,6 +41,7 @@ dtb-$(RPI_DT_OVERLAYS) += pwm-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pwm-2chan-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += raspidac3-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += rpi-backlight-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += rpi-cirrus-wm5102-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += rpi-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += rpi-display-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += rpi-ft5406-overlay.dtb +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..85b3d15 +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/rpi-cirrus-wm5102-overlay.dts +@@ -0,0 +1,141 @@ ++// 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>; ++ brcm,function = <1 1 0>; ++ }; ++ }; ++ }; ++ ++ 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; ++ }; ++ ++ wsp_preinit: wsp_preinit { ++ compatible = "wlf,rpi-wm5102-preinit"; ++ }; ++ }; ++ }; ++ ++ 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@3a { ++ #sound-dai-cells = <0>; ++ compatible = "wlf,wm8804"; ++ reg = <0x3a>; ++ status = "okay"; ++ PVDD-supply = <&ldo0>; ++ DVDD-supply = <&ldo0>; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig +index fb402e8..74ebdc7 100644 +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -639,6 +639,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 +@@ -844,6 +847,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 eb81555e..6528468 100644 +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -632,6 +632,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 +@@ -837,6 +840,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/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 1b9c3be..39b6698 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -62,10 +62,17 @@ + #include "bcm2708.h" + #include "armctrl.h" + ++#include <linux/mfd/arizona/pdata.h> ++#include <linux/regulator/machine.h> ++#include <linux/regulator/fixed.h> ++ + #ifdef CONFIG_BCM_VC_CMA + #include <linux/broadcom/vc_cma.h> + #endif + ++#define GPIO_WM5102_IRQ 27 ++#define GPIO_WM5102_RST 17 ++#define GPIO_WM5102_LDOEN 22 + + /* Effectively we have an IOMMU (ARM<->VideoCore map) that is set up to + * give us IO access only to 64Mbytes of physical memory (26 bits). We could +@@ -587,7 +594,9 @@ static struct spi_board_info bcm2708_spi_devices[] = { + .bus_num = 0, + .chip_select = 0, + .mode = SPI_MODE_0, +- }, { ++ }, ++#if !defined (CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP_MODULE) && !defined (CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP) ++ { + .modalias = "spidev", + .max_speed_hz = 500000, + .bus_num = 0, +@@ -595,6 +604,7 @@ static struct spi_board_info bcm2708_spi_devices[] = { + .mode = SPI_MODE_0, + } + #endif ++#endif + }; + #endif + +@@ -663,6 +673,123 @@ static struct platform_device bcm2708_i2s_device = { + }; + #endif + ++#if defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE) ++static struct regulator_consumer_supply dc1v8_consumers[] = { ++ REGULATOR_SUPPLY("LDOVDD", "spi0.1"), ++ REGULATOR_SUPPLY("AVDD", "spi0.1"), ++ REGULATOR_SUPPLY("DBVDD1", "spi0.1"), ++ REGULATOR_SUPPLY("CPVDD", "spi0.1"), ++ REGULATOR_SUPPLY("DBVDD2", "spi0.1"), ++ REGULATOR_SUPPLY("DBVDD3", "spi0.1"), ++ REGULATOR_SUPPLY("PVDD", "1-003a"), ++ REGULATOR_SUPPLY("DVDD", "1-003a"), ++}; ++ ++static struct regulator_init_data dc1v8_data = { ++ .constraints = { ++ .always_on = 1, ++ }, ++ .num_consumer_supplies = ARRAY_SIZE(dc1v8_consumers), ++ .consumer_supplies = dc1v8_consumers, ++}; ++ ++static struct fixed_voltage_config dc1v8vdd_pdata = { ++ .supply_name = "DC_1V8", ++ .microvolts = 1800000, ++ .init_data = &dc1v8_data, ++ .gpio = -1, ++}; ++ ++static struct platform_device dc1v8_device = { ++ .name = "reg-fixed-voltage", ++ .id = 0, ++ .dev = { ++ .platform_data = &dc1v8vdd_pdata, ++ }, ++}; ++ ++static struct regulator_consumer_supply dc5v_consumers[] = { ++ REGULATOR_SUPPLY("SPKVDDL", "spi0.1"), ++ REGULATOR_SUPPLY("SPKVDDR", "spi0.1"), ++}; ++ ++static struct regulator_init_data dc5v_data = { ++ .constraints = { ++ .always_on = 1, ++ }, ++ .num_consumer_supplies = ARRAY_SIZE(dc5v_consumers), ++ .consumer_supplies = dc5v_consumers, ++}; ++ ++static struct fixed_voltage_config dc5vvdd_pdata = { ++ .supply_name = "DC_5V", ++ .microvolts = 5000000, ++ .init_data = &dc5v_data, ++ .gpio = -1, ++}; ++ ++static struct platform_device dc5v_device = { ++ .name = "reg-fixed-voltage", ++ .id = 1, ++ .dev = { ++ .platform_data = &dc5vvdd_pdata, ++ }, ++}; ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP_MODULE) || defined(CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP) ++#include <linux/mfd/arizona/registers.h> ++ ++static struct platform_device snd_rpi_wsp_device = { ++ .name = "snd-rpi-wsp", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct arizona_micd_config wm5102_micd[] = { ++ { 0, 1, 0 }, ++}; ++ ++static struct arizona_pdata snd_rpi_wsp_spi_platform_data = { ++ .reset = GPIO_WM5102_RST, ++ .ldoena = GPIO_WM5102_LDOEN, ++ .irq_flags = IRQF_TRIGGER_RISING, ++ .gpio_defaults = { ++ [2] = 0x04, /* OPCLK */ ++ [3] = 0x3d, /* ASYNC OPCLK */ ++ }, ++ .micd_configs = wm5102_micd, ++ .num_micd_configs = ARRAY_SIZE(wm5102_micd), ++ .dmic_ref = { ++ [1] = ARIZONA_DMIC_MICBIAS2, ++ }, ++ .inmode = { ++ [1] = ARIZONA_INMODE_DMIC, ++ [2] = ARIZONA_INMODE_SE, ++ }, ++ .clk32k_src = ARIZONA_32KZ_NONE, ++ .irq_gpio = GPIO_WM5102_IRQ, ++}; ++ ++static struct spi_board_info __initdata snd_rpi_wsp_spi_devices[] = { ++ { ++ .modalias = "wm5102", ++ .platform_data = &snd_rpi_wsp_spi_platform_data, ++ .max_speed_hz = 500000, ++ .bus_num = 0, ++ .chip_select = 1, ++ .mode = SPI_MODE_0, ++ } ++}; ++ ++static struct i2c_board_info __initdata snd_rpi_wsp_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("wm8804", 0x3A), ++ }, ++}; ++ ++#endif ++ + #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) + static struct platform_device snd_hifiberry_dac_device = { + .name = "snd-hifiberry-dac", +@@ -772,6 +899,9 @@ int __init bcm_register_device(struct platform_device *pdev) + #define i2c_register_board_info_dt(busnum, info, n) \ + if (!use_dt) i2c_register_board_info(busnum, info, n) + ++#define spi_register_board_info_dt(info, n) \ ++ if (!use_dt) spi_register_board_info(info, n) ++ + int calc_rsts(int partition) + { + return PM_PASSWORD | +@@ -965,6 +1095,18 @@ void __init bcm2708_init(void) + i2c_register_board_info_dt(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); + #endif + ++#if defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE) ++ bcm_register_device_dt(&dc1v8_device); ++ bcm_register_device_dt(&dc5v_device); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP_MODULE) || defined(CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP) ++ bcm_register_device_dt(&snd_rpi_wsp_device); ++ spi_register_board_info_dt(snd_rpi_wsp_spi_devices, ARRAY_SIZE(snd_rpi_wsp_spi_devices)); ++ i2c_register_board_info_dt(1, snd_rpi_wsp_i2c_devices, ++ ARRAY_SIZE(snd_rpi_wsp_i2c_devices)); ++#endif ++ + if (!use_dt) { + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; +diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c +index 6523903..4211644 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> +@@ -94,6 +95,91 @@ int arizona_clk32k_disable(struct arizona *arizona) + } + EXPORT_SYMBOL_GPL(arizona_clk32k_disable); + ++int arizona_dvfs_up(struct arizona *arizona, unsigned int flags) ++{ ++ unsigned int new_flags; ++ int ret = 0; ++ ++ mutex_lock(&arizona->subsys_max_lock); ++ ++ new_flags = arizona->subsys_max_rq | flags; ++ ++ if (arizona->subsys_max_rq != new_flags) { ++ switch (arizona->type) { ++ case WM5102: ++ case WM8997: ++ ret = regulator_set_voltage(arizona->dcvdd, ++ 1800000, 1800000); ++ if (ret != 0) { ++ dev_err(arizona->dev, ++ "Failed to set DCVDD (DVFS up): %d\n", ++ ret); ++ goto err; ++ } ++ ++ ret = regmap_update_bits(arizona->regmap, ++ ARIZONA_DYNAMIC_FREQUENCY_SCALING_1, ++ ARIZONA_SUBSYS_MAX_FREQ, 1); ++ if (ret != 0) { ++ dev_err(arizona->dev, ++ "Failed to enable subsys max: %d\n", ++ ret); ++ regulator_set_voltage(arizona->dcvdd, ++ 1200000, 1800000); ++ goto err; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ arizona->subsys_max_rq = new_flags; ++ } ++err: ++ mutex_unlock(&arizona->subsys_max_lock); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(arizona_dvfs_up); ++ ++int arizona_dvfs_down(struct arizona *arizona, unsigned int flags) ++{ ++ int ret = 0; ++ ++ mutex_lock(&arizona->subsys_max_lock); ++ ++ arizona->subsys_max_rq &= ~flags; ++ ++ if (arizona->subsys_max_rq == 0) { ++ switch (arizona->type) { ++ case WM5102: ++ case WM8997: ++ ret = regmap_update_bits(arizona->regmap, ++ ARIZONA_DYNAMIC_FREQUENCY_SCALING_1, ++ ARIZONA_SUBSYS_MAX_FREQ, 0); ++ if (ret != 0) ++ dev_err(arizona->dev, ++ "Failed to disable subsys max: %d\n", ++ ret); ++ ++ ret = regulator_set_voltage(arizona->dcvdd, ++ 1200000, 1800000); ++ if (ret != 0) ++ dev_err(arizona->dev, ++ "Failed to set DCVDD (DVFS down): %d\n", ++ ret); ++ break; ++ ++ default: ++ break; ++ } ++ } ++ ++ mutex_unlock(&arizona->subsys_max_lock); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(arizona_dvfs_down); ++ + static irqreturn_t arizona_clkgen_err(int irq, void *data) + { + struct arizona *arizona = data; +@@ -330,6 +416,20 @@ err_fll: + return err; + } + ++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_runtime_resume(struct device *dev) + { +@@ -519,6 +619,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; +@@ -552,6 +800,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(arizona->pdata.inmode)) +@@ -571,6 +828,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; + } + +@@ -670,6 +930,7 @@ int arizona_dev_init(struct arizona *arizona) + + dev_set_drvdata(arizona->dev, arizona); + mutex_init(&arizona->clk_lock); ++ mutex_init(&arizona->subsys_max_lock); + + if (dev_get_platdata(arizona->dev)) + memcpy(&arizona->pdata, dev_get_platdata(arizona->dev), +@@ -726,6 +987,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 = gpio_request_one(arizona->pdata.reset, +@@ -733,16 +1002,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); +@@ -1060,6 +1332,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: +@@ -1073,6 +1347,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 a1d07d3..353b930 100644 +--- a/drivers/regulator/arizona-ldo1.c ++++ b/drivers/regulator/arizona-ldo1.c +@@ -78,11 +78,6 @@ static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev, + if (ret != 0) + return ret; + +- ret = regmap_update_bits(regmap, ARIZONA_DYNAMIC_FREQUENCY_SCALING_1, +- ARIZONA_SUBSYS_MAX_FREQ, val); +- if (ret != 0) +- return ret; +- + if (val) + return 0; + +@@ -268,6 +263,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 16a498f..e54676c 100644 +--- a/include/linux/mfd/arizona/core.h ++++ b/include/linux/mfd/arizona/core.h +@@ -117,6 +117,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; + + struct arizona_pdata pdata; + +@@ -135,6 +136,9 @@ struct arizona { + + bool ctrlif_error; + ++ struct mutex subsys_max_lock; ++ unsigned int subsys_max_rq; ++ + struct snd_soc_dapm_context *dapm; + + int tdm_width[ARIZONA_MAX_AIF]; +@@ -145,8 +149,17 @@ struct arizona { + struct mutex dac_comp_lock; + }; + ++#define ARIZONA_DVFS_SR1_RQ 0x00000001 ++#define ARIZONA_DVFS_SR2_RQ 0x00000002 ++#define ARIZONA_DVFS_SR3_RQ 0x00000004 ++#define ARIZONA_DVFS_ASR1_RQ 0x00000010 ++#define ARIZONA_DVFS_ASR2_RQ 0x00000020 ++#define ARIZONA_DVFS_ADSP1_RQ 0x00010000 ++ + int arizona_clk32k_enable(struct arizona *arizona); + int arizona_clk32k_disable(struct arizona *arizona); ++int arizona_dvfs_up(struct arizona *arizona, unsigned int mask); ++int arizona_dvfs_down(struct arizona *arizona, unsigned int mask); + + int arizona_request_irq(struct arizona *arizona, int irq, char *name, + irq_handler_t handler, void *data); +diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h +index 1065095..edd011a 100644 +--- a/include/sound/soc-dapm.h ++++ b/include/sound/soc-dapm.h +@@ -377,10 +377,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); +@@ -530,7 +531,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 f6226914..a9389cf 100644 +--- a/include/sound/soc.h ++++ b/include/sound/soc.h +@@ -954,7 +954,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 3db2852..7c5699a 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -75,3 +75,15 @@ 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_PREINIT ++ tristate ++ ++config SND_BCM2708_SOC_RPI_CODEC_WSP ++ tristate "Support for Cirrus sound pi" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_WM5102 ++ select SND_SOC_WM8804 ++ select SND_BCM2708_SOC_RPI_CODEC_WSP_PREINIT ++ 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 621358b..65e120d 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -17,6 +17,8 @@ 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 ++snd-soc-rpi-wsp-preinit-objs := rpi-cirrus-sound-pi-preinit.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 +@@ -26,3 +28,5 @@ 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 ++obj-$(CONFIG_SND_BCM2708_SOC_RPI_CODEC_WSP_PREINIT) += snd-soc-rpi-wsp-preinit.o +diff --git a/sound/soc/bcm/rpi-cirrus-sound-pi-preinit.c b/sound/soc/bcm/rpi-cirrus-sound-pi-preinit.c +new file mode 100644 +index 0000000..a2f17e4 +--- /dev/null ++++ b/sound/soc/bcm/rpi-cirrus-sound-pi-preinit.c +@@ -0,0 +1,188 @@ ++/* ++ * Cirrus Audio Card wm8804 preinit code ++ * ++ * Refactored out of the Cirrus Audio Card code to work around ++ * init dependecy issues. ++ * ++ * Copyright 2015 Cirrus Logic Inc. ++ * ++ * Author: Nikesh Oswal, <Nikesh.Oswal@wolfsonmicro.com> ++ * Author: Matthias Reichl, <hias@horus.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/platform_device.h> ++#include <linux/gpio.h> ++#include <linux/delay.h> ++#include <linux/io.h> ++ ++#include <asm/system_info.h> ++ ++/*TODO: Shift this to platform data*/ ++#define GPIO_WM8804_RST 8 ++#define GPIO_WM8804_MODE 2 ++#define GPIO_WM8804_SW_MODE 23 ++#define GPIO_WM8804_I2C_ADDR_B 18 ++#define GPIO_WM8804_I2C_ADDR_B_PLUS 13 ++ ++static void bcm2708_set_gpio_alt(int pin, int alt) ++{ ++ /* ++ * This is the common way to handle the GPIO pins for ++ * the Raspberry Pi. ++ * TODO This is a hack. Use pinmux / pinctrl. ++ */ ++#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) ++#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) ++ unsigned int *gpio; ++ gpio = ioremap(GPIO_BASE, SZ_16K); ++ INP_GPIO(pin); ++ SET_GPIO_ALT(pin, alt); ++ iounmap(gpio); ++#undef INP_GPIO ++#undef SET_GPIO_ALT ++} ++ ++static int wm8804_reset(void) ++ { ++ int ret; ++ unsigned int gpio_wm8804_i2c_addr; ++ ++ if ((system_rev & 0xffffff) >= 0x10) { ++ /* Model B+ or later */ ++ gpio_wm8804_i2c_addr = GPIO_WM8804_I2C_ADDR_B_PLUS; ++ } else { ++ gpio_wm8804_i2c_addr = GPIO_WM8804_I2C_ADDR_B; ++ } ++ ++ if (!gpio_is_valid(GPIO_WM8804_RST)) { ++ pr_err("Skipping unavailable gpio %d (%s)\n", GPIO_WM8804_RST, "wm8804_rst"); ++ return -ENOMEM; ++ } ++ ++ if (!gpio_is_valid(GPIO_WM8804_MODE)) { ++ pr_err("Skipping unavailable gpio %d (%s)\n", GPIO_WM8804_MODE, "wm8804_mode"); ++ return -ENOMEM; ++ } ++ ++ if (!gpio_is_valid(GPIO_WM8804_SW_MODE)) { ++ pr_err("Skipping unavailable gpio %d (%s)\n", GPIO_WM8804_SW_MODE, "wm8804_sw_mode"); ++ return -ENOMEM; ++ } ++ ++ if (!gpio_is_valid(gpio_wm8804_i2c_addr)) { ++ pr_err("Skipping unavailable gpio %d (%s)\n", gpio_wm8804_i2c_addr, "wm8804_i2c_addr"); ++ return -ENOMEM; ++ } ++ ++ ret = gpio_request(GPIO_WM8804_RST, "wm8804_rst"); ++ if (ret < 0) { ++ pr_err("gpio_request wm8804_rst failed\n"); ++ return ret; ++ } ++ ++ ret = gpio_request(GPIO_WM8804_MODE, "wm8804_mode"); ++ if (ret < 0) { ++ pr_err("gpio_request wm8804_mode failed\n"); ++ return ret; ++ } ++ ++ ret = gpio_request(GPIO_WM8804_SW_MODE, "wm8804_sw_mode"); ++ if (ret < 0) { ++ pr_err("gpio_request wm8804_sw_mode failed\n"); ++ return ret; ++ } ++ ++ ret = gpio_request(gpio_wm8804_i2c_addr, "wm8804_i2c_addr"); ++ if (ret < 0) { ++ pr_err("gpio_request wm8804_i2c_addr failed\n"); ++ return ret; ++ } ++ ++ /*GPIO2 is used for SW/HW Mode Select and after Reset the same pin is used as ++ I2C data line, so initially it is configured as GPIO OUT from BCM perspective*/ ++ /*Set SW Mode*/ ++ ret = gpio_direction_output(GPIO_WM8804_MODE, 1); ++ if (ret < 0) { ++ pr_err("gpio_direction_output wm8804_mode failed\n"); ++ } ++ ++ /*Set 2 Wire (I2C) Mode*/ ++ ret = gpio_direction_output(GPIO_WM8804_SW_MODE, 0); ++ if (ret < 0) { ++ pr_err("gpio_direction_output wm8804_sw_mode failed\n"); ++ } ++ ++ /*Set 2 Wire (I2C) Addr to 0x3A, writing 1 will make the Addr as 0x3B*/ ++ ret = gpio_direction_output(gpio_wm8804_i2c_addr, 0); ++ if (ret < 0) { ++ pr_err("gpio_direction_output wm8804_i2c_addr failed\n"); ++ } ++ ++ /*Take WM8804 out of reset*/ ++ ret = gpio_direction_output(GPIO_WM8804_RST, 1); ++ if (ret < 0) { ++ pr_err("gpio_direction_output wm8804_rst failed\n"); ++ } ++ ++ /*Put WM8804 in reset*/ ++ gpio_set_value(GPIO_WM8804_RST, 0); ++ mdelay(500); ++ /*Take WM8804 out of reset*/ ++ gpio_set_value(GPIO_WM8804_RST, 1); ++ mdelay(500); ++ ++ gpio_free(GPIO_WM8804_RST); ++ gpio_free(GPIO_WM8804_MODE); ++ gpio_free(GPIO_WM8804_SW_MODE); ++ gpio_free(gpio_wm8804_i2c_addr); ++ ++ /*GPIO2 is used for SW/HW Mode Select and after Reset the same pin is used as ++ I2C data line, so after reset it is configured as I2C data line i.e ALT0 function*/ ++ bcm2708_set_gpio_alt(GPIO_WM8804_MODE, 0); ++ ++ return ret; ++} ++ ++static int snd_rpi_wsp_preinit_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct device *dev = &pdev->dev; ++ ++ dev_info(dev, "initializing wm8804 on Cirrus audio card\n"); ++ ret = wm8804_reset(); ++ if (ret) ++ dev_err(dev, "wm8804_reset returned %d\n", ret); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id snd_rpi_wsp_preinit_of_match[] = { ++ { .compatible = "wlf,rpi-wm5102-preinit", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, snd_rpi_wsp_preinit_of_match); ++#endif /* CONFIG_OF */ ++ ++static struct platform_driver snd_rpi_wsp_preinit_driver = { ++ .driver = { ++ .name = "snd-rpi-wsp-preinit", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(snd_rpi_wsp_preinit_of_match), ++ }, ++ .probe = snd_rpi_wsp_preinit_probe, ++}; ++ ++module_platform_driver(snd_rpi_wsp_preinit_driver); ++ ++MODULE_AUTHOR("Nikesh Oswal"); ++MODULE_AUTHOR("Liu Xin"); ++MODULE_AUTHOR("Matthias Reichl"); ++MODULE_DESCRIPTION("Cirrus sound pi wm8804 one-time initialisation code"); ++MODULE_LICENSE("GPL"); +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..f7b8839 +--- /dev/null ++++ b/sound/soc/bcm/rpi-cirrus-sound-pi.c +@@ -0,0 +1,530 @@ ++/* ++ * 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/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*/ ++ ++static struct snd_soc_card snd_rpi_wsp; ++ ++struct wm5102_machine_priv { ++ int wm8804_sr; ++ int wm5102_sr; ++ int sync_path_enable; ++}; ++ ++enum { ++ GPIO_FSEL_INPUT, GPIO_FSEL_OUTPUT, ++ GPIO_FSEL_ALT5, GPIO_FSEL_ALT_4, ++ GPIO_FSEL_ALT0, GPIO_FSEL_ALT1, ++ GPIO_FSEL_ALT2, GPIO_FSEL_ALT3, ++}; ++ ++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_codec *wm5102_codec = card->rtd[0].codec; ++ int ret = 0; ++ int clk_freq; ++ int sr = priv->wm8804_sr; ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ /* 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); ++ return ret; ++ } ++ ++ ret = snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1, ++ ARIZONA_CLK_SRC_AIF2BCLK, ++ sr * 64, clk_freq); ++ 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_codec *wm5102_codec = card->rtd[0].codec; ++ 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; ++ ++ switch (level) { ++ case SND_SOC_BIAS_OFF: ++ break; ++ case SND_SOC_BIAS_ON: ++ if (!priv->sync_path_enable) { ++ ret = snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1, ++ 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); ++ return ret; ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ ++ dapm->bias_level = level; ++ ++ return 0; ++} ++ ++static int rpi_set_bias_level_post(struct snd_soc_card *card, ++ struct snd_soc_dapm_context *dapm, ++ enum snd_soc_bias_level level) ++{ ++ struct snd_soc_codec *wm5102_codec = card->rtd[0].codec; ++ ++ switch (level) { ++ case SND_SOC_BIAS_STANDBY: ++ snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1, ++ ARIZONA_FLL_SRC_NONE, 0, 0); ++ snd_soc_codec_set_pll(wm5102_codec, WM5102_FLL1_REFCLK, ++ ARIZONA_FLL_SRC_NONE, 0, 0); ++ break; ++ default: ++ break; ++ } ++ ++ dapm->bias_level = level; ++ ++ return 0; ++} ++ ++static int snd_rpi_wsp_config_5102_clks(struct snd_soc_codec *wm5102_codec, int sr) ++{ ++ int ret; ++ int clk_freq = (sr % 4000 == 0) ? WM5102_MAX_SYSCLK_1 : WM5102_MAX_SYSCLK_2; ++ ++ /*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 != 0) { ++ 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_codec *wm5102_codec = rtd->codec; ++ struct snd_soc_dai *bcm_i2s_dai = rtd->cpu_dai; ++ struct snd_soc_codec *wm8804_codec = card->rtd[1].codec; ++ struct snd_soc_dai *wm8804_codec_dai = card->rtd[1].codec_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; ++ ++ 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(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-003a", ++ .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); ++ int ret; ++ ++ priv->wm8804_sr = RPI_WLF_SR; ++ priv->wm5102_sr = RPI_WLF_SR; ++ priv->sync_path_enable = 0; ++ ++ ret = snd_soc_codec_set_sysclk(card->rtd[0].codec, ARIZONA_CLK_SYSCLK, ARIZONA_CLK_SRC_FLL1, ++ 0, SND_SOC_CLOCK_IN); ++ if (ret != 0) { ++ dev_err(card->rtd[0].codec->dev, "Failed to set SYSCLK to Zero: %d\n", ret); ++ return ret; ++ } ++ ++ ret = snd_rpi_wsp_config_8804_clks(card->rtd[1].codec, card->rtd[1].codec_dai, RPI_WLF_SR); ++ ++ if (ret != 0) { ++ dev_err(card->rtd[1].codec->dev, "snd_rpi_wsp_config_8804_clks failed: %d\n", ret); ++ return ret; ++ } ++ ++ ret = snd_soc_dai_set_sysclk(card->rtd[0].codec_dai, ARIZONA_CLK_SYSCLK, 0, 0); ++ if (ret != 0) { ++ dev_err(card->rtd[0].codec->dev, "Failed to set codec dai clk domain: %d\n", ret); ++ return ret; ++ } ++ ++ ret = snd_soc_dai_set_sysclk(card->rtd[1].cpu_dai, ARIZONA_CLK_SYSCLK, 0, 0); ++ if (ret != 0) { ++ dev_err(card->rtd[0].codec->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", ++ .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, ++ .set_bias_level_post = rpi_set_bias_level_post, ++}; ++ ++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; ++ ++ 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[0]; ++ 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) { ++ 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_SOFTDEP("pre: snd_soc_wm8804_i2c"); ++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 ee91edc..6f19255 100644 +--- a/sound/soc/codecs/arizona.c ++++ b/sound/soc/codecs/arizona.c +@@ -1266,7 +1266,7 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream, + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + int base = dai->driver->base; +- int i, sr_val; ++ int i, sr_val, ret; + + /* + * We will need to be more flexible than this in future, +@@ -1282,6 +1282,26 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream, + } + sr_val = i; + ++ switch (priv->arizona->type) { ++ case WM5102: ++ case WM8997: ++ if (arizona_sr_vals[sr_val] >= 88200) ++ ret = arizona_dvfs_up(priv->arizona, ++ ARIZONA_DVFS_SR1_RQ); ++ else ++ ret = arizona_dvfs_down(priv->arizona, ++ ARIZONA_DVFS_SR1_RQ); ++ ++ if (ret != 0) { ++ arizona_aif_err(dai, "Failed to change DVFS %d\n", ret); ++ return ret; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ + switch (dai_priv->clk) { + case ARIZONA_CLK_SYSCLK: + switch (priv->arizona->type) { +diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c +index d476221..fc50c6d 100644 +--- a/sound/soc/codecs/wm5102.c ++++ b/sound/soc/codecs/wm5102.c +@@ -613,6 +613,49 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, + return 0; + } + ++static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); ++ struct arizona *arizona = dev_get_drvdata(codec->dev->parent); ++ unsigned int v; ++ int ret; ++ ++ switch (event) { ++ case SND_SOC_DAPM_PRE_PMU: ++ ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v); ++ if (ret != 0) { ++ dev_err(codec->dev, ++ "Failed to read SYSCLK state: %d\n", ret); ++ return -EIO; ++ } ++ ++ v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; ++ ++ if (v >= 3) { ++ ret = arizona_dvfs_up(arizona, ARIZONA_DVFS_ADSP1_RQ); ++ if (ret != 0) { ++ dev_err(codec->dev, ++ "Failed to raise DVFS: %d\n", ret); ++ return ret; ++ } ++ } ++ break; ++ ++ case SND_SOC_DAPM_POST_PMD: ++ ret = arizona_dvfs_down(arizona, ARIZONA_DVFS_ADSP1_RQ); ++ if (ret != 0) ++ dev_warn(codec->dev, ++ "Failed to lower DVFS: %d\n", ret); ++ break; ++ ++ default: ++ break; ++ } ++ ++ return wm_adsp2_early_event(w, kcontrol, event); ++} ++ + static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +@@ -1367,7 +1410,7 @@ ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), + ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +-WM_ADSP2("DSP1", 0), ++WM_ADSP2_E("DSP1", 0, wm5102_adsp_power_ev), + + SND_SOC_DAPM_OUTPUT("HPOUT1L"), + SND_SOC_DAPM_OUTPUT("HPOUT1R"), +@@ -1918,7 +1961,7 @@ static int wm5102_probe(struct platform_device *pdev) + wm5102->core.adsp[0].mem = wm5102_dsp1_regions; + wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions); + +- ret = wm_adsp2_init(&wm5102->core.adsp[0], true); ++ ret = wm_adsp2_init(&wm5102->core.adsp[0]); + if (ret != 0) + return ret; + +diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c +index d01c209..bbbc5c2 100644 +--- a/sound/soc/codecs/wm_adsp.c ++++ b/sound/soc/codecs/wm_adsp.c +@@ -1531,35 +1531,6 @@ static void wm_adsp2_boot_work(struct work_struct *work) + return; + } + +- if (dsp->dvfs) { +- ret = regmap_read(dsp->regmap, +- dsp->base + ADSP2_CLOCKING, &val); +- if (ret != 0) { +- adsp_err(dsp, "Failed to read clocking: %d\n", ret); +- return; +- } +- +- if ((val & ADSP2_CLK_SEL_MASK) >= 3) { +- ret = regulator_enable(dsp->dvfs); +- if (ret != 0) { +- adsp_err(dsp, +- "Failed to enable supply: %d\n", +- ret); +- return; +- } +- +- ret = regulator_set_voltage(dsp->dvfs, +- 1800000, +- 1800000); +- if (ret != 0) { +- adsp_err(dsp, +- "Failed to raise supply: %d\n", +- ret); +- return; +- } +- } +- } +- + ret = wm_adsp2_ena(dsp); + if (ret != 0) + return; +@@ -1653,21 +1624,6 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, + regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); + regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); + +- if (dsp->dvfs) { +- ret = regulator_set_voltage(dsp->dvfs, 1200000, +- 1800000); +- if (ret != 0) +- adsp_warn(dsp, +- "Failed to lower supply: %d\n", +- ret); +- +- ret = regulator_disable(dsp->dvfs); +- if (ret != 0) +- adsp_err(dsp, +- "Failed to enable supply: %d\n", +- ret); +- } +- + list_for_each_entry(ctl, &dsp->ctl_list, list) + ctl->enabled = 0; + +@@ -1694,7 +1650,7 @@ err: + } + EXPORT_SYMBOL_GPL(wm_adsp2_event); + +-int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) ++int wm_adsp2_init(struct wm_adsp *adsp) + { + int ret; + +@@ -1713,33 +1669,6 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) + INIT_LIST_HEAD(&adsp->ctl_list); + INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work); + +- if (dvfs) { +- adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); +- if (IS_ERR(adsp->dvfs)) { +- ret = PTR_ERR(adsp->dvfs); +- adsp_err(adsp, "Failed to get DCVDD: %d\n", ret); +- return ret; +- } +- +- ret = regulator_enable(adsp->dvfs); +- if (ret != 0) { +- adsp_err(adsp, "Failed to enable DCVDD: %d\n", ret); +- return ret; +- } +- +- ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); +- if (ret != 0) { +- adsp_err(adsp, "Failed to initialise DVFS: %d\n", ret); +- return ret; +- } +- +- ret = regulator_disable(adsp->dvfs); +- if (ret != 0) { +- adsp_err(adsp, "Failed to disable DCVDD: %d\n", ret); +- return ret; +- } +- } +- + return 0; + } + EXPORT_SYMBOL_GPL(wm_adsp2_init); +diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h +index a4f6b64..a86e531 100644 +--- a/sound/soc/codecs/wm_adsp.h ++++ b/sound/soc/codecs/wm_adsp.h +@@ -56,8 +56,6 @@ struct wm_adsp { + int fw; + bool running; + +- struct regulator *dvfs; +- + struct list_head ctl_list; + + struct work_struct boot_work; +@@ -67,19 +65,22 @@ struct wm_adsp { + SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \ + wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) + +-#define WM_ADSP2(wname, num) \ ++#define WM_ADSP2_E(wname, num, event_fn) \ + { .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \ +- .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_early_event, \ +- .event_flags = SND_SOC_DAPM_PRE_PMU }, \ ++ .reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \ ++ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }, \ + { .id = snd_soc_dapm_out_drv, .name = wname, \ + .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \ + .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } + ++#define WM_ADSP2(wname, num) \ ++ WM_ADSP2_E(wname, num, wm_adsp2_early_event) ++ + extern const struct snd_kcontrol_new wm_adsp1_fw_controls[]; + extern const struct snd_kcontrol_new wm_adsp2_fw_controls[]; + + int wm_adsp1_init(struct wm_adsp *adsp); +-int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); ++int wm_adsp2_init(struct wm_adsp *adsp); + int wm_adsp1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, +diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c +index 2373252..ee92ec7 100644 +--- a/sound/soc/soc-core.c ++++ b/sound/soc/soc-core.c +@@ -1299,7 +1299,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); +@@ -1312,7 +1312,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 b6c12dc..e767365 100644 +--- a/sound/soc/soc-dapm.c ++++ b/sound/soc/soc-dapm.c +@@ -3227,11 +3227,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->sources) || list_empty(&w->sinks))) +@@ -3251,6 +3252,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; +@@ -3350,10 +3361,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; +@@ -3455,6 +3467,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) +-- +2.6.1 + |