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 . 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 192bda7..6046f13 100644 --- a/arch/arm/boot/dts/overlays/Makefile +++ b/arch/arm/boot/dts/overlays/Makefile @@ -38,6 +38,7 @@ dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb 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-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 630da1b..b4e2906 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 @@ -843,6 +846,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 df4cebc..bfe13a3 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 @@ -836,6 +839,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 +#include +#include + #ifdef CONFIG_BCM_VC_CMA #include #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 + +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 #include #include +#include #include #include #include @@ -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, + * Author: Matthias Reichl, + * 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 +#include +#include +#include +#include + +#include + +/*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, + * 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 +#include +#include + +#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)