diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-10-26 08:32:46 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-10-26 12:14:49 +0100 |
commit | d82ad8e0c0ecc49e81bf7d57fdc09b7d76c1afc0 (patch) | |
tree | df819814633bf1b2a22a423c4ef67b462ba0871c | |
parent | 274035751e25ee15a064e43cde7b4e7a9b75d921 (diff) | |
parent | bb9a13a0209c56cdf27d125a1f2f6f34378c64f4 (diff) | |
download | op-kernel-dev-d82ad8e0c0ecc49e81bf7d57fdc09b7d76c1afc0.zip op-kernel-dev-d82ad8e0c0ecc49e81bf7d57fdc09b7d76c1afc0.tar.gz |
Merge tag 'asoc-v4.3-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next
ASoC: Updates for v4.4
Not much core work here, a few small tweaks to interfaces but mainly the
changes here are driver ones. Highlights include:
- Updates to the topology userspace interface
- Big updates to the Renesas support from Morimoto-san
- Most of the support for Intel Sky Lake systems.
- New drivers for Asahi Kasei Microdevices AK4613, Allwinnner A10,
Cirrus Logic WM8998, Dialog DA7219, Nuvoton NAU8825 and Rockchip
S/PDIF.
- A new driver for the Atmel Class D speaker drivers
334 files changed, 13681 insertions, 1628 deletions
@@ -59,6 +59,7 @@ James Bottomley <jejb@mulgrave.(none)> James Bottomley <jejb@titanic.il.steeleye.com> James E Wilson <wilson@specifix.com> James Ketrenos <jketreno@io.(none)> +<javier@osg.samsung.com> <javier.martinez@collabora.co.uk> Jean Tourrilhes <jt@hpl.hp.com> Jeff Garzik <jgarzik@pretzel.yyz.us> Jens Axboe <axboe@suse.de> diff --git a/Documentation/arm/OMAP/README b/Documentation/arm/OMAP/README new file mode 100644 index 0000000..75645c4 --- /dev/null +++ b/Documentation/arm/OMAP/README @@ -0,0 +1,7 @@ +This file contains documentation for running mainline +kernel on omaps. + +KERNEL NEW DEPENDENCIES +v4.3+ Update is needed for custom .config files to make sure + CONFIG_REGULATOR_PBIAS is enabled for MMC1 to work + properly. diff --git a/Documentation/devicetree/bindings/sound/atmel-classd.txt b/Documentation/devicetree/bindings/sound/atmel-classd.txt new file mode 100644 index 0000000..0018451 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/atmel-classd.txt @@ -0,0 +1,52 @@ +* Atmel ClassD driver under ALSA SoC architecture + +Required properties: +- compatible + Should be "atmel,sama5d2-classd". +- reg + Should contain ClassD registers location and length. +- interrupts + Should contain the IRQ line for the ClassD. +- dmas + One DMA specifiers as described in atmel-dma.txt and dma.txt files. +- dma-names + Must be "tx". +- clock-names + Tuple listing input clock names. + Required elements: "pclk", "gclk" and "aclk". +- clocks + Please refer to clock-bindings.txt. + +Optional properties: +- pinctrl-names, pinctrl-0 + Please refer to pinctrl-bindings.txt. +- atmel,model + The user-visible name of this sound complex. + The default value is "CLASSD". +- atmel,pwm-type + PWM modulation type, "single" or "diff". + The default value is "single". +- atmel,non-overlap-time + Set non-overlapping time, the unit is nanosecond(ns). + There are four values, + <5>, <10>, <15>, <20>, the default value is <10>. + Non-overlapping will be disabled if not specified. + +Example: +classd: classd@fc048000 { + compatible = "atmel,sama5d2-classd"; + reg = <0xfc048000 0x100>; + interrupts = <59 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) + | AT91_XDMAC_DT_PERID(47))>; + dma-names = "tx"; + clocks = <&classd_clk>, <&classd_gclk>, <&audio_pll_pmc>; + clock-names = "pclk", "gclk", "aclk"; + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_classd_default>; + atmel,model = "classd @ SAMA5D2-Xplained"; + atmel,pwm-type = "diff"; + atmel,non-overlap-time = <10>; +}; diff --git a/Documentation/devicetree/bindings/sound/da7213.txt b/Documentation/devicetree/bindings/sound/da7213.txt new file mode 100644 index 0000000..5890280 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/da7213.txt @@ -0,0 +1,41 @@ +Dialog Semiconductor DA7213 Audio Codec bindings + +====== + +Required properties: +- compatible : Should be "dlg,da7213" +- reg: Specifies the I2C slave address + +Optional properties: +- clocks : phandle and clock specifier for codec MCLK. +- clock-names : Clock name string for 'clocks' attribute, should be "mclk". + +- dlg,micbias1-lvl : Voltage (mV) for Mic Bias 1 + [<1600>, <2200>, <2500>, <3000>] +- dlg,micbias2-lvl : Voltage (mV) for Mic Bias 2 + [<1600>, <2200>, <2500>, <3000>] +- dlg,dmic-data-sel : DMIC channel select based on clock edge. + ["lrise_rfall", "lfall_rrise"] +- dlg,dmic-samplephase : When to sample audio from DMIC. + ["on_clkedge", "between_clkedge"] +- dlg,dmic-clkrate : DMIC clock frequency (Hz). + [<1500000>, <3000000>] + +====== + +Example: + + codec_i2c: da7213@1a { + compatible = "dlg,da7213"; + reg = <0x1a>; + + clocks = <&clks 201>; + clock-names = "mclk"; + + dlg,micbias1-lvl = <2500>; + dlg,micbias2-lvl = <2500>; + + dlg,dmic-data-sel = "lrise_rfall"; + dlg,dmic-samplephase = "between_clkedge"; + dlg,dmic-clkrate = <3000000>; + }; diff --git a/Documentation/devicetree/bindings/sound/da7219.txt b/Documentation/devicetree/bindings/sound/da7219.txt new file mode 100644 index 0000000..1b70309 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/da7219.txt @@ -0,0 +1,106 @@ +Dialog Semiconductor DA7219 Audio Codec bindings + +DA7219 is an audio codec with advanced accessory detect features. + +====== + +Required properties: +- compatible : Should be "dlg,da7219" +- reg: Specifies the I2C slave address + +- interrupt-parent : Specifies the phandle of the interrupt controller to which + the IRQs from DA7219 are delivered to. +- interrupts : IRQ line info for DA7219. + (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for + further information relating to interrupt properties) + +- VDD-supply: VDD power supply for the device +- VDDMIC-supply: VDDMIC power supply for the device +- VDDIO-supply: VDDIO power supply for the device + (See Documentation/devicetree/bindings/regulator/regulator.txt for further + information relating to regulators) + +Optional properties: +- interrupt-names : Name associated with interrupt line. Should be "wakeup" if + interrupt is to be used to wake system, otherwise "irq" should be used. +- wakeup-source: Flag to indicate this device can wake system (suspend/resume). + +- clocks : phandle and clock specifier for codec MCLK. +- clock-names : Clock name string for 'clocks' attribute, should be "mclk". + +- dlg,ldo-lvl : Required internal LDO voltage (mV) level for digital engine + [<1050>, <1100>, <1200>, <1400>] +- dlg,micbias-lvl : Voltage (mV) for Mic Bias + [<1800>, <2000>, <2200>, <2400>, <2600>] +- dlg,mic-amp-in-sel : Mic input source type + ["diff", "se_p", "se_n"] + +====== + +Child node - 'da7219_aad': + +Optional properties: +- dlg,micbias-pulse-lvl : Mic bias higher voltage pulse level (mV). + [<2800>, <2900>] +- dlg,micbias-pulse-time : Mic bias higher voltage pulse duration (ms) +- dlg,btn-cfg : Periodic button press measurements for 4-pole jack (ms) + [<2>, <5>, <10>, <50>, <100>, <200>, <500>] +- dlg,mic-det-thr : Impedance threshold for mic detection measurement (Ohms) + [<200>, <500>, <750>, <1000>] +- dlg,jack-ins-deb : Debounce time for jack insertion (ms) + [<5>, <10>, <20>, <50>, <100>, <200>, <500>, <1000>] +- dlg,jack-det-rate: Jack type detection latency (3/4 pole) + ["32ms_64ms", "64ms_128ms", "128ms_256ms", "256ms_512ms"] +- dlg,jack-rem-deb : Debounce time for jack removal (ms) + [<1>, <5>, <10>, <20>] +- dlg,a-d-btn-thr : Impedance threshold between buttons A and D + [0x0 - 0xFF] +- dlg,d-b-btn-thr : Impedance threshold between buttons D and B + [0x0 - 0xFF] +- dlg,b-c-btn-thr : Impedance threshold between buttons B and C + [0x0 - 0xFF] +- dlg,c-mic-btn-thr : Impedance threshold between button C and Mic + [0x0 - 0xFF] +- dlg,btn-avg : Number of 8-bit readings for averaged button measurement + [<1>, <2>, <4>, <8>] +- dlg,adc-1bit-rpt : Repeat count for 1-bit button measurement + [<1>, <2>, <4>, <8>] + +====== + +Example: + + codec: da7219@1a { + compatible = "dlg,da7219"; + reg = <0x1a>; + + interrupt-parent = <&gpio6>; + interrupts = <11 IRQ_TYPE_LEVEL_HIGH>; + + VDD-supply = <®_audio>; + VDDMIC-supply = <®_audio>; + VDDIO-supply = <®_audio>; + + clocks = <&clks 201>; + clock-names = "mclk"; + + dlg,ldo-lvl = <1200>; + dlg,micbias-lvl = <2600>; + dlg,mic-amp-in-sel = "diff"; + + da7219_aad { + dlg,btn-cfg = <50>; + dlg,mic-det-thr = <500>; + dlg,jack-ins-deb = <20>; + dlg,jack-det-rate = "32ms_64ms"; + dlg,jack-rem-deb = <1>; + + dlg,a-d-btn-thr = <0xa>; + dlg,d-b-btn-thr = <0x16>; + dlg,b-c-btn-thr = <0x21>; + dlg,c-mic-btn-thr = <0x3E>; + + dlg,btn-avg = <4>; + dlg,adc-1bit-rpt = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt index a96774c..ce55c0a 100644 --- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt +++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt @@ -13,13 +13,15 @@ So having this generic sound card allows all Freescale SoC users to benefit from the simplification of a new card support and the capability of the wide sample rates support through ASRC. -Note: The card is initially designed for those sound cards who use I2S and - PCM DAI formats. However, it'll be also possible to support those non - I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long - as the driver has been properly upgraded. +Note: The card is initially designed for those sound cards who use AC'97, I2S + and PCM DAI formats. However, it'll be also possible to support those non + AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as + long as the driver has been properly upgraded. The compatible list for this generic sound card currently: + "fsl,imx-audio-ac97" + "fsl,imx-audio-cs42888" "fsl,imx-audio-wm8962" diff --git a/Documentation/devicetree/bindings/sound/nau8825.txt b/Documentation/devicetree/bindings/sound/nau8825.txt new file mode 100644 index 0000000..d337423 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nau8825.txt @@ -0,0 +1,102 @@ +Nuvoton NAU8825 audio codec + +This device supports I2C only. + +Required properties: + - compatible : Must be "nuvoton,nau8825" + + - reg : the I2C address of the device. This is either 0x1a (CSB=0) or 0x1b (CSB=1). + +Optional properties: + - nuvoton,jkdet-enable: Enable jack detection via JKDET pin. + - nuvoton,jkdet-pull-enable: Enable JKDET pin pull. If set - pin pull enabled, + otherwise pin in high impedance state. + - nuvoton,jkdet-pull-up: Pull-up JKDET pin. If set then JKDET pin is pull up, otherwise pull down. + - nuvoton,jkdet-polarity: JKDET pin polarity. 0 - active high, 1 - active low. + + - nuvoton,vref-impedance: VREF Impedance selection + 0 - Open + 1 - 25 kOhm + 2 - 125 kOhm + 3 - 2.5 kOhm + + - nuvoton,micbias-voltage: Micbias voltage level. + 0 - VDDA + 1 - VDDA + 2 - VDDA * 1.1 + 3 - VDDA * 1.2 + 4 - VDDA * 1.3 + 5 - VDDA * 1.4 + 6 - VDDA * 1.53 + 7 - VDDA * 1.53 + + - nuvoton,sar-threshold-num: Number of buttons supported + - nuvoton,sar-threshold: Impedance threshold for each button. Array that contains up to 8 buttons configuration. SAR value is calculated as + SAR = 255 * MICBIAS / SAR_VOLTAGE * R / (2000 + R) + where MICBIAS is configured by 'nuvoton,micbias-voltage', SAR_VOLTAGE is configured by 'nuvoton,sar-voltage', R - button impedance. + Refer datasheet section 10.2 for more information about threshold calculation. + + - nuvoton,sar-hysteresis: Button impedance measurement hysteresis. + + - nuvoton,sar-voltage: Reference voltage for button impedance measurement. + 0 - VDDA + 1 - VDDA + 2 - VDDA * 1.1 + 3 - VDDA * 1.2 + 4 - VDDA * 1.3 + 5 - VDDA * 1.4 + 6 - VDDA * 1.53 + 7 - VDDA * 1.53 + + - nuvoton,sar-compare-time: SAR compare time + 0 - 500 ns + 1 - 1 us + 2 - 2 us + 3 - 4 us + + - nuvoton,sar-sampling-time: SAR sampling time + 0 - 2 us + 1 - 4 us + 2 - 8 us + 3 - 16 us + + - nuvoton,short-key-debounce: Button short key press debounce time. + 0 - 30 ms + 1 - 50 ms + 2 - 100 ms + 3 - 30 ms + + - nuvoton,jack-insert-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms + - nuvoton,jack-eject-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms + + - clocks: list of phandle and clock specifier pairs according to common clock bindings for the + clocks described in clock-names + - clock-names: should include "mclk" for the MCLK master clock + +Example: + + headset: nau8825@1a { + compatible = "nuvoton,nau8825"; + reg = <0x1a>; + interrupt-parent = <&gpio>; + interrupts = <TEGRA_GPIO(E, 6) IRQ_TYPE_LEVEL_LOW>; + nuvoton,jkdet-enable; + nuvoton,jkdet-pull-enable; + nuvoton,jkdet-pull-up; + nuvoton,jkdet-polarity = <GPIO_ACTIVE_LOW>; + nuvoton,vref-impedance = <2>; + nuvoton,micbias-voltage = <6>; + // Setup 4 buttons impedance according to Android specification + nuvoton,sar-threshold-num = <4>; + nuvoton,sar-threshold = <0xc 0x1e 0x38 0x60>; + nuvoton,sar-hysteresis = <1>; + nuvoton,sar-voltage = <0>; + nuvoton,sar-compare-time = <0>; + nuvoton,sar-sampling-time = <0>; + nuvoton,short-key-debounce = <2>; + nuvoton,jack-insert-debounce = <7>; + nuvoton,jack-eject-debounce = <7>; + + clock-names = "mclk"; + clocks = <&tegra_car TEGRA210_CLK_CLK_OUT_2>; + }; diff --git a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt index 9b82c20..2267d24 100644 --- a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt +++ b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt @@ -12,8 +12,6 @@ Required properties: - reg: physical base address of the controller and length of memory mapped region. - interrupts: should contain the I2S interrupt. -- #address-cells: should be 1. -- #size-cells: should be 0. - dmas: DMA specifiers for tx and rx dma. See the DMA client binding, Documentation/devicetree/bindings/dma/dma.txt - dma-names: should include "tx" and "rx". @@ -21,6 +19,7 @@ Required properties: - clock-names: should contain followings: - "i2s_hclk": clock for I2S BUS - "i2s_clk" : clock for I2S controller +- rockchip,capture-channels: max capture channels, if not set, 2 channels default. Example for rk3288 I2S controller: @@ -28,10 +27,9 @@ i2s@ff890000 { compatible = "rockchip,rk3288-i2s", "rockchip,rk3066-i2s"; reg = <0xff890000 0x10000>; interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>; - #address-cells = <1>; - #size-cells = <0>; dmas = <&pdma1 0>, <&pdma1 1>; dma-names = "tx", "rx"; clock-names = "i2s_hclk", "i2s_clk"; clocks = <&cru HCLK_I2S0>, <&cru SCLK_I2S0>; + rockchip,capture-channels = <2>; }; diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt new file mode 100644 index 0000000..e64dbde --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt @@ -0,0 +1,40 @@ +* Rockchip SPDIF transceiver + +The S/PDIF audio block is a stereo transceiver that allows the +processor to receive and transmit digital audio via an coaxial cable or +a fibre cable. + +Required properties: + +- compatible: should be one of the following: + - "rockchip,rk3288-spdif", "rockchip,rk3188-spdif" or + "rockchip,rk3066-spdif" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: should contain the SPDIF interrupt. +- dmas: DMA specifiers for tx dma. See the DMA client binding, + Documentation/devicetree/bindings/dma/dma.txt +- dma-names: should be "tx" +- clocks: a list of phandle + clock-specifier pairs, one for each entry + in clock-names. +- clock-names: should contain following: + - "hclk": clock for SPDIF controller + - "mclk" : clock for SPDIF bus + +Required properties on RK3288: + - rockchip,grf: the phandle of the syscon node for the general register + file (GRF) + +Example for the rk3188 SPDIF controller: + +spdif: spdif@0x1011e000 { + compatible = "rockchip,rk3188-spdif", "rockchip,rk3066-spdif"; + reg = <0x1011e000 0x2000>; + interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac1_s 8>; + dma-names = "tx"; + clock-names = "hclk", "mclk"; + clocks = <&cru HCLK_SPDIF>, <&cru SCLK_SPDIF>; + status = "disabled"; + #sound-dai-cells = <0>; +}; diff --git a/Documentation/devicetree/bindings/sound/rt5640.txt b/Documentation/devicetree/bindings/sound/rt5640.txt index bac4d9a..9e62f6e 100644 --- a/Documentation/devicetree/bindings/sound/rt5640.txt +++ b/Documentation/devicetree/bindings/sound/rt5640.txt @@ -14,7 +14,8 @@ Optional properties: - realtek,in1-differential - realtek,in2-differential - Boolean. Indicate MIC1/2 input are differential, rather than single-ended. +- realtek,in3-differential + Boolean. Indicate MIC1/2/3 input are differential, rather than single-ended. - realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin. @@ -24,9 +25,11 @@ Pins on the device (for linking into audio routes) for RT5639/RT5640: * DMIC2 * MICBIAS1 * IN1P - * IN1R + * IN1N * IN2P - * IN2R + * IN2N + * IN3P + * IN3N * HPOL * HPOR * LOUTL diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt index 680144b..c92966b 100644 --- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt +++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt @@ -13,10 +13,6 @@ Required properties: - clock-names: should contain followings: - "apb": the parent APB clock for this controller - "codec": the parent module clock -- routing : A list of the connections between audio components. Each - entry is a pair of strings, the first being the connection's sink, - the second being the connection's source. - Example: codec: codec@01c22c00 { @@ -28,6 +24,4 @@ codec: codec@01c22c00 { clock-names = "apb", "codec"; dmas = <&dma 0 19>, <&dma 0 19>; dma-names = "rx", "tx"; - routing = "Headphone Jack", "HP Right", - "Headphone Jack", "HP Left"; }; diff --git a/MAINTAINERS b/MAINTAINERS index b8577ad9..cb1a432a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -894,11 +894,12 @@ M: Lennert Buytenhek <kernel@wantstofly.org> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -ARM/Allwinner A1X SoC support +ARM/Allwinner sunXi SoC support M: Maxime Ripard <maxime.ripard@free-electrons.com> +M: Chen-Yu Tsai <wens@csie.org> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -N: sun[x4567]i +N: sun[x456789]i ARM/Allwinner SoC Clock Support M: Emilio López <emilio@elopez.com.ar> @@ -3367,6 +3368,7 @@ M: Support Opensource <support.opensource@diasemi.com> W: http://www.dialog-semiconductor.com/products S: Supported F: Documentation/hwmon/da90?? +F: Documentation/devicetree/bindings/sound/da[79]*.txt F: drivers/gpio/gpio-da90??.c F: drivers/hwmon/da90??-hwmon.c F: drivers/iio/adc/da91??-*.c @@ -6793,7 +6795,6 @@ F: drivers/scsi/megaraid/ MELLANOX ETHERNET DRIVER (mlx4_en) M: Amir Vadai <amirv@mellanox.com> -M: Ido Shamay <idos@mellanox.com> L: netdev@vger.kernel.org S: Supported W: http://www.mellanox.com @@ -11675,6 +11676,7 @@ F: drivers/tty/serial/zs.* ZSMALLOC COMPRESSED SLAB MEMORY ALLOCATOR M: Minchan Kim <minchan@kernel.org> M: Nitin Gupta <ngupta@vflare.org> +R: Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com> L: linux-mm@kvack.org S: Maintained F: mm/zsmalloc.c @@ -1,7 +1,7 @@ VERSION = 4 PATCHLEVEL = 3 SUBLEVEL = 0 -EXTRAVERSION = -rc6 +EXTRAVERSION = -rc7 NAME = Blurry Fish Butt # *DOCUMENTATION* diff --git a/arch/arm/boot/dts/am57xx-beagle-x15.dts b/arch/arm/boot/dts/am57xx-beagle-x15.dts index 568adf5..d55e3ea 100644 --- a/arch/arm/boot/dts/am57xx-beagle-x15.dts +++ b/arch/arm/boot/dts/am57xx-beagle-x15.dts @@ -402,11 +402,12 @@ /* SMPS9 unused */ ldo1_reg: ldo1 { - /* VDD_SD */ + /* VDD_SD / VDDSHV8 */ regulator-name = "ldo1"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; regulator-boot-on; + regulator-always-on; }; ldo2_reg: ldo2 { diff --git a/arch/arm/boot/dts/armada-385-db-ap.dts b/arch/arm/boot/dts/armada-385-db-ap.dts index 89f5a95..4047621 100644 --- a/arch/arm/boot/dts/armada-385-db-ap.dts +++ b/arch/arm/boot/dts/armada-385-db-ap.dts @@ -46,7 +46,7 @@ / { model = "Marvell Armada 385 Access Point Development Board"; - compatible = "marvell,a385-db-ap", "marvell,armada385", "marvell,armada38x"; + compatible = "marvell,a385-db-ap", "marvell,armada385", "marvell,armada380"; chosen { stdout-path = "serial1:115200n8"; diff --git a/arch/arm/boot/dts/berlin2q.dtsi b/arch/arm/boot/dts/berlin2q.dtsi index 63a4849..d4dbd28 100644 --- a/arch/arm/boot/dts/berlin2q.dtsi +++ b/arch/arm/boot/dts/berlin2q.dtsi @@ -152,7 +152,7 @@ }; usb_phy2: phy@a2f400 { - compatible = "marvell,berlin2-usb-phy"; + compatible = "marvell,berlin2cd-usb-phy"; reg = <0xa2f400 0x128>; #phy-cells = <0>; resets = <&chip_rst 0x104 14>; @@ -170,7 +170,7 @@ }; usb_phy0: phy@b74000 { - compatible = "marvell,berlin2-usb-phy"; + compatible = "marvell,berlin2cd-usb-phy"; reg = <0xb74000 0x128>; #phy-cells = <0>; resets = <&chip_rst 0x104 12>; @@ -178,7 +178,7 @@ }; usb_phy1: phy@b78000 { - compatible = "marvell,berlin2-usb-phy"; + compatible = "marvell,berlin2cd-usb-phy"; reg = <0xb78000 0x128>; #phy-cells = <0>; resets = <&chip_rst 0x104 13>; diff --git a/arch/arm/boot/dts/exynos5420-peach-pit.dts b/arch/arm/boot/dts/exynos5420-peach-pit.dts index 8f4d76c..1b95da7 100644 --- a/arch/arm/boot/dts/exynos5420-peach-pit.dts +++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts @@ -915,6 +915,11 @@ }; }; +&pmu_system_controller { + assigned-clocks = <&pmu_system_controller 0>; + assigned-clock-parents = <&clock CLK_FIN_PLL>; +}; + &rtc { status = "okay"; clocks = <&clock CLK_RTC>, <&max77802 MAX77802_CLK_32K_AP>; diff --git a/arch/arm/boot/dts/exynos5800-peach-pi.dts b/arch/arm/boot/dts/exynos5800-peach-pi.dts index 7d5b386..8f40c7e 100644 --- a/arch/arm/boot/dts/exynos5800-peach-pi.dts +++ b/arch/arm/boot/dts/exynos5800-peach-pi.dts @@ -878,6 +878,11 @@ }; }; +&pmu_system_controller { + assigned-clocks = <&pmu_system_controller 0>; + assigned-clock-parents = <&clock CLK_FIN_PLL>; +}; + &rtc { status = "okay"; clocks = <&clock CLK_RTC>, <&max77802 MAX77802_CLK_32K_AP>; diff --git a/arch/arm/boot/dts/imx7d.dtsi b/arch/arm/boot/dts/imx7d.dtsi index b738ce0..6e444bb 100644 --- a/arch/arm/boot/dts/imx7d.dtsi +++ b/arch/arm/boot/dts/imx7d.dtsi @@ -588,10 +588,10 @@ status = "disabled"; }; - uart2: serial@30870000 { + uart2: serial@30890000 { compatible = "fsl,imx7d-uart", "fsl,imx6q-uart"; - reg = <0x30870000 0x10000>; + reg = <0x30890000 0x10000>; interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clks IMX7D_UART2_ROOT_CLK>, <&clks IMX7D_UART2_ROOT_CLK>; diff --git a/arch/arm/boot/dts/logicpd-torpedo-37xx-devkit.dts b/arch/arm/boot/dts/logicpd-torpedo-37xx-devkit.dts index 91146c3..5b04300 100644 --- a/arch/arm/boot/dts/logicpd-torpedo-37xx-devkit.dts +++ b/arch/arm/boot/dts/logicpd-torpedo-37xx-devkit.dts @@ -12,7 +12,7 @@ / { model = "LogicPD Zoom DM3730 Torpedo Development Kit"; - compatible = "logicpd,dm3730-torpedo-devkit", "ti,omap36xx"; + compatible = "logicpd,dm3730-torpedo-devkit", "ti,omap3630", "ti,omap3"; gpio_keys { compatible = "gpio-keys"; diff --git a/arch/arm/boot/dts/meson.dtsi b/arch/arm/boot/dts/meson.dtsi index 5484413..8c77c87 100644 --- a/arch/arm/boot/dts/meson.dtsi +++ b/arch/arm/boot/dts/meson.dtsi @@ -67,7 +67,7 @@ timer@c1109940 { compatible = "amlogic,meson6-timer"; - reg = <0xc1109940 0x14>; + reg = <0xc1109940 0x18>; interrupts = <0 10 1>; }; @@ -80,36 +80,37 @@ wdt: watchdog@c1109900 { compatible = "amlogic,meson6-wdt"; reg = <0xc1109900 0x8>; + interrupts = <0 0 1>; }; uart_AO: serial@c81004c0 { compatible = "amlogic,meson-uart"; - reg = <0xc81004c0 0x14>; + reg = <0xc81004c0 0x18>; interrupts = <0 90 1>; clocks = <&clk81>; status = "disabled"; }; - uart_A: serial@c81084c0 { + uart_A: serial@c11084c0 { compatible = "amlogic,meson-uart"; - reg = <0xc81084c0 0x14>; - interrupts = <0 90 1>; + reg = <0xc11084c0 0x18>; + interrupts = <0 26 1>; clocks = <&clk81>; status = "disabled"; }; - uart_B: serial@c81084dc { + uart_B: serial@c11084dc { compatible = "amlogic,meson-uart"; - reg = <0xc81084dc 0x14>; - interrupts = <0 90 1>; + reg = <0xc11084dc 0x18>; + interrupts = <0 75 1>; clocks = <&clk81>; status = "disabled"; }; - uart_C: serial@c8108700 { + uart_C: serial@c1108700 { compatible = "amlogic,meson-uart"; - reg = <0xc8108700 0x14>; - interrupts = <0 90 1>; + reg = <0xc1108700 0x18>; + interrupts = <0 93 1>; clocks = <&clk81>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/omap3-evm-37xx.dts b/arch/arm/boot/dts/omap3-evm-37xx.dts index 16e8ce3..bb339d1 100644 --- a/arch/arm/boot/dts/omap3-evm-37xx.dts +++ b/arch/arm/boot/dts/omap3-evm-37xx.dts @@ -13,7 +13,7 @@ / { model = "TI OMAP37XX EVM (TMDSEVM3730)"; - compatible = "ti,omap3-evm-37xx", "ti,omap36xx"; + compatible = "ti,omap3-evm-37xx", "ti,omap3630", "ti,omap3"; memory { device_type = "memory"; diff --git a/arch/arm/boot/dts/ste-hrefv60plus.dtsi b/arch/arm/boot/dts/ste-hrefv60plus.dtsi index 810cda7..9c2387b 100644 --- a/arch/arm/boot/dts/ste-hrefv60plus.dtsi +++ b/arch/arm/boot/dts/ste-hrefv60plus.dtsi @@ -56,7 +56,7 @@ /* VMMCI level-shifter enable */ default_hrefv60_cfg2 { pins = "GPIO169_D22"; - ste,config = <&gpio_out_lo>; + ste,config = <&gpio_out_hi>; }; /* VMMCI level-shifter voltage select */ default_hrefv60_cfg3 { diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi index 9d4f86e..d845bd1 100644 --- a/arch/arm/boot/dts/tegra114.dtsi +++ b/arch/arm/boot/dts/tegra114.dtsi @@ -234,7 +234,9 @@ gpio-controller; #interrupt-cells = <2>; interrupt-controller; + /* gpio-ranges = <&pinmux 0 0 246>; + */ }; apbmisc@70000800 { diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi index 1e204a6..819e2ae 100644 --- a/arch/arm/boot/dts/tegra124.dtsi +++ b/arch/arm/boot/dts/tegra124.dtsi @@ -258,7 +258,9 @@ gpio-controller; #interrupt-cells = <2>; interrupt-controller; + /* gpio-ranges = <&pinmux 0 0 251>; + */ }; apbdma: dma@0,60020000 { diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index e058709..969b828 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -244,7 +244,9 @@ gpio-controller; #interrupt-cells = <2>; interrupt-controller; + /* gpio-ranges = <&pinmux 0 0 224>; + */ }; apbmisc@70000800 { diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index fe04fb5..c6938ad 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -349,7 +349,9 @@ gpio-controller; #interrupt-cells = <2>; interrupt-controller; + /* gpio-ranges = <&pinmux 0 0 248>; + */ }; apbmisc@70000800 { diff --git a/arch/arm/boot/dts/uniphier-ph1-ld6b-ref.dts b/arch/arm/boot/dts/uniphier-ph1-ld6b-ref.dts index 33963ac..f80f772 100644 --- a/arch/arm/boot/dts/uniphier-ph1-ld6b-ref.dts +++ b/arch/arm/boot/dts/uniphier-ph1-ld6b-ref.dts @@ -85,7 +85,7 @@ }; ðsc { - interrupts = <0 50 4>; + interrupts = <0 52 4>; }; &serial0 { diff --git a/arch/arm/kvm/Kconfig b/arch/arm/kvm/Kconfig index 210ecca..356970f 100644 --- a/arch/arm/kvm/Kconfig +++ b/arch/arm/kvm/Kconfig @@ -21,6 +21,7 @@ config KVM depends on MMU && OF select PREEMPT_NOTIFIERS select ANON_INODES + select ARM_GIC select HAVE_KVM_CPU_RELAX_INTERCEPT select HAVE_KVM_ARCH_TLB_FLUSH_ALL select KVM_MMIO diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index dc017ad..78b2869 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -1080,7 +1080,7 @@ static int init_hyp_mode(void) */ err = kvm_timer_hyp_init(); if (err) - goto out_free_mappings; + goto out_free_context; #ifndef CONFIG_HOTPLUG_CPU free_boot_hyp_pgd(); diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index 4a87e86..7c21760 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -200,15 +200,15 @@ no_clk: args.args_count = 0; child_domain = of_genpd_get_from_provider(&args); if (IS_ERR(child_domain)) - goto next_pd; + continue; if (of_parse_phandle_with_args(np, "power-domains", "#power-domain-cells", 0, &args) != 0) - goto next_pd; + continue; parent_domain = of_genpd_get_from_provider(&args); if (IS_ERR(parent_domain)) - goto next_pd; + continue; if (pm_genpd_add_subdomain(parent_domain, child_domain)) pr_warn("%s failed to add subdomain: %s\n", @@ -216,8 +216,6 @@ no_clk: else pr_info("%s has as child subdomain: %s.\n", parent_domain->name, child_domain->name); -next_pd: - of_node_put(np); } return 0; diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index b3a0dff..33d1460 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -49,6 +49,7 @@ config SOC_OMAP5 select OMAP_INTERCONNECT select OMAP_INTERCONNECT_BARRIER select PM_OPP if PM + select ZONE_DMA if ARM_LPAE config SOC_AM33XX bool "TI AM33XX" @@ -78,6 +79,7 @@ config SOC_DRA7XX select OMAP_INTERCONNECT select OMAP_INTERCONNECT_BARRIER select PM_OPP if PM + select ZONE_DMA if ARM_LPAE config ARCH_OMAP2PLUS bool diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c index 6133eaa..fb219a3 100644 --- a/arch/arm/mach-omap2/board-generic.c +++ b/arch/arm/mach-omap2/board-generic.c @@ -106,6 +106,7 @@ DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)") MACHINE_END static const char *const omap36xx_boards_compat[] __initconst = { + "ti,omap3630", "ti,omap36xx", NULL, }; @@ -243,6 +244,9 @@ static const char *const omap5_boards_compat[] __initconst = { }; DT_MACHINE_START(OMAP5_DT, "Generic OMAP5 (Flattened Device Tree)") +#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE) + .dma_zone_size = SZ_2G, +#endif .reserve = omap_reserve, .smp = smp_ops(omap4_smp_ops), .map_io = omap5_map_io, @@ -288,6 +292,9 @@ static const char *const dra74x_boards_compat[] __initconst = { }; DT_MACHINE_START(DRA74X_DT, "Generic DRA74X (Flattened Device Tree)") +#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE) + .dma_zone_size = SZ_2G, +#endif .reserve = omap_reserve, .smp = smp_ops(omap4_smp_ops), .map_io = dra7xx_map_io, @@ -308,6 +315,9 @@ static const char *const dra72x_boards_compat[] __initconst = { }; DT_MACHINE_START(DRA72X_DT, "Generic DRA72X (Flattened Device Tree)") +#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE) + .dma_zone_size = SZ_2G, +#endif .reserve = omap_reserve, .map_io = dra7xx_map_io, .init_early = dra7xx_init_early, diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index ea56397..1dfe346 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -559,7 +559,14 @@ static void pdata_quirks_check(struct pdata_init *quirks) void __init pdata_quirks_init(const struct of_device_id *omap_dt_match_table) { - omap_sdrc_init(NULL, NULL); + /* + * We still need this for omap2420 and omap3 PM to work, others are + * using drivers/misc/sram.c already. + */ + if (of_machine_is_compatible("ti,omap2420") || + of_machine_is_compatible("ti,omap3")) + omap_sdrc_init(NULL, NULL); + pdata_quirks_check(auxdata_quirks); of_platform_populate(NULL, omap_dt_match_table, omap_auxdata_lookup, NULL); diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c index 06005d3..20ce2d3 100644 --- a/arch/arm/mach-pxa/pxa3xx.c +++ b/arch/arm/mach-pxa/pxa3xx.c @@ -42,10 +42,6 @@ #define PECR_IS(n) ((1 << ((n) * 2)) << 29) extern void __init pxa_dt_irq_init(int (*fn)(struct irq_data *, unsigned int)); -#ifdef CONFIG_PM - -#define ISRAM_START 0x5c000000 -#define ISRAM_SIZE SZ_256K /* * NAND NFC: DFI bus arbitration subset @@ -54,6 +50,11 @@ extern void __init pxa_dt_irq_init(int (*fn)(struct irq_data *, unsigned int)); #define NDCR_ND_ARB_EN (1 << 12) #define NDCR_ND_ARB_CNTL (1 << 19) +#ifdef CONFIG_PM + +#define ISRAM_START 0x5c000000 +#define ISRAM_SIZE SZ_256K + static void __iomem *sram; static unsigned long wakeup_src; diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c index 876060b..b8efb8c 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c @@ -614,6 +614,7 @@ load_common: case BPF_LD | BPF_B | BPF_IND: load_order = 0; load_ind: + update_on_xread(ctx); OP_IMM3(ARM_ADD, r_off, r_X, k, ctx); goto load_common; case BPF_LDX | BPF_IMM: diff --git a/arch/arm/plat-orion/common.c b/arch/arm/plat-orion/common.c index 2235081..8861c36 100644 --- a/arch/arm/plat-orion/common.c +++ b/arch/arm/plat-orion/common.c @@ -495,7 +495,7 @@ void __init orion_ge00_switch_init(struct dsa_platform_data *d, int irq) d->netdev = &orion_ge00.dev; for (i = 0; i < d->nr_chips; i++) - d->chip[i].host_dev = &orion_ge00_shared.dev; + d->chip[i].host_dev = &orion_ge_mvmdio.dev; orion_switch_device.dev.platform_data = d; platform_device_register(&orion_switch_device); diff --git a/arch/powerpc/include/asm/cache.h b/arch/powerpc/include/asm/cache.h index 0dc42c5..5f8229e 100644 --- a/arch/powerpc/include/asm/cache.h +++ b/arch/powerpc/include/asm/cache.h @@ -3,7 +3,6 @@ #ifdef __KERNEL__ -#include <asm/reg.h> /* bytes per L1 cache line */ #if defined(CONFIG_8xx) || defined(CONFIG_403GCX) @@ -40,12 +39,6 @@ struct ppc64_caches { }; extern struct ppc64_caches ppc64_caches; - -static inline void logmpp(u64 x) -{ - asm volatile(PPC_LOGMPP(R1) : : "r" (x)); -} - #endif /* __powerpc64__ && ! __ASSEMBLY__ */ #if defined(__ASSEMBLY__) diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 827a38d..887c259 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -297,8 +297,6 @@ struct kvmppc_vcore { u32 arch_compat; ulong pcr; ulong dpdes; /* doorbell state (POWER8) */ - void *mpp_buffer; /* Micro Partition Prefetch buffer */ - bool mpp_buffer_is_valid; ulong conferring_threads; }; diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 790f5d1..7ab04fc 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -141,7 +141,6 @@ #define PPC_INST_ISEL 0x7c00001e #define PPC_INST_ISEL_MASK 0xfc00003e #define PPC_INST_LDARX 0x7c0000a8 -#define PPC_INST_LOGMPP 0x7c0007e4 #define PPC_INST_LSWI 0x7c0004aa #define PPC_INST_LSWX 0x7c00042a #define PPC_INST_LWARX 0x7c000028 @@ -285,20 +284,6 @@ #define __PPC_EH(eh) 0 #endif -/* POWER8 Micro Partition Prefetch (MPP) parameters */ -/* Address mask is common for LOGMPP instruction and MPPR SPR */ -#define PPC_MPPE_ADDRESS_MASK 0xffffffffc000ULL - -/* Bits 60 and 61 of MPP SPR should be set to one of the following */ -/* Aborting the fetch is indeed setting 00 in the table size bits */ -#define PPC_MPPR_FETCH_ABORT (0x0ULL << 60) -#define PPC_MPPR_FETCH_WHOLE_TABLE (0x2ULL << 60) - -/* Bits 54 and 55 of register for LOGMPP instruction should be set to: */ -#define PPC_LOGMPP_LOG_L2 (0x02ULL << 54) -#define PPC_LOGMPP_LOG_L2L3 (0x01ULL << 54) -#define PPC_LOGMPP_LOG_ABORT (0x03ULL << 54) - /* Deal with instructions that older assemblers aren't aware of */ #define PPC_DCBAL(a, b) stringify_in_c(.long PPC_INST_DCBAL | \ __PPC_RA(a) | __PPC_RB(b)) @@ -307,8 +292,6 @@ #define PPC_LDARX(t, a, b, eh) stringify_in_c(.long PPC_INST_LDARX | \ ___PPC_RT(t) | ___PPC_RA(a) | \ ___PPC_RB(b) | __PPC_EH(eh)) -#define PPC_LOGMPP(b) stringify_in_c(.long PPC_INST_LOGMPP | \ - __PPC_RB(b)) #define PPC_LWARX(t, a, b, eh) stringify_in_c(.long PPC_INST_LWARX | \ ___PPC_RT(t) | ___PPC_RA(a) | \ ___PPC_RB(b) | __PPC_EH(eh)) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index aa1cc5f0..a908ada 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -226,7 +226,6 @@ #define CTRL_TE 0x00c00000 /* thread enable */ #define CTRL_RUNLATCH 0x1 #define SPRN_DAWR 0xB4 -#define SPRN_MPPR 0xB8 /* Micro Partition Prefetch Register */ #define SPRN_RPR 0xBA /* Relative Priority Register */ #define SPRN_CIABR 0xBB #define CIABR_PRIV 0x3 diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 84bf934..5a753fa 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -1043,6 +1043,9 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) if (!capable(CAP_SYS_ADMIN)) return -EPERM; + if (!rtas.entry) + return -EINVAL; + if (copy_from_user(&args, uargs, 3 * sizeof(u32)) != 0) return -EFAULT; diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 2280497..9c26c5a 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -36,7 +36,6 @@ #include <asm/reg.h> #include <asm/cputable.h> -#include <asm/cache.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> #include <asm/uaccess.h> @@ -75,12 +74,6 @@ static DECLARE_BITMAP(default_enabled_hcalls, MAX_HCALL_OPCODE/4 + 1); -#if defined(CONFIG_PPC_64K_PAGES) -#define MPP_BUFFER_ORDER 0 -#elif defined(CONFIG_PPC_4K_PAGES) -#define MPP_BUFFER_ORDER 3 -#endif - static int dynamic_mt_modes = 6; module_param(dynamic_mt_modes, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(dynamic_mt_modes, "Set of allowed dynamic micro-threading modes: 0 (= none), 2, 4, or 6 (= 2 or 4)"); @@ -1455,13 +1448,6 @@ static struct kvmppc_vcore *kvmppc_vcore_create(struct kvm *kvm, int core) vcore->kvm = kvm; INIT_LIST_HEAD(&vcore->preempt_list); - vcore->mpp_buffer_is_valid = false; - - if (cpu_has_feature(CPU_FTR_ARCH_207S)) - vcore->mpp_buffer = (void *)__get_free_pages( - GFP_KERNEL|__GFP_ZERO, - MPP_BUFFER_ORDER); - return vcore; } @@ -1894,33 +1880,6 @@ static int on_primary_thread(void) return 1; } -static void kvmppc_start_saving_l2_cache(struct kvmppc_vcore *vc) -{ - phys_addr_t phy_addr, mpp_addr; - - phy_addr = (phys_addr_t)virt_to_phys(vc->mpp_buffer); - mpp_addr = phy_addr & PPC_MPPE_ADDRESS_MASK; - - mtspr(SPRN_MPPR, mpp_addr | PPC_MPPR_FETCH_ABORT); - logmpp(mpp_addr | PPC_LOGMPP_LOG_L2); - - vc->mpp_buffer_is_valid = true; -} - -static void kvmppc_start_restoring_l2_cache(const struct kvmppc_vcore *vc) -{ - phys_addr_t phy_addr, mpp_addr; - - phy_addr = virt_to_phys(vc->mpp_buffer); - mpp_addr = phy_addr & PPC_MPPE_ADDRESS_MASK; - - /* We must abort any in-progress save operations to ensure - * the table is valid so that prefetch engine knows when to - * stop prefetching. */ - logmpp(mpp_addr | PPC_LOGMPP_LOG_ABORT); - mtspr(SPRN_MPPR, mpp_addr | PPC_MPPR_FETCH_WHOLE_TABLE); -} - /* * A list of virtual cores for each physical CPU. * These are vcores that could run but their runner VCPU tasks are @@ -2471,14 +2430,8 @@ static noinline void kvmppc_run_core(struct kvmppc_vcore *vc) srcu_idx = srcu_read_lock(&vc->kvm->srcu); - if (vc->mpp_buffer_is_valid) - kvmppc_start_restoring_l2_cache(vc); - __kvmppc_vcore_entry(); - if (vc->mpp_buffer) - kvmppc_start_saving_l2_cache(vc); - srcu_read_unlock(&vc->kvm->srcu, srcu_idx); spin_lock(&vc->lock); @@ -3073,14 +3026,8 @@ static void kvmppc_free_vcores(struct kvm *kvm) { long int i; - for (i = 0; i < KVM_MAX_VCORES; ++i) { - if (kvm->arch.vcores[i] && kvm->arch.vcores[i]->mpp_buffer) { - struct kvmppc_vcore *vc = kvm->arch.vcores[i]; - free_pages((unsigned long)vc->mpp_buffer, - MPP_BUFFER_ORDER); - } + for (i = 0; i < KVM_MAX_VCORES; ++i) kfree(kvm->arch.vcores[i]); - } kvm->arch.online_vcores = 0; } diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c index 8f70ba6..ca26483 100644 --- a/arch/powerpc/platforms/powernv/smp.c +++ b/arch/powerpc/platforms/powernv/smp.c @@ -171,7 +171,26 @@ static void pnv_smp_cpu_kill_self(void) * so clear LPCR:PECE1. We keep PECE2 enabled. */ mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1); + + /* + * Hard-disable interrupts, and then clear irq_happened flags + * that we can safely ignore while off-line, since they + * are for things for which we do no processing when off-line + * (or in the case of HMI, all the processing we need to do + * is done in lower-level real-mode code). + */ + hard_irq_disable(); + local_paca->irq_happened &= ~(PACA_IRQ_DEC | PACA_IRQ_HMI); + while (!generic_check_cpu_restart(cpu)) { + /* + * Clear IPI flag, since we don't handle IPIs while + * offline, except for those when changing micro-threading + * mode, which are handled explicitly below, and those + * for coming online, which are handled via + * generic_check_cpu_restart() calls. + */ + kvmppc_set_host_ipi(cpu, 0); ppc64_runlatch_off(); @@ -196,20 +215,20 @@ static void pnv_smp_cpu_kill_self(void) * having finished executing in a KVM guest, then srr1 * contains 0. */ - if ((srr1 & wmask) == SRR1_WAKEEE) { + if (((srr1 & wmask) == SRR1_WAKEEE) || + (local_paca->irq_happened & PACA_IRQ_EE)) { icp_native_flush_interrupt(); - local_paca->irq_happened &= PACA_IRQ_HARD_DIS; - smp_mb(); } else if ((srr1 & wmask) == SRR1_WAKEHDBELL) { unsigned long msg = PPC_DBELL_TYPE(PPC_DBELL_SERVER); asm volatile(PPC_MSGCLR(%0) : : "r" (msg)); - kvmppc_set_host_ipi(cpu, 0); } + local_paca->irq_happened &= ~(PACA_IRQ_EE | PACA_IRQ_DBELL); + smp_mb(); if (cpu_core_split_required()) continue; - if (!generic_check_cpu_restart(cpu)) + if (srr1 && !generic_check_cpu_restart(cpu)) DBG("CPU%d Unexpected exit while offline !\n", cpu); } mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1); diff --git a/arch/um/Makefile b/arch/um/Makefile index 098ab33..e3abe6f 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -70,8 +70,8 @@ KBUILD_AFLAGS += $(ARCH_INCLUDE) USER_CFLAGS = $(patsubst $(KERNEL_DEFINES),,$(patsubst -I%,,$(KBUILD_CFLAGS))) \ $(ARCH_INCLUDE) $(MODE_INCLUDE) $(filter -I%,$(CFLAGS)) \ - -D_FILE_OFFSET_BITS=64 -idirafter include \ - -D__KERNEL__ -D__UM_HOST__ + -D_FILE_OFFSET_BITS=64 -idirafter $(srctree)/include \ + -idirafter $(obj)/include -D__KERNEL__ -D__UM_HOST__ #This will adjust *FLAGS accordingly to the platform. include $(ARCH_DIR)/Makefile-os-$(OS) diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index d8a9fce..98783dd 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -220,7 +220,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, show_regs(container_of(regs, struct pt_regs, regs)); panic("Segfault with no mm"); } - else if (!is_user && address < TASK_SIZE) { + else if (!is_user && address > PAGE_SIZE && address < TASK_SIZE) { show_regs(container_of(regs, struct pt_regs, regs)); panic("Kernel tried to access user memory at addr 0x%lx, ip 0x%lx", address, ip); diff --git a/arch/um/os-Linux/helper.c b/arch/um/os-Linux/helper.c index e3ee4a5..3f02d42 100644 --- a/arch/um/os-Linux/helper.c +++ b/arch/um/os-Linux/helper.c @@ -96,7 +96,7 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv) "ret = %d\n", -n); ret = n; } - CATCH_EINTR(waitpid(pid, NULL, __WCLONE)); + CATCH_EINTR(waitpid(pid, NULL, __WALL)); } out_free2: @@ -129,7 +129,7 @@ int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags, return err; } if (stack_out == NULL) { - CATCH_EINTR(pid = waitpid(pid, &status, __WCLONE)); + CATCH_EINTR(pid = waitpid(pid, &status, __WALL)); if (pid < 0) { err = -errno; printk(UM_KERN_ERR "run_helper_thread - wait failed, " @@ -148,7 +148,7 @@ int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags, int helper_wait(int pid) { int ret, status; - int wflags = __WCLONE; + int wflags = __WALL; CATCH_EINTR(ret = waitpid(pid, &status, wflags)); if (ret < 0) { diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index ee1b6d3..db51c1f 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -667,6 +667,7 @@ setup_gop32(struct screen_info *si, efi_guid_t *proto, bool conout_found = false; void *dummy = NULL; u32 h = handles[i]; + u32 current_fb_base; status = efi_call_early(handle_protocol, h, proto, (void **)&gop32); @@ -678,7 +679,7 @@ setup_gop32(struct screen_info *si, efi_guid_t *proto, if (status == EFI_SUCCESS) conout_found = true; - status = __gop_query32(gop32, &info, &size, &fb_base); + status = __gop_query32(gop32, &info, &size, ¤t_fb_base); if (status == EFI_SUCCESS && (!first_gop || conout_found)) { /* * Systems that use the UEFI Console Splitter may @@ -692,6 +693,7 @@ setup_gop32(struct screen_info *si, efi_guid_t *proto, pixel_format = info->pixel_format; pixel_info = info->pixel_information; pixels_per_scan_line = info->pixels_per_scan_line; + fb_base = current_fb_base; /* * Once we've found a GOP supporting ConOut, @@ -770,6 +772,7 @@ setup_gop64(struct screen_info *si, efi_guid_t *proto, bool conout_found = false; void *dummy = NULL; u64 h = handles[i]; + u32 current_fb_base; status = efi_call_early(handle_protocol, h, proto, (void **)&gop64); @@ -781,7 +784,7 @@ setup_gop64(struct screen_info *si, efi_guid_t *proto, if (status == EFI_SUCCESS) conout_found = true; - status = __gop_query64(gop64, &info, &size, &fb_base); + status = __gop_query64(gop64, &info, &size, ¤t_fb_base); if (status == EFI_SUCCESS && (!first_gop || conout_found)) { /* * Systems that use the UEFI Console Splitter may @@ -795,6 +798,7 @@ setup_gop64(struct screen_info *si, efi_guid_t *proto, pixel_format = info->pixel_format; pixel_info = info->pixel_information; pixels_per_scan_line = info->pixels_per_scan_line; + fb_base = current_fb_base; /* * Once we've found a GOP supporting ConOut, diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h index e466119..ff8b9a1 100644 --- a/arch/x86/include/asm/string_64.h +++ b/arch/x86/include/asm/string_64.h @@ -27,12 +27,11 @@ static __always_inline void *__inline_memcpy(void *to, const void *from, size_t function. */ #define __HAVE_ARCH_MEMCPY 1 +extern void *memcpy(void *to, const void *from, size_t len); extern void *__memcpy(void *to, const void *from, size_t len); #ifndef CONFIG_KMEMCHECK -#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4 -extern void *memcpy(void *to, const void *from, size_t len); -#else +#if (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || __GNUC__ < 4 #define memcpy(dst, src, len) \ ({ \ size_t __len = (len); \ diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 5c60bb1..bb6bfc0 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -2907,6 +2907,7 @@ int mp_irqdomain_alloc(struct irq_domain *domain, unsigned int virq, struct irq_data *irq_data; struct mp_chip_data *data; struct irq_alloc_info *info = arg; + unsigned long flags; if (!info || nr_irqs > 1) return -EINVAL; @@ -2939,11 +2940,14 @@ int mp_irqdomain_alloc(struct irq_domain *domain, unsigned int virq, cfg = irqd_cfg(irq_data); add_pin_to_irq_node(data, ioapic_alloc_attr_node(info), ioapic, pin); + + local_irq_save(flags); if (info->ioapic_entry) mp_setup_entry(cfg, data, info->ioapic_entry); mp_register_handler(virq, data->trigger); if (virq < nr_legacy_irqs()) legacy_pic->mask(virq); + local_irq_restore(flags); apic_printk(APIC_VERBOSE, KERN_DEBUG "IOAPIC[%d]: Set routing entry (%d-%d -> 0x%x -> IRQ %d Mode:%i Active:%i Dest:%d)\n", diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 39e585a..e28db18 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -550,14 +550,14 @@ unsigned long get_wchan(struct task_struct *p) if (sp < bottom || sp > top) return 0; - fp = READ_ONCE(*(unsigned long *)sp); + fp = READ_ONCE_NOCHECK(*(unsigned long *)sp); do { if (fp < bottom || fp > top) return 0; - ip = READ_ONCE(*(unsigned long *)(fp + sizeof(unsigned long))); + ip = READ_ONCE_NOCHECK(*(unsigned long *)(fp + sizeof(unsigned long))); if (!in_sched_functions(ip)) return ip; - fp = READ_ONCE(*(unsigned long *)fp); + fp = READ_ONCE_NOCHECK(*(unsigned long *)fp); } while (count++ < 16 && p->state != TASK_RUNNING); return 0; } diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index fdb7f2a..a3cccbf 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1173,6 +1173,14 @@ void __init setup_arch(char **cmdline_p) clone_pgd_range(initial_page_table + KERNEL_PGD_BOUNDARY, swapper_pg_dir + KERNEL_PGD_BOUNDARY, KERNEL_PGD_PTRS); + + /* + * sync back low identity map too. It is used for example + * in the 32-bit EFI stub. + */ + clone_pgd_range(initial_page_table, + swapper_pg_dir + KERNEL_PGD_BOUNDARY, + KERNEL_PGD_PTRS); #endif tboot_probe(); diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index e0c198e..892ee2e5 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -509,7 +509,7 @@ void __inquire_remote_apic(int apicid) */ #define UDELAY_10MS_DEFAULT 10000 -static unsigned int init_udelay = UDELAY_10MS_DEFAULT; +static unsigned int init_udelay = INT_MAX; static int __init cpu_init_udelay(char *str) { @@ -522,13 +522,16 @@ early_param("cpu_init_udelay", cpu_init_udelay); static void __init smp_quirk_init_udelay(void) { /* if cmdline changed it from default, leave it alone */ - if (init_udelay != UDELAY_10MS_DEFAULT) + if (init_udelay != INT_MAX) return; /* if modern processor, use no delay */ if (((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && (boot_cpu_data.x86 == 6)) || ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && (boot_cpu_data.x86 >= 0xF))) init_udelay = 0; + + /* else, use legacy delay */ + init_udelay = UDELAY_10MS_DEFAULT; } /* @@ -657,7 +660,9 @@ wakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_eip) /* * Give the other CPU some time to accept the IPI. */ - if (init_udelay) + if (init_udelay == 0) + udelay(10); + else udelay(300); pr_debug("Startup point 1\n"); @@ -668,7 +673,9 @@ wakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_eip) /* * Give the other CPU some time to accept the IPI. */ - if (init_udelay) + if (init_udelay == 0) + udelay(10); + else udelay(200); if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ diff --git a/arch/x86/um/ldt.c b/arch/x86/um/ldt.c index 9701a4f..836a1eb 100644 --- a/arch/x86/um/ldt.c +++ b/arch/x86/um/ldt.c @@ -12,7 +12,10 @@ #include <skas.h> #include <sysdep/tls.h> -extern int modify_ldt(int func, void *ptr, unsigned long bytecount); +static inline int modify_ldt (int func, void *ptr, unsigned long bytecount) +{ + return syscall(__NR_modify_ldt, func, ptr, bytecount); +} static long write_ldt_entry(struct mm_id *mm_idp, int func, struct user_desc *desc, void **addr, int done) diff --git a/block/blk-core.c b/block/blk-core.c index 2eb722d..18e92a6 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -576,7 +576,7 @@ void blk_cleanup_queue(struct request_queue *q) q->queue_lock = &q->__queue_lock; spin_unlock_irq(lock); - bdi_destroy(&q->backing_dev_info); + bdi_unregister(&q->backing_dev_info); /* @q is and will stay empty, shutdown and put */ blk_put_queue(q); diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index ed96474..ec2d119 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -641,6 +641,7 @@ void blk_mq_free_tags(struct blk_mq_tags *tags) { bt_free(&tags->bitmap_tags); bt_free(&tags->breserved_tags); + free_cpumask_var(tags->cpumask); kfree(tags); } diff --git a/block/blk-mq.c b/block/blk-mq.c index 7785ae96..85f0143 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -2296,10 +2296,8 @@ void blk_mq_free_tag_set(struct blk_mq_tag_set *set) int i; for (i = 0; i < set->nr_hw_queues; i++) { - if (set->tags[i]) { + if (set->tags[i]) blk_mq_free_rq_map(set, set->tags[i], i); - free_cpumask_var(set->tags[i]->cpumask); - } } kfree(set->tags); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 3e44a9d..07b42f5 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -540,6 +540,7 @@ static void blk_release_queue(struct kobject *kobj) struct request_queue *q = container_of(kobj, struct request_queue, kobj); + bdi_exit(&q->backing_dev_info); blkcg_exit_queue(q); if (q->elevator) { diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 950fff9..a12ff98 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -187,7 +187,7 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, * global one. Requires architecture specific dev_get_cma_area() helper * function. */ -struct page *dma_alloc_from_contiguous(struct device *dev, int count, +struct page *dma_alloc_from_contiguous(struct device *dev, size_t count, unsigned int align) { if (align > CONFIG_CMA_ALIGNMENT) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 293495a..1b87623 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -60,6 +60,7 @@ struct nbd_device { bool disconnect; /* a disconnect has been requested by user */ struct timer_list timeout_timer; + spinlock_t tasks_lock; struct task_struct *task_recv; struct task_struct *task_send; @@ -140,21 +141,23 @@ static void sock_shutdown(struct nbd_device *nbd) static void nbd_xmit_timeout(unsigned long arg) { struct nbd_device *nbd = (struct nbd_device *)arg; - struct task_struct *task; + unsigned long flags; if (list_empty(&nbd->queue_head)) return; nbd->disconnect = true; - task = READ_ONCE(nbd->task_recv); - if (task) - force_sig(SIGKILL, task); + spin_lock_irqsave(&nbd->tasks_lock, flags); + + if (nbd->task_recv) + force_sig(SIGKILL, nbd->task_recv); - task = READ_ONCE(nbd->task_send); - if (task) + if (nbd->task_send) force_sig(SIGKILL, nbd->task_send); + spin_unlock_irqrestore(&nbd->tasks_lock, flags); + dev_err(nbd_to_dev(nbd), "Connection timed out, killed receiver and sender, shutting down connection\n"); } @@ -403,17 +406,24 @@ static int nbd_thread_recv(struct nbd_device *nbd) { struct request *req; int ret; + unsigned long flags; BUG_ON(nbd->magic != NBD_MAGIC); sk_set_memalloc(nbd->sock->sk); + spin_lock_irqsave(&nbd->tasks_lock, flags); nbd->task_recv = current; + spin_unlock_irqrestore(&nbd->tasks_lock, flags); ret = device_create_file(disk_to_dev(nbd->disk), &pid_attr); if (ret) { dev_err(disk_to_dev(nbd->disk), "device_create_file failed!\n"); + + spin_lock_irqsave(&nbd->tasks_lock, flags); nbd->task_recv = NULL; + spin_unlock_irqrestore(&nbd->tasks_lock, flags); + return ret; } @@ -429,7 +439,9 @@ static int nbd_thread_recv(struct nbd_device *nbd) device_remove_file(disk_to_dev(nbd->disk), &pid_attr); + spin_lock_irqsave(&nbd->tasks_lock, flags); nbd->task_recv = NULL; + spin_unlock_irqrestore(&nbd->tasks_lock, flags); if (signal_pending(current)) { siginfo_t info; @@ -534,8 +546,11 @@ static int nbd_thread_send(void *data) { struct nbd_device *nbd = data; struct request *req; + unsigned long flags; + spin_lock_irqsave(&nbd->tasks_lock, flags); nbd->task_send = current; + spin_unlock_irqrestore(&nbd->tasks_lock, flags); set_user_nice(current, MIN_NICE); while (!kthread_should_stop() || !list_empty(&nbd->waiting_queue)) { @@ -572,7 +587,15 @@ static int nbd_thread_send(void *data) nbd_handle_req(nbd, req); } + spin_lock_irqsave(&nbd->tasks_lock, flags); nbd->task_send = NULL; + spin_unlock_irqrestore(&nbd->tasks_lock, flags); + + /* Clear maybe pending signals */ + if (signal_pending(current)) { + siginfo_t info; + dequeue_signal_lock(current, ¤t->blocked, &info); + } return 0; } @@ -1052,6 +1075,7 @@ static int __init nbd_init(void) nbd_dev[i].magic = NBD_MAGIC; INIT_LIST_HEAD(&nbd_dev[i].waiting_queue); spin_lock_init(&nbd_dev[i].queue_lock); + spin_lock_init(&nbd_dev[i].tasks_lock); INIT_LIST_HEAD(&nbd_dev[i].queue_head); mutex_init(&nbd_dev[i].tx_lock); init_timer(&nbd_dev[i].timeout_timer); diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index 6f04771..ccc0c1f 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -603,27 +603,31 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx, struct nvme_iod *iod = ctx; struct request *req = iod_get_private(iod); struct nvme_cmd_info *cmd_rq = blk_mq_rq_to_pdu(req); - u16 status = le16_to_cpup(&cqe->status) >> 1; + bool requeue = false; + int error = 0; if (unlikely(status)) { if (!(status & NVME_SC_DNR || blk_noretry_request(req)) && (jiffies - req->start_time) < req->timeout) { unsigned long flags; + requeue = true; blk_mq_requeue_request(req); spin_lock_irqsave(req->q->queue_lock, flags); if (!blk_queue_stopped(req->q)) blk_mq_kick_requeue_list(req->q); spin_unlock_irqrestore(req->q->queue_lock, flags); - return; + goto release_iod; } if (req->cmd_type == REQ_TYPE_DRV_PRIV) { if (cmd_rq->ctx == CMD_CTX_CANCELLED) - status = -EINTR; + error = -EINTR; + else + error = status; } else { - status = nvme_error_status(status); + error = nvme_error_status(status); } } @@ -635,8 +639,9 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx, if (cmd_rq->aborted) dev_warn(nvmeq->dev->dev, "completing aborted command with status:%04x\n", - status); + error); +release_iod: if (iod->nents) { dma_unmap_sg(nvmeq->dev->dev, iod->sg, iod->nents, rq_data_dir(req) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); @@ -649,7 +654,8 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx, } nvme_free_iod(nvmeq->dev, iod); - blk_mq_complete_request(req, status); + if (likely(!requeue)) + blk_mq_complete_request(req, error); } /* length is in bytes. gfp flags indicates whether we may sleep. */ @@ -1804,7 +1810,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) length = (io.nblocks + 1) << ns->lba_shift; meta_len = (io.nblocks + 1) * ns->ms; - metadata = (void __user *)(unsigned long)io.metadata; + metadata = (void __user *)(uintptr_t)io.metadata; write = io.opcode & 1; if (ns->ext) { @@ -1844,7 +1850,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) c.rw.metadata = cpu_to_le64(meta_dma); status = __nvme_submit_sync_cmd(ns->queue, &c, NULL, - (void __user *)io.addr, length, NULL, 0); + (void __user *)(uintptr_t)io.addr, length, NULL, 0); unmap: if (meta) { if (status == NVME_SC_SUCCESS && !write) { @@ -1886,7 +1892,7 @@ static int nvme_user_cmd(struct nvme_dev *dev, struct nvme_ns *ns, timeout = msecs_to_jiffies(cmd.timeout_ms); status = __nvme_submit_sync_cmd(ns ? ns->queue : dev->admin_q, &c, - NULL, (void __user *)cmd.addr, cmd.data_len, + NULL, (void __user *)(uintptr_t)cmd.addr, cmd.data_len, &cmd.result, timeout); if (status >= 0) { if (put_user(cmd.result, &ucmd->result)) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index f5e49b6..6f26cf3 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -96,6 +96,8 @@ static int atomic_dec_return_safe(atomic_t *v) #define RBD_MINORS_PER_MAJOR 256 #define RBD_SINGLE_MAJOR_PART_SHIFT 4 +#define RBD_MAX_PARENT_CHAIN_LEN 16 + #define RBD_SNAP_DEV_NAME_PREFIX "snap_" #define RBD_MAX_SNAP_NAME_LEN \ (NAME_MAX - (sizeof (RBD_SNAP_DEV_NAME_PREFIX) - 1)) @@ -426,7 +428,7 @@ static ssize_t rbd_add_single_major(struct bus_type *bus, const char *buf, size_t count); static ssize_t rbd_remove_single_major(struct bus_type *bus, const char *buf, size_t count); -static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping); +static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth); static void rbd_spec_put(struct rbd_spec *spec); static int rbd_dev_id_to_minor(int dev_id) @@ -5131,44 +5133,51 @@ out_err: return ret; } -static int rbd_dev_probe_parent(struct rbd_device *rbd_dev) +/* + * @depth is rbd_dev_image_probe() -> rbd_dev_probe_parent() -> + * rbd_dev_image_probe() recursion depth, which means it's also the + * length of the already discovered part of the parent chain. + */ +static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth) { struct rbd_device *parent = NULL; - struct rbd_spec *parent_spec; - struct rbd_client *rbdc; int ret; if (!rbd_dev->parent_spec) return 0; - /* - * We need to pass a reference to the client and the parent - * spec when creating the parent rbd_dev. Images related by - * parent/child relationships always share both. - */ - parent_spec = rbd_spec_get(rbd_dev->parent_spec); - rbdc = __rbd_get_client(rbd_dev->rbd_client); - ret = -ENOMEM; - parent = rbd_dev_create(rbdc, parent_spec, NULL); - if (!parent) + if (++depth > RBD_MAX_PARENT_CHAIN_LEN) { + pr_info("parent chain is too long (%d)\n", depth); + ret = -EINVAL; + goto out_err; + } + + parent = rbd_dev_create(rbd_dev->rbd_client, rbd_dev->parent_spec, + NULL); + if (!parent) { + ret = -ENOMEM; goto out_err; + } + + /* + * Images related by parent/child relationships always share + * rbd_client and spec/parent_spec, so bump their refcounts. + */ + __rbd_get_client(rbd_dev->rbd_client); + rbd_spec_get(rbd_dev->parent_spec); - ret = rbd_dev_image_probe(parent, false); + ret = rbd_dev_image_probe(parent, depth); if (ret < 0) goto out_err; + rbd_dev->parent = parent; atomic_set(&rbd_dev->parent_ref, 1); - return 0; + out_err: - if (parent) { - rbd_dev_unparent(rbd_dev); + rbd_dev_unparent(rbd_dev); + if (parent) rbd_dev_destroy(parent); - } else { - rbd_put_client(rbdc); - rbd_spec_put(parent_spec); - } - return ret; } @@ -5286,7 +5295,7 @@ static void rbd_dev_image_release(struct rbd_device *rbd_dev) * parent), initiate a watch on its header object before using that * object to get detailed information about the rbd image. */ -static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) +static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth) { int ret; @@ -5304,7 +5313,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) if (ret) goto err_out_format; - if (mapping) { + if (!depth) { ret = rbd_dev_header_watch_sync(rbd_dev); if (ret) { if (ret == -ENOENT) @@ -5325,7 +5334,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) * Otherwise this is a parent image, identified by pool, image * and snap ids - need to fill in names for those ids. */ - if (mapping) + if (!depth) ret = rbd_spec_fill_snap_id(rbd_dev); else ret = rbd_spec_fill_names(rbd_dev); @@ -5347,12 +5356,12 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) * Need to warn users if this image is the one being * mapped and has a parent. */ - if (mapping && rbd_dev->parent_spec) + if (!depth && rbd_dev->parent_spec) rbd_warn(rbd_dev, "WARNING: kernel layering is EXPERIMENTAL!"); } - ret = rbd_dev_probe_parent(rbd_dev); + ret = rbd_dev_probe_parent(rbd_dev, depth); if (ret) goto err_out_probe; @@ -5363,7 +5372,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) err_out_probe: rbd_dev_unprobe(rbd_dev); err_out_watch: - if (mapping) + if (!depth) rbd_dev_header_unwatch_sync(rbd_dev); out_header_name: kfree(rbd_dev->header_name); @@ -5426,7 +5435,7 @@ static ssize_t do_rbd_add(struct bus_type *bus, spec = NULL; /* rbd_dev now owns this */ rbd_opts = NULL; /* rbd_dev now owns this */ - rc = rbd_dev_image_probe(rbd_dev, true); + rc = rbd_dev_image_probe(rbd_dev, 0); if (rc < 0) goto err_out_rbd_dev; diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 6111708..a69c02d 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -1956,7 +1956,8 @@ static void blkback_changed(struct xenbus_device *dev, break; /* Missed the backend's Closing state -- fallthrough */ case XenbusStateClosing: - blkfront_closing(info); + if (info) + blkfront_closing(info); break; } } diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c index 7d9879e..7082c72 100644 --- a/drivers/bus/arm-ccn.c +++ b/drivers/bus/arm-ccn.c @@ -1184,11 +1184,12 @@ static int arm_ccn_pmu_cpu_notifier(struct notifier_block *nb, if (!cpumask_test_and_clear_cpu(cpu, &dt->cpu)) break; target = cpumask_any_but(cpu_online_mask, cpu); - if (target < 0) + if (target >= nr_cpu_ids) break; perf_pmu_migrate_context(&dt->pmu, cpu, target); cpumask_set_cpu(target, &dt->cpu); - WARN_ON(irq_set_affinity(ccn->irq, &dt->cpu) != 0); + if (ccn->irq) + WARN_ON(irq_set_affinity(ccn->irq, &dt->cpu) != 0); default: break; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c index efed115..ed2bbe5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c @@ -294,10 +294,14 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, struct amdgpu_device *adev = dev_get_drvdata(dev); umode_t effective_mode = attr->mode; - /* Skip limit attributes if DPM is not enabled */ + /* Skip attributes if DPM is not enabled */ if (!adev->pm.dpm_enabled && (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr || - attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)) + attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr || + attr == &sensor_dev_attr_pwm1.dev_attr.attr || + attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr || + attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || + attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) return 0; /* Skip fan attributes if fan is not present */ diff --git a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c index 9745ed3..7e9154c 100644 --- a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c @@ -2997,6 +2997,9 @@ static int kv_dpm_late_init(void *handle) struct amdgpu_device *adev = (struct amdgpu_device *)handle; int ret; + if (!amdgpu_dpm) + return 0; + /* init the sysfs and debugfs files late */ ret = amdgpu_pm_sysfs_init(adev); if (ret) diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5bca390..809959d 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1194,17 +1194,18 @@ static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_ list_for_each_entry(port, &mstb->ports, next) { if (port->port_num == port_num) { - if (!port->mstb) { + mstb = port->mstb; + if (!mstb) { DRM_ERROR("failed to lookup MSTB with lct %d, rad %02x\n", lct, rad[0]); - return NULL; + goto out; } - mstb = port->mstb; break; } } } kref_get(&mstb->kref); +out: mutex_unlock(&mgr->lock); return mstb; } diff --git a/drivers/gpu/drm/i915/i915_gem_shrinker.c b/drivers/gpu/drm/i915/i915_gem_shrinker.c index f6ecbda..6743417 100644 --- a/drivers/gpu/drm/i915/i915_gem_shrinker.c +++ b/drivers/gpu/drm/i915/i915_gem_shrinker.c @@ -143,7 +143,7 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, } /** - * i915_gem_shrink - Shrink buffer object caches completely + * i915_gem_shrink_all - Shrink buffer object caches completely * @dev_priv: i915 device * * This is a simple wraper around i915_gem_shrink() to aggressively shrink all diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index 8fd431b..a96b900 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -804,7 +804,10 @@ static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = { * Also note, that the object created here is not currently a "first class" * object, in that several ioctls are banned. These are the CPU access * ioctls: mmap(), pwrite and pread. In practice, you are expected to use - * direct access via your pointer rather than use those ioctls. + * direct access via your pointer rather than use those ioctls. Another + * restriction is that we do not allow userptr surfaces to be pinned to the + * hardware and so we reject any attempt to create a framebuffer out of a + * userptr. * * If you think this is a good interface to use to pass GPU memory between * drivers, please use dma-buf instead. In fact, wherever possible use diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index cf418be..b2270d5 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1724,6 +1724,15 @@ static void i9xx_enable_pll(struct intel_crtc *crtc) I915_READ(DPLL(!crtc->pipe)) | DPLL_DVO_2X_MODE); } + /* + * Apparently we need to have VGA mode enabled prior to changing + * the P1/P2 dividers. Otherwise the DPLL will keep using the old + * dividers, even though the register value does change. + */ + I915_WRITE(reg, 0); + + I915_WRITE(reg, dpll); + /* Wait for the clocks to stabilize. */ POSTING_READ(reg); udelay(150); @@ -14107,6 +14116,11 @@ static int intel_user_framebuffer_create_handle(struct drm_framebuffer *fb, struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); struct drm_i915_gem_object *obj = intel_fb->obj; + if (obj->userptr.mm) { + DRM_DEBUG("attempting to use a userptr for a framebuffer, denied\n"); + return -EINVAL; + } + return drm_gem_handle_create(file, &obj->base, handle); } @@ -14897,9 +14911,19 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) /* restore vblank interrupts to correct state */ drm_crtc_vblank_reset(&crtc->base); if (crtc->active) { + struct intel_plane *plane; + drm_calc_timestamping_constants(&crtc->base, &crtc->base.hwmode); update_scanline_offset(crtc); drm_crtc_vblank_on(&crtc->base); + + /* Disable everything but the primary plane */ + for_each_intel_plane_on_crtc(dev, crtc, plane) { + if (plane->base.type == DRM_PLANE_TYPE_PRIMARY) + continue; + + plane->disable_plane(&plane->base, &crtc->base); + } } /* We need to sanitize the plane -> pipe mapping first because this will @@ -15067,38 +15091,25 @@ void i915_redisable_vga(struct drm_device *dev) i915_redisable_vga_power_on(dev); } -static bool primary_get_hw_state(struct intel_crtc *crtc) +static bool primary_get_hw_state(struct intel_plane *plane) { - struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); - return !!(I915_READ(DSPCNTR(crtc->plane)) & DISPLAY_PLANE_ENABLE); + return I915_READ(DSPCNTR(plane->plane)) & DISPLAY_PLANE_ENABLE; } -static void readout_plane_state(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state) +/* FIXME read out full plane state for all planes */ +static void readout_plane_state(struct intel_crtc *crtc) { - struct intel_plane *p; - struct intel_plane_state *plane_state; - bool active = crtc_state->base.active; - - for_each_intel_plane(crtc->base.dev, p) { - if (crtc->pipe != p->pipe) - continue; - - plane_state = to_intel_plane_state(p->base.state); + struct drm_plane *primary = crtc->base.primary; + struct intel_plane_state *plane_state = + to_intel_plane_state(primary->state); - if (p->base.type == DRM_PLANE_TYPE_PRIMARY) { - plane_state->visible = primary_get_hw_state(crtc); - if (plane_state->visible) - crtc->base.state->plane_mask |= - 1 << drm_plane_index(&p->base); - } else { - if (active) - p->disable_plane(&p->base, &crtc->base); + plane_state->visible = + primary_get_hw_state(to_intel_plane(primary)); - plane_state->visible = false; - } - } + if (plane_state->visible) + crtc->base.state->plane_mask |= 1 << drm_plane_index(primary); } static void intel_modeset_readout_hw_state(struct drm_device *dev) @@ -15121,34 +15132,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) crtc->base.state->active = crtc->active; crtc->base.enabled = crtc->active; - memset(&crtc->base.mode, 0, sizeof(crtc->base.mode)); - if (crtc->base.state->active) { - intel_mode_from_pipe_config(&crtc->base.mode, crtc->config); - intel_mode_from_pipe_config(&crtc->base.state->adjusted_mode, crtc->config); - WARN_ON(drm_atomic_set_mode_for_crtc(crtc->base.state, &crtc->base.mode)); - - /* - * The initial mode needs to be set in order to keep - * the atomic core happy. It wants a valid mode if the - * crtc's enabled, so we do the above call. - * - * At this point some state updated by the connectors - * in their ->detect() callback has not run yet, so - * no recalculation can be done yet. - * - * Even if we could do a recalculation and modeset - * right now it would cause a double modeset if - * fbdev or userspace chooses a different initial mode. - * - * If that happens, someone indicated they wanted a - * mode change, which means it's safe to do a full - * recalculation. - */ - crtc->base.state->mode.private_flags = I915_MODE_FLAG_INHERITED; - } - - crtc->base.hwmode = crtc->config->base.adjusted_mode; - readout_plane_state(crtc, to_intel_crtc_state(crtc->base.state)); + readout_plane_state(crtc); DRM_DEBUG_KMS("[CRTC:%d] hw state readout: %s\n", crtc->base.base.id, @@ -15207,6 +15191,36 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) connector->base.name, connector->base.encoder ? "enabled" : "disabled"); } + + for_each_intel_crtc(dev, crtc) { + crtc->base.hwmode = crtc->config->base.adjusted_mode; + + memset(&crtc->base.mode, 0, sizeof(crtc->base.mode)); + if (crtc->base.state->active) { + intel_mode_from_pipe_config(&crtc->base.mode, crtc->config); + intel_mode_from_pipe_config(&crtc->base.state->adjusted_mode, crtc->config); + WARN_ON(drm_atomic_set_mode_for_crtc(crtc->base.state, &crtc->base.mode)); + + /* + * The initial mode needs to be set in order to keep + * the atomic core happy. It wants a valid mode if the + * crtc's enabled, so we do the above call. + * + * At this point some state updated by the connectors + * in their ->detect() callback has not run yet, so + * no recalculation can be done yet. + * + * Even if we could do a recalculation and modeset + * right now it would cause a double modeset if + * fbdev or userspace chooses a different initial mode. + * + * If that happens, someone indicated they wanted a + * mode change, which means it's safe to do a full + * recalculation. + */ + crtc->base.state->mode.private_flags = I915_MODE_FLAG_INHERITED; + } + } } /* Scan out the current hw modeset state, diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 7412cae..29dd448 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1659,6 +1659,7 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request, if (flush_domains) { flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; + flags |= PIPE_CONTROL_FLUSH_ENABLE; } if (invalidate_domains) { diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 6e6b8db..61b451f 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -347,6 +347,7 @@ gen7_render_ring_flush(struct drm_i915_gem_request *req, if (flush_domains) { flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; + flags |= PIPE_CONTROL_FLUSH_ENABLE; } if (invalidate_domains) { flags |= PIPE_CONTROL_TLB_INVALIDATE; @@ -418,6 +419,7 @@ gen8_render_ring_flush(struct drm_i915_gem_request *req, if (flush_domains) { flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; + flags |= PIPE_CONTROL_FLUSH_ENABLE; } if (invalidate_domains) { flags |= PIPE_CONTROL_TLB_INVALIDATE; diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 2c99815..41be584 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -227,11 +227,12 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem, struct nouveau_bo *nvbo = nouveau_gem_object(gem); struct nvkm_vma *vma; - if (nvbo->bo.mem.mem_type == TTM_PL_TT) + if (is_power_of_2(nvbo->valid_domains)) + rep->domain = nvbo->valid_domains; + else if (nvbo->bo.mem.mem_type == TTM_PL_TT) rep->domain = NOUVEAU_GEM_DOMAIN_GART; else rep->domain = NOUVEAU_GEM_DOMAIN_VRAM; - rep->offset = nvbo->bo.offset; if (cli->vm) { vma = nouveau_bo_vma_find(nvbo, cli->vm); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 44489cc..6a0a176 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -717,10 +717,14 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, struct radeon_device *rdev = dev_get_drvdata(dev); umode_t effective_mode = attr->mode; - /* Skip limit attributes if DPM is not enabled */ + /* Skip attributes if DPM is not enabled */ if (rdev->pm.pm_method != PM_METHOD_DPM && (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr || - attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)) + attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr || + attr == &sensor_dev_attr_pwm1.dev_attr.attr || + attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr || + attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || + attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) return 0; /* Skip fan attributes if fan is not present */ diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index ff30f88..fb93111 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -149,8 +149,6 @@ #define ST_ACCEL_4_BDU_MASK 0x40 #define ST_ACCEL_4_DRDY_IRQ_ADDR 0x21 #define ST_ACCEL_4_DRDY_IRQ_INT1_MASK 0x04 -#define ST_ACCEL_4_IG1_EN_ADDR 0x21 -#define ST_ACCEL_4_IG1_EN_MASK 0x08 #define ST_ACCEL_4_MULTIREAD_BIT true /* CUSTOM VALUES FOR SENSOR 5 */ @@ -489,10 +487,6 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .drdy_irq = { .addr = ST_ACCEL_4_DRDY_IRQ_ADDR, .mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK, - .ig1 = { - .en_addr = ST_ACCEL_4_IG1_EN_ADDR, - .en_mask = ST_ACCEL_4_IG1_EN_MASK, - }, }, .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT, .bootime = 2, /* guess */ diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index ebe415f..0c74869 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -45,13 +45,18 @@ #include <linux/types.h> #include <linux/gfp.h> #include <linux/err.h> +#include <linux/regulator/consumer.h> #include <linux/iio/iio.h> +#define TWL4030_USB_SEL_MADC_MCPC (1<<3) +#define TWL4030_USB_CARKIT_ANA_CTRL 0xBB + /** * struct twl4030_madc_data - a container for madc info * @dev: Pointer to device structure for madc * @lock: Mutex protecting this data structure + * @regulator: Pointer to bias regulator for madc * @requests: Array of request struct corresponding to SW1, SW2 and RT * @use_second_irq: IRQ selection (main or co-processor) * @imr: Interrupt mask register of MADC @@ -60,6 +65,7 @@ struct twl4030_madc_data { struct device *dev; struct mutex lock; /* mutex protecting this data structure */ + struct regulator *usb3v1; struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; bool use_second_irq; u8 imr; @@ -841,6 +847,32 @@ static int twl4030_madc_probe(struct platform_device *pdev) } twl4030_madc = madc; + /* Configure MADC[3:6] */ + ret = twl_i2c_read_u8(TWL_MODULE_USB, ®val, + TWL4030_USB_CARKIT_ANA_CTRL); + if (ret) { + dev_err(&pdev->dev, "unable to read reg CARKIT_ANA_CTRL 0x%X\n", + TWL4030_USB_CARKIT_ANA_CTRL); + goto err_i2c; + } + regval |= TWL4030_USB_SEL_MADC_MCPC; + ret = twl_i2c_write_u8(TWL_MODULE_USB, regval, + TWL4030_USB_CARKIT_ANA_CTRL); + if (ret) { + dev_err(&pdev->dev, "unable to write reg CARKIT_ANA_CTRL 0x%X\n", + TWL4030_USB_CARKIT_ANA_CTRL); + goto err_i2c; + } + + /* Enable 3v1 bias regulator for MADC[3:6] */ + madc->usb3v1 = devm_regulator_get(madc->dev, "vusb3v1"); + if (IS_ERR(madc->usb3v1)) + return -ENODEV; + + ret = regulator_enable(madc->usb3v1); + if (ret) + dev_err(madc->dev, "could not enable 3v1 bias regulator\n"); + ret = iio_device_register(iio_dev); if (ret) { dev_err(&pdev->dev, "could not register iio device\n"); @@ -866,6 +898,8 @@ static int twl4030_madc_remove(struct platform_device *pdev) twl4030_madc_set_current_generator(madc, 0, 0); twl4030_madc_set_power(madc, 0); + regulator_disable(madc->usb3v1); + return 0; } diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index 8f66c67..87471ef 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -508,12 +508,12 @@ void ib_cache_gid_set_default_gid(struct ib_device *ib_dev, u8 port, memset(&gid_attr, 0, sizeof(gid_attr)); gid_attr.ndev = ndev; + mutex_lock(&table->lock); ix = find_gid(table, NULL, NULL, true, GID_ATTR_FIND_MASK_DEFAULT); /* Coudn't find default GID location */ WARN_ON(ix < 0); - mutex_lock(&table->lock); if (!__ib_cache_gid_get(ib_dev, port, ix, ¤t_gid, ¤t_gid_attr) && mode == IB_CACHE_GID_DEFAULT_MODE_SET && diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index ea4db9c..4f918b9 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -835,6 +835,11 @@ retest: case IB_CM_SIDR_REQ_RCVD: spin_unlock_irq(&cm_id_priv->lock); cm_reject_sidr_req(cm_id_priv, IB_SIDR_REJECT); + spin_lock_irq(&cm.lock); + if (!RB_EMPTY_NODE(&cm_id_priv->sidr_id_node)) + rb_erase(&cm_id_priv->sidr_id_node, + &cm.remote_sidr_table); + spin_unlock_irq(&cm.lock); break; case IB_CM_REQ_SENT: case IB_CM_MRA_REQ_RCVD: @@ -3172,7 +3177,10 @@ int ib_send_cm_sidr_rep(struct ib_cm_id *cm_id, spin_unlock_irqrestore(&cm_id_priv->lock, flags); spin_lock_irqsave(&cm.lock, flags); - rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table); + if (!RB_EMPTY_NODE(&cm_id_priv->sidr_id_node)) { + rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table); + RB_CLEAR_NODE(&cm_id_priv->sidr_id_node); + } spin_unlock_irqrestore(&cm.lock, flags); return 0; diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 59a2daf..36b12d5 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -1067,14 +1067,14 @@ static int cma_save_req_info(const struct ib_cm_event *ib_event, sizeof(req->local_gid)); req->has_gid = true; req->service_id = req_param->primary_path->service_id; - req->pkey = req_param->bth_pkey; + req->pkey = be16_to_cpu(req_param->primary_path->pkey); break; case IB_CM_SIDR_REQ_RECEIVED: req->device = sidr_param->listen_id->device; req->port = sidr_param->port; req->has_gid = false; req->service_id = sidr_param->service_id; - req->pkey = sidr_param->bth_pkey; + req->pkey = sidr_param->pkey; break; default: return -EINVAL; @@ -1324,7 +1324,7 @@ static struct rdma_id_private *cma_id_from_event(struct ib_cm_id *cm_id, bind_list = cma_ps_find(rdma_ps_from_service_id(req.service_id), cma_port_from_service_id(req.service_id)); id_priv = cma_find_listener(bind_list, cm_id, ib_event, &req, *net_dev); - if (IS_ERR(id_priv)) { + if (IS_ERR(id_priv) && *net_dev) { dev_put(*net_dev); *net_dev = NULL; } diff --git a/drivers/infiniband/core/roce_gid_mgmt.c b/drivers/infiniband/core/roce_gid_mgmt.c index 6b24cba..178f984 100644 --- a/drivers/infiniband/core/roce_gid_mgmt.c +++ b/drivers/infiniband/core/roce_gid_mgmt.c @@ -250,25 +250,44 @@ static void enum_netdev_ipv4_ips(struct ib_device *ib_dev, u8 port, struct net_device *ndev) { struct in_device *in_dev; + struct sin_list { + struct list_head list; + struct sockaddr_in ip; + }; + struct sin_list *sin_iter; + struct sin_list *sin_temp; + LIST_HEAD(sin_list); if (ndev->reg_state >= NETREG_UNREGISTERING) return; - in_dev = in_dev_get(ndev); - if (!in_dev) + rcu_read_lock(); + in_dev = __in_dev_get_rcu(ndev); + if (!in_dev) { + rcu_read_unlock(); return; + } for_ifa(in_dev) { - struct sockaddr_in ip; + struct sin_list *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); - ip.sin_family = AF_INET; - ip.sin_addr.s_addr = ifa->ifa_address; - update_gid_ip(GID_ADD, ib_dev, port, ndev, - (struct sockaddr *)&ip); + if (!entry) { + pr_warn("roce_gid_mgmt: couldn't allocate entry for IPv4 update\n"); + continue; + } + entry->ip.sin_family = AF_INET; + entry->ip.sin_addr.s_addr = ifa->ifa_address; + list_add_tail(&entry->list, &sin_list); } endfor_ifa(in_dev); + rcu_read_unlock(); - in_dev_put(in_dev); + list_for_each_entry_safe(sin_iter, sin_temp, &sin_list, list) { + update_gid_ip(GID_ADD, ib_dev, port, ndev, + (struct sockaddr *)&sin_iter->ip); + list_del(&sin_iter->list); + kfree(sin_iter); + } } static void enum_netdev_ipv6_ips(struct ib_device *ib_dev, diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index a53fc9b..30467d1 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -1624,11 +1624,16 @@ static int ucma_open(struct inode *inode, struct file *filp) if (!file) return -ENOMEM; + file->close_wq = create_singlethread_workqueue("ucma_close_id"); + if (!file->close_wq) { + kfree(file); + return -ENOMEM; + } + INIT_LIST_HEAD(&file->event_list); INIT_LIST_HEAD(&file->ctx_list); init_waitqueue_head(&file->poll_wait); mutex_init(&file->mut); - file->close_wq = create_singlethread_workqueue("ucma_close_id"); filp->private_data = file; file->filp = filp; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 35365f0..d65cf42 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -2115,15 +2115,19 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, return -ENOMEM; /* It is large page*/ if (largepage_lvl > 1) { + unsigned long nr_superpages, end_pfn; + pteval |= DMA_PTE_LARGE_PAGE; lvl_pages = lvl_to_nr_pages(largepage_lvl); + + nr_superpages = sg_res / lvl_pages; + end_pfn = iov_pfn + nr_superpages * lvl_pages - 1; + /* * Ensure that old small page tables are - * removed to make room for superpage, - * if they exist. + * removed to make room for superpage(s). */ - dma_pte_free_pagetable(domain, iov_pfn, - iov_pfn + lvl_pages - 1); + dma_pte_free_pagetable(domain, iov_pfn, end_pfn); } else { pteval &= ~(uint64_t)DMA_PTE_LARGE_PAGE; } diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index 20cc36b..0a17d1b 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c @@ -634,10 +634,10 @@ static int __commit_transaction(struct dm_cache_metadata *cmd, disk_super = dm_block_data(sblock); + disk_super->flags = cpu_to_le32(cmd->flags); if (mutator) update_flags(disk_super, mutator); - disk_super->flags = cpu_to_le32(cmd->flags); disk_super->mapping_root = cpu_to_le64(cmd->root); disk_super->hint_root = cpu_to_le64(cmd->hint_root); disk_super->discard_root = cpu_to_le64(cmd->discard_root); diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c index 421a36c..2e4c4cb 100644 --- a/drivers/md/persistent-data/dm-btree-remove.c +++ b/drivers/md/persistent-data/dm-btree-remove.c @@ -301,11 +301,16 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent, { int s; uint32_t max_entries = le32_to_cpu(left->header.max_entries); - unsigned target = (nr_left + nr_center + nr_right) / 3; - BUG_ON(target > max_entries); + unsigned total = nr_left + nr_center + nr_right; + unsigned target_right = total / 3; + unsigned remainder = (target_right * 3) != total; + unsigned target_left = target_right + remainder; + + BUG_ON(target_left > max_entries); + BUG_ON(target_right > max_entries); if (nr_left < nr_right) { - s = nr_left - target; + s = nr_left - target_left; if (s < 0 && nr_center < -s) { /* not enough in central node */ @@ -316,10 +321,10 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent, } else shift(left, center, s); - shift(center, right, target - nr_right); + shift(center, right, target_right - nr_right); } else { - s = target - nr_right; + s = target_right - nr_right; if (s > 0 && nr_center < s) { /* not enough in central node */ shift(center, right, nr_center); @@ -329,7 +334,7 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent, } else shift(center, right, s); - shift(left, center, nr_left - target); + shift(left, center, nr_left - target_left); } *key_ptr(parent, c->index) = center->keys[0]; diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c index b6cec25..0e09aef 100644 --- a/drivers/md/persistent-data/dm-btree.c +++ b/drivers/md/persistent-data/dm-btree.c @@ -523,7 +523,7 @@ static int btree_split_beneath(struct shadow_spine *s, uint64_t key) r = new_block(s->info, &right); if (r < 0) { - /* FIXME: put left */ + unlock_block(s->info, left); return r; } diff --git a/drivers/media/dvb-frontends/horus3a.h b/drivers/media/dvb-frontends/horus3a.h index b055319..c1e2d18 100644 --- a/drivers/media/dvb-frontends/horus3a.h +++ b/drivers/media/dvb-frontends/horus3a.h @@ -46,8 +46,8 @@ extern struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, const struct horus3a_config *config, struct i2c_adapter *i2c); #else -static inline struct dvb_frontend *horus3a_attach( - const struct cxd2820r_config *config, +static inline struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, + const struct horus3a_config *config, struct i2c_adapter *i2c) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); diff --git a/drivers/media/dvb-frontends/lnbh25.h b/drivers/media/dvb-frontends/lnbh25.h index 69f30e2..1f329ef 100644 --- a/drivers/media/dvb-frontends/lnbh25.h +++ b/drivers/media/dvb-frontends/lnbh25.h @@ -43,7 +43,7 @@ struct dvb_frontend *lnbh25_attach( struct lnbh25_config *cfg, struct i2c_adapter *i2c); #else -static inline dvb_frontend *lnbh25_attach( +static inline struct dvb_frontend *lnbh25_attach( struct dvb_frontend *fe, struct lnbh25_config *cfg, struct i2c_adapter *i2c) diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index ff31e7a..feeeb70 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -18,6 +18,27 @@ static struct dvb_frontend_ops m88ds3103_ops; +/* write single register with mask */ +static int m88ds3103_update_bits(struct m88ds3103_dev *dev, + u8 reg, u8 mask, u8 val) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = regmap_bulk_read(dev->regmap, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return regmap_bulk_write(dev->regmap, reg, &val, 1); +} + /* write reg val table using reg addr auto increment */ static int m88ds3103_wr_reg_val_tab(struct m88ds3103_dev *dev, const struct m88ds3103_reg_val *tab, int tab_len) @@ -394,10 +415,10 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) u8tmp2 = 0x00; /* 0b00 */ break; } - ret = regmap_update_bits(dev->regmap, 0x22, 0xc0, u8tmp1 << 6); + ret = m88ds3103_update_bits(dev, 0x22, 0xc0, u8tmp1 << 6); if (ret) goto err; - ret = regmap_update_bits(dev->regmap, 0x24, 0xc0, u8tmp2 << 6); + ret = m88ds3103_update_bits(dev, 0x24, 0xc0, u8tmp2 << 6); if (ret) goto err; } @@ -455,13 +476,13 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (ret) goto err; } - ret = regmap_update_bits(dev->regmap, 0x9d, 0x08, 0x08); + ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08); if (ret) goto err; ret = regmap_write(dev->regmap, 0xf1, 0x01); if (ret) goto err; - ret = regmap_update_bits(dev->regmap, 0x30, 0x80, 0x80); + ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80); if (ret) goto err; } @@ -498,7 +519,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) switch (dev->cfg->ts_mode) { case M88DS3103_TS_SERIAL: case M88DS3103_TS_SERIAL_D7: - ret = regmap_update_bits(dev->regmap, 0x29, 0x20, u8tmp1); + ret = m88ds3103_update_bits(dev, 0x29, 0x20, u8tmp1); if (ret) goto err; u8tmp1 = 0; @@ -567,11 +588,11 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - ret = regmap_update_bits(dev->regmap, 0x4d, 0x02, dev->cfg->spec_inv << 1); + ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1); if (ret) goto err; - ret = regmap_update_bits(dev->regmap, 0x30, 0x10, dev->cfg->agc_inv << 4); + ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4); if (ret) goto err; @@ -625,13 +646,13 @@ static int m88ds3103_init(struct dvb_frontend *fe) dev->warm = false; /* wake up device from sleep */ - ret = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x01); + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01); if (ret) goto err; - ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x00); + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00); if (ret) goto err; - ret = regmap_update_bits(dev->regmap, 0x23, 0x10, 0x00); + ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00); if (ret) goto err; @@ -749,18 +770,18 @@ static int m88ds3103_sleep(struct dvb_frontend *fe) utmp = 0x29; else utmp = 0x27; - ret = regmap_update_bits(dev->regmap, utmp, 0x01, 0x00); + ret = m88ds3103_update_bits(dev, utmp, 0x01, 0x00); if (ret) goto err; /* sleep */ - ret = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x00); + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00); if (ret) goto err; - ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x01); + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01); if (ret) goto err; - ret = regmap_update_bits(dev->regmap, 0x23, 0x10, 0x10); + ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10); if (ret) goto err; @@ -992,12 +1013,12 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe, } utmp = tone << 7 | dev->cfg->envelope_mode << 5; - ret = regmap_update_bits(dev->regmap, 0xa2, 0xe0, utmp); + ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); if (ret) goto err; utmp = 1 << 2; - ret = regmap_update_bits(dev->regmap, 0xa1, reg_a1_mask, utmp); + ret = m88ds3103_update_bits(dev, 0xa1, reg_a1_mask, utmp); if (ret) goto err; @@ -1047,7 +1068,7 @@ static int m88ds3103_set_voltage(struct dvb_frontend *fe, voltage_dis ^= dev->cfg->lnb_en_pol; utmp = voltage_dis << 1 | voltage_sel << 0; - ret = regmap_update_bits(dev->regmap, 0xa2, 0x03, utmp); + ret = m88ds3103_update_bits(dev, 0xa2, 0x03, utmp); if (ret) goto err; @@ -1080,7 +1101,7 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, } utmp = dev->cfg->envelope_mode << 5; - ret = regmap_update_bits(dev->regmap, 0xa2, 0xe0, utmp); + ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); if (ret) goto err; @@ -1115,12 +1136,12 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, } else { dev_dbg(&client->dev, "diseqc tx timeout\n"); - ret = regmap_update_bits(dev->regmap, 0xa1, 0xc0, 0x40); + ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40); if (ret) goto err; } - ret = regmap_update_bits(dev->regmap, 0xa2, 0xc0, 0x80); + ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80); if (ret) goto err; @@ -1152,7 +1173,7 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, } utmp = dev->cfg->envelope_mode << 5; - ret = regmap_update_bits(dev->regmap, 0xa2, 0xe0, utmp); + ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); if (ret) goto err; @@ -1194,12 +1215,12 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, } else { dev_dbg(&client->dev, "diseqc tx timeout\n"); - ret = regmap_update_bits(dev->regmap, 0xa1, 0xc0, 0x40); + ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40); if (ret) goto err; } - ret = regmap_update_bits(dev->regmap, 0xa2, 0xc0, 0x80); + ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80); if (ret) goto err; @@ -1435,13 +1456,13 @@ static int m88ds3103_probe(struct i2c_client *client, goto err_kfree; /* sleep */ - ret = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x00); + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00); if (ret) goto err_kfree; - ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x01); + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01); if (ret) goto err_kfree; - ret = regmap_update_bits(dev->regmap, 0x23, 0x10, 0x10); + ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10); if (ret) goto err_kfree; diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 81788c5..821a8f4 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -502,6 +502,10 @@ static int si2168_init(struct dvb_frontend *fe) /* firmware is in the new format */ for (remaining = fw->size; remaining > 0; remaining -= 17) { len = fw->data[fw->size - remaining]; + if (len > SI2168_ARGLEN) { + ret = -EINVAL; + break; + } memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); cmd.wlen = len; cmd.rlen = 1; diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c b/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c index f55b327..56773f3 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c @@ -80,11 +80,9 @@ irqreturn_t netup_spi_interrupt(struct netup_spi *spi) u16 reg; unsigned long flags; - if (!spi) { - dev_dbg(&spi->master->dev, - "%s(): SPI not initialized\n", __func__); + if (!spi) return IRQ_NONE; - } + spin_lock_irqsave(&spi->lock, flags); reg = readw(&spi->regs->control_stat); if (!(reg & NETUP_SPI_CTRL_IRQ)) { @@ -234,11 +232,9 @@ void netup_spi_release(struct netup_unidvb_dev *ndev) unsigned long flags; struct netup_spi *spi = ndev->spi; - if (!spi) { - dev_dbg(&spi->master->dev, - "%s(): SPI not initialized\n", __func__); + if (!spi) return; - } + spin_lock_irqsave(&spi->lock, flags); reg = readw(&spi->regs->control_stat); writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat); diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 486aef5..f922f2e 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -1097,7 +1097,7 @@ static int load_slim_core_fw(const struct firmware *fw, void *context) Elf32_Ehdr *ehdr; Elf32_Phdr *phdr; u8 __iomem *dst; - int err, i; + int err = 0, i; if (!fw || !context) return -EINVAL; @@ -1106,7 +1106,7 @@ static int load_slim_core_fw(const struct firmware *fw, void *context) phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff); /* go through the available ELF segments */ - for (i = 0; i < ehdr->e_phnum && !err; i++, phdr++) { + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { /* Only consider LOAD segments */ if (phdr->p_type != PT_LOAD) @@ -1192,7 +1192,6 @@ err: static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei) { - int ret; int err; dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA); @@ -1207,7 +1206,7 @@ static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei) if (err) { dev_err(fei->dev, "request_firmware_nowait err: %d.\n", err); complete_all(&fei->fw_ack); - return ret; + return err; } return 0; diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c index 1c087cb..d0549fb 100644 --- a/drivers/media/rc/ir-hix5hd2.c +++ b/drivers/media/rc/ir-hix5hd2.c @@ -257,7 +257,7 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) goto clkerr; if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt, - IRQF_NO_SUSPEND, pdev->name, priv) < 0) { + 0, pdev->name, priv) < 0) { dev_err(dev, "IRQ %d register failed\n", priv->irq); ret = -EINVAL; goto regerr; diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 5073821..ce157ed 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -166,6 +166,10 @@ static int si2157_init(struct dvb_frontend *fe) for (remaining = fw->size; remaining > 0; remaining -= 17) { len = fw->data[fw->size - remaining]; + if (len > SI2157_ARGLEN) { + dev_err(&client->dev, "Bad firmware length\n"); + goto err_release_firmware; + } memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); cmd.wlen = len; cmd.rlen = 1; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index c3cac4c..197a4f2 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -34,6 +34,14 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) unsigned int pipe; u8 requesttype; + mutex_lock(&d->usb_mutex); + + if (req->size > sizeof(dev->buf)) { + dev_err(&d->intf->dev, "too large message %u\n", req->size); + ret = -EINVAL; + goto err_mutex_unlock; + } + if (req->index & CMD_WR_FLAG) { /* write */ memcpy(dev->buf, req->data, req->size); @@ -50,14 +58,17 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) dvb_usb_dbg_usb_control_msg(d->udev, 0, requesttype, req->value, req->index, dev->buf, req->size); if (ret < 0) - goto err; + goto err_mutex_unlock; /* read request, copy returned data to return buf */ if (requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) memcpy(req->data, dev->buf, req->size); + mutex_unlock(&d->usb_mutex); + return 0; -err: +err_mutex_unlock: + mutex_unlock(&d->usb_mutex); dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h index 9f6115a..1380629 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h @@ -71,7 +71,7 @@ struct rtl28xxu_dev { - u8 buf[28]; + u8 buf[128]; u8 chip_id; u8 tuner; char *tuner_name; diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 82876a6..9beece0 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -47,7 +47,7 @@ config V4L2_MEM2MEM_DEV # Used by LED subsystem flash drivers config V4L2_FLASH_LED_CLASS tristate "V4L2 flash API for LED flash class devices" - depends on VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on LEDS_CLASS_FLASH ---help--- Say Y here to enable V4L2 flash API support for LED flash diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index c6a644b..6f31546 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -58,12 +58,18 @@ config OMAP_GPMC memory drives like NOR, NAND, OneNAND, SRAM. config OMAP_GPMC_DEBUG - bool + bool "Enable GPMC debug output and skip reset of GPMC during init" depends on OMAP_GPMC help Enables verbose debugging mostly to decode the bootloader provided - timings. Enable this during development to configure devices - connected to the GPMC bus. + timings. To preserve the bootloader provided timings, the reset + of GPMC is skipped during init. Enable this during development to + configure devices connected to the GPMC bus. + + NOTE: In addition to matching the register setup with the bootloader + you also need to match the GPMC FCLK frequency used by the + bootloader or else the GPMC timings won't be identical with the + bootloader timings. config MVEBU_DEVBUS bool "Marvell EBU Device Bus Controller" diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 32ac049..6515dfc 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -696,7 +696,6 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t, int div; u32 l; - gpmc_cs_show_timings(cs, "before gpmc_cs_set_timings"); div = gpmc_calc_divider(t->sync_clk); if (div < 0) return div; @@ -1988,6 +1987,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, if (ret < 0) goto err; + gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings"); ret = gpmc_cs_program_settings(cs, &gpmc_s); if (ret < 0) goto err; diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index b78cf5d..7fc9174 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -2263,15 +2263,12 @@ static int mmc_test_profile_sglen_r_nonblock_perf(struct mmc_test_card *test) /* * eMMC hardware reset. */ -static int mmc_test_hw_reset(struct mmc_test_card *test) +static int mmc_test_reset(struct mmc_test_card *test) { struct mmc_card *card = test->card; struct mmc_host *host = card->host; int err; - if (!mmc_card_mmc(card) || !mmc_can_reset(card)) - return RESULT_UNSUP_CARD; - err = mmc_hw_reset(host); if (!err) return RESULT_OK; @@ -2605,8 +2602,8 @@ static const struct mmc_test_case mmc_test_cases[] = { }, { - .name = "eMMC hardware reset", - .run = mmc_test_hw_reset, + .name = "Reset test", + .run = mmc_test_reset, }, }; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index e726903..f6cd995 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1924,7 +1924,6 @@ EXPORT_SYMBOL(mmc_can_reset); static int mmc_reset(struct mmc_host *host) { struct mmc_card *card = host->card; - u32 status; if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) return -EOPNOTSUPP; @@ -1937,12 +1936,6 @@ static int mmc_reset(struct mmc_host *host) host->ops->hw_reset(host); - /* If the reset has happened, then a status command will fail */ - if (!mmc_send_status(card, &status)) { - mmc_host_clk_release(host); - return -ENOSYS; - } - /* Set initial state and call mmc_set_ios */ mmc_set_initial_state(host); mmc_host_clk_release(host); diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c index e5fac36..131026f 100644 --- a/drivers/net/can/sja1000/peak_pci.c +++ b/drivers/net/can/sja1000/peak_pci.c @@ -87,6 +87,7 @@ static const struct pci_device_id peak_pci_tbl[] = { {PEAK_PCI_VENDOR_ID, PEAK_PC_104P_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, {PEAK_PCI_VENDOR_ID, PEAK_PCI_104E_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, {PEAK_PCI_VENDOR_ID, PEAK_CPCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_PCIE_OEM_ID, PCI_ANY_ID, PCI_ANY_ID,}, #ifdef CONFIG_CAN_PEAK_PCIEC {PEAK_PCI_VENDOR_ID, PEAK_PCIEC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, {PEAK_PCI_VENDOR_ID, PEAK_PCIEC34_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c index 2c063b6..96f485a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c @@ -327,9 +327,13 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata) pdata->debugfs_xpcs_reg = 0; buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name); + if (!buf) + return; + pdata->xgbe_debugfs = debugfs_create_dir(buf, NULL); if (!pdata->xgbe_debugfs) { netdev_err(pdata->netdev, "debugfs_create_dir failed\n"); + kfree(buf); return; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index aeb7ce6..be628bd 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -3351,6 +3351,13 @@ static int bnx2x_set_rss_flags(struct bnx2x *bp, struct ethtool_rxnfc *info) udp_rss_requested = 0; else return -EINVAL; + + if (CHIP_IS_E1x(bp) && udp_rss_requested) { + DP(BNX2X_MSG_ETHTOOL, + "57710, 57711 boards don't support RSS according to UDP 4-tuple\n"); + return -EINVAL; + } + if ((info->flow_type == UDP_V4_FLOW) && (bp->rss_conf_obj.udp_rss_v4 != udp_rss_requested)) { bp->rss_conf_obj.udp_rss_v4 = udp_rss_requested; diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 3bc701e..1805541 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1683,6 +1683,24 @@ static void bcmgenet_intr_disable(struct bcmgenet_priv *priv) bcmgenet_intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); } +static void bcmgenet_link_intr_enable(struct bcmgenet_priv *priv) +{ + u32 int0_enable = 0; + + /* Monitor cable plug/unplugged event for internal PHY, external PHY + * and MoCA PHY + */ + if (priv->internal_phy) { + int0_enable |= UMAC_IRQ_LINK_EVENT; + } else if (priv->ext_phy) { + int0_enable |= UMAC_IRQ_LINK_EVENT; + } else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { + if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET) + int0_enable |= UMAC_IRQ_LINK_EVENT; + } + bcmgenet_intrl2_0_writel(priv, int0_enable, INTRL2_CPU_MASK_CLEAR); +} + static int init_umac(struct bcmgenet_priv *priv) { struct device *kdev = &priv->pdev->dev; @@ -1723,15 +1741,8 @@ static int init_umac(struct bcmgenet_priv *priv) /* Enable Tx default queue 16 interrupts */ int0_enable |= UMAC_IRQ_TXDMA_DONE; - /* Monitor cable plug/unplugged event for internal PHY */ - if (priv->internal_phy) { - int0_enable |= UMAC_IRQ_LINK_EVENT; - } else if (priv->ext_phy) { - int0_enable |= UMAC_IRQ_LINK_EVENT; - } else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { - if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET) - int0_enable |= UMAC_IRQ_LINK_EVENT; - + /* Configure backpressure vectors for MoCA */ + if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { reg = bcmgenet_bp_mc_get(priv); reg |= BIT(priv->hw_params->bp_in_en_shift); @@ -2645,6 +2656,9 @@ static void bcmgenet_netif_start(struct net_device *dev) netif_tx_start_all_queues(dev); + /* Monitor link interrupts now */ + bcmgenet_link_intr_enable(priv); + phy_start(priv->phydev); } diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 8215409..d463563 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -592,6 +592,7 @@ struct be_adapter { int be_get_temp_freq; struct be_hwmon hwmon_info; u8 pf_number; + u8 pci_func_num; struct rss_info rss_info; /* Filters for packets that need to be sent to BMC */ u32 bmc_filt_mask; diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index eb32391..1795c93 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -851,8 +851,10 @@ static int be_cmd_notify_wait(struct be_adapter *adapter, return status; dest_wrb = be_cmd_copy(adapter, wrb); - if (!dest_wrb) - return -EBUSY; + if (!dest_wrb) { + status = -EBUSY; + goto unlock; + } if (use_mcc(adapter)) status = be_mcc_notify_wait(adapter); @@ -862,6 +864,7 @@ static int be_cmd_notify_wait(struct be_adapter *adapter, if (!status) memcpy(wrb, dest_wrb, sizeof(*wrb)); +unlock: be_cmd_unlock(adapter); return status; } @@ -1984,6 +1987,8 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value) be_if_cap_flags(adapter)); } flags &= be_if_cap_flags(adapter); + if (!flags) + return -ENOTSUPP; return __be_cmd_rx_filter(adapter, flags, value); } @@ -2887,6 +2892,7 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter) if (!status) { attribs = attribs_cmd.va + sizeof(struct be_cmd_resp_hdr); adapter->hba_port_num = attribs->hba_attribs.phy_port; + adapter->pci_func_num = attribs->pci_func_num; serial_num = attribs->hba_attribs.controller_serial_number; for (i = 0; i < CNTL_SERIAL_NUM_WORDS; i++) adapter->serial_num[i] = le32_to_cpu(serial_num[i]) & @@ -3709,7 +3715,6 @@ int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res) status = -EINVAL; goto err; } - adapter->pf_number = desc->pf_num; be_copy_nic_desc(res, desc); } @@ -3721,7 +3726,10 @@ err: return status; } -/* Will use MBOX only if MCCQ has not been created */ +/* Will use MBOX only if MCCQ has not been created + * non-zero domain => a PF is querying this on behalf of a VF + * zero domain => a PF or a VF is querying this for itself + */ int be_cmd_get_profile_config(struct be_adapter *adapter, struct be_resources *res, u8 query, u8 domain) { @@ -3748,10 +3756,15 @@ int be_cmd_get_profile_config(struct be_adapter *adapter, OPCODE_COMMON_GET_PROFILE_CONFIG, cmd.size, &wrb, &cmd); - req->hdr.domain = domain; if (!lancer_chip(adapter)) req->hdr.version = 1; req->type = ACTIVE_PROFILE_TYPE; + /* When a function is querying profile information relating to + * itself hdr.pf_number must be set to it's pci_func_num + 1 + */ + req->hdr.domain = domain; + if (domain == 0) + req->hdr.pf_num = adapter->pci_func_num + 1; /* When QUERY_MODIFIABLE_FIELDS_TYPE bit is set, cmd returns the * descriptors with all bits set to "1" for the fields which can be @@ -3921,12 +3934,16 @@ static void be_fill_vf_res_template(struct be_adapter *adapter, vf_if_cap_flags &= ~(BE_IF_FLAGS_RSS | BE_IF_FLAGS_DEFQ_RSS); } - - nic_vft->cap_flags = cpu_to_le32(vf_if_cap_flags); } else { num_vf_qs = 1; } + if (res_mod.vf_if_cap_flags & BE_IF_FLAGS_VLAN_PROMISCUOUS) { + nic_vft->flags |= BIT(IF_CAPS_FLAGS_VALID_SHIFT); + vf_if_cap_flags &= ~BE_IF_FLAGS_VLAN_PROMISCUOUS; + } + + nic_vft->cap_flags = cpu_to_le32(vf_if_cap_flags); nic_vft->rq_count = cpu_to_le16(num_vf_qs); nic_vft->txq_count = cpu_to_le16(num_vf_qs); nic_vft->rssq_count = cpu_to_le16(num_vf_qs); diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 7d178bd..91155ea 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -289,7 +289,9 @@ struct be_cmd_req_hdr { u32 timeout; /* dword 1 */ u32 request_length; /* dword 2 */ u8 version; /* dword 3 */ - u8 rsvd[3]; /* dword 3 */ + u8 rsvd1; /* dword 3 */ + u8 pf_num; /* dword 3 */ + u8 rsvd2; /* dword 3 */ }; #define RESP_HDR_INFO_OPCODE_SHIFT 0 /* bits 0 - 7 */ @@ -1652,7 +1654,11 @@ struct mgmt_hba_attribs { struct mgmt_controller_attrib { struct mgmt_hba_attribs hba_attribs; - u32 rsvd0[10]; + u32 rsvd0[2]; + u16 rsvd1; + u8 pci_func_num; + u8 rsvd2; + u32 rsvd3[7]; } __packed; struct be_cmd_req_cntl_attribs { diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 7bf51a1..eb48a97 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1123,11 +1123,12 @@ static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, struct sk_buff *skb, struct be_wrb_params *wrb_params) { - /* Lancer, SH-R ASICs have a bug wherein Packets that are 32 bytes or - * less may cause a transmit stall on that port. So the work-around is - * to pad short packets (<= 32 bytes) to a 36-byte length. + /* Lancer, SH and BE3 in SRIOV mode have a bug wherein + * packets that are 32b or less may cause a transmit stall + * on that port. The workaround is to pad such packets + * (len <= 32 bytes) to a minimum length of 36b. */ - if (unlikely(!BEx_chip(adapter) && skb->len <= 32)) { + if (skb->len <= 32) { if (skb_put_padto(skb, 36)) return NULL; } @@ -4205,10 +4206,6 @@ static int be_get_config(struct be_adapter *adapter) int status, level; u16 profile_id; - status = be_cmd_get_cntl_attributes(adapter); - if (status) - return status; - status = be_cmd_query_fw_cfg(adapter); if (status) return status; @@ -4407,6 +4404,11 @@ static int be_setup(struct be_adapter *adapter) if (!lancer_chip(adapter)) be_cmd_req_native_mode(adapter); + /* Need to invoke this cmd first to get the PCI Function Number */ + status = be_cmd_get_cntl_attributes(adapter); + if (status) + return status; + if (!BE2_chip(adapter) && be_physfn(adapter)) be_alloc_sriov_res(adapter); @@ -4999,7 +5001,15 @@ static bool be_check_ufi_compatibility(struct be_adapter *adapter, return false; } - return (fhdr->asic_type_rev >= adapter->asic_rev); + /* In BE3 FW images the "asic_type_rev" field doesn't track the + * asic_rev of the chips it is compatible with. + * When asic_type_rev is 0 the image is compatible only with + * pre-BE3-R chips (asic_rev < 0x10) + */ + if (BEx_chip(adapter) && fhdr->asic_type_rev == 0) + return adapter->asic_rev < 0x10; + else + return (fhdr->asic_type_rev >= adapter->asic_rev); } static int be_fw_download(struct be_adapter *adapter, const struct firmware* fw) diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c index 3c40f6b..55c3623 100644 --- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c +++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c @@ -198,11 +198,13 @@ static int fsl_pq_mdio_reset(struct mii_bus *bus) #if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE) /* + * Return the TBIPA address, starting from the address + * of the mapped GFAR MDIO registers (struct gfar) * This is mildly evil, but so is our hardware for doing this. * Also, we have to cast back to struct gfar because of * definition weirdness done in gianfar.h. */ -static uint32_t __iomem *get_gfar_tbipa(void __iomem *p) +static uint32_t __iomem *get_gfar_tbipa_from_mdio(void __iomem *p) { struct gfar __iomem *enet_regs = p; @@ -210,6 +212,15 @@ static uint32_t __iomem *get_gfar_tbipa(void __iomem *p) } /* + * Return the TBIPA address, starting from the address + * of the mapped GFAR MII registers (gfar_mii_regs[] within struct gfar) + */ +static uint32_t __iomem *get_gfar_tbipa_from_mii(void __iomem *p) +{ + return get_gfar_tbipa_from_mdio(container_of(p, struct gfar, gfar_mii_regs)); +} + +/* * Return the TBIPAR address for an eTSEC2 node */ static uint32_t __iomem *get_etsec_tbipa(void __iomem *p) @@ -220,11 +231,12 @@ static uint32_t __iomem *get_etsec_tbipa(void __iomem *p) #if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE) /* - * Return the TBIPAR address for a QE MDIO node + * Return the TBIPAR address for a QE MDIO node, starting from the address + * of the mapped MII registers (struct fsl_pq_mii) */ static uint32_t __iomem *get_ucc_tbipa(void __iomem *p) { - struct fsl_pq_mdio __iomem *mdio = p; + struct fsl_pq_mdio __iomem *mdio = container_of(p, struct fsl_pq_mdio, mii); return &mdio->utbipar; } @@ -300,14 +312,14 @@ static const struct of_device_id fsl_pq_mdio_match[] = { .compatible = "fsl,gianfar-tbi", .data = &(struct fsl_pq_mdio_data) { .mii_offset = 0, - .get_tbipa = get_gfar_tbipa, + .get_tbipa = get_gfar_tbipa_from_mii, }, }, { .compatible = "fsl,gianfar-mdio", .data = &(struct fsl_pq_mdio_data) { .mii_offset = 0, - .get_tbipa = get_gfar_tbipa, + .get_tbipa = get_gfar_tbipa_from_mii, }, }, { @@ -315,7 +327,7 @@ static const struct of_device_id fsl_pq_mdio_match[] = { .compatible = "gianfar", .data = &(struct fsl_pq_mdio_data) { .mii_offset = offsetof(struct fsl_pq_mdio, mii), - .get_tbipa = get_gfar_tbipa, + .get_tbipa = get_gfar_tbipa_from_mdio, }, }, { @@ -445,6 +457,16 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev) tbipa = data->get_tbipa(priv->map); + /* + * Add consistency check to make sure TBI is contained + * within the mapped range (not because we would get a + * segfault, rather to catch bugs in computing TBI + * address). Print error message but continue anyway. + */ + if ((void *)tbipa > priv->map + resource_size(&res) - 4) + dev_err(&pdev->dev, "invalid register map (should be at least 0x%04x to contain TBI address)\n", + ((void *)tbipa - priv->map) + 4); + iowrite32be(be32_to_cpup(prop), tbipa); } } diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index 62488a6..c0e943a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -386,7 +386,6 @@ static i40e_status i40e_init_asq(struct i40e_hw *hw) hw->aq.asq.next_to_use = 0; hw->aq.asq.next_to_clean = 0; - hw->aq.asq.count = hw->aq.num_asq_entries; /* allocate the ring memory */ ret_code = i40e_alloc_adminq_asq_ring(hw); @@ -404,6 +403,7 @@ static i40e_status i40e_init_asq(struct i40e_hw *hw) goto init_adminq_free_rings; /* success! */ + hw->aq.asq.count = hw->aq.num_asq_entries; goto init_adminq_exit; init_adminq_free_rings: @@ -445,7 +445,6 @@ static i40e_status i40e_init_arq(struct i40e_hw *hw) hw->aq.arq.next_to_use = 0; hw->aq.arq.next_to_clean = 0; - hw->aq.arq.count = hw->aq.num_arq_entries; /* allocate the ring memory */ ret_code = i40e_alloc_adminq_arq_ring(hw); @@ -463,6 +462,7 @@ static i40e_status i40e_init_arq(struct i40e_hw *hw) goto init_adminq_free_rings; /* success! */ + hw->aq.arq.count = hw->aq.num_arq_entries; goto init_adminq_exit; init_adminq_free_rings: diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2fdf978..dd44faf 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8389,6 +8389,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_GRE | NETIF_F_TSO; netdev->features = NETIF_F_SG | @@ -8396,6 +8397,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) NETIF_F_SCTP_CSUM | NETIF_F_HIGHDMA | NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_GRE | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER | diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c index 929d471..a23ebfd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c @@ -373,7 +373,6 @@ static i40e_status i40e_init_asq(struct i40e_hw *hw) hw->aq.asq.next_to_use = 0; hw->aq.asq.next_to_clean = 0; - hw->aq.asq.count = hw->aq.num_asq_entries; /* allocate the ring memory */ ret_code = i40e_alloc_adminq_asq_ring(hw); @@ -391,6 +390,7 @@ static i40e_status i40e_init_asq(struct i40e_hw *hw) goto init_adminq_free_rings; /* success! */ + hw->aq.asq.count = hw->aq.num_asq_entries; goto init_adminq_exit; init_adminq_free_rings: @@ -432,7 +432,6 @@ static i40e_status i40e_init_arq(struct i40e_hw *hw) hw->aq.arq.next_to_use = 0; hw->aq.arq.next_to_clean = 0; - hw->aq.arq.count = hw->aq.num_arq_entries; /* allocate the ring memory */ ret_code = i40e_alloc_adminq_arq_ring(hw); @@ -450,6 +449,7 @@ static i40e_status i40e_init_arq(struct i40e_hw *hw) goto init_adminq_free_rings; /* success! */ + hw->aq.arq.count = hw->aq.num_arq_entries; goto init_adminq_exit; init_adminq_free_rings: diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 8e81e53..c344884 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -1364,6 +1364,10 @@ int mlx4_test_interrupts(struct mlx4_dev *dev) * and performing a NOP command */ for(i = 0; !err && (i < dev->caps.num_comp_vectors); ++i) { + /* Make sure request_irq was called */ + if (!priv->eq_table.eq[i].have_irq) + continue; + /* Temporary use polling for command completions */ mlx4_cmd_use_polling(dev); diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 006757f..cc3a989 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -2669,14 +2669,11 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) if (msi_x) { int nreq = dev->caps.num_ports * num_online_cpus() + 1; - bool shared_ports = false; nreq = min_t(int, dev->caps.num_eqs - dev->caps.reserved_eqs, nreq); - if (nreq > MAX_MSIX) { + if (nreq > MAX_MSIX) nreq = MAX_MSIX; - shared_ports = true; - } entries = kcalloc(nreq, sizeof *entries, GFP_KERNEL); if (!entries) @@ -2699,9 +2696,6 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) bitmap_zero(priv->eq_table.eq[MLX4_EQ_ASYNC].actv_ports.ports, dev->caps.num_ports); - if (MLX4_IS_LEGACY_EQ_MODE(dev->caps)) - shared_ports = true; - for (i = 0; i < dev->caps.num_comp_vectors + 1; i++) { if (i == MLX4_EQ_ASYNC) continue; @@ -2709,7 +2703,7 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) priv->eq_table.eq[i].irq = entries[i + 1 - !!(i > MLX4_EQ_ASYNC)].vector; - if (shared_ports) { + if (MLX4_IS_LEGACY_EQ_MODE(dev->caps)) { bitmap_fill(priv->eq_table.eq[i].actv_ports.ports, dev->caps.num_ports); /* We don't set affinity hint when there diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c b/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c index e71563c..22d603f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c @@ -598,6 +598,8 @@ void mlx5e_enable_vlan_filter(struct mlx5e_priv *priv) return; priv->vlan.filter_disabled = false; + if (priv->netdev->flags & IFF_PROMISC) + return; mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0); } @@ -607,6 +609,8 @@ void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv) return; priv->vlan.filter_disabled = true; + if (priv->netdev->flags & IFF_PROMISC) + return; mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0); } @@ -717,8 +721,12 @@ void mlx5e_set_rx_mode_work(struct work_struct *work) bool enable_broadcast = !ea->broadcast_enabled && broadcast_enabled; bool disable_broadcast = ea->broadcast_enabled && !broadcast_enabled; - if (enable_promisc) + if (enable_promisc) { mlx5e_add_eth_addr_rule(priv, &ea->promisc, MLX5E_PROMISC); + if (!priv->vlan.filter_disabled) + mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, + 0); + } if (enable_allmulti) mlx5e_add_eth_addr_rule(priv, &ea->allmulti, MLX5E_ALLMULTI); if (enable_broadcast) @@ -730,8 +738,12 @@ void mlx5e_set_rx_mode_work(struct work_struct *work) mlx5e_del_eth_addr_from_flow_table(priv, &ea->broadcast); if (disable_allmulti) mlx5e_del_eth_addr_from_flow_table(priv, &ea->allmulti); - if (disable_promisc) + if (disable_promisc) { + if (!priv->vlan.filter_disabled) + mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, + 0); mlx5e_del_eth_addr_from_flow_table(priv, &ea->promisc); + } ea->promisc_enabled = promisc_enabled; ea->allmulti_enabled = allmulti_enabled; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index 821caaa..3b9480f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -311,7 +311,7 @@ static int mlx5_query_port_pvlc(struct mlx5_core_dev *dev, u32 *pvlc, int err; memset(in, 0, sizeof(in)); - MLX5_SET(ptys_reg, in, local_port, local_port); + MLX5_SET(pvlc_reg, in, local_port, local_port); err = mlx5_core_access_reg(dev, in, sizeof(in), pvlc, pvlc_size, MLX5_REG_PVLC, 0, 0); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index dbcaf5d..28c19cc 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -374,26 +374,31 @@ static int __mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core, int err; int ret; + mlxsw_core->emad.trans_active = true; + err = mlxsw_core_skb_transmit(mlxsw_core->driver_priv, skb, tx_info); if (err) { dev_err(mlxsw_core->bus_info->dev, "Failed to transmit EMAD (tid=%llx)\n", mlxsw_core->emad.tid); dev_kfree_skb(skb); - return err; + goto trans_inactive_out; } - mlxsw_core->emad.trans_active = true; ret = wait_event_timeout(mlxsw_core->emad.wait, !(mlxsw_core->emad.trans_active), msecs_to_jiffies(MLXSW_EMAD_TIMEOUT_MS)); if (!ret) { dev_warn(mlxsw_core->bus_info->dev, "EMAD timed-out (tid=%llx)\n", mlxsw_core->emad.tid); - mlxsw_core->emad.trans_active = false; - return -EIO; + err = -EIO; + goto trans_inactive_out; } return 0; + +trans_inactive_out: + mlxsw_core->emad.trans_active = false; + return err; } static int mlxsw_emad_process_status(struct mlxsw_core *mlxsw_core, diff --git a/drivers/net/ethernet/mellanox/mlxsw/item.h b/drivers/net/ethernet/mellanox/mlxsw/item.h index ffd55d0..36fb1ce 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/item.h +++ b/drivers/net/ethernet/mellanox/mlxsw/item.h @@ -187,6 +187,7 @@ __mlxsw_item_bit_array_offset(struct mlxsw_item *item, u16 index, u8 *shift) { u16 max_index, be_index; u16 offset; /* byte offset inside the array */ + u8 in_byte_index; BUG_ON(index && !item->element_size); if (item->offset % sizeof(u32) != 0 || @@ -199,7 +200,8 @@ __mlxsw_item_bit_array_offset(struct mlxsw_item *item, u16 index, u8 *shift) max_index = (item->size.bytes << 3) / item->element_size - 1; be_index = max_index - index; offset = be_index * item->element_size >> 3; - *shift = index % (BITS_PER_BYTE / item->element_size) << 1; + in_byte_index = index % (BITS_PER_BYTE / item->element_size); + *shift = in_byte_index * item->element_size; return item->offset + offset; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 462cea3..cef866c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -1582,11 +1582,11 @@ static int mlxsw_pci_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod, if (in_mbox) memcpy(mlxsw_pci->cmd.in_mbox.buf, in_mbox, in_mbox_size); - mlxsw_pci_write32(mlxsw_pci, CIR_IN_PARAM_HI, in_mapaddr >> 32); - mlxsw_pci_write32(mlxsw_pci, CIR_IN_PARAM_LO, in_mapaddr); + mlxsw_pci_write32(mlxsw_pci, CIR_IN_PARAM_HI, upper_32_bits(in_mapaddr)); + mlxsw_pci_write32(mlxsw_pci, CIR_IN_PARAM_LO, lower_32_bits(in_mapaddr)); - mlxsw_pci_write32(mlxsw_pci, CIR_OUT_PARAM_HI, out_mapaddr >> 32); - mlxsw_pci_write32(mlxsw_pci, CIR_OUT_PARAM_LO, out_mapaddr); + mlxsw_pci_write32(mlxsw_pci, CIR_OUT_PARAM_HI, upper_32_bits(out_mapaddr)); + mlxsw_pci_write32(mlxsw_pci, CIR_OUT_PARAM_LO, lower_32_bits(out_mapaddr)); mlxsw_pci_write32(mlxsw_pci, CIR_IN_MODIFIER, in_mod); mlxsw_pci_write32(mlxsw_pci, CIR_TOKEN, 0); diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 3e52ee9..62cbbd1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1069,9 +1069,9 @@ static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port) return 0; err_register_netdev: -err_port_admin_status_set: err_port_mac_learning_mode_set: err_port_stp_state_set: +err_port_admin_status_set: err_port_mtu_set: err_port_speed_set: err_port_swid_set: diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 66fd868..b159ef8 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -476,13 +476,12 @@ static void __lpc_get_mac(struct netdata_local *pldat, u8 *mac) mac[5] = tmp >> 8; } -static void __lpc_eth_clock_enable(struct netdata_local *pldat, - bool enable) +static void __lpc_eth_clock_enable(struct netdata_local *pldat, bool enable) { if (enable) - clk_enable(pldat->clk); + clk_prepare_enable(pldat->clk); else - clk_disable(pldat->clk); + clk_disable_unprepare(pldat->clk); } static void __lpc_params_setup(struct netdata_local *pldat) @@ -1494,7 +1493,7 @@ err_out_free_irq: err_out_iounmap: iounmap(pldat->net_base); err_out_disable_clocks: - clk_disable(pldat->clk); + clk_disable_unprepare(pldat->clk); clk_put(pldat->clk); err_out_free_dev: free_netdev(ndev); @@ -1519,7 +1518,7 @@ static int lpc_eth_drv_remove(struct platform_device *pdev) iounmap(pldat->net_base); mdiobus_unregister(pldat->mii_bus); mdiobus_free(pldat->mii_bus); - clk_disable(pldat->clk); + clk_disable_unprepare(pldat->clk); clk_put(pldat->clk); free_netdev(ndev); @@ -1540,7 +1539,7 @@ static int lpc_eth_drv_suspend(struct platform_device *pdev, if (netif_running(ndev)) { netif_device_detach(ndev); __lpc_eth_shutdown(pldat); - clk_disable(pldat->clk); + clk_disable_unprepare(pldat->clk); /* * Reset again now clock is disable to be sure diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index a832637..2b7550c 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -2134,10 +2134,11 @@ static int rhine_rx(struct net_device *dev, int limit) } skb_put(skb, pkt_len); - skb->protocol = eth_type_trans(skb, dev); rhine_rx_vlan_tag(skb, desc, data_size); + skb->protocol = eth_type_trans(skb, dev); + netif_receive_skb(skb); u64_stats_update_begin(&rp->rx_stats.syncp); diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 8f5c02e..cde29f8 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -870,14 +870,14 @@ static int geneve_newlink(struct net *net, struct net_device *dev, __be16 dst_port = htons(GENEVE_UDP_PORT); __u8 ttl = 0, tos = 0; bool metadata = false; - __be32 rem_addr; - __u32 vni; + __be32 rem_addr = 0; + __u32 vni = 0; - if (!data[IFLA_GENEVE_ID] || !data[IFLA_GENEVE_REMOTE]) - return -EINVAL; + if (data[IFLA_GENEVE_ID]) + vni = nla_get_u32(data[IFLA_GENEVE_ID]); - vni = nla_get_u32(data[IFLA_GENEVE_ID]); - rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]); + if (data[IFLA_GENEVE_REMOTE]) + rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]); if (data[IFLA_GENEVE_TTL]) ttl = nla_get_u8(data[IFLA_GENEVE_TTL]); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index c5ad98a..11e3975 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -168,8 +168,6 @@ config MDIO_OCTEON busses. It is required by the Octeon and ThunderX ethernet device drivers. - If in doubt, say Y. - config MDIO_SUN4I tristate "Allwinner sun4i MDIO interface support" depends on ARCH_SUNXI diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 3837ae3..2ed7506 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -313,7 +313,6 @@ static void pppoe_flush_dev(struct net_device *dev) if (po->pppoe_dev == dev && sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) { pppox_unbind_sock(sk); - sk->sk_state = PPPOX_ZOMBIE; sk->sk_state_change(sk); po->pppoe_dev = NULL; dev_put(dev); diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index fbb9325..e66805e 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -164,6 +164,7 @@ config USB_NET_AX8817X * Aten UC210T * ASIX AX88172 * Billionton Systems, USB2AR + * Billionton Systems, GUSB2AM-1G-B * Buffalo LUA-U2-KTX * Corega FEther USB2-TX * D-Link DUB-E100 diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 75d6f26..079069a 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -91,8 +91,10 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, } rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, rx->size); - if (!rx->ax_skb) + if (!rx->ax_skb) { + rx->size = 0; return 0; + } } if (rx->size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 1173a24..5cabefc 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -959,6 +959,10 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x08dd, 0x90ff), .driver_info = (unsigned long) &ax8817x_info, }, { + // Billionton Systems, GUSB2AM-1G-B + USB_DEVICE(0x08dd, 0x0114), + .driver_info = (unsigned long) &ax88178_info, +}, { // ATEN UC210T USB_DEVICE (0x0557, 0x2009), .driver_info = (unsigned long) &ax8817x_info, diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index bbac1d3..afdc65f 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2745,11 +2745,10 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev, struct vxlan_config conf; int err; - if (!data[IFLA_VXLAN_ID]) - return -EINVAL; - memset(&conf, 0, sizeof(conf)); - conf.vni = nla_get_u32(data[IFLA_VXLAN_ID]); + + if (data[IFLA_VXLAN_ID]) + conf.vni = nla_get_u32(data[IFLA_VXLAN_ID]); if (data[IFLA_VXLAN_GROUP]) { conf.remote_ip.sin.sin_addr.s_addr = nla_get_in_addr(data[IFLA_VXLAN_GROUP]); diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 23afcda..678d72a 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -337,7 +337,7 @@ enum ath10k_hw_rate_cck { #define TARGET_10X_MAX_FRAG_ENTRIES 0 /* 10.2 parameters */ -#define TARGET_10_2_DMA_BURST_SIZE 1 +#define TARGET_10_2_DMA_BURST_SIZE 0 /* Target specific defines for WMI-TLV firmware */ #define TARGET_TLV_NUM_VDEVS 4 @@ -391,7 +391,7 @@ enum ath10k_hw_rate_cck { #define TARGET_10_4_TX_DBG_LOG_SIZE 1024 #define TARGET_10_4_NUM_WDS_ENTRIES 32 -#define TARGET_10_4_DMA_BURST_SIZE 1 +#define TARGET_10_4_DMA_BURST_SIZE 0 #define TARGET_10_4_MAC_AGGR_DELIM 0 #define TARGET_10_4_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1 #define TARGET_10_4_VOW_CONFIG 0 diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 57f95f2..90eb750 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -880,6 +880,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); hw->vif_data_size = sizeof(struct ath_vif); + hw->extra_tx_headroom = 4; hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1; hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 2849070..71d3e9a 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -120,6 +120,7 @@ MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if over #ifdef CONFIG_B43_BCMA static const struct bcma_device_id b43_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x15, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1C, BCMA_ANY_CLASS), diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index ab45819..e18629a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -1020,7 +1020,7 @@ static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, u8 *pn = seq.ccmp.pn; ieee80211_get_key_rx_seq(key, i, &seq); - aes_sc->pn = cpu_to_le64( + aes_sc[i].pn = cpu_to_le64( (u64)pn[5] | ((u64)pn[4] << 8) | ((u64)pn[3] << 16) | diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 6951aba..3fb327d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -348,6 +348,6 @@ const struct iwl_cfg iwl7265d_n_cfg = { }; MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); -MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK)); +MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 04264e4..5761876 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -274,18 +274,13 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, break; case WLAN_CIPHER_SUITE_CCMP: if (sta) { - u8 *pn = seq.ccmp.pn; + u64 pn64; aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; - ieee80211_get_key_tx_seq(key, &seq); - aes_tx_sc->pn = cpu_to_le64((u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); + pn64 = atomic64_read(&key->tx_pn); + aes_tx_sc->pn = cpu_to_le64(pn64); } else { aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; } @@ -298,12 +293,12 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, u8 *pn = seq.ccmp.pn; ieee80211_get_key_rx_seq(key, i, &seq); - aes_sc->pn = cpu_to_le64((u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); + aes_sc[i].pn = cpu_to_le64((u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); } data->use_rsc_tsc = true; break; @@ -1453,15 +1448,15 @@ static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw, switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: - iwl_mvm_aes_sc_to_seq(&sc->aes.tsc, &seq); iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key); + atomic64_set(&key->tx_pn, le64_to_cpu(sc->aes.tsc.pn)); break; case WLAN_CIPHER_SUITE_TKIP: iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq); iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key); + ieee80211_set_key_tx_seq(key, &seq); break; } - ieee80211_set_key_tx_seq(key, &seq); /* that's it for this key */ return; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index 4a0ce83..5c7f7cc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -703,7 +703,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) * abort after reading the nvm in case RF Kill is on, we will complete * the init seq later when RF kill will switch to off */ - if (iwl_mvm_is_radio_killed(mvm)) { + if (iwl_mvm_is_radio_hw_killed(mvm)) { IWL_DEBUG_RF_KILL(mvm, "jump over all phy activities due to RF kill\n"); iwl_remove_notification(&mvm->notif_wait, &calib_wait); @@ -736,7 +736,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait, MVM_UCODE_CALIB_TIMEOUT); - if (ret && iwl_mvm_is_radio_killed(mvm)) { + if (ret && iwl_mvm_is_radio_hw_killed(mvm)) { IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n"); ret = 1; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index aa8c2b7..7c2944a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -2388,6 +2388,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); RCU_INIT_POINTER(mvm->csa_vif, NULL); + mvmvif->csa_countdown = false; } if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) { diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index b95a07e..c754051 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -860,6 +860,11 @@ static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); } +static inline bool iwl_mvm_is_radio_hw_killed(struct iwl_mvm *mvm) +{ + return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); +} + /* Must be called with rcu_read_lock() held and it can only be * released when mvmsta is not needed anymore. */ diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index a37de3f..f0cb092 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -590,6 +590,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, ieee80211_unregister_hw(mvm->hw); iwl_mvm_leds_exit(mvm); out_free: + flush_delayed_work(&mvm->fw_dump_wk); iwl_phy_db_free(mvm->phy_db); kfree(mvm->scan_cmd); if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name) diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index b0825c4..644b58b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -414,6 +414,11 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5F10, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5212, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x520A, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9000, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9400, iwl7265_2ac_cfg)}, /* 8000 Series */ {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)}, diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 5932306..bf9afbf 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -1114,6 +1114,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x0db0, 0x871c) }, { USB_DEVICE(0x0db0, 0x899a) }, /* Ovislink */ + { USB_DEVICE(0x1b75, 0x3070) }, { USB_DEVICE(0x1b75, 0x3071) }, { USB_DEVICE(0x1b75, 0x3072) }, { USB_DEVICE(0x1b75, 0xa200) }, diff --git a/drivers/net/wireless/rtlwifi/pci.h b/drivers/net/wireless/rtlwifi/pci.h index d4567d1..5da6703 100644 --- a/drivers/net/wireless/rtlwifi/pci.h +++ b/drivers/net/wireless/rtlwifi/pci.h @@ -247,6 +247,8 @@ struct rtl_pci { /* MSI support */ bool msi_support; bool using_msi; + /* interrupt clear before set */ + bool int_clear; }; struct mp_adapter { diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c index b7f18e21..6e9418e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c @@ -2253,11 +2253,28 @@ void rtl8821ae_set_qos(struct ieee80211_hw *hw, int aci) } } +static void rtl8821ae_clear_interrupt(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 tmp = rtl_read_dword(rtlpriv, REG_HISR); + + rtl_write_dword(rtlpriv, REG_HISR, tmp); + + tmp = rtl_read_dword(rtlpriv, REG_HISRE); + rtl_write_dword(rtlpriv, REG_HISRE, tmp); + + tmp = rtl_read_dword(rtlpriv, REG_HSISR); + rtl_write_dword(rtlpriv, REG_HSISR, tmp); +} + void rtl8821ae_enable_interrupt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + if (!rtlpci->int_clear) + rtl8821ae_clear_interrupt(hw);/*clear it here first*/ + rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF); rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF); rtlpci->irq_enabled = true; diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c index a4988121..8ee141a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c @@ -96,6 +96,7 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) rtl8821ae_bt_reg_init(hw); rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; + rtlpci->int_clear = rtlpriv->cfg->mod_params->int_clear; rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer(); rtlpriv->dm.dm_initialgain_enable = 1; @@ -167,6 +168,7 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; + rtlpci->msi_support = rtlpriv->cfg->mod_params->int_clear; if (rtlpriv->cfg->mod_params->disable_watchdog) pr_info("watchdog disabled\n"); rtlpriv->psc.reg_fwctrl_lps = 3; @@ -308,6 +310,7 @@ static struct rtl_mod_params rtl8821ae_mod_params = { .swctrl_lps = false, .fwctrl_lps = true, .msi_support = true, + .int_clear = true, .debug = DBG_EMERG, .disable_watchdog = 0, }; @@ -437,6 +440,7 @@ module_param_named(fwlps, rtl8821ae_mod_params.fwctrl_lps, bool, 0444); module_param_named(msi, rtl8821ae_mod_params.msi_support, bool, 0444); module_param_named(disable_watchdog, rtl8821ae_mod_params.disable_watchdog, bool, 0444); +module_param_named(int_clear, rtl8821ae_mod_params.int_clear, bool, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); @@ -444,6 +448,7 @@ MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 1)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n"); +MODULE_PARM_DESC(int_clear, "Set to 1 to disable interrupt clear before set (default 0)\n"); static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index b90ca61..4544752 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -2249,6 +2249,9 @@ struct rtl_mod_params { /* default 0: 1 means disable */ bool disable_watchdog; + + /* default 0: 1 means do not disable interrupts */ + bool int_clear; }; struct rtl_hal_usbint_cfg { diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 929a6e7..56ebd82 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -788,6 +788,12 @@ static void connect(struct backend_info *be) /* Use the number of queues requested by the frontend */ be->vif->queues = vzalloc(requested_num_queues * sizeof(struct xenvif_queue)); + if (!be->vif->queues) { + xenbus_dev_fatal(dev, -ENOMEM, + "allocating queues"); + return; + } + be->vif->num_queues = requested_num_queues; be->vif->stalled_queues = requested_num_queues; diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 2365a32..be3755c 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -823,9 +823,15 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu) } /* Now look up the logical CPU number */ - for_each_possible_cpu(cpu) - if (dn == of_cpu_device_node_get(cpu)) + for_each_possible_cpu(cpu) { + struct device_node *cpu_dn; + + cpu_dn = of_cpu_device_node_get(cpu); + of_node_put(cpu_dn); + + if (dn == cpu_dn) break; + } if (cpu >= nr_cpu_ids) { pr_warn("Failed to find logical CPU for %s\n", diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c index 23685e7..bd2c69f 100644 --- a/drivers/staging/iio/accel/sca3000_ring.c +++ b/drivers/staging/iio/accel/sca3000_ring.c @@ -116,7 +116,7 @@ static int sca3000_read_first_n_hw_rb(struct iio_buffer *r, if (ret) goto error_ret; - for (i = 0; i < num_read; i++) + for (i = 0; i < num_read / sizeof(u16); i++) *(((u16 *)rx) + i) = be16_to_cpup((__be16 *)rx + i); if (copy_to_user(buf, rx, num_read)) diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index 3f7715c..47fc00a 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -915,11 +915,12 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev, case IIO_CHAN_INFO_OFFSET: if (chan->type == IIO_TEMP) { /* The calculated value from the ADC is in Kelvin, we - * want Celsius for hwmon so the offset is - * -272.15 * scale + * want Celsius for hwmon so the offset is -273.15 + * The offset is applied before scaling so it is + * actually -213.15 * 4 / 1.012 = -1079.644268 */ - *val = -1075; - *val2 = 691699; + *val = -1079; + *val2 = 644268; return IIO_VAL_INT_PLUS_MICRO; } diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 0bae8cc..ca920b0 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -932,7 +932,7 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, if (data->soc == SOC_ARCH_EXYNOS5260) emul_con = EXYNOS5260_EMUL_CON; - if (data->soc == SOC_ARCH_EXYNOS5433) + else if (data->soc == SOC_ARCH_EXYNOS5433) emul_con = EXYNOS5433_TMU_EMUL_CON; else if (data->soc == SOC_ARCH_EXYNOS7) emul_con = EXYNOS7_TMU_REG_EMUL_CON; diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 21d01a4..e508939 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -80,10 +80,6 @@ int serial8250_tx_dma(struct uart_8250_port *p) return 0; dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - if (dma->tx_size < p->port.fifosize) { - ret = -EINVAL; - goto err; - } desc = dmaengine_prep_slave_single(dma->txchan, dma->tx_addr + xmit->tail, diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index c79d336..c47d3e4 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -147,6 +147,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) { xhci->quirks |= XHCI_SPURIOUS_REBOOT; + xhci->quirks |= XHCI_SPURIOUS_WAKEUP; } if (pdev->vendor == PCI_VENDOR_ID_INTEL && (pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI || diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 43291f9..97ffe39 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2191,6 +2191,10 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, } /* Fast path - was this the last TRB in the TD for this URB? */ } else if (event_trb == td->last_trb) { + if (td->urb_length_set && trb_comp_code == COMP_SHORT_TX) + return finish_td(xhci, td, event_trb, event, ep, + status, false); + if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) { td->urb->actual_length = td->urb->transfer_buffer_length - @@ -2242,6 +2246,12 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, td->urb->actual_length += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); + + if (trb_comp_code == COMP_SHORT_TX) { + xhci_dbg(xhci, "mid bulk/intr SP, wait for last TRB event\n"); + td->urb_length_set = true; + return 0; + } } return finish_td(xhci, td, event_trb, event, ep, status, false); @@ -2274,6 +2284,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, u32 trb_comp_code; int ret = 0; int td_num = 0; + bool handling_skipped_tds = false; slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); xdev = xhci->devs[slot_id]; @@ -2410,6 +2421,10 @@ static int handle_tx_event(struct xhci_hcd *xhci, ep->skip = true; xhci_dbg(xhci, "Miss service interval error, set skip flag\n"); goto cleanup; + case COMP_PING_ERR: + ep->skip = true; + xhci_dbg(xhci, "No Ping response error, Skip one Isoc TD\n"); + goto cleanup; default: if (xhci_is_vendor_info_code(xhci, trb_comp_code)) { status = 0; @@ -2546,13 +2561,18 @@ static int handle_tx_event(struct xhci_hcd *xhci, ep, &status); cleanup: + + + handling_skipped_tds = ep->skip && + trb_comp_code != COMP_MISSED_INT && + trb_comp_code != COMP_PING_ERR; + /* - * Do not update event ring dequeue pointer if ep->skip is set. - * Will roll back to continue process missed tds. + * Do not update event ring dequeue pointer if we're in a loop + * processing missed tds. */ - if (trb_comp_code == COMP_MISSED_INT || !ep->skip) { + if (!handling_skipped_tds) inc_deq(xhci, xhci->event_ring); - } if (ret) { urb = td->urb; @@ -2587,7 +2607,7 @@ cleanup: * Process them as short transfer until reach the td pointed by * the event. */ - } while (ep->skip && trb_comp_code != COMP_MISSED_INT); + } while (handling_skipped_tds); return 0; } diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 1aaf893..92f3949 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -1093,6 +1093,7 @@ static void fbcon_init(struct vc_data *vc, int init) con_copy_unimap(vc, svc); ops = info->fbcon_par; + ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); p->con_rotate = initial_rotation; set_blitting_type(vc, info); diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index b823fac9..8c6f247 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2584,7 +2584,7 @@ static long btrfs_fallocate(struct file *file, int mode, alloc_start); if (ret) goto out; - } else { + } else if (offset + len > inode->i_size) { /* * If we are fallocating from the end of the file onward we * need to zero out the end of the page if i_size lands in the diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 3e3e613..8d20f3b 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4641,7 +4641,7 @@ locked: if (bctl->flags & ~(BTRFS_BALANCE_ARGS_MASK | BTRFS_BALANCE_TYPE_MASK)) { ret = -EINVAL; - goto out_bargs; + goto out_bctl; } do_balance: @@ -4655,12 +4655,15 @@ do_balance: need_unlock = false; ret = btrfs_balance(bctl, bargs); + bctl = NULL; if (arg) { if (copy_to_user(arg, bargs, sizeof(*bargs))) ret = -EFAULT; } +out_bctl: + kfree(bctl); out_bargs: kfree(bargs); out_unlock: diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 091a364..29e4599 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -778,19 +778,24 @@ static void bdi_split_work_to_wbs(struct backing_dev_info *bdi, struct wb_writeback_work *base_work, bool skip_if_busy) { - int next_memcg_id = 0; - struct bdi_writeback *wb; - struct wb_iter iter; + struct bdi_writeback *last_wb = NULL; + struct bdi_writeback *wb = list_entry_rcu(&bdi->wb_list, + struct bdi_writeback, bdi_node); might_sleep(); restart: rcu_read_lock(); - bdi_for_each_wb(wb, bdi, &iter, next_memcg_id) { + list_for_each_entry_continue_rcu(wb, &bdi->wb_list, bdi_node) { DEFINE_WB_COMPLETION_ONSTACK(fallback_work_done); struct wb_writeback_work fallback_work; struct wb_writeback_work *work; long nr_pages; + if (last_wb) { + wb_put(last_wb); + last_wb = NULL; + } + /* SYNC_ALL writes out I_DIRTY_TIME too */ if (!wb_has_dirty_io(wb) && (base_work->sync_mode == WB_SYNC_NONE || @@ -819,12 +824,22 @@ restart: wb_queue_work(wb, work); - next_memcg_id = wb->memcg_css->id + 1; + /* + * Pin @wb so that it stays on @bdi->wb_list. This allows + * continuing iteration from @wb after dropping and + * regrabbing rcu read lock. + */ + wb_get(wb); + last_wb = wb; + rcu_read_unlock(); wb_wait_for_completion(bdi, &fallback_work_done); goto restart; } rcu_read_unlock(); + + if (last_wb) + wb_put(last_wb); } #else /* CONFIG_CGROUP_WRITEBACK */ @@ -1857,12 +1872,11 @@ void wakeup_flusher_threads(long nr_pages, enum wb_reason reason) rcu_read_lock(); list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) { struct bdi_writeback *wb; - struct wb_iter iter; if (!bdi_has_dirty_io(bdi)) continue; - bdi_for_each_wb(wb, bdi, &iter, 0) + list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node) wb_start_writeback(wb, wb_split_bdi_pages(wb, nr_pages), false, reason); } @@ -1894,11 +1908,10 @@ static void wakeup_dirtytime_writeback(struct work_struct *w) rcu_read_lock(); list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) { struct bdi_writeback *wb; - struct wb_iter iter; - bdi_for_each_wb(wb, bdi, &iter, 0) - if (!list_empty(&bdi->wb.b_dirty_time)) - wb_wakeup(&bdi->wb); + list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node) + if (!list_empty(&wb->b_dirty_time)) + wb_wakeup(wb); } rcu_read_unlock(); schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ); diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c index ee5aa4d..ce38b4c 100644 --- a/fs/ocfs2/dlm/dlmmaster.c +++ b/fs/ocfs2/dlm/dlmmaster.c @@ -1658,12 +1658,13 @@ send_response: if (ret < 0) { mlog(ML_ERROR, "failed to dispatch assert master work\n"); response = DLM_MASTER_RESP_ERROR; + spin_unlock(&res->spinlock); dlm_lockres_put(res); } else { dispatched = 1; __dlm_lockres_grab_inflight_worker(dlm, res); + spin_unlock(&res->spinlock); } - spin_unlock(&res->spinlock); } else { if (res) dlm_lockres_put(res); diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index 3d90ad7..58eaa5c 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -1723,8 +1723,8 @@ int dlm_master_requery_handler(struct o2net_msg *msg, u32 len, void *data, } else { dispatched = 1; __dlm_lockres_grab_inflight_worker(dlm, res); + spin_unlock(&res->spinlock); } - spin_unlock(&res->spinlock); } else { /* put.. incase we are not the master */ spin_unlock(&res->spinlock); diff --git a/include/linux/backing-dev-defs.h b/include/linux/backing-dev-defs.h index a23209b..1b4d69f 100644 --- a/include/linux/backing-dev-defs.h +++ b/include/linux/backing-dev-defs.h @@ -116,6 +116,8 @@ struct bdi_writeback { struct list_head work_list; struct delayed_work dwork; /* work item used for writeback */ + struct list_head bdi_node; /* anchored at bdi->wb_list */ + #ifdef CONFIG_CGROUP_WRITEBACK struct percpu_ref refcnt; /* used only for !root wb's */ struct fprop_local_percpu memcg_completions; @@ -150,6 +152,7 @@ struct backing_dev_info { atomic_long_t tot_write_bandwidth; struct bdi_writeback wb; /* the root writeback info for this bdi */ + struct list_head wb_list; /* list of all wbs */ #ifdef CONFIG_CGROUP_WRITEBACK struct radix_tree_root cgwb_tree; /* radix tree of active cgroup wbs */ struct rb_root cgwb_congested_tree; /* their congested states */ diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index d5eb4ad1..c85f749 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -19,13 +19,17 @@ #include <linux/slab.h> int __must_check bdi_init(struct backing_dev_info *bdi); -void bdi_destroy(struct backing_dev_info *bdi); +void bdi_exit(struct backing_dev_info *bdi); __printf(3, 4) int bdi_register(struct backing_dev_info *bdi, struct device *parent, const char *fmt, ...); int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev); +void bdi_unregister(struct backing_dev_info *bdi); + int __must_check bdi_setup_and_register(struct backing_dev_info *, char *); +void bdi_destroy(struct backing_dev_info *bdi); + void wb_start_writeback(struct bdi_writeback *wb, long nr_pages, bool range_cyclic, enum wb_reason reason); void wb_start_background_writeback(struct bdi_writeback *wb); @@ -408,61 +412,6 @@ static inline void unlocked_inode_to_wb_end(struct inode *inode, bool locked) rcu_read_unlock(); } -struct wb_iter { - int start_memcg_id; - struct radix_tree_iter tree_iter; - void **slot; -}; - -static inline struct bdi_writeback *__wb_iter_next(struct wb_iter *iter, - struct backing_dev_info *bdi) -{ - struct radix_tree_iter *titer = &iter->tree_iter; - - WARN_ON_ONCE(!rcu_read_lock_held()); - - if (iter->start_memcg_id >= 0) { - iter->slot = radix_tree_iter_init(titer, iter->start_memcg_id); - iter->start_memcg_id = -1; - } else { - iter->slot = radix_tree_next_slot(iter->slot, titer, 0); - } - - if (!iter->slot) - iter->slot = radix_tree_next_chunk(&bdi->cgwb_tree, titer, 0); - if (iter->slot) - return *iter->slot; - return NULL; -} - -static inline struct bdi_writeback *__wb_iter_init(struct wb_iter *iter, - struct backing_dev_info *bdi, - int start_memcg_id) -{ - iter->start_memcg_id = start_memcg_id; - - if (start_memcg_id) - return __wb_iter_next(iter, bdi); - else - return &bdi->wb; -} - -/** - * bdi_for_each_wb - walk all wb's of a bdi in ascending memcg ID order - * @wb_cur: cursor struct bdi_writeback pointer - * @bdi: bdi to walk wb's of - * @iter: pointer to struct wb_iter to be used as iteration buffer - * @start_memcg_id: memcg ID to start iteration from - * - * Iterate @wb_cur through the wb's (bdi_writeback's) of @bdi in ascending - * memcg ID order starting from @start_memcg_id. @iter is struct wb_iter - * to be used as temp storage during iteration. rcu_read_lock() must be - * held throughout iteration. - */ -#define bdi_for_each_wb(wb_cur, bdi, iter, start_memcg_id) \ - for ((wb_cur) = __wb_iter_init(iter, bdi, start_memcg_id); \ - (wb_cur); (wb_cur) = __wb_iter_next(iter, bdi)) - #else /* CONFIG_CGROUP_WRITEBACK */ static inline bool inode_cgwb_enabled(struct inode *inode) @@ -522,14 +471,6 @@ static inline void wb_blkcg_offline(struct blkcg *blkcg) { } -struct wb_iter { - int next_id; -}; - -#define bdi_for_each_wb(wb_cur, bdi, iter, start_blkcg_id) \ - for ((iter)->next_id = (start_blkcg_id); \ - ({ (wb_cur) = !(iter)->next_id++ ? &(bdi)->wb : NULL; }); ) - static inline int inode_congested(struct inode *inode, int cong_bits) { return wb_congested(&inode_to_bdi(inode)->wb, cong_bits); diff --git a/include/linux/cma.h b/include/linux/cma.h index f7ef093..29f9e77 100644 --- a/include/linux/cma.h +++ b/include/linux/cma.h @@ -26,6 +26,6 @@ extern int __init cma_declare_contiguous(phys_addr_t base, extern int cma_init_reserved_mem(phys_addr_t base, phys_addr_t size, unsigned int order_per_bit, struct cma **res_cma); -extern struct page *cma_alloc(struct cma *cma, unsigned int count, unsigned int align); +extern struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align); extern bool cma_release(struct cma *cma, const struct page *pages, unsigned int count); #endif diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h index dfaa7b3..8efb40e 100644 --- a/include/linux/compiler-gcc.h +++ b/include/linux/compiler-gcc.h @@ -237,12 +237,25 @@ #define KASAN_ABI_VERSION 3 #endif +#if GCC_VERSION >= 40902 +/* + * Tell the compiler that address safety instrumentation (KASAN) + * should not be applied to that function. + * Conflicts with inlining: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368 + */ +#define __no_sanitize_address __attribute__((no_sanitize_address)) +#endif + #endif /* gcc version >= 40000 specific checks */ #if !defined(__noclone) #define __noclone /* not needed */ #endif +#if !defined(__no_sanitize_address) +#define __no_sanitize_address +#endif + /* * A trick to suppress uninitialized variable warning without generating any * code diff --git a/include/linux/compiler.h b/include/linux/compiler.h index c836eb2..3d78103 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -198,19 +198,45 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect); #include <uapi/linux/types.h> -static __always_inline void __read_once_size(const volatile void *p, void *res, int size) +#define __READ_ONCE_SIZE \ +({ \ + switch (size) { \ + case 1: *(__u8 *)res = *(volatile __u8 *)p; break; \ + case 2: *(__u16 *)res = *(volatile __u16 *)p; break; \ + case 4: *(__u32 *)res = *(volatile __u32 *)p; break; \ + case 8: *(__u64 *)res = *(volatile __u64 *)p; break; \ + default: \ + barrier(); \ + __builtin_memcpy((void *)res, (const void *)p, size); \ + barrier(); \ + } \ +}) + +static __always_inline +void __read_once_size(const volatile void *p, void *res, int size) { - switch (size) { - case 1: *(__u8 *)res = *(volatile __u8 *)p; break; - case 2: *(__u16 *)res = *(volatile __u16 *)p; break; - case 4: *(__u32 *)res = *(volatile __u32 *)p; break; - case 8: *(__u64 *)res = *(volatile __u64 *)p; break; - default: - barrier(); - __builtin_memcpy((void *)res, (const void *)p, size); - barrier(); - } + __READ_ONCE_SIZE; +} + +#ifdef CONFIG_KASAN +/* + * This function is not 'inline' because __no_sanitize_address confilcts + * with inlining. Attempt to inline it may cause a build failure. + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368 + * '__maybe_unused' allows us to avoid defined-but-not-used warnings. + */ +static __no_sanitize_address __maybe_unused +void __read_once_size_nocheck(const volatile void *p, void *res, int size) +{ + __READ_ONCE_SIZE; +} +#else +static __always_inline +void __read_once_size_nocheck(const volatile void *p, void *res, int size) +{ + __READ_ONCE_SIZE; } +#endif static __always_inline void __write_once_size(volatile void *p, void *res, int size) { @@ -248,8 +274,22 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s * required ordering. */ -#define READ_ONCE(x) \ - ({ union { typeof(x) __val; char __c[1]; } __u; __read_once_size(&(x), __u.__c, sizeof(x)); __u.__val; }) +#define __READ_ONCE(x, check) \ +({ \ + union { typeof(x) __val; char __c[1]; } __u; \ + if (check) \ + __read_once_size(&(x), __u.__c, sizeof(x)); \ + else \ + __read_once_size_nocheck(&(x), __u.__c, sizeof(x)); \ + __u.__val; \ +}) +#define READ_ONCE(x) __READ_ONCE(x, 1) + +/* + * Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need + * to hide memory access from KASAN. + */ +#define READ_ONCE_NOCHECK(x) __READ_ONCE(x, 0) #define WRITE_ONCE(x, val) \ ({ \ diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h index 569bbd0..fec734d 100644 --- a/include/linux/dma-contiguous.h +++ b/include/linux/dma-contiguous.h @@ -111,7 +111,7 @@ static inline int dma_declare_contiguous(struct device *dev, phys_addr_t size, return ret; } -struct page *dma_alloc_from_contiguous(struct device *dev, int count, +struct page *dma_alloc_from_contiguous(struct device *dev, size_t count, unsigned int order); bool dma_release_from_contiguous(struct device *dev, struct page *pages, int count); @@ -144,7 +144,7 @@ int dma_declare_contiguous(struct device *dev, phys_addr_t size, } static inline -struct page *dma_alloc_from_contiguous(struct device *dev, int count, +struct page *dma_alloc_from_contiguous(struct device *dev, size_t count, unsigned int order) { return NULL; diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 6452ff4..3e3318dd 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -676,8 +676,9 @@ enum { struct list_head *mem_cgroup_cgwb_list(struct mem_cgroup *memcg); struct wb_domain *mem_cgroup_wb_domain(struct bdi_writeback *wb); -void mem_cgroup_wb_stats(struct bdi_writeback *wb, unsigned long *pavail, - unsigned long *pdirty, unsigned long *pwriteback); +void mem_cgroup_wb_stats(struct bdi_writeback *wb, unsigned long *pfilepages, + unsigned long *pheadroom, unsigned long *pdirty, + unsigned long *pwriteback); #else /* CONFIG_CGROUP_WRITEBACK */ @@ -687,7 +688,8 @@ static inline struct wb_domain *mem_cgroup_wb_domain(struct bdi_writeback *wb) } static inline void mem_cgroup_wb_stats(struct bdi_writeback *wb, - unsigned long *pavail, + unsigned long *pfilepages, + unsigned long *pheadroom, unsigned long *pdirty, unsigned long *pwriteback) { diff --git a/include/net/af_unix.h b/include/net/af_unix.h index cb1b9bb..b36d837 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -64,7 +64,7 @@ struct unix_sock { struct socket_wq peer_wq; }; -static inline struct unix_sock *unix_sk(struct sock *sk) +static inline struct unix_sock *unix_sk(const struct sock *sk) { return (struct unix_sock *)sk; } diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index 186f3a1..fc19376 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h @@ -113,12 +113,12 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk, void __inet_twsk_schedule(struct inet_timewait_sock *tw, int timeo, bool rearm); -static void inline inet_twsk_schedule(struct inet_timewait_sock *tw, int timeo) +static inline void inet_twsk_schedule(struct inet_timewait_sock *tw, int timeo) { __inet_twsk_schedule(tw, timeo, false); } -static void inline inet_twsk_reschedule(struct inet_timewait_sock *tw, int timeo) +static inline void inet_twsk_reschedule(struct inet_timewait_sock *tw, int timeo) { __inet_twsk_schedule(tw, timeo, true); } diff --git a/include/net/sock.h b/include/net/sock.h index 7aa7844..e237170 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -828,6 +828,14 @@ static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *s if (sk_rcvqueues_full(sk, limit)) return -ENOBUFS; + /* + * If the skb was allocated from pfmemalloc reserves, only + * allow SOCK_MEMALLOC sockets to use it as this socket is + * helping free memory + */ + if (skb_pfmemalloc(skb) && !sock_flag(sk, SOCK_MEMALLOC)) + return -ENOMEM; + __sk_add_backlog(sk, skb); sk->sk_backlog.len += skb->truesize; return 0; diff --git a/include/sound/da7213.h b/include/sound/da7213.h index 673f5c3..e7eac897 100644 --- a/include/sound/da7213.h +++ b/include/sound/da7213.h @@ -44,9 +44,6 @@ struct da7213_platform_data { enum da7213_dmic_data_sel dmic_data_sel; enum da7213_dmic_samplephase dmic_samplephase; enum da7213_dmic_clk_rate dmic_clk_rate; - - /* MCLK squaring config */ - bool mclk_squaring; }; #endif /* _DA7213_PDATA_H */ diff --git a/include/sound/da7219-aad.h b/include/sound/da7219-aad.h new file mode 100644 index 0000000..17802fb --- /dev/null +++ b/include/sound/da7219-aad.h @@ -0,0 +1,99 @@ +/* + * da7219-aad.h - DA7322 ASoC Codec AAD Driver Platform Data + * + * Copyright (c) 2015 Dialog Semiconductor Ltd. + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __DA7219_AAD_PDATA_H +#define __DA7219_AAD_PDATA_H + +enum da7219_aad_micbias_pulse_lvl { + DA7219_AAD_MICBIAS_PULSE_LVL_OFF = 0, + DA7219_AAD_MICBIAS_PULSE_LVL_2_8V = 6, + DA7219_AAD_MICBIAS_PULSE_LVL_2_9V, +}; + +enum da7219_aad_btn_cfg { + DA7219_AAD_BTN_CFG_2MS = 1, + DA7219_AAD_BTN_CFG_5MS, + DA7219_AAD_BTN_CFG_10MS, + DA7219_AAD_BTN_CFG_50MS, + DA7219_AAD_BTN_CFG_100MS, + DA7219_AAD_BTN_CFG_200MS, + DA7219_AAD_BTN_CFG_500MS, +}; + +enum da7219_aad_mic_det_thr { + DA7219_AAD_MIC_DET_THR_200_OHMS = 0, + DA7219_AAD_MIC_DET_THR_500_OHMS, + DA7219_AAD_MIC_DET_THR_750_OHMS, + DA7219_AAD_MIC_DET_THR_1000_OHMS, +}; + +enum da7219_aad_jack_ins_deb { + DA7219_AAD_JACK_INS_DEB_5MS = 0, + DA7219_AAD_JACK_INS_DEB_10MS, + DA7219_AAD_JACK_INS_DEB_20MS, + DA7219_AAD_JACK_INS_DEB_50MS, + DA7219_AAD_JACK_INS_DEB_100MS, + DA7219_AAD_JACK_INS_DEB_200MS, + DA7219_AAD_JACK_INS_DEB_500MS, + DA7219_AAD_JACK_INS_DEB_1S, +}; + +enum da7219_aad_jack_det_rate { + DA7219_AAD_JACK_DET_RATE_32_64MS = 0, + DA7219_AAD_JACK_DET_RATE_64_128MS, + DA7219_AAD_JACK_DET_RATE_128_256MS, + DA7219_AAD_JACK_DET_RATE_256_512MS, +}; + +enum da7219_aad_jack_rem_deb { + DA7219_AAD_JACK_REM_DEB_1MS = 0, + DA7219_AAD_JACK_REM_DEB_5MS, + DA7219_AAD_JACK_REM_DEB_10MS, + DA7219_AAD_JACK_REM_DEB_20MS, +}; + +enum da7219_aad_btn_avg { + DA7219_AAD_BTN_AVG_1 = 0, + DA7219_AAD_BTN_AVG_2, + DA7219_AAD_BTN_AVG_4, + DA7219_AAD_BTN_AVG_8, +}; + +enum da7219_aad_adc_1bit_rpt { + DA7219_AAD_ADC_1BIT_RPT_1 = 0, + DA7219_AAD_ADC_1BIT_RPT_2, + DA7219_AAD_ADC_1BIT_RPT_4, + DA7219_AAD_ADC_1BIT_RPT_8, +}; + +struct da7219_aad_pdata { + int irq; + + enum da7219_aad_micbias_pulse_lvl micbias_pulse_lvl; + u32 micbias_pulse_time; + enum da7219_aad_btn_cfg btn_cfg; + enum da7219_aad_mic_det_thr mic_det_thr; + enum da7219_aad_jack_ins_deb jack_ins_deb; + enum da7219_aad_jack_det_rate jack_det_rate; + enum da7219_aad_jack_rem_deb jack_rem_deb; + + u8 a_d_btn_thr; + u8 d_b_btn_thr; + u8 b_c_btn_thr; + u8 c_mic_btn_thr; + + enum da7219_aad_btn_avg btn_avg; + enum da7219_aad_adc_1bit_rpt adc_1bit_rpt; +}; + +#endif /* __DA7219_AAD_PDATA_H */ diff --git a/include/sound/da7219.h b/include/sound/da7219.h new file mode 100644 index 0000000..3f39e13 --- /dev/null +++ b/include/sound/da7219.h @@ -0,0 +1,55 @@ +/* + * da7219.h - DA7219 ASoC Codec Driver Platform Data + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __DA7219_PDATA_H +#define __DA7219_PDATA_H + +/* LDO */ +enum da7219_ldo_lvl_sel { + DA7219_LDO_LVL_SEL_1_05V = 0, + DA7219_LDO_LVL_SEL_1_10V, + DA7219_LDO_LVL_SEL_1_20V, + DA7219_LDO_LVL_SEL_1_40V, +}; + +/* Mic Bias */ +enum da7219_micbias_voltage { + DA7219_MICBIAS_1_8V = 1, + DA7219_MICBIAS_2_0V, + DA7219_MICBIAS_2_2V, + DA7219_MICBIAS_2_4V, + DA7219_MICBIAS_2_6V, +}; + +/* Mic input type */ +enum da7219_mic_amp_in_sel { + DA7219_MIC_AMP_IN_SEL_DIFF = 0, + DA7219_MIC_AMP_IN_SEL_SE_P, + DA7219_MIC_AMP_IN_SEL_SE_N, +}; + +struct da7219_aad_pdata; + +struct da7219_pdata { + /* Internal LDO */ + enum da7219_ldo_lvl_sel ldo_lvl_sel; + + /* Mic */ + enum da7219_micbias_voltage micbias_lvl; + enum da7219_mic_amp_in_sel mic_amp_in_sel; + + /* AAD */ + struct da7219_aad_pdata *aad_pdata; +}; + +#endif /* __DA7219_PDATA_H */ diff --git a/include/sound/designware_i2s.h b/include/sound/designware_i2s.h index 3a8fca9..8966ba7 100644 --- a/include/sound/designware_i2s.h +++ b/include/sound/designware_i2s.h @@ -38,6 +38,8 @@ struct i2s_clk_config_data { struct i2s_platform_data { #define DWC_I2S_PLAY (1 << 0) #define DWC_I2S_RECORD (1 << 1) + #define DW_I2S_SLAVE (1 << 2) + #define DW_I2S_MASTER (1 << 3) unsigned int cap; int channel; u32 snd_fmts; diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h index 56e818e..6ef629b 100644 --- a/include/sound/pxa2xx-lib.h +++ b/include/sound/pxa2xx-lib.h @@ -12,7 +12,6 @@ extern int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream); extern int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd); extern snd_pcm_uframes_t pxa2xx_pcm_pointer(struct snd_pcm_substream *substream); extern int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream); -extern void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id); extern int __pxa2xx_pcm_open(struct snd_pcm_substream *substream); extern int __pxa2xx_pcm_close(struct snd_pcm_substream *substream); extern int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, diff --git a/include/sound/rt5640.h b/include/sound/rt5640.h index 59d26dd..e3c84b9 100644 --- a/include/sound/rt5640.h +++ b/include/sound/rt5640.h @@ -12,9 +12,10 @@ #define __LINUX_SND_RT5640_H struct rt5640_platform_data { - /* IN1 & IN2 can optionally be differential */ + /* IN1 & IN2 & IN3 can optionally be differential */ bool in1_diff; bool in2_diff; + bool in3_diff; bool dmic_en; bool dmic1_data_pin; /* 0 = IN1P; 1 = GPIO3 */ diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 2df96b1..212eaaf 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -48,10 +48,25 @@ struct snd_compr_stream; #define SND_SOC_DAIFMT_GATED (0 << 4) /* clock is gated */ /* - * DAI hardware signal inversions. + * DAI hardware signal polarity. * * Specifies whether the DAI can also support inverted clocks for the specified * format. + * + * BCLK: + * - "normal" polarity means signal is available at rising edge of BCLK + * - "inverted" polarity means signal is available at falling edge of BCLK + * + * FSYNC "normal" polarity depends on the frame format: + * - I2S: frame consists of left then right channel data. Left channel starts + * with falling FSYNC edge, right channel starts with rising FSYNC edge. + * - Left/Right Justified: frame consists of left then right channel data. + * Left channel starts with rising FSYNC edge, right channel starts with + * falling FSYNC edge. + * - DSP A/B: Frame starts with rising FSYNC edge. + * - AC97: Frame starts with rising FSYNC edge. + * + * "Negative" FSYNC polarity is the one opposite of "normal" polarity. */ #define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */ #define SND_SOC_DAIFMT_NB_IF (2 << 8) /* normal BCLK + inv FRM */ @@ -214,7 +229,7 @@ struct snd_soc_dai_driver { int (*suspend)(struct snd_soc_dai *dai); int (*resume)(struct snd_soc_dai *dai); /* compress dai */ - bool compress_dai; + int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num); /* DAI is also used for the control bus */ bool bus_control; diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 5abba03..7855cfe 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -451,6 +451,9 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( struct snd_kcontrol *kcontrol); +struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget( + struct snd_kcontrol *kcontrol); + int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level); diff --git a/include/sound/soc.h b/include/sound/soc.h index 470f208..a8b4b9c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -86,7 +86,7 @@ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_READWRITE, \ .tlv.p = (tlv_array),\ - .info = snd_soc_info_volsw, \ + .info = snd_soc_info_volsw_sx, \ .get = snd_soc_get_volsw_sx,\ .put = snd_soc_put_volsw_sx, \ .private_value = (unsigned long)&(struct soc_mixer_control) \ @@ -156,7 +156,7 @@ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_READWRITE, \ .tlv.p = (tlv_array), \ - .info = snd_soc_info_volsw, \ + .info = snd_soc_info_volsw_sx, \ .get = snd_soc_get_volsw_sx, \ .put = snd_soc_put_volsw_sx, \ .private_value = (unsigned long)&(struct soc_mixer_control) \ @@ -217,6 +217,13 @@ .get = xhandler_get, .put = xhandler_put, \ .private_value = \ SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert, 0) } +#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ + xmax, xinvert) } #define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -452,7 +459,9 @@ int snd_soc_platform_read(struct snd_soc_platform *platform, int snd_soc_platform_write(struct snd_soc_platform *platform, unsigned int reg, unsigned int val); int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); -int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num); +#ifdef CONFIG_SND_SOC_COMPRESS +int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num); +#endif struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, const char *dai_link, int stream); @@ -586,6 +595,8 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); +int snd_soc_info_volsw_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); #define snd_soc_info_bool_ext snd_ctl_boolean_mono_info int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); @@ -603,7 +614,7 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_soc_limit_volume(struct snd_soc_codec *codec, +int snd_soc_limit_volume(struct snd_soc_card *card, const char *name, int max); int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h index 898be3a..6d8f8fb 100644 --- a/include/sound/wm8904.h +++ b/include/sound/wm8904.h @@ -119,7 +119,7 @@ #define WM8904_MIC_REGS 2 #define WM8904_GPIO_REGS 4 #define WM8904_DRC_REGS 4 -#define WM8904_EQ_REGS 25 +#define WM8904_EQ_REGS 24 /** * DRC configurations are specified with a label and a set of register diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 32e07d8..036f73b 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -323,10 +323,10 @@ enum ovs_key_attr { OVS_KEY_ATTR_MPLS, /* array of struct ovs_key_mpls. * The implementation may restrict * the accepted length of the array. */ - OVS_KEY_ATTR_CT_STATE, /* u8 bitmask of OVS_CS_F_* */ + OVS_KEY_ATTR_CT_STATE, /* u32 bitmask of OVS_CS_F_* */ OVS_KEY_ATTR_CT_ZONE, /* u16 connection tracking zone. */ OVS_KEY_ATTR_CT_MARK, /* u32 connection tracking mark */ - OVS_KEY_ATTR_CT_LABEL, /* 16-octet connection tracking label */ + OVS_KEY_ATTR_CT_LABELS, /* 16-octet connection tracking label */ #ifdef __KERNEL__ OVS_KEY_ATTR_TUNNEL_INFO, /* struct ip_tunnel_info */ @@ -439,9 +439,9 @@ struct ovs_key_nd { __u8 nd_tll[ETH_ALEN]; }; -#define OVS_CT_LABEL_LEN 16 -struct ovs_key_ct_label { - __u8 ct_label[OVS_CT_LABEL_LEN]; +#define OVS_CT_LABELS_LEN 16 +struct ovs_key_ct_labels { + __u8 ct_labels[OVS_CT_LABELS_LEN]; }; /* OVS_KEY_ATTR_CT_STATE flags */ @@ -449,9 +449,9 @@ struct ovs_key_ct_label { #define OVS_CS_F_ESTABLISHED 0x02 /* Part of an existing connection. */ #define OVS_CS_F_RELATED 0x04 /* Related to an established * connection. */ -#define OVS_CS_F_INVALID 0x20 /* Could not track connection. */ -#define OVS_CS_F_REPLY_DIR 0x40 /* Flow is in the reply direction. */ -#define OVS_CS_F_TRACKED 0x80 /* Conntrack has occurred. */ +#define OVS_CS_F_REPLY_DIR 0x08 /* Flow is in the reply direction. */ +#define OVS_CS_F_INVALID 0x10 /* Could not track connection. */ +#define OVS_CS_F_TRACKED 0x20 /* Conntrack has occurred. */ /** * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands. @@ -618,22 +618,24 @@ struct ovs_action_hash { /** * enum ovs_ct_attr - Attributes for %OVS_ACTION_ATTR_CT action. - * @OVS_CT_ATTR_FLAGS: u32 connection tracking flags. + * @OVS_CT_ATTR_COMMIT: If present, commits the connection to the conntrack + * table. This allows future packets for the same connection to be identified + * as 'established' or 'related'. * @OVS_CT_ATTR_ZONE: u16 connection tracking zone. * @OVS_CT_ATTR_MARK: u32 value followed by u32 mask. For each bit set in the * mask, the corresponding bit in the value is copied to the connection * tracking mark field in the connection. - * @OVS_CT_ATTR_LABEL: %OVS_CT_LABEL_LEN value followed by %OVS_CT_LABEL_LEN + * @OVS_CT_ATTR_LABEL: %OVS_CT_LABELS_LEN value followed by %OVS_CT_LABELS_LEN * mask. For each bit set in the mask, the corresponding bit in the value is * copied to the connection tracking label field in the connection. * @OVS_CT_ATTR_HELPER: variable length string defining conntrack ALG. */ enum ovs_ct_attr { OVS_CT_ATTR_UNSPEC, - OVS_CT_ATTR_FLAGS, /* u8 bitmask of OVS_CT_F_*. */ + OVS_CT_ATTR_COMMIT, /* No argument, commits connection. */ OVS_CT_ATTR_ZONE, /* u16 zone id. */ OVS_CT_ATTR_MARK, /* mark to associate with this connection. */ - OVS_CT_ATTR_LABEL, /* label to associate with this connection. */ + OVS_CT_ATTR_LABELS, /* labels to associate with this connection. */ OVS_CT_ATTR_HELPER, /* netlink helper to assist detection of related connections. */ __OVS_CT_ATTR_MAX @@ -641,14 +643,6 @@ enum ovs_ct_attr { #define OVS_CT_ATTR_MAX (__OVS_CT_ATTR_MAX - 1) -/* - * OVS_CT_ATTR_FLAGS flags - bitmask of %OVS_CT_F_* - * @OVS_CT_F_COMMIT: Commits the flow to the conntrack table. This allows - * future packets for the same connection to be identified as 'established' - * or 'related'. - */ -#define OVS_CT_F_COMMIT 0x01 - /** * enum ovs_action_attr - Action types. * @@ -705,7 +699,7 @@ enum ovs_action_attr { * data immediately followed by a mask. * The data must be zero for the unmasked * bits. */ - OVS_ACTION_ATTR_CT, /* One nested OVS_CT_ATTR_* . */ + OVS_ACTION_ATTR_CT, /* Nested OVS_CT_ATTR_* . */ __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 7020247..9d8f5d1 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -160,7 +160,7 @@ struct rtattr { /* Macros to handle rtattributes */ -#define RTA_ALIGNTO 4 +#define RTA_ALIGNTO 4U #define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) #define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ (rta)->rta_len >= sizeof(struct rtattr) && \ diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 247c50b..26539a7 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -83,7 +83,7 @@ #define SND_SOC_TPLG_NUM_TEXTS 16 /* ABI version */ -#define SND_SOC_TPLG_ABI_VERSION 0x3 +#define SND_SOC_TPLG_ABI_VERSION 0x4 /* Max size of TLV data */ #define SND_SOC_TPLG_TLV_SIZE 32 @@ -103,7 +103,8 @@ #define SND_SOC_TPLG_TYPE_PCM 7 #define SND_SOC_TPLG_TYPE_MANIFEST 8 #define SND_SOC_TPLG_TYPE_CODEC_LINK 9 -#define SND_SOC_TPLG_TYPE_PDATA 10 +#define SND_SOC_TPLG_TYPE_BACKEND_LINK 10 +#define SND_SOC_TPLG_TYPE_PDATA 11 #define SND_SOC_TPLG_TYPE_MAX SND_SOC_TPLG_TYPE_PDATA /* vendor block IDs - please add new vendor types to end */ @@ -198,7 +199,7 @@ struct snd_soc_tplg_ctl_hdr { struct snd_soc_tplg_stream_caps { __le32 size; /* in bytes of this structure */ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - __le64 formats[SND_SOC_TPLG_MAX_FORMATS]; /* supported formats SNDRV_PCM_FMTBIT_* */ + __le64 formats; /* supported formats SNDRV_PCM_FMTBIT_* */ __le32 rates; /* supported rates SNDRV_PCM_RATE_* */ __le32 rate_min; /* min rate */ __le32 rate_max; /* max rate */ @@ -217,23 +218,12 @@ struct snd_soc_tplg_stream_caps { */ struct snd_soc_tplg_stream { __le32 size; /* in bytes of this structure */ + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* Name of the stream */ __le64 format; /* SNDRV_PCM_FMTBIT_* */ __le32 rate; /* SNDRV_PCM_RATE_* */ __le32 period_bytes; /* size of period in bytes */ __le32 buffer_bytes; /* size of buffer in bytes */ __le32 channels; /* channels */ - __le32 tdm_slot; /* optional BE bitmask of supported TDM slots */ - __le32 dai_fmt; /* SND_SOC_DAIFMT_ */ -} __attribute__((packed)); - -/* - * Duplex stream configuration supported by SW/FW. - */ -struct snd_soc_tplg_stream_config { - __le32 size; /* in bytes of this structure */ - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - struct snd_soc_tplg_stream playback; - struct snd_soc_tplg_stream capture; } __attribute__((packed)); /* @@ -366,11 +356,11 @@ struct snd_soc_tplg_dapm_widget { __le32 shift; /* bits to shift */ __le32 mask; /* non-shifted mask */ __le32 subseq; /* sort within widget type */ - __u32 invert; /* invert the power bit */ - __u32 ignore_suspend; /* kept enabled over suspend */ - __u16 event_flags; - __u16 event_type; - __u16 num_kcontrols; + __le32 invert; /* invert the power bit */ + __le32 ignore_suspend; /* kept enabled over suspend */ + __le16 event_flags; + __le16 event_type; + __le32 num_kcontrols; struct snd_soc_tplg_private priv; /* * kcontrols that relate to this widget @@ -378,30 +368,46 @@ struct snd_soc_tplg_dapm_widget { */ } __attribute__((packed)); -struct snd_soc_tplg_pcm_cfg_caps { - struct snd_soc_tplg_stream_caps caps; - struct snd_soc_tplg_stream_config configs[SND_SOC_TPLG_STREAM_CONFIG_MAX]; - __le32 num_configs; /* number of configs */ -} __attribute__((packed)); /* - * Describes SW/FW specific features of PCM or DAI link. + * Describes SW/FW specific features of PCM (FE DAI & DAI link). * - * File block representation for PCM/DAI-Link :- + * File block representation for PCM :- * +-----------------------------------+-----+ * | struct snd_soc_tplg_hdr | 1 | * +-----------------------------------+-----+ - * | struct snd_soc_tplg_dapm_pcm_dai | N | + * | struct snd_soc_tplg_pcm | N | * +-----------------------------------+-----+ */ -struct snd_soc_tplg_pcm_dai { +struct snd_soc_tplg_pcm { __le32 size; /* in bytes of this structure */ - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - __le32 id; /* unique ID - used to match */ - __le32 playback; /* supports playback mode */ - __le32 capture; /* supports capture mode */ - __le32 compress; /* 1 = compressed; 0 = PCM */ - struct snd_soc_tplg_pcm_cfg_caps capconf[2]; /* capabilities and configs */ + char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + __le32 pcm_id; /* unique ID - used to match */ + __le32 dai_id; /* unique ID - used to match */ + __le32 playback; /* supports playback mode */ + __le32 capture; /* supports capture mode */ + __le32 compress; /* 1 = compressed; 0 = PCM */ + struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* for DAI link */ + __le32 num_streams; /* number of streams */ + struct snd_soc_tplg_stream_caps caps[2]; /* playback and capture for DAI */ } __attribute__((packed)); + +/* + * Describes the BE or CC link runtime supported configs or params + * + * File block representation for BE/CC link config :- + * +-----------------------------------+-----+ + * | struct snd_soc_tplg_hdr | 1 | + * +-----------------------------------+-----+ + * | struct snd_soc_tplg_link_config | N | + * +-----------------------------------+-----+ + */ +struct snd_soc_tplg_link_config { + __le32 size; /* in bytes of this structure */ + __le32 id; /* unique ID - used to match */ + struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* supported configs playback and captrure */ + __le32 num_streams; /* number of streams */ +} __attribute__((packed)); #endif diff --git a/kernel/kmod.c b/kernel/kmod.c index da98d05..0277d12 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -327,9 +327,13 @@ static void call_usermodehelper_exec_work(struct work_struct *work) call_usermodehelper_exec_sync(sub_info); } else { pid_t pid; - + /* + * Use CLONE_PARENT to reparent it to kthreadd; we do not + * want to pollute current->children, and we need a parent + * that always ignores SIGCHLD to ensure auto-reaping. + */ pid = kernel_thread(call_usermodehelper_exec_async, sub_info, - SIGCHLD); + CLONE_PARENT | SIGCHLD); if (pid < 0) { sub_info->retval = pid; umh_complete(sub_info); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 10a8faa..bcd214e 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2366,8 +2366,15 @@ void wake_up_new_task(struct task_struct *p) trace_sched_wakeup_new(p); check_preempt_curr(rq, p, WF_FORK); #ifdef CONFIG_SMP - if (p->sched_class->task_woken) + if (p->sched_class->task_woken) { + /* + * Nothing relies on rq->lock after this, so its fine to + * drop it. + */ + lockdep_unpin_lock(&rq->lock); p->sched_class->task_woken(rq, p); + lockdep_pin_lock(&rq->lock); + } #endif task_rq_unlock(rq, p, &flags); } @@ -7238,9 +7245,6 @@ void __init sched_init_smp(void) alloc_cpumask_var(&non_isolated_cpus, GFP_KERNEL); alloc_cpumask_var(&fallback_doms, GFP_KERNEL); - /* nohz_full won't take effect without isolating the cpus. */ - tick_nohz_full_add_cpus_to(cpu_isolated_map); - sched_init_numa(); /* diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index fc8f010..8b0a15e 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -668,8 +668,15 @@ static enum hrtimer_restart dl_task_timer(struct hrtimer *timer) * Queueing this task back might have overloaded rq, check if we need * to kick someone away. */ - if (has_pushable_dl_tasks(rq)) + if (has_pushable_dl_tasks(rq)) { + /* + * Nothing relies on rq->lock after this, so its safe to drop + * rq->lock. + */ + lockdep_unpin_lock(&rq->lock); push_dl_task(rq); + lockdep_pin_lock(&rq->lock); + } #endif unlock: @@ -1066,8 +1073,9 @@ select_task_rq_dl(struct task_struct *p, int cpu, int sd_flag, int flags) int target = find_later_rq(p); if (target != -1 && - dl_time_before(p->dl.deadline, - cpu_rq(target)->dl.earliest_dl.curr)) + (dl_time_before(p->dl.deadline, + cpu_rq(target)->dl.earliest_dl.curr) || + (cpu_rq(target)->dl.dl_nr_running == 0))) cpu = target; } rcu_read_unlock(); @@ -1417,7 +1425,8 @@ static struct rq *find_lock_later_rq(struct task_struct *task, struct rq *rq) later_rq = cpu_rq(cpu); - if (!dl_time_before(task->dl.deadline, + if (later_rq->dl.dl_nr_running && + !dl_time_before(task->dl.deadline, later_rq->dl.earliest_dl.curr)) { /* * Target rq has tasks of equal or earlier deadline, diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 6e2e348..9a5e60f 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2363,7 +2363,7 @@ static inline long calc_tg_weight(struct task_group *tg, struct cfs_rq *cfs_rq) */ tg_weight = atomic_long_read(&tg->load_avg); tg_weight -= cfs_rq->tg_load_avg_contrib; - tg_weight += cfs_rq_load_avg(cfs_rq); + tg_weight += cfs_rq->load.weight; return tg_weight; } @@ -2373,7 +2373,7 @@ static long calc_cfs_shares(struct cfs_rq *cfs_rq, struct task_group *tg) long tg_weight, load, shares; tg_weight = calc_tg_weight(tg, cfs_rq); - load = cfs_rq_load_avg(cfs_rq); + load = cfs_rq->load.weight; shares = (tg->shares * load); if (tg_weight) @@ -2664,13 +2664,14 @@ static inline u64 cfs_rq_clock_task(struct cfs_rq *cfs_rq); /* Group cfs_rq's load_avg is used for task_h_load and update_cfs_share */ static inline int update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq) { - int decayed; struct sched_avg *sa = &cfs_rq->avg; + int decayed, removed = 0; if (atomic_long_read(&cfs_rq->removed_load_avg)) { long r = atomic_long_xchg(&cfs_rq->removed_load_avg, 0); sa->load_avg = max_t(long, sa->load_avg - r, 0); sa->load_sum = max_t(s64, sa->load_sum - r * LOAD_AVG_MAX, 0); + removed = 1; } if (atomic_long_read(&cfs_rq->removed_util_avg)) { @@ -2688,7 +2689,7 @@ static inline int update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq) cfs_rq->load_last_update_time_copy = sa->last_update_time; #endif - return decayed; + return decayed || removed; } /* Update task and its cfs_rq load average */ diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 8f177c7..4a2ef5a 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -57,9 +57,11 @@ static inline int cpu_idle_poll(void) rcu_idle_enter(); trace_cpu_idle_rcuidle(0, smp_processor_id()); local_irq_enable(); + stop_critical_timings(); while (!tif_need_resched() && (cpu_idle_force_poll || tick_check_broadcast_expired())) cpu_relax(); + start_critical_timings(); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id()); rcu_idle_exit(); return 1; diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c index b746399..8abf1ba 100644 --- a/kernel/trace/trace_stack.c +++ b/kernel/trace/trace_stack.c @@ -85,9 +85,19 @@ check_stack(unsigned long ip, unsigned long *stack) if (!object_is_on_stack(stack)) return; + /* Can't do this from NMI context (can cause deadlocks) */ + if (in_nmi()) + return; + local_irq_save(flags); arch_spin_lock(&max_stack_lock); + /* + * RCU may not be watching, make it see us. + * The stack trace code uses rcu_sched. + */ + rcu_irq_enter(); + /* In case another CPU set the tracer_frame on us */ if (unlikely(!frame_size)) this_size -= tracer_frame; @@ -169,6 +179,7 @@ check_stack(unsigned long ip, unsigned long *stack) } out: + rcu_irq_exit(); arch_spin_unlock(&max_stack_lock); local_irq_restore(flags); } diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ab76b99..1d1521c 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -197,6 +197,7 @@ config ENABLE_MUST_CHECK config FRAME_WARN int "Warn for stack frames larger than (needs gcc 4.4)" range 0 8192 + default 0 if KASAN default 1024 if !64BIT default 2048 if 64BIT help diff --git a/lib/fault-inject.c b/lib/fault-inject.c index f1cdeb0..6a823a5 100644 --- a/lib/fault-inject.c +++ b/lib/fault-inject.c @@ -44,7 +44,7 @@ static void fail_dump(struct fault_attr *attr) printk(KERN_NOTICE "FAULT_INJECTION: forcing a failure.\n" "name %pd, interval %lu, probability %lu, " "space %d, times %d\n", attr->dname, - attr->probability, attr->interval, + attr->interval, attr->probability, atomic_read(&attr->space), atomic_read(&attr->times)); if (attr->verbose > 1) diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 2df8ddc..619984f 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -480,6 +480,10 @@ static void cgwb_release_workfn(struct work_struct *work) release_work); struct backing_dev_info *bdi = wb->bdi; + spin_lock_irq(&cgwb_lock); + list_del_rcu(&wb->bdi_node); + spin_unlock_irq(&cgwb_lock); + wb_shutdown(wb); css_put(wb->memcg_css); @@ -575,6 +579,7 @@ static int cgwb_create(struct backing_dev_info *bdi, ret = radix_tree_insert(&bdi->cgwb_tree, memcg_css->id, wb); if (!ret) { atomic_inc(&bdi->usage_cnt); + list_add_tail_rcu(&wb->bdi_node, &bdi->wb_list); list_add(&wb->memcg_node, memcg_cgwb_list); list_add(&wb->blkcg_node, blkcg_cgwb_list); css_get(memcg_css); @@ -676,7 +681,7 @@ static int cgwb_bdi_init(struct backing_dev_info *bdi) static void cgwb_bdi_destroy(struct backing_dev_info *bdi) { struct radix_tree_iter iter; - struct bdi_writeback_congested *congested, *congested_n; + struct rb_node *rbn; void **slot; WARN_ON(test_bit(WB_registered, &bdi->wb.state)); @@ -686,9 +691,11 @@ static void cgwb_bdi_destroy(struct backing_dev_info *bdi) radix_tree_for_each_slot(slot, &bdi->cgwb_tree, &iter, 0) cgwb_kill(*slot); - rbtree_postorder_for_each_entry_safe(congested, congested_n, - &bdi->cgwb_congested_tree, rb_node) { - rb_erase(&congested->rb_node, &bdi->cgwb_congested_tree); + while ((rbn = rb_first(&bdi->cgwb_congested_tree))) { + struct bdi_writeback_congested *congested = + rb_entry(rbn, struct bdi_writeback_congested, rb_node); + + rb_erase(rbn, &bdi->cgwb_congested_tree); congested->bdi = NULL; /* mark @congested unlinked */ } @@ -764,15 +771,22 @@ static void cgwb_bdi_destroy(struct backing_dev_info *bdi) { } int bdi_init(struct backing_dev_info *bdi) { + int ret; + bdi->dev = NULL; bdi->min_ratio = 0; bdi->max_ratio = 100; bdi->max_prop_frac = FPROP_FRAC_BASE; INIT_LIST_HEAD(&bdi->bdi_list); + INIT_LIST_HEAD(&bdi->wb_list); init_waitqueue_head(&bdi->wb_waitq); - return cgwb_bdi_init(bdi); + ret = cgwb_bdi_init(bdi); + + list_add_tail_rcu(&bdi->wb.bdi_node, &bdi->wb_list); + + return ret; } EXPORT_SYMBOL(bdi_init); @@ -823,7 +837,7 @@ static void bdi_remove_from_list(struct backing_dev_info *bdi) synchronize_rcu_expedited(); } -void bdi_destroy(struct backing_dev_info *bdi) +void bdi_unregister(struct backing_dev_info *bdi) { /* make sure nobody finds us on the bdi_list anymore */ bdi_remove_from_list(bdi); @@ -835,9 +849,19 @@ void bdi_destroy(struct backing_dev_info *bdi) device_unregister(bdi->dev); bdi->dev = NULL; } +} +void bdi_exit(struct backing_dev_info *bdi) +{ + WARN_ON_ONCE(bdi->dev); wb_exit(&bdi->wb); } + +void bdi_destroy(struct backing_dev_info *bdi) +{ + bdi_unregister(bdi); + bdi_exit(bdi); +} EXPORT_SYMBOL(bdi_destroy); /* @@ -361,7 +361,7 @@ err: * This function allocates part of contiguous memory on specific * contiguous memory area. */ -struct page *cma_alloc(struct cma *cma, unsigned int count, unsigned int align) +struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align) { unsigned long mask, offset, pfn, start = 0; unsigned long bitmap_maxno, bitmap_no, bitmap_count; @@ -371,7 +371,7 @@ struct page *cma_alloc(struct cma *cma, unsigned int count, unsigned int align) if (!cma || !cma->count) return NULL; - pr_debug("%s(cma %p, count %d, align %d)\n", __func__, (void *)cma, + pr_debug("%s(cma %p, count %zu, align %d)\n", __func__, (void *)cma, count, align); if (!count) diff --git a/mm/filemap.c b/mm/filemap.c index 1cc5467..327910c 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2488,6 +2488,11 @@ again: break; } + if (fatal_signal_pending(current)) { + status = -EINTR; + break; + } + status = a_ops->write_begin(file, mapping, pos, bytes, flags, &page, &fsdata); if (unlikely(status < 0)) @@ -2525,10 +2530,6 @@ again: written += copied; balance_dirty_pages_ratelimited(mapping); - if (fatal_signal_pending(current)) { - status = -EINTR; - break; - } } while (iov_iter_count(i)); return written ? written : status; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 4b06b8d..bbac913 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2206,7 +2206,8 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma, for (_pte = pte; _pte < pte+HPAGE_PMD_NR; _pte++, address += PAGE_SIZE) { pte_t pteval = *_pte; - if (pte_none(pteval) || is_zero_pfn(pte_pfn(pteval))) { + if (pte_none(pteval) || (pte_present(pteval) && + is_zero_pfn(pte_pfn(pteval)))) { if (!userfaultfd_armed(vma) && ++none_or_zero <= khugepaged_max_ptes_none) continue; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index d9b5c81..c57c442 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -3741,44 +3741,43 @@ struct wb_domain *mem_cgroup_wb_domain(struct bdi_writeback *wb) /** * mem_cgroup_wb_stats - retrieve writeback related stats from its memcg * @wb: bdi_writeback in question - * @pavail: out parameter for number of available pages + * @pfilepages: out parameter for number of file pages + * @pheadroom: out parameter for number of allocatable pages according to memcg * @pdirty: out parameter for number of dirty pages * @pwriteback: out parameter for number of pages under writeback * - * Determine the numbers of available, dirty, and writeback pages in @wb's - * memcg. Dirty and writeback are self-explanatory. Available is a bit - * more involved. + * Determine the numbers of file, headroom, dirty, and writeback pages in + * @wb's memcg. File, dirty and writeback are self-explanatory. Headroom + * is a bit more involved. * - * A memcg's headroom is "min(max, high) - used". The available memory is - * calculated as the lowest headroom of itself and the ancestors plus the - * number of pages already being used for file pages. Note that this - * doesn't consider the actual amount of available memory in the system. - * The caller should further cap *@pavail accordingly. + * A memcg's headroom is "min(max, high) - used". In the hierarchy, the + * headroom is calculated as the lowest headroom of itself and the + * ancestors. Note that this doesn't consider the actual amount of + * available memory in the system. The caller should further cap + * *@pheadroom accordingly. */ -void mem_cgroup_wb_stats(struct bdi_writeback *wb, unsigned long *pavail, - unsigned long *pdirty, unsigned long *pwriteback) +void mem_cgroup_wb_stats(struct bdi_writeback *wb, unsigned long *pfilepages, + unsigned long *pheadroom, unsigned long *pdirty, + unsigned long *pwriteback) { struct mem_cgroup *memcg = mem_cgroup_from_css(wb->memcg_css); struct mem_cgroup *parent; - unsigned long head_room = PAGE_COUNTER_MAX; - unsigned long file_pages; *pdirty = mem_cgroup_read_stat(memcg, MEM_CGROUP_STAT_DIRTY); /* this should eventually include NR_UNSTABLE_NFS */ *pwriteback = mem_cgroup_read_stat(memcg, MEM_CGROUP_STAT_WRITEBACK); + *pfilepages = mem_cgroup_nr_lru_pages(memcg, (1 << LRU_INACTIVE_FILE) | + (1 << LRU_ACTIVE_FILE)); + *pheadroom = PAGE_COUNTER_MAX; - file_pages = mem_cgroup_nr_lru_pages(memcg, (1 << LRU_INACTIVE_FILE) | - (1 << LRU_ACTIVE_FILE)); while ((parent = parent_mem_cgroup(memcg))) { unsigned long ceiling = min(memcg->memory.limit, memcg->high); unsigned long used = page_counter_read(&memcg->memory); - head_room = min(head_room, ceiling - min(ceiling, used)); + *pheadroom = min(*pheadroom, ceiling - min(ceiling, used)); memcg = parent; } - - *pavail = file_pages + head_room; } #else /* CONFIG_CGROUP_WRITEBACK */ diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 0a931cd..2c90357 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -145,9 +145,6 @@ struct dirty_throttle_control { unsigned long pos_ratio; }; -#define DTC_INIT_COMMON(__wb) .wb = (__wb), \ - .wb_completions = &(__wb)->completions - /* * Length of period for aging writeout fractions of bdis. This is an * arbitrarily chosen number. The longer the period, the slower fractions will @@ -157,12 +154,16 @@ struct dirty_throttle_control { #ifdef CONFIG_CGROUP_WRITEBACK -#define GDTC_INIT(__wb) .dom = &global_wb_domain, \ - DTC_INIT_COMMON(__wb) +#define GDTC_INIT(__wb) .wb = (__wb), \ + .dom = &global_wb_domain, \ + .wb_completions = &(__wb)->completions + #define GDTC_INIT_NO_WB .dom = &global_wb_domain -#define MDTC_INIT(__wb, __gdtc) .dom = mem_cgroup_wb_domain(__wb), \ - .gdtc = __gdtc, \ - DTC_INIT_COMMON(__wb) + +#define MDTC_INIT(__wb, __gdtc) .wb = (__wb), \ + .dom = mem_cgroup_wb_domain(__wb), \ + .wb_completions = &(__wb)->memcg_completions, \ + .gdtc = __gdtc static bool mdtc_valid(struct dirty_throttle_control *dtc) { @@ -213,7 +214,8 @@ static void wb_min_max_ratio(struct bdi_writeback *wb, #else /* CONFIG_CGROUP_WRITEBACK */ -#define GDTC_INIT(__wb) DTC_INIT_COMMON(__wb) +#define GDTC_INIT(__wb) .wb = (__wb), \ + .wb_completions = &(__wb)->completions #define GDTC_INIT_NO_WB #define MDTC_INIT(__wb, __gdtc) @@ -682,13 +684,19 @@ static unsigned long hard_dirty_limit(struct wb_domain *dom, return max(thresh, dom->dirty_limit); } -/* memory available to a memcg domain is capped by system-wide clean memory */ -static void mdtc_cap_avail(struct dirty_throttle_control *mdtc) +/* + * Memory which can be further allocated to a memcg domain is capped by + * system-wide clean memory excluding the amount being used in the domain. + */ +static void mdtc_calc_avail(struct dirty_throttle_control *mdtc, + unsigned long filepages, unsigned long headroom) { struct dirty_throttle_control *gdtc = mdtc_gdtc(mdtc); - unsigned long clean = gdtc->avail - min(gdtc->avail, gdtc->dirty); + unsigned long clean = filepages - min(filepages, mdtc->dirty); + unsigned long global_clean = gdtc->avail - min(gdtc->avail, gdtc->dirty); + unsigned long other_clean = global_clean - min(global_clean, clean); - mdtc->avail = min(mdtc->avail, clean); + mdtc->avail = filepages + min(headroom, other_clean); } /** @@ -1562,16 +1570,16 @@ static void balance_dirty_pages(struct address_space *mapping, } if (mdtc) { - unsigned long writeback; + unsigned long filepages, headroom, writeback; /* * If @wb belongs to !root memcg, repeat the same * basic calculations for the memcg domain. */ - mem_cgroup_wb_stats(wb, &mdtc->avail, &mdtc->dirty, - &writeback); - mdtc_cap_avail(mdtc); + mem_cgroup_wb_stats(wb, &filepages, &headroom, + &mdtc->dirty, &writeback); mdtc->dirty += writeback; + mdtc_calc_avail(mdtc, filepages, headroom); domain_dirty_limits(mdtc); @@ -1893,10 +1901,11 @@ bool wb_over_bg_thresh(struct bdi_writeback *wb) return true; if (mdtc) { - unsigned long writeback; + unsigned long filepages, headroom, writeback; - mem_cgroup_wb_stats(wb, &mdtc->avail, &mdtc->dirty, &writeback); - mdtc_cap_avail(mdtc); + mem_cgroup_wb_stats(wb, &filepages, &headroom, &mdtc->dirty, + &writeback); + mdtc_calc_avail(mdtc, filepages, headroom); domain_dirty_limits(mdtc); /* ditto, ignore writeback */ if (mdtc->dirty > mdtc->bg_thresh) @@ -1956,7 +1965,6 @@ void laptop_mode_timer_fn(unsigned long data) int nr_pages = global_page_state(NR_FILE_DIRTY) + global_page_state(NR_UNSTABLE_NFS); struct bdi_writeback *wb; - struct wb_iter iter; /* * We want to write everything out, not just down to the dirty @@ -1965,10 +1973,12 @@ void laptop_mode_timer_fn(unsigned long data) if (!bdi_has_dirty_io(&q->backing_dev_info)) return; - bdi_for_each_wb(wb, &q->backing_dev_info, &iter, 0) + rcu_read_lock(); + list_for_each_entry_rcu(wb, &q->backing_dev_info.wb_list, bdi_node) if (wb_has_dirty_io(wb)) wb_start_writeback(wb, nr_pages, true, WB_REASON_LAPTOP_TIMER); + rcu_read_unlock(); } /* diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index b4548c73..2dda439 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -91,10 +91,50 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn) * autoconnect action, remove them completely. If they are, just unmark * them as waiting for connection, by clearing explicit_connect field. */ - if (params->auto_connect == HCI_AUTO_CONN_EXPLICIT) + params->explicit_connect = false; + + list_del_init(¶ms->action); + + switch (params->auto_connect) { + case HCI_AUTO_CONN_EXPLICIT: hci_conn_params_del(conn->hdev, bdaddr, bdaddr_type); - else - params->explicit_connect = false; + /* return instead of break to avoid duplicate scan update */ + return; + case HCI_AUTO_CONN_DIRECT: + case HCI_AUTO_CONN_ALWAYS: + list_add(¶ms->action, &conn->hdev->pend_le_conns); + break; + case HCI_AUTO_CONN_REPORT: + list_add(¶ms->action, &conn->hdev->pend_le_reports); + break; + default: + break; + } + + hci_update_background_scan(conn->hdev); +} + +static void hci_conn_cleanup(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + + if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags)) + hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type); + + hci_chan_list_flush(conn); + + hci_conn_hash_del(hdev, conn); + + if (hdev->notify) + hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); + + hci_conn_del_sysfs(conn); + + debugfs_remove_recursive(conn->debugfs); + + hci_dev_put(hdev); + + hci_conn_put(conn); } /* This function requires the caller holds hdev->lock */ @@ -102,8 +142,13 @@ static void hci_connect_le_scan_remove(struct hci_conn *conn) { hci_connect_le_scan_cleanup(conn); - hci_conn_hash_del(conn->hdev, conn); - hci_update_background_scan(conn->hdev); + /* We can't call hci_conn_del here since that would deadlock + * with trying to call cancel_delayed_work_sync(&conn->disc_work). + * Instead, call just hci_conn_cleanup() which contains the bare + * minimum cleanup operations needed for a connection in this + * state. + */ + hci_conn_cleanup(conn); } static void hci_acl_create_connection(struct hci_conn *conn) @@ -581,27 +626,17 @@ int hci_conn_del(struct hci_conn *conn) } } - hci_chan_list_flush(conn); - if (conn->amp_mgr) amp_mgr_put(conn->amp_mgr); - hci_conn_hash_del(hdev, conn); - if (hdev->notify) - hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); - skb_queue_purge(&conn->data_q); - hci_conn_del_sysfs(conn); - - debugfs_remove_recursive(conn->debugfs); - - if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags)) - hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type); - - hci_dev_put(hdev); - - hci_conn_put(conn); + /* Remove the connection from the list and cleanup its remaining + * state. This is a separate function since for some cases like + * BT_CONNECT_SCAN we *only* want the cleanup part without the + * rest of hci_conn_del. + */ + hci_conn_cleanup(conn); return 0; } @@ -973,15 +1008,23 @@ static int hci_explicit_conn_params_set(struct hci_request *req, if (is_connected(hdev, addr, addr_type)) return -EISCONN; - params = hci_conn_params_add(hdev, addr, addr_type); - if (!params) - return -EIO; + params = hci_conn_params_lookup(hdev, addr, addr_type); + if (!params) { + params = hci_conn_params_add(hdev, addr, addr_type); + if (!params) + return -ENOMEM; - /* If we created new params, or existing params were marked as disabled, - * mark them to be used just once to connect. - */ - if (params->auto_connect == HCI_AUTO_CONN_DISABLED) { + /* If we created new params, mark them to be deleted in + * hci_connect_le_scan_cleanup. It's different case than + * existing disabled params, those will stay after cleanup. + */ params->auto_connect = HCI_AUTO_CONN_EXPLICIT; + } + + /* We're trying to connect, so make sure params are at pend_le_conns */ + if (params->auto_connect == HCI_AUTO_CONN_DISABLED || + params->auto_connect == HCI_AUTO_CONN_REPORT || + params->auto_connect == HCI_AUTO_CONN_EXPLICIT) { list_del_init(¶ms->action); list_add(¶ms->action, &hdev->pend_le_conns); } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index adcbc74..e837539 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2861,13 +2861,6 @@ struct hci_conn_params *hci_explicit_connect_lookup(struct hci_dev *hdev, return param; } - list_for_each_entry(param, &hdev->pend_le_reports, action) { - if (bacmp(¶m->addr, addr) == 0 && - param->addr_type == addr_type && - param->explicit_connect) - return param; - } - return NULL; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 1860418..bc31099 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -55,7 +55,12 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) wake_up_bit(&hdev->flags, HCI_INQUIRY); hci_dev_lock(hdev); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + /* Set discovery state to stopped if we're not doing LE active + * scanning. + */ + if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) || + hdev->le_scan_type != LE_SCAN_ACTIVE) + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); hci_dev_unlock(hdev); hci_conn_check_pending(hdev); @@ -4648,8 +4653,8 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev, /* If we're not connectable only connect devices that we have in * our pend_le_conns list. */ - params = hci_explicit_connect_lookup(hdev, addr, addr_type); - + params = hci_pend_le_action_lookup(&hdev->pend_le_conns, addr, + addr_type); if (!params) return NULL; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ccaf5a4..c4fe2fe 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3545,6 +3545,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, auth_type); } else { u8 addr_type; + struct hci_conn_params *p; /* Convert from L2CAP channel address type to HCI address type */ @@ -3562,7 +3563,10 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, * If connection parameters already exist, then they * will be kept and this function does nothing. */ - hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type); + p = hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type); + + if (p->auto_connect == HCI_AUTO_CONN_EXPLICIT) + p->auto_connect = HCI_AUTO_CONN_DISABLED; conn = hci_connect_le_scan(hdev, &cp->addr.bdaddr, addr_type, sec_level, @@ -6117,14 +6121,21 @@ static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr, __hci_update_background_scan(req); break; case HCI_AUTO_CONN_REPORT: - list_add(¶ms->action, &hdev->pend_le_reports); + if (params->explicit_connect) + list_add(¶ms->action, &hdev->pend_le_conns); + else + list_add(¶ms->action, &hdev->pend_le_reports); __hci_update_background_scan(req); break; case HCI_AUTO_CONN_DIRECT: case HCI_AUTO_CONN_ALWAYS: if (!is_connected(hdev, addr, addr_type)) { list_add(¶ms->action, &hdev->pend_le_conns); - __hci_update_background_scan(req); + /* If we are in scan phase of connecting, we were + * already added to pend_le_conns and scanning. + */ + if (params->auto_connect != HCI_AUTO_CONN_EXPLICIT) + __hci_update_background_scan(req); } break; } @@ -6379,7 +6390,8 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } - if (params->auto_connect == HCI_AUTO_CONN_DISABLED) { + if (params->auto_connect == HCI_AUTO_CONN_DISABLED || + params->auto_connect == HCI_AUTO_CONN_EXPLICIT) { err = cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); mgmt_pending_remove(cmd); @@ -6415,6 +6427,10 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, if (p->auto_connect == HCI_AUTO_CONN_DISABLED) continue; device_removed(sk, hdev, &p->addr, p->addr_type); + if (p->explicit_connect) { + p->auto_connect = HCI_AUTO_CONN_EXPLICIT; + continue; + } list_del(&p->action); list_del(&p->list); kfree(p); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index b495ab1..29edf74 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -1284,7 +1284,7 @@ static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) gstrings.len = ret; - data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER); + data = kcalloc(gstrings.len, ETH_GSTRING_LEN, GFP_USER); if (!data) return -ENOMEM; diff --git a/net/core/filter.c b/net/core/filter.c index 05a04ea..bb18c36 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1415,6 +1415,7 @@ static u64 bpf_clone_redirect(u64 r1, u64 ifindex, u64 flags, u64 r4, u64 r5) return dev_forward_skb(dev, skb2); skb2->dev = dev; + skb_sender_cpu_clear(skb2); return dev_queue_xmit(skb2); } @@ -1854,9 +1855,13 @@ int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, goto out; /* We're copying the filter that has been originally attached, - * so no conversion/decode needed anymore. + * so no conversion/decode needed anymore. eBPF programs that + * have no original program cannot be dumped through this. */ + ret = -EACCES; fprog = filter->prog->orig_prog; + if (!fprog) + goto out; ret = fprog->len; if (!len) diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index c59fa5d..adb5325 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -22,6 +22,7 @@ #include <linux/of_platform.h> #include <linux/of_net.h> #include <linux/sysfs.h> +#include <linux/phy_fixed.h> #include "dsa_priv.h" char dsa_driver_version[] = "0.1"; @@ -305,7 +306,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) if (ret < 0) goto out; - ds->slave_mii_bus = mdiobus_alloc(); + ds->slave_mii_bus = devm_mdiobus_alloc(parent); if (ds->slave_mii_bus == NULL) { ret = -ENOMEM; goto out; @@ -314,7 +315,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) ret = mdiobus_register(ds->slave_mii_bus); if (ret < 0) - goto out_free; + goto out; /* @@ -367,10 +368,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) return ret; -out_free: - mdiobus_free(ds->slave_mii_bus); out: - kfree(ds); return ret; } @@ -400,7 +398,7 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, /* * Allocate and initialise switch state. */ - ds = kzalloc(sizeof(*ds) + drv->priv_size, GFP_KERNEL); + ds = devm_kzalloc(parent, sizeof(*ds) + drv->priv_size, GFP_KERNEL); if (ds == NULL) return ERR_PTR(-ENOMEM); @@ -420,10 +418,47 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, static void dsa_switch_destroy(struct dsa_switch *ds) { + struct device_node *port_dn; + struct phy_device *phydev; + struct dsa_chip_data *cd = ds->pd; + int port; + #ifdef CONFIG_NET_DSA_HWMON if (ds->hwmon_dev) hwmon_device_unregister(ds->hwmon_dev); #endif + + /* Disable configuration of the CPU and DSA ports */ + for (port = 0; port < DSA_MAX_PORTS; port++) { + if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) + continue; + + port_dn = cd->port_dn[port]; + if (of_phy_is_fixed_link(port_dn)) { + phydev = of_phy_find_device(port_dn); + if (phydev) { + int addr = phydev->addr; + + phy_device_free(phydev); + of_node_put(port_dn); + fixed_phy_del(addr); + } + } + } + + /* Destroy network devices for physical switch ports. */ + for (port = 0; port < DSA_MAX_PORTS; port++) { + if (!(ds->phys_port_mask & (1 << port))) + continue; + + if (!ds->ports[port]) + continue; + + unregister_netdev(ds->ports[port]); + free_netdev(ds->ports[port]); + } + + mdiobus_unregister(ds->slave_mii_bus); } #ifdef CONFIG_PM_SLEEP @@ -802,10 +837,11 @@ static inline void dsa_of_remove(struct device *dev) } #endif -static void dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, - struct device *parent, struct dsa_platform_data *pd) +static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, + struct device *parent, struct dsa_platform_data *pd) { int i; + unsigned configured = 0; dst->pd = pd; dst->master_netdev = dev; @@ -825,9 +861,17 @@ static void dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, dst->ds[i] = ds; if (ds->drv->poll_link != NULL) dst->link_poll_needed = 1; + + ++configured; } /* + * If no switch was found, exit cleanly + */ + if (!configured) + return -EPROBE_DEFER; + + /* * If we use a tagging format that doesn't have an ethertype * field, make sure that all packets from this point on get * sent to the tag format's receive function. @@ -843,6 +887,8 @@ static void dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, dst->link_poll_timer.expires = round_jiffies(jiffies + HZ); add_timer(&dst->link_poll_timer); } + + return 0; } static int dsa_probe(struct platform_device *pdev) @@ -883,7 +929,7 @@ static int dsa_probe(struct platform_device *pdev) goto out; } - dst = kzalloc(sizeof(*dst), GFP_KERNEL); + dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); if (dst == NULL) { dev_put(dev); ret = -ENOMEM; @@ -892,7 +938,9 @@ static int dsa_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dst); - dsa_setup_dst(dst, dev, &pdev->dev, pd); + ret = dsa_setup_dst(dst, dev, &pdev->dev, pd); + if (ret) + goto out; return 0; @@ -914,7 +962,7 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst) for (i = 0; i < dst->pd->nr_chips; i++) { struct dsa_switch *ds = dst->ds[i]; - if (ds != NULL) + if (ds) dsa_switch_destroy(ds); } } diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index f03db8b..0c9c348 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -312,7 +312,7 @@ static void arp_send_dst(int type, int ptype, __be32 dest_ip, if (!skb) return; - skb_dst_set(skb, dst); + skb_dst_set(skb, dst_clone(dst)); arp_xmit(skb); } @@ -384,7 +384,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb) } if (skb && !(dev->priv_flags & IFF_XMIT_DST_RELEASE)) - dst = dst_clone(skb_dst(skb)); + dst = skb_dst(skb); arp_send_dst(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr, dst_hw, dev->dev_addr, NULL, dst); } @@ -811,7 +811,7 @@ static int arp_process(struct sock *sk, struct sk_buff *skb) } else { pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb); - return 0; + goto out_free_dst; } goto out; } @@ -865,6 +865,8 @@ static int arp_process(struct sock *sk, struct sk_buff *skb) out: consume_skb(skb); +out_free_dst: + dst_release(reply_dst); return 0; } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7bb9c39..61b45a1 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -577,21 +577,22 @@ EXPORT_SYMBOL(inet_rtx_syn_ack); static bool reqsk_queue_unlink(struct request_sock_queue *queue, struct request_sock *req) { - struct listen_sock *lopt = queue->listen_opt; struct request_sock **prev; + struct listen_sock *lopt; bool found = false; spin_lock(&queue->syn_wait_lock); - - for (prev = &lopt->syn_table[req->rsk_hash]; *prev != NULL; - prev = &(*prev)->dl_next) { - if (*prev == req) { - *prev = req->dl_next; - found = true; - break; + lopt = queue->listen_opt; + if (lopt) { + for (prev = &lopt->syn_table[req->rsk_hash]; *prev != NULL; + prev = &(*prev)->dl_next) { + if (*prev == req) { + *prev = req->dl_next; + found = true; + break; + } } } - spin_unlock(&queue->syn_wait_lock); if (timer_pending(&req->rsk_timer) && del_timer_sync(&req->rsk_timer)) reqsk_put(req); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 9001133..36b85bd 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3119,6 +3119,8 @@ static void addrconf_gre_config(struct net_device *dev) } addrconf_addr_gen(idev, true); + if (dev->flags & IFF_POINTOPOINT) + addrconf_add_mroute(dev); } #endif diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 92b1aa3..61d403e 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -376,6 +376,9 @@ int ip6_forward(struct sk_buff *skb) if (skb->pkt_type != PACKET_HOST) goto drop; + if (unlikely(skb->sk)) + goto drop; + if (skb_warn_if_lro(skb)) goto drop; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index cb32ce2..968f31c 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -142,6 +142,9 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) struct net_device *loopback_dev = net->loopback_dev; int cpu; + if (dev == loopback_dev) + return; + for_each_possible_cpu(cpu) { struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); struct rt6_info *rt; @@ -151,14 +154,12 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) struct inet6_dev *rt_idev = rt->rt6i_idev; struct net_device *rt_dev = rt->dst.dev; - if (rt_idev && (rt_idev->dev == dev || !dev) && - rt_idev->dev != loopback_dev) { + if (rt_idev->dev == dev) { rt->rt6i_idev = in6_dev_get(loopback_dev); in6_dev_put(rt_idev); } - if (rt_dev && (rt_dev == dev || !dev) && - rt_dev != loopback_dev) { + if (rt_dev == dev) { rt->dst.dev = loopback_dev; dev_hold(rt->dst.dev); dev_put(rt_dev); @@ -247,12 +248,6 @@ static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, { } -static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, - unsigned long old) -{ - return NULL; -} - static struct dst_ops ip6_dst_blackhole_ops = { .family = AF_INET6, .destroy = ip6_dst_destroy, @@ -261,7 +256,7 @@ static struct dst_ops ip6_dst_blackhole_ops = { .default_advmss = ip6_default_advmss, .update_pmtu = ip6_rt_blackhole_update_pmtu, .redirect = ip6_rt_blackhole_redirect, - .cow_metrics = ip6_rt_blackhole_cow_metrics, + .cow_metrics = dst_cow_metrics_generic, .neigh_lookup = ip6_neigh_lookup, }; @@ -318,6 +313,15 @@ static const struct rt6_info ip6_blk_hole_entry_template = { #endif +static void rt6_info_init(struct rt6_info *rt) +{ + struct dst_entry *dst = &rt->dst; + + memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); + INIT_LIST_HEAD(&rt->rt6i_siblings); + INIT_LIST_HEAD(&rt->rt6i_uncached); +} + /* allocate dst with ip6_dst_ops */ static struct rt6_info *__ip6_dst_alloc(struct net *net, struct net_device *dev, @@ -326,13 +330,9 @@ static struct rt6_info *__ip6_dst_alloc(struct net *net, struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 0, DST_OBSOLETE_FORCE_CHK, flags); - if (rt) { - struct dst_entry *dst = &rt->dst; + if (rt) + rt6_info_init(rt); - memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); - INIT_LIST_HEAD(&rt->rt6i_siblings); - INIT_LIST_HEAD(&rt->rt6i_uncached); - } return rt; } @@ -1213,24 +1213,20 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); if (rt) { - new = &rt->dst; - - memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); + rt6_info_init(rt); + new = &rt->dst; new->__use = 1; new->input = dst_discard; new->output = dst_discard_sk; - if (dst_metrics_read_only(&ort->dst)) - new->_metrics = ort->dst._metrics; - else - dst_copy_metrics(new, &ort->dst); + dst_copy_metrics(new, &ort->dst); rt->rt6i_idev = ort->rt6i_idev; if (rt->rt6i_idev) in6_dev_hold(rt->rt6i_idev); rt->rt6i_gateway = ort->rt6i_gateway; - rt->rt6i_flags = ort->rt6i_flags; + rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU; rt->rt6i_metric = 0; memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); @@ -2622,7 +2618,8 @@ void rt6_ifdown(struct net *net, struct net_device *dev) fib6_clean_all(net, fib6_ifdown, &adn); icmp6_clean_all(fib6_ifdown, &adn); - rt6_uncached_list_flush_dev(net, dev); + if (dev) + rt6_uncached_list_flush_dev(net, dev); } struct rt6_mtu_change_arg { diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 30caa28..5cedfda 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -37,6 +37,7 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_oif = oif; + fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF; memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr)); if (saddr) memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr)); diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index ced6bf3..1560c84 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -149,7 +149,7 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, for (i = 0; i < NUM_IEEE80211_HW_FLAGS; i++) { if (test_bit(i, local->hw.flags)) - pos += scnprintf(pos, end - pos, "%s", + pos += scnprintf(pos, end - pos, "%s\n", hw_flag_names[i]); } diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 8ba5832..3ed7ddf 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -101,6 +101,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, * when it wakes up for the next time. */ set_sta_flag(sta, WLAN_STA_CLEAR_PS_FILT); + ieee80211_clear_fast_xmit(sta); /* * This code races in the following way: diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 84e0e8c..7892eb8 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1218,8 +1218,10 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, if (!tx->sta) info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; - else if (test_and_clear_sta_flag(tx->sta, WLAN_STA_CLEAR_PS_FILT)) + else if (test_and_clear_sta_flag(tx->sta, WLAN_STA_CLEAR_PS_FILT)) { info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; + ieee80211_check_fast_xmit(tx->sta); + } info->flags |= IEEE80211_TX_CTL_FIRST_FRAGMENT; @@ -2451,7 +2453,8 @@ void ieee80211_check_fast_xmit(struct sta_info *sta) if (test_sta_flag(sta, WLAN_STA_PS_STA) || test_sta_flag(sta, WLAN_STA_PS_DRIVER) || - test_sta_flag(sta, WLAN_STA_PS_DELIVER)) + test_sta_flag(sta, WLAN_STA_PS_DELIVER) || + test_sta_flag(sta, WLAN_STA_CLEAR_PS_FILT)) goto out; if (sdata->noack_map) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 8f060d7..0a49a8c 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2785,6 +2785,7 @@ static int netlink_dump(struct sock *sk) struct sk_buff *skb = NULL; struct nlmsghdr *nlh; int len, err = -ENOBUFS; + int alloc_min_size; int alloc_size; mutex_lock(nlk->cb_mutex); @@ -2793,9 +2794,6 @@ static int netlink_dump(struct sock *sk) goto errout_skb; } - cb = &nlk->cb; - alloc_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE); - if (!netlink_rx_is_mmaped(sk) && atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) goto errout_skb; @@ -2805,23 +2803,35 @@ static int netlink_dump(struct sock *sk) * to reduce number of system calls on dump operations, if user * ever provided a big enough buffer. */ - if (alloc_size < nlk->max_recvmsg_len) { - skb = netlink_alloc_skb(sk, - nlk->max_recvmsg_len, - nlk->portid, + cb = &nlk->cb; + alloc_min_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE); + + if (alloc_min_size < nlk->max_recvmsg_len) { + alloc_size = nlk->max_recvmsg_len; + skb = netlink_alloc_skb(sk, alloc_size, nlk->portid, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); - /* available room should be exact amount to avoid MSG_TRUNC */ - if (skb) - skb_reserve(skb, skb_tailroom(skb) - - nlk->max_recvmsg_len); } - if (!skb) + if (!skb) { + alloc_size = alloc_min_size; skb = netlink_alloc_skb(sk, alloc_size, nlk->portid, GFP_KERNEL); + } if (!skb) goto errout_skb; + + /* Trim skb to allocated size. User is expected to provide buffer as + * large as max(min_dump_alloc, 16KiB (mac_recvmsg_len capped at + * netlink_recvmsg())). dump will pack as many smaller messages as + * could fit within the allocated skb. skb is typically allocated + * with larger space than required (could be as much as near 2x the + * requested size with align to next power of 2 approach). Allowing + * dump to use the excess space makes it difficult for a user to have a + * reasonable static buffer based on the expected largest dump of a + * single netdev. The outcome is MSG_TRUNC error. + */ + skb_reserve(skb, skb_tailroom(skb) - alloc_size); netlink_skb_set_owner_r(skb, sk); len = cb->dump(skb, cb); diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 315f533..c6a39bf 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -684,7 +684,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru, { if (skb_network_offset(skb) > MAX_L2_LEN) { OVS_NLERR(1, "L2 header too long to fragment"); - return; + goto err; } if (ethertype == htons(ETH_P_IP)) { @@ -708,8 +708,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru, struct rt6_info ovs_rt; if (!v6ops) { - kfree_skb(skb); - return; + goto err; } prepare_frag(vport, skb); @@ -728,8 +727,12 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru, WARN_ONCE(1, "Failed fragment ->%s: eth=%04x, MRU=%d, MTU=%d.", ovs_vport_name(vport), ntohs(ethertype), mru, vport->dev->mtu); - kfree_skb(skb); + goto err; } + + return; +err: + kfree_skb(skb); } static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, @@ -968,7 +971,7 @@ static int execute_masked_set_action(struct sk_buff *skb, case OVS_KEY_ATTR_CT_STATE: case OVS_KEY_ATTR_CT_ZONE: case OVS_KEY_ATTR_CT_MARK: - case OVS_KEY_ATTR_CT_LABEL: + case OVS_KEY_ATTR_CT_LABELS: err = -EINVAL; break; } @@ -1099,6 +1102,12 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, break; case OVS_ACTION_ATTR_CT: + if (!is_flow_key_valid(key)) { + err = ovs_flow_key_update(skb, key); + if (err) + return err; + } + err = ovs_ct_execute(ovs_dp_get_net(dp), skb, key, nla_data(a)); diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 002a755..80bf702 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -37,9 +37,9 @@ struct md_mark { }; /* Metadata label for masked write to conntrack label. */ -struct md_label { - struct ovs_key_ct_label value; - struct ovs_key_ct_label mask; +struct md_labels { + struct ovs_key_ct_labels value; + struct ovs_key_ct_labels mask; }; /* Conntrack action context for execution. */ @@ -47,10 +47,10 @@ struct ovs_conntrack_info { struct nf_conntrack_helper *helper; struct nf_conntrack_zone zone; struct nf_conn *ct; - u32 flags; + u8 commit : 1; u16 family; struct md_mark mark; - struct md_label label; + struct md_labels labels; }; static u16 key_to_nfproto(const struct sw_flow_key *key) @@ -109,21 +109,21 @@ static u32 ovs_ct_get_mark(const struct nf_conn *ct) #endif } -static void ovs_ct_get_label(const struct nf_conn *ct, - struct ovs_key_ct_label *label) +static void ovs_ct_get_labels(const struct nf_conn *ct, + struct ovs_key_ct_labels *labels) { struct nf_conn_labels *cl = ct ? nf_ct_labels_find(ct) : NULL; if (cl) { size_t len = cl->words * sizeof(long); - if (len > OVS_CT_LABEL_LEN) - len = OVS_CT_LABEL_LEN; - else if (len < OVS_CT_LABEL_LEN) - memset(label, 0, OVS_CT_LABEL_LEN); - memcpy(label, cl->bits, len); + if (len > OVS_CT_LABELS_LEN) + len = OVS_CT_LABELS_LEN; + else if (len < OVS_CT_LABELS_LEN) + memset(labels, 0, OVS_CT_LABELS_LEN); + memcpy(labels, cl->bits, len); } else { - memset(label, 0, OVS_CT_LABEL_LEN); + memset(labels, 0, OVS_CT_LABELS_LEN); } } @@ -134,7 +134,7 @@ static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state, key->ct.state = state; key->ct.zone = zone->id; key->ct.mark = ovs_ct_get_mark(ct); - ovs_ct_get_label(ct, &key->ct.label); + ovs_ct_get_labels(ct, &key->ct.labels); } /* Update 'key' based on skb->nfct. If 'post_ct' is true, then OVS has @@ -167,7 +167,7 @@ void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key) int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb) { - if (nla_put_u8(skb, OVS_KEY_ATTR_CT_STATE, key->ct.state)) + if (nla_put_u32(skb, OVS_KEY_ATTR_CT_STATE, key->ct.state)) return -EMSGSIZE; if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && @@ -179,8 +179,8 @@ int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb) return -EMSGSIZE; if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && - nla_put(skb, OVS_KEY_ATTR_CT_LABEL, sizeof(key->ct.label), - &key->ct.label)) + nla_put(skb, OVS_KEY_ATTR_CT_LABELS, sizeof(key->ct.labels), + &key->ct.labels)) return -EMSGSIZE; return 0; @@ -213,9 +213,9 @@ static int ovs_ct_set_mark(struct sk_buff *skb, struct sw_flow_key *key, #endif } -static int ovs_ct_set_label(struct sk_buff *skb, struct sw_flow_key *key, - const struct ovs_key_ct_label *label, - const struct ovs_key_ct_label *mask) +static int ovs_ct_set_labels(struct sk_buff *skb, struct sw_flow_key *key, + const struct ovs_key_ct_labels *labels, + const struct ovs_key_ct_labels *mask) { enum ip_conntrack_info ctinfo; struct nf_conn_labels *cl; @@ -235,15 +235,15 @@ static int ovs_ct_set_label(struct sk_buff *skb, struct sw_flow_key *key, nf_ct_labels_ext_add(ct); cl = nf_ct_labels_find(ct); } - if (!cl || cl->words * sizeof(long) < OVS_CT_LABEL_LEN) + if (!cl || cl->words * sizeof(long) < OVS_CT_LABELS_LEN) return -ENOSPC; - err = nf_connlabels_replace(ct, (u32 *)label, (u32 *)mask, - OVS_CT_LABEL_LEN / sizeof(u32)); + err = nf_connlabels_replace(ct, (u32 *)labels, (u32 *)mask, + OVS_CT_LABELS_LEN / sizeof(u32)); if (err) return err; - ovs_ct_get_label(ct, &key->ct.label); + ovs_ct_get_labels(ct, &key->ct.labels); return 0; } @@ -465,12 +465,12 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key, return 0; } -static bool label_nonzero(const struct ovs_key_ct_label *label) +static bool labels_nonzero(const struct ovs_key_ct_labels *labels) { size_t i; - for (i = 0; i < sizeof(*label); i++) - if (label->ct_label[i]) + for (i = 0; i < sizeof(*labels); i++) + if (labels->ct_labels[i]) return true; return false; @@ -493,7 +493,7 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb, return err; } - if (info->flags & OVS_CT_F_COMMIT) + if (info->commit) err = ovs_ct_commit(net, key, info, skb); else err = ovs_ct_lookup(net, key, info, skb); @@ -506,9 +506,9 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb, if (err) goto err; } - if (label_nonzero(&info->label.mask)) - err = ovs_ct_set_label(skb, key, &info->label.value, - &info->label.mask); + if (labels_nonzero(&info->labels.mask)) + err = ovs_ct_set_labels(skb, key, &info->labels.value, + &info->labels.mask); err: skb_push(skb, nh_ofs); return err; @@ -539,14 +539,13 @@ static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name, } static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = { - [OVS_CT_ATTR_FLAGS] = { .minlen = sizeof(u32), - .maxlen = sizeof(u32) }, + [OVS_CT_ATTR_COMMIT] = { .minlen = 0, .maxlen = 0 }, [OVS_CT_ATTR_ZONE] = { .minlen = sizeof(u16), .maxlen = sizeof(u16) }, [OVS_CT_ATTR_MARK] = { .minlen = sizeof(struct md_mark), .maxlen = sizeof(struct md_mark) }, - [OVS_CT_ATTR_LABEL] = { .minlen = sizeof(struct md_label), - .maxlen = sizeof(struct md_label) }, + [OVS_CT_ATTR_LABELS] = { .minlen = sizeof(struct md_labels), + .maxlen = sizeof(struct md_labels) }, [OVS_CT_ATTR_HELPER] = { .minlen = 1, .maxlen = NF_CT_HELPER_NAME_LEN } }; @@ -576,8 +575,8 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info, } switch (type) { - case OVS_CT_ATTR_FLAGS: - info->flags = nla_get_u32(a); + case OVS_CT_ATTR_COMMIT: + info->commit = true; break; #ifdef CONFIG_NF_CONNTRACK_ZONES case OVS_CT_ATTR_ZONE: @@ -593,10 +592,10 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info, } #endif #ifdef CONFIG_NF_CONNTRACK_LABELS - case OVS_CT_ATTR_LABEL: { - struct md_label *label = nla_data(a); + case OVS_CT_ATTR_LABELS: { + struct md_labels *labels = nla_data(a); - info->label = *label; + info->labels = *labels; break; } #endif @@ -633,7 +632,7 @@ bool ovs_ct_verify(struct net *net, enum ovs_key_attr attr) attr == OVS_KEY_ATTR_CT_MARK) return true; if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && - attr == OVS_KEY_ATTR_CT_LABEL) { + attr == OVS_KEY_ATTR_CT_LABELS) { struct ovs_net *ovs_net = net_generic(net, ovs_net_id); return ovs_net->xt_label; @@ -701,7 +700,7 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info, if (!start) return -EMSGSIZE; - if (nla_put_u32(skb, OVS_CT_ATTR_FLAGS, ct_info->flags)) + if (ct_info->commit && nla_put_flag(skb, OVS_CT_ATTR_COMMIT)) return -EMSGSIZE; if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && nla_put_u16(skb, OVS_CT_ATTR_ZONE, ct_info->zone.id)) @@ -711,8 +710,8 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info, &ct_info->mark)) return -EMSGSIZE; if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && - nla_put(skb, OVS_CT_ATTR_LABEL, sizeof(ct_info->label), - &ct_info->label)) + nla_put(skb, OVS_CT_ATTR_LABELS, sizeof(ct_info->labels), + &ct_info->labels)) return -EMSGSIZE; if (ct_info->helper) { if (nla_put_string(skb, OVS_CT_ATTR_HELPER, @@ -737,7 +736,7 @@ void ovs_ct_free_action(const struct nlattr *a) void ovs_ct_init(struct net *net) { - unsigned int n_bits = sizeof(struct ovs_key_ct_label) * BITS_PER_BYTE; + unsigned int n_bits = sizeof(struct ovs_key_ct_labels) * BITS_PER_BYTE; struct ovs_net *ovs_net = net_generic(net, ovs_net_id); if (nf_connlabels_get(net, n_bits)) { diff --git a/net/openvswitch/conntrack.h b/net/openvswitch/conntrack.h index 43f5dd7..da87149 100644 --- a/net/openvswitch/conntrack.h +++ b/net/openvswitch/conntrack.h @@ -34,6 +34,13 @@ int ovs_ct_execute(struct net *, struct sk_buff *, struct sw_flow_key *, void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key); int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb); void ovs_ct_free_action(const struct nlattr *a); + +static inline bool ovs_ct_state_supported(u32 state) +{ + return !(state & ~(OVS_CS_F_NEW | OVS_CS_F_ESTABLISHED | + OVS_CS_F_RELATED | OVS_CS_F_REPLY_DIR | + OVS_CS_F_INVALID | OVS_CS_F_TRACKED)); +} #else #include <linux/errno.h> @@ -46,6 +53,11 @@ static inline bool ovs_ct_verify(struct net *net, int attr) return false; } +static inline bool ovs_ct_state_supported(u32 state) +{ + return false; +} + static inline int ovs_ct_copy_action(struct net *net, const struct nlattr *nla, const struct sw_flow_key *key, struct sw_flow_actions **acts, bool log) @@ -72,7 +84,7 @@ static inline void ovs_ct_fill_key(const struct sk_buff *skb, key->ct.state = 0; key->ct.zone = 0; key->ct.mark = 0; - memset(&key->ct.label, 0, sizeof(key->ct.label)); + memset(&key->ct.labels, 0, sizeof(key->ct.labels)); } static inline int ovs_ct_put_key(const struct sw_flow_key *key, diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index fe527d2..8cfa15a 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -116,7 +116,7 @@ struct sw_flow_key { u16 zone; u32 mark; u8 state; - struct ovs_key_ct_label label; + struct ovs_key_ct_labels labels; } ct; } __aligned(BITS_PER_LONG/8); /* Ensure that we can do comparisons as longs. */ diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 5c030a4..171a691 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -291,10 +291,10 @@ size_t ovs_key_attr_size(void) + nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */ + nla_total_size(4) /* OVS_KEY_ATTR_DP_HASH */ + nla_total_size(4) /* OVS_KEY_ATTR_RECIRC_ID */ - + nla_total_size(1) /* OVS_KEY_ATTR_CT_STATE */ + + nla_total_size(4) /* OVS_KEY_ATTR_CT_STATE */ + nla_total_size(2) /* OVS_KEY_ATTR_CT_ZONE */ + nla_total_size(4) /* OVS_KEY_ATTR_CT_MARK */ - + nla_total_size(16) /* OVS_KEY_ATTR_CT_LABEL */ + + nla_total_size(16) /* OVS_KEY_ATTR_CT_LABELS */ + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */ + nla_total_size(4) /* OVS_KEY_ATTR_VLAN */ @@ -349,10 +349,10 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_TUNNEL] = { .len = OVS_ATTR_NESTED, .next = ovs_tunnel_key_lens, }, [OVS_KEY_ATTR_MPLS] = { .len = sizeof(struct ovs_key_mpls) }, - [OVS_KEY_ATTR_CT_STATE] = { .len = sizeof(u8) }, + [OVS_KEY_ATTR_CT_STATE] = { .len = sizeof(u32) }, [OVS_KEY_ATTR_CT_ZONE] = { .len = sizeof(u16) }, [OVS_KEY_ATTR_CT_MARK] = { .len = sizeof(u32) }, - [OVS_KEY_ATTR_CT_LABEL] = { .len = sizeof(struct ovs_key_ct_label) }, + [OVS_KEY_ATTR_CT_LABELS] = { .len = sizeof(struct ovs_key_ct_labels) }, }; static bool check_attr_len(unsigned int attr_len, unsigned int expected_len) @@ -814,7 +814,13 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, if (*attrs & (1 << OVS_KEY_ATTR_CT_STATE) && ovs_ct_verify(net, OVS_KEY_ATTR_CT_STATE)) { - u8 ct_state = nla_get_u8(a[OVS_KEY_ATTR_CT_STATE]); + u32 ct_state = nla_get_u32(a[OVS_KEY_ATTR_CT_STATE]); + + if (!is_mask && !ovs_ct_state_supported(ct_state)) { + OVS_NLERR(log, "ct_state flags %08x unsupported", + ct_state); + return -EINVAL; + } SW_FLOW_KEY_PUT(match, ct.state, ct_state, is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_STATE); @@ -833,14 +839,14 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, SW_FLOW_KEY_PUT(match, ct.mark, mark, is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_MARK); } - if (*attrs & (1 << OVS_KEY_ATTR_CT_LABEL) && - ovs_ct_verify(net, OVS_KEY_ATTR_CT_LABEL)) { - const struct ovs_key_ct_label *cl; + if (*attrs & (1 << OVS_KEY_ATTR_CT_LABELS) && + ovs_ct_verify(net, OVS_KEY_ATTR_CT_LABELS)) { + const struct ovs_key_ct_labels *cl; - cl = nla_data(a[OVS_KEY_ATTR_CT_LABEL]); - SW_FLOW_KEY_MEMCPY(match, ct.label, cl->ct_label, + cl = nla_data(a[OVS_KEY_ATTR_CT_LABELS]); + SW_FLOW_KEY_MEMCPY(match, ct.labels, cl->ct_labels, sizeof(*cl), is_mask); - *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_LABEL); + *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_LABELS); } return 0; } @@ -1973,7 +1979,7 @@ static int validate_set(const struct nlattr *a, case OVS_KEY_ATTR_PRIORITY: case OVS_KEY_ATTR_SKB_MARK: case OVS_KEY_ATTR_CT_MARK: - case OVS_KEY_ATTR_CT_LABEL: + case OVS_KEY_ATTR_CT_LABELS: case OVS_KEY_ATTR_ETHERNET: break; diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index f2ea83b..c7f74aa 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c @@ -93,7 +93,8 @@ struct sw_flow *ovs_flow_alloc(void) /* Initialize the default stat node. */ stats = kmem_cache_alloc_node(flow_stats_cache, - GFP_KERNEL | __GFP_ZERO, 0); + GFP_KERNEL | __GFP_ZERO, + node_online(0) ? 0 : NUMA_NO_NODE); if (!stats) goto err; diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index dc81dc6..12a36ac2 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -280,35 +280,19 @@ void ovs_vport_del(struct vport *vport) */ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats) { - struct net_device *dev = vport->dev; - int i; - - memset(stats, 0, sizeof(*stats)); - stats->rx_errors = dev->stats.rx_errors; - stats->tx_errors = dev->stats.tx_errors; - stats->tx_dropped = dev->stats.tx_dropped; - stats->rx_dropped = dev->stats.rx_dropped; - - stats->rx_dropped += atomic_long_read(&dev->rx_dropped); - stats->tx_dropped += atomic_long_read(&dev->tx_dropped); - - for_each_possible_cpu(i) { - const struct pcpu_sw_netstats *percpu_stats; - struct pcpu_sw_netstats local_stats; - unsigned int start; - - percpu_stats = per_cpu_ptr(dev->tstats, i); - - do { - start = u64_stats_fetch_begin_irq(&percpu_stats->syncp); - local_stats = *percpu_stats; - } while (u64_stats_fetch_retry_irq(&percpu_stats->syncp, start)); - - stats->rx_bytes += local_stats.rx_bytes; - stats->rx_packets += local_stats.rx_packets; - stats->tx_bytes += local_stats.tx_bytes; - stats->tx_packets += local_stats.tx_packets; - } + const struct rtnl_link_stats64 *dev_stats; + struct rtnl_link_stats64 temp; + + dev_stats = dev_get_stats(vport->dev, &temp); + stats->rx_errors = dev_stats->rx_errors; + stats->tx_errors = dev_stats->tx_errors; + stats->tx_dropped = dev_stats->tx_dropped; + stats->rx_dropped = dev_stats->rx_dropped; + + stats->rx_bytes = dev_stats->rx_bytes; + stats->rx_packets = dev_stats->rx_packets; + stats->tx_bytes = dev_stats->tx_bytes; + stats->tx_packets = dev_stats->tx_packets; } /** @@ -460,6 +444,15 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, OVS_CB(skb)->input_vport = vport; OVS_CB(skb)->mru = 0; + if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) { + u32 mark; + + mark = skb->mark; + skb_scrub_packet(skb, true); + skb->mark = mark; + tun_info = NULL; + } + /* Extract flow from 'skb' into 'key'. */ error = ovs_flow_key_extract(tun_info, skb, &key); if (unlikely(error)) { diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 2d1be4a..32fcdec 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -31,13 +31,17 @@ #define MIRRED_TAB_MASK 7 static LIST_HEAD(mirred_list); +static DEFINE_SPINLOCK(mirred_list_lock); static void tcf_mirred_release(struct tc_action *a, int bind) { struct tcf_mirred *m = to_mirred(a); struct net_device *dev = rcu_dereference_protected(m->tcfm_dev, 1); + /* We could be called either in a RCU callback or with RTNL lock held. */ + spin_lock_bh(&mirred_list_lock); list_del(&m->tcfm_list); + spin_unlock_bh(&mirred_list_lock); if (dev) dev_put(dev); } @@ -103,10 +107,10 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, } else { if (bind) return 0; - if (!ovr) { - tcf_hash_release(a, bind); + + tcf_hash_release(a, bind); + if (!ovr) return -EEXIST; - } } m = to_mirred(a); @@ -123,7 +127,9 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, } if (ret == ACT_P_CREATED) { + spin_lock_bh(&mirred_list_lock); list_add(&m->tcfm_list, &mirred_list); + spin_unlock_bh(&mirred_list_lock); tcf_hash_insert(a); } @@ -173,6 +179,7 @@ static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a, skb2->skb_iif = skb->dev->ifindex; skb2->dev = dev; + skb_sender_cpu_clear(skb2); err = dev_queue_xmit(skb2); if (err) { @@ -221,7 +228,8 @@ static int mirred_device_event(struct notifier_block *unused, struct tcf_mirred *m; ASSERT_RTNL(); - if (event == NETDEV_UNREGISTER) + if (event == NETDEV_UNREGISTER) { + spin_lock_bh(&mirred_list_lock); list_for_each_entry(m, &mirred_list, tcfm_list) { if (rcu_access_pointer(m->tcfm_dev) == dev) { dev_put(dev); @@ -231,6 +239,8 @@ static int mirred_device_event(struct notifier_block *unused, RCU_INIT_POINTER(m->tcfm_dev, NULL); } } + spin_unlock_bh(&mirred_list_lock); + } return NOTIFY_DONE; } diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 9d15cb6..86b04e3 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -368,6 +368,15 @@ static unsigned int hhf_drop(struct Qdisc *sch) return bucket - q->buckets; } +static unsigned int hhf_qdisc_drop(struct Qdisc *sch) +{ + unsigned int prev_backlog; + + prev_backlog = sch->qstats.backlog; + hhf_drop(sch); + return prev_backlog - sch->qstats.backlog; +} + static int hhf_enqueue(struct sk_buff *skb, struct Qdisc *sch) { struct hhf_sched_data *q = qdisc_priv(sch); @@ -696,7 +705,7 @@ static struct Qdisc_ops hhf_qdisc_ops __read_mostly = { .enqueue = hhf_enqueue, .dequeue = hhf_dequeue, .peek = qdisc_peek_dequeued, - .drop = hhf_drop, + .drop = hhf_qdisc_drop, .init = hhf_init, .reset = hhf_reset, .destroy = hhf_destroy, diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index fda38f8..77f5d17e 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -16,6 +16,7 @@ #include <linux/notifier.h> #include <linux/netdevice.h> #include <linux/if_bridge.h> +#include <linux/if_vlan.h> #include <net/ip_fib.h> #include <net/switchdev.h> @@ -634,6 +635,8 @@ static int switchdev_port_br_afspec(struct net_device *dev, if (nla_len(attr) != sizeof(struct bridge_vlan_info)) return -EINVAL; vinfo = nla_data(attr); + if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK) + return -EINVAL; vlan->flags = vinfo->flags; if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { if (vlan->vid_begin) diff --git a/net/tipc/msg.h b/net/tipc/msg.h index a82c584..5351a3f 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -357,7 +357,7 @@ static inline u32 msg_importance(struct tipc_msg *m) if (likely((usr <= TIPC_CRITICAL_IMPORTANCE) && !msg_errcode(m))) return usr; if ((usr == MSG_FRAGMENTER) || (usr == MSG_BUNDLER)) - return msg_bits(m, 5, 13, 0x7); + return msg_bits(m, 9, 0, 0x7); return TIPC_SYSTEM_IMPORTANCE; } @@ -366,7 +366,7 @@ static inline void msg_set_importance(struct tipc_msg *m, u32 i) int usr = msg_user(m); if (likely((usr == MSG_FRAGMENTER) || (usr == MSG_BUNDLER))) - msg_set_bits(m, 5, 13, 0x7, i); + msg_set_bits(m, 9, 0, 0x7, i); else if (i < TIPC_SYSTEM_IMPORTANCE) msg_set_user(m, i); else diff --git a/net/tipc/node.c b/net/tipc/node.c index 703875f..2c32a83 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1116,7 +1116,7 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb, } /* Ignore duplicate packets */ - if (less(oseqno, rcv_nxt)) + if ((usr != LINK_PROTOCOL) && less(oseqno, rcv_nxt)) return true; /* Initiate or update failover mode if applicable */ @@ -1146,8 +1146,8 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb, if (!pl || !tipc_link_is_up(pl)) return true; - /* Initiate or update synch mode if applicable */ - if ((usr == TUNNEL_PROTOCOL) && (mtyp == SYNCH_MSG)) { + /* Initiate synch mode if applicable */ + if ((usr == TUNNEL_PROTOCOL) && (mtyp == SYNCH_MSG) && (oseqno == 1)) { syncpt = iseqno + exp_pkts - 1; if (!tipc_link_is_up(l)) { tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index ef31b40..94f6582 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2064,6 +2064,11 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state) goto out; } + if (flags & MSG_PEEK) + skip = sk_peek_offset(sk, flags); + else + skip = 0; + do { int chunk; struct sk_buff *skb, *last; @@ -2112,7 +2117,6 @@ unlock: break; } - skip = sk_peek_offset(sk, flags); while (skip >= unix_skb_len(skb)) { skip -= unix_skb_len(skb); last = skb; @@ -2179,14 +2183,12 @@ unlock: if (UNIXCB(skb).fp) scm.fp = scm_fp_dup(UNIXCB(skb).fp); - if (skip) { - sk_peek_offset_fwd(sk, chunk); - skip -= chunk; - } + sk_peek_offset_fwd(sk, chunk); if (UNIXCB(skb).fp) break; + skip = 0; last = skb; last_len = skb->len; unix_state_lock(sk); diff --git a/security/keys/gc.c b/security/keys/gc.c index 39eac1f..addf060 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -134,8 +134,10 @@ static noinline void key_gc_unused_keys(struct list_head *keys) kdebug("- %u", key->serial); key_check(key); - /* Throw away the key data */ - if (key->type->destroy) + /* Throw away the key data if the key is instantiated */ + if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags) && + !test_bit(KEY_FLAG_NEGATIVE, &key->flags) && + key->type->destroy) key->type->destroy(key); security_key_free(key); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 486ef6f..0d62531 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -440,6 +440,9 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx, kenter(""); + if (ctx->index_key.type == &key_type_keyring) + return ERR_PTR(-EPERM); + user = key_user_lookup(current_fsuid()); if (!user) return ERR_PTR(-ENOMEM); diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 38590b3..fbd5dad 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/dmaengine.h> +#include <linux/dma/pxa-dma.h> #include <sound/core.h> #include <sound/pcm.h> @@ -43,7 +44,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_reset, }; -static unsigned long pxa2xx_ac97_pcm_out_req = 12; +static struct pxad_param pxa2xx_ac97_pcm_out_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 12, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -51,7 +56,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { .filter_data = &pxa2xx_ac97_pcm_out_req, }; -static unsigned long pxa2xx_ac97_pcm_in_req = 11; +static struct pxad_param pxa2xx_ac97_pcm_in_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 11, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index 01f8fdc..e9b98af 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> +#include <linux/dma/pxa-dma.h> #include <sound/core.h> #include <sound/pcm.h> @@ -15,8 +16,6 @@ #include <sound/pxa2xx-lib.h> #include <sound/dmaengine_pcm.h> -#include <mach/dma.h> - #include "pxa2xx-pcm.h" static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { @@ -31,7 +30,7 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { .period_bytes_min = 32, .period_bytes_max = 8192 - 32, .periods_min = 1, - .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc), + .periods_max = 256, .buffer_bytes_max = 128 * 1024, .fifo_size = 32, }; @@ -39,65 +38,29 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *rtd = runtime->private_data; - size_t totsize = params_buffer_bytes(params); - size_t period = params_period_bytes(params); - pxa_dma_desc *dma_desc; - dma_addr_t dma_buff_phys, next_desc_phys; - u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; + struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_dmaengine_dai_dma_data *dma_params; + struct dma_slave_config config; + int ret; - /* temporary transition hack */ - switch (rtd->params->addr_width) { - case DMA_SLAVE_BUSWIDTH_1_BYTE: - dcmd |= DCMD_WIDTH1; - break; - case DMA_SLAVE_BUSWIDTH_2_BYTES: - dcmd |= DCMD_WIDTH2; - break; - case DMA_SLAVE_BUSWIDTH_4_BYTES: - dcmd |= DCMD_WIDTH4; - break; - default: - /* can't happen */ - break; - } + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (!dma_params) + return 0; - switch (rtd->params->maxburst) { - case 8: - dcmd |= DCMD_BURST8; - break; - case 16: - dcmd |= DCMD_BURST16; - break; - case 32: - dcmd |= DCMD_BURST32; - break; - } + ret = snd_hwparams_to_dma_slave_config(substream, params, &config); + if (ret) + return ret; - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - runtime->dma_bytes = totsize; + snd_dmaengine_pcm_set_config_from_dai_data(substream, + snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), + &config); - dma_desc = rtd->dma_desc_array; - next_desc_phys = rtd->dma_desc_array_phys; - dma_buff_phys = runtime->dma_addr; - do { - next_desc_phys += sizeof(pxa_dma_desc); - dma_desc->ddadr = next_desc_phys; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dma_desc->dsadr = dma_buff_phys; - dma_desc->dtadr = rtd->params->addr; - } else { - dma_desc->dsadr = rtd->params->addr; - dma_desc->dtadr = dma_buff_phys; - } - if (period > totsize) - period = totsize; - dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN; - dma_desc++; - dma_buff_phys += period; - } while (totsize -= period); - dma_desc[-1].ddadr = rtd->dma_desc_array_phys; + ret = dmaengine_slave_config(chan, &config); + if (ret) + return ret; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); return 0; } @@ -105,13 +68,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_params); int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { - struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - - if (rtd && rtd->params && rtd->params->filter_data) { - unsigned long req = *(unsigned long *) rtd->params->filter_data; - DRCMR(req) = 0; - } - snd_pcm_set_runtime_buffer(substream, NULL); return 0; } @@ -119,100 +75,36 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_free); int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; - DCSR(prtd->dma_ch) = DCSR_RUN; - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - DCSR(prtd->dma_ch) &= ~DCSR_RUN; - break; - - case SNDRV_PCM_TRIGGER_RESUME: - DCSR(prtd->dma_ch) |= DCSR_RUN; - break; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; - DCSR(prtd->dma_ch) |= DCSR_RUN; - break; - - default: - ret = -EINVAL; - } - - return ret; + return snd_dmaengine_pcm_trigger(substream, cmd); } EXPORT_SYMBOL(pxa2xx_pcm_trigger); snd_pcm_uframes_t pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *prtd = runtime->private_data; - - dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch); - snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr); - - if (x == runtime->buffer_size) - x = 0; - return x; + return snd_dmaengine_pcm_pointer(substream); } EXPORT_SYMBOL(pxa2xx_pcm_pointer); int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) { - struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; - unsigned long req; - - if (!prtd || !prtd->params) - return 0; - - if (prtd->dma_ch == -1) - return -EINVAL; - - DCSR(prtd->dma_ch) &= ~DCSR_RUN; - DCSR(prtd->dma_ch) = 0; - DCMD(prtd->dma_ch) = 0; - req = *(unsigned long *) prtd->params->filter_data; - DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD; - return 0; } EXPORT_SYMBOL(__pxa2xx_pcm_prepare); -void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) -{ - struct snd_pcm_substream *substream = dev_id; - int dcsr; - - dcsr = DCSR(dma_ch); - DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; - - if (dcsr & DCSR_ENDINTR) { - snd_pcm_period_elapsed(substream); - } else { - printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n", - dma_ch, dcsr); - snd_pcm_stop_xrun(substream); - } -} -EXPORT_SYMBOL(pxa2xx_pcm_dma_irq); - int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *rtd; + struct snd_dmaengine_dai_dma_data *dma_params; int ret; runtime->hw = pxa2xx_pcm_hardware; + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (!dma_params) + return 0; + /* * For mysterious reasons (and despite what the manual says) * playback samples are lost if the DMA count is not a multiple @@ -221,48 +113,27 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); if (ret) - goto out; + return ret; ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); if (ret) - goto out; + return ret; ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) - goto out; - - ret = -ENOMEM; - rtd = kzalloc(sizeof(*rtd), GFP_KERNEL); - if (!rtd) - goto out; - rtd->dma_desc_array = - dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, - &rtd->dma_desc_array_phys, GFP_KERNEL); - if (!rtd->dma_desc_array) - goto err1; + return ret; - rtd->dma_ch = -1; - runtime->private_data = rtd; - return 0; - - err1: - kfree(rtd); - out: - return ret; + return snd_dmaengine_pcm_open_request_chan(substream, + pxad_filter_fn, + dma_params->filter_data); } EXPORT_SYMBOL(__pxa2xx_pcm_open); int __pxa2xx_pcm_close(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *rtd = runtime->private_data; - - dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, - rtd->dma_desc_array, rtd->dma_desc_array_phys); - kfree(rtd); - return 0; + return snd_dmaengine_pcm_close_release_chan(substream); } EXPORT_SYMBOL(__pxa2xx_pcm_close); diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c index 83be8e3..83fcfac 100644 --- a/sound/arm/pxa2xx-pcm.c +++ b/sound/arm/pxa2xx-pcm.c @@ -46,17 +46,13 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? client->playback_params : client->capture_params; - ret = pxa_request_dma("dma", DMA_PRIO_LOW, - pxa2xx_pcm_dma_irq, substream); - if (ret < 0) - goto err2; - rtd->dma_ch = ret; ret = client->startup(substream); if (!ret) - goto out; + goto err2; + + return 0; - pxa_free_dma(rtd->dma_ch); err2: __pxa2xx_pcm_close(substream); out: @@ -66,9 +62,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) static int pxa2xx_pcm_close(struct snd_pcm_substream *substream) { struct pxa2xx_pcm_client *client = substream->private_data; - struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - pxa_free_dma(rtd->dma_ch); client->shutdown(substream); return __pxa2xx_pcm_close(substream); diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h index 0033098..8fa2b7c 100644 --- a/sound/arm/pxa2xx-pcm.h +++ b/sound/arm/pxa2xx-pcm.h @@ -13,8 +13,6 @@ struct pxa2xx_runtime_data { int dma_ch; struct snd_dmaengine_dai_dma_data *params; - struct pxa_dma_desc *dma_desc_array; - dma_addr_t dma_desc_array_phys; }; struct pxa2xx_pcm_client { diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2eeaf5e..83741887f 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3323,10 +3323,8 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec) int dev, err; err = snd_hda_codec_parse_pcms(codec); - if (err < 0) { - snd_hda_codec_reset(codec); + if (err < 0) return err; - } /* attach a new PCM streams */ list_for_each_entry(cpcm, &codec->pcm_list_head, list) { diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 7de792b..7ff7d88 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -9,7 +9,6 @@ menuconfig SND_SOC select SND_JACK if INPUT=y || INPUT=SND select REGMAP_I2C if I2C select REGMAP_SPI if SPI_MASTER - select SND_COMPRESS_OFFLOAD ---help--- If you want ASoC support, you should say Y here and also to the @@ -30,6 +29,10 @@ config SND_SOC_GENERIC_DMAENGINE_PCM bool select SND_DMAENGINE_PCM +config SND_SOC_COMPRESS + bool + select SND_COMPRESS_OFFLOAD + config SND_SOC_TOPOLOGY bool diff --git a/sound/soc/Makefile b/sound/soc/Makefile index af0a571..8eb06db 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,5 +1,6 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o -snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o +snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o +snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o ifneq ($(CONFIG_SND_SOC_TOPOLOGY),) snd-soc-core-objs += soc-topology.o diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 1489cd4..2d30464 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -59,4 +59,13 @@ config SND_AT91_SOC_SAM9X5_WM8731 help Say Y if you want to add support for audio SoC on an at91sam9x5 based board that is using WM8731 codec. + +config SND_ATMEL_SOC_CLASSD + tristate "Atmel ASoC driver for boards using CLASSD" + depends on ARCH_AT91 || COMPILE_TEST + select SND_ATMEL_SOC_DMA + select REGMAP_MMIO + help + Say Y if you want to add support for Atmel ASoC driver for boards using + CLASSD. endif diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index b327e5c..f6f7db4 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -11,7 +11,9 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o snd-atmel-soc-wm8904-objs := atmel_wm8904.o snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o +snd-atmel-soc-classd-objs := atmel-classd.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o +obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c new file mode 100644 index 0000000..8276675 --- /dev/null +++ b/sound/soc/atmel/atmel-classd.c @@ -0,0 +1,679 @@ +/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver + * + * Copyright (C) 2015 Atmel + * + * Author: Songjun Wu <songjun.wu@atmel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or later + * as published by the Free Software Foundation. + */ + +#include <linux/of.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> +#include "atmel-classd.h" + +struct atmel_classd_pdata { + bool non_overlap_enable; + int non_overlap_time; + int pwm_type; + const char *card_name; +}; + +struct atmel_classd { + dma_addr_t phy_base; + struct regmap *regmap; + struct clk *pclk; + struct clk *gclk; + struct clk *aclk; + int irq; + const struct atmel_classd_pdata *pdata; +}; + +#ifdef CONFIG_OF +static const struct of_device_id atmel_classd_of_match[] = { + { + .compatible = "atmel,sama5d2-classd", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, atmel_classd_of_match); + +static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct atmel_classd_pdata *pdata; + const char *pwm_type; + int ret; + + if (!np) { + dev_err(dev, "device node not found\n"); + return ERR_PTR(-EINVAL); + } + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type); + if ((ret == 0) && (strcmp(pwm_type, "diff") == 0)) + pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF; + else + pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE; + + ret = of_property_read_u32(np, + "atmel,non-overlap-time", &pdata->non_overlap_time); + if (ret) + pdata->non_overlap_enable = false; + else + pdata->non_overlap_enable = true; + + ret = of_property_read_string(np, "atmel,model", &pdata->card_name); + if (ret) + pdata->card_name = "CLASSD"; + + return pdata; +} +#else +static inline struct atmel_classd_pdata * +atmel_classd_dt_init(struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif + +#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \ + | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 \ + | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 \ + | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 \ + | SNDRV_PCM_RATE_96000) + +static const struct snd_pcm_hardware atmel_classd_hw = { + .info = SNDRV_PCM_INFO_MMAP + | SNDRV_PCM_INFO_MMAP_VALID + | SNDRV_PCM_INFO_INTERLEAVED + | SNDRV_PCM_INFO_RESUME + | SNDRV_PCM_INFO_PAUSE, + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = ATMEL_CLASSD_RATES, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 64 * 1024, + .period_bytes_min = 256, + .period_bytes_max = 32 * 1024, + .periods_min = 2, + .periods_max = 256, +}; + +#define ATMEL_CLASSD_PREALLOC_BUF_SIZE (64 * 1024) + +/* cpu dai component */ +static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + regmap_write(dd->regmap, CLASSD_THR, 0x0); + + return clk_prepare_enable(dd->pclk); +} + +static void atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + clk_disable_unprepare(dd->pclk); +} + +static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = { + .startup = atmel_classd_cpu_dai_startup, + .shutdown = atmel_classd_cpu_dai_shutdown, +}; + +static struct snd_soc_dai_driver atmel_classd_cpu_dai = { + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = ATMEL_CLASSD_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = &atmel_classd_cpu_dai_ops, +}; + +static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = { + .name = "atmel-classd", +}; + +/* platform */ +static int +atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct dma_slave_config *slave_config) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + if (params_physical_width(params) != 16) { + dev_err(rtd->platform->dev, + "only supports 16-bit audio data\n"); + return -EINVAL; + } + + slave_config->direction = DMA_MEM_TO_DEV; + slave_config->dst_addr = dd->phy_base + CLASSD_THR; + slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slave_config->dst_maxburst = 1; + slave_config->src_maxburst = 1; + slave_config->device_fc = false; + + return 0; +} + +static const struct snd_dmaengine_pcm_config +atmel_classd_dmaengine_pcm_config = { + .prepare_slave_config = atmel_classd_platform_configure_dma, + .pcm_hardware = &atmel_classd_hw, + .prealloc_buffer_size = ATMEL_CLASSD_PREALLOC_BUF_SIZE, +}; + +/* codec */ +static const char * const mono_mode_text[] = { + "mix", "sat", "left", "right" +}; + +static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum, + CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT, + mono_mode_text); + +static const char * const eqcfg_text[] = { + "Treble-12dB", "Treble-6dB", + "Medium-8dB", "Medium-3dB", + "Bass-12dB", "Bass-6dB", + "0 dB", + "Bass+6dB", "Bass+12dB", + "Medium+3dB", "Medium+8dB", + "Treble+6dB", "Treble+12dB", +}; + +static const unsigned int eqcfg_value[] = { + CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6, + CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3, + CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6, + CLASSD_INTPMR_EQCFG_FLAT, + CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12, + CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8, + CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum, + CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf, + eqcfg_text, eqcfg_value); + +static const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1); + +static const struct snd_kcontrol_new atmel_classd_snd_controls[] = { +SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR, + CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT, + 78, 1, classd_digital_tlv), + +SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR, + CLASSD_INTPMR_DEEMP_SHIFT, 1, 0), + +SOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0), + +SOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0), + +SOC_ENUM("Mono Mode", classd_mono_mode_enum), + +SOC_ENUM("EQ", classd_eqcfg_enum), +}; + +static const char * const pwm_type[] = { + "Single ended", "Differential" +}; + +static int atmel_classd_codec_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec); + struct atmel_classd *dd = snd_soc_card_get_drvdata(card); + const struct atmel_classd_pdata *pdata = dd->pdata; + u32 mask, val; + + mask = CLASSD_MR_PWMTYP_MASK; + val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT; + + mask |= CLASSD_MR_NON_OVERLAP_MASK; + if (pdata->non_overlap_enable) { + val |= (CLASSD_MR_NON_OVERLAP_EN + << CLASSD_MR_NON_OVERLAP_SHIFT); + + mask |= CLASSD_MR_NOVR_VAL_MASK; + switch (pdata->non_overlap_time) { + case 5: + val |= (CLASSD_MR_NOVR_VAL_5NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + case 10: + val |= (CLASSD_MR_NOVR_VAL_10NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + case 15: + val |= (CLASSD_MR_NOVR_VAL_15NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + case 20: + val |= (CLASSD_MR_NOVR_VAL_20NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + default: + val |= (CLASSD_MR_NOVR_VAL_10NS + << CLASSD_MR_NOVR_VAL_SHIFT); + dev_warn(codec->dev, + "non-overlapping value %d is invalid, the default value 10 is specified\n", + pdata->non_overlap_time); + break; + } + } + + snd_soc_update_bits(codec, CLASSD_MR, mask, val); + + dev_info(codec->dev, + "PWM modulation type is %s, non-overlapping is %s\n", + pwm_type[pdata->pwm_type], + pdata->non_overlap_enable?"enabled":"disabled"); + + return 0; +} + +static struct regmap *atmel_classd_codec_get_remap(struct device *dev) +{ + return dev_get_regmap(dev, NULL); +} + +static struct snd_soc_codec_driver soc_codec_dev_classd = { + .probe = atmel_classd_codec_probe, + .controls = atmel_classd_snd_controls, + .num_controls = ARRAY_SIZE(atmel_classd_snd_controls), + .get_regmap = atmel_classd_codec_get_remap, +}; + +/* codec dai component */ +static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + int ret; + + ret = clk_prepare_enable(dd->aclk); + if (ret) + return ret; + + return clk_prepare_enable(dd->gclk); +} + +static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai, + int mute) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u32 mask, val; + + mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK; + + if (mute) + val = mask; + else + val = 0; + + snd_soc_update_bits(codec, CLASSD_MR, mask, val); + + return 0; +} + +#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8) +#define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8) + +static struct { + int rate; + int sample_rate; + int dsp_clk; + unsigned long aclk_rate; +} const sample_rates[] = { + { 8000, CLASSD_INTPMR_FRAME_8K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 16000, CLASSD_INTPMR_FRAME_16K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 32000, CLASSD_INTPMR_FRAME_32K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 48000, CLASSD_INTPMR_FRAME_48K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 96000, CLASSD_INTPMR_FRAME_96K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 22050, CLASSD_INTPMR_FRAME_22K, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, + { 44100, CLASSD_INTPMR_FRAME_44K, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, + { 88200, CLASSD_INTPMR_FRAME_88K, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, +}; + +static int +atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_codec *codec = codec_dai->codec; + int fs; + int i, best, best_val, cur_val, ret; + u32 mask, val; + + fs = params_rate(params); + + best = 0; + best_val = abs(fs - sample_rates[0].rate); + for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { + /* Closest match */ + cur_val = abs(fs - sample_rates[i].rate); + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + + dev_dbg(codec->dev, + "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n", + sample_rates[best].rate, sample_rates[best].aclk_rate); + + clk_disable_unprepare(dd->gclk); + clk_disable_unprepare(dd->aclk); + + ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate); + if (ret) + return ret; + + mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK; + val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT) + | (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT); + + snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val); + + ret = clk_prepare_enable(dd->aclk); + if (ret) + return ret; + + return clk_prepare_enable(dd->gclk); +} + +static void +atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + clk_disable_unprepare(dd->gclk); + clk_disable_unprepare(dd->aclk); +} + +static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_codec *codec = codec_dai->codec; + + snd_soc_update_bits(codec, CLASSD_MR, + CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK, + (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT) + |(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT)); + + return 0; +} + +static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *codec_dai) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u32 mask, val; + + mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = mask; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT) + | (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT); + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, CLASSD_MR, mask, val); + + return 0; +} + +static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops = { + .digital_mute = atmel_classd_codec_dai_digital_mute, + .startup = atmel_classd_codec_dai_startup, + .shutdown = atmel_classd_codec_dai_shutdown, + .hw_params = atmel_classd_codec_dai_hw_params, + .prepare = atmel_classd_codec_dai_prepare, + .trigger = atmel_classd_codec_dai_trigger, +}; + +#define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi" + +static struct snd_soc_dai_driver atmel_classd_codec_dai = { + .name = ATMEL_CLASSD_CODEC_DAI_NAME, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ATMEL_CLASSD_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &atmel_classd_codec_dai_ops, +}; + +/* ASoC sound card */ +static int atmel_classd_asoc_card_init(struct device *dev, + struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link; + struct atmel_classd *dd = snd_soc_card_get_drvdata(card); + + dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL); + if (!dai_link) + return -ENOMEM; + + dai_link->name = "CLASSD"; + dai_link->stream_name = "CLASSD PCM"; + dai_link->codec_dai_name = ATMEL_CLASSD_CODEC_DAI_NAME; + dai_link->cpu_dai_name = dev_name(dev); + dai_link->codec_name = dev_name(dev); + dai_link->platform_name = dev_name(dev); + + card->dai_link = dai_link; + card->num_links = 1; + card->name = dd->pdata->card_name; + card->dev = dev; + + return 0; +}; + +/* regmap configuration */ +static const struct reg_default atmel_classd_reg_defaults[] = { + { CLASSD_INTPMR, 0x00301212 }, +}; + +#define ATMEL_CLASSD_REG_MAX 0xE4 +static const struct regmap_config atmel_classd_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = ATMEL_CLASSD_REG_MAX, + + .cache_type = REGCACHE_FLAT, + .reg_defaults = atmel_classd_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(atmel_classd_reg_defaults), +}; + +static int atmel_classd_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct atmel_classd *dd; + struct resource *res; + void __iomem *io_base; + const struct atmel_classd_pdata *pdata; + struct snd_soc_card *card; + int ret; + + pdata = dev_get_platdata(dev); + if (!pdata) { + pdata = atmel_classd_dt_init(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + + dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + + dd->pdata = pdata; + + dd->irq = platform_get_irq(pdev, 0); + if (dd->irq < 0) { + ret = dd->irq; + dev_err(dev, "failed to could not get irq: %d\n", ret); + return ret; + } + + dd->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(dd->pclk)) { + ret = PTR_ERR(dd->pclk); + dev_err(dev, "failed to get peripheral clock: %d\n", ret); + return ret; + } + + dd->gclk = devm_clk_get(dev, "gclk"); + if (IS_ERR(dd->gclk)) { + ret = PTR_ERR(dd->gclk); + dev_err(dev, "failed to get GCK clock: %d\n", ret); + return ret; + } + + dd->aclk = devm_clk_get(dev, "aclk"); + if (IS_ERR(dd->aclk)) { + ret = PTR_ERR(dd->aclk); + dev_err(dev, "failed to get audio clock: %d\n", ret); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no memory resource\n"); + return -ENXIO; + } + + io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(io_base)) { + ret = PTR_ERR(io_base); + dev_err(dev, "failed to remap register memory: %d\n", ret); + return ret; + } + + dd->phy_base = res->start; + + dd->regmap = devm_regmap_init_mmio(dev, io_base, + &atmel_classd_regmap_config); + if (IS_ERR(dd->regmap)) { + ret = PTR_ERR(dd->regmap); + dev_err(dev, "failed to init register map: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(dev, + &atmel_classd_cpu_dai_component, + &atmel_classd_cpu_dai, 1); + if (ret) { + dev_err(dev, "could not register CPU DAI: %d\n", ret); + return ret; + } + + ret = devm_snd_dmaengine_pcm_register(dev, + &atmel_classd_dmaengine_pcm_config, + 0); + if (ret) { + dev_err(dev, "could not register platform: %d\n", ret); + return ret; + } + + ret = snd_soc_register_codec(dev, &soc_codec_dev_classd, + &atmel_classd_codec_dai, 1); + if (ret) { + dev_err(dev, "could not register codec: %d\n", ret); + return ret; + } + + /* register sound card */ + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, dd); + platform_set_drvdata(pdev, card); + + ret = atmel_classd_asoc_card_init(dev, card); + if (ret) { + dev_err(dev, "failed to init sound card\n"); + return ret; + } + + ret = devm_snd_soc_register_card(dev, card); + if (ret) { + dev_err(dev, "failed to register sound card: %d\n", ret); + return ret; + } + + return 0; +} + +static int atmel_classd_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver atmel_classd_driver = { + .driver = { + .name = "atmel-classd", + .of_match_table = of_match_ptr(atmel_classd_of_match), + .pm = &snd_soc_pm_ops, + }, + .probe = atmel_classd_probe, + .remove = atmel_classd_remove, +}; +module_platform_driver(atmel_classd_driver); + +MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture"); +MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/atmel/atmel-classd.h b/sound/soc/atmel/atmel-classd.h new file mode 100644 index 0000000..73f8fdd --- /dev/null +++ b/sound/soc/atmel/atmel-classd.h @@ -0,0 +1,120 @@ +#ifndef __ATMEL_CLASSD_H_ +#define __ATMEL_CLASSD_H_ + +#define CLASSD_CR 0x00000000 +#define CLASSD_CR_RESET 0x1 + +#define CLASSD_MR 0x00000004 + +#define CLASSD_MR_LEN_DIS 0x0 +#define CLASSD_MR_LEN_EN 0x1 +#define CLASSD_MR_LEN_MASK (0x1 << 0) +#define CLASSD_MR_LEN_SHIFT (0) + +#define CLASSD_MR_LMUTE_DIS 0x0 +#define CLASSD_MR_LMUTE_EN 0x1 +#define CLASSD_MR_LMUTE_SHIFT (0x1) +#define CLASSD_MR_LMUTE_MASK (0x1 << 1) + +#define CLASSD_MR_REN_DIS 0x0 +#define CLASSD_MR_REN_EN 0x1 +#define CLASSD_MR_REN_MASK (0x1 << 4) +#define CLASSD_MR_REN_SHIFT (4) + +#define CLASSD_MR_RMUTE_DIS 0x0 +#define CLASSD_MR_RMUTE_EN 0x1 +#define CLASSD_MR_RMUTE_SHIFT (0x5) +#define CLASSD_MR_RMUTE_MASK (0x1 << 5) + +#define CLASSD_MR_PWMTYP_SINGLE 0x0 +#define CLASSD_MR_PWMTYP_DIFF 0x1 +#define CLASSD_MR_PWMTYP_MASK (0x1 << 8) +#define CLASSD_MR_PWMTYP_SHIFT (8) + +#define CLASSD_MR_NON_OVERLAP_DIS 0x0 +#define CLASSD_MR_NON_OVERLAP_EN 0x1 +#define CLASSD_MR_NON_OVERLAP_MASK (0x1 << 16) +#define CLASSD_MR_NON_OVERLAP_SHIFT (16) + +#define CLASSD_MR_NOVR_VAL_5NS 0x0 +#define CLASSD_MR_NOVR_VAL_10NS 0x1 +#define CLASSD_MR_NOVR_VAL_15NS 0x2 +#define CLASSD_MR_NOVR_VAL_20NS 0x3 +#define CLASSD_MR_NOVR_VAL_MASK (0x3 << 20) +#define CLASSD_MR_NOVR_VAL_SHIFT (20) + +#define CLASSD_INTPMR 0x00000008 + +#define CLASSD_INTPMR_ATTL_MASK (0x3f << 0) +#define CLASSD_INTPMR_ATTL_SHIFT (0) +#define CLASSD_INTPMR_ATTR_MASK (0x3f << 8) +#define CLASSD_INTPMR_ATTR_SHIFT (8) + +#define CLASSD_INTPMR_DSP_CLK_FREQ_12M288 0x0 +#define CLASSD_INTPMR_DSP_CLK_FREQ_11M2896 0x1 +#define CLASSD_INTPMR_DSP_CLK_FREQ_MASK (0x1 << 16) +#define CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT (16) + +#define CLASSD_INTPMR_DEEMP_DIS 0x0 +#define CLASSD_INTPMR_DEEMP_EN 0x1 +#define CLASSD_INTPMR_DEEMP_MASK (0x1 << 18) +#define CLASSD_INTPMR_DEEMP_SHIFT (18) + +#define CLASSD_INTPMR_SWAP_LEFT_ON_LSB 0x0 +#define CLASSD_INTPMR_SWAP_RIGHT_ON_LSB 0x1 +#define CLASSD_INTPMR_SWAP_MASK (0x1 << 19) +#define CLASSD_INTPMR_SWAP_SHIFT (19) + +#define CLASSD_INTPMR_FRAME_8K 0x0 +#define CLASSD_INTPMR_FRAME_16K 0x1 +#define CLASSD_INTPMR_FRAME_32K 0x2 +#define CLASSD_INTPMR_FRAME_48K 0x3 +#define CLASSD_INTPMR_FRAME_96K 0x4 +#define CLASSD_INTPMR_FRAME_22K 0x5 +#define CLASSD_INTPMR_FRAME_44K 0x6 +#define CLASSD_INTPMR_FRAME_88K 0x7 +#define CLASSD_INTPMR_FRAME_MASK (0x7 << 20) +#define CLASSD_INTPMR_FRAME_SHIFT (20) + +#define CLASSD_INTPMR_EQCFG_FLAT 0x0 +#define CLASSD_INTPMR_EQCFG_B_BOOST_12 0x1 +#define CLASSD_INTPMR_EQCFG_B_BOOST_6 0x2 +#define CLASSD_INTPMR_EQCFG_B_CUT_12 0x3 +#define CLASSD_INTPMR_EQCFG_B_CUT_6 0x4 +#define CLASSD_INTPMR_EQCFG_M_BOOST_3 0x5 +#define CLASSD_INTPMR_EQCFG_M_BOOST_8 0x6 +#define CLASSD_INTPMR_EQCFG_M_CUT_3 0x7 +#define CLASSD_INTPMR_EQCFG_M_CUT_8 0x8 +#define CLASSD_INTPMR_EQCFG_T_BOOST_12 0x9 +#define CLASSD_INTPMR_EQCFG_T_BOOST_6 0xa +#define CLASSD_INTPMR_EQCFG_T_CUT_12 0xb +#define CLASSD_INTPMR_EQCFG_T_CUT_6 0xc +#define CLASSD_INTPMR_EQCFG_SHIFT (24) + +#define CLASSD_INTPMR_MONO_DIS 0x0 +#define CLASSD_INTPMR_MONO_EN 0x1 +#define CLASSD_INTPMR_MONO_MASK (0x1 << 28) +#define CLASSD_INTPMR_MONO_SHIFT (28) + +#define CLASSD_INTPMR_MONO_MODE_MIX 0x0 +#define CLASSD_INTPMR_MONO_MODE_SAT 0x1 +#define CLASSD_INTPMR_MONO_MODE_LEFT 0x2 +#define CLASSD_INTPMR_MONO_MODE_RIGHT 0x3 +#define CLASSD_INTPMR_MONO_MODE_MASK (0x3 << 29) +#define CLASSD_INTPMR_MONO_MODE_SHIFT (29) + +#define CLASSD_INTSR 0x0000000c + +#define CLASSD_THR 0x00000010 + +#define CLASSD_IER 0x00000014 + +#define CLASSD_IDR 0x00000018 + +#define CLASSD_IMR 0x0000001c + +#define CLASSD_ISR 0x00000020 + +#define CLASSD_WPMR 0x000000e4 + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 70e5a75..cfdafc4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -58,6 +58,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CX20442 if TTY select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI select SND_SOC_DA7213 if I2C + select SND_SOC_DA7219 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C select SND_SOC_DMIC @@ -80,6 +81,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX9877 if I2C select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C + select SND_SOC_NAU8825 if I2C select SND_SOC_PCM1681 if I2C select SND_SOC_PCM1792A if SPI_MASTER select SND_SOC_PCM3008 @@ -171,6 +173,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8996 if I2C select SND_SOC_WM8997 if MFD_WM8997 + select SND_SOC_WM8998 if MFD_WM8998 select SND_SOC_WM9081 if I2C select SND_SOC_WM9090 if I2C select SND_SOC_WM9705 if SND_SOC_AC97_BUS @@ -195,9 +198,11 @@ config SND_SOC_ARIZONA default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y default y if SND_SOC_WM8997=y + default y if SND_SOC_WM8998=y default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m default m if SND_SOC_WM8997=m + default m if SND_SOC_WM8998=m config SND_SOC_WM_HUBS tristate @@ -434,6 +439,9 @@ config SND_SOC_DA7210 config SND_SOC_DA7213 tristate +config SND_SOC_DA7219 + tristate + config SND_SOC_DA732X tristate @@ -866,6 +874,9 @@ config SND_SOC_WM8996 config SND_SOC_WM8997 tristate +config SND_SOC_WM8998 + tristate + config SND_SOC_WM9081 tristate @@ -897,6 +908,9 @@ config SND_SOC_MC13783 config SND_SOC_ML26124 tristate +config SND_SOC_NAU8825 + tristate + config SND_SOC_TPA6130A2 tristate "Texas Instruments TPA6130A2 headphone amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index be1491a..f632fc4 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -50,6 +50,7 @@ snd-soc-cs4349-objs := cs4349.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o snd-soc-da7213-objs := da7213.o +snd-soc-da7219-objs := da7219.o da7219-aad.o snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-bt-sco-objs := bt-sco.o @@ -73,6 +74,7 @@ snd-soc-max98925-objs := max98925.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o +snd-soc-nau8825-objs := nau8825.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1792a-codec-objs := pcm1792a.o snd-soc-pcm3008-objs := pcm3008.o @@ -176,6 +178,7 @@ snd-soc-wm8993-objs := wm8993.o snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o snd-soc-wm8995-objs := wm8995.o snd-soc-wm8997-objs := wm8997.o +snd-soc-wm8998-objs := wm8998.o snd-soc-wm9081-objs := wm9081.o snd-soc-wm9090-objs := wm9090.o snd-soc-wm9705-objs := wm9705.o @@ -242,6 +245,7 @@ obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o +obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o @@ -265,6 +269,7 @@ obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o +obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o @@ -364,6 +369,7 @@ obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o +obj-$(CONFIG_SND_SOC_WM8998) += snd-soc-wm8998.o obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c index df3a1a4..1713136 100644 --- a/sound/soc/codecs/ad193x-i2c.c +++ b/sound/soc/codecs/ad193x-i2c.c @@ -15,8 +15,8 @@ #include "ad193x.h" static const struct i2c_device_id ad193x_id[] = { - { "ad1936", 0 }, - { "ad1937", 0 }, + { "ad1936", AD193X }, + { "ad1937", AD193X }, { } }; MODULE_DEVICE_TABLE(i2c, ad193x_id); @@ -30,7 +30,9 @@ static int ad193x_i2c_probe(struct i2c_client *client, config.val_bits = 8; config.reg_bits = 8; - return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config)); + return ad193x_probe(&client->dev, + devm_regmap_init_i2c(client, &config), + (enum ad193x_type)id->driver_data); } static int ad193x_i2c_remove(struct i2c_client *client) diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c index 390cef9..431f95d 100644 --- a/sound/soc/codecs/ad193x-spi.c +++ b/sound/soc/codecs/ad193x-spi.c @@ -16,6 +16,7 @@ static int ad193x_spi_probe(struct spi_device *spi) { + const struct spi_device_id *id = spi_get_device_id(spi); struct regmap_config config; config = ad193x_regmap_config; @@ -24,7 +25,8 @@ static int ad193x_spi_probe(struct spi_device *spi) config.read_flag_mask = 0x09; config.write_flag_mask = 0x08; - return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config)); + return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config), + (enum ad193x_type)id->driver_data); } static int ad193x_spi_remove(struct spi_device *spi) @@ -33,6 +35,17 @@ static int ad193x_spi_remove(struct spi_device *spi) return 0; } +static const struct spi_device_id ad193x_spi_id[] = { + { "ad193x", AD193X }, + { "ad1933", AD1933 }, + { "ad1934", AD1934 }, + { "ad1938", AD193X }, + { "ad1939", AD193X }, + { "adau1328", AD193X }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad193x_spi_id); + static struct spi_driver ad193x_spi_driver = { .driver = { .name = "ad193x", @@ -40,6 +53,7 @@ static struct spi_driver ad193x_spi_driver = { }, .probe = ad193x_spi_probe, .remove = ad193x_spi_remove, + .id_table = ad193x_spi_id, }; module_spi_driver(ad193x_spi_driver); diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index 17c9535..3a3f3f2 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -23,6 +23,7 @@ /* codec private data */ struct ad193x_priv { struct regmap *regmap; + enum ad193x_type type; int sysclk; }; @@ -47,12 +48,6 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = { SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL, AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv), - /* ADC switch control */ - SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE, - AD193X_ADCR1_MUTE, 1, 1), - SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE, - AD193X_ADCR2_MUTE, 1, 1), - /* DAC switch control */ SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE, AD193X_DACR1_MUTE, 1, 1), @@ -63,26 +58,37 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = { SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE, AD193X_DACR4_MUTE, 1, 1), + /* DAC de-emphasis */ + SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum), +}; + +static const struct snd_kcontrol_new ad193x_adc_snd_controls[] = { + /* ADC switch control */ + SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE, + AD193X_ADCR1_MUTE, 1, 1), + SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE, + AD193X_ADCR2_MUTE, 1, 1), + /* ADC high-pass filter */ SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0, AD193X_ADC_HIGHPASS_FILTER, 1, 0), - - /* DAC de-emphasis */ - SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum), }; static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0), - SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0), SND_SOC_DAPM_VMID("VMID"), SND_SOC_DAPM_OUTPUT("DAC1OUT"), SND_SOC_DAPM_OUTPUT("DAC2OUT"), SND_SOC_DAPM_OUTPUT("DAC3OUT"), SND_SOC_DAPM_OUTPUT("DAC4OUT"), +}; + +static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = { + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0), SND_SOC_DAPM_INPUT("ADC1IN"), SND_SOC_DAPM_INPUT("ADC2IN"), }; @@ -91,18 +97,33 @@ static const struct snd_soc_dapm_route audio_paths[] = { { "DAC", NULL, "SYSCLK" }, { "DAC Output", NULL, "DAC" }, { "DAC Output", NULL, "VMID" }, - { "ADC", NULL, "SYSCLK" }, - { "DAC", NULL, "ADC_PWR" }, - { "ADC", NULL, "ADC_PWR" }, { "DAC1OUT", NULL, "DAC Output" }, { "DAC2OUT", NULL, "DAC Output" }, { "DAC3OUT", NULL, "DAC Output" }, { "DAC4OUT", NULL, "DAC Output" }, + { "SYSCLK", NULL, "PLL_PWR" }, +}; + +static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = { + { "ADC", NULL, "SYSCLK" }, + { "ADC", NULL, "ADC_PWR" }, { "ADC", NULL, "ADC1IN" }, { "ADC", NULL, "ADC2IN" }, - { "SYSCLK", NULL, "PLL_PWR" }, }; +static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x) +{ + switch (ad193x->type) { + case AD1933: + case AD1934: + return false; + default: + break; + } + + return true; +} + /* * DAI ops entries */ @@ -147,8 +168,10 @@ static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT); - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, - AD193X_ADC_CHAN_MASK, channels << AD193X_ADC_CHAN_SHFT); + if (ad193x_has_adc(ad193x)) + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, + AD193X_ADC_CHAN_MASK, + channels << AD193X_ADC_CHAN_SHFT); return 0; } @@ -172,7 +195,9 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, adc_serfmt |= AD193X_ADC_SERFMT_AUX; break; default: - return -EINVAL; + if (ad193x_has_adc(ad193x)) + return -EINVAL; + break; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -217,10 +242,12 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, - AD193X_ADC_SERFMT_MASK, adc_serfmt); - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, - AD193X_ADC_FMT_MASK, adc_fmt); + if (ad193x_has_adc(ad193x)) { + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, + AD193X_ADC_SERFMT_MASK, adc_serfmt); + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, + AD193X_ADC_FMT_MASK, adc_fmt); + } regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, AD193X_DAC_FMT_MASK, dac_fmt); @@ -287,8 +314,9 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream, AD193X_DAC_WORD_LEN_MASK, word_len << AD193X_DAC_WORD_LEN_SHFT); - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, - AD193X_ADC_WORD_LEN_MASK, word_len); + if (ad193x_has_adc(ad193x)) + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, + AD193X_ADC_WORD_LEN_MASK, word_len); return 0; } @@ -326,6 +354,8 @@ static struct snd_soc_dai_driver ad193x_dai = { static int ad193x_codec_probe(struct snd_soc_codec *codec) { struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int num, ret; /* default setting for ad193x */ @@ -335,14 +365,46 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec) regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A); /* dac in tdm mode */ regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40); - /* high-pass filter enable */ - regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3); - /* sata delay=1, adc aux mode */ - regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43); + + /* adc only */ + if (ad193x_has_adc(ad193x)) { + /* high-pass filter enable */ + regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3); + /* sata delay=1, adc aux mode */ + regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43); + } + /* pll input: mclki/xi */ regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */ regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04); + /* adc only */ + if (ad193x_has_adc(ad193x)) { + /* add adc controls */ + num = ARRAY_SIZE(ad193x_adc_snd_controls); + ret = snd_soc_add_codec_controls(codec, + ad193x_adc_snd_controls, + num); + if (ret) + return ret; + + /* add adc widgets */ + num = ARRAY_SIZE(ad193x_adc_widgets); + ret = snd_soc_dapm_new_controls(dapm, + ad193x_adc_widgets, + num); + if (ret) + return ret; + + /* add adc routes */ + num = ARRAY_SIZE(ad193x_adc_audio_paths); + ret = snd_soc_dapm_add_routes(dapm, + ad193x_adc_audio_paths, + num); + if (ret) + return ret; + } + return 0; } @@ -356,18 +418,13 @@ static struct snd_soc_codec_driver soc_codec_dev_ad193x = { .num_dapm_routes = ARRAY_SIZE(audio_paths), }; -static bool adau193x_reg_volatile(struct device *dev, unsigned int reg) -{ - return false; -} - const struct regmap_config ad193x_regmap_config = { .max_register = AD193X_NUM_REGS - 1, - .volatile_reg = adau193x_reg_volatile, }; EXPORT_SYMBOL_GPL(ad193x_regmap_config); -int ad193x_probe(struct device *dev, struct regmap *regmap) +int ad193x_probe(struct device *dev, struct regmap *regmap, + enum ad193x_type type) { struct ad193x_priv *ad193x; @@ -379,6 +436,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap) return -ENOMEM; ad193x->regmap = regmap; + ad193x->type = type; dev_set_drvdata(dev, ad193x); diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h index ab9a998..8b1e65f 100644 --- a/sound/soc/codecs/ad193x.h +++ b/sound/soc/codecs/ad193x.h @@ -13,8 +13,15 @@ struct device; +enum ad193x_type { + AD193X, + AD1933, + AD1934, +}; + extern const struct regmap_config ad193x_regmap_config; -int ad193x_probe(struct device *dev, struct regmap *regmap); +int ad193x_probe(struct device *dev, struct regmap *regmap, + enum ad193x_type type); #define AD193X_PLL_CLK_CTRL0 0x00 #define AD193X_PLL_POWERDOWN 0x01 diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index ac21b85..9929efc 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -316,6 +316,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "Tone Generator 2", "Haptics", "AEC", + "AEC2", "Mic Mute Mixer", "Noise Generator", "IN1L", @@ -423,6 +424,7 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { 0x05, 0x06, /* Haptics */ 0x08, /* AEC */ + 0x09, /* AEC2 */ 0x0c, /* Noise mixer */ 0x0d, /* Comfort noise */ 0x10, /* IN1L */ @@ -527,6 +529,32 @@ EXPORT_SYMBOL_GPL(arizona_mixer_values); const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0); EXPORT_SYMBOL_GPL(arizona_mixer_tlv); +const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = { + "12kHz", "24kHz", "48kHz", "96kHz", "192kHz", + "11.025kHz", "22.05kHz", "44.1kHz", "88.2kHz", "176.4kHz", + "4kHz", "8kHz", "16kHz", "32kHz", +}; +EXPORT_SYMBOL_GPL(arizona_sample_rate_text); + +const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, +}; +EXPORT_SYMBOL_GPL(arizona_sample_rate_val); + +const char *arizona_sample_rate_val_to_name(unsigned int rate_val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(arizona_sample_rate_val); ++i) { + if (arizona_sample_rate_val[i] == rate_val) + return arizona_sample_rate_text[i]; + } + + return "Illegal"; +} +EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name); + const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = { "SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate", }; @@ -1884,6 +1912,11 @@ static int arizona_calc_fratio(struct arizona_fll *fll, if (fll->arizona->rev < 3 || sync) return init_ratio; break; + case WM8998: + case WM1814: + if (sync) + return init_ratio; + break; default: return init_ratio; } diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 7b68d05..fea8b8a 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -93,12 +93,17 @@ struct arizona_priv { bool dvfs_cached; }; -#define ARIZONA_NUM_MIXER_INPUTS 103 +#define ARIZONA_NUM_MIXER_INPUTS 104 extern const unsigned int arizona_mixer_tlv[]; extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; +#define ARIZONA_GAINMUX_CONTROLS(name, base) \ + SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv) + #define ARIZONA_MIXER_CONTROLS(name, base) \ SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ @@ -209,8 +214,12 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; .num_regs = 1 }) } #define ARIZONA_RATE_ENUM_SIZE 4 +#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14 + extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE]; extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE]; +extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE]; +extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE]; extern const struct soc_enum arizona_isrc_fsl[]; extern const struct soc_enum arizona_isrc_fsh[]; @@ -296,4 +305,5 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output, extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift); +extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val); #endif diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index a9c86ef..7278f93 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -12,6 +12,7 @@ * option) any later version. */ +#include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/regmap.h> @@ -1222,23 +1223,44 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if ((da7213->clk_src == clk_id) && (da7213->mclk_rate == freq)) + return 0; + + if (((freq < 5000000) && (freq != 32768)) || (freq > 54000000)) { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } switch (clk_id) { case DA7213_CLKSRC_MCLK: - if ((freq == 32768) || - ((freq >= 5000000) && (freq <= 54000000))) { - da7213->mclk_rate = freq; - return 0; - } else { - dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", - freq); - return -EINVAL; - } + da7213->mclk_squarer_en = false; + break; + case DA7213_CLKSRC_MCLK_SQR: + da7213->mclk_squarer_en = true; break; default: dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); return -EINVAL; } + + da7213->clk_src = clk_id; + + if (da7213->mclk) { + freq = clk_round_rate(da7213->mclk, freq); + ret = clk_set_rate(da7213->mclk, freq); + if (ret) { + dev_err(codec_dai->dev, "Failed to set clock rate %d\n", + freq); + return ret; + } + } + + da7213->mclk_rate = freq; + + return 0; } /* Supported PLL input frequencies are 5MHz - 54MHz. */ @@ -1366,12 +1388,25 @@ static struct snd_soc_dai_driver da7213_dai = { static int da7213_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + int ret; + switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + /* MCLK */ + if (da7213->mclk) { + ret = clk_prepare_enable(da7213->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable mclk\n"); + return ret; + } + } + /* Enable VMID reference & master bias */ snd_soc_update_bits(codec, DA7213_REFERENCES, DA7213_VMID_EN | DA7213_BIAS_EN, @@ -1382,15 +1417,127 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, /* Disable VMID reference & master bias */ snd_soc_update_bits(codec, DA7213_REFERENCES, DA7213_VMID_EN | DA7213_BIAS_EN, 0); + + /* MCLK */ + if (da7213->mclk) + clk_disable_unprepare(da7213->mclk); break; } return 0; } +/* DT */ +static const struct of_device_id da7213_of_match[] = { + { .compatible = "dlg,da7213", }, + { } +}; +MODULE_DEVICE_TABLE(of, da7213_of_match); + +static enum da7213_micbias_voltage + da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1600: + return DA7213_MICBIAS_1_6V; + case 2200: + return DA7213_MICBIAS_2_2V; + case 2500: + return DA7213_MICBIAS_2_5V; + case 3000: + return DA7213_MICBIAS_3_0V; + default: + dev_warn(codec->dev, "Invalid micbias level\n"); + return DA7213_MICBIAS_2_2V; + } +} + +static enum da7213_dmic_data_sel + da7213_of_dmic_data_sel(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "lrise_rfall")) { + return DA7213_DMIC_DATA_LRISE_RFALL; + } else if (!strcmp(str, "lfall_rrise")) { + return DA7213_DMIC_DATA_LFALL_RRISE; + } else { + dev_warn(codec->dev, "Invalid DMIC data select type\n"); + return DA7213_DMIC_DATA_LRISE_RFALL; + } +} + +static enum da7213_dmic_samplephase + da7213_of_dmic_samplephase(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "on_clkedge")) { + return DA7213_DMIC_SAMPLE_ON_CLKEDGE; + } else if (!strcmp(str, "between_clkedge")) { + return DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE; + } else { + dev_warn(codec->dev, "Invalid DMIC sample phase\n"); + return DA7213_DMIC_SAMPLE_ON_CLKEDGE; + } +} + +static enum da7213_dmic_clk_rate + da7213_of_dmic_clkrate(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1500000: + return DA7213_DMIC_CLK_1_5MHZ; + case 3000000: + return DA7213_DMIC_CLK_3_0MHZ; + default: + dev_warn(codec->dev, "Invalid DMIC clock rate\n"); + return DA7213_DMIC_CLK_1_5MHZ; + } +} + +static struct da7213_platform_data + *da7213_of_to_pdata(struct snd_soc_codec *codec) +{ + struct device_node *np = codec->dev->of_node; + struct da7213_platform_data *pdata; + const char *of_str; + u32 of_val32; + + pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_warn(codec->dev, "Failed to allocate memory for pdata\n"); + return NULL; + } + + if (of_property_read_u32(np, "dlg,micbias1-lvl", &of_val32) >= 0) + pdata->micbias1_lvl = da7213_of_micbias_lvl(codec, of_val32); + else + pdata->micbias1_lvl = DA7213_MICBIAS_2_2V; + + if (of_property_read_u32(np, "dlg,micbias2-lvl", &of_val32) >= 0) + pdata->micbias2_lvl = da7213_of_micbias_lvl(codec, of_val32); + else + pdata->micbias2_lvl = DA7213_MICBIAS_2_2V; + + if (!of_property_read_string(np, "dlg,dmic-data-sel", &of_str)) + pdata->dmic_data_sel = da7213_of_dmic_data_sel(codec, of_str); + else + pdata->dmic_data_sel = DA7213_DMIC_DATA_LRISE_RFALL; + + if (!of_property_read_string(np, "dlg,dmic-samplephase", &of_str)) + pdata->dmic_samplephase = + da7213_of_dmic_samplephase(codec, of_str); + else + pdata->dmic_samplephase = DA7213_DMIC_SAMPLE_ON_CLKEDGE; + + if (of_property_read_u32(np, "dlg,dmic-clkrate", &of_val32) >= 0) + pdata->dmic_clk_rate = da7213_of_dmic_clkrate(codec, of_val32); + else + pdata->dmic_clk_rate = DA7213_DMIC_CLK_3_0MHZ; + + return pdata; +} + + static int da7213_probe(struct snd_soc_codec *codec) { struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); - struct da7213_platform_data *pdata = da7213->pdata; /* Default to using ALC auto offset calibration mode. */ snd_soc_update_bits(codec, DA7213_ALC_CTRL1, @@ -1450,8 +1597,15 @@ static int da7213_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, DA7213_LINE_CTRL, DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE); + /* Handle DT/Platform data */ + if (codec->dev->of_node) + da7213->pdata = da7213_of_to_pdata(codec); + else + da7213->pdata = dev_get_platdata(codec->dev); + /* Set platform data values */ if (da7213->pdata) { + struct da7213_platform_data *pdata = da7213->pdata; u8 micbias_lvl = 0, dmic_cfg = 0; /* Set Mic Bias voltages */ @@ -1503,10 +1657,17 @@ static int da7213_probe(struct snd_soc_codec *codec) DA7213_DMIC_DATA_SEL_MASK | DA7213_DMIC_SAMPLEPHASE_MASK | DA7213_DMIC_CLK_RATE_MASK, dmic_cfg); + } - /* Set MCLK squaring */ - da7213->mclk_squarer_en = pdata->mclk_squaring; + /* Check if MCLK provided */ + da7213->mclk = devm_clk_get(codec->dev, "mclk"); + if (IS_ERR(da7213->mclk)) { + if (PTR_ERR(da7213->mclk) != -ENOENT) + return PTR_ERR(da7213->mclk); + else + da7213->mclk = NULL; } + return 0; } @@ -1537,7 +1698,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct da7213_priv *da7213; - struct da7213_platform_data *pdata = dev_get_platdata(&i2c->dev); int ret; da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv), @@ -1545,9 +1705,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c, if (!da7213) return -ENOMEM; - if (pdata) - da7213->pdata = pdata; - i2c_set_clientdata(i2c, da7213); da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config); @@ -1582,6 +1739,7 @@ MODULE_DEVICE_TABLE(i2c, da7213_i2c_id); static struct i2c_driver da7213_i2c_driver = { .driver = { .name = "da7213", + .of_match_table = of_match_ptr(da7213_of_match), }, .probe = da7213_i2c_probe, .remove = da7213_remove, diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index 9cb9ddd..030fd69 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -13,6 +13,7 @@ #ifndef _DA7213_H #define _DA7213_H +#include <linux/clk.h> #include <linux/regmap.h> #include <sound/da7213.h> @@ -504,14 +505,17 @@ #define DA7213_PLL_INDIV_20_40_MHZ_VAL 8 #define DA7213_PLL_INDIV_40_54_MHZ_VAL 16 -enum clk_src { - DA7213_CLKSRC_MCLK +enum da7213_clk_src { + DA7213_CLKSRC_MCLK = 0, + DA7213_CLKSRC_MCLK_SQR, }; /* Codec private data */ struct da7213_priv { struct regmap *regmap; + struct clk *mclk; unsigned int mclk_rate; + int clk_src; bool master; bool mclk_squarer_en; bool srm_en; diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c new file mode 100644 index 0000000..9459593 --- /dev/null +++ b/sound/soc/codecs/da7219-aad.c @@ -0,0 +1,823 @@ +/* + * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver + * + * Copyright (c) 2015 Dialog Semiconductor Ltd. + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/pm_wakeirq.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/da7219.h> + +#include "da7219.h" +#include "da7219-aad.h" + + +/* + * Detection control + */ + +void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + da7219->aad->jack = jack; + da7219->aad->jack_inserted = false; + + /* Send an initial empty report */ + snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK); + + /* Enable/Disable jack detection */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_ACCDET_EN_MASK, + (jack ? DA7219_ACCDET_EN_MASK : 0)); +} +EXPORT_SYMBOL_GPL(da7219_aad_jack_det); + +/* + * Button/HPTest work + */ + +static void da7219_aad_btn_det_work(struct work_struct *work) +{ + struct da7219_aad_priv *da7219_aad = + container_of(work, struct da7219_aad_priv, btn_det_work); + struct snd_soc_codec *codec = da7219_aad->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + u8 statusa, micbias_ctrl; + bool micbias_up = false; + int retries = 0; + + /* Drive headphones/lineout */ + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK, + DA7219_HP_L_AMP_OE_MASK); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK, + DA7219_HP_R_AMP_OE_MASK); + + /* Make sure mic bias is up */ + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + + do { + statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A); + if (statusa & DA7219_MICBIAS_UP_STS_MASK) + micbias_up = true; + else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES) + msleep(DA7219_AAD_MICBIAS_CHK_DELAY); + } while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES)); + + if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES) + dev_warn(codec->dev, "Mic bias status check timed out"); + + /* + * Mic bias pulse required to enable mic, must be done before enabling + * button detection to prevent erroneous button readings. + */ + if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) { + /* Pulse higher level voltage */ + micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL); + snd_soc_update_bits(codec, DA7219_MICBIAS_CTRL, + DA7219_MICBIAS1_LEVEL_MASK, + da7219_aad->micbias_pulse_lvl); + msleep(da7219_aad->micbias_pulse_time); + snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_ctrl); + + } + + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, + da7219_aad->btn_cfg); +} + +static void da7219_aad_hptest_work(struct work_struct *work) +{ + struct da7219_aad_priv *da7219_aad = + container_of(work, struct da7219_aad_priv, hptest_work); + struct snd_soc_codec *codec = da7219_aad->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + u16 tonegen_freq_hptest; + u8 accdet_cfg8; + int report = 0; + + /* Lock DAPM and any Kcontrols that are affected by this test */ + snd_soc_dapm_mutex_lock(dapm); + mutex_lock(&da7219->lock); + + /* Bypass cache so it saves current settings */ + regcache_cache_bypass(da7219->regmap, true); + + /* Make sure Tone Generator is disabled */ + snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0); + + /* Enable HPTest block, 1KOhms check */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8, + DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK, + DA7219_HPTEST_EN_MASK | + DA7219_HPTEST_RES_SEL_1KOHMS); + + /* Set gains to 0db */ + snd_soc_write(codec, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB); + snd_soc_write(codec, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB); + snd_soc_write(codec, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB); + snd_soc_write(codec, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB); + + /* Disable DAC filters, EQs and soft mute */ + snd_soc_update_bits(codec, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK, + 0); + snd_soc_update_bits(codec, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK, + 0); + snd_soc_update_bits(codec, DA7219_DAC_FILTERS5, + DA7219_DAC_SOFTMUTE_EN_MASK, 0); + + /* Enable HP left & right paths */ + snd_soc_update_bits(codec, DA7219_CP_CTRL, DA7219_CP_EN_MASK, + DA7219_CP_EN_MASK); + snd_soc_update_bits(codec, DA7219_DIG_ROUTING_DAC, + DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK, + DA7219_DAC_L_SRC_TONEGEN | + DA7219_DAC_R_SRC_TONEGEN); + snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, + DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK, + DA7219_DAC_L_EN_MASK); + snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, + DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK, + DA7219_DAC_R_EN_MASK); + snd_soc_update_bits(codec, DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_L_MIX_SELECT_MASK, + DA7219_MIXOUT_L_MIX_SELECT_MASK); + snd_soc_update_bits(codec, DA7219_MIXOUT_R_SELECT, + DA7219_MIXOUT_R_MIX_SELECT_MASK, + DA7219_MIXOUT_R_MIX_SELECT_MASK); + snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1L, + DA7219_OUTFILT_ST_1L_SRC_MASK, + DA7219_DMIX_ST_SRC_OUTFILT1L); + snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1R, + DA7219_OUTFILT_ST_1R_SRC_MASK, + DA7219_DMIX_ST_SRC_OUTFILT1R); + snd_soc_update_bits(codec, DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_L_AMP_EN_MASK, + DA7219_MIXOUT_L_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL, + DA7219_MIXOUT_R_AMP_EN_MASK, + DA7219_MIXOUT_R_AMP_EN_MASK); + snd_soc_write(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK); + snd_soc_write(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK); + + /* Configure & start Tone Generator */ + snd_soc_write(codec, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK); + tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ); + regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L, + &tonegen_freq_hptest, sizeof(tonegen_freq_hptest)); + snd_soc_update_bits(codec, DA7219_TONE_GEN_CFG2, + DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK, + DA7219_SWG_SEL_SRAMP | + DA7219_TONE_GEN_GAIN_MINUS_15DB); + snd_soc_write(codec, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK); + + msleep(DA7219_AAD_HPTEST_PERIOD); + + /* Grab comparator reading */ + accdet_cfg8 = snd_soc_read(codec, DA7219_ACCDET_CONFIG_8); + if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK) + report |= SND_JACK_HEADPHONE; + else + report |= SND_JACK_LINEOUT; + + /* Stop tone generator */ + snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0); + + msleep(DA7219_AAD_HPTEST_PERIOD); + + /* Restore original settings from cache */ + regcache_mark_dirty(da7219->regmap); + regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL); + regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_R_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L, + DA7219_DROUTING_ST_OUTFILT_1R); + regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_R_SELECT); + regcache_sync_region(da7219->regmap, DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DIG_ROUTING_DAC, + DA7219_DIG_ROUTING_DAC); + regcache_sync_region(da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS5, + DA7219_DAC_FILTERS5); + regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS4, + DA7219_DAC_FILTERS1); + regcache_sync_region(da7219->regmap, DA7219_HP_L_GAIN, + DA7219_HP_R_GAIN); + regcache_sync_region(da7219->regmap, DA7219_DAC_L_GAIN, + DA7219_DAC_R_GAIN); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_ON_PER, + DA7219_TONE_GEN_ON_PER); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_FREQ1_L, + DA7219_TONE_GEN_FREQ1_U); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_CFG1, + DA7219_TONE_GEN_CFG2); + + regcache_cache_bypass(da7219->regmap, false); + + /* Disable HPTest block */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8, + DA7219_HPTEST_EN_MASK, 0); + + /* Drive Headphones/lineout */ + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK, + DA7219_HP_L_AMP_OE_MASK); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK, + DA7219_HP_R_AMP_OE_MASK); + + mutex_unlock(&da7219->lock); + snd_soc_dapm_mutex_unlock(dapm); + + /* + * Only send report if jack hasn't been removed during process, + * otherwise it's invalid and we drop it. + */ + if (da7219_aad->jack_inserted) + snd_soc_jack_report(da7219_aad->jack, report, + SND_JACK_HEADSET | SND_JACK_LINEOUT); +} + + +/* + * IRQ + */ + +static irqreturn_t da7219_aad_irq_thread(int irq, void *data) +{ + struct da7219_aad_priv *da7219_aad = data; + struct snd_soc_codec *codec = da7219_aad->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 events[DA7219_AAD_IRQ_REG_MAX]; + u8 statusa; + int i, report = 0, mask = 0; + + /* Read current IRQ events */ + regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A, + events, DA7219_AAD_IRQ_REG_MAX); + + if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B]) + return IRQ_NONE; + + /* Read status register for jack insertion & type status */ + statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A); + + /* Clear events */ + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A, + events, DA7219_AAD_IRQ_REG_MAX); + + dev_dbg(codec->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n", + events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B], + statusa); + + if (statusa & DA7219_JACK_INSERTION_STS_MASK) { + /* Jack Insertion */ + if (events[DA7219_AAD_IRQ_REG_A] & + DA7219_E_JACK_INSERTED_MASK) { + report |= SND_JACK_MECHANICAL; + mask |= SND_JACK_MECHANICAL; + da7219_aad->jack_inserted = true; + } + + /* Jack type detection */ + if (events[DA7219_AAD_IRQ_REG_A] & + DA7219_E_JACK_DETECT_COMPLETE_MASK) { + /* + * If 4-pole, then enable button detection, else perform + * HP impedance test to determine output type to report. + * + * We schedule work here as the tasks themselves can + * take time to complete, and in particular for hptest + * we want to be able to check if the jack was removed + * during the procedure as this will invalidate the + * result. By doing this as work, the IRQ thread can + * handle a removal, and we can check at the end of + * hptest if we have a valid result or not. + */ + if (statusa & DA7219_JACK_TYPE_STS_MASK) { + report |= SND_JACK_HEADSET; + mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT; + schedule_work(&da7219_aad->btn_det_work); + } else { + schedule_work(&da7219_aad->hptest_work); + } + } + + /* Button support for 4-pole jack */ + if (statusa & DA7219_JACK_TYPE_STS_MASK) { + for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) { + /* Button Press */ + if (events[DA7219_AAD_IRQ_REG_B] & + (DA7219_E_BUTTON_A_PRESSED_MASK << i)) { + report |= SND_JACK_BTN_0 >> i; + mask |= SND_JACK_BTN_0 >> i; + } + } + snd_soc_jack_report(da7219_aad->jack, report, mask); + + for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) { + /* Button Release */ + if (events[DA7219_AAD_IRQ_REG_B] & + (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) { + report &= ~(SND_JACK_BTN_0 >> i); + mask |= SND_JACK_BTN_0 >> i; + } + } + } + } else { + /* Jack removal */ + if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) { + report = 0; + mask |= DA7219_AAD_REPORT_ALL_MASK; + da7219_aad->jack_inserted = false; + + /* Un-drive headphones/lineout */ + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK, 0); + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK, 0); + + /* Ensure button detection disabled */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, 0); + + /* Disable mic bias */ + snd_soc_dapm_disable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + + /* Cancel any pending work */ + cancel_work_sync(&da7219_aad->btn_det_work); + cancel_work_sync(&da7219_aad->hptest_work); + } + } + + snd_soc_jack_report(da7219_aad->jack, report, mask); + + return IRQ_HANDLED; +} + +/* + * DT to pdata conversion + */ + +static enum da7219_aad_micbias_pulse_lvl + da7219_aad_of_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 2800: + return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V; + case 2900: + return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V; + default: + dev_warn(codec->dev, "Invalid micbias pulse level"); + return DA7219_AAD_MICBIAS_PULSE_LVL_OFF; + } +} + +static enum da7219_aad_btn_cfg + da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 2: + return DA7219_AAD_BTN_CFG_2MS; + case 5: + return DA7219_AAD_BTN_CFG_5MS; + case 10: + return DA7219_AAD_BTN_CFG_10MS; + case 50: + return DA7219_AAD_BTN_CFG_50MS; + case 100: + return DA7219_AAD_BTN_CFG_100MS; + case 200: + return DA7219_AAD_BTN_CFG_200MS; + case 500: + return DA7219_AAD_BTN_CFG_500MS; + default: + dev_warn(codec->dev, "Invalid button config"); + return DA7219_AAD_BTN_CFG_10MS; + } +} + +static enum da7219_aad_mic_det_thr + da7219_aad_of_mic_det_thr(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 200: + return DA7219_AAD_MIC_DET_THR_200_OHMS; + case 500: + return DA7219_AAD_MIC_DET_THR_500_OHMS; + case 750: + return DA7219_AAD_MIC_DET_THR_750_OHMS; + case 1000: + return DA7219_AAD_MIC_DET_THR_1000_OHMS; + default: + dev_warn(codec->dev, "Invalid mic detect threshold"); + return DA7219_AAD_MIC_DET_THR_500_OHMS; + } +} + +static enum da7219_aad_jack_ins_deb + da7219_aad_of_jack_ins_deb(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 5: + return DA7219_AAD_JACK_INS_DEB_5MS; + case 10: + return DA7219_AAD_JACK_INS_DEB_10MS; + case 20: + return DA7219_AAD_JACK_INS_DEB_20MS; + case 50: + return DA7219_AAD_JACK_INS_DEB_50MS; + case 100: + return DA7219_AAD_JACK_INS_DEB_100MS; + case 200: + return DA7219_AAD_JACK_INS_DEB_200MS; + case 500: + return DA7219_AAD_JACK_INS_DEB_500MS; + case 1000: + return DA7219_AAD_JACK_INS_DEB_1S; + default: + dev_warn(codec->dev, "Invalid jack insert debounce"); + return DA7219_AAD_JACK_INS_DEB_20MS; + } +} + +static enum da7219_aad_jack_det_rate + da7219_aad_of_jack_det_rate(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "32ms_64ms")) { + return DA7219_AAD_JACK_DET_RATE_32_64MS; + } else if (!strcmp(str, "64ms_128ms")) { + return DA7219_AAD_JACK_DET_RATE_64_128MS; + } else if (!strcmp(str, "128ms_256ms")) { + return DA7219_AAD_JACK_DET_RATE_128_256MS; + } else if (!strcmp(str, "256ms_512ms")) { + return DA7219_AAD_JACK_DET_RATE_256_512MS; + } else { + dev_warn(codec->dev, "Invalid jack detect rate"); + return DA7219_AAD_JACK_DET_RATE_256_512MS; + } +} + +static enum da7219_aad_jack_rem_deb + da7219_aad_of_jack_rem_deb(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_JACK_REM_DEB_1MS; + case 5: + return DA7219_AAD_JACK_REM_DEB_5MS; + case 10: + return DA7219_AAD_JACK_REM_DEB_10MS; + case 20: + return DA7219_AAD_JACK_REM_DEB_20MS; + default: + dev_warn(codec->dev, "Invalid jack removal debounce"); + return DA7219_AAD_JACK_REM_DEB_1MS; + } +} + +static enum da7219_aad_btn_avg + da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_BTN_AVG_1; + case 2: + return DA7219_AAD_BTN_AVG_2; + case 4: + return DA7219_AAD_BTN_AVG_4; + case 8: + return DA7219_AAD_BTN_AVG_8; + default: + dev_warn(codec->dev, "Invalid button average value"); + return DA7219_AAD_BTN_AVG_2; + } +} + +static enum da7219_aad_adc_1bit_rpt + da7219_aad_of_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_ADC_1BIT_RPT_1; + case 2: + return DA7219_AAD_ADC_1BIT_RPT_2; + case 4: + return DA7219_AAD_ADC_1BIT_RPT_4; + case 8: + return DA7219_AAD_ADC_1BIT_RPT_8; + default: + dev_warn(codec->dev, "Invalid ADC 1-bit repeat value"); + return DA7219_AAD_ADC_1BIT_RPT_1; + } +} + +static struct da7219_aad_pdata *da7219_aad_of_to_pdata(struct snd_soc_codec *codec) +{ + struct device_node *np = codec->dev->of_node; + struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad"); + struct da7219_aad_pdata *aad_pdata; + const char *of_str; + u32 of_val32; + + if (!aad_np) + return NULL; + + aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL); + if (!aad_pdata) + goto out; + + aad_pdata->irq = irq_of_parse_and_map(np, 0); + + if (of_property_read_u32(aad_np, "dlg,micbias-pulse-lvl", + &of_val32) >= 0) + aad_pdata->micbias_pulse_lvl = + da7219_aad_of_micbias_pulse_lvl(codec, of_val32); + else + aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF; + + if (of_property_read_u32(aad_np, "dlg,micbias-pulse-time", + &of_val32) >= 0) + aad_pdata->micbias_pulse_time = of_val32; + + if (of_property_read_u32(aad_np, "dlg,btn-cfg", &of_val32) >= 0) + aad_pdata->btn_cfg = da7219_aad_of_btn_cfg(codec, of_val32); + else + aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS; + + if (of_property_read_u32(aad_np, "dlg,mic-det-thr", &of_val32) >= 0) + aad_pdata->mic_det_thr = + da7219_aad_of_mic_det_thr(codec, of_val32); + else + aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS; + + if (of_property_read_u32(aad_np, "dlg,jack-ins-deb", &of_val32) >= 0) + aad_pdata->jack_ins_deb = + da7219_aad_of_jack_ins_deb(codec, of_val32); + else + aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS; + + if (!of_property_read_string(aad_np, "dlg,jack-det-rate", &of_str)) + aad_pdata->jack_det_rate = + da7219_aad_of_jack_det_rate(codec, of_str); + else + aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS; + + if (of_property_read_u32(aad_np, "dlg,jack-rem-deb", &of_val32) >= 0) + aad_pdata->jack_rem_deb = + da7219_aad_of_jack_rem_deb(codec, of_val32); + else + aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS; + + if (of_property_read_u32(aad_np, "dlg,a-d-btn-thr", &of_val32) >= 0) + aad_pdata->a_d_btn_thr = (u8) of_val32; + else + aad_pdata->a_d_btn_thr = 0xA; + + if (of_property_read_u32(aad_np, "dlg,d-b-btn-thr", &of_val32) >= 0) + aad_pdata->d_b_btn_thr = (u8) of_val32; + else + aad_pdata->d_b_btn_thr = 0x16; + + if (of_property_read_u32(aad_np, "dlg,b-c-btn-thr", &of_val32) >= 0) + aad_pdata->b_c_btn_thr = (u8) of_val32; + else + aad_pdata->b_c_btn_thr = 0x21; + + if (of_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &of_val32) >= 0) + aad_pdata->c_mic_btn_thr = (u8) of_val32; + else + aad_pdata->c_mic_btn_thr = 0x3E; + + if (of_property_read_u32(aad_np, "dlg,btn-avg", &of_val32) >= 0) + aad_pdata->btn_avg = da7219_aad_of_btn_avg(codec, of_val32); + else + aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2; + + if (of_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &of_val32) >= 0) + aad_pdata->adc_1bit_rpt = + da7219_aad_of_adc_1bit_rpt(codec, of_val32); + else + aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1; + +out: + of_node_put(aad_np); + + return aad_pdata; +} + +static void da7219_aad_handle_pdata(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_aad_priv *da7219_aad = da7219->aad; + struct da7219_pdata *pdata = da7219->pdata; + + if ((pdata) && (pdata->aad_pdata)) { + struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata; + u8 cfg, mask; + + da7219_aad->irq = aad_pdata->irq; + + switch (aad_pdata->micbias_pulse_lvl) { + case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V: + case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V: + da7219_aad->micbias_pulse_lvl = + (aad_pdata->micbias_pulse_lvl << + DA7219_MICBIAS1_LEVEL_SHIFT); + break; + default: + break; + } + + da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time; + + switch (aad_pdata->btn_cfg) { + case DA7219_AAD_BTN_CFG_2MS: + case DA7219_AAD_BTN_CFG_5MS: + case DA7219_AAD_BTN_CFG_10MS: + case DA7219_AAD_BTN_CFG_50MS: + case DA7219_AAD_BTN_CFG_100MS: + case DA7219_AAD_BTN_CFG_200MS: + case DA7219_AAD_BTN_CFG_500MS: + da7219_aad->btn_cfg = (aad_pdata->btn_cfg << + DA7219_BUTTON_CONFIG_SHIFT); + } + + cfg = 0; + mask = 0; + switch (aad_pdata->mic_det_thr) { + case DA7219_AAD_MIC_DET_THR_200_OHMS: + case DA7219_AAD_MIC_DET_THR_500_OHMS: + case DA7219_AAD_MIC_DET_THR_750_OHMS: + case DA7219_AAD_MIC_DET_THR_1000_OHMS: + cfg |= (aad_pdata->mic_det_thr << + DA7219_MIC_DET_THRESH_SHIFT); + mask |= DA7219_MIC_DET_THRESH_MASK; + } + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, mask, cfg); + + cfg = 0; + mask = 0; + switch (aad_pdata->jack_ins_deb) { + case DA7219_AAD_JACK_INS_DEB_5MS: + case DA7219_AAD_JACK_INS_DEB_10MS: + case DA7219_AAD_JACK_INS_DEB_20MS: + case DA7219_AAD_JACK_INS_DEB_50MS: + case DA7219_AAD_JACK_INS_DEB_100MS: + case DA7219_AAD_JACK_INS_DEB_200MS: + case DA7219_AAD_JACK_INS_DEB_500MS: + case DA7219_AAD_JACK_INS_DEB_1S: + cfg |= (aad_pdata->jack_ins_deb << + DA7219_JACKDET_DEBOUNCE_SHIFT); + mask |= DA7219_JACKDET_DEBOUNCE_MASK; + } + switch (aad_pdata->jack_det_rate) { + case DA7219_AAD_JACK_DET_RATE_32_64MS: + case DA7219_AAD_JACK_DET_RATE_64_128MS: + case DA7219_AAD_JACK_DET_RATE_128_256MS: + case DA7219_AAD_JACK_DET_RATE_256_512MS: + cfg |= (aad_pdata->jack_det_rate << + DA7219_JACK_DETECT_RATE_SHIFT); + mask |= DA7219_JACK_DETECT_RATE_MASK; + } + switch (aad_pdata->jack_rem_deb) { + case DA7219_AAD_JACK_REM_DEB_1MS: + case DA7219_AAD_JACK_REM_DEB_5MS: + case DA7219_AAD_JACK_REM_DEB_10MS: + case DA7219_AAD_JACK_REM_DEB_20MS: + cfg |= (aad_pdata->jack_rem_deb << + DA7219_JACKDET_REM_DEB_SHIFT); + mask |= DA7219_JACKDET_REM_DEB_MASK; + } + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_2, mask, cfg); + + snd_soc_write(codec, DA7219_ACCDET_CONFIG_3, + aad_pdata->a_d_btn_thr); + snd_soc_write(codec, DA7219_ACCDET_CONFIG_4, + aad_pdata->d_b_btn_thr); + snd_soc_write(codec, DA7219_ACCDET_CONFIG_5, + aad_pdata->b_c_btn_thr); + snd_soc_write(codec, DA7219_ACCDET_CONFIG_6, + aad_pdata->c_mic_btn_thr); + + cfg = 0; + mask = 0; + switch (aad_pdata->btn_avg) { + case DA7219_AAD_BTN_AVG_1: + case DA7219_AAD_BTN_AVG_2: + case DA7219_AAD_BTN_AVG_4: + case DA7219_AAD_BTN_AVG_8: + cfg |= (aad_pdata->btn_avg << + DA7219_BUTTON_AVERAGE_SHIFT); + mask |= DA7219_BUTTON_AVERAGE_MASK; + } + switch (aad_pdata->adc_1bit_rpt) { + case DA7219_AAD_ADC_1BIT_RPT_1: + case DA7219_AAD_ADC_1BIT_RPT_2: + case DA7219_AAD_ADC_1BIT_RPT_4: + case DA7219_AAD_ADC_1BIT_RPT_8: + cfg |= (aad_pdata->adc_1bit_rpt << + DA7219_ADC_1_BIT_REPEAT_SHIFT); + mask |= DA7219_ADC_1_BIT_REPEAT_MASK; + } + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_7, mask, cfg); + } +} + + +/* + * Init/Exit + */ + +int da7219_aad_init(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_aad_priv *da7219_aad; + u8 mask[DA7219_AAD_IRQ_REG_MAX]; + int ret; + + da7219_aad = devm_kzalloc(codec->dev, sizeof(*da7219_aad), GFP_KERNEL); + if (!da7219_aad) + return -ENOMEM; + + da7219->aad = da7219_aad; + da7219_aad->codec = codec; + + /* Handle any DT/platform data */ + if ((codec->dev->of_node) && (da7219->pdata)) + da7219->pdata->aad_pdata = da7219_aad_of_to_pdata(codec); + + da7219_aad_handle_pdata(codec); + + /* Disable button detection */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, 0); + + INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work); + INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work); + + ret = request_threaded_irq(da7219_aad->irq, NULL, + da7219_aad_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "da7219-aad", da7219_aad); + if (ret) { + dev_err(codec->dev, "Failed to request IRQ: %d\n", ret); + return ret; + } + + /* Unmask AAD IRQs */ + memset(mask, 0, DA7219_AAD_IRQ_REG_MAX); + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A, + &mask, DA7219_AAD_IRQ_REG_MAX); + + return 0; +} +EXPORT_SYMBOL_GPL(da7219_aad_init); + +void da7219_aad_exit(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_aad_priv *da7219_aad = da7219->aad; + u8 mask[DA7219_AAD_IRQ_REG_MAX]; + + /* Mask off AAD IRQs */ + memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX); + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A, + mask, DA7219_AAD_IRQ_REG_MAX); + + free_irq(da7219_aad->irq, da7219_aad); + + cancel_work_sync(&da7219_aad->btn_det_work); + cancel_work_sync(&da7219_aad->hptest_work); +} +EXPORT_SYMBOL_GPL(da7219_aad_exit); + +MODULE_DESCRIPTION("ASoC DA7219 AAD Driver"); +MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h new file mode 100644 index 0000000..4fccf67 --- /dev/null +++ b/sound/soc/codecs/da7219-aad.h @@ -0,0 +1,212 @@ +/* + * da7219-aad.h - DA7322 ASoC AAD Driver + * + * Copyright (c) 2015 Dialog Semiconductor Ltd. + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __DA7219_AAD_H +#define __DA7219_AAD_H + +#include <linux/timer.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/da7219-aad.h> + +/* + * Registers + */ + +#define DA7219_ACCDET_STATUS_A 0xC0 +#define DA7219_ACCDET_STATUS_B 0xC1 +#define DA7219_ACCDET_IRQ_EVENT_A 0xC2 +#define DA7219_ACCDET_IRQ_EVENT_B 0xC3 +#define DA7219_ACCDET_IRQ_MASK_A 0xC4 +#define DA7219_ACCDET_IRQ_MASK_B 0xC5 +#define DA7219_ACCDET_CONFIG_1 0xC6 +#define DA7219_ACCDET_CONFIG_2 0xC7 +#define DA7219_ACCDET_CONFIG_3 0xC8 +#define DA7219_ACCDET_CONFIG_4 0xC9 +#define DA7219_ACCDET_CONFIG_5 0xCA +#define DA7219_ACCDET_CONFIG_6 0xCB +#define DA7219_ACCDET_CONFIG_7 0xCC +#define DA7219_ACCDET_CONFIG_8 0xCD + + +/* + * Bit Fields + */ + +/* DA7219_ACCDET_STATUS_A = 0xC0 */ +#define DA7219_JACK_INSERTION_STS_SHIFT 0 +#define DA7219_JACK_INSERTION_STS_MASK (0x1 << 0) +#define DA7219_JACK_TYPE_STS_SHIFT 1 +#define DA7219_JACK_TYPE_STS_MASK (0x1 << 1) +#define DA7219_JACK_PIN_ORDER_STS_SHIFT 2 +#define DA7219_JACK_PIN_ORDER_STS_MASK (0x1 << 2) +#define DA7219_MICBIAS_UP_STS_SHIFT 3 +#define DA7219_MICBIAS_UP_STS_MASK (0x1 << 3) + +/* DA7219_ACCDET_STATUS_B = 0xC1 */ +#define DA7219_BUTTON_TYPE_STS_SHIFT 0 +#define DA7219_BUTTON_TYPE_STS_MASK (0xFF << 0) + +/* DA7219_ACCDET_IRQ_EVENT_A = 0xC2 */ +#define DA7219_E_JACK_INSERTED_SHIFT 0 +#define DA7219_E_JACK_INSERTED_MASK (0x1 << 0) +#define DA7219_E_JACK_REMOVED_SHIFT 1 +#define DA7219_E_JACK_REMOVED_MASK (0x1 << 1) +#define DA7219_E_JACK_DETECT_COMPLETE_SHIFT 2 +#define DA7219_E_JACK_DETECT_COMPLETE_MASK (0x1 << 2) + +/* DA7219_ACCDET_IRQ_EVENT_B = 0xC3 */ +#define DA7219_E_BUTTON_A_PRESSED_SHIFT 0 +#define DA7219_E_BUTTON_A_PRESSED_MASK (0x1 << 0) +#define DA7219_E_BUTTON_B_PRESSED_SHIFT 1 +#define DA7219_E_BUTTON_B_PRESSED_MASK (0x1 << 1) +#define DA7219_E_BUTTON_C_PRESSED_SHIFT 2 +#define DA7219_E_BUTTON_C_PRESSED_MASK (0x1 << 2) +#define DA7219_E_BUTTON_D_PRESSED_SHIFT 3 +#define DA7219_E_BUTTON_D_PRESSED_MASK (0x1 << 3) +#define DA7219_E_BUTTON_D_RELEASED_SHIFT 4 +#define DA7219_E_BUTTON_D_RELEASED_MASK (0x1 << 4) +#define DA7219_E_BUTTON_C_RELEASED_SHIFT 5 +#define DA7219_E_BUTTON_C_RELEASED_MASK (0x1 << 5) +#define DA7219_E_BUTTON_B_RELEASED_SHIFT 6 +#define DA7219_E_BUTTON_B_RELEASED_MASK (0x1 << 6) +#define DA7219_E_BUTTON_A_RELEASED_SHIFT 7 +#define DA7219_E_BUTTON_A_RELEASED_MASK (0x1 << 7) + +/* DA7219_ACCDET_IRQ_MASK_A = 0xC4 */ +#define DA7219_M_JACK_INSERTED_SHIFT 0 +#define DA7219_M_JACK_INSERTED_MASK (0x1 << 0) +#define DA7219_M_JACK_REMOVED_SHIFT 1 +#define DA7219_M_JACK_REMOVED_MASK (0x1 << 1) +#define DA7219_M_JACK_DETECT_COMPLETE_SHIFT 2 +#define DA7219_M_JACK_DETECT_COMPLETE_MASK (0x1 << 2) + +/* DA7219_ACCDET_IRQ_MASK_B = 0xC5 */ +#define DA7219_M_BUTTON_A_PRESSED_SHIFT 0 +#define DA7219_M_BUTTON_A_PRESSED_MASK (0x1 << 0) +#define DA7219_M_BUTTON_B_PRESSED_SHIFT 1 +#define DA7219_M_BUTTON_B_PRESSED_MASK (0x1 << 1) +#define DA7219_M_BUTTON_C_PRESSED_SHIFT 2 +#define DA7219_M_BUTTON_C_PRESSED_MASK (0x1 << 2) +#define DA7219_M_BUTTON_D_PRESSED_SHIFT 3 +#define DA7219_M_BUTTON_D_PRESSED_MASK (0x1 << 3) +#define DA7219_M_BUTTON_D_RELEASED_SHIFT 4 +#define DA7219_M_BUTTON_D_RELEASED_MASK (0x1 << 4) +#define DA7219_M_BUTTON_C_RELEASED_SHIFT 5 +#define DA7219_M_BUTTON_C_RELEASED_MASK (0x1 << 5) +#define DA7219_M_BUTTON_B_RELEASED_SHIFT 6 +#define DA7219_M_BUTTON_B_RELEASED_MASK (0x1 << 6) +#define DA7219_M_BUTTON_A_RELEASED_SHIFT 7 +#define DA7219_M_BUTTON_A_RELEASED_MASK (0x1 << 7) + +/* DA7219_ACCDET_CONFIG_1 = 0xC6 */ +#define DA7219_ACCDET_EN_SHIFT 0 +#define DA7219_ACCDET_EN_MASK (0x1 << 0) +#define DA7219_BUTTON_CONFIG_SHIFT 1 +#define DA7219_BUTTON_CONFIG_MASK (0x7 << 1) +#define DA7219_MIC_DET_THRESH_SHIFT 4 +#define DA7219_MIC_DET_THRESH_MASK (0x3 << 4) +#define DA7219_JACK_TYPE_DET_EN_SHIFT 6 +#define DA7219_JACK_TYPE_DET_EN_MASK (0x1 << 6) +#define DA7219_PIN_ORDER_DET_EN_SHIFT 7 +#define DA7219_PIN_ORDER_DET_EN_MASK (0x1 << 7) + +/* DA7219_ACCDET_CONFIG_2 = 0xC7 */ +#define DA7219_ACCDET_PAUSE_SHIFT 0 +#define DA7219_ACCDET_PAUSE_MASK (0x1 << 0) +#define DA7219_JACKDET_DEBOUNCE_SHIFT 1 +#define DA7219_JACKDET_DEBOUNCE_MASK (0x7 << 1) +#define DA7219_JACK_DETECT_RATE_SHIFT 4 +#define DA7219_JACK_DETECT_RATE_MASK (0x3 << 4) +#define DA7219_JACKDET_REM_DEB_SHIFT 6 +#define DA7219_JACKDET_REM_DEB_MASK (0x3 << 6) + +/* DA7219_ACCDET_CONFIG_3 = 0xC8 */ +#define DA7219_A_D_BUTTON_THRESH_SHIFT 0 +#define DA7219_A_D_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_4 = 0xC9 */ +#define DA7219_D_B_BUTTON_THRESH_SHIFT 0 +#define DA7219_D_B_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_5 = 0xCA */ +#define DA7219_B_C_BUTTON_THRESH_SHIFT 0 +#define DA7219_B_C_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_6 = 0xCB */ +#define DA7219_C_MIC_BUTTON_THRESH_SHIFT 0 +#define DA7219_C_MIC_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_7 = 0xCC */ +#define DA7219_BUTTON_AVERAGE_SHIFT 0 +#define DA7219_BUTTON_AVERAGE_MASK (0x3 << 0) +#define DA7219_ADC_1_BIT_REPEAT_SHIFT 2 +#define DA7219_ADC_1_BIT_REPEAT_MASK (0x3 << 2) +#define DA7219_PIN_ORDER_FORCE_SHIFT 4 +#define DA7219_PIN_ORDER_FORCE_MASK (0x1 << 4) +#define DA7219_JACK_TYPE_FORCE_SHIFT 5 +#define DA7219_JACK_TYPE_FORCE_MASK (0x1 << 5) + +/* DA7219_ACCDET_CONFIG_8 = 0xCD */ +#define DA7219_HPTEST_EN_SHIFT 0 +#define DA7219_HPTEST_EN_MASK (0x1 << 0) +#define DA7219_HPTEST_RES_SEL_SHIFT 1 +#define DA7219_HPTEST_RES_SEL_MASK (0x3 << 1) +#define DA7219_HPTEST_RES_SEL_1KOHMS (0x0 << 1) +#define DA7219_HPTEST_COMP_SHIFT 4 +#define DA7219_HPTEST_COMP_MASK (0x1 << 4) + + +#define DA7219_AAD_MAX_BUTTONS 4 +#define DA7219_AAD_REPORT_ALL_MASK (SND_JACK_MECHANICAL | \ + SND_JACK_HEADSET | SND_JACK_LINEOUT | \ + SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3) + +#define DA7219_AAD_MICBIAS_CHK_DELAY 10 +#define DA7219_AAD_MICBIAS_CHK_RETRIES 5 + +#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28 +#define DA7219_AAD_HPTEST_PERIOD 65 + +enum da7219_aad_event_regs { + DA7219_AAD_IRQ_REG_A = 0, + DA7219_AAD_IRQ_REG_B, + DA7219_AAD_IRQ_REG_MAX, +}; + +/* Private data */ +struct da7219_aad_priv { + struct snd_soc_codec *codec; + int irq; + + u8 micbias_pulse_lvl; + u32 micbias_pulse_time; + + u8 btn_cfg; + + struct work_struct btn_det_work; + struct work_struct hptest_work; + + struct snd_soc_jack *jack; + bool jack_inserted; +}; + +/* AAD control */ +void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack); + +/* Init/Exit */ +int da7219_aad_init(struct snd_soc_codec *codec); +void da7219_aad_exit(struct snd_soc_codec *codec); + +#endif /* __DA7219_AAD_H */ diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c new file mode 100644 index 0000000..f238c1e --- /dev/null +++ b/sound/soc/codecs/da7219.c @@ -0,0 +1,1955 @@ +/* + * da7219.c - DA7219 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/clk.h> +#include <linux/i2c.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/pm.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <asm/div64.h> + +#include <sound/da7219.h> +#include "da7219.h" +#include "da7219-aad.h" + + +/* + * TLVs and Enums + */ + +/* Input TLVs */ +static const DECLARE_TLV_DB_SCALE(da7219_mic_gain_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7219_adc_dig_gain_tlv, -8325, 75, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_gain_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_ana_gain_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_sidetone_gain_tlv, -4200, 300, 0); +static const DECLARE_TLV_DB_SCALE(da7219_tonegen_gain_tlv, -4500, 300, 0); + +/* Output TLVs */ +static const DECLARE_TLV_DB_SCALE(da7219_dac_eq_band_tlv, -1050, 150, 0); + +static const DECLARE_TLV_DB_RANGE(da7219_dac_dig_gain_tlv, + 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* -77.25dB to 12dB */ + 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7725, 75, 0) +); + +static const DECLARE_TLV_DB_SCALE(da7219_dac_ng_threshold_tlv, -10200, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_hp_gain_tlv, -5700, 100, 0); + +/* Input Enums */ +static const char * const da7219_alc_attack_rate_txt[] = { + "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs", + "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs", + "30024/fs" +}; + +static const struct soc_enum da7219_alc_attack_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_ATTACK_SHIFT, + DA7219_ALC_ATTACK_MAX, da7219_alc_attack_rate_txt); + +static const char * const da7219_alc_release_rate_txt[] = { + "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs", + "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs" +}; + +static const struct soc_enum da7219_alc_release_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_RELEASE_SHIFT, + DA7219_ALC_RELEASE_MAX, da7219_alc_release_rate_txt); + +static const char * const da7219_alc_hold_time_txt[] = { + "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs", + "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs", + "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" +}; + +static const struct soc_enum da7219_alc_hold_time = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_HOLD_SHIFT, + DA7219_ALC_HOLD_MAX, da7219_alc_hold_time_txt); + +static const char * const da7219_alc_env_rate_txt[] = { + "1/4", "1/16", "1/256", "1/65536" +}; + +static const struct soc_enum da7219_alc_env_attack_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_ATTACK_SHIFT, + DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt); + +static const struct soc_enum da7219_alc_env_release_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_RELEASE_SHIFT, + DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt); + +static const char * const da7219_alc_anticlip_step_txt[] = { + "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs" +}; + +static const struct soc_enum da7219_alc_anticlip_step = + SOC_ENUM_SINGLE(DA7219_ALC_ANTICLIP_CTRL, + DA7219_ALC_ANTICLIP_STEP_SHIFT, + DA7219_ALC_ANTICLIP_STEP_MAX, + da7219_alc_anticlip_step_txt); + +/* Input/Output Enums */ +static const char * const da7219_gain_ramp_rate_txt[] = { + "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8", + "Nominal Rate / 16" +}; + +static const struct soc_enum da7219_gain_ramp_rate = + SOC_ENUM_SINGLE(DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_SHIFT, + DA7219_GAIN_RAMP_RATE_MAX, da7219_gain_ramp_rate_txt); + +static const char * const da7219_hpf_mode_txt[] = { + "Disabled", "Audio", "Voice" +}; + +static const unsigned int da7219_hpf_mode_val[] = { + DA7219_HPF_DISABLED, DA7219_HPF_AUDIO_EN, DA7219_HPF_VOICE_EN, +}; + +static const struct soc_enum da7219_adc_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_ADC_FILTERS1, DA7219_HPF_MODE_SHIFT, + DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX, + da7219_hpf_mode_txt, da7219_hpf_mode_val); + +static const struct soc_enum da7219_dac_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_DAC_FILTERS1, DA7219_HPF_MODE_SHIFT, + DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX, + da7219_hpf_mode_txt, da7219_hpf_mode_val); + +static const char * const da7219_audio_hpf_corner_txt[] = { + "2Hz", "4Hz", "8Hz", "16Hz" +}; + +static const struct soc_enum da7219_adc_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1, + DA7219_ADC_AUDIO_HPF_CORNER_SHIFT, + DA7219_AUDIO_HPF_CORNER_MAX, + da7219_audio_hpf_corner_txt); + +static const struct soc_enum da7219_dac_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1, + DA7219_DAC_AUDIO_HPF_CORNER_SHIFT, + DA7219_AUDIO_HPF_CORNER_MAX, + da7219_audio_hpf_corner_txt); + +static const char * const da7219_voice_hpf_corner_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static const struct soc_enum da7219_adc_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1, + DA7219_ADC_VOICE_HPF_CORNER_SHIFT, + DA7219_VOICE_HPF_CORNER_MAX, + da7219_voice_hpf_corner_txt); + +static const struct soc_enum da7219_dac_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1, + DA7219_DAC_VOICE_HPF_CORNER_SHIFT, + DA7219_VOICE_HPF_CORNER_MAX, + da7219_voice_hpf_corner_txt); + +static const char * const da7219_tonegen_dtmf_key_txt[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", + "*", "#" +}; + +static const struct soc_enum da7219_tonegen_dtmf_key = + SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG1, DA7219_DTMF_REG_SHIFT, + DA7219_DTMF_REG_MAX, da7219_tonegen_dtmf_key_txt); + +static const char * const da7219_tonegen_swg_sel_txt[] = { + "Sum", "SWG1", "SWG2", "SWG1_1-Cos" +}; + +static const struct soc_enum da7219_tonegen_swg_sel = + SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG2, DA7219_SWG_SEL_SHIFT, + DA7219_SWG_SEL_MAX, da7219_tonegen_swg_sel_txt); + +/* Output Enums */ +static const char * const da7219_dac_softmute_rate_txt[] = { + "1 Sample", "2 Samples", "4 Samples", "8 Samples", "16 Samples", + "32 Samples", "64 Samples" +}; + +static const struct soc_enum da7219_dac_softmute_rate = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS5, DA7219_DAC_SOFTMUTE_RATE_SHIFT, + DA7219_DAC_SOFTMUTE_RATE_MAX, + da7219_dac_softmute_rate_txt); + +static const char * const da7219_dac_ng_setup_time_txt[] = { + "256 Samples", "512 Samples", "1024 Samples", "2048 Samples" +}; + +static const struct soc_enum da7219_dac_ng_setup_time = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_SETUP_TIME_SHIFT, + DA7219_DAC_NG_SETUP_TIME_MAX, + da7219_dac_ng_setup_time_txt); + +static const char * const da7219_dac_ng_rampup_txt[] = { + "0.22ms/dB", "0.0138ms/dB" +}; + +static const struct soc_enum da7219_dac_ng_rampup_rate = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_RAMPUP_RATE_SHIFT, + DA7219_DAC_NG_RAMP_RATE_MAX, + da7219_dac_ng_rampup_txt); + +static const char * const da7219_dac_ng_rampdown_txt[] = { + "0.88ms/dB", "14.08ms/dB" +}; + +static const struct soc_enum da7219_dac_ng_rampdown_rate = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_RAMPDN_RATE_SHIFT, + DA7219_DAC_NG_RAMP_RATE_MAX, + da7219_dac_ng_rampdown_txt); + + +static const char * const da7219_cp_track_mode_txt[] = { + "Largest Volume", "DAC Volume", "Signal Magnitude" +}; + +static const unsigned int da7219_cp_track_mode_val[] = { + DA7219_CP_MCHANGE_LARGEST_VOL, DA7219_CP_MCHANGE_DAC_VOL, + DA7219_CP_MCHANGE_SIG_MAG +}; + +static const struct soc_enum da7219_cp_track_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_CP_CTRL, DA7219_CP_MCHANGE_SHIFT, + DA7219_CP_MCHANGE_REL_MASK, DA7219_CP_MCHANGE_MAX, + da7219_cp_track_mode_txt, + da7219_cp_track_mode_val); + + +/* + * Control Functions + */ + +/* Locked Kcontrol calls */ +static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_get_volsw(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_put_volsw(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_get_enum_double(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +/* ALC */ +static void da7219_alc_calib(struct snd_soc_codec *codec) +{ + u8 mic_ctrl, mixin_ctrl, adc_ctrl, calib_ctrl; + + /* Save current state of mic control register */ + mic_ctrl = snd_soc_read(codec, DA7219_MIC_1_CTRL); + + /* Save current state of input mixer control register */ + mixin_ctrl = snd_soc_read(codec, DA7219_MIXIN_L_CTRL); + + /* Save current state of input ADC control register */ + adc_ctrl = snd_soc_read(codec, DA7219_ADC_L_CTRL); + + /* Enable then Mute MIC PGAs */ + snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK, + DA7219_MIC_1_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_MUTE_EN_MASK, + DA7219_MIC_1_AMP_MUTE_EN_MASK); + + /* Enable input mixers unmuted */ + snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_EN_MASK | + DA7219_MIXIN_L_AMP_MUTE_EN_MASK, + DA7219_MIXIN_L_AMP_EN_MASK); + + /* Enable input filters unmuted */ + snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, + DA7219_ADC_L_MUTE_EN_MASK | DA7219_ADC_L_EN_MASK, + DA7219_ADC_L_EN_MASK); + + /* Perform auto calibration */ + snd_soc_update_bits(codec, DA7219_ALC_CTRL1, + DA7219_ALC_AUTO_CALIB_EN_MASK, + DA7219_ALC_AUTO_CALIB_EN_MASK); + do { + calib_ctrl = snd_soc_read(codec, DA7219_ALC_CTRL1); + } while (calib_ctrl & DA7219_ALC_AUTO_CALIB_EN_MASK); + + /* If auto calibration fails, disable DC offset, hybrid ALC */ + if (calib_ctrl & DA7219_ALC_CALIB_OVERFLOW_MASK) { + dev_warn(codec->dev, + "ALC auto calibration failed with overflow\n"); + snd_soc_update_bits(codec, DA7219_ALC_CTRL1, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK, 0); + } else { + /* Enable DC offset cancellation, hybrid mode */ + snd_soc_update_bits(codec, DA7219_ALC_CTRL1, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK); + } + + /* Restore input filter control register to original state */ + snd_soc_write(codec, DA7219_ADC_L_CTRL, adc_ctrl); + + /* Restore input mixer control registers to original state */ + snd_soc_write(codec, DA7219_MIXIN_L_CTRL, mixin_ctrl); + + /* Restore MIC control registers to original states */ + snd_soc_write(codec, DA7219_MIC_1_CTRL, mic_ctrl); +} + +static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + /* + * If ALC in operation and value of control has been updated, + * make sure calibrated offsets are updated. + */ + if ((ret == 1) && (da7219->alc_en)) + da7219_alc_calib(codec); + + return ret; +} + +static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + + /* Force ALC offset calibration if enabling ALC */ + if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) { + da7219_alc_calib(codec); + da7219->alc_en = true; + } else { + da7219->alc_en = false; + } + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +/* ToneGen */ +static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + int ret; + + mutex_lock(&da7219->lock); + ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); + mutex_unlock(&da7219->lock); + + if (ret) + return ret; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to host endianness here. + */ + ucontrol->value.integer.value[0] = le16_to_cpu(val); + + return 0; +} + +static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + int ret; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to little endian here to align with + * HW registers. + */ + val = cpu_to_le16(ucontrol->value.integer.value[0]); + + mutex_lock(&da7219->lock); + ret = regmap_raw_write(da7219->regmap, reg, &val, sizeof(val)); + mutex_unlock(&da7219->lock); + + return ret; +} + + +/* + * KControls + */ + +static const struct snd_kcontrol_new da7219_snd_controls[] = { + /* Mics */ + SOC_SINGLE_TLV("Mic Volume", DA7219_MIC_1_GAIN, + DA7219_MIC_1_AMP_GAIN_SHIFT, DA7219_MIC_1_AMP_GAIN_MAX, + DA7219_NO_INVERT, da7219_mic_gain_tlv), + SOC_SINGLE("Mic Switch", DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + + /* Mixer Input */ + SOC_SINGLE_EXT_TLV("Mixin Volume", DA7219_MIXIN_L_GAIN, + DA7219_MIXIN_L_AMP_GAIN_SHIFT, + DA7219_MIXIN_L_AMP_GAIN_MAX, DA7219_NO_INVERT, + snd_soc_get_volsw, da7219_mixin_gain_put, + da7219_mixin_gain_tlv), + SOC_SINGLE("Mixin Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + SOC_SINGLE("Mixin Gain Ramp Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + SOC_SINGLE("Mixin ZC Gain Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_ZC_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + + /* ADC */ + SOC_SINGLE_TLV("Capture Digital Volume", DA7219_ADC_L_GAIN, + DA7219_ADC_L_DIGITAL_GAIN_SHIFT, + DA7219_ADC_L_DIGITAL_GAIN_MAX, DA7219_NO_INVERT, + da7219_adc_dig_gain_tlv), + SOC_SINGLE("Capture Digital Switch", DA7219_ADC_L_CTRL, + DA7219_ADC_L_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + SOC_SINGLE("Capture Digital Gain Ramp Switch", DA7219_ADC_L_CTRL, + DA7219_ADC_L_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + + /* ALC */ + SOC_ENUM("ALC Attack Rate", da7219_alc_attack_rate), + SOC_ENUM("ALC Release Rate", da7219_alc_release_rate), + SOC_ENUM("ALC Hold Time", da7219_alc_hold_time), + SOC_ENUM("ALC Envelope Attack Rate", da7219_alc_env_attack_rate), + SOC_ENUM("ALC Envelope Release Rate", da7219_alc_env_release_rate), + SOC_SINGLE_TLV("ALC Noise Threshold", DA7219_ALC_NOISE, + DA7219_ALC_NOISE_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Min Threshold", DA7219_ALC_TARGET_MIN, + DA7219_ALC_THRESHOLD_MIN_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Threshold", DA7219_ALC_TARGET_MAX, + DA7219_ALC_THRESHOLD_MAX_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Attenuation", DA7219_ALC_GAIN_LIMITS, + DA7219_ALC_ATTEN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_gain_tlv), + SOC_SINGLE_TLV("ALC Max Volume", DA7219_ALC_GAIN_LIMITS, + DA7219_ALC_GAIN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Min Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS, + DA7219_ALC_ANA_GAIN_MIN_SHIFT, + DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_ana_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Max Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS, + DA7219_ALC_ANA_GAIN_MAX_SHIFT, + DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_ana_gain_tlv), + SOC_ENUM("ALC Anticlip Step", da7219_alc_anticlip_step), + SOC_SINGLE("ALC Anticlip Switch", DA7219_ALC_ANTICLIP_CTRL, + DA7219_ALC_ANTIPCLIP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + SOC_SINGLE_EXT("ALC Switch", DA7219_ALC_CTRL1, DA7219_ALC_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT, + snd_soc_get_volsw, da7219_alc_sw_put), + + /* Input High-Pass Filters */ + SOC_ENUM("ADC HPF Mode", da7219_adc_hpf_mode), + SOC_ENUM("ADC HPF Corner Audio", da7219_adc_audio_hpf_corner), + SOC_ENUM("ADC HPF Corner Voice", da7219_adc_voice_hpf_corner), + + /* Sidetone Filter */ + SOC_SINGLE_TLV("Sidetone Volume", DA7219_SIDETONE_GAIN, + DA7219_SIDETONE_GAIN_SHIFT, DA7219_SIDETONE_GAIN_MAX, + DA7219_NO_INVERT, da7219_sidetone_gain_tlv), + SOC_SINGLE("Sidetone Switch", DA7219_SIDETONE_CTRL, + DA7219_SIDETONE_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + + /* Tone Generator */ + SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7219_TONE_GEN_CFG2, + DA7219_TONE_GEN_GAIN_SHIFT, DA7219_TONE_GEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put, da7219_tonegen_gain_tlv), + SOC_ENUM_EXT("ToneGen DTMF Key", da7219_tonegen_dtmf_key, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7219_TONE_GEN_CFG1, + DA7219_DTMF_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7219_tonegen_swg_sel, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7219_TONE_GEN_FREQ1_L, + DA7219_FREQ1_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT, + da7219_tonegen_freq_get, da7219_tonegen_freq_put), + SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7219_TONE_GEN_FREQ2_L, + DA7219_FREQ2_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT, + da7219_tonegen_freq_get, da7219_tonegen_freq_put), + SOC_SINGLE_EXT("ToneGen On Time", DA7219_TONE_GEN_ON_PER, + DA7219_BEEP_ON_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_SINGLE("ToneGen Off Time", DA7219_TONE_GEN_OFF_PER, + DA7219_BEEP_OFF_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX, + DA7219_NO_INVERT), + + /* Gain ramping */ + SOC_ENUM("Gain Ramp Rate", da7219_gain_ramp_rate), + + /* DAC High-Pass Filter */ + SOC_ENUM_EXT("DAC HPF Mode", da7219_dac_hpf_mode, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_ENUM("DAC HPF Corner Audio", da7219_dac_audio_hpf_corner), + SOC_ENUM("DAC HPF Corner Voice", da7219_dac_voice_hpf_corner), + + /* DAC 5-Band Equaliser */ + SOC_SINGLE_TLV("DAC EQ Band1 Volume", DA7219_DAC_FILTERS2, + DA7219_DAC_EQ_BAND1_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band2 Volume", DA7219_DAC_FILTERS2, + DA7219_DAC_EQ_BAND2_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band3 Volume", DA7219_DAC_FILTERS3, + DA7219_DAC_EQ_BAND3_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band4 Volume", DA7219_DAC_FILTERS3, + DA7219_DAC_EQ_BAND4_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band5 Volume", DA7219_DAC_FILTERS4, + DA7219_DAC_EQ_BAND5_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_EXT("DAC EQ Switch", DA7219_DAC_FILTERS4, + DA7219_DAC_EQ_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + + /* DAC Softmute */ + SOC_ENUM("DAC Soft Mute Rate", da7219_dac_softmute_rate), + SOC_SINGLE_EXT("DAC Soft Mute Switch", DA7219_DAC_FILTERS5, + DA7219_DAC_SOFTMUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + + /* DAC Noise Gate */ + SOC_ENUM("DAC NG Setup Time", da7219_dac_ng_setup_time), + SOC_ENUM("DAC NG Rampup Rate", da7219_dac_ng_rampup_rate), + SOC_ENUM("DAC NG Rampdown Rate", da7219_dac_ng_rampdown_rate), + SOC_SINGLE_TLV("DAC NG Off Threshold", DA7219_DAC_NG_OFF_THRESH, + DA7219_DAC_NG_OFF_THRESHOLD_SHIFT, + DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT, + da7219_dac_ng_threshold_tlv), + SOC_SINGLE_TLV("DAC NG On Threshold", DA7219_DAC_NG_ON_THRESH, + DA7219_DAC_NG_ON_THRESHOLD_SHIFT, + DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT, + da7219_dac_ng_threshold_tlv), + SOC_SINGLE("DAC NG Switch", DA7219_DAC_NG_CTRL, DA7219_DAC_NG_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + + /* DACs */ + SOC_DOUBLE_R_EXT_TLV("Playback Digital Volume", DA7219_DAC_L_GAIN, + DA7219_DAC_R_GAIN, DA7219_DAC_L_DIGITAL_GAIN_SHIFT, + DA7219_DAC_DIGITAL_GAIN_MAX, DA7219_NO_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put, + da7219_dac_dig_gain_tlv), + SOC_DOUBLE_R_EXT("Playback Digital Switch", DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL, DA7219_DAC_L_MUTE_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put), + SOC_DOUBLE_R("Playback Digital Gain Ramp Switch", DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL, DA7219_DAC_L_RAMP_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + + /* CP */ + SOC_ENUM("Charge Pump Track Mode", da7219_cp_track_mode), + SOC_SINGLE("Charge Pump Threshold", DA7219_CP_VOL_THRESHOLD1, + DA7219_CP_THRESH_VDD2_SHIFT, DA7219_CP_THRESH_VDD2_MAX, + DA7219_NO_INVERT), + + /* Headphones */ + SOC_DOUBLE_R_EXT_TLV("Headphone Volume", DA7219_HP_L_GAIN, + DA7219_HP_R_GAIN, DA7219_HP_L_AMP_GAIN_SHIFT, + DA7219_HP_AMP_GAIN_MAX, DA7219_NO_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put, + da7219_hp_gain_tlv), + SOC_DOUBLE_R_EXT("Headphone Switch", DA7219_HP_L_CTRL, DA7219_HP_R_CTRL, + DA7219_HP_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL, DA7219_HP_L_AMP_RAMP_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL, DA7219_HP_L_AMP_ZC_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + + +/* + * DAPM Mux Controls + */ + +static const char * const da7219_out_sel_txt[] = { + "ADC", "Tone Generator", "DAIL", "DAIR" +}; + +static const struct soc_enum da7219_out_dail_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI, + DA7219_DAI_L_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dail_sel_mux = + SOC_DAPM_ENUM("Out DAIL Mux", da7219_out_dail_sel); + +static const struct soc_enum da7219_out_dair_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI, + DA7219_DAI_R_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dair_sel_mux = + SOC_DAPM_ENUM("Out DAIR Mux", da7219_out_dair_sel); + +static const struct soc_enum da7219_out_dacl_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC, + DA7219_DAC_L_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dacl_sel_mux = + SOC_DAPM_ENUM("Out DACL Mux", da7219_out_dacl_sel); + +static const struct soc_enum da7219_out_dacr_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC, + DA7219_DAC_R_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dacr_sel_mux = + SOC_DAPM_ENUM("Out DACR Mux", da7219_out_dacr_sel); + + +/* + * DAPM Mixer Controls + */ + +static const struct snd_kcontrol_new da7219_mixin_controls[] = { + SOC_DAPM_SINGLE("Mic Switch", DA7219_MIXIN_L_SELECT, + DA7219_MIXIN_L_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +static const struct snd_kcontrol_new da7219_mixout_l_controls[] = { + SOC_DAPM_SINGLE("DACL Switch", DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_L_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +static const struct snd_kcontrol_new da7219_mixout_r_controls[] = { + SOC_DAPM_SINGLE("DACR Switch", DA7219_MIXOUT_R_SELECT, + DA7219_MIXOUT_R_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +#define DA7219_DMIX_ST_CTRLS(reg) \ + SOC_DAPM_SINGLE("Out FilterL Switch", reg, \ + DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \ + SOC_DAPM_SINGLE("Out FilterR Switch", reg, \ + DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \ + SOC_DAPM_SINGLE("Sidetone Switch", reg, \ + DA7219_DMIX_ST_SRC_SIDETONE_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT) \ + +static const struct snd_kcontrol_new da7219_st_out_filtl_mix_controls[] = { + DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1L), +}; + +static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = { + DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1R), +}; + + +/* + * DAPM Events + */ + +static int da7219_dai_event(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 da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 pll_ctrl, pll_status; + int i = 0; + bool srm_lock = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (da7219->master) + /* Enable DAI clks for master mode */ + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_CLK_EN_MASK, + DA7219_DAI_CLK_EN_MASK); + + /* PC synchronised to DAI */ + snd_soc_update_bits(codec, DA7219_PC_COUNT, + DA7219_PC_FREERUN_MASK, 0); + + /* Slave mode, if SRM not enabled no need for status checks */ + pll_ctrl = snd_soc_read(codec, DA7219_PLL_CTRL); + if ((pll_ctrl & DA7219_PLL_MODE_MASK) != DA7219_PLL_MODE_SRM) + return 0; + + /* Check SRM has locked */ + do { + pll_status = snd_soc_read(codec, DA7219_PLL_SRM_STS); + if (pll_status & DA7219_PLL_SRM_STS_SRM_LOCK) { + srm_lock = true; + } else { + ++i; + msleep(50); + } + } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock)); + + if (!srm_lock) + dev_warn(codec->dev, "SRM failed to lock\n"); + + return 0; + case SND_SOC_DAPM_POST_PMD: + /* PC free-running */ + snd_soc_update_bits(codec, DA7219_PC_COUNT, + DA7219_PC_FREERUN_MASK, + DA7219_PC_FREERUN_MASK); + + /* Disable DAI clks if in master mode */ + if (da7219->master) + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_CLK_EN_MASK, 0); + return 0; + default: + return -EINVAL; + } +} + + +/* + * DAPM Widgets + */ + +static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = { + /* Input Supplies */ + SND_SOC_DAPM_SUPPLY("Mic Bias", DA7219_MICBIAS_CTRL, + DA7219_MICBIAS1_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("MIC"), + + /* Input PGAs */ + SND_SOC_DAPM_PGA("Mic PGA", DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixin PGA", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + + /* Input Filters */ + SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT, + DA7219_NO_INVERT), + + /* Tone Generator */ + SND_SOC_DAPM_SIGGEN("TONE"), + SND_SOC_DAPM_PGA("Tone Generator", DA7219_TONE_GEN_CFG1, + DA7219_START_STOPN_SHIFT, DA7219_NO_INVERT, NULL, 0), + + /* Sidetone Input */ + SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7219_SIDETONE_CTRL, + DA7219_SIDETONE_EN_SHIFT, DA7219_NO_INVERT), + + /* Input Mixer Supply */ + SND_SOC_DAPM_SUPPLY("Mixer In Supply", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_MIX_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + + /* Input Mixer */ + SND_SOC_DAPM_MIXER("Mixer In", SND_SOC_NOPM, 0, 0, + da7219_mixin_controls, + ARRAY_SIZE(da7219_mixin_controls)), + + /* Input Muxes */ + SND_SOC_DAPM_MUX("Out DAIL Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dail_sel_mux), + SND_SOC_DAPM_MUX("Out DAIR Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dair_sel_mux), + + /* DAI Supply */ + SND_SOC_DAPM_SUPPLY("DAI", DA7219_DAI_CTRL, DA7219_DAI_EN_SHIFT, + DA7219_NO_INVERT, da7219_dai_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* DAI */ + SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Muxes */ + SND_SOC_DAPM_MUX("Out DACL Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dacl_sel_mux), + SND_SOC_DAPM_MUX("Out DACR Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dacr_sel_mux), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7219_mixout_l_controls, + ARRAY_SIZE(da7219_mixout_l_controls)), + SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0, + da7219_mixout_r_controls, + ARRAY_SIZE(da7219_mixout_r_controls)), + + /* Sidetone Mixers */ + SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7219_st_out_filtl_mix_controls, + ARRAY_SIZE(da7219_st_out_filtl_mix_controls)), + SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0, + 0, da7219_st_out_filtr_mix_controls, + ARRAY_SIZE(da7219_st_out_filtr_mix_controls)), + + /* DACs */ + SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT, + DA7219_NO_INVERT), + SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT, + DA7219_NO_INVERT), + + /* Output PGAs */ + SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL, + DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0), + + /* Output Supplies */ + SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT, + DA7219_NO_INVERT, NULL, 0), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), +}; + + +/* + * DAPM Mux Routes + */ + +#define DA7219_OUT_DAI_MUX_ROUTES(name) \ + {name, "ADC", "Mixer In"}, \ + {name, "Tone Generator", "Tone Generator"}, \ + {name, "DAIL", "DAIOUT"}, \ + {name, "DAIR", "DAIOUT"} + +#define DA7219_OUT_DAC_MUX_ROUTES(name) \ + {name, "ADC", "Mixer In"}, \ + {name, "Tone Generator", "Tone Generator"}, \ + {name, "DAIL", "DAIIN"}, \ + {name, "DAIR", "DAIIN"} + +/* + * DAPM Mixer Routes + */ + +#define DA7219_DMIX_ST_ROUTES(name) \ + {name, "Out FilterL Switch", "Mixer Out FilterL"}, \ + {name, "Out FilterR Switch", "Mixer Out FilterR"}, \ + {name, "Sidetone Switch", "Sidetone Filter"} + + +/* + * DAPM audio route definition + */ + +static const struct snd_soc_dapm_route da7219_audio_map[] = { + /* Input paths */ + {"MIC", NULL, "Mic Bias"}, + {"Mic PGA", NULL, "MIC"}, + {"Mixin PGA", NULL, "Mic PGA"}, + {"ADC", NULL, "Mixin PGA"}, + + {"Sidetone Filter", NULL, "ADC"}, + {"Mixer In", NULL, "Mixer In Supply"}, + {"Mixer In", "Mic Switch", "ADC"}, + + {"Tone Generator", NULL, "TONE"}, + + DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"), + DA7219_OUT_DAI_MUX_ROUTES("Out DAIR Mux"), + + {"DAIOUT", NULL, "Out DAIL Mux"}, + {"DAIOUT", NULL, "Out DAIR Mux"}, + {"DAIOUT", NULL, "DAI"}, + + /* Output paths */ + {"DAIIN", NULL, "DAI"}, + + DA7219_OUT_DAC_MUX_ROUTES("Out DACL Mux"), + DA7219_OUT_DAC_MUX_ROUTES("Out DACR Mux"), + + {"Mixer Out FilterL", "DACL Switch", "Out DACL Mux"}, + {"Mixer Out FilterR", "DACR Switch", "Out DACR Mux"}, + + DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterL"), + DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterR"), + + {"DACL", NULL, "ST Mixer Out FilterL"}, + {"DACR", NULL, "ST Mixer Out FilterR"}, + + {"Mixout Left PGA", NULL, "DACL"}, + {"Mixout Right PGA", NULL, "DACR"}, + + {"Headphone Left PGA", NULL, "Mixout Left PGA"}, + {"Headphone Right PGA", NULL, "Mixout Right PGA"}, + + {"HPL", NULL, "Headphone Left PGA"}, + {"HPR", NULL, "Headphone Right PGA"}, + + {"HPL", NULL, "Charge Pump"}, + {"HPR", NULL, "Charge Pump"}, +}; + + +/* + * DAI operations + */ + +static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) + return 0; + + if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } + + switch (clk_id) { + case DA7219_CLKSRC_MCLK_SQR: + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_MCLK_SQR_EN_MASK, + DA7219_PLL_MCLK_SQR_EN_MASK); + break; + case DA7219_CLKSRC_MCLK: + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_MCLK_SQR_EN_MASK, 0); + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } + + da7219->clk_src = clk_id; + + if (da7219->mclk) { + freq = clk_round_rate(da7219->mclk, freq); + ret = clk_set_rate(da7219->mclk, freq); + if (ret) { + dev_err(codec_dai->dev, "Failed to set clock rate %d\n", + freq); + return ret; + } + } + + da7219->mclk_rate = freq; + + return 0; +} + +static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + u8 pll_ctrl, indiv_bits, indiv; + u8 pll_frac_top, pll_frac_bot, pll_integer; + u32 freq_ref; + u64 frac_div; + + /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */ + if (da7219->mclk_rate == 32768) { + indiv_bits = DA7219_PLL_INDIV_2_5_MHZ; + indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL; + } else if (da7219->mclk_rate < 2000000) { + dev_err(codec->dev, "PLL input clock %d below valid range\n", + da7219->mclk_rate); + return -EINVAL; + } else if (da7219->mclk_rate <= 5000000) { + indiv_bits = DA7219_PLL_INDIV_2_5_MHZ; + indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL; + } else if (da7219->mclk_rate <= 10000000) { + indiv_bits = DA7219_PLL_INDIV_5_10_MHZ; + indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL; + } else if (da7219->mclk_rate <= 20000000) { + indiv_bits = DA7219_PLL_INDIV_10_20_MHZ; + indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL; + } else if (da7219->mclk_rate <= 40000000) { + indiv_bits = DA7219_PLL_INDIV_20_40_MHZ; + indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL; + } else if (da7219->mclk_rate <= 54000000) { + indiv_bits = DA7219_PLL_INDIV_40_54_MHZ; + indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL; + } else { + dev_err(codec->dev, "PLL input clock %d above valid range\n", + da7219->mclk_rate); + return -EINVAL; + } + freq_ref = (da7219->mclk_rate / indiv); + pll_ctrl = indiv_bits; + + /* Configure PLL */ + switch (source) { + case DA7219_SYSCLK_MCLK: + pll_ctrl |= DA7219_PLL_MODE_BYPASS; + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_INDIV_MASK | + DA7219_PLL_MODE_MASK, pll_ctrl); + return 0; + case DA7219_SYSCLK_PLL: + pll_ctrl |= DA7219_PLL_MODE_NORMAL; + break; + case DA7219_SYSCLK_PLL_SRM: + pll_ctrl |= DA7219_PLL_MODE_SRM; + break; + case DA7219_SYSCLK_PLL_32KHZ: + pll_ctrl |= DA7219_PLL_MODE_32KHZ; + break; + default: + dev_err(codec->dev, "Invalid PLL config\n"); + return -EINVAL; + } + + /* Calculate dividers for PLL */ + pll_integer = fout / freq_ref; + frac_div = (u64)(fout % freq_ref) * 8192ULL; + do_div(frac_div, freq_ref); + pll_frac_top = (frac_div >> DA7219_BYTE_SHIFT) & DA7219_BYTE_MASK; + pll_frac_bot = (frac_div) & DA7219_BYTE_MASK; + + /* Write PLL config & dividers */ + snd_soc_write(codec, DA7219_PLL_FRAC_TOP, pll_frac_top); + snd_soc_write(codec, DA7219_PLL_FRAC_BOT, pll_frac_bot); + snd_soc_write(codec, DA7219_PLL_INTEGER, pll_integer); + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_INDIV_MASK | DA7219_PLL_MODE_MASK, + pll_ctrl); + + return 0; +} + +static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 dai_clk_mode = 0, dai_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + da7219->master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + da7219->master = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7219_DAI_WCLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + dai_clk_mode |= DA7219_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7219_DAI_WCLK_POL_INV | + DA7219_DAI_CLK_POL_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_ctrl |= DA7219_DAI_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_ctrl |= DA7219_DAI_FORMAT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dai_ctrl |= DA7219_DAI_FORMAT_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_B: + dai_ctrl |= DA7219_DAI_FORMAT_DSP; + break; + default: + return -EINVAL; + } + + /* By default 64 BCLKs per WCLK is supported */ + dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64; + + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK | + DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK, + dai_clk_mode); + snd_soc_update_bits(codec, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK, + dai_ctrl); + + return 0; +} + +static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 dai_bclks_per_wclk; + u16 offset; + u32 frame_size; + + /* No channels enabled so disable TDM, revert to 64-bit frames */ + if (!tx_mask) { + snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL, + DA7219_DAI_TDM_CH_EN_MASK | + DA7219_DAI_TDM_MODE_EN_MASK, 0); + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + DA7219_DAI_BCLKS_PER_WCLK_64); + return 0; + } + + /* Check we have valid slots */ + if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) { + dev_err(codec->dev, "Invalid number of slots, max = %d\n", + DA7219_DAI_TDM_MAX_SLOTS); + return -EINVAL; + } + + /* Check we have a valid offset given */ + if (rx_mask > DA7219_DAI_OFFSET_MAX) { + dev_err(codec->dev, "Invalid slot offset, max = %d\n", + DA7219_DAI_OFFSET_MAX); + return -EINVAL; + } + + /* Calculate & validate frame size based on slot info provided. */ + frame_size = slots * slot_width; + switch (frame_size) { + case 32: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; + break; + case 64: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; + break; + case 128: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128; + break; + case 256: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256; + break; + default: + dev_err(codec->dev, "Invalid frame size %d\n", frame_size); + return -EINVAL; + } + + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + dai_bclks_per_wclk); + + offset = cpu_to_le16(rx_mask); + regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER, + &offset, sizeof(offset)); + + snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL, + DA7219_DAI_TDM_CH_EN_MASK | + DA7219_DAI_TDM_MODE_EN_MASK, + (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) | + DA7219_DAI_TDM_MODE_EN_MASK); + + return 0; +} + +static int da7219_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + u8 dai_ctrl = 0, fs; + unsigned int channels; + + switch (params_width(params)) { + case 16: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE; + break; + case 20: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE; + break; + case 24: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE; + break; + case 32: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE; + break; + default: + return -EINVAL; + } + + channels = params_channels(params); + if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) { + dev_err(codec->dev, + "Invalid number of channels, only 1 to %d supported\n", + DA7219_DAI_CH_NUM_MAX); + return -EINVAL; + } + dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT; + + switch (params_rate(params)) { + case 8000: + fs = DA7219_SR_8000; + break; + case 11025: + fs = DA7219_SR_11025; + break; + case 12000: + fs = DA7219_SR_12000; + break; + case 16000: + fs = DA7219_SR_16000; + break; + case 22050: + fs = DA7219_SR_22050; + break; + case 24000: + fs = DA7219_SR_24000; + break; + case 32000: + fs = DA7219_SR_32000; + break; + case 44100: + fs = DA7219_SR_44100; + break; + case 48000: + fs = DA7219_SR_48000; + break; + case 88200: + fs = DA7219_SR_88200; + break; + case 96000: + fs = DA7219_SR_96000; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, DA7219_DAI_CTRL, + DA7219_DAI_WORD_LENGTH_MASK | + DA7219_DAI_CH_NUM_MASK, + dai_ctrl); + snd_soc_write(codec, DA7219_SR, fs); + + return 0; +} + +static const struct snd_soc_dai_ops da7219_dai_ops = { + .hw_params = da7219_hw_params, + .set_sysclk = da7219_set_dai_sysclk, + .set_pll = da7219_set_dai_pll, + .set_fmt = da7219_set_dai_fmt, + .set_tdm_slot = da7219_set_dai_tdm_slot, +}; + +#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver da7219_dai = { + .name = "da7219-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = DA7219_DAI_CH_NUM_MAX, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7219_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = DA7219_DAI_CH_NUM_MAX, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7219_FORMATS, + }, + .ops = &da7219_dai_ops, + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, +}; + + +/* + * DT + */ + +static const struct of_device_id da7219_of_match[] = { + { .compatible = "dlg,da7219", }, + { } +}; +MODULE_DEVICE_TABLE(of, da7219_of_match); + +static enum da7219_ldo_lvl_sel da7219_of_ldo_lvl(struct snd_soc_codec *codec, + u32 val) +{ + switch (val) { + case 1050: + return DA7219_LDO_LVL_SEL_1_05V; + case 1100: + return DA7219_LDO_LVL_SEL_1_10V; + case 1200: + return DA7219_LDO_LVL_SEL_1_20V; + case 1400: + return DA7219_LDO_LVL_SEL_1_40V; + default: + dev_warn(codec->dev, "Invalid LDO level"); + return DA7219_LDO_LVL_SEL_1_05V; + } +} + +static enum da7219_micbias_voltage + da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1800: + return DA7219_MICBIAS_1_8V; + case 2000: + return DA7219_MICBIAS_2_0V; + case 2200: + return DA7219_MICBIAS_2_2V; + case 2400: + return DA7219_MICBIAS_2_4V; + case 2600: + return DA7219_MICBIAS_2_6V; + default: + dev_warn(codec->dev, "Invalid micbias level"); + return DA7219_MICBIAS_2_2V; + } +} + +static enum da7219_mic_amp_in_sel + da7219_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "diff")) { + return DA7219_MIC_AMP_IN_SEL_DIFF; + } else if (!strcmp(str, "se_p")) { + return DA7219_MIC_AMP_IN_SEL_SE_P; + } else if (!strcmp(str, "se_n")) { + return DA7219_MIC_AMP_IN_SEL_SE_N; + } else { + dev_warn(codec->dev, "Invalid mic input type selection"); + return DA7219_MIC_AMP_IN_SEL_DIFF; + } +} + +static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec) +{ + struct device_node *np = codec->dev->of_node; + struct da7219_pdata *pdata; + const char *of_str; + u32 of_val32; + + pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_u32(np, "dlg,ldo-lvl", &of_val32) >= 0) + pdata->ldo_lvl_sel = da7219_of_ldo_lvl(codec, of_val32); + + if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0) + pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32); + else + pdata->micbias_lvl = DA7219_MICBIAS_2_2V; + + if (!of_property_read_string(np, "dlg,mic-amp-in-sel", &of_str)) + pdata->mic_amp_in_sel = da7219_of_mic_amp_in_sel(codec, of_str); + else + pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF; + + return pdata; +} + + +/* + * Codec driver functions + */ + +static int da7219_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + /* MCLK */ + if (da7219->mclk) { + ret = clk_prepare_enable(da7219->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable mclk\n"); + return ret; + } + } + + /* Master bias */ + snd_soc_update_bits(codec, DA7219_REFERENCES, + DA7219_BIAS_EN_MASK, + DA7219_BIAS_EN_MASK); + + /* Enable Internal Digital LDO */ + snd_soc_update_bits(codec, DA7219_LDO_CTRL, + DA7219_LDO_EN_MASK, + DA7219_LDO_EN_MASK); + } + break; + case SND_SOC_BIAS_OFF: + /* Only disable if jack detection not active */ + if (!da7219->aad->jack) { + /* Bypass Internal Digital LDO */ + snd_soc_update_bits(codec, DA7219_LDO_CTRL, + DA7219_LDO_EN_MASK, 0); + + /* Master bias */ + snd_soc_update_bits(codec, DA7219_REFERENCES, + DA7219_BIAS_EN_MASK, 0); + } + + /* MCLK */ + if (da7219->mclk) + clk_disable_unprepare(da7219->mclk); + break; + } + + return 0; +} + +static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = { + [DA7219_SUPPLY_VDD] = "VDD", + [DA7219_SUPPLY_VDDMIC] = "VDDMIC", + [DA7219_SUPPLY_VDDIO] = "VDDIO", +}; + +static int da7219_handle_supplies(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct regulator *vddio; + u8 io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V; + int i, ret; + + /* Get required supplies */ + for (i = 0; i < DA7219_NUM_SUPPLIES; ++i) + da7219->supplies[i].supply = da7219_supply_names[i]; + + ret = devm_regulator_bulk_get(codec->dev, DA7219_NUM_SUPPLIES, + da7219->supplies); + if (ret) { + dev_err(codec->dev, "Failed to get supplies"); + return ret; + } + + /* Determine VDDIO voltage provided */ + vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer; + ret = regulator_get_voltage(vddio); + if (ret < 1200000) + dev_warn(codec->dev, "Invalid VDDIO voltage\n"); + else if (ret < 2800000) + io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V; + + /* Enable main supplies */ + ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies); + if (ret) { + dev_err(codec->dev, "Failed to enable supplies"); + return ret; + } + + /* Ensure device in active mode */ + snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK); + + /* Update IO voltage level range */ + snd_soc_write(codec, DA7219_IO_CTRL, io_voltage_lvl); + + return 0; +} + +static void da7219_handle_pdata(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_pdata *pdata = da7219->pdata; + + if (pdata) { + u8 micbias_lvl = 0; + + /* Internal LDO */ + switch (pdata->ldo_lvl_sel) { + case DA7219_LDO_LVL_SEL_1_05V: + case DA7219_LDO_LVL_SEL_1_10V: + case DA7219_LDO_LVL_SEL_1_20V: + case DA7219_LDO_LVL_SEL_1_40V: + snd_soc_update_bits(codec, DA7219_LDO_CTRL, + DA7219_LDO_LEVEL_SELECT_MASK, + (pdata->ldo_lvl_sel << + DA7219_LDO_LEVEL_SELECT_SHIFT)); + break; + } + + /* Mic Bias voltages */ + switch (pdata->micbias_lvl) { + case DA7219_MICBIAS_1_8V: + case DA7219_MICBIAS_2_0V: + case DA7219_MICBIAS_2_2V: + case DA7219_MICBIAS_2_4V: + case DA7219_MICBIAS_2_6V: + micbias_lvl |= (pdata->micbias_lvl << + DA7219_MICBIAS1_LEVEL_SHIFT); + break; + } + + snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_lvl); + + /* Mic */ + switch (pdata->mic_amp_in_sel) { + case DA7219_MIC_AMP_IN_SEL_DIFF: + case DA7219_MIC_AMP_IN_SEL_SE_P: + case DA7219_MIC_AMP_IN_SEL_SE_N: + snd_soc_write(codec, DA7219_MIC_1_SELECT, + pdata->mic_amp_in_sel); + break; + } + } +} + +static int da7219_probe(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_init(&da7219->lock); + + /* Regulator configuration */ + ret = da7219_handle_supplies(codec); + if (ret) + return ret; + + /* Handle DT/Platform data */ + if (codec->dev->of_node) + da7219->pdata = da7219_of_to_pdata(codec); + else + da7219->pdata = dev_get_platdata(codec->dev); + + da7219_handle_pdata(codec); + + /* Check if MCLK provided */ + da7219->mclk = devm_clk_get(codec->dev, "mclk"); + if (IS_ERR(da7219->mclk)) { + if (PTR_ERR(da7219->mclk) != -ENOENT) + return PTR_ERR(da7219->mclk); + else + da7219->mclk = NULL; + } + + /* Default PC counter to free-running */ + snd_soc_update_bits(codec, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK, + DA7219_PC_FREERUN_MASK); + + /* Default gain ramping */ + snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_RAMP_EN_MASK, + DA7219_MIXIN_L_AMP_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK, + DA7219_ADC_L_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK, + DA7219_DAC_L_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK, + DA7219_DAC_R_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_RAMP_EN_MASK, + DA7219_HP_L_AMP_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_RAMP_EN_MASK, + DA7219_HP_R_AMP_RAMP_EN_MASK); + + /* Default infinite tone gen, start/stop by Kcontrol */ + snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK); + + /* Initialise AAD block */ + return da7219_aad_init(codec); +} + +static int da7219_remove(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + da7219_aad_exit(codec); + + /* Supplies */ + return regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies); +} + +#ifdef CONFIG_PM +static int da7219_suspend(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); + + /* Put device into standby mode if jack detection disabled */ + if (!da7219->aad->jack) + snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, 0); + + return 0; +} + +static int da7219_resume(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + /* Put device into active mode if previously pushed to standby */ + if (!da7219->aad->jack) + snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, + DA7219_SYSTEM_ACTIVE_MASK); + + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define da7219_suspend NULL +#define da7219_resume NULL +#endif + +static struct snd_soc_codec_driver soc_codec_dev_da7219 = { + .probe = da7219_probe, + .remove = da7219_remove, + .suspend = da7219_suspend, + .resume = da7219_resume, + .set_bias_level = da7219_set_bias_level, + + .controls = da7219_snd_controls, + .num_controls = ARRAY_SIZE(da7219_snd_controls), + + .dapm_widgets = da7219_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets), + .dapm_routes = da7219_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7219_audio_map), +}; + + +/* + * Regmap configs + */ + +static struct reg_default da7219_reg_defaults[] = { + { DA7219_MIC_1_SELECT, 0x00 }, + { DA7219_CIF_TIMEOUT_CTRL, 0x01 }, + { DA7219_SR_24_48, 0x00 }, + { DA7219_SR, 0x0A }, + { DA7219_CIF_I2C_ADDR_CFG, 0x02 }, + { DA7219_PLL_CTRL, 0x10 }, + { DA7219_PLL_FRAC_TOP, 0x00 }, + { DA7219_PLL_FRAC_BOT, 0x00 }, + { DA7219_PLL_INTEGER, 0x20 }, + { DA7219_DIG_ROUTING_DAI, 0x10 }, + { DA7219_DAI_CLK_MODE, 0x01 }, + { DA7219_DAI_CTRL, 0x28 }, + { DA7219_DAI_TDM_CTRL, 0x40 }, + { DA7219_DIG_ROUTING_DAC, 0x32 }, + { DA7219_DAI_OFFSET_LOWER, 0x00 }, + { DA7219_DAI_OFFSET_UPPER, 0x00 }, + { DA7219_REFERENCES, 0x00 }, + { DA7219_MIXIN_L_SELECT, 0x00 }, + { DA7219_MIXIN_L_GAIN, 0x03 }, + { DA7219_ADC_L_GAIN, 0x6F }, + { DA7219_ADC_FILTERS1, 0x80 }, + { DA7219_MIC_1_GAIN, 0x01 }, + { DA7219_SIDETONE_CTRL, 0x40 }, + { DA7219_SIDETONE_GAIN, 0x0E }, + { DA7219_DROUTING_ST_OUTFILT_1L, 0x01 }, + { DA7219_DROUTING_ST_OUTFILT_1R, 0x02 }, + { DA7219_DAC_FILTERS5, 0x00 }, + { DA7219_DAC_FILTERS2, 0x88 }, + { DA7219_DAC_FILTERS3, 0x88 }, + { DA7219_DAC_FILTERS4, 0x08 }, + { DA7219_DAC_FILTERS1, 0x80 }, + { DA7219_DAC_L_GAIN, 0x6F }, + { DA7219_DAC_R_GAIN, 0x6F }, + { DA7219_CP_CTRL, 0x20 }, + { DA7219_HP_L_GAIN, 0x39 }, + { DA7219_HP_R_GAIN, 0x39 }, + { DA7219_MIXOUT_L_SELECT, 0x00 }, + { DA7219_MIXOUT_R_SELECT, 0x00 }, + { DA7219_MICBIAS_CTRL, 0x03 }, + { DA7219_MIC_1_CTRL, 0x40 }, + { DA7219_MIXIN_L_CTRL, 0x40 }, + { DA7219_ADC_L_CTRL, 0x40 }, + { DA7219_DAC_L_CTRL, 0x40 }, + { DA7219_DAC_R_CTRL, 0x40 }, + { DA7219_HP_L_CTRL, 0x40 }, + { DA7219_HP_R_CTRL, 0x40 }, + { DA7219_MIXOUT_L_CTRL, 0x10 }, + { DA7219_MIXOUT_R_CTRL, 0x10 }, + { DA7219_CHIP_ID1, 0x23 }, + { DA7219_CHIP_ID2, 0x93 }, + { DA7219_CHIP_REVISION, 0x00 }, + { DA7219_LDO_CTRL, 0x00 }, + { DA7219_IO_CTRL, 0x00 }, + { DA7219_GAIN_RAMP_CTRL, 0x00 }, + { DA7219_PC_COUNT, 0x02 }, + { DA7219_CP_VOL_THRESHOLD1, 0x0E }, + { DA7219_DIG_CTRL, 0x00 }, + { DA7219_ALC_CTRL2, 0x00 }, + { DA7219_ALC_CTRL3, 0x00 }, + { DA7219_ALC_NOISE, 0x3F }, + { DA7219_ALC_TARGET_MIN, 0x3F }, + { DA7219_ALC_TARGET_MAX, 0x00 }, + { DA7219_ALC_GAIN_LIMITS, 0xFF }, + { DA7219_ALC_ANA_GAIN_LIMITS, 0x71 }, + { DA7219_ALC_ANTICLIP_CTRL, 0x00 }, + { DA7219_ALC_ANTICLIP_LEVEL, 0x00 }, + { DA7219_DAC_NG_SETUP_TIME, 0x00 }, + { DA7219_DAC_NG_OFF_THRESH, 0x00 }, + { DA7219_DAC_NG_ON_THRESH, 0x00 }, + { DA7219_DAC_NG_CTRL, 0x00 }, + { DA7219_TONE_GEN_CFG1, 0x00 }, + { DA7219_TONE_GEN_CFG2, 0x00 }, + { DA7219_TONE_GEN_CYCLES, 0x00 }, + { DA7219_TONE_GEN_FREQ1_L, 0x55 }, + { DA7219_TONE_GEN_FREQ1_U, 0x15 }, + { DA7219_TONE_GEN_FREQ2_L, 0x00 }, + { DA7219_TONE_GEN_FREQ2_U, 0x40 }, + { DA7219_TONE_GEN_ON_PER, 0x02 }, + { DA7219_TONE_GEN_OFF_PER, 0x01 }, + { DA7219_ACCDET_IRQ_MASK_A, 0x00 }, + { DA7219_ACCDET_IRQ_MASK_B, 0x00 }, + { DA7219_ACCDET_CONFIG_1, 0xD6 }, + { DA7219_ACCDET_CONFIG_2, 0x34 }, + { DA7219_ACCDET_CONFIG_3, 0x0A }, + { DA7219_ACCDET_CONFIG_4, 0x16 }, + { DA7219_ACCDET_CONFIG_5, 0x21 }, + { DA7219_ACCDET_CONFIG_6, 0x3E }, + { DA7219_ACCDET_CONFIG_7, 0x01 }, + { DA7219_SYSTEM_ACTIVE, 0x00 }, +}; + +static bool da7219_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA7219_MIC_1_GAIN_STATUS: + case DA7219_MIXIN_L_GAIN_STATUS: + case DA7219_ADC_L_GAIN_STATUS: + case DA7219_DAC_L_GAIN_STATUS: + case DA7219_DAC_R_GAIN_STATUS: + case DA7219_HP_L_GAIN_STATUS: + case DA7219_HP_R_GAIN_STATUS: + case DA7219_CIF_CTRL: + case DA7219_PLL_SRM_STS: + case DA7219_ALC_CTRL1: + case DA7219_SYSTEM_MODES_INPUT: + case DA7219_SYSTEM_MODES_OUTPUT: + case DA7219_ALC_OFFSET_AUTO_M_L: + case DA7219_ALC_OFFSET_AUTO_U_L: + case DA7219_TONE_GEN_CFG1: + case DA7219_ACCDET_STATUS_A: + case DA7219_ACCDET_STATUS_B: + case DA7219_ACCDET_IRQ_EVENT_A: + case DA7219_ACCDET_IRQ_EVENT_B: + case DA7219_ACCDET_CONFIG_8: + case DA7219_SYSTEM_STATUS: + return 1; + default: + return 0; + } +} + +static const struct regmap_config da7219_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DA7219_SYSTEM_ACTIVE, + .reg_defaults = da7219_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7219_reg_defaults), + .volatile_reg = da7219_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + + +/* + * I2C layer + */ + +static int da7219_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da7219_priv *da7219; + int ret; + + da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv), + GFP_KERNEL); + if (!da7219) + return -ENOMEM; + + i2c_set_clientdata(i2c, da7219); + + da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config); + if (IS_ERR(da7219->regmap)) { + ret = PTR_ERR(da7219->regmap); + dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7219, + &da7219_dai, 1); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register da7219 codec: %d\n", + ret); + } + return ret; +} + +static int da7219_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id da7219_i2c_id[] = { + { "da7219", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, da7219_i2c_id); + +static struct i2c_driver da7219_i2c_driver = { + .driver = { + .name = "da7219", + .of_match_table = of_match_ptr(da7219_of_match), + }, + .probe = da7219_i2c_probe, + .remove = da7219_i2c_remove, + .id_table = da7219_i2c_id, +}; + +module_i2c_driver(da7219_i2c_driver); + +MODULE_DESCRIPTION("ASoC DA7219 Codec Driver"); +MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h new file mode 100644 index 0000000..b514268 --- /dev/null +++ b/sound/soc/codecs/da7219.h @@ -0,0 +1,820 @@ +/* + * da7219.h - DA7219 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __DA7219_H +#define __DA7219_H + +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <sound/da7219.h> + +/* + * Registers + */ + +#define DA7219_MIC_1_GAIN_STATUS 0x6 +#define DA7219_MIXIN_L_GAIN_STATUS 0x8 +#define DA7219_ADC_L_GAIN_STATUS 0xA +#define DA7219_DAC_L_GAIN_STATUS 0xC +#define DA7219_DAC_R_GAIN_STATUS 0xD +#define DA7219_HP_L_GAIN_STATUS 0xE +#define DA7219_HP_R_GAIN_STATUS 0xF +#define DA7219_MIC_1_SELECT 0x10 +#define DA7219_CIF_TIMEOUT_CTRL 0x12 +#define DA7219_CIF_CTRL 0x13 +#define DA7219_SR_24_48 0x16 +#define DA7219_SR 0x17 +#define DA7219_CIF_I2C_ADDR_CFG 0x1B +#define DA7219_PLL_CTRL 0x20 +#define DA7219_PLL_FRAC_TOP 0x22 +#define DA7219_PLL_FRAC_BOT 0x23 +#define DA7219_PLL_INTEGER 0x24 +#define DA7219_PLL_SRM_STS 0x25 +#define DA7219_DIG_ROUTING_DAI 0x2A +#define DA7219_DAI_CLK_MODE 0x2B +#define DA7219_DAI_CTRL 0x2C +#define DA7219_DAI_TDM_CTRL 0x2D +#define DA7219_DIG_ROUTING_DAC 0x2E +#define DA7219_ALC_CTRL1 0x2F +#define DA7219_DAI_OFFSET_LOWER 0x30 +#define DA7219_DAI_OFFSET_UPPER 0x31 +#define DA7219_REFERENCES 0x32 +#define DA7219_MIXIN_L_SELECT 0x33 +#define DA7219_MIXIN_L_GAIN 0x34 +#define DA7219_ADC_L_GAIN 0x36 +#define DA7219_ADC_FILTERS1 0x38 +#define DA7219_MIC_1_GAIN 0x39 +#define DA7219_SIDETONE_CTRL 0x3A +#define DA7219_SIDETONE_GAIN 0x3B +#define DA7219_DROUTING_ST_OUTFILT_1L 0x3C +#define DA7219_DROUTING_ST_OUTFILT_1R 0x3D +#define DA7219_DAC_FILTERS5 0x40 +#define DA7219_DAC_FILTERS2 0x41 +#define DA7219_DAC_FILTERS3 0x42 +#define DA7219_DAC_FILTERS4 0x43 +#define DA7219_DAC_FILTERS1 0x44 +#define DA7219_DAC_L_GAIN 0x45 +#define DA7219_DAC_R_GAIN 0x46 +#define DA7219_CP_CTRL 0x47 +#define DA7219_HP_L_GAIN 0x48 +#define DA7219_HP_R_GAIN 0x49 +#define DA7219_MIXOUT_L_SELECT 0x4B +#define DA7219_MIXOUT_R_SELECT 0x4C +#define DA7219_SYSTEM_MODES_INPUT 0x50 +#define DA7219_SYSTEM_MODES_OUTPUT 0x51 +#define DA7219_MICBIAS_CTRL 0x62 +#define DA7219_MIC_1_CTRL 0x63 +#define DA7219_MIXIN_L_CTRL 0x65 +#define DA7219_ADC_L_CTRL 0x67 +#define DA7219_DAC_L_CTRL 0x69 +#define DA7219_DAC_R_CTRL 0x6A +#define DA7219_HP_L_CTRL 0x6B +#define DA7219_HP_R_CTRL 0x6C +#define DA7219_MIXOUT_L_CTRL 0x6E +#define DA7219_MIXOUT_R_CTRL 0x6F +#define DA7219_CHIP_ID1 0x81 +#define DA7219_CHIP_ID2 0x82 +#define DA7219_CHIP_REVISION 0x83 +#define DA7219_LDO_CTRL 0x90 +#define DA7219_IO_CTRL 0x91 +#define DA7219_GAIN_RAMP_CTRL 0x92 +#define DA7219_PC_COUNT 0x94 +#define DA7219_CP_VOL_THRESHOLD1 0x95 +#define DA7219_CP_DELAY 0x96 +#define DA7219_DIG_CTRL 0x99 +#define DA7219_ALC_CTRL2 0x9A +#define DA7219_ALC_CTRL3 0x9B +#define DA7219_ALC_NOISE 0x9C +#define DA7219_ALC_TARGET_MIN 0x9D +#define DA7219_ALC_TARGET_MAX 0x9E +#define DA7219_ALC_GAIN_LIMITS 0x9F +#define DA7219_ALC_ANA_GAIN_LIMITS 0xA0 +#define DA7219_ALC_ANTICLIP_CTRL 0xA1 +#define DA7219_ALC_ANTICLIP_LEVEL 0xA2 +#define DA7219_ALC_OFFSET_AUTO_M_L 0xA3 +#define DA7219_ALC_OFFSET_AUTO_U_L 0xA4 +#define DA7219_DAC_NG_SETUP_TIME 0xAF +#define DA7219_DAC_NG_OFF_THRESH 0xB0 +#define DA7219_DAC_NG_ON_THRESH 0xB1 +#define DA7219_DAC_NG_CTRL 0xB2 +#define DA7219_TONE_GEN_CFG1 0xB4 +#define DA7219_TONE_GEN_CFG2 0xB5 +#define DA7219_TONE_GEN_CYCLES 0xB6 +#define DA7219_TONE_GEN_FREQ1_L 0xB7 +#define DA7219_TONE_GEN_FREQ1_U 0xB8 +#define DA7219_TONE_GEN_FREQ2_L 0xB9 +#define DA7219_TONE_GEN_FREQ2_U 0xBA +#define DA7219_TONE_GEN_ON_PER 0xBB +#define DA7219_TONE_GEN_OFF_PER 0xBC +#define DA7219_SYSTEM_STATUS 0xE0 +#define DA7219_SYSTEM_ACTIVE 0xFD + + +/* + * Bit Fields + */ + +#define DA7219_SWITCH_EN_MAX 0x1 + +/* DA7219_MIC_1_GAIN_STATUS = 0x6 */ +#define DA7219_MIC_1_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_MIC_1_AMP_GAIN_STATUS_MASK (0x7 << 0) +#define DA7219_MIC_1_AMP_GAIN_MAX 0x7 + +/* DA7219_MIXIN_L_GAIN_STATUS = 0x8 */ +#define DA7219_MIXIN_L_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_MIXIN_L_AMP_GAIN_STATUS_MASK (0xF << 0) + +/* DA7219_ADC_L_GAIN_STATUS = 0xA */ +#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_DAC_L_GAIN_STATUS = 0xC */ +#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_DAC_R_GAIN_STATUS = 0xD */ +#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_HP_L_GAIN_STATUS = 0xE */ +#define DA7219_HP_L_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_HP_L_AMP_GAIN_STATUS_MASK (0x3F << 0) + +/* DA7219_HP_R_GAIN_STATUS = 0xF */ +#define DA7219_HP_R_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_HP_R_AMP_GAIN_STATUS_MASK (0x3F << 0) + +/* DA7219_MIC_1_SELECT = 0x10 */ +#define DA7219_MIC_1_AMP_IN_SEL_SHIFT 0 +#define DA7219_MIC_1_AMP_IN_SEL_MASK (0x3 << 0) + +/* DA7219_CIF_TIMEOUT_CTRL = 0x12 */ +#define DA7219_I2C_TIMEOUT_EN_SHIFT 0 +#define DA7219_I2C_TIMEOUT_EN_MASK (0x1 << 0) + +/* DA7219_CIF_CTRL = 0x13 */ +#define DA7219_CIF_I2C_WRITE_MODE_SHIFT 0 +#define DA7219_CIF_I2C_WRITE_MODE_MASK (0x1 << 0) +#define DA7219_CIF_REG_SOFT_RESET_SHIFT 7 +#define DA7219_CIF_REG_SOFT_RESET_MASK (0x1 << 7) + +/* DA7219_SR_24_48 = 0x16 */ +#define DA7219_SR_24_48_SHIFT 0 +#define DA7219_SR_24_48_MASK (0x1 << 0) + +/* DA7219_SR = 0x17 */ +#define DA7219_SR_SHIFT 0 +#define DA7219_SR_MASK (0xF << 0) +#define DA7219_SR_8000 (0x01 << 0) +#define DA7219_SR_11025 (0x02 << 0) +#define DA7219_SR_12000 (0x03 << 0) +#define DA7219_SR_16000 (0x05 << 0) +#define DA7219_SR_22050 (0x06 << 0) +#define DA7219_SR_24000 (0x07 << 0) +#define DA7219_SR_32000 (0x09 << 0) +#define DA7219_SR_44100 (0x0A << 0) +#define DA7219_SR_48000 (0x0B << 0) +#define DA7219_SR_88200 (0x0E << 0) +#define DA7219_SR_96000 (0x0F << 0) + +/* DA7219_CIF_I2C_ADDR_CFG = 0x1B */ +#define DA7219_CIF_I2C_ADDR_CFG_SHIFT 0 +#define DA7219_CIF_I2C_ADDR_CFG_MASK (0x3 << 0) + +/* DA7219_PLL_CTRL = 0x20 */ +#define DA7219_PLL_INDIV_SHIFT 2 +#define DA7219_PLL_INDIV_MASK (0x7 << 2) +#define DA7219_PLL_INDIV_2_5_MHZ (0x0 << 2) +#define DA7219_PLL_INDIV_5_10_MHZ (0x1 << 2) +#define DA7219_PLL_INDIV_10_20_MHZ (0x2 << 2) +#define DA7219_PLL_INDIV_20_40_MHZ (0x3 << 2) +#define DA7219_PLL_INDIV_40_54_MHZ (0x4 << 2) +#define DA7219_PLL_MCLK_SQR_EN_SHIFT 5 +#define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5) +#define DA7219_PLL_MODE_SHIFT 6 +#define DA7219_PLL_MODE_MASK (0x3 << 6) +#define DA7219_PLL_MODE_BYPASS (0x0 << 6) +#define DA7219_PLL_MODE_NORMAL (0x1 << 6) +#define DA7219_PLL_MODE_SRM (0x2 << 6) +#define DA7219_PLL_MODE_32KHZ (0x3 << 6) + +/* DA7219_PLL_FRAC_TOP = 0x22 */ +#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0 +#define DA7219_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0) + +/* DA7219_PLL_FRAC_BOT = 0x23 */ +#define DA7219_PLL_FBDIV_FRAC_BOT_SHIFT 0 +#define DA7219_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0) + +/* DA7219_PLL_INTEGER = 0x24 */ +#define DA7219_PLL_FBDIV_INTEGER_SHIFT 0 +#define DA7219_PLL_FBDIV_INTEGER_MASK (0x7F << 0) + +/* DA7219_PLL_SRM_STS = 0x25 */ +#define DA7219_PLL_SRM_STATE_SHIFT 0 +#define DA7219_PLL_SRM_STATE_MASK (0xF << 0) +#define DA7219_PLL_SRM_STATUS_SHIFT 4 +#define DA7219_PLL_SRM_STATUS_MASK (0xF << 4) +#define DA7219_PLL_SRM_STS_SRM_LOCK (0x1 << 7) + +/* DA7219_DIG_ROUTING_DAI = 0x2A */ +#define DA7219_DAI_L_SRC_SHIFT 0 +#define DA7219_DAI_L_SRC_MASK (0x3 << 0) +#define DA7219_DAI_R_SRC_SHIFT 4 +#define DA7219_DAI_R_SRC_MASK (0x3 << 4) +#define DA7219_OUT_SRC_MAX 4 + +/* DA7219_DAI_CLK_MODE = 0x2B */ +#define DA7219_DAI_BCLKS_PER_WCLK_SHIFT 0 +#define DA7219_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_32 (0x0 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_64 (0x1 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_128 (0x2 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_256 (0x3 << 0) +#define DA7219_DAI_CLK_POL_SHIFT 2 +#define DA7219_DAI_CLK_POL_MASK (0x1 << 2) +#define DA7219_DAI_CLK_POL_INV (0x1 << 2) +#define DA7219_DAI_WCLK_POL_SHIFT 3 +#define DA7219_DAI_WCLK_POL_MASK (0x1 << 3) +#define DA7219_DAI_WCLK_POL_INV (0x1 << 3) +#define DA7219_DAI_WCLK_TRI_STATE_SHIFT 4 +#define DA7219_DAI_WCLK_TRI_STATE_MASK (0x1 << 4) +#define DA7219_DAI_CLK_EN_SHIFT 7 +#define DA7219_DAI_CLK_EN_MASK (0x1 << 7) + +/* DA7219_DAI_CTRL = 0x2C */ +#define DA7219_DAI_FORMAT_SHIFT 0 +#define DA7219_DAI_FORMAT_MASK (0x3 << 0) +#define DA7219_DAI_FORMAT_I2S (0x0 << 0) +#define DA7219_DAI_FORMAT_LEFT_J (0x1 << 0) +#define DA7219_DAI_FORMAT_RIGHT_J (0x2 << 0) +#define DA7219_DAI_FORMAT_DSP (0x3 << 0) +#define DA7219_DAI_WORD_LENGTH_SHIFT 2 +#define DA7219_DAI_WORD_LENGTH_MASK (0x3 << 2) +#define DA7219_DAI_WORD_LENGTH_S16_LE (0x0 << 2) +#define DA7219_DAI_WORD_LENGTH_S20_LE (0x1 << 2) +#define DA7219_DAI_WORD_LENGTH_S24_LE (0x2 << 2) +#define DA7219_DAI_WORD_LENGTH_S32_LE (0x3 << 2) +#define DA7219_DAI_CH_NUM_SHIFT 4 +#define DA7219_DAI_CH_NUM_MASK (0x3 << 4) +#define DA7219_DAI_CH_NUM_MAX 2 +#define DA7219_DAI_EN_SHIFT 7 +#define DA7219_DAI_EN_MASK (0x1 << 7) + +/* DA7219_DAI_TDM_CTRL = 0x2D */ +#define DA7219_DAI_TDM_CH_EN_SHIFT 0 +#define DA7219_DAI_TDM_CH_EN_MASK (0x3 << 0) +#define DA7219_DAI_OE_SHIFT 6 +#define DA7219_DAI_OE_MASK (0x1 << 6) +#define DA7219_DAI_TDM_MODE_EN_SHIFT 7 +#define DA7219_DAI_TDM_MODE_EN_MASK (0x1 << 7) +#define DA7219_DAI_TDM_MAX_SLOTS 2 + +/* DA7219_DIG_ROUTING_DAC = 0x2E */ +#define DA7219_DAC_L_SRC_SHIFT 0 +#define DA7219_DAC_L_SRC_MASK (0x3 << 0) +#define DA7219_DAC_L_SRC_TONEGEN (0x1 << 0) +#define DA7219_DAC_L_MONO_SHIFT 3 +#define DA7219_DAC_L_MONO_MASK (0x1 << 3) +#define DA7219_DAC_R_SRC_SHIFT 4 +#define DA7219_DAC_R_SRC_MASK (0x3 << 4) +#define DA7219_DAC_R_SRC_TONEGEN (0x1 << 4) +#define DA7219_DAC_R_MONO_SHIFT 7 +#define DA7219_DAC_R_MONO_MASK (0x1 << 7) + +/* DA7219_ALC_CTRL1 = 0x2F */ +#define DA7219_ALC_OFFSET_EN_SHIFT 0 +#define DA7219_ALC_OFFSET_EN_MASK (0x1 << 0) +#define DA7219_ALC_SYNC_MODE_SHIFT 1 +#define DA7219_ALC_SYNC_MODE_MASK (0x1 << 1) +#define DA7219_ALC_EN_SHIFT 3 +#define DA7219_ALC_EN_MASK (0x1 << 3) +#define DA7219_ALC_AUTO_CALIB_EN_SHIFT 4 +#define DA7219_ALC_AUTO_CALIB_EN_MASK (0x1 << 4) +#define DA7219_ALC_CALIB_OVERFLOW_SHIFT 5 +#define DA7219_ALC_CALIB_OVERFLOW_MASK (0x1 << 5) + +/* DA7219_DAI_OFFSET_LOWER = 0x30 */ +#define DA7219_DAI_OFFSET_LOWER_SHIFT 0 +#define DA7219_DAI_OFFSET_LOWER_MASK (0xFF << 0) + +/* DA7219_DAI_OFFSET_UPPER = 0x31 */ +#define DA7219_DAI_OFFSET_UPPER_SHIFT 0 +#define DA7219_DAI_OFFSET_UPPER_MASK (0x7 << 0) +#define DA7219_DAI_OFFSET_MAX 0x2FF + +/* DA7219_REFERENCES = 0x32 */ +#define DA7219_BIAS_EN_SHIFT 3 +#define DA7219_BIAS_EN_MASK (0x1 << 3) +#define DA7219_VMID_FAST_CHARGE_SHIFT 4 +#define DA7219_VMID_FAST_CHARGE_MASK (0x1 << 4) + +/* DA7219_MIXIN_L_SELECT = 0x33 */ +#define DA7219_MIXIN_L_MIX_SELECT_SHIFT 0 +#define DA7219_MIXIN_L_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_MIXIN_L_GAIN = 0x34 */ +#define DA7219_MIXIN_L_AMP_GAIN_SHIFT 0 +#define DA7219_MIXIN_L_AMP_GAIN_MASK (0xF << 0) +#define DA7219_MIXIN_L_AMP_GAIN_MAX 0xF + +/* DA7219_ADC_L_GAIN = 0x36 */ +#define DA7219_ADC_L_DIGITAL_GAIN_SHIFT 0 +#define DA7219_ADC_L_DIGITAL_GAIN_MASK (0x7F << 0) +#define DA7219_ADC_L_DIGITAL_GAIN_MAX 0x7F + +/* DA7219_ADC_FILTERS1 = 0x38 */ +#define DA7219_ADC_VOICE_HPF_CORNER_SHIFT 0 +#define DA7219_ADC_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7219_VOICE_HPF_CORNER_MAX 8 +#define DA7219_ADC_VOICE_EN_SHIFT 3 +#define DA7219_ADC_VOICE_EN_MASK (0x1 << 3) +#define DA7219_ADC_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7219_ADC_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7219_AUDIO_HPF_CORNER_MAX 4 +#define DA7219_ADC_HPF_EN_SHIFT 7 +#define DA7219_ADC_HPF_EN_MASK (0x1 << 7) +#define DA7219_HPF_MODE_SHIFT 0 +#define DA7219_HPF_DISABLED ((0x0 << 3) | (0x0 << 7)) +#define DA7219_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7)) +#define DA7219_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7)) +#define DA7219_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7)) +#define DA7219_HPF_MODE_MAX 3 + +/* DA7219_MIC_1_GAIN = 0x39 */ +#define DA7219_MIC_1_AMP_GAIN_SHIFT 0 +#define DA7219_MIC_1_AMP_GAIN_MASK (0x7 << 0) + +/* DA7219_SIDETONE_CTRL = 0x3A */ +#define DA7219_SIDETONE_MUTE_EN_SHIFT 6 +#define DA7219_SIDETONE_MUTE_EN_MASK (0x1 << 6) +#define DA7219_SIDETONE_EN_SHIFT 7 +#define DA7219_SIDETONE_EN_MASK (0x1 << 7) + +/* DA7219_SIDETONE_GAIN = 0x3B */ +#define DA7219_SIDETONE_GAIN_SHIFT 0 +#define DA7219_SIDETONE_GAIN_MASK (0xF << 0) +#define DA7219_SIDETONE_GAIN_MAX 0xE + +/* DA7219_DROUTING_ST_OUTFILT_1L = 0x3C */ +#define DA7219_OUTFILT_ST_1L_SRC_SHIFT 0 +#define DA7219_OUTFILT_ST_1L_SRC_MASK (0x7 << 0) +#define DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT 0 +#define DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT 1 +#define DA7219_DMIX_ST_SRC_SIDETONE_SHIFT 2 +#define DA7219_DMIX_ST_SRC_OUTFILT1L (0x1 << 0) +#define DA7219_DMIX_ST_SRC_OUTFILT1R (0x1 << 1) + +/* DA7219_DROUTING_ST_OUTFILT_1R = 0x3D */ +#define DA7219_OUTFILT_ST_1R_SRC_SHIFT 0 +#define DA7219_OUTFILT_ST_1R_SRC_MASK (0x7 << 0) + +/* DA7219_DAC_FILTERS5 = 0x40 */ +#define DA7219_DAC_SOFTMUTE_RATE_SHIFT 4 +#define DA7219_DAC_SOFTMUTE_RATE_MASK (0x7 << 4) +#define DA7219_DAC_SOFTMUTE_RATE_MAX 7 +#define DA7219_DAC_SOFTMUTE_EN_SHIFT 7 +#define DA7219_DAC_SOFTMUTE_EN_MASK (0x1 << 7) + +/* DA7219_DAC_FILTERS2 = 0x41 */ +#define DA7219_DAC_EQ_BAND1_SHIFT 0 +#define DA7219_DAC_EQ_BAND1_MASK (0xF << 0) +#define DA7219_DAC_EQ_BAND2_SHIFT 4 +#define DA7219_DAC_EQ_BAND2_MASK (0xF << 4) +#define DA7219_DAC_EQ_BAND_MAX 0xF + +/* DA7219_DAC_FILTERS3 = 0x42 */ +#define DA7219_DAC_EQ_BAND3_SHIFT 0 +#define DA7219_DAC_EQ_BAND3_MASK (0xF << 0) +#define DA7219_DAC_EQ_BAND4_SHIFT 4 +#define DA7219_DAC_EQ_BAND4_MASK (0xF << 4) + +/* DA7219_DAC_FILTERS4 = 0x43 */ +#define DA7219_DAC_EQ_BAND5_SHIFT 0 +#define DA7219_DAC_EQ_BAND5_MASK (0xF << 0) +#define DA7219_DAC_EQ_EN_SHIFT 7 +#define DA7219_DAC_EQ_EN_MASK (0x1 << 7) + +/* DA7219_DAC_FILTERS1 = 0x44 */ +#define DA7219_DAC_VOICE_HPF_CORNER_SHIFT 0 +#define DA7219_DAC_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7219_DAC_VOICE_EN_SHIFT 3 +#define DA7219_DAC_VOICE_EN_MASK (0x1 << 3) +#define DA7219_DAC_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7219_DAC_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7219_DAC_HPF_EN_SHIFT 7 +#define DA7219_DAC_HPF_EN_MASK (0x1 << 7) + +/* DA7219_DAC_L_GAIN = 0x45 */ +#define DA7219_DAC_L_DIGITAL_GAIN_SHIFT 0 +#define DA7219_DAC_L_DIGITAL_GAIN_MASK (0x7F << 0) +#define DA7219_DAC_DIGITAL_GAIN_MAX 0x7F +#define DA7219_DAC_DIGITAL_GAIN_0DB (0x6F << 0) + +/* DA7219_DAC_R_GAIN = 0x46 */ +#define DA7219_DAC_R_DIGITAL_GAIN_SHIFT 0 +#define DA7219_DAC_R_DIGITAL_GAIN_MASK (0x7F << 0) + +/* DA7219_CP_CTRL = 0x47 */ +#define DA7219_CP_MCHANGE_SHIFT 4 +#define DA7219_CP_MCHANGE_MASK (0x3 << 4) +#define DA7219_CP_MCHANGE_REL_MASK 0x3 +#define DA7219_CP_MCHANGE_MAX 3 +#define DA7219_CP_MCHANGE_LARGEST_VOL 0x1 +#define DA7219_CP_MCHANGE_DAC_VOL 0x2 +#define DA7219_CP_MCHANGE_SIG_MAG 0x3 +#define DA7219_CP_EN_SHIFT 7 +#define DA7219_CP_EN_MASK (0x1 << 7) + +/* DA7219_HP_L_GAIN = 0x48 */ +#define DA7219_HP_L_AMP_GAIN_SHIFT 0 +#define DA7219_HP_L_AMP_GAIN_MASK (0x3F << 0) +#define DA7219_HP_AMP_GAIN_MAX 0x3F +#define DA7219_HP_AMP_GAIN_0DB (0x39 << 0) + +/* DA7219_HP_R_GAIN = 0x49 */ +#define DA7219_HP_R_AMP_GAIN_SHIFT 0 +#define DA7219_HP_R_AMP_GAIN_MASK (0x3F << 0) + +/* DA7219_MIXOUT_L_SELECT = 0x4B */ +#define DA7219_MIXOUT_L_MIX_SELECT_SHIFT 0 +#define DA7219_MIXOUT_L_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_MIXOUT_R_SELECT = 0x4C */ +#define DA7219_MIXOUT_R_MIX_SELECT_SHIFT 0 +#define DA7219_MIXOUT_R_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_SYSTEM_MODES_INPUT = 0x50 */ +#define DA7219_MODE_SUBMIT_SHIFT 0 +#define DA7219_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7219_ADC_MODE_SHIFT 1 +#define DA7219_ADC_MODE_MASK (0x7F << 1) + +/* DA7219_SYSTEM_MODES_OUTPUT = 0x51 */ +#define DA7219_MODE_SUBMIT_SHIFT 0 +#define DA7219_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7219_DAC_MODE_SHIFT 1 +#define DA7219_DAC_MODE_MASK (0x7F << 1) + +/* DA7219_MICBIAS_CTRL = 0x62 */ +#define DA7219_MICBIAS1_LEVEL_SHIFT 0 +#define DA7219_MICBIAS1_LEVEL_MASK (0x7 << 0) +#define DA7219_MICBIAS1_EN_SHIFT 3 +#define DA7219_MICBIAS1_EN_MASK (0x1 << 3) + +/* DA7219_MIC_1_CTRL = 0x63 */ +#define DA7219_MIC_1_AMP_RAMP_EN_SHIFT 5 +#define DA7219_MIC_1_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_MIC_1_AMP_MUTE_EN_SHIFT 6 +#define DA7219_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_MIC_1_AMP_EN_SHIFT 7 +#define DA7219_MIC_1_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXIN_L_CTRL = 0x65 */ +#define DA7219_MIXIN_L_MIX_EN_SHIFT 3 +#define DA7219_MIXIN_L_MIX_EN_MASK (0x1 << 3) +#define DA7219_MIXIN_L_AMP_ZC_EN_SHIFT 4 +#define DA7219_MIXIN_L_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT 5 +#define DA7219_MIXIN_L_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT 6 +#define DA7219_MIXIN_L_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_MIXIN_L_AMP_EN_SHIFT 7 +#define DA7219_MIXIN_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_ADC_L_CTRL = 0x67 */ +#define DA7219_ADC_L_BIAS_SHIFT 0 +#define DA7219_ADC_L_BIAS_MASK (0x3 << 0) +#define DA7219_ADC_L_RAMP_EN_SHIFT 5 +#define DA7219_ADC_L_RAMP_EN_MASK (0x1 << 5) +#define DA7219_ADC_L_MUTE_EN_SHIFT 6 +#define DA7219_ADC_L_MUTE_EN_MASK (0x1 << 6) +#define DA7219_ADC_L_EN_SHIFT 7 +#define DA7219_ADC_L_EN_MASK (0x1 << 7) + +/* DA7219_DAC_L_CTRL = 0x69 */ +#define DA7219_DAC_L_RAMP_EN_SHIFT 5 +#define DA7219_DAC_L_RAMP_EN_MASK (0x1 << 5) +#define DA7219_DAC_L_MUTE_EN_SHIFT 6 +#define DA7219_DAC_L_MUTE_EN_MASK (0x1 << 6) +#define DA7219_DAC_L_EN_SHIFT 7 +#define DA7219_DAC_L_EN_MASK (0x1 << 7) + +/* DA7219_DAC_R_CTRL = 0x6A */ +#define DA7219_DAC_R_RAMP_EN_SHIFT 5 +#define DA7219_DAC_R_RAMP_EN_MASK (0x1 << 5) +#define DA7219_DAC_R_MUTE_EN_SHIFT 6 +#define DA7219_DAC_R_MUTE_EN_MASK (0x1 << 6) +#define DA7219_DAC_R_EN_SHIFT 7 +#define DA7219_DAC_R_EN_MASK (0x1 << 7) + +/* DA7219_HP_L_CTRL = 0x6B */ +#define DA7219_HP_L_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7219_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7219_HP_L_AMP_OE_SHIFT 3 +#define DA7219_HP_L_AMP_OE_MASK (0x1 << 3) +#define DA7219_HP_L_AMP_ZC_EN_SHIFT 4 +#define DA7219_HP_L_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_HP_L_AMP_RAMP_EN_SHIFT 5 +#define DA7219_HP_L_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_HP_L_AMP_MUTE_EN_SHIFT 6 +#define DA7219_HP_L_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_HP_L_AMP_EN_SHIFT 7 +#define DA7219_HP_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_HP_R_CTRL = 0x6C */ +#define DA7219_HP_R_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7219_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7219_HP_R_AMP_OE_SHIFT 3 +#define DA7219_HP_R_AMP_OE_MASK (0x1 << 3) +#define DA7219_HP_R_AMP_ZC_EN_SHIFT 4 +#define DA7219_HP_R_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_HP_R_AMP_RAMP_EN_SHIFT 5 +#define DA7219_HP_R_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_HP_R_AMP_MUTE_EN_SHIFT 6 +#define DA7219_HP_R_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_HP_R_AMP_EN_SHIFT 7 +#define DA7219_HP_R_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXOUT_L_CTRL = 0x6E */ +#define DA7219_MIXOUT_L_AMP_EN_SHIFT 7 +#define DA7219_MIXOUT_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXOUT_R_CTRL = 0x6F */ +#define DA7219_MIXOUT_R_AMP_EN_SHIFT 7 +#define DA7219_MIXOUT_R_AMP_EN_MASK (0x1 << 7) + +/* DA7219_CHIP_ID1 = 0x81 */ +#define DA7219_CHIP_ID1_SHIFT 0 +#define DA7219_CHIP_ID1_MASK (0xFF << 0) + +/* DA7219_CHIP_ID2 = 0x82 */ +#define DA7219_CHIP_ID2_SHIFT 0 +#define DA7219_CHIP_ID2_MASK (0xFF << 0) + +/* DA7219_CHIP_REVISION = 0x83 */ +#define DA7219_CHIP_MINOR_SHIFT 0 +#define DA7219_CHIP_MINOR_MASK (0xF << 0) +#define DA7219_CHIP_MAJOR_SHIFT 4 +#define DA7219_CHIP_MAJOR_MASK (0xF << 4) + +/* DA7219_LDO_CTRL = 0x90 */ +#define DA7219_LDO_LEVEL_SELECT_SHIFT 4 +#define DA7219_LDO_LEVEL_SELECT_MASK (0x3 << 4) +#define DA7219_LDO_EN_SHIFT 7 +#define DA7219_LDO_EN_MASK (0x1 << 7) + +/* DA7219_IO_CTRL = 0x91 */ +#define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0 +#define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0) +#define DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V 0 +#define DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V 1 + +/* DA7219_GAIN_RAMP_CTRL = 0x92 */ +#define DA7219_GAIN_RAMP_RATE_SHIFT 0 +#define DA7219_GAIN_RAMP_RATE_MASK (0x3 << 0) +#define DA7219_GAIN_RAMP_RATE_MAX 4 + +/* DA7219_PC_COUNT = 0x94 */ +#define DA7219_PC_FREERUN_SHIFT 0 +#define DA7219_PC_FREERUN_MASK (0x1 << 0) +#define DA7219_PC_RESYNC_AUTO_SHIFT 1 +#define DA7219_PC_RESYNC_AUTO_MASK (0x1 << 1) + +/* DA7219_CP_VOL_THRESHOLD1 = 0x95 */ +#define DA7219_CP_THRESH_VDD2_SHIFT 0 +#define DA7219_CP_THRESH_VDD2_MASK (0x3F << 0) +#define DA7219_CP_THRESH_VDD2_MAX 0x3F + +/* DA7219_DIG_CTRL = 0x99 */ +#define DA7219_DAC_L_INV_SHIFT 3 +#define DA7219_DAC_L_INV_MASK (0x1 << 3) +#define DA7219_DAC_R_INV_SHIFT 7 +#define DA7219_DAC_R_INV_MASK (0x1 << 7) + +/* DA7219_ALC_CTRL2 = 0x9A */ +#define DA7219_ALC_ATTACK_SHIFT 0 +#define DA7219_ALC_ATTACK_MASK (0xF << 0) +#define DA7219_ALC_ATTACK_MAX 13 +#define DA7219_ALC_RELEASE_SHIFT 4 +#define DA7219_ALC_RELEASE_MASK (0xF << 4) +#define DA7219_ALC_RELEASE_MAX 11 + +/* DA7219_ALC_CTRL3 = 0x9B */ +#define DA7219_ALC_HOLD_SHIFT 0 +#define DA7219_ALC_HOLD_MASK (0xF << 0) +#define DA7219_ALC_HOLD_MAX 16 +#define DA7219_ALC_INTEG_ATTACK_SHIFT 4 +#define DA7219_ALC_INTEG_ATTACK_MASK (0x3 << 4) +#define DA7219_ALC_INTEG_RELEASE_SHIFT 6 +#define DA7219_ALC_INTEG_RELEASE_MASK (0x3 << 6) +#define DA7219_ALC_INTEG_MAX 4 + +/* DA7219_ALC_NOISE = 0x9C */ +#define DA7219_ALC_NOISE_SHIFT 0 +#define DA7219_ALC_NOISE_MASK (0x3F << 0) +#define DA7219_ALC_THRESHOLD_MAX 0x3F + +/* DA7219_ALC_TARGET_MIN = 0x9D */ +#define DA7219_ALC_THRESHOLD_MIN_SHIFT 0 +#define DA7219_ALC_THRESHOLD_MIN_MASK (0x3F << 0) + +/* DA7219_ALC_TARGET_MAX = 0x9E */ +#define DA7219_ALC_THRESHOLD_MAX_SHIFT 0 +#define DA7219_ALC_THRESHOLD_MAX_MASK (0x3F << 0) + +/* DA7219_ALC_GAIN_LIMITS = 0x9F */ +#define DA7219_ALC_ATTEN_MAX_SHIFT 0 +#define DA7219_ALC_ATTEN_MAX_MASK (0xF << 0) +#define DA7219_ALC_GAIN_MAX_SHIFT 4 +#define DA7219_ALC_GAIN_MAX_MASK (0xF << 4) +#define DA7219_ALC_ATTEN_GAIN_MAX 0xF + +/* DA7219_ALC_ANA_GAIN_LIMITS = 0xA0 */ +#define DA7219_ALC_ANA_GAIN_MIN_SHIFT 0 +#define DA7219_ALC_ANA_GAIN_MIN_MASK (0x7 << 0) +#define DA7219_ALC_ANA_GAIN_MIN 0x1 +#define DA7219_ALC_ANA_GAIN_MAX_SHIFT 4 +#define DA7219_ALC_ANA_GAIN_MAX_MASK (0x7 << 4) +#define DA7219_ALC_ANA_GAIN_MAX 0x7 + +/* DA7219_ALC_ANTICLIP_CTRL = 0xA1 */ +#define DA7219_ALC_ANTICLIP_STEP_SHIFT 0 +#define DA7219_ALC_ANTICLIP_STEP_MASK (0x3 << 0) +#define DA7219_ALC_ANTICLIP_STEP_MAX 4 +#define DA7219_ALC_ANTIPCLIP_EN_SHIFT 7 +#define DA7219_ALC_ANTIPCLIP_EN_MASK (0x1 << 7) + +/* DA7219_ALC_ANTICLIP_LEVEL = 0xA2 */ +#define DA7219_ALC_ANTICLIP_LEVEL_SHIFT 0 +#define DA7219_ALC_ANTICLIP_LEVEL_MASK (0x7F << 0) + +/* DA7219_ALC_OFFSET_AUTO_M_L = 0xA3 */ +#define DA7219_ALC_OFFSET_AUTO_M_L_SHIFT 0 +#define DA7219_ALC_OFFSET_AUTO_M_L_MASK (0xFF << 0) + +/* DA7219_ALC_OFFSET_AUTO_U_L = 0xA4 */ +#define DA7219_ALC_OFFSET_AUTO_U_L_SHIFT 0 +#define DA7219_ALC_OFFSET_AUTO_U_L_MASK (0xF << 0) + +/* DA7219_DAC_NG_SETUP_TIME = 0xAF */ +#define DA7219_DAC_NG_SETUP_TIME_SHIFT 0 +#define DA7219_DAC_NG_SETUP_TIME_MASK (0x3 << 0) +#define DA7219_DAC_NG_SETUP_TIME_MAX 4 +#define DA7219_DAC_NG_RAMPUP_RATE_SHIFT 2 +#define DA7219_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2) +#define DA7219_DAC_NG_RAMPDN_RATE_SHIFT 3 +#define DA7219_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3) +#define DA7219_DAC_NG_RAMP_RATE_MAX 2 + +/* DA7219_DAC_NG_OFF_THRESH = 0xB0 */ +#define DA7219_DAC_NG_OFF_THRESHOLD_SHIFT 0 +#define DA7219_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0) +#define DA7219_DAC_NG_THRESHOLD_MAX 0x7 + +/* DA7219_DAC_NG_ON_THRESH = 0xB1 */ +#define DA7219_DAC_NG_ON_THRESHOLD_SHIFT 0 +#define DA7219_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0) + +/* DA7219_DAC_NG_CTRL = 0xB2 */ +#define DA7219_DAC_NG_EN_SHIFT 7 +#define DA7219_DAC_NG_EN_MASK (0x1 << 7) + +/* DA7219_TONE_GEN_CFG1 = 0xB4 */ +#define DA7219_DTMF_REG_SHIFT 0 +#define DA7219_DTMF_REG_MASK (0xF << 0) +#define DA7219_DTMF_REG_MAX 16 +#define DA7219_DTMF_EN_SHIFT 4 +#define DA7219_DTMF_EN_MASK (0x1 << 4) +#define DA7219_START_STOPN_SHIFT 7 +#define DA7219_START_STOPN_MASK (0x1 << 7) + +/* DA7219_TONE_GEN_CFG2 = 0xB5 */ +#define DA7219_SWG_SEL_SHIFT 0 +#define DA7219_SWG_SEL_MASK (0x3 << 0) +#define DA7219_SWG_SEL_MAX 4 +#define DA7219_SWG_SEL_SRAMP (0x3 << 0) +#define DA7219_TONE_GEN_GAIN_SHIFT 4 +#define DA7219_TONE_GEN_GAIN_MASK (0xF << 4) +#define DA7219_TONE_GEN_GAIN_MAX 0xF +#define DA7219_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4) +#define DA7219_TONE_GEN_GAIN_MINUS_15DB (0x5 << 4) + +/* DA7219_TONE_GEN_CYCLES = 0xB6 */ +#define DA7219_BEEP_CYCLES_SHIFT 0 +#define DA7219_BEEP_CYCLES_MASK (0x7 << 0) + +/* DA7219_TONE_GEN_FREQ1_L = 0xB7 */ +#define DA7219_FREQ1_L_SHIFT 0 +#define DA7219_FREQ1_L_MASK (0xFF << 0) +#define DA7219_FREQ_MAX 0xFFFF + +/* DA7219_TONE_GEN_FREQ1_U = 0xB8 */ +#define DA7219_FREQ1_U_SHIFT 0 +#define DA7219_FREQ1_U_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_FREQ2_L = 0xB9 */ +#define DA7219_FREQ2_L_SHIFT 0 +#define DA7219_FREQ2_L_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_FREQ2_U = 0xBA */ +#define DA7219_FREQ2_U_SHIFT 0 +#define DA7219_FREQ2_U_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_ON_PER = 0xBB */ +#define DA7219_BEEP_ON_PER_SHIFT 0 +#define DA7219_BEEP_ON_PER_MASK (0x3F << 0) +#define DA7219_BEEP_ON_OFF_MAX 0x3F + +/* DA7219_TONE_GEN_OFF_PER = 0xBC */ +#define DA7219_BEEP_OFF_PER_SHIFT 0 +#define DA7219_BEEP_OFF_PER_MASK (0x3F << 0) + +/* DA7219_SYSTEM_STATUS = 0xE0 */ +#define DA7219_SC1_BUSY_SHIFT 0 +#define DA7219_SC1_BUSY_MASK (0x1 << 0) +#define DA7219_SC2_BUSY_SHIFT 1 +#define DA7219_SC2_BUSY_MASK (0x1 << 1) + +/* DA7219_SYSTEM_ACTIVE = 0xFD */ +#define DA7219_SYSTEM_ACTIVE_SHIFT 0 +#define DA7219_SYSTEM_ACTIVE_MASK (0x1 << 0) + + +/* + * General defines & data + */ + +/* Register inversion */ +#define DA7219_NO_INVERT 0 +#define DA7219_INVERT 1 + +/* Byte related defines */ +#define DA7219_BYTE_SHIFT 8 +#define DA7219_BYTE_MASK 0xFF + +/* PLL Output Frequencies */ +#define DA7219_PLL_FREQ_OUT_90316 90316800 +#define DA7219_PLL_FREQ_OUT_98304 98304000 + +/* PLL Frequency Dividers */ +#define DA7219_PLL_INDIV_2_5_MHZ_VAL 1 +#define DA7219_PLL_INDIV_5_10_MHZ_VAL 2 +#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4 +#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8 +#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16 + +/* SRM */ +#define DA7219_SRM_CHECK_RETRIES 8 + +enum da7219_clk_src { + DA7219_CLKSRC_MCLK = 0, + DA7219_CLKSRC_MCLK_SQR, +}; + +enum da7219_sys_clk { + DA7219_SYSCLK_MCLK = 0, + DA7219_SYSCLK_PLL, + DA7219_SYSCLK_PLL_SRM, + DA7219_SYSCLK_PLL_32KHZ +}; + +/* Regulators */ +enum da7219_supplies { + DA7219_SUPPLY_VDD = 0, + DA7219_SUPPLY_VDDMIC, + DA7219_SUPPLY_VDDIO, + DA7219_NUM_SUPPLIES, +}; + +struct da7219_aad_priv; + +/* Private data */ +struct da7219_priv { + struct da7219_aad_priv *aad; + struct da7219_pdata *pdata; + + struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES]; + struct regmap *regmap; + struct mutex lock; + + struct clk *mclk; + unsigned int mclk_rate; + int clk_src; + + bool master; + bool alc_en; +}; + +#endif /* __DA7219_H */ diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 6a09101..969e337 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -129,7 +129,7 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; int ret; if (deemph > 1) diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c new file mode 100644 index 0000000..7fc7b4e --- /dev/null +++ b/sound/soc/codecs/nau8825.c @@ -0,0 +1,1309 @@ +/* + * Nuvoton NAU8825 audio codec driver + * + * Copyright 2015 Google Chromium project. + * Author: Anatol Pomozov <anatol@chromium.org> + * Copyright 2015 Nuvoton Technology Corp. + * Co-author: Meng-Huang Kuo <mhkuo@nuvoton.com> + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/acpi.h> +#include <linux/math64.h> + +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> + + +#include "nau8825.h" + +#define NAU_FREF_MAX 13500000 +#define NAU_FVCO_MAX 100000000 +#define NAU_FVCO_MIN 90000000 + +struct nau8825_fll { + int mclk_src; + int ratio; + int fll_frac; + int fll_int; + int clk_ref_div; +}; + +struct nau8825_fll_attr { + unsigned int param; + unsigned int val; +}; + +/* scaling for mclk from sysclk_src output */ +static const struct nau8825_fll_attr mclk_src_scaling[] = { + { 1, 0x0 }, + { 2, 0x2 }, + { 4, 0x3 }, + { 8, 0x4 }, + { 16, 0x5 }, + { 32, 0x6 }, + { 3, 0x7 }, + { 6, 0xa }, + { 12, 0xb }, + { 24, 0xc }, + { 48, 0xd }, + { 96, 0xe }, + { 5, 0xf }, +}; + +/* ratio for input clk freq */ +static const struct nau8825_fll_attr fll_ratio[] = { + { 512000, 0x01 }, + { 256000, 0x02 }, + { 128000, 0x04 }, + { 64000, 0x08 }, + { 32000, 0x10 }, + { 8000, 0x20 }, + { 4000, 0x40 }, +}; + +static const struct nau8825_fll_attr fll_pre_scalar[] = { + { 1, 0x0 }, + { 2, 0x1 }, + { 4, 0x2 }, + { 8, 0x3 }, +}; + +static const struct reg_default nau8825_reg_defaults[] = { + { NAU8825_REG_ENA_CTRL, 0x00ff }, + { NAU8825_REG_CLK_DIVIDER, 0x0050 }, + { NAU8825_REG_FLL1, 0x0 }, + { NAU8825_REG_FLL2, 0x3126 }, + { NAU8825_REG_FLL3, 0x0008 }, + { NAU8825_REG_FLL4, 0x0010 }, + { NAU8825_REG_FLL5, 0x0 }, + { NAU8825_REG_FLL6, 0x6000 }, + { NAU8825_REG_FLL_VCO_RSV, 0xf13c }, + { NAU8825_REG_HSD_CTRL, 0x000c }, + { NAU8825_REG_JACK_DET_CTRL, 0x0 }, + { NAU8825_REG_INTERRUPT_MASK, 0x0 }, + { NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff }, + { NAU8825_REG_SAR_CTRL, 0x0015 }, + { NAU8825_REG_KEYDET_CTRL, 0x0110 }, + { NAU8825_REG_VDET_THRESHOLD_1, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_2, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_3, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_4, 0x0 }, + { NAU8825_REG_GPIO34_CTRL, 0x0 }, + { NAU8825_REG_GPIO12_CTRL, 0x0 }, + { NAU8825_REG_TDM_CTRL, 0x0 }, + { NAU8825_REG_I2S_PCM_CTRL1, 0x000b }, + { NAU8825_REG_I2S_PCM_CTRL2, 0x8010 }, + { NAU8825_REG_LEFT_TIME_SLOT, 0x0 }, + { NAU8825_REG_RIGHT_TIME_SLOT, 0x0 }, + { NAU8825_REG_BIQ_CTRL, 0x0 }, + { NAU8825_REG_BIQ_COF1, 0x0 }, + { NAU8825_REG_BIQ_COF2, 0x0 }, + { NAU8825_REG_BIQ_COF3, 0x0 }, + { NAU8825_REG_BIQ_COF4, 0x0 }, + { NAU8825_REG_BIQ_COF5, 0x0 }, + { NAU8825_REG_BIQ_COF6, 0x0 }, + { NAU8825_REG_BIQ_COF7, 0x0 }, + { NAU8825_REG_BIQ_COF8, 0x0 }, + { NAU8825_REG_BIQ_COF9, 0x0 }, + { NAU8825_REG_BIQ_COF10, 0x0 }, + { NAU8825_REG_ADC_RATE, 0x0010 }, + { NAU8825_REG_DAC_CTRL1, 0x0001 }, + { NAU8825_REG_DAC_CTRL2, 0x0 }, + { NAU8825_REG_DAC_DGAIN_CTRL, 0x0 }, + { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf }, + { NAU8825_REG_MUTE_CTRL, 0x0 }, + { NAU8825_REG_HSVOL_CTRL, 0x0 }, + { NAU8825_REG_DACL_CTRL, 0x02cf }, + { NAU8825_REG_DACR_CTRL, 0x00cf }, + { NAU8825_REG_ADC_DRC_KNEE_IP12, 0x1486 }, + { NAU8825_REG_ADC_DRC_KNEE_IP34, 0x0f12 }, + { NAU8825_REG_ADC_DRC_SLOPES, 0x25ff }, + { NAU8825_REG_ADC_DRC_ATKDCY, 0x3457 }, + { NAU8825_REG_DAC_DRC_KNEE_IP12, 0x1486 }, + { NAU8825_REG_DAC_DRC_KNEE_IP34, 0x0f12 }, + { NAU8825_REG_DAC_DRC_SLOPES, 0x25f9 }, + { NAU8825_REG_DAC_DRC_ATKDCY, 0x3457 }, + { NAU8825_REG_IMM_MODE_CTRL, 0x0 }, + { NAU8825_REG_CLASSG_CTRL, 0x0 }, + { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 }, + { NAU8825_REG_MISC_CTRL, 0x0 }, + { NAU8825_REG_BIAS_ADJ, 0x0 }, + { NAU8825_REG_TRIM_SETTINGS, 0x0 }, + { NAU8825_REG_ANALOG_CONTROL_1, 0x0 }, + { NAU8825_REG_ANALOG_CONTROL_2, 0x0 }, + { NAU8825_REG_ANALOG_ADC_1, 0x0011 }, + { NAU8825_REG_ANALOG_ADC_2, 0x0020 }, + { NAU8825_REG_RDAC, 0x0008 }, + { NAU8825_REG_MIC_BIAS, 0x0006 }, + { NAU8825_REG_BOOST, 0x0 }, + { NAU8825_REG_FEPGA, 0x0 }, + { NAU8825_REG_POWER_UP_CONTROL, 0x0 }, + { NAU8825_REG_CHARGE_PUMP, 0x0 }, +}; + +static bool nau8825_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_ENA_CTRL: + case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: + case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL: + case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL: + case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY: + case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY: + case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R: + case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: + case NAU8825_REG_MISC_CTRL: + case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS: + case NAU8825_REG_BIAS_ADJ: + case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: + case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: + case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA: + case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_GENERAL_STATUS: + return true; + default: + return false; + } + +} + +static bool nau8825_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL: + case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: + case NAU8825_REG_INTERRUPT_MASK: + case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL: + case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL: + case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY: + case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY: + case NAU8825_REG_IMM_MODE_CTRL: + case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: + case NAU8825_REG_MISC_CTRL: + case NAU8825_REG_BIAS_ADJ: + case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: + case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: + case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA: + case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_CHARGE_PUMP: + return true; + default: + return false; + } +} + +static bool nau8825_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_RESET: + case NAU8825_REG_IRQ_STATUS: + case NAU8825_REG_INT_CLR_KEY_STATUS: + case NAU8825_REG_IMM_RMS_L: + case NAU8825_REG_IMM_RMS_R: + case NAU8825_REG_I2C_DEVICE_ID: + case NAU8825_REG_SARDOUT_RAM_STATUS: + case NAU8825_REG_CHARGE_PUMP_INPUT_READ: + case NAU8825_REG_GENERAL_STATUS: + return true; + default: + return false; + } +} + +static int nau8825_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Prevent startup click by letting charge pump to ramp up */ + msleep(10); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const char * const nau8825_adc_decimation[] = { + "32", "64", "128", "256" +}; + +static const struct soc_enum nau8825_adc_decimation_enum = + SOC_ENUM_SINGLE(NAU8825_REG_ADC_RATE, NAU8825_ADC_SYNC_DOWN_SFT, + ARRAY_SIZE(nau8825_adc_decimation), nau8825_adc_decimation); + +static const char * const nau8825_dac_oversampl[] = { + "64", "256", "128", "", "32" +}; + +static const struct soc_enum nau8825_dac_oversampl_enum = + SOC_ENUM_SINGLE(NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_SFT, + ARRAY_SIZE(nau8825_dac_oversampl), nau8825_dac_oversampl); + +static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -10300, 2400); +static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0); +static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -5400, 0); +static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600); +static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -9600, 2400); + +static const struct snd_kcontrol_new nau8825_controls[] = { + SOC_SINGLE_TLV("Mic Volume", NAU8825_REG_ADC_DGAIN_CTRL, + 0, 0xff, 0, adc_vol_tlv), + SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8825_REG_ADC_DGAIN_CTRL, + 12, 8, 0x0f, 0, sidetone_vol_tlv), + SOC_DOUBLE_TLV("Headphone Volume", NAU8825_REG_HSVOL_CTRL, + 6, 0, 0x3f, 1, dac_vol_tlv), + SOC_SINGLE_TLV("Frontend PGA Volume", NAU8825_REG_POWER_UP_CONTROL, + 8, 37, 0, fepga_gain_tlv), + SOC_DOUBLE_TLV("Headphone Crosstalk Volume", NAU8825_REG_DAC_DGAIN_CTRL, + 0, 8, 0xff, 0, crosstalk_vol_tlv), + + SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum), + SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum), +}; + +/* DAC Mux 0x33[9] and 0x34[9] */ +static const char * const nau8825_dac_src[] = { + "DACL", "DACR", +}; + +static SOC_ENUM_SINGLE_DECL( + nau8825_dacl_enum, NAU8825_REG_DACL_CTRL, + NAU8825_DACL_CH_SEL_SFT, nau8825_dac_src); + +static SOC_ENUM_SINGLE_DECL( + nau8825_dacr_enum, NAU8825_REG_DACR_CTRL, + NAU8825_DACR_CH_SEL_SFT, nau8825_dac_src); + +static const struct snd_kcontrol_new nau8825_dacl_mux = + SOC_DAPM_ENUM("DACL Source", nau8825_dacl_enum); + +static const struct snd_kcontrol_new nau8825_dacr_mux = + SOC_DAPM_ENUM("DACR Source", nau8825_dacr_enum); + + +static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2, + 15, 1), + + SND_SOC_DAPM_INPUT("MIC"), + SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0), + + SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0, + NULL, 0), + + SND_SOC_DAPM_ADC("ADC", NULL, NAU8825_REG_ENA_CTRL, 8, 0), + SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL, + 0), + + /* ADC for button press detection */ + SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_ADC_EN_SFT, 0), + + SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0), + SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0), + SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACR_SFT, 0), + SND_SOC_DAPM_DAC("DDACL", NULL, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACL_SFT, 0), + SND_SOC_DAPM_SUPPLY("DDAC Clock", NAU8825_REG_ENA_CTRL, 6, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux), + SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux), + + SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, + 0), + + SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0, + nau8825_pump_event, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_PGA("Output Driver R Stage 1", + NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Output Driver L Stage 1", + NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Output Driver R Stage 2", + NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Output Driver L Stage 2", + NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1, + NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1, + NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0), + SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route nau8825_dapm_routes[] = { + {"Frontend PGA", NULL, "MIC"}, + {"ADC", NULL, "Frontend PGA"}, + {"ADC", NULL, "ADC Clock"}, + {"ADC", NULL, "ADC Power"}, + {"AIFTX", NULL, "ADC"}, + + {"DDACL", NULL, "Playback"}, + {"DDACR", NULL, "Playback"}, + {"DDACL", NULL, "DDAC Clock"}, + {"DDACR", NULL, "DDAC Clock"}, + {"DACL Mux", "DACL", "DDACL"}, + {"DACL Mux", "DACR", "DDACR"}, + {"DACR Mux", "DACL", "DDACL"}, + {"DACR Mux", "DACR", "DDACR"}, + {"HP amp L", NULL, "DACL Mux"}, + {"HP amp R", NULL, "DACR Mux"}, + {"HP amp L", NULL, "HP amp power"}, + {"HP amp R", NULL, "HP amp power"}, + {"ADACL", NULL, "HP amp L"}, + {"ADACR", NULL, "HP amp R"}, + {"ADACL", NULL, "ADACL Clock"}, + {"ADACR", NULL, "ADACR Clock"}, + {"Output Driver L Stage 1", NULL, "ADACL"}, + {"Output Driver R Stage 1", NULL, "ADACR"}, + {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"}, + {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"}, + {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"}, + {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"}, + {"Output DACL", NULL, "Output Driver L Stage 3"}, + {"Output DACR", NULL, "Output Driver R Stage 3"}, + {"HPOL", NULL, "Output DACL"}, + {"HPOR", NULL, "Output DACR"}, + {"HPOL", NULL, "Charge Pump"}, + {"HPOR", NULL, "Charge Pump"}, +}; + +static int nau8825_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + unsigned int val_len = 0; + + switch (params_width(params)) { + case 16: + val_len |= NAU8825_I2S_DL_16; + break; + case 20: + val_len |= NAU8825_I2S_DL_20; + break; + case 24: + val_len |= NAU8825_I2S_DL_24; + break; + case 32: + val_len |= NAU8825_I2S_DL_32; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, + NAU8825_I2S_DL_MASK, val_len); + + return 0; +} + +static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + unsigned int ctrl1_val = 0, ctrl2_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl2_val |= NAU8825_I2S_MS_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= NAU8825_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= NAU8825_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= NAU8825_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1_val |= NAU8825_I2S_DF_RIGTH; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= NAU8825_I2S_DF_PCM_AB; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1_val |= NAU8825_I2S_DF_PCM_AB; + ctrl1_val |= NAU8825_I2S_PCMB_EN; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, + NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK | + NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK, + ctrl1_val); + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, ctrl2_val); + + return 0; +} + +static const struct snd_soc_dai_ops nau8825_dai_ops = { + .hw_params = nau8825_hw_params, + .set_fmt = nau8825_set_dai_fmt, +}; + +#define NAU8825_RATES SNDRV_PCM_RATE_8000_192000 +#define NAU8825_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver nau8825_dai = { + .name = "nau8825-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8825_RATES, + .formats = NAU8825_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = NAU8825_RATES, + .formats = NAU8825_FORMATS, + }, + .ops = &nau8825_dai_ops, +}; + +/** + * nau8825_enable_jack_detect - Specify a jack for event reporting + * + * @component: component to register the jack with + * @jack: jack to use to report headset and button events on + * + * After this function has been called the headset insert/remove and button + * events will be routed to the given jack. Jack can be null to stop + * reporting. + */ +int nau8825_enable_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + struct regmap *regmap = nau8825->regmap; + + nau8825->jack = jack; + + /* Ground HP Outputs[1:0], needed for headset auto detection + * Enable Automatic Mic/Gnd switching reading on insert interrupt[6] + */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, + NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L); + + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect); + + +static bool nau8825_is_jack_inserted(struct regmap *regmap) +{ + int status; + + regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status); + return !(status & NAU8825_GPIO2JD1); +} + +static void nau8825_restart_jack_detection(struct regmap *regmap) +{ + /* this will restart the entire jack detection process including MIC/GND + * switching and create interrupts. We have to go from 0 to 1 and back + * to 0 to restart. + */ + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_DET_RESTART, NAU8825_JACK_DET_RESTART); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_DET_RESTART, 0); +} + +static void nau8825_eject_jack(struct nau8825 *nau8825) +{ + struct snd_soc_dapm_context *dapm = nau8825->dapm; + struct regmap *regmap = nau8825->regmap; + + snd_soc_dapm_disable_pin(dapm, "SAR"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS"); + /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0); + /* ground HPL/HPR, MICGRND1/2 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf); + + snd_soc_dapm_sync(dapm); +} + +static int nau8825_button_decode(int value) +{ + int buttons = 0; + + /* The chip supports up to 8 buttons, but ALSA defines only 6 buttons */ + if (value & BIT(0)) + buttons |= SND_JACK_BTN_0; + if (value & BIT(1)) + buttons |= SND_JACK_BTN_1; + if (value & BIT(2)) + buttons |= SND_JACK_BTN_2; + if (value & BIT(3)) + buttons |= SND_JACK_BTN_3; + if (value & BIT(4)) + buttons |= SND_JACK_BTN_4; + if (value & BIT(5)) + buttons |= SND_JACK_BTN_5; + + return buttons; +} + +static int nau8825_jack_insert(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + struct snd_soc_dapm_context *dapm = nau8825->dapm; + int jack_status_reg, mic_detected; + int type = 0; + + regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg); + mic_detected = (jack_status_reg >> 10) & 3; + + switch (mic_detected) { + case 0: + /* no mic */ + type = SND_JACK_HEADPHONE; + break; + case 1: + dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n"); + type = SND_JACK_HEADSET; + + /* Unground MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2, + 1 << 2); + /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKR2); + /* Attach SARADC to MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK, + NAU8825_SAR_INPUT_JKR2); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + break; + case 2: + case 3: + dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n"); + type = SND_JACK_HEADSET; + + /* Unground MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2, + 2 << 2); + /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKSLV); + /* Attach SARADC to MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK, + NAU8825_SAR_INPUT_JKSLV); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + break; + } + + if (type & SND_JACK_HEADPHONE) { + /* Unground HPL/R */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0); + } + + return type; +} + +#define NAU8825_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3) + +static irqreturn_t nau8825_interrupt(int irq, void *data) +{ + struct nau8825 *nau8825 = (struct nau8825 *)data; + struct regmap *regmap = nau8825->regmap; + int active_irq, clear_irq = 0, event = 0, event_mask = 0; + + regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq); + + if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) == + NAU8825_JACK_EJECTION_DETECTED) { + + nau8825_eject_jack(nau8825); + event_mask |= SND_JACK_HEADSET; + clear_irq = NAU8825_JACK_EJECTION_IRQ_MASK; + } else if (active_irq & NAU8825_KEY_SHORT_PRESS_IRQ) { + int key_status; + + regmap_read(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, + &key_status); + + /* upper 8 bits of the register are for short pressed keys, + * lower 8 bits - for long pressed buttons + */ + nau8825->button_pressed = nau8825_button_decode( + key_status >> 8); + + event |= nau8825->button_pressed; + event_mask |= NAU8825_BUTTONS; + clear_irq = NAU8825_KEY_SHORT_PRESS_IRQ; + } else if (active_irq & NAU8825_KEY_RELEASE_IRQ) { + event_mask = NAU8825_BUTTONS; + clear_irq = NAU8825_KEY_RELEASE_IRQ; + } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) { + if (nau8825_is_jack_inserted(regmap)) { + event |= nau8825_jack_insert(nau8825); + } else { + dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n"); + nau8825_eject_jack(nau8825); + } + + event_mask |= SND_JACK_HEADSET; + clear_irq = NAU8825_HEADSET_COMPLETION_IRQ; + } + + if (!clear_irq) + clear_irq = active_irq; + /* clears the rightmost interruption */ + regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq); + + if (event_mask) + snd_soc_jack_report(nau8825->jack, event, event_mask); + + return IRQ_HANDLED; +} + +static void nau8825_setup_buttons(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_TRACKING_GAIN_MASK, + nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_COMPARE_TIME_MASK, + nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_SAMPLING_TIME_MASK, + nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT); + + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_LEVELS_NR_MASK, + (nau8825->sar_threshold_num - 1) << NAU8825_KEYDET_LEVELS_NR_SFT); + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_HYSTERESIS_MASK, + nau8825->sar_hysteresis << NAU8825_KEYDET_HYSTERESIS_SFT); + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK, + nau8825->key_debounce << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT); + + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_1, + (nau8825->sar_threshold[0] << 8) | nau8825->sar_threshold[1]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_2, + (nau8825->sar_threshold[2] << 8) | nau8825->sar_threshold[3]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_3, + (nau8825->sar_threshold[4] << 8) | nau8825->sar_threshold[5]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_4, + (nau8825->sar_threshold[6] << 8) | nau8825->sar_threshold[7]); + + /* Enable short press and release interruptions */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_KEY_SHORT_PRESS_EN | NAU8825_IRQ_KEY_RELEASE_EN, + 0); +} + +static void nau8825_init_regs(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + + /* Enable Bias/Vmid */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_VMID, NAU8825_BIAS_VMID); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_GLOBAL_BIAS_EN, NAU8825_GLOBAL_BIAS_EN); + + /* VMID Tieoff */ + regmap_update_bits(regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_VMID_SEL_MASK, + nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT); + /* Disable Boost Driver, Automatic Short circuit protection enable */ + regmap_update_bits(regmap, NAU8825_REG_BOOST, + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | + NAU8825_SHORT_SHUTDOWN_EN, + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | + NAU8825_SHORT_SHUTDOWN_EN); + + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_OUTPUT_EN, + nau8825->jkdet_enable ? 0 : NAU8825_JKDET_OUTPUT_EN); + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_PULL_EN, + nau8825->jkdet_pull_enable ? 0 : NAU8825_JKDET_PULL_EN); + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_PULL_UP, + nau8825->jkdet_pull_up ? NAU8825_JKDET_PULL_UP : 0); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_POLARITY, + /* jkdet_polarity - 1 is for active-low */ + nau8825->jkdet_polarity ? 0 : NAU8825_JACK_POLARITY); + + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_INSERT_DEBOUNCE_MASK, + nau8825->jack_insert_debounce << NAU8825_JACK_INSERT_DEBOUNCE_SFT); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_EJECT_DEBOUNCE_MASK, + nau8825->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT); + + /* Mask unneeded IRQs: 1 - disable, 0 - enable */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff); + + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage); + + if (nau8825->sar_threshold_num) + nau8825_setup_buttons(nau8825); + + /* Default oversampling/decimations settings are unusable + * (audible hiss). Set it to something better. + */ + regmap_update_bits(regmap, NAU8825_REG_ADC_RATE, + NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128); + regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, + NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128); +} + +static const struct regmap_config nau8825_regmap_config = { + .val_bits = 16, + .reg_bits = 16, + + .max_register = NAU8825_REG_MAX, + .readable_reg = nau8825_readable_reg, + .writeable_reg = nau8825_writeable_reg, + .volatile_reg = nau8825_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8825_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8825_reg_defaults), +}; + +static int nau8825_codec_probe(struct snd_soc_codec *codec) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + nau8825->dapm = dapm; + + /* The interrupt clock is gated by x1[10:8], + * one of them needs to be enabled all the time for + * interrupts to happen. + */ + snd_soc_dapm_force_enable_pin(dapm, "DDACR"); + snd_soc_dapm_sync(dapm); + + /* Unmask interruptions. Handler uses dapm object so we can enable + * interruptions only after dapm is fully initialized. + */ + regmap_write(nau8825->regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0); + nau8825_restart_jack_detection(nau8825->regmap); + + return 0; +} + +/** + * nau8825_calc_fll_param - Calculate FLL parameters. + * @fll_in: external clock provided to codec. + * @fs: sampling rate. + * @fll_param: Pointer to structure of FLL parameters. + * + * Calculate FLL parameters to configure codec. + * + * Returns 0 for success or negative error code. + */ +static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, + struct nau8825_fll *fll_param) +{ + u64 fvco; + unsigned int fref, i; + + /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing + * freq_in by 1, 2, 4, or 8 using FLL pre-scalar. + * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK + */ + for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) { + fref = fll_in / fll_pre_scalar[i].param; + if (fref <= NAU_FREF_MAX) + break; + } + if (i == ARRAY_SIZE(fll_pre_scalar)) + return -EINVAL; + fll_param->clk_ref_div = fll_pre_scalar[i].val; + + /* Choose the FLL ratio based on FREF */ + for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) { + if (fref >= fll_ratio[i].param) + break; + } + if (i == ARRAY_SIZE(fll_ratio)) + return -EINVAL; + fll_param->ratio = fll_ratio[i].val; + + /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. + * FDCO must be within the 90MHz - 100MHz or the FFL cannot be + * guaranteed across the full range of operation. + * FDCO = freq_out * 2 * mclk_src_scaling + */ + for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { + fvco = 256 * fs * 2 * mclk_src_scaling[i].param; + if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX) + break; + } + if (i == ARRAY_SIZE(mclk_src_scaling)) + return -EINVAL; + fll_param->mclk_src = mclk_src_scaling[i].val; + + /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional + * input based on FDCO, FREF and FLL ratio. + */ + fvco = div_u64(fvco << 16, fref * fll_param->ratio); + fll_param->fll_int = (fvco >> 16) & 0x3FF; + fll_param->fll_frac = fvco & 0xFFFF; + return 0; +} + +static void nau8825_fll_apply(struct nau8825 *nau8825, + struct nau8825_fll *fll_param) +{ + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1, + NAU8825_FLL_RATIO_MASK, fll_param->ratio); + /* FLL 16-bit fractional input */ + regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac); + /* FLL 10-bit integer input */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3, + NAU8825_FLL_INTEGER_MASK, fll_param->fll_int); + /* FLL pre-scaler */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4, + NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div); + /* select divided VCO input */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, + NAU8825_FLL_FILTER_SW_MASK, 0x0000); + /* FLL sigma delta modulator enable */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, + NAU8825_SDM_EN_MASK, NAU8825_SDM_EN); +} + +/* freq_out must be 256*Fs in order to achieve the best performance */ +static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + struct nau8825_fll fll_param; + int ret, fs; + + fs = freq_out / 256; + ret = nau8825_calc_fll_param(freq_in, fs, &fll_param); + if (ret < 0) { + dev_err(codec->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + dev_dbg(codec->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n", + fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac, + fll_param.fll_int, fll_param.clk_ref_div); + + nau8825_fll_apply(nau8825, &fll_param); + mdelay(2); + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); + return 0; +} + +static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, + unsigned int freq) +{ + struct regmap *regmap = nau8825->regmap; + int ret; + + switch (clk_id) { + case NAU8825_CLK_MCLK: + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK); + regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0); + + /* We selected MCLK source but the clock itself managed externally */ + if (!nau8825->mclk) + break; + + if (!nau8825->mclk_freq) { + ret = clk_prepare_enable(nau8825->mclk); + if (ret) { + dev_err(nau8825->dev, "Unable to prepare codec mclk\n"); + return ret; + } + } + + if (nau8825->mclk_freq != freq) { + nau8825->mclk_freq = freq; + + freq = clk_round_rate(nau8825->mclk, freq); + ret = clk_set_rate(nau8825->mclk, freq); + if (ret) { + dev_err(nau8825->dev, "Unable to set mclk rate\n"); + return ret; + } + } + + break; + case NAU8825_CLK_INTERNAL: + regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, + NAU8825_DCO_EN); + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); + + if (nau8825->mclk_freq) { + clk_disable_unprepare(nau8825->mclk); + nau8825->mclk_freq = 0; + } + + break; + default: + dev_err(nau8825->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + dev_dbg(nau8825->dev, "Sysclk is %dHz and clock id is %d\n", freq, + clk_id); + return 0; +} + +static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + + return nau8825_configure_sysclk(nau8825, clk_id, freq); +} + +static int nau8825_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + if (nau8825->mclk_freq) { + ret = clk_prepare_enable(nau8825->mclk); + if (ret) { + dev_err(nau8825->dev, "Unable to prepare codec mclk\n"); + return ret; + } + } + + ret = regcache_sync(nau8825->regmap); + if (ret) { + dev_err(codec->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + + break; + + case SND_SOC_BIAS_OFF: + if (nau8825->mclk_freq) + clk_disable_unprepare(nau8825->mclk); + + regcache_mark_dirty(nau8825->regmap); + break; + } + return 0; +} + +static struct snd_soc_codec_driver nau8825_codec_driver = { + .probe = nau8825_codec_probe, + .set_sysclk = nau8825_set_sysclk, + .set_pll = nau8825_set_pll, + .set_bias_level = nau8825_set_bias_level, + .suspend_bias_off = true, + + .controls = nau8825_controls, + .num_controls = ARRAY_SIZE(nau8825_controls), + .dapm_widgets = nau8825_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets), + .dapm_routes = nau8825_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes), +}; + +static void nau8825_reset_chip(struct regmap *regmap) +{ + regmap_write(regmap, NAU8825_REG_RESET, 0x00); + regmap_write(regmap, NAU8825_REG_RESET, 0x00); +} + +static void nau8825_print_device_properties(struct nau8825 *nau8825) +{ + int i; + struct device *dev = nau8825->dev; + + dev_dbg(dev, "jkdet-enable: %d\n", nau8825->jkdet_enable); + dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8825->jkdet_pull_enable); + dev_dbg(dev, "jkdet-pull-up: %d\n", nau8825->jkdet_pull_up); + dev_dbg(dev, "jkdet-polarity: %d\n", nau8825->jkdet_polarity); + dev_dbg(dev, "micbias-voltage: %d\n", nau8825->micbias_voltage); + dev_dbg(dev, "vref-impedance: %d\n", nau8825->vref_impedance); + + dev_dbg(dev, "sar-threshold-num: %d\n", nau8825->sar_threshold_num); + for (i = 0; i < nau8825->sar_threshold_num; i++) + dev_dbg(dev, "sar-threshold[%d]=%d\n", i, + nau8825->sar_threshold[i]); + + dev_dbg(dev, "sar-hysteresis: %d\n", nau8825->sar_hysteresis); + dev_dbg(dev, "sar-voltage: %d\n", nau8825->sar_voltage); + dev_dbg(dev, "sar-compare-time: %d\n", nau8825->sar_compare_time); + dev_dbg(dev, "sar-sampling-time: %d\n", nau8825->sar_sampling_time); + dev_dbg(dev, "short-key-debounce: %d\n", nau8825->key_debounce); + dev_dbg(dev, "jack-insert-debounce: %d\n", + nau8825->jack_insert_debounce); + dev_dbg(dev, "jack-eject-debounce: %d\n", + nau8825->jack_eject_debounce); +} + +static int nau8825_read_device_properties(struct device *dev, + struct nau8825 *nau8825) { + + nau8825->jkdet_enable = device_property_read_bool(dev, + "nuvoton,jkdet-enable"); + nau8825->jkdet_pull_enable = device_property_read_bool(dev, + "nuvoton,jkdet-pull-enable"); + nau8825->jkdet_pull_up = device_property_read_bool(dev, + "nuvoton,jkdet-pull-up"); + device_property_read_u32(dev, "nuvoton,jkdet-polarity", + &nau8825->jkdet_polarity); + device_property_read_u32(dev, "nuvoton,micbias-voltage", + &nau8825->micbias_voltage); + device_property_read_u32(dev, "nuvoton,vref-impedance", + &nau8825->vref_impedance); + device_property_read_u32(dev, "nuvoton,sar-threshold-num", + &nau8825->sar_threshold_num); + device_property_read_u32_array(dev, "nuvoton,sar-threshold", + nau8825->sar_threshold, nau8825->sar_threshold_num); + device_property_read_u32(dev, "nuvoton,sar-hysteresis", + &nau8825->sar_hysteresis); + device_property_read_u32(dev, "nuvoton,sar-voltage", + &nau8825->sar_voltage); + device_property_read_u32(dev, "nuvoton,sar-compare-time", + &nau8825->sar_compare_time); + device_property_read_u32(dev, "nuvoton,sar-sampling-time", + &nau8825->sar_sampling_time); + device_property_read_u32(dev, "nuvoton,short-key-debounce", + &nau8825->key_debounce); + device_property_read_u32(dev, "nuvoton,jack-insert-debounce", + &nau8825->jack_insert_debounce); + device_property_read_u32(dev, "nuvoton,jack-eject-debounce", + &nau8825->jack_eject_debounce); + + nau8825->mclk = devm_clk_get(dev, "mclk"); + if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (PTR_ERR(nau8825->mclk) == -ENOENT) { + /* The MCLK is managed externally or not used at all */ + nau8825->mclk = NULL; + dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally"); + } else if (IS_ERR(nau8825->mclk)) { + return -EINVAL; + } + + return 0; +} + +static int nau8825_setup_irq(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + int ret; + + /* IRQ Output Enable */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_OUTPUT_EN, NAU8825_IRQ_OUTPUT_EN); + + /* Enable internal VCO needed for interruptions */ + nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0); + + /* Enable DDACR needed for interrupts + * It is the same as force_enable_pin("DDACR") we do later + */ + regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR); + + /* Chip needs one FSCLK cycle in order to generate interrupts, + * as we cannot guarantee one will be provided by the system. Turning + * master mode on then off enables us to generate that FSCLK cycle + * with a minimum of contention on the clock bus. + */ + regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER); + regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE); + + ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL, + nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "nau8825", nau8825); + + if (ret) { + dev_err(nau8825->dev, "Cannot request irq %d (%d)\n", + nau8825->irq, ret); + return ret; + } + + return 0; +} + +static int nau8825_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev); + int ret, value; + + if (!nau8825) { + nau8825 = devm_kzalloc(dev, sizeof(*nau8825), GFP_KERNEL); + if (!nau8825) + return -ENOMEM; + ret = nau8825_read_device_properties(dev, nau8825); + if (ret) + return ret; + } + + i2c_set_clientdata(i2c, nau8825); + + nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap_config); + if (IS_ERR(nau8825->regmap)) + return PTR_ERR(nau8825->regmap); + nau8825->dev = dev; + nau8825->irq = i2c->irq; + + nau8825_print_device_properties(nau8825); + + nau8825_reset_chip(nau8825->regmap); + ret = regmap_read(nau8825->regmap, NAU8825_REG_I2C_DEVICE_ID, &value); + if (ret < 0) { + dev_err(dev, "Failed to read device id from the NAU8825: %d\n", + ret); + return ret; + } + if ((value & NAU8825_SOFTWARE_ID_MASK) != + NAU8825_SOFTWARE_ID_NAU8825) { + dev_err(dev, "Not a NAU8825 chip\n"); + return -ENODEV; + } + + nau8825_init_regs(nau8825); + + if (i2c->irq) + nau8825_setup_irq(nau8825); + + return snd_soc_register_codec(&i2c->dev, &nau8825_codec_driver, + &nau8825_dai, 1); +} + +static int nau8825_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id nau8825_i2c_ids[] = { + { "nau8825", 0 }, + { } +}; + +#ifdef CONFIG_OF +static const struct of_device_id nau8825_of_ids[] = { + { .compatible = "nuvoton,nau8825", }, + {} +}; +MODULE_DEVICE_TABLE(of, nau8825_of_ids); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id nau8825_acpi_match[] = { + { "10508825", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, nau8825_acpi_match); +#endif + +static struct i2c_driver nau8825_driver = { + .driver = { + .name = "nau8825", + .of_match_table = of_match_ptr(nau8825_of_ids), + .acpi_match_table = ACPI_PTR(nau8825_acpi_match), + }, + .probe = nau8825_i2c_probe, + .remove = nau8825_i2c_remove, + .id_table = nau8825_i2c_ids, +}; +module_i2c_driver(nau8825_driver); + +MODULE_DESCRIPTION("ASoC nau8825 driver"); +MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h new file mode 100644 index 0000000..dff8edb --- /dev/null +++ b/sound/soc/codecs/nau8825.h @@ -0,0 +1,341 @@ +/* + * NAU8825 ALSA SoC audio driver + * + * Copyright 2015 Google Inc. + * Author: Anatol Pomozov <anatol.pomozov@chrominium.org> + * + * 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. + */ + +#ifndef __NAU8825_H__ +#define __NAU8825_H__ + +#define NAU8825_REG_RESET 0x00 +#define NAU8825_REG_ENA_CTRL 0x01 +#define NAU8825_REG_CLK_DIVIDER 0x03 +#define NAU8825_REG_FLL1 0x04 +#define NAU8825_REG_FLL2 0x05 +#define NAU8825_REG_FLL3 0x06 +#define NAU8825_REG_FLL4 0x07 +#define NAU8825_REG_FLL5 0x08 +#define NAU8825_REG_FLL6 0x09 +#define NAU8825_REG_FLL_VCO_RSV 0x0a +#define NAU8825_REG_HSD_CTRL 0x0c +#define NAU8825_REG_JACK_DET_CTRL 0x0d +#define NAU8825_REG_INTERRUPT_MASK 0x0f +#define NAU8825_REG_IRQ_STATUS 0x10 +#define NAU8825_REG_INT_CLR_KEY_STATUS 0x11 +#define NAU8825_REG_INTERRUPT_DIS_CTRL 0x12 +#define NAU8825_REG_SAR_CTRL 0x13 +#define NAU8825_REG_KEYDET_CTRL 0x14 +#define NAU8825_REG_VDET_THRESHOLD_1 0x15 +#define NAU8825_REG_VDET_THRESHOLD_2 0x16 +#define NAU8825_REG_VDET_THRESHOLD_3 0x17 +#define NAU8825_REG_VDET_THRESHOLD_4 0x18 +#define NAU8825_REG_GPIO34_CTRL 0x19 +#define NAU8825_REG_GPIO12_CTRL 0x1a +#define NAU8825_REG_TDM_CTRL 0x1b +#define NAU8825_REG_I2S_PCM_CTRL1 0x1c +#define NAU8825_REG_I2S_PCM_CTRL2 0x1d +#define NAU8825_REG_LEFT_TIME_SLOT 0x1e +#define NAU8825_REG_RIGHT_TIME_SLOT 0x1f +#define NAU8825_REG_BIQ_CTRL 0x20 +#define NAU8825_REG_BIQ_COF1 0x21 +#define NAU8825_REG_BIQ_COF2 0x22 +#define NAU8825_REG_BIQ_COF3 0x23 +#define NAU8825_REG_BIQ_COF4 0x24 +#define NAU8825_REG_BIQ_COF5 0x25 +#define NAU8825_REG_BIQ_COF6 0x26 +#define NAU8825_REG_BIQ_COF7 0x27 +#define NAU8825_REG_BIQ_COF8 0x28 +#define NAU8825_REG_BIQ_COF9 0x29 +#define NAU8825_REG_BIQ_COF10 0x2a +#define NAU8825_REG_ADC_RATE 0x2b +#define NAU8825_REG_DAC_CTRL1 0x2c +#define NAU8825_REG_DAC_CTRL2 0x2d +#define NAU8825_REG_DAC_DGAIN_CTRL 0x2f +#define NAU8825_REG_ADC_DGAIN_CTRL 0x30 +#define NAU8825_REG_MUTE_CTRL 0x31 +#define NAU8825_REG_HSVOL_CTRL 0x32 +#define NAU8825_REG_DACL_CTRL 0x33 +#define NAU8825_REG_DACR_CTRL 0x34 +#define NAU8825_REG_ADC_DRC_KNEE_IP12 0x38 +#define NAU8825_REG_ADC_DRC_KNEE_IP34 0x39 +#define NAU8825_REG_ADC_DRC_SLOPES 0x3a +#define NAU8825_REG_ADC_DRC_ATKDCY 0x3b +#define NAU8825_REG_DAC_DRC_KNEE_IP12 0x45 +#define NAU8825_REG_DAC_DRC_KNEE_IP34 0x46 +#define NAU8825_REG_DAC_DRC_SLOPES 0x47 +#define NAU8825_REG_DAC_DRC_ATKDCY 0x48 +#define NAU8825_REG_IMM_MODE_CTRL 0x4c +#define NAU8825_REG_IMM_RMS_L 0x4d +#define NAU8825_REG_IMM_RMS_R 0x4e +#define NAU8825_REG_CLASSG_CTRL 0x50 +#define NAU8825_REG_OPT_EFUSE_CTRL 0x51 +#define NAU8825_REG_MISC_CTRL 0x55 +#define NAU8825_REG_I2C_DEVICE_ID 0x58 +#define NAU8825_REG_SARDOUT_RAM_STATUS 0x59 +#define NAU8825_REG_BIAS_ADJ 0x66 +#define NAU8825_REG_TRIM_SETTINGS 0x68 +#define NAU8825_REG_ANALOG_CONTROL_1 0x69 +#define NAU8825_REG_ANALOG_CONTROL_2 0x6a +#define NAU8825_REG_ANALOG_ADC_1 0x71 +#define NAU8825_REG_ANALOG_ADC_2 0x72 +#define NAU8825_REG_RDAC 0x73 +#define NAU8825_REG_MIC_BIAS 0x74 +#define NAU8825_REG_BOOST 0x76 +#define NAU8825_REG_FEPGA 0x77 +#define NAU8825_REG_POWER_UP_CONTROL 0x7f +#define NAU8825_REG_CHARGE_PUMP 0x80 +#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81 +#define NAU8825_REG_GENERAL_STATUS 0x82 +#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS + +/* ENA_CTRL (0x1) */ +#define NAU8825_ENABLE_DACR_SFT 10 +#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT) +#define NAU8825_ENABLE_DACL_SFT 9 +#define NAU8825_ENABLE_ADC_SFT 8 +#define NAU8825_ENABLE_SAR_SFT 1 + +/* CLK_DIVIDER (0x3) */ +#define NAU8825_CLK_SRC_SFT 15 +#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0) + +/* FLL1 (0x04) */ +#define NAU8825_FLL_RATIO_MASK (0x7f << 0) + +/* FLL3 (0x06) */ +#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0) + +/* FLL4 (0x07) */ +#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10) + +/* FLL5 (0x08) */ +#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14) + +/* FLL6 (0x9) */ +#define NAU8825_DCO_EN_MASK (0x1 << 15) +#define NAU8825_DCO_EN (0x1 << 15) +#define NAU8825_DCO_DIS (0x0 << 15) +#define NAU8825_SDM_EN_MASK (0x1 << 14) +#define NAU8825_SDM_EN (0x1 << 14) +#define NAU8825_SDM_DIS (0x0 << 14) + +/* HSD_CTRL (0xc) */ +#define NAU8825_HSD_AUTO_MODE (1 << 6) +/* 0 - short to GND, 1 - open */ +#define NAU8825_SPKR_DWN1R (1 << 1) +#define NAU8825_SPKR_DWN1L (1 << 0) + +/* JACK_DET_CTRL (0xd) */ +#define NAU8825_JACK_DET_RESTART (1 << 9) +#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5 +#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT) +#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2 +#define NAU8825_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_EJECT_DEBOUNCE_SFT) +#define NAU8825_JACK_POLARITY (1 << 1) /* 0 - active low, 1 - active high */ + +/* INTERRUPT_MASK (0xf) */ +#define NAU8825_IRQ_OUTPUT_EN (1 << 11) +#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10) +#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7) +#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5) +#define NAU8825_IRQ_EJECT_EN (1 << 2) + +/* IRQ_STATUS (0x10) */ +#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10) +#define NAU8825_SHORT_CIRCUIT_IRQ (1 << 9) +#define NAU8825_IMPEDANCE_MEAS_IRQ (1 << 8) +#define NAU8825_KEY_IRQ_MASK (0x7 << 5) +#define NAU8825_KEY_RELEASE_IRQ (1 << 7) +#define NAU8825_KEY_LONG_PRESS_IRQ (1 << 6) +#define NAU8825_KEY_SHORT_PRESS_IRQ (1 << 5) +#define NAU8825_MIC_DETECTION_IRQ (1 << 4) +#define NAU8825_JACK_EJECTION_IRQ_MASK (3 << 2) +#define NAU8825_JACK_EJECTION_DETECTED (1 << 2) +#define NAU8825_JACK_INSERTION_IRQ_MASK (3 << 0) +#define NAU8825_JACK_INSERTION_DETECTED (1 << 0) + +/* INTERRUPT_DIS_CTRL (0x12) */ +#define NAU8825_IRQ_HEADSET_COMPLETE_DIS (1 << 10) +#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7) +#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5) +#define NAU8825_IRQ_EJECT_DIS (1 << 2) + +/* SAR_CTRL (0x13) */ +#define NAU8825_SAR_ADC_EN_SFT 12 +#define NAU8825_SAR_ADC_EN (1 << NAU8825_SAR_ADC_EN_SFT) +#define NAU8825_SAR_INPUT_MASK (1 << 11) +#define NAU8825_SAR_INPUT_JKSLV (1 << 11) +#define NAU8825_SAR_INPUT_JKR2 (0 << 11) +#define NAU8825_SAR_TRACKING_GAIN_SFT 8 +#define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT) +#define NAU8825_SAR_COMPARE_TIME_SFT 2 +#define NAU8825_SAR_COMPARE_TIME_MASK (3 << 2) +#define NAU8825_SAR_SAMPLING_TIME_SFT 0 +#define NAU8825_SAR_SAMPLING_TIME_MASK (3 << 0) + +/* KEYDET_CTRL (0x14) */ +#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT 12 +#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT) +#define NAU8825_KEYDET_LEVELS_NR_SFT 8 +#define NAU8825_KEYDET_LEVELS_NR_MASK (0x7 << 8) +#define NAU8825_KEYDET_HYSTERESIS_SFT 0 +#define NAU8825_KEYDET_HYSTERESIS_MASK 0xf + +/* GPIO12_CTRL (0x1a) */ +#define NAU8825_JKDET_PULL_UP (1 << 11) /* 0 - pull down, 1 - pull up */ +#define NAU8825_JKDET_PULL_EN (1 << 9) /* 0 - enable pull, 1 - disable */ +#define NAU8825_JKDET_OUTPUT_EN (1 << 8) /* 0 - enable input, 1 - enable output */ + +/* I2S_PCM_CTRL1 (0x1c) */ +#define NAU8825_I2S_BP_SFT 7 +#define NAU8825_I2S_BP_MASK (1 << NAU8825_I2S_BP_SFT) +#define NAU8825_I2S_BP_INV (1 << NAU8825_I2S_BP_SFT) +#define NAU8825_I2S_PCMB_SFT 6 +#define NAU8825_I2S_PCMB_MASK (1 << NAU8825_I2S_PCMB_SFT) +#define NAU8825_I2S_PCMB_EN (1 << NAU8825_I2S_PCMB_SFT) +#define NAU8825_I2S_DL_SFT 2 +#define NAU8825_I2S_DL_MASK (0x3 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_16 (0 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_20 (1 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_24 (2 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_32 (3 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DF_SFT 0 +#define NAU8825_I2S_DF_MASK (0x3 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_RIGTH (0 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_LEFT (1 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_I2S (2 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_PCM_AB (3 << NAU8825_I2S_DF_SFT) + +/* I2S_PCM_CTRL2 (0x1d) */ +#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */ +#define NAU8825_I2S_MS_SFT 3 +#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT) +#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT) +#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT) + +/* ADC_RATE (0x2b) */ +#define NAU8825_ADC_SYNC_DOWN_SFT 0 +#define NAU8825_ADC_SYNC_DOWN_MASK 0x3 +#define NAU8825_ADC_SYNC_DOWN_32 0 +#define NAU8825_ADC_SYNC_DOWN_64 1 +#define NAU8825_ADC_SYNC_DOWN_128 2 +#define NAU8825_ADC_SYNC_DOWN_256 3 + +/* DAC_CTRL1 (0x2c) */ +#define NAU8825_DAC_CLIP_OFF (1 << 7) +#define NAU8825_DAC_OVERSAMPLE_SFT 0 +#define NAU8825_DAC_OVERSAMPLE_MASK 0x7 +#define NAU8825_DAC_OVERSAMPLE_64 0 +#define NAU8825_DAC_OVERSAMPLE_256 1 +#define NAU8825_DAC_OVERSAMPLE_128 2 +#define NAU8825_DAC_OVERSAMPLE_32 4 + +/* MUTE_CTRL (0x31) */ +#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9) +#define NAU8825_DAC_SOFT_MUTE (1 << 9) + +/* HSVOL_CTRL (0x32) */ +#define NAU8825_HP_MUTE (1 << 15) + +/* DACL_CTRL (0x33) */ +#define NAU8825_DACL_CH_SEL_SFT 9 + +/* DACR_CTRL (0x34) */ +#define NAU8825_DACR_CH_SEL_SFT 9 + +/* I2C_DEVICE_ID (0x58) */ +#define NAU8825_GPIO2JD1 (1 << 7) +#define NAU8825_SOFTWARE_ID_MASK 0x3 +#define NAU8825_SOFTWARE_ID_NAU8825 0x0 + +/* BIAS_ADJ (0x66) */ +#define NAU8825_BIAS_VMID (1 << 6) +#define NAU8825_BIAS_VMID_SEL_SFT 4 +#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT) + +/* ANALOG_CONTROL_2 (0x6a) */ +#define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12) +#define NAU8825_DAC_CAPACITOR_MSB (1 << 1) +#define NAU8825_DAC_CAPACITOR_LSB (1 << 0) + +/* ANALOG_ADC_2 (0x72) */ +#define NAU8825_ADC_VREFSEL_MASK (0x3 << 8) +#define NAU8825_ADC_VREFSEL_ANALOG (0 << 8) +#define NAU8825_ADC_VREFSEL_VMID (1 << 8) +#define NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB (2 << 8) +#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8) +#define NAU8825_POWERUP_ADCL (1 << 6) + +/* MIC_BIAS (0x74) */ +#define NAU8825_MICBIAS_JKSLV (1 << 14) +#define NAU8825_MICBIAS_JKR2 (1 << 12) +#define NAU8825_MICBIAS_POWERUP_SFT 8 +#define NAU8825_MICBIAS_VOLTAGE_SFT 0 +#define NAU8825_MICBIAS_VOLTAGE_MASK 0x7 + +/* BOOST (0x76) */ +#define NAU8825_PRECHARGE_DIS (1 << 13) +#define NAU8825_GLOBAL_BIAS_EN (1 << 12) +#define NAU8825_HP_BOOST_G_DIS (1 << 8) +#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6) + +/* POWER_UP_CONTROL (0x7f) */ +#define NAU8825_POWERUP_INTEGR_R (1 << 5) +#define NAU8825_POWERUP_INTEGR_L (1 << 4) +#define NAU8825_POWERUP_DRV_IN_R (1 << 3) +#define NAU8825_POWERUP_DRV_IN_L (1 << 2) +#define NAU8825_POWERUP_HP_DRV_R (1 << 1) +#define NAU8825_POWERUP_HP_DRV_L (1 << 0) + +/* CHARGE_PUMP (0x80) */ +#define NAU8825_JAMNODCLOW (1 << 10) +#define NAU8825_POWER_DOWN_DACR (1 << 9) +#define NAU8825_POWER_DOWN_DACL (1 << 8) +#define NAU8825_CHANRGE_PUMP_EN (1 << 5) + + +/* System Clock Source */ +enum { + NAU8825_CLK_MCLK = 0, + NAU8825_CLK_INTERNAL, +}; + +struct nau8825 { + struct device *dev; + struct regmap *regmap; + struct snd_soc_dapm_context *dapm; + struct snd_soc_jack *jack; + struct clk *mclk; + int irq; + int mclk_freq; /* 0 - mclk is disabled */ + int button_pressed; + int micbias_voltage; + int vref_impedance; + bool jkdet_enable; + bool jkdet_pull_enable; + bool jkdet_pull_up; + int jkdet_polarity; + int sar_threshold_num; + int sar_threshold[8]; + int sar_hysteresis; + int sar_voltage; + int sar_compare_time; + int sar_sampling_time; + int key_debounce; + int jack_insert_debounce; + int jack_eject_debounce; +}; + +int nau8825_enable_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); + + +#endif /* __NAU8825_H__ */ diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c index 91d5166..a4b910e 100644 --- a/sound/soc/codecs/rl6347a.c +++ b/sound/soc/codecs/rl6347a.c @@ -11,25 +11,8 @@ */ #include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/pm.h> #include <linux/i2c.h> -#include <linux/platform_device.h> -#include <linux/spi/spi.h> -#include <linux/dmi.h> -#include <linux/acpi.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> -#include <sound/soc-dapm.h> -#include <sound/initval.h> -#include <sound/tlv.h> -#include <sound/jack.h> -#include <linux/workqueue.h> -#include <sound/hda_verbs.h> +#include <linux/regmap.h> #include "rl6347a.h" diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h index 1cb56e5..e127919 100644 --- a/sound/soc/codecs/rl6347a.h +++ b/sound/soc/codecs/rl6347a.h @@ -12,6 +12,8 @@ #ifndef __RL6347A_H__ #define __RL6347A_H__ +#include <sound/hda_verbs.h> + #define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D) #define RL6347A_VENDOR_REGISTERS 0x20 diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index bd93658..af2ed77 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -29,7 +29,6 @@ #include <sound/jack.h> #include <linux/workqueue.h> #include <sound/rt286.h> -#include <sound/hda_verbs.h> #include "rl6347a.h" #include "rt286.h" @@ -38,7 +37,7 @@ #define RT288_VENDOR_ID 0x10ec0288 struct rt286_priv { - const struct reg_default *index_cache; + struct reg_default *index_cache; int index_cache_size; struct regmap *regmap; struct snd_soc_codec *codec; @@ -1161,7 +1160,11 @@ static int rt286_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - rt286->index_cache = rt286_index_def; + rt286->index_cache = devm_kmemdup(&i2c->dev, rt286_index_def, + sizeof(rt286_index_def), GFP_KERNEL); + if (!rt286->index_cache) + return -ENOMEM; + rt286->index_cache_size = INDEX_CACHE_SIZE; rt286->i2c = i2c; i2c_set_clientdata(i2c, rt286); diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 3c2f0f8..b3f795c 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -28,7 +28,6 @@ #include <sound/jack.h> #include <linux/workqueue.h> #include <sound/rt298.h> -#include <sound/hda_verbs.h> #include "rl6347a.h" #include "rt298.h" @@ -49,25 +48,25 @@ struct rt298_priv { int is_hp_in; }; -static struct reg_default rt298_index_def[] = { - { 0x01, 0xaaaa }, - { 0x02, 0x8aaa }, +static const struct reg_default rt298_index_def[] = { + { 0x01, 0xa5a8 }, + { 0x02, 0x8e95 }, { 0x03, 0x0002 }, - { 0x04, 0xaf01 }, - { 0x08, 0x000d }, - { 0x09, 0xd810 }, - { 0x0a, 0x0120 }, + { 0x04, 0xaf67 }, + { 0x08, 0x200f }, + { 0x09, 0xd010 }, + { 0x0a, 0x0100 }, { 0x0b, 0x0000 }, { 0x0d, 0x2800 }, - { 0x0f, 0x0000 }, - { 0x19, 0x0a17 }, + { 0x0f, 0x0022 }, + { 0x19, 0x0217 }, { 0x20, 0x0020 }, { 0x33, 0x0208 }, { 0x46, 0x0300 }, - { 0x49, 0x0004 }, - { 0x4f, 0x50e9 }, - { 0x50, 0x2000 }, - { 0x63, 0x2902 }, + { 0x49, 0x4004 }, + { 0x4f, 0x50c9 }, + { 0x50, 0x3000 }, + { 0x63, 0x1b02 }, { 0x67, 0x1111 }, { 0x68, 0x1016 }, { 0x69, 0x273f }, @@ -129,7 +128,7 @@ static bool rt298_volatile_register(struct device *dev, unsigned int reg) case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0): return true; default: - return true; + return false; } @@ -1165,7 +1164,11 @@ static int rt298_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - rt298->index_cache = rt298_index_def; + rt298->index_cache = devm_kmemdup(&i2c->dev, rt298_index_def, + sizeof(rt298_index_def), GFP_KERNEL); + if (!rt298->index_cache) + return -ENOMEM; + rt298->index_cache_size = INDEX_CACHE_SIZE; rt298->i2c = i2c; i2c_set_clientdata(i2c, rt298); @@ -1214,7 +1217,7 @@ static int rt298_i2c_probe(struct i2c_client *i2c, mdelay(10); if (!rt298->pdata.gpio2_en) - regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0x4000); + regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0x40); else regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0); diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index e1ceeb8..f2beb1a 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -405,11 +405,14 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = { SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL, RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 175, 0, dac_vol_tlv), - /* IN1/IN2 Control */ + /* IN1/IN2/IN3 Control */ SOC_SINGLE_TLV("IN1 Boost", RT5640_IN1_IN2, RT5640_BST_SFT1, 8, 0, bst_tlv), SOC_SINGLE_TLV("IN2 Boost", RT5640_IN3_IN4, RT5640_BST_SFT2, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN3 Boost", RT5640_IN1_IN2, + RT5640_BST_SFT2, 8, 0, bst_tlv), + /* INL/INR Volume Control */ SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL, RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT, @@ -598,6 +601,8 @@ static const struct snd_kcontrol_new rt5640_rec_l_mix[] = { RT5640_M_HP_L_RM_L_SFT, 1, 1), SOC_DAPM_SINGLE("INL Switch", RT5640_REC_L2_MIXER, RT5640_M_IN_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_L2_MIXER, + RT5640_M_BST2_RM_L_SFT, 1, 1), SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_L2_MIXER, RT5640_M_BST4_RM_L_SFT, 1, 1), SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER, @@ -611,6 +616,8 @@ static const struct snd_kcontrol_new rt5640_rec_r_mix[] = { RT5640_M_HP_R_RM_R_SFT, 1, 1), SOC_DAPM_SINGLE("INR Switch", RT5640_REC_R2_MIXER, RT5640_M_IN_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_R2_MIXER, + RT5640_M_BST2_RM_R_SFT, 1, 1), SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_R2_MIXER, RT5640_M_BST4_RM_R_SFT, 1, 1), SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER, @@ -1065,6 +1072,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN1N"), SND_SOC_DAPM_INPUT("IN2P"), SND_SOC_DAPM_INPUT("IN2N"), + SND_SOC_DAPM_INPUT("IN3P"), + SND_SOC_DAPM_INPUT("IN3N"), SND_SOC_DAPM_PGA("DMIC L1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("DMIC R1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("DMIC L2", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -1081,6 +1090,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { RT5640_PWR_BST1_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2, RT5640_PWR_BST4_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST3", RT5640_PWR_ANLG2, + RT5640_PWR_BST2_BIT, 0, NULL, 0), /* Input Volume */ SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL, RT5640_PWR_IN_L_BIT, 0, NULL, 0), @@ -1310,6 +1321,7 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = { static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"IN1P", NULL, "LDO2"}, {"IN2P", NULL, "LDO2"}, + {"IN3P", NULL, "LDO2"}, {"DMIC L1", NULL, "DMIC1"}, {"DMIC R1", NULL, "DMIC1"}, @@ -1320,18 +1332,22 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"BST1", NULL, "IN1N"}, {"BST2", NULL, "IN2P"}, {"BST2", NULL, "IN2N"}, + {"BST3", NULL, "IN3P"}, + {"BST3", NULL, "IN3N"}, {"INL VOL", NULL, "IN2P"}, {"INR VOL", NULL, "IN2N"}, {"RECMIXL", "HPOL Switch", "HPOL"}, {"RECMIXL", "INL Switch", "INL VOL"}, + {"RECMIXL", "BST3 Switch", "BST3"}, {"RECMIXL", "BST2 Switch", "BST2"}, {"RECMIXL", "BST1 Switch", "BST1"}, {"RECMIXL", "OUT MIXL Switch", "OUT MIXL"}, {"RECMIXR", "HPOR Switch", "HPOR"}, {"RECMIXR", "INR Switch", "INR VOL"}, + {"RECMIXR", "BST3 Switch", "BST3"}, {"RECMIXR", "BST2 Switch", "BST2"}, {"RECMIXR", "BST1 Switch", "BST1"}, {"RECMIXR", "OUT MIXR Switch", "OUT MIXR"}, @@ -2260,6 +2276,10 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4, RT5640_IN_DF2, RT5640_IN_DF2); + if (rt5640->pdata.in3_diff) + regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2, + RT5640_IN_DF2, RT5640_IN_DF2); + rt5640->hp_mute = 1; return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640, diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 080cc1c..2813237 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -42,6 +42,8 @@ #define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING)) +#define RT5645_HWEQ_NUM 57 + static const struct regmap_range_cfg rt5645_ranges[] = { { .name = "PR", @@ -224,6 +226,11 @@ static const struct reg_default rt5645_reg[] = { { 0xff, 0x6308 }, }; +struct rt5645_eq_param_s { + unsigned short reg; + unsigned short val; +}; + static const char *const rt5645_supply_names[] = { "avdd", "cpvdd", @@ -240,6 +247,7 @@ struct rt5645_priv { struct snd_soc_jack *btn_jack; struct delayed_work jack_detect_work; struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)]; + struct rt5645_eq_param_s *eq_param; int codec_type; int sysclk; @@ -469,6 +477,94 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv, 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) ); +/* {-6, -4.5, -3, -1.5, 0, 0.82, 1.58, 2.28} dB */ +static const DECLARE_TLV_DB_RANGE(spk_clsd_tlv, + 0, 4, TLV_DB_SCALE_ITEM(-600, 150, 0), + 5, 5, TLV_DB_SCALE_ITEM(82, 0, 0), + 6, 6, TLV_DB_SCALE_ITEM(158, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(228, 0, 0) +); + +static int rt5645_hweq_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s); + + return 0; +} + +static int rt5645_hweq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + struct rt5645_eq_param_s *eq_param = + (struct rt5645_eq_param_s *)ucontrol->value.bytes.data; + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + eq_param[i].reg = cpu_to_be16(rt5645->eq_param[i].reg); + eq_param[i].val = cpu_to_be16(rt5645->eq_param[i].val); + } + + return 0; +} + +static bool rt5645_validate_hweq(unsigned short reg) +{ + if ((reg >= 0x1a4 && reg <= 0x1cd) | (reg >= 0x1e5 && reg <= 0x1f8) | + (reg == RT5645_EQ_CTRL2)) + return true; + + return false; +} + +static int rt5645_hweq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + struct rt5645_eq_param_s *eq_param = + (struct rt5645_eq_param_s *)ucontrol->value.bytes.data; + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + eq_param[i].reg = be16_to_cpu(eq_param[i].reg); + eq_param[i].val = be16_to_cpu(eq_param[i].val); + } + + /* The final setting of the table should be RT5645_EQ_CTRL2 */ + for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) { + if (eq_param[i].reg == 0) + continue; + else if (eq_param[i].reg != RT5645_EQ_CTRL2) + return 0; + else + break; + } + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + if (!rt5645_validate_hweq(eq_param[i].reg) && + eq_param[i].reg != 0) + return 0; + else if (eq_param[i].reg == 0) + break; + } + + memcpy(rt5645->eq_param, eq_param, + RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s)); + + return 0; +} + +#define RT5645_HWEQ(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = rt5645_hweq_info, \ + .get = rt5645_hweq_get, \ + .put = rt5645_hweq_put \ +} + static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* Speaker Output Volume */ SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, @@ -476,6 +572,10 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL, RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), + /* ClassD modulator Speaker Gain Ratio */ + SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO, + RT5645_SPK_G_CLSD_SFT, 7, 0, spk_clsd_tlv), + /* Headphone Output Volume */ SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL, RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), @@ -529,6 +629,7 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* I2S2 function select */ SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT, 1, 1), + RT5645_HWEQ("Speaker HWEQ"), }; /** @@ -619,6 +720,22 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source, } +static int rt5645_enable_hweq(struct snd_soc_codec *codec) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + if (rt5645_validate_hweq(rt5645->eq_param[i].reg)) + regmap_write(rt5645->regmap, rt5645->eq_param[i].reg, + rt5645->eq_param[i].val); + else + break; + } + + return 0; +} + /** * rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters * @codec: SoC audio codec device. @@ -1523,6 +1640,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: + rt5645_enable_hweq(codec); snd_soc_update_bits(codec, RT5645_PWR_DIG1, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | RT5645_PWR_CLS_D_L, @@ -1531,6 +1649,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_PRE_PMD: + snd_soc_write(codec, RT5645_EQ_CTRL2, 0); snd_soc_update_bits(codec, RT5645_PWR_DIG1, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | RT5645_PWR_CLS_D_L, 0); @@ -2733,6 +2852,10 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, RT5645_PWR_ANLG1, RT5645_PWR_FV1 | RT5645_PWR_FV2, RT5645_PWR_FV1 | RT5645_PWR_FV2); + if (rt5645->en_button_func && + snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) + queue_delayed_work(system_power_efficient_wq, + &rt5645->jack_detect_work, msecs_to_jiffies(0)); break; case SND_SOC_BIAS_OFF: @@ -3044,6 +3167,9 @@ static int rt5645_probe(struct snd_soc_codec *codec) snd_soc_dapm_sync(dapm); } + rt5645->eq_param = devm_kzalloc(codec->dev, + RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL); + return 0; } @@ -3104,7 +3230,7 @@ static struct snd_soc_dai_driver rt5645_dai[] = { .capture = { .stream_name = "AIF1 Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 4, .rates = RT5645_STEREO_RATES, .formats = RT5645_FORMATS, }, @@ -3215,6 +3341,13 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"), }, }, + { + .ident = "Google Reks", + .callback = strago_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Reks"), + }, + }, { } }; @@ -3232,7 +3365,7 @@ static int buddy_quirk_cb(const struct dmi_system_id *id) return 1; } -static struct dmi_system_id dmi_platform_intel_broadwell[] __initdata = { +static struct dmi_system_id dmi_platform_intel_broadwell[] = { { .ident = "Chrome Buddy", .callback = buddy_quirk_cb, @@ -3505,6 +3638,8 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c) RT5645_CBJ_MN_JD); regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, RT5645_CBJ_BST1_EN, 0); + msleep(20); + regmap_write(rt5645->regmap, RT5645_RESET, 0); } static struct i2c_driver rt5645_i2c_driver = { diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 61bc8ab..093e46d 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -621,14 +621,14 @@ #define RT5645_G_OM_L_SM_L_SFT 6 #define RT5645_M_BST1_L_SM_L (0x1 << 5) #define RT5645_M_BST1_L_SM_L_SFT 5 +#define RT5645_M_BST3_L_SM_L (0x1 << 4) +#define RT5645_M_BST3_L_SM_L_SFT 4 #define RT5645_M_IN_L_SM_L (0x1 << 3) #define RT5645_M_IN_L_SM_L_SFT 3 -#define RT5645_M_DAC_L1_SM_L (0x1 << 1) -#define RT5645_M_DAC_L1_SM_L_SFT 1 #define RT5645_M_DAC_L2_SM_L (0x1 << 2) #define RT5645_M_DAC_L2_SM_L_SFT 2 -#define RT5645_M_BST3_L_SM_L (0x1 << 4) -#define RT5645_M_BST3_L_SM_L_SFT 4 +#define RT5645_M_DAC_L1_SM_L (0x1 << 1) +#define RT5645_M_DAC_L1_SM_L_SFT 1 /* SPK Right Mixer Control (0x47) */ #define RT5645_G_RM_R_SM_R_MASK (0x3 << 14) @@ -643,14 +643,14 @@ #define RT5645_G_OM_R_SM_R_SFT 6 #define RT5645_M_BST2_R_SM_R (0x1 << 5) #define RT5645_M_BST2_R_SM_R_SFT 5 +#define RT5645_M_BST3_R_SM_R (0x1 << 4) +#define RT5645_M_BST3_R_SM_R_SFT 4 #define RT5645_M_IN_R_SM_R (0x1 << 3) #define RT5645_M_IN_R_SM_R_SFT 3 -#define RT5645_M_DAC_R1_SM_R (0x1 << 1) -#define RT5645_M_DAC_R1_SM_R_SFT 1 #define RT5645_M_DAC_R2_SM_R (0x1 << 2) #define RT5645_M_DAC_R2_SM_R_SFT 2 -#define RT5645_M_BST3_R_SM_R (0x1 << 4) -#define RT5645_M_BST3_R_SM_R_SFT 4 +#define RT5645_M_DAC_R1_SM_R (0x1 << 1) +#define RT5645_M_DAC_R1_SM_R_SFT 1 /* SPOLMIX Control (0x48) */ #define RT5645_M_DAC_L1_SPM_L (0x1 << 15) @@ -670,13 +670,17 @@ #define RT5645_M_SV_R_SPM_R (0x1 << 0) #define RT5645_M_SV_R_SPM_R_SFT 0 +/* SPOMIX Ratio Control (0x4a) */ +#define RT5645_SPK_G_CLSD_MASK (0x7 << 0) +#define RT5645_SPK_G_CLSD_SFT 0 + /* Mono Output Mixer Control (0x4c) */ +#define RT5645_G_MONOMIX_MASK (0x1 << 10) +#define RT5645_G_MONOMIX_SFT 10 #define RT5645_M_OV_L_MM (0x1 << 9) #define RT5645_M_OV_L_MM_SFT 9 #define RT5645_M_DAC_L2_MA (0x1 << 8) #define RT5645_M_DAC_L2_MA_SFT 8 -#define RT5645_G_MONOMIX_MASK (0x1 << 10) -#define RT5645_G_MONOMIX_SFT 10 #define RT5645_M_BST2_MM (0x1 << 4) #define RT5645_M_BST2_MM_SFT 4 #define RT5645_M_DAC_R1_MM (0x1 << 3) diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index ddb0203..86b81a6 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -723,17 +723,11 @@ static struct snd_soc_codec_driver ssm2518_codec_driver = { .num_dapm_routes = ARRAY_SIZE(ssm2518_routes), }; -static bool ssm2518_register_volatile(struct device *dev, unsigned int reg) -{ - return false; -} - static const struct regmap_config ssm2518_regmap_config = { .val_bits = 8, .reg_bits = 8, .max_register = SSM2518_REG_DRC_9, - .volatile_reg = ssm2518_register_volatile, .cache_type = REGCACHE_RBTREE, .reg_defaults = ssm2518_reg_defaults, diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 786abd0..a67ea10 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -620,7 +620,7 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); - int anc_active = ucontrol->value.integer.value[0]; + unsigned int anc_active = ucontrol->value.integer.value[0]; int ret; if (anc_active > 1) @@ -653,7 +653,7 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); - int val = ucontrol->value.integer.value[0]; + unsigned int val = ucontrol->value.integer.value[0]; int ret; if (val > 1) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index ace8645..07cf1bd 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -132,7 +132,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; int ret = 0; if (deemph > 1) diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index b011253..e4cc41e 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -452,7 +452,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; int ret = 0; if (deemph > 1) diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index b783743..2aa23f1 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -534,7 +534,7 @@ static int wm8904_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 12e4435..9db00d5 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -402,7 +402,7 @@ static int wm8955_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index dbd8840..0563753 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -201,7 +201,7 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 2fbc6ef..39ebd7b 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3808,6 +3808,8 @@ static int wm8962_runtime_resume(struct device *dev) wm8962_reset(wm8962); + regcache_mark_dirty(wm8962->regmap); + /* SYSCLK defaults to on; make sure it is off so we can safely * write to registers if the device is declocked. */ diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c new file mode 100644 index 0000000..8782dfb --- /dev/null +++ b/sound/soc/codecs/wm8998.c @@ -0,0 +1,1430 @@ +/* + * wm8998.c -- ALSA SoC Audio driver for WM8998 codecs + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com> + * + * 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/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/registers.h> + +#include "arizona.h" +#include "wm8998.h" + +struct wm8998_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static int wm8998_asrc_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); + unsigned int val; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + val = snd_soc_read(codec, ARIZONA_ASRC_RATE1); + val &= ARIZONA_ASRC_RATE1_MASK; + val >>= ARIZONA_ASRC_RATE1_SHIFT; + + switch (val) { + case 0: + case 1: + case 2: + val = snd_soc_read(codec, + ARIZONA_SAMPLE_RATE_1 + val); + if (val >= 0x11) { + dev_warn(codec->dev, + "Unsupported ASRC rate1 (%s)\n", + arizona_sample_rate_val_to_name(val)); + return -EINVAL; + } + break; + default: + dev_err(codec->dev, + "Illegal ASRC rate1 selector (0x%x)\n", + val); + return -EINVAL; + } + + val = snd_soc_read(codec, ARIZONA_ASRC_RATE2); + val &= ARIZONA_ASRC_RATE2_MASK; + val >>= ARIZONA_ASRC_RATE2_SHIFT; + + switch (val) { + case 8: + case 9: + val -= 0x8; + val = snd_soc_read(codec, + ARIZONA_ASYNC_SAMPLE_RATE_1 + val); + if (val >= 0x11) { + dev_warn(codec->dev, + "Unsupported ASRC rate2 (%s)\n", + arizona_sample_rate_val_to_name(val)); + return -EINVAL; + } + break; + default: + dev_err(codec->dev, + "Illegal ASRC rate2 selector (0x%x)\n", + val); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8998_in1mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = wm8998->core.arizona; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, inmode; + unsigned int mode_val, src_val; + + mux = ucontrol->value.enumerated.item[0]; + if (mux > 1) + return -EINVAL; + + /* L and R registers have same shift and mask */ + inmode = arizona->pdata.inmode[2 * mux]; + src_val = mux << ARIZONA_IN1L_SRC_SHIFT; + if (inmode & ARIZONA_INMODE_SE) + src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT; + + switch (arizona->pdata.inmode[0]) { + case ARIZONA_INMODE_DMIC: + if (mux) + mode_val = 0; /* B always analogue */ + else + mode_val = 1 << ARIZONA_IN1_MODE_SHIFT; + + snd_soc_update_bits(codec, ARIZONA_IN1L_CONTROL, + ARIZONA_IN1_MODE_MASK, mode_val); + + /* IN1A is digital so L and R must change together */ + /* src_val setting same for both registers */ + snd_soc_update_bits(codec, + ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_SRC_MASK | + ARIZONA_IN1L_SRC_SE_MASK, src_val); + snd_soc_update_bits(codec, + ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_SRC_MASK | + ARIZONA_IN1R_SRC_SE_MASK, src_val); + break; + default: + /* both analogue */ + snd_soc_update_bits(codec, + e->reg, + ARIZONA_IN1L_SRC_MASK | + ARIZONA_IN1L_SRC_SE_MASK, + src_val); + break; + } + + return snd_soc_dapm_mux_update_power(dapm, kcontrol, + ucontrol->value.enumerated.item[0], + e, NULL); +} + +static int wm8998_in2mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = wm8998->core.arizona; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, inmode, src_val, mode_val; + + mux = ucontrol->value.enumerated.item[0]; + if (mux > 1) + return -EINVAL; + + inmode = arizona->pdata.inmode[1 + (2 * mux)]; + if (inmode & ARIZONA_INMODE_DMIC) + mode_val = 1 << ARIZONA_IN2_MODE_SHIFT; + else + mode_val = 0; + + src_val = mux << ARIZONA_IN2L_SRC_SHIFT; + if (inmode & ARIZONA_INMODE_SE) + src_val |= 1 << ARIZONA_IN2L_SRC_SE_SHIFT; + + snd_soc_update_bits(codec, ARIZONA_IN2L_CONTROL, + ARIZONA_IN2_MODE_MASK, mode_val); + + snd_soc_update_bits(codec, ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_SRC_MASK | ARIZONA_IN2L_SRC_SE_MASK, + src_val); + + return snd_soc_dapm_mux_update_power(dapm, kcontrol, + ucontrol->value.enumerated.item[0], + e, NULL); +} + +static const char * const wm8998_inmux_texts[] = { + "A", + "B", +}; + +static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_SRC_SHIFT, + wm8998_inmux_texts); + +static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_SRC_SHIFT, + wm8998_inmux_texts); + +static const SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum, + ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_SRC_SHIFT, + wm8998_inmux_texts); + +static const struct snd_kcontrol_new wm8998_in1mux[2] = { + SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum, + snd_soc_dapm_get_enum_double, wm8998_in1mux_put), + SOC_DAPM_ENUM_EXT("IN1R Mux", wm8998_in1muxr_enum, + snd_soc_dapm_get_enum_double, wm8998_in1mux_put), +}; + +static const struct snd_kcontrol_new wm8998_in2mux = + SOC_DAPM_ENUM_EXT("IN2 Mux", wm8998_in2mux_enum, + snd_soc_dapm_get_enum_double, wm8998_in2mux_put); + +static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define WM8998_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUTL Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUTR Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG LINEOUTL Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG LINEOUTR Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0) + +static const struct snd_kcontrol_new wm8998_snd_controls[] = { +SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2 HPF Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +ARIZONA_GAINMUX_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), + +SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19), +SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19), +SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19), +SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19), +SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_GAINMUX_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1), +SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1), +SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), +SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]), +SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]), +SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1), + +ARIZONA_MIXER_CONTROLS("HPOUTL", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUTR", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LINEOUTL", ARIZONA_OUT2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LINEOUTR", ARIZONA_OUT2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDATL", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDATR", ARIZONA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_DOUBLE_R("HPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("LINEOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("LINEOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_DOUBLE("SPKDAT Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, + ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM8998_NG_SRC("HPOUTL", ARIZONA_NOISE_GATE_SELECT_1L), +WM8998_NG_SRC("HPOUTR", ARIZONA_NOISE_GATE_SELECT_1R), +WM8998_NG_SRC("LINEOUTL", ARIZONA_NOISE_GATE_SELECT_2L), +WM8998_NG_SRC("LINEOUTR", ARIZONA_NOISE_GATE_SELECT_2R), +WM8998_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM8998_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM8998_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM8998_NG_SRC("SPKDATL", ARIZONA_NOISE_GATE_SELECT_5L), +WM8998_NG_SRC("SPKDATR", ARIZONA_NOISE_GATE_SELECT_5R), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), + +ARIZONA_GAINMUX_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), + +ARIZONA_GAINMUX_CONTROLS("SPDIFTX1", ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SPDIFTX2", ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE), +}; + +ARIZONA_MUX_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDATL, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDATR, ARIZONA_OUT5RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(SPD1TX1, ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SPD1TX2, ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + +static const char * const wm8998_aec_loopback_texts[] = { + "HPOUTL", "HPOUTR", "LINEOUTL", "LINEOUTR", "EPOUT", + "SPKOUTL", "SPKOUTR", "SPKDATL", "SPKDATR", +}; + +static const unsigned int wm8998_aec_loopback_values[] = { + 0, 1, 2, 3, 4, 6, 7, 8, 9, +}; + +static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback, + ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); + +static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback, + ARIZONA_DAC_AEC_CONTROL_2, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); + +static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = { + SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback), + SOC_DAPM_ENUM("AEC2 Loopback", wm8998_aec2_loopback), +}; + +static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, + ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1AL"), +SND_SOC_DAPM_INPUT("IN1AR"), +SND_SOC_DAPM_INPUT("IN1BL"), +SND_SOC_DAPM_INPUT("IN1BR"), +SND_SOC_DAPM_INPUT("IN2A"), +SND_SOC_DAPM_INPUT("IN2B"), + +SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[0]), +SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[1]), +SND_SOC_DAPM_MUX("IN2 Mux", SND_SOC_NOPM, 0, 0, &wm8998_in2mux), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2 PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA_E("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_MUX("AEC1 Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &wm8998_aec_loopback_mux[0]), + +SND_SOC_DAPM_MUX("AEC2 Loopback", ARIZONA_DAC_AEC_CONTROL_2, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &wm8998_aec_loopback_mux[1]), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX4_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("SPD1TX1", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_VAL1_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("SPD1TX2", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_VAL2_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("SPD1", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_ENA_SHIFT, 0, NULL, 0), + +ARIZONA_MUX_WIDGETS(EQ1, "EQ1"), +ARIZONA_MUX_WIDGETS(EQ2, "EQ2"), +ARIZONA_MUX_WIDGETS(EQ3, "EQ3"), +ARIZONA_MUX_WIDGETS(EQ4, "EQ4"), + +ARIZONA_MUX_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MUX_WIDGETS(DRC1R, "DRC1R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUTL"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUTR"), +ARIZONA_MIXER_WIDGETS(OUT2L, "LINEOUTL"), +ARIZONA_MIXER_WIDGETS(OUT2R, "LINEOUTR"), +ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"), +ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"), +ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"), +ARIZONA_MIXER_WIDGETS(SPKDATL, "SPKDATL"), +ARIZONA_MIXER_WIDGETS(SPKDATR, "SPKDATR"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), + +ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +ARIZONA_MUX_WIDGETS(SLIMTX1, "SLIMTX1"), +ARIZONA_MUX_WIDGETS(SLIMTX2, "SLIMTX2"), +ARIZONA_MUX_WIDGETS(SLIMTX3, "SLIMTX3"), +ARIZONA_MUX_WIDGETS(SLIMTX4, "SLIMTX4"), +ARIZONA_MUX_WIDGETS(SLIMTX5, "SLIMTX5"), +ARIZONA_MUX_WIDGETS(SLIMTX6, "SLIMTX6"), + +ARIZONA_MUX_WIDGETS(SPD1TX1, "SPDIFTX1"), +ARIZONA_MUX_WIDGETS(SPD1TX2, "SPDIFTX2"), + +ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"), +ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), +ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), +ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +SND_SOC_DAPM_OUTPUT("LINEOUTL"), +SND_SOC_DAPM_OUTPUT("LINEOUTR"), +SND_SOC_DAPM_OUTPUT("EPOUT"), +SND_SOC_DAPM_OUTPUT("SPKOUTLN"), +SND_SOC_DAPM_OUTPUT("SPKOUTLP"), +SND_SOC_DAPM_OUTPUT("SPKOUTRN"), +SND_SOC_DAPM_OUTPUT("SPKOUTRP"), +SND_SOC_DAPM_OUTPUT("SPKDATL"), +SND_SOC_DAPM_OUTPUT("SPKDATR"), +SND_SOC_DAPM_OUTPUT("SPDIF"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC1 Loopback" }, \ + { name, "AEC2", "AEC2 Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2 PGA" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1L", "ASRC1L" }, \ + { name, "ASRC1R", "ASRC1R" }, \ + { name, "ASRC2L", "ASRC2L" }, \ + { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" } + +static const struct snd_soc_dapm_route wm8998_dapm_routes[] = { + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "AIF3 Capture", NULL, "DBVDD3" }, + { "AIF3 Playback", NULL, "DBVDD3" }, + + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + { "OUT2L", NULL, "CPVDD" }, + { "OUT2R", NULL, "CPVDD" }, + { "OUT3", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDDL" }, + { "OUT4R", NULL, "SPKVDDR" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT4R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + + { "IN1AL", NULL, "SYSCLK" }, + { "IN1AR", NULL, "SYSCLK" }, + { "IN1BL", NULL, "SYSCLK" }, + { "IN1BR", NULL, "SYSCLK" }, + { "IN2A", NULL, "SYSCLK" }, + { "IN2B", NULL, "SYSCLK" }, + + { "SPD1", NULL, "SYSCLK" }, + { "SPD1", NULL, "SPD1TX1" }, + { "SPD1", NULL, "SPD1TX2" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + + { "SLIMRX3", NULL, "Slim2 Playback" }, + { "SLIMRX4", NULL, "Slim2 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + + { "IN1L Mux", "A", "IN1AL" }, + { "IN1R Mux", "A", "IN1AR" }, + { "IN1L Mux", "B", "IN1BL" }, + { "IN1R Mux", "B", "IN1BR" }, + + { "IN2 Mux", "A", "IN2A" }, + { "IN2 Mux", "B", "IN2B" }, + + { "IN1L PGA", NULL, "IN1L Mux" }, + { "IN1R PGA", NULL, "IN1R Mux" }, + { "IN2 PGA", NULL, "IN2 Mux" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUTL"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUTR"), + ARIZONA_MIXER_ROUTES("OUT2L", "LINEOUTL"), + ARIZONA_MIXER_ROUTES("OUT2R", "LINEOUTR"), + ARIZONA_MIXER_ROUTES("OUT3", "EPOUT"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"), + ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"), + ARIZONA_MIXER_ROUTES("OUT5L", "SPKDATL"), + ARIZONA_MIXER_ROUTES("OUT5R", "SPKDATR"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), + + ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + ARIZONA_MUX_ROUTES("SLIMTX1", "SLIMTX1"), + ARIZONA_MUX_ROUTES("SLIMTX2", "SLIMTX2"), + ARIZONA_MUX_ROUTES("SLIMTX3", "SLIMTX3"), + ARIZONA_MUX_ROUTES("SLIMTX4", "SLIMTX4"), + ARIZONA_MUX_ROUTES("SLIMTX5", "SLIMTX5"), + ARIZONA_MUX_ROUTES("SLIMTX6", "SLIMTX6"), + + ARIZONA_MUX_ROUTES("SPD1TX1", "SPDIFTX1"), + ARIZONA_MUX_ROUTES("SPD1TX2", "SPDIFTX2"), + + ARIZONA_MUX_ROUTES("EQ1", "EQ1"), + ARIZONA_MUX_ROUTES("EQ2", "EQ2"), + ARIZONA_MUX_ROUTES("EQ3", "EQ3"), + ARIZONA_MUX_ROUTES("EQ4", "EQ4"), + + ARIZONA_MUX_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MUX_ROUTES("DRC1R", "DRC1R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + { "AEC1 Loopback", "HPOUTL", "OUT1L" }, + { "AEC1 Loopback", "HPOUTR", "OUT1R" }, + { "AEC2 Loopback", "HPOUTL", "OUT1L" }, + { "AEC2 Loopback", "HPOUTR", "OUT1R" }, + { "HPOUTL", NULL, "OUT1L" }, + { "HPOUTR", NULL, "OUT1R" }, + + { "AEC1 Loopback", "LINEOUTL", "OUT2L" }, + { "AEC1 Loopback", "LINEOUTR", "OUT2R" }, + { "AEC2 Loopback", "LINEOUTL", "OUT2L" }, + { "AEC2 Loopback", "LINEOUTR", "OUT2R" }, + { "LINEOUTL", NULL, "OUT2L" }, + { "LINEOUTR", NULL, "OUT2R" }, + + { "AEC1 Loopback", "EPOUT", "OUT3" }, + { "AEC2 Loopback", "EPOUT", "OUT3" }, + { "EPOUT", NULL, "OUT3" }, + + { "AEC1 Loopback", "SPKOUTL", "OUT4L" }, + { "AEC2 Loopback", "SPKOUTL", "OUT4L" }, + { "SPKOUTLN", NULL, "OUT4L" }, + { "SPKOUTLP", NULL, "OUT4L" }, + + { "AEC1 Loopback", "SPKOUTR", "OUT4R" }, + { "AEC2 Loopback", "SPKOUTR", "OUT4R" }, + { "SPKOUTRN", NULL, "OUT4R" }, + { "SPKOUTRP", NULL, "OUT4R" }, + + { "SPDIF", NULL, "SPD1" }, + + { "AEC1 Loopback", "SPKDATL", "OUT5L" }, + { "AEC1 Loopback", "SPKDATR", "OUT5R" }, + { "AEC2 Loopback", "SPKDATL", "OUT5L" }, + { "AEC2 Loopback", "SPKDATR", "OUT5R" }, + { "SPKDATL", NULL, "OUT5L" }, + { "SPKDATR", NULL, "OUT5R" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, +}; + +#define WM8998_RATES SNDRV_PCM_RATE_8000_192000 + +#define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm8998_dai[] = { + { + .name = "wm8998-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "wm8998-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "wm8998-aif3", + .id = 3, + .base = ARIZONA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "wm8998-slim1", + .id = 4, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm8998-slim2", + .id = 5, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, +}; + +static int wm8998_set_fll(struct snd_soc_codec *codec, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); + + switch (fll_id) { + case WM8998_FLL1: + return arizona_set_fll(&wm8998->fll[0], source, Fref, Fout); + case WM8998_FLL2: + return arizona_set_fll(&wm8998->fll[1], source, Fref, Fout); + case WM8998_FLL1_REFCLK: + return arizona_set_fll_refclk(&wm8998->fll[0], source, Fref, + Fout); + case WM8998_FLL2_REFCLK: + return arizona_set_fll_refclk(&wm8998->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +static int wm8998_codec_probe(struct snd_soc_codec *codec) +{ + struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + priv->core.arizona->dapm = dapm; + + arizona_init_spk(codec); + arizona_init_gpio(codec); + + snd_soc_dapm_disable_pin(dapm, "HAPTICS"); + + return 0; +} + +static int wm8998_codec_remove(struct snd_soc_codec *codec) +{ + struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec); + + priv->core.arizona->dapm = NULL; + + return 0; +} + +#define WM8998_DIG_VU 0x0200 + +static unsigned int wm8998_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, + ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, + ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, +}; + +static struct regmap *wm8998_get_regmap(struct device *dev) +{ + struct wm8998_priv *priv = dev_get_drvdata(dev); + + return priv->core.arizona->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_wm8998 = { + .probe = wm8998_codec_probe, + .remove = wm8998_codec_remove, + .get_regmap = wm8998_get_regmap, + + .idle_bias_off = true, + + .set_sysclk = arizona_set_sysclk, + .set_pll = wm8998_set_fll, + + .controls = wm8998_snd_controls, + .num_controls = ARRAY_SIZE(wm8998_snd_controls), + .dapm_widgets = wm8998_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets), + .dapm_routes = wm8998_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes), +}; + +static int wm8998_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct wm8998_priv *wm8998; + int i; + + wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv), + GFP_KERNEL); + if (!wm8998) + return -ENOMEM; + platform_set_drvdata(pdev, wm8998); + + wm8998->core.arizona = arizona; + wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */ + + for (i = 0; i < ARRAY_SIZE(wm8998->fll); i++) + wm8998->fll[i].vco_mult = 1; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &wm8998->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &wm8998->fll[1]); + + for (i = 0; i < ARRAY_SIZE(wm8998_dai); i++) + arizona_init_dai(&wm8998->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm8998_digital_vu); i++) + regmap_update_bits(arizona->regmap, wm8998_digital_vu[i], + WM8998_DIG_VU, WM8998_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998, + wm8998_dai, ARRAY_SIZE(wm8998_dai)); +} + +static int wm8998_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver wm8998_codec_driver = { + .driver = { + .name = "wm8998-codec", + }, + .probe = wm8998_probe, + .remove = wm8998_remove, +}; + +module_platform_driver(wm8998_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8998 driver"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:wm8998-codec"); diff --git a/sound/soc/codecs/wm8998.h b/sound/soc/codecs/wm8998.h new file mode 100644 index 0000000..1e86472 --- /dev/null +++ b/sound/soc/codecs/wm8998.h @@ -0,0 +1,23 @@ +/* + * wm8998.h -- ALSA SoC Audio driver for WM8998 codecs + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com> + * + * 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. + */ + +#ifndef _WM8998_H +#define _WM8998_H + +#include "arizona.h" + +#define WM8998_FLL1 1 +#define WM8998_FLL2 2 +#define WM8998_FLL1_REFCLK 3 +#define WM8998_FLL2_REFCLK 4 + +#endif diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index ba34252..6e6a70c 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -282,23 +282,25 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, config->sample_rate = params_rate(params); - if (dev->i2s_clk_cfg) { - ret = dev->i2s_clk_cfg(config); - if (ret < 0) { - dev_err(dev->dev, "runtime audio clk config fail\n"); - return ret; - } - } else { - u32 bitclk = config->sample_rate * config->data_width * 2; - - ret = clk_set_rate(dev->clk, bitclk); - if (ret) { - dev_err(dev->dev, "Can't set I2S clock rate: %d\n", - ret); - return ret; + if (dev->capability & DW_I2S_MASTER) { + if (dev->i2s_clk_cfg) { + ret = dev->i2s_clk_cfg(config); + if (ret < 0) { + dev_err(dev->dev, "runtime audio clk config fail\n"); + return ret; + } + } else { + u32 bitclk = config->sample_rate * + config->data_width * 2; + + ret = clk_set_rate(dev->clk, bitclk); + if (ret) { + dev_err(dev->dev, "Can't set I2S clock rate: %d\n", + ret); + return ret; + } } } - return 0; } @@ -348,12 +350,43 @@ static int dw_i2s_trigger(struct snd_pcm_substream *substream, return ret; } +static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + int ret = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + if (dev->capability & DW_I2S_SLAVE) + ret = 0; + else + ret = -EINVAL; + break; + case SND_SOC_DAIFMT_CBS_CFS: + if (dev->capability & DW_I2S_MASTER) + ret = 0; + else + ret = -EINVAL; + break; + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + ret = -EINVAL; + break; + default: + dev_dbg(dev->dev, "dwc : Invalid master/slave format\n"); + ret = -EINVAL; + break; + } + return ret; +} + static struct snd_soc_dai_ops dw_i2s_dai_ops = { .startup = dw_i2s_startup, .shutdown = dw_i2s_shutdown, .hw_params = dw_i2s_hw_params, .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, + .set_fmt = dw_i2s_set_fmt, }; static const struct snd_soc_component_driver dw_i2s_component = { @@ -366,7 +399,8 @@ static int dw_i2s_suspend(struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - clk_disable(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_disable(dev->clk); return 0; } @@ -374,7 +408,8 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - clk_enable(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_enable(dev->clk); return 0; } @@ -452,6 +487,14 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, dw_i2s_dai->capture.rates = rates; } + if (COMP1_MODE_EN(comp1)) { + dev_dbg(dev->dev, "designware: i2s master mode supported\n"); + dev->capability |= DW_I2S_MASTER; + } else { + dev_dbg(dev->dev, "designware: i2s slave mode supported\n"); + dev->capability |= DW_I2S_SLAVE; + } + return 0; } @@ -538,6 +581,7 @@ static int dw_i2s_probe(struct platform_device *pdev) struct resource *res; int ret; struct snd_soc_dai_driver *dw_i2s_dai; + const char *clk_id; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) { @@ -559,32 +603,35 @@ static int dw_i2s_probe(struct platform_device *pdev) return PTR_ERR(dev->i2s_base); dev->dev = &pdev->dev; + if (pdata) { + dev->capability = pdata->cap; + clk_id = NULL; ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); - if (ret < 0) - return ret; + } else { + clk_id = "i2sclk"; + ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); + } + if (ret < 0) + return ret; - dev->capability = pdata->cap; - dev->i2s_clk_cfg = pdata->i2s_clk_cfg; - if (!dev->i2s_clk_cfg) { - dev_err(&pdev->dev, "no clock configure method\n"); - return -ENODEV; + if (dev->capability & DW_I2S_MASTER) { + if (pdata) { + dev->i2s_clk_cfg = pdata->i2s_clk_cfg; + if (!dev->i2s_clk_cfg) { + dev_err(&pdev->dev, "no clock configure method\n"); + return -ENODEV; + } } + dev->clk = devm_clk_get(&pdev->dev, clk_id); - dev->clk = devm_clk_get(&pdev->dev, NULL); - } else { - ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + + ret = clk_prepare_enable(dev->clk); if (ret < 0) return ret; - - dev->clk = devm_clk_get(&pdev->dev, "i2sclk"); } - if (IS_ERR(dev->clk)) - return PTR_ERR(dev->clk); - - ret = clk_prepare_enable(dev->clk); - if (ret < 0) - return ret; dev_set_drvdata(&pdev->dev, dev); ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component, @@ -606,7 +653,8 @@ static int dw_i2s_probe(struct platform_device *pdev) return 0; err_clk_disable: - clk_disable_unprepare(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_disable_unprepare(dev->clk); return ret; } @@ -614,7 +662,8 @@ static int dw_i2s_remove(struct platform_device *pdev) { struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); - clk_disable_unprepare(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_disable_unprepare(dev->clk); return 0; } diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 0901d5e..1b05d1c 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -14,6 +14,9 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/of_platform.h> +#if IS_ENABLED(CONFIG_SND_AC97_CODEC) +#include <sound/ac97_codec.h> +#endif #include <sound/pcm_params.h> #include <sound/soc.h> @@ -115,6 +118,11 @@ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = { SND_SOC_DAPM_MIC("DMIC", NULL), }; +static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv) +{ + return priv->dai_fmt == SND_SOC_DAIFMT_AC97; +} + static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -133,7 +141,9 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, * set_bias_level(), bypass the remaining settings in hw_params(). * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS. */ - if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) + if ((priv->card.set_bias_level && + priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) || + fsl_asoc_card_is_ac97(priv)) return 0; /* Specific configurations of DAIs starts from here */ @@ -300,7 +310,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, ext_port--; /* - * Use asynchronous mode (6 wires) for all cases. + * Use asynchronous mode (6 wires) for all cases except AC97. * If only 4 wires are needed, just set SSI into * synchronous mode and enable 4 PADs in IOMUX. */ @@ -346,15 +356,30 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, IMX_AUDMUX_V2_PTCR_TCLKDIR; break; default: - return -EINVAL; + if (!fsl_asoc_card_is_ac97(priv)) + return -EINVAL; + } + + if (fsl_asoc_card_is_ac97(priv)) { + int_ptcr = IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TCLKDIR; + ext_ptcr = IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | + IMX_AUDMUX_V2_PTCR_TFSDIR; } /* Asynchronous mode can not be set along with RCLKDIR */ - ret = imx_audmux_v2_configure_port(int_port, 0, - IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); - if (ret) { - dev_err(dev, "audmux internal port setup failed\n"); - return ret; + if (!fsl_asoc_card_is_ac97(priv)) { + unsigned int pdcr = + IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port); + + ret = imx_audmux_v2_configure_port(int_port, 0, + pdcr); + if (ret) { + dev_err(dev, "audmux internal port setup failed\n"); + return ret; + } } ret = imx_audmux_v2_configure_port(int_port, int_ptcr, @@ -364,11 +389,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, return ret; } - ret = imx_audmux_v2_configure_port(ext_port, 0, - IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); - if (ret) { - dev_err(dev, "audmux external port setup failed\n"); - return ret; + if (!fsl_asoc_card_is_ac97(priv)) { + unsigned int pdcr = + IMX_AUDMUX_V2_PDCR_RXDSEL(int_port); + + ret = imx_audmux_v2_configure_port(ext_port, 0, + pdcr); + if (ret) { + dev_err(dev, "audmux external port setup failed\n"); + return ret; + } } ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr, @@ -389,6 +419,23 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) struct device *dev = card->dev; int ret; + if (fsl_asoc_card_is_ac97(priv)) { +#if IS_ENABLED(CONFIG_SND_AC97_CODEC) + struct snd_soc_codec *codec = card->rtd[0].codec; + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + + /* + * Use slots 3/4 for S/PDIF so SSI won't try to enable + * other slots and send some samples there + * due to SLOTREQ bits for S/PDIF received from codec + */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, + AC97_EA_SPSA_SLOT_MASK, AC97_EA_SPSA_3_4); +#endif + + return 0; + } + ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, codec_priv->mclk_freq, SND_SOC_CLOCK_IN); if (ret) { @@ -407,7 +454,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) struct platform_device *cpu_pdev; struct fsl_asoc_card_priv *priv; struct i2c_client *codec_dev; - struct clk *codec_clk; const char *codec_dai_name; u32 width; int ret; @@ -420,9 +466,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Give a chance to old DT binding */ if (!cpu_np) cpu_np = of_parse_phandle(np, "ssi-controller", 0); - codec_np = of_parse_phandle(np, "audio-codec", 0); - if (!cpu_np || !codec_np) { - dev_err(&pdev->dev, "phandle missing or invalid\n"); + if (!cpu_np) { + dev_err(&pdev->dev, "CPU phandle missing or invalid\n"); ret = -EINVAL; goto fail; } @@ -434,22 +479,24 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto fail; } - codec_dev = of_find_i2c_device_by_node(codec_np); - if (!codec_dev) { - dev_err(&pdev->dev, "failed to find codec platform device\n"); - ret = -EINVAL; - goto fail; - } + codec_np = of_parse_phandle(np, "audio-codec", 0); + if (codec_np) + codec_dev = of_find_i2c_device_by_node(codec_np); + else + codec_dev = NULL; asrc_np = of_parse_phandle(np, "audio-asrc", 0); if (asrc_np) asrc_pdev = of_find_device_by_node(asrc_np); /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ - codec_clk = clk_get(&codec_dev->dev, NULL); - if (!IS_ERR(codec_clk)) { - priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); - clk_put(codec_clk); + if (codec_dev) { + struct clk *codec_clk = clk_get(&codec_dev->dev, NULL); + + if (!IS_ERR(codec_clk)) { + priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); + clk_put(codec_clk); + } } /* Default sample rate and format, will be updated in hw_params() */ @@ -486,12 +533,22 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO; priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO; priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { + codec_dai_name = "ac97-hifi"; + priv->card.set_bias_level = NULL; + priv->dai_fmt = SND_SOC_DAIFMT_AC97; } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); ret = -EINVAL; goto asrc_fail; } + if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) { + dev_err(&pdev->dev, "failed to find codec device\n"); + ret = -EINVAL; + goto asrc_fail; + } + /* Common settings for corresponding Freescale CPU DAI driver */ if (strstr(cpu_np->name, "ssi")) { /* Only SSI needs to configure AUDMUX */ @@ -508,7 +565,9 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; } - sprintf(priv->name, "%s-audio", codec_dev->name); + snprintf(priv->name, sizeof(priv->name), "%s-audio", + fsl_asoc_card_is_ac97(priv) ? "ac97" : + codec_dev->name); /* Initialize sound card */ priv->pdev = pdev; @@ -532,8 +591,26 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Normal DAI Link */ priv->dai_link[0].cpu_of_node = cpu_np; - priv->dai_link[0].codec_of_node = codec_np; priv->dai_link[0].codec_dai_name = codec_dai_name; + + if (!fsl_asoc_card_is_ac97(priv)) + priv->dai_link[0].codec_of_node = codec_np; + else { + u32 idx; + + ret = of_property_read_u32(cpu_np, "cell-index", &idx); + if (ret) { + dev_err(&pdev->dev, + "cannot get CPU index property\n"); + goto asrc_fail; + } + + priv->dai_link[0].codec_name = + devm_kasprintf(&pdev->dev, GFP_KERNEL, + "ac97-codec.%u", + (unsigned int)idx); + } + priv->dai_link[0].platform_of_node = cpu_np; priv->dai_link[0].dai_fmt = priv->dai_fmt; priv->card.num_links = 1; @@ -544,6 +621,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->dai_link[1].platform_of_node = asrc_np; priv->dai_link[2].codec_dai_name = codec_dai_name; priv->dai_link[2].codec_of_node = codec_np; + priv->dai_link[2].codec_name = + priv->dai_link[0].codec_name; priv->dai_link[2].cpu_of_node = cpu_np; priv->dai_link[2].dai_fmt = priv->dai_fmt; priv->card.num_links = 3; @@ -579,14 +658,15 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) asrc_fail: of_node_put(asrc_np); -fail: of_node_put(codec_np); +fail: of_node_put(cpu_np); return ret; } static const struct of_device_id fsl_asoc_card_dt_ids[] = { + { .compatible = "fsl,imx-audio-ac97", }, { .compatible = "fsl,imx-audio-cs42888", }, { .compatible = "fsl,imx-audio-sgtl5000", }, { .compatible = "fsl,imx-audio-wm8962", }, diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 837979e..59f234e 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -652,6 +652,24 @@ static const struct snd_soc_component_driver fsl_esai_component = { .name = "fsl-esai", }; +static const struct reg_default fsl_esai_reg_defaults[] = { + {0x8, 0x00000000}, + {0x10, 0x00000000}, + {0x18, 0x00000000}, + {0x98, 0x00000000}, + {0xd0, 0x00000000}, + {0xd4, 0x00000000}, + {0xd8, 0x00000000}, + {0xdc, 0x00000000}, + {0xe0, 0x00000000}, + {0xe4, 0x0000ffff}, + {0xe8, 0x0000ffff}, + {0xec, 0x0000ffff}, + {0xf0, 0x0000ffff}, + {0xf8, 0x00000000}, + {0xfc, 0x00000000}, +}; + static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -684,6 +702,31 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) } } +static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_ESAI_ETDR: + case REG_ESAI_ERDR: + case REG_ESAI_ESR: + case REG_ESAI_TFSR: + case REG_ESAI_RFSR: + case REG_ESAI_TX0: + case REG_ESAI_TX1: + case REG_ESAI_TX2: + case REG_ESAI_TX3: + case REG_ESAI_TX4: + case REG_ESAI_TX5: + case REG_ESAI_RX0: + case REG_ESAI_RX1: + case REG_ESAI_RX2: + case REG_ESAI_RX3: + case REG_ESAI_SAISR: + return true; + default: + return false; + } +} + static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -721,8 +764,12 @@ static const struct regmap_config fsl_esai_regmap_config = { .val_bits = 32, .max_register = REG_ESAI_PCRC, + .reg_defaults = fsl_esai_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_esai_reg_defaults), .readable_reg = fsl_esai_readable_reg, + .volatile_reg = fsl_esai_volatile_reg, .writeable_reg = fsl_esai_writeable_reg, + .cache_type = REGCACHE_RBTREE, }; static int fsl_esai_probe(struct platform_device *pdev) @@ -853,10 +900,51 @@ static const struct of_device_id fsl_esai_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); +#ifdef CONFIG_PM_SLEEP +static int fsl_esai_suspend(struct device *dev) +{ + struct fsl_esai *esai = dev_get_drvdata(dev); + + regcache_cache_only(esai->regmap, true); + regcache_mark_dirty(esai->regmap); + + return 0; +} + +static int fsl_esai_resume(struct device *dev) +{ + struct fsl_esai *esai = dev_get_drvdata(dev); + int ret; + + regcache_cache_only(esai->regmap, false); + + /* FIFO reset for safety */ + regmap_update_bits(esai->regmap, REG_ESAI_TFCR, + ESAI_xFCR_xFR, ESAI_xFCR_xFR); + regmap_update_bits(esai->regmap, REG_ESAI_RFCR, + ESAI_xFCR_xFR, ESAI_xFCR_xFR); + + ret = regcache_sync(esai->regmap); + if (ret) + return ret; + + /* FIFO reset done */ + regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0); + regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_esai_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_esai_suspend, fsl_esai_resume) +}; + static struct platform_driver fsl_esai_driver = { .probe = fsl_esai_probe, .driver = { .name = "fsl-esai-dai", + .pm = &fsl_esai_pm_ops, .of_match_table = fsl_esai_dt_ids, }, }; diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 9366b5a..a4435f5 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -27,13 +27,13 @@ #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ FSL_SAI_CSR_FEIE) -static u32 fsl_sai_rates[] = { +static const unsigned int fsl_sai_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000 }; -static struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = { +static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = { .count = ARRAY_SIZE(fsl_sai_rates), .list = fsl_sai_rates, }; @@ -637,6 +637,8 @@ static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { + case FSL_SAI_TCSR: + case FSL_SAI_RCSR: case FSL_SAI_TFR: case FSL_SAI_RFR: case FSL_SAI_TDR: @@ -681,6 +683,7 @@ static const struct regmap_config fsl_sai_regmap_config = { .readable_reg = fsl_sai_readable_reg, .volatile_reg = fsl_sai_volatile_reg, .writeable_reg = fsl_sai_writeable_reg, + .cache_type = REGCACHE_FLAT, }; static int fsl_sai_probe(struct platform_device *pdev) @@ -803,10 +806,40 @@ static const struct of_device_id fsl_sai_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_sai_ids); +#ifdef CONFIG_PM_SLEEP +static int fsl_sai_suspend(struct device *dev) +{ + struct fsl_sai *sai = dev_get_drvdata(dev); + + regcache_cache_only(sai->regmap, true); + regcache_mark_dirty(sai->regmap); + + return 0; +} + +static int fsl_sai_resume(struct device *dev) +{ + struct fsl_sai *sai = dev_get_drvdata(dev); + + regcache_cache_only(sai->regmap, false); + regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR); + regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR); + msleep(1); + regmap_write(sai->regmap, FSL_SAI_TCSR, 0); + regmap_write(sai->regmap, FSL_SAI_RCSR, 0); + return regcache_sync(sai->regmap); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_sai_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume) +}; + static struct platform_driver fsl_sai_driver = { .probe = fsl_sai_probe, .driver = { .name = "fsl-sai", + .pm = &fsl_sai_pm_ops, .of_match_table = fsl_sai_ids, }, }; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index ab729f2..3d59bb6 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -108,6 +108,8 @@ struct fsl_spdif_priv { struct clk *sysclk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; + /* regcache for SRPC */ + u32 regcache_srpc; }; /* DPLL locked and lock loss interrupt handler */ @@ -300,6 +302,8 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv) struct regmap *regmap = spdif_priv->regmap; u32 val, cycle = 1000; + regcache_cache_bypass(regmap, true); + regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET); /* @@ -310,6 +314,10 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv) regmap_read(regmap, REG_SPDIF_SCR, &val); } while ((val & SCR_SOFT_RESET) && cycle--); + regcache_cache_bypass(regmap, false); + regcache_mark_dirty(regmap); + regcache_sync(regmap); + if (cycle) return 0; else @@ -997,6 +1005,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = { }; /* FSL SPDIF REGMAP */ +static const struct reg_default fsl_spdif_reg_defaults[] = { + {0x0, 0x00000400}, + {0x4, 0x00000000}, + {0xc, 0x00000000}, + {0x34, 0x00000000}, + {0x38, 0x00000000}, + {0x50, 0x00020f00}, +}; static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) { @@ -1022,6 +1038,26 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) } } +static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_SPDIF_SRPC: + case REG_SPDIF_SIS: + case REG_SPDIF_SRL: + case REG_SPDIF_SRR: + case REG_SPDIF_SRCSH: + case REG_SPDIF_SRCSL: + case REG_SPDIF_SRU: + case REG_SPDIF_SRQ: + case REG_SPDIF_STL: + case REG_SPDIF_STR: + case REG_SPDIF_SRFM: + return true; + default: + return false; + } +} + static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -1047,8 +1083,12 @@ static const struct regmap_config fsl_spdif_regmap_config = { .val_bits = 32, .max_register = REG_SPDIF_STC, + .reg_defaults = fsl_spdif_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_spdif_reg_defaults), .readable_reg = fsl_spdif_readable_reg, + .volatile_reg = fsl_spdif_volatile_reg, .writeable_reg = fsl_spdif_writeable_reg, + .cache_type = REGCACHE_RBTREE, }; static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, @@ -1271,6 +1311,38 @@ static int fsl_spdif_probe(struct platform_device *pdev) return ret; } +#ifdef CONFIG_PM_SLEEP +static int fsl_spdif_suspend(struct device *dev) +{ + struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); + + regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC, + &spdif_priv->regcache_srpc); + + regcache_cache_only(spdif_priv->regmap, true); + regcache_mark_dirty(spdif_priv->regmap); + + return 0; +} + +static int fsl_spdif_resume(struct device *dev) +{ + struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); + + regcache_cache_only(spdif_priv->regmap, false); + + regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC, + SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK, + spdif_priv->regcache_srpc); + + return regcache_sync(spdif_priv->regmap); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_spdif_pm = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume) +}; + static const struct of_device_id fsl_spdif_dt_ids[] = { { .compatible = "fsl,imx35-spdif", }, { .compatible = "fsl,vf610-spdif", }, @@ -1282,6 +1354,7 @@ static struct platform_driver fsl_spdif_driver = { .driver = { .name = "fsl-spdif-dai", .of_match_table = fsl_spdif_dt_ids, + .pm = &fsl_spdif_pm, }, .probe = fsl_spdif_probe, }; diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 37c5cd4..95d2392 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -111,12 +111,75 @@ struct fsl_ssi_rxtx_reg_val { struct fsl_ssi_reg_val rx; struct fsl_ssi_reg_val tx; }; + +static const struct reg_default fsl_ssi_reg_defaults[] = { + {0x10, 0x00000000}, + {0x18, 0x00003003}, + {0x1c, 0x00000200}, + {0x20, 0x00000200}, + {0x24, 0x00040000}, + {0x28, 0x00040000}, + {0x38, 0x00000000}, + {0x48, 0x00000000}, + {0x4c, 0x00000000}, + {0x54, 0x00000000}, + {0x58, 0x00000000}, +}; + +static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CCSR_SSI_SACCEN: + case CCSR_SSI_SACCDIS: + return false; + default: + return true; + } +} + +static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CCSR_SSI_STX0: + case CCSR_SSI_STX1: + case CCSR_SSI_SRX0: + case CCSR_SSI_SRX1: + case CCSR_SSI_SISR: + case CCSR_SSI_SFCSR: + case CCSR_SSI_SACADD: + case CCSR_SSI_SACDAT: + case CCSR_SSI_SATAG: + case CCSR_SSI_SACCST: + return true; + default: + return false; + } +} + +static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CCSR_SSI_SRX0: + case CCSR_SSI_SRX1: + case CCSR_SSI_SACCST: + return false; + default: + return true; + } +} + static const struct regmap_config fsl_ssi_regconfig = { .max_register = CCSR_SSI_SACCDIS, .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .val_format_endian = REGMAP_ENDIAN_NATIVE, + .reg_defaults = fsl_ssi_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults), + .readable_reg = fsl_ssi_readable_reg, + .volatile_reg = fsl_ssi_volatile_reg, + .writeable_reg = fsl_ssi_writeable_reg, + .cache_type = REGCACHE_RBTREE, }; struct fsl_ssi_soc_data { @@ -176,6 +239,9 @@ struct fsl_ssi_private { unsigned int baudclk_streams; unsigned int bitclk_freq; + /*regcache for SFCSR*/ + u32 regcache_sfcsr; + /* DMA params */ struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; @@ -1514,10 +1580,46 @@ static int fsl_ssi_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int fsl_ssi_suspend(struct device *dev) +{ + struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev); + struct regmap *regs = ssi_private->regs; + + regmap_read(regs, CCSR_SSI_SFCSR, + &ssi_private->regcache_sfcsr); + + regcache_cache_only(regs, true); + regcache_mark_dirty(regs); + + return 0; +} + +static int fsl_ssi_resume(struct device *dev) +{ + struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev); + struct regmap *regs = ssi_private->regs; + + regcache_cache_only(regs, false); + + regmap_update_bits(regs, CCSR_SSI_SFCSR, + CCSR_SSI_SFCSR_RFWM1_MASK | CCSR_SSI_SFCSR_TFWM1_MASK | + CCSR_SSI_SFCSR_RFWM0_MASK | CCSR_SSI_SFCSR_TFWM0_MASK, + ssi_private->regcache_sfcsr); + + return regcache_sync(regs); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_ssi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume) +}; + static struct platform_driver fsl_ssi_driver = { .driver = { .name = "fsl-ssi-dai", .of_match_table = fsl_ssi_ids, + .pm = &fsl_ssi_pm, }, .probe = fsl_ssi_probe, .remove = fsl_ssi_remove, diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index 33da26a..a407e83 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -89,6 +89,7 @@ MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids); static struct platform_driver imx_spdif_driver = { .driver = { .name = "imx-spdif", + .pm = &snd_soc_pm_ops, .of_match_table = imx_spdif_dt_ids, }, .probe = imx_spdif_audio_probe, diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 05fde5e6e..7b778ab 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -12,6 +12,7 @@ config SND_MFLD_MACHINE config SND_SST_MFLD_PLATFORM tristate + select SND_SOC_COMPRESS config SND_SST_IPC tristate @@ -138,4 +139,18 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH config SND_SOC_INTEL_SKYLAKE tristate select SND_HDA_EXT_CORE + select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST + +config SND_SOC_INTEL_SKL_RT286_MACH + tristate "ASoC Audio driver for SKL with RT286 I2S mode" + depends on X86 && ACPI + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT286 + select SND_SOC_DMIC + help + This adds support for ASoC machine driver for Skylake platforms + with RT286 I2S audio codec. + Say Y if you have such a device + If unsure select "N". diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 5e9c316..0487cfa 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -512,7 +512,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { }, { .name = "compress-cpu-dai", - .compress_dai = 1, + .compress_new = snd_soc_new_compress, .ops = &sst_compr_dai_ops, .playback = { .stream_name = "Compress Playback", diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index cb94895..371c456 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -6,6 +6,7 @@ snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o +snd-soc-skl_rt286-objs := skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -15,3 +16,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o +obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c new file mode 100644 index 0000000..a73a431 --- /dev/null +++ b/sound/soc/intel/boards/skl_rt286.c @@ -0,0 +1,259 @@ +/* + * Intel Skylake I2S Machine Driver + * + * Copyright (C) 2014-2015, Intel Corporation. All rights reserved. + * + * Modified from: + * Intel Broadwell Wildcatpoint SST Audio + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> +#include "../../codecs/rt286.h" + +static struct snd_soc_jack skylake_headset; +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin skylake_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new skylake_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget skylake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route skylake_rt286_map[] = { + /* speaker */ + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "SPOL"}, + + /* HP jack connectors - unknown if we have jack deteck */ + {"Headphone Jack", NULL, "HPO Pin"}, + + /* other jacks */ + {"MIC1", NULL, "Mic Jack"}, + + /* digital mics */ + {"DMIC1 Pin", NULL, "DMIC2"}, + {"DMIC AIF", NULL, "SoC DMIC"}, + + /* CODEC BE connections */ + { "AIF1 Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec0_out"}, + { "ssp0 Tx", NULL, "codec1_out"}, + + { "codec0_in", NULL, "ssp0 Rx" }, + { "codec1_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "AIF1 Capture" }, + + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "Capture" }, + + { "hif1", NULL, "iDisp Tx"}, + { "iDisp Tx", NULL, "iDisp_out"}, + +}; + +static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &skylake_headset, + skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins)); + + if (ret) + return ret; + + rt286_mic_detect(codec, &skylake_headset); + + return 0; +} + + +static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The output is 48KHz, stereo, 16bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static int skylake_rt286_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_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); + + return ret; +} + +static struct snd_soc_ops skylake_rt286_ops = { + .hw_params = skylake_rt286_hw_params, +}; + +/* skylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link skylake_rt286_dais[] = { + /* Front End DAI links */ + { + .name = "Skl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "Skl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_capture = 1, + }, + { + .name = "Skl Audio Reference cap", + .stream_name = "refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .be_id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-INT343A:00", + .codec_dai_name = "rt286-aif1", + .init = skylake_rt286_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = skylake_ssp0_fixup, + .ops = &skylake_rt286_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .be_id = 1, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:1f.3", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, +}; + +/* skylake audio machine driver for SPT + RT286S */ +static struct snd_soc_card skylake_rt286 = { + .name = "skylake-rt286", + .owner = THIS_MODULE, + .dai_link = skylake_rt286_dais, + .num_links = ARRAY_SIZE(skylake_rt286_dais), + .controls = skylake_controls, + .num_controls = ARRAY_SIZE(skylake_controls), + .dapm_widgets = skylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(skylake_widgets), + .dapm_routes = skylake_rt286_map, + .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map), + .fully_routed = true, +}; + +static int skylake_audio_probe(struct platform_device *pdev) +{ + skylake_rt286.dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286); +} + +static struct platform_driver skylake_audio = { + .probe = skylake_audio_probe, + .driver = { + .name = "skl_alc286s_i2s", + }, +}; + +module_platform_driver(skylake_audio) + +/* Module information */ +MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>"); +MODULE_DESCRIPTION("Intel SST Audio for Skylake"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:skl_alc286s_i2s"); diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index f24154c..d910558 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,7 +1,11 @@ -snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o +snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o +ifneq ($(CONFIG_DW_DMAC_CORE),) +snd-soc-sst-dsp-objs += sst-firmware.o +endif + obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index cbd568e..2151652 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -314,6 +314,7 @@ struct sst_dsp { int sst_state; struct skl_cl_dev cl_dev; u32 intr_status; + const struct firmware *fw; }; /* Size optimised DRAM/IRAM memcpy */ diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index a627236..c9452e0 100644 --- a/sound/soc/intel/common/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c @@ -420,6 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes) } EXPORT_SYMBOL_GPL(sst_dsp_inbox_read); +#if IS_ENABLED(CONFIG_DW_DMAC_CORE) struct sst_dsp *sst_dsp_new(struct device *dev, struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) { @@ -484,6 +485,7 @@ void sst_dsp_free(struct sst_dsp *sst) sst_dma_free(sst->dma); } EXPORT_SYMBOL_GPL(sst_dsp_free); +#endif /* Module information */ MODULE_AUTHOR("Liam Girdwood"); diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 1f45f18..859f0de 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -216,10 +216,12 @@ struct sst_pdata { void *dsp; }; +#if IS_ENABLED(CONFIG_DW_DMAC_CORE) /* Initialization */ struct sst_dsp *sst_dsp_new(struct device *dev, struct sst_dsp_device *sst_dev, struct sst_pdata *pdata); void sst_dsp_free(struct sst_dsp *sst); +#endif /* SHIM Read / Write */ void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value); diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index ebcca6d..1636a1e 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -26,7 +26,6 @@ #include <linux/acpi.h> /* supported DMA engine drivers */ -#include <linux/platform_data/dma-dw.h> #include <linux/dma/dw.h> #include <asm/page.h> @@ -169,12 +168,6 @@ err: return ret; } -static struct dw_dma_platform_data dw_pdata = { - .is_private = 1, - .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, - .chan_priority = CHAN_PRIORITY_ASCENDING, -}; - static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, int irq) { @@ -195,7 +188,8 @@ static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, return ERR_PTR(err); chip->dev = dev; - err = dw_dma_probe(chip, &dw_pdata); + + err = dw_dma_probe(chip, NULL); if (err) return ERR_PTR(err); diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 27db221..914b6da 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -1,4 +1,5 @@ -snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o +snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \ +skl-topology.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 826d4fd..50a1095 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -54,6 +54,24 @@ static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab) return 0; } +#define NOTIFICATION_PARAM_ID 3 +#define NOTIFICATION_MASK 0xf + +/* disable notfication for underruns/overruns from firmware module */ +static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable) +{ + struct notification_mask mask; + struct skl_ipc_large_config_msg msg = {0}; + + mask.notify = NOTIFICATION_MASK; + mask.enable = enable; + + msg.large_param_id = NOTIFICATION_PARAM_ID; + msg.param_data_size = sizeof(mask); + + skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask); +} + int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; @@ -79,7 +97,10 @@ int skl_init_dsp(struct skl *skl) ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, loader_ops, &skl->skl_sst); + if (ret < 0) + return ret; + skl_dsp_enable_notification(skl->skl_sst, false); dev_dbg(bus->dev, "dsp registration status=%d\n", ret); return ret; @@ -122,6 +143,7 @@ int skl_suspend_dsp(struct skl *skl) int skl_resume_dsp(struct skl *skl) { struct skl_sst *ctx = skl->skl_sst; + int ret; /* if ppcap is not supported return 0 */ if (!skl->ebus.ppcap) @@ -131,7 +153,12 @@ int skl_resume_dsp(struct skl *skl) snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); - return skl_dsp_wake(ctx->dsp); + ret = skl_dsp_wake(ctx->dsp); + if (ret < 0) + return ret; + + skl_dsp_enable_notification(skl->skl_sst, false); + return ret; } enum skl_bitdepth skl_get_bit_depth(int params) @@ -294,6 +321,7 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig, (mconfig->formats_config.caps_size) / 4; } +#define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF /* * Calculate the gatewat settings required for copier module, type of * gateway and index of gateway to use @@ -303,6 +331,7 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, struct skl_cpr_cfg *cpr_mconfig) { union skl_connector_node_id node_id = {0}; + union skl_ssp_dma_node ssp_node = {0}; struct skl_pipe_params *params = mconfig->pipe->p_params; switch (mconfig->dev_type) { @@ -320,9 +349,9 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_I2S_LINK_OUTPUT_CLASS : SKL_DMA_I2S_LINK_INPUT_CLASS; - node_id.node.vindex = params->host_dma_id + - (mconfig->time_slot << 1) + - (mconfig->vbus_id << 3); + ssp_node.dma_node.time_slot_index = mconfig->time_slot; + ssp_node.dma_node.i2s_instance = mconfig->vbus_id; + node_id.node.vindex = ssp_node.val; break; case SKL_DEVICE_DMIC: @@ -339,13 +368,18 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, node_id.node.vindex = params->link_dma_id; break; - default: + case SKL_DEVICE_HDAHOST: node_id.node.dma_type = (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_HDA_HOST_OUTPUT_CLASS : SKL_DMA_HDA_HOST_INPUT_CLASS; node_id.node.vindex = params->host_dma_id; break; + + default: + cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID; + cpr_mconfig->cpr_feature_mask = 0; + return; } cpr_mconfig->gtw_cfg.node_id = node_id.val; diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 13036b1..b0c7bd1 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -25,7 +25,7 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45, #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS" -void __iomem *skl_nhlt_init(struct device *dev) +void *skl_nhlt_init(struct device *dev) { acpi_handle handle; union acpi_object *obj; @@ -40,17 +40,17 @@ void __iomem *skl_nhlt_init(struct device *dev) if (obj && obj->type == ACPI_TYPE_BUFFER) { nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; - return ioremap_cache(nhlt_ptr->min_addr, nhlt_ptr->length); + return memremap(nhlt_ptr->min_addr, nhlt_ptr->length, + MEMREMAP_WB); } dev_err(dev, "device specific method to extract NHLT blob failed\n"); return NULL; } -void skl_nhlt_free(void __iomem *addr) +void skl_nhlt_free(void *addr) { - iounmap(addr); - addr = NULL; + memunmap(addr); } static struct nhlt_specific_cfg *skl_get_specific_cfg( diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index bea2673..a2f94ce 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -24,6 +24,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include "skl.h" +#include "skl-topology.h" #define HDA_MONO 1 #define HDA_STEREO 2 @@ -115,7 +116,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); ret = pm_runtime_get_sync(dai->dev); - if (ret) + if (ret < 0) return ret; stream = snd_hdac_ext_stream_assign(ebus, substream, @@ -214,6 +215,8 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; + struct skl_pipe_params p_params = {0}; + struct skl_module_cfg *m_cfg; int ret, dma_id; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); @@ -228,6 +231,16 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, dma_id = hdac_stream(stream)->stream_tag - 1; dev_dbg(dai->dev, "dma_id=%d\n", dma_id); + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.host_dma_id = dma_id; + p_params.stream = substream->stream; + + m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream); + if (m_cfg) + skl_tplg_update_pipe_params(dai->dev, m_cfg, &p_params); + return 0; } @@ -268,6 +281,46 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, return skl_substream_free_pages(ebus_to_hbus(ebus), substream); } +static int skl_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct skl_pipe_params p_params = {0}; + + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.stream = substream->stream; + skl_tplg_be_update_params(dai, &p_params); + + return 0; +} + +static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_sst *ctx = skl->skl_sst; + struct skl_module_cfg *mconfig; + + mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + if (!mconfig) + return -EIO; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + return skl_run_pipe(ctx, mconfig->pipe); + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + return skl_stop_pipe(ctx, mconfig->pipe); + + default: + return 0; + } +} + static int skl_link_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -277,9 +330,8 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct skl_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; - int dma_id; + struct skl_pipe_params p_params = {0}; - pr_debug("%s\n", __func__); link_dev = snd_hdac_ext_stream_assign(ebus, substream, HDAC_EXT_STREAM_TYPE_LINK); if (!link_dev) @@ -293,7 +345,14 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, if (dma_params) dma_params->stream_tag = hdac_stream(link_dev)->stream_tag; snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params); - dma_id = hdac_stream(link_dev)->stream_tag - 1; + + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.stream = substream->stream; + p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1; + + skl_tplg_be_update_params(dai, &p_params); return 0; } @@ -308,27 +367,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, unsigned int format_val = 0; struct skl_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_pcm_hw_params *params; - struct snd_interval *channels, *rate; struct hdac_ext_link *link; - dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); if (link_dev->link_prepared) { dev_dbg(dai->dev, "already stream is prepared - returning\n"); return 0; } - params = devm_kzalloc(dai->dev, sizeof(*params), GFP_KERNEL); - if (params == NULL) - return -ENOMEM; - - channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = substream->runtime->channels; - rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - rate->min = rate->max = substream->runtime->rate; - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - substream->runtime->format); - dma_params = (struct skl_dma_params *) snd_soc_dai_get_dma_data(codec_dai, substream); @@ -399,13 +443,13 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream, return 0; } -static int skl_hda_be_startup(struct snd_pcm_substream *substream, +static int skl_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { return pm_runtime_get_sync(dai->dev); } -static void skl_hda_be_shutdown(struct snd_pcm_substream *substream, +static void skl_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { pm_runtime_mark_last_busy(dai->dev); @@ -418,20 +462,28 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = { .prepare = skl_pcm_prepare, .hw_params = skl_pcm_hw_params, .hw_free = skl_pcm_hw_free, + .trigger = skl_pcm_trigger, }; static struct snd_soc_dai_ops skl_dmic_dai_ops = { - .startup = skl_hda_be_startup, - .shutdown = skl_hda_be_shutdown, + .startup = skl_be_startup, + .hw_params = skl_be_hw_params, + .shutdown = skl_be_shutdown, +}; + +static struct snd_soc_dai_ops skl_be_ssp_dai_ops = { + .startup = skl_be_startup, + .hw_params = skl_be_hw_params, + .shutdown = skl_be_shutdown, }; static struct snd_soc_dai_ops skl_link_dai_ops = { - .startup = skl_hda_be_startup, + .startup = skl_be_startup, .prepare = skl_link_pcm_prepare, .hw_params = skl_link_hw_params, .hw_free = skl_link_hw_free, .trigger = skl_link_pcm_trigger, - .shutdown = skl_hda_be_shutdown, + .shutdown = skl_be_shutdown, }; static struct snd_soc_dai_driver skl_platform_dai[] = { @@ -488,6 +540,24 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, /* BE CPU Dais */ { + .name = "SSP0 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp0 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp0 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ .name = "iDisp Pin", .ops = &skl_link_dai_ops, .playback = { @@ -544,7 +614,7 @@ static int skl_platform_open(struct snd_pcm_substream *substream) return 0; } -static int skl_pcm_trigger(struct snd_pcm_substream *substream, +static int skl_coupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); @@ -618,7 +688,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, return 0; } -static int skl_dsp_trigger(struct snd_pcm_substream *substream, +static int skl_decoupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); @@ -675,9 +745,9 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = get_bus_ctx(substream); if (ebus->ppcap) - return skl_dsp_trigger(substream, cmd); + return skl_decoupled_trigger(substream, cmd); else - return skl_pcm_trigger(substream, cmd); + return skl_coupled_trigger(substream, cmd); } /* calculate runtime delay from LPIB */ @@ -844,7 +914,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) return retval; } +static int skl_platform_soc_probe(struct snd_soc_platform *platform) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev); + + if (ebus->ppcap) + return skl_tplg_init(platform, ebus); + + return 0; +} static struct snd_soc_platform_driver skl_platform_drv = { + .probe = skl_platform_soc_probe, .ops = &skl_platform_ops, .pcm_new = skl_pcm_new, .pcm_free = skl_pcm_free, @@ -857,6 +937,11 @@ static const struct snd_soc_component_driver skl_component = { int skl_platform_register(struct device *dev) { int ret; + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct skl *skl = ebus_to_skl(ebus); + + INIT_LIST_HEAD(&skl->ppl_list); + INIT_LIST_HEAD(&skl->dapm_path_list); ret = snd_soc_register_platform(dev, &skl_platform_drv); if (ret) { diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 94875b0..1bfb7f6 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -175,7 +175,7 @@ static int skl_dsp_core_power_down(struct sst_dsp *ctx) /* poll with timeout to check if operation successful */ return sst_dsp_register_poll(ctx, SKL_ADSP_REG_ADSPCS, - SKL_ADSPCS_SPA_MASK, + SKL_ADSPCS_CPA_MASK, 0, SKL_DSP_PD_TO, "Power down"); @@ -262,6 +262,11 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id) val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS); ctx->intr_status = val; + if (val == 0xffffffff) { + spin_unlock(&ctx->spinlock); + return IRQ_NONE; + } + if (val & SKL_ADSPIS_IPC) { skl_ipc_int_disable(ctx); result = IRQ_WAKE_THREAD; diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 937a0a3..3345ea0 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -464,6 +464,18 @@ void skl_ipc_op_int_enable(struct sst_dsp *ctx) SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY); } +void skl_ipc_op_int_disable(struct sst_dsp *ctx) +{ + /* disable IPC DONE interrupt */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_DONE, 0); + + /* Disable IPC BUSY interrupt */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_BUSY, 0); + +} + bool skl_ipc_int_status(struct sst_dsp *ctx) { return sst_dsp_shim_read_unlocked(ctx, diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 9f5f672..f1a154e 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -116,6 +116,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); +void skl_ipc_op_int_disable(struct sst_dsp *ctx); void skl_ipc_int_disable(struct sst_dsp *dsp); bool skl_ipc_int_status(struct sst_dsp *dsp); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index c18ea51..3b83dc9 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -70,15 +70,31 @@ static int skl_transfer_firmware(struct sst_dsp *ctx, static int skl_load_base_firmware(struct sst_dsp *ctx) { int ret = 0, i; - const struct firmware *fw = NULL; struct skl_sst *skl = ctx->thread_context; u32 reg; - ret = request_firmware(&fw, "dsp_fw_release.bin", ctx->dev); + skl->boot_complete = false; + init_waitqueue_head(&skl->boot_wait); + + if (ctx->fw == NULL) { + ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev); + if (ret < 0) { + dev_err(ctx->dev, "Request firmware failed %d\n", ret); + skl_dsp_disable_core(ctx); + return -EIO; + } + } + + ret = skl_dsp_boot(ctx); if (ret < 0) { - dev_err(ctx->dev, "Request firmware failed %d\n", ret); - skl_dsp_disable_core(ctx); - return -EIO; + dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret); + goto skl_load_base_firmware_failed; + } + + ret = skl_cldma_prepare(ctx); + if (ret < 0) { + dev_err(ctx->dev, "CL dma prepare failed : %d", ret); + goto skl_load_base_firmware_failed; } /* enable Interrupt */ @@ -102,7 +118,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) goto skl_load_base_firmware_failed; } - ret = skl_transfer_firmware(ctx, fw->data, fw->size); + ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size); if (ret < 0) { dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); goto skl_load_base_firmware_failed; @@ -118,13 +134,12 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); } - release_firmware(fw); - return 0; skl_load_base_firmware_failed: skl_dsp_disable_core(ctx); - release_firmware(fw); + release_firmware(ctx->fw); + ctx->fw = NULL; return ret; } @@ -172,6 +187,12 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx) } skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); + /* disable Interrupt */ + ctx->cl_dev.ops.cl_cleanup_controller(ctx); + skl_cldma_int_disable(ctx); + skl_ipc_op_int_disable(ctx); + skl_ipc_int_disable(ctx); + return ret; } @@ -235,22 +256,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, if (ret) return ret; - skl->boot_complete = false; - init_waitqueue_head(&skl->boot_wait); - - ret = skl_dsp_boot(sst); - if (ret < 0) { - dev_err(skl->dev, "Boot dsp core failed ret: %d", ret); - goto free_ipc; - } - - ret = skl_cldma_prepare(sst); - if (ret < 0) { - dev_err(dev, "CL dma prepare failed : %d", ret); - goto free_ipc; - } - - ret = sst->fw_ops.load_fw(sst); if (ret < 0) { dev_err(dev, "Load base fw failed : %d", ret); @@ -262,7 +267,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return 0; -free_ipc: skl_ipc_free(&skl->ipc); return ret; } diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c new file mode 100644 index 0000000..a7854c8 --- /dev/null +++ b/sound/soc/intel/skylake/skl-topology.c @@ -0,0 +1,1252 @@ +/* + * skl-topology.c - Implements Platform component ALSA controls/widget + * handlers. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Jeeja KP <jeeja.kp@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/firmware.h> +#include <sound/soc.h> +#include <sound/soc-topology.h> +#include "skl-sst-dsp.h" +#include "skl-sst-ipc.h" +#include "skl-topology.h" +#include "skl.h" +#include "skl-tplg-interface.h" + +#define SKL_CH_FIXUP_MASK (1 << 0) +#define SKL_RATE_FIXUP_MASK (1 << 1) +#define SKL_FMT_FIXUP_MASK (1 << 2) + +/* + * SKL DSP driver modelling uses only few DAPM widgets so for rest we will + * ignore. This helpers checks if the SKL driver handles this widget type + */ +static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w) +{ + switch (w->id) { + case snd_soc_dapm_dai_link: + case snd_soc_dapm_dai_in: + case snd_soc_dapm_aif_in: + case snd_soc_dapm_aif_out: + case snd_soc_dapm_dai_out: + case snd_soc_dapm_switch: + return false; + default: + return true; + } +} + +/* + * Each pipelines needs memory to be allocated. Check if we have free memory + * from available pool. Then only add this to pool + * This is freed when pipe is deleted + * Note: DSP does actual memory management we only keep track for complete + * pool + */ +static bool skl_tplg_alloc_pipe_mem(struct skl *skl, + struct skl_module_cfg *mconfig) +{ + struct skl_sst *ctx = skl->skl_sst; + + if (skl->resource.mem + mconfig->pipe->memory_pages > + skl->resource.max_mem) { + dev_err(ctx->dev, + "%s: module_id %d instance %d\n", __func__, + mconfig->id.module_id, + mconfig->id.instance_id); + dev_err(ctx->dev, + "exceeds ppl memory available %d mem %d\n", + skl->resource.max_mem, skl->resource.mem); + return false; + } + + skl->resource.mem += mconfig->pipe->memory_pages; + return true; +} + +/* + * Pipeline needs needs DSP CPU resources for computation, this is + * quantified in MCPS (Million Clocks Per Second) required for module/pipe + * + * Each pipelines needs mcps to be allocated. Check if we have mcps for this + * pipe. This adds the mcps to driver counter + * This is removed on pipeline delete + */ +static bool skl_tplg_alloc_pipe_mcps(struct skl *skl, + struct skl_module_cfg *mconfig) +{ + struct skl_sst *ctx = skl->skl_sst; + + if (skl->resource.mcps + mconfig->mcps > skl->resource.max_mcps) { + dev_err(ctx->dev, + "%s: module_id %d instance %d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + dev_err(ctx->dev, + "exceeds ppl memory available %d > mem %d\n", + skl->resource.max_mcps, skl->resource.mcps); + return false; + } + + skl->resource.mcps += mconfig->mcps; + return true; +} + +/* + * Free the mcps when tearing down + */ +static void +skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig) +{ + skl->resource.mcps -= mconfig->mcps; +} + +/* + * Free the memory when tearing down + */ +static void +skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig) +{ + skl->resource.mem -= mconfig->pipe->memory_pages; +} + + +static void skl_dump_mconfig(struct skl_sst *ctx, + struct skl_module_cfg *mcfg) +{ + dev_dbg(ctx->dev, "Dumping config\n"); + dev_dbg(ctx->dev, "Input Format:\n"); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg); + dev_dbg(ctx->dev, "valid bit depth = %d\n", + mcfg->in_fmt.valid_bit_depth); + dev_dbg(ctx->dev, "Output Format:\n"); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq); + dev_dbg(ctx->dev, "valid bit depth = %d\n", + mcfg->out_fmt.valid_bit_depth); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg); +} + +static void skl_tplg_update_params(struct skl_module_fmt *fmt, + struct skl_pipe_params *params, int fixup) +{ + if (fixup & SKL_RATE_FIXUP_MASK) + fmt->s_freq = params->s_freq; + if (fixup & SKL_CH_FIXUP_MASK) + fmt->channels = params->ch; + if (fixup & SKL_FMT_FIXUP_MASK) + fmt->valid_bit_depth = params->s_fmt; +} + +/* + * A pipeline may have modules which impact the pcm parameters, like SRC, + * channel converter, format converter. + * We need to calculate the output params by applying the 'fixup' + * Topology will tell driver which type of fixup is to be applied by + * supplying the fixup mask, so based on that we calculate the output + * + * Now In FE the pcm hw_params is source/target format. Same is applicable + * for BE with its hw_params invoked. + * here based on FE, BE pipeline and direction we calculate the input and + * outfix and then apply that for a module + */ +static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg, + struct skl_pipe_params *params, bool is_fe) +{ + int in_fixup, out_fixup; + struct skl_module_fmt *in_fmt, *out_fmt; + + in_fmt = &m_cfg->in_fmt; + out_fmt = &m_cfg->out_fmt; + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (is_fe) { + in_fixup = m_cfg->params_fixup; + out_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } else { + out_fixup = m_cfg->params_fixup; + in_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } + } else { + if (is_fe) { + out_fixup = m_cfg->params_fixup; + in_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } else { + in_fixup = m_cfg->params_fixup; + out_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } + } + + skl_tplg_update_params(in_fmt, params, in_fixup); + skl_tplg_update_params(out_fmt, params, out_fixup); +} + +/* + * A module needs input and output buffers, which are dependent upon pcm + * params, so once we have calculate params, we need buffer calculation as + * well. + */ +static void skl_tplg_update_buffer_size(struct skl_sst *ctx, + struct skl_module_cfg *mcfg) +{ + int multiplier = 1; + + if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT) + multiplier = 5; + + mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) * + (mcfg->in_fmt.channels) * + (mcfg->in_fmt.bit_depth >> 3) * + multiplier; + + mcfg->obs = (mcfg->out_fmt.s_freq / 1000) * + (mcfg->out_fmt.channels) * + (mcfg->out_fmt.bit_depth >> 3) * + multiplier; +} + +static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx) +{ + struct skl_module_cfg *m_cfg = w->priv; + struct skl_pipe_params *params = m_cfg->pipe->p_params; + int p_conn_type = m_cfg->pipe->conn_type; + bool is_fe; + + if (!m_cfg->params_fixup) + return; + + dev_dbg(ctx->dev, "Mconfig for widget=%s BEFORE updation\n", + w->name); + + skl_dump_mconfig(ctx, m_cfg); + + if (p_conn_type == SKL_PIPE_CONN_TYPE_FE) + is_fe = true; + else + is_fe = false; + + skl_tplg_update_params_fixup(m_cfg, params, is_fe); + skl_tplg_update_buffer_size(ctx, m_cfg); + + dev_dbg(ctx->dev, "Mconfig for widget=%s AFTER updation\n", + w->name); + + skl_dump_mconfig(ctx, m_cfg); +} + +/* + * A pipe can have multiple modules, each of them will be a DAPM widget as + * well. While managing a pipeline we need to get the list of all the + * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps + * to get the SKL type widgets in that pipeline + */ +static int skl_tplg_alloc_pipe_widget(struct device *dev, + struct snd_soc_dapm_widget *w, struct skl_pipe *pipe) +{ + struct skl_module_cfg *src_module = NULL; + struct snd_soc_dapm_path *p = NULL; + struct skl_pipe_module *p_module = NULL; + + p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL); + if (!p_module) + return -ENOMEM; + + p_module->w = w; + list_add_tail(&p_module->node, &pipe->w_list); + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if ((p->sink->priv == NULL) + && (!is_skl_dsp_widget_type(w))) + continue; + + if ((p->sink->priv != NULL) && p->connect + && is_skl_dsp_widget_type(p->sink)) { + + src_module = p->sink->priv; + if (pipe->ppl_id == src_module->pipe->ppl_id) + skl_tplg_alloc_pipe_widget(dev, + p->sink, pipe); + } + } + return 0; +} + +/* + * Inside a pipe instance, we can have various modules. These modules need + * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by + * skl_init_module() routine, so invoke that for all modules in a pipeline + */ +static int +skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) +{ + struct skl_pipe_module *w_module; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + struct skl_sst *ctx = skl->skl_sst; + int ret = 0; + + list_for_each_entry(w_module, &pipe->w_list, node) { + w = w_module->w; + mconfig = w->priv; + + /* check resource available */ + if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + return -ENOMEM; + + /* + * apply fix/conversion to module params based on + * FE/BE params + */ + skl_tplg_update_module_params(w, ctx); + ret = skl_init_module(ctx, mconfig, NULL); + if (ret < 0) + return ret; + } + + return 0; +} + +/* + * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we + * need create the pipeline. So we do following: + * - check the resources + * - Create the pipeline + * - Initialize the modules in pipeline + * - finally bind all modules together + */ +static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + int ret; + struct skl_module_cfg *mconfig = w->priv; + struct skl_pipe_module *w_module; + struct skl_pipe *s_pipe = mconfig->pipe; + struct skl_module_cfg *src_module = NULL, *dst_module; + struct skl_sst *ctx = skl->skl_sst; + + /* check resource available */ + if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + return -EBUSY; + + if (!skl_tplg_alloc_pipe_mem(skl, mconfig)) + return -ENOMEM; + + /* + * Create a list of modules for pipe. + * This list contains modules from source to sink + */ + ret = skl_create_pipeline(ctx, mconfig->pipe); + if (ret < 0) + return ret; + + /* + * we create a w_list of all widgets in that pipe. This list is not + * freed on PMD event as widgets within a pipe are static. This + * saves us cycles to get widgets in pipe every time. + * + * So if we have already initialized all the widgets of a pipeline + * we skip, so check for list_empty and create the list if empty + */ + if (list_empty(&s_pipe->w_list)) { + ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe); + if (ret < 0) + return ret; + } + + /* Init all pipe modules from source to sink */ + ret = skl_tplg_init_pipe_modules(skl, s_pipe); + if (ret < 0) + return ret; + + /* Bind modules from source to sink */ + list_for_each_entry(w_module, &s_pipe->w_list, node) { + dst_module = w_module->w->priv; + + if (src_module == NULL) { + src_module = dst_module; + continue; + } + + ret = skl_bind_modules(ctx, src_module, dst_module); + if (ret < 0) + return ret; + + src_module = dst_module; + } + + return 0; +} + +/* + * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA + * we need to do following: + * - Bind to sink pipeline + * Since the sink pipes can be running and we don't get mixer event on + * connect for already running mixer, we need to find the sink pipes + * here and bind to them. This way dynamic connect works. + * - Start sink pipeline, if not running + * - Then run current pipe + */ +static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_path *p; + struct skl_dapm_path_list *path_list; + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + struct skl_sst *ctx = skl->skl_sst; + int ret = 0; + + source = w; + src_mconfig = source->priv; + + /* + * find which sink it is connected to, bind with the sink, + * if sink is not started, start sink pipe first, then start + * this pipe + */ + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (!p->connect) + continue; + + dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name); + dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name); + + /* + * here we will check widgets in sink pipelines, so that + * can be any widgets type and we are only interested if + * they are ones used for SKL so check that first + */ + if ((p->sink->priv != NULL) && + is_skl_dsp_widget_type(p->sink)) { + + sink = p->sink; + src_mconfig = source->priv; + sink_mconfig = sink->priv; + + /* Bind source to sink, mixin is always source */ + ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig); + if (ret) + return ret; + + /* Start sinks pipe first */ + if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) { + ret = skl_run_pipe(ctx, sink_mconfig->pipe); + if (ret) + return ret; + } + + path_list = kzalloc( + sizeof(struct skl_dapm_path_list), + GFP_KERNEL); + if (path_list == NULL) + return -ENOMEM; + + /* Add connected path to one global list */ + path_list->dapm_path = p; + list_add_tail(&path_list->node, &skl->dapm_path_list); + break; + } + } + + /* Start source pipe last after starting all sinks */ + ret = skl_run_pipe(ctx, src_mconfig->pipe); + if (ret) + return ret; + + return 0; +} + +/* + * in the Post-PMU event of mixer we need to do following: + * - Check if this pipe is running + * - if not, then + * - bind this pipeline to its source pipeline + * if source pipe is already running, this means it is a dynamic + * connection and we need to bind only to that pipe + * - start this pipeline + */ +static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + int ret = 0; + struct snd_soc_dapm_path *p; + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + struct skl_sst *ctx = skl->skl_sst; + int src_pipe_started = 0; + + sink = w; + sink_mconfig = sink->priv; + + /* + * If source pipe is already started, that means source is driving + * one more sink before this sink got connected, Since source is + * started, bind this sink to source and start this pipe. + */ + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->connect) + continue; + + dev_dbg(ctx->dev, "sink widget=%s\n", w->name); + dev_dbg(ctx->dev, "src widget=%s\n", p->source->name); + + /* + * here we will check widgets in sink pipelines, so that + * can be any widgets type and we are only interested if + * they are ones used for SKL so check that first + */ + if ((p->source->priv != NULL) && + is_skl_dsp_widget_type(p->source)) { + source = p->source; + src_mconfig = source->priv; + sink_mconfig = sink->priv; + src_pipe_started = 1; + + /* + * check pipe state, then no need to bind or start + * the pipe + */ + if (src_mconfig->pipe->state != SKL_PIPE_STARTED) + src_pipe_started = 0; + } + } + + if (src_pipe_started) { + ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig); + if (ret) + return ret; + + ret = skl_run_pipe(ctx, sink_mconfig->pipe); + } + + return ret; +} + +/* + * in the Pre-PMD event of mixer we need to do following: + * - Stop the pipe + * - find the source connections and remove that from dapm_path_list + * - unbind with source pipelines if still connected + */ +static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + int ret = 0, path_found = 0; + struct skl_dapm_path_list *path_list, *tmp_list; + struct skl_sst *ctx = skl->skl_sst; + + sink = w; + sink_mconfig = sink->priv; + + /* Stop the pipe */ + ret = skl_stop_pipe(ctx, sink_mconfig->pipe); + if (ret) + return ret; + + /* + * This list, dapm_path_list handling here does not need any locks + * as we are under dapm lock while handling widget events. + * List can be manipulated safely only under dapm widgets handler + * routines + */ + list_for_each_entry_safe(path_list, tmp_list, + &skl->dapm_path_list, node) { + if (path_list->dapm_path->sink == sink) { + dev_dbg(ctx->dev, "Path found = %s\n", + path_list->dapm_path->name); + source = path_list->dapm_path->source; + src_mconfig = source->priv; + path_found = 1; + + list_del(&path_list->node); + kfree(path_list); + break; + } + } + + /* + * If path_found == 1, that means pmd for source pipe has + * not occurred, source is connected to some other sink. + * so its responsibility of sink to unbind itself from source. + */ + if (path_found) { + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret < 0) + return ret; + + ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); + } + + return ret; +} + +/* + * in the Post-PMD event of mixer we need to do following: + * - Free the mcps used + * - Free the mem used + * - Unbind the modules within the pipeline + * - Delete the pipeline (modules are not required to be explicitly + * deleted, pipeline delete is enough here + */ +static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct skl_module_cfg *mconfig = w->priv; + struct skl_pipe_module *w_module; + struct skl_module_cfg *src_module = NULL, *dst_module; + struct skl_sst *ctx = skl->skl_sst; + struct skl_pipe *s_pipe = mconfig->pipe; + int ret = 0; + + skl_tplg_free_pipe_mcps(skl, mconfig); + + list_for_each_entry(w_module, &s_pipe->w_list, node) { + dst_module = w_module->w->priv; + + if (src_module == NULL) { + src_module = dst_module; + continue; + } + + ret = skl_unbind_modules(ctx, src_module, dst_module); + if (ret < 0) + return ret; + + src_module = dst_module; + } + + ret = skl_delete_pipe(ctx, mconfig->pipe); + skl_tplg_free_pipe_mem(skl, mconfig); + + return ret; +} + +/* + * in the Post-PMD event of PGA we need to do following: + * - Free the mcps used + * - Stop the pipeline + * - In source pipe is connected, unbind with source pipelines + */ +static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + int ret = 0, path_found = 0; + struct skl_dapm_path_list *path_list, *tmp_path_list; + struct skl_sst *ctx = skl->skl_sst; + + source = w; + src_mconfig = source->priv; + + skl_tplg_free_pipe_mcps(skl, src_mconfig); + /* Stop the pipe since this is a mixin module */ + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret) + return ret; + + list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) { + if (path_list->dapm_path->source == source) { + dev_dbg(ctx->dev, "Path found = %s\n", + path_list->dapm_path->name); + sink = path_list->dapm_path->sink; + sink_mconfig = sink->priv; + path_found = 1; + + list_del(&path_list->node); + kfree(path_list); + break; + } + } + + /* + * This is a connector and if path is found that means + * unbind between source and sink has not happened yet + */ + if (path_found) { + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret < 0) + return ret; + + ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); + } + + return ret; +} + +/* + * In modelling, we assume there will be ONLY one mixer in a pipeline. If + * mixer is not required then it is treated as static mixer aka vmixer with + * a hard path to source module + * So we don't need to check if source is started or not as hard path puts + * dependency on each other + */ +static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_mixer_dapm_post_pmd_event(w, skl); + } + + return 0; +} + +/* + * In modelling, we assume there will be ONLY one mixer in a pipeline. If a + * second one is required that is created as another pipe entity. + * The mixer is responsible for pipe management and represent a pipeline + * instance + */ +static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMU: + return skl_tplg_mixer_dapm_post_pmu_event(w, skl); + + case SND_SOC_DAPM_PRE_PMD: + return skl_tplg_mixer_dapm_pre_pmd_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_mixer_dapm_post_pmd_event(w, skl); + } + + return 0; +} + +/* + * In modelling, we assumed rest of the modules in pipeline are PGA. But we + * are interested in last PGA (leaf PGA) in a pipeline to disconnect with + * the sink when it is running (two FE to one BE or one FE to two BE) + * scenarios + */ +static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) + +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_pga_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_pga_dapm_post_pmd_event(w, skl); + } + + return 0; +} + +/* + * The FE params are passed by hw_params of the DAI. + * On hw_params, the params are stored in Gateway module of the FE and we + * need to calculate the format in DSP module configuration, that + * conversion is done here + */ +int skl_tplg_update_pipe_params(struct device *dev, + struct skl_module_cfg *mconfig, + struct skl_pipe_params *params) +{ + struct skl_pipe *pipe = mconfig->pipe; + struct skl_module_fmt *format = NULL; + + memcpy(pipe->p_params, params, sizeof(*params)); + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) + format = &mconfig->in_fmt; + else + format = &mconfig->out_fmt; + + /* set the hw_params */ + format->s_freq = params->s_freq; + format->channels = params->ch; + format->valid_bit_depth = skl_get_bit_depth(params->s_fmt); + + /* + * 16 bit is 16 bit container whereas 24 bit is in 32 bit + * container so update bit depth accordingly + */ + switch (format->valid_bit_depth) { + case SKL_DEPTH_16BIT: + format->bit_depth = format->valid_bit_depth; + break; + + case SKL_DEPTH_24BIT: + format->bit_depth = SKL_DEPTH_32BIT; + break; + + default: + dev_err(dev, "Invalid bit depth %x for pipe\n", + format->valid_bit_depth); + return -EINVAL; + } + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mconfig->ibs = (format->s_freq / 1000) * + (format->channels) * + (format->bit_depth >> 3); + } else { + mconfig->obs = (format->s_freq / 1000) * + (format->channels) * + (format->bit_depth >> 3); + } + + return 0; +} + +/* + * Query the module config for the FE DAI + * This is used to find the hw_params set for that DAI and apply to FE + * pipeline + */ +struct skl_module_cfg * +skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_path *p = NULL; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + w = dai->playback_widget; + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (p->connect && p->sink->power && + is_skl_dsp_widget_type(p->sink)) + continue; + + if (p->sink->priv) { + dev_dbg(dai->dev, "set params for %s\n", + p->sink->name); + return p->sink->priv; + } + } + } else { + w = dai->capture_widget; + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (p->connect && p->source->power && + is_skl_dsp_widget_type(p->source)) + continue; + + if (p->source->priv) { + dev_dbg(dai->dev, "set params for %s\n", + p->source->name); + return p->source->priv; + } + } + } + + return NULL; +} + +static u8 skl_tplg_be_link_type(int dev_type) +{ + int ret; + + switch (dev_type) { + case SKL_DEVICE_BT: + ret = NHLT_LINK_SSP; + break; + + case SKL_DEVICE_DMIC: + ret = NHLT_LINK_DMIC; + break; + + case SKL_DEVICE_I2S: + ret = NHLT_LINK_SSP; + break; + + case SKL_DEVICE_HDALINK: + ret = NHLT_LINK_HDA; + break; + + default: + ret = NHLT_LINK_INVALID; + break; + } + + return ret; +} + +/* + * Fill the BE gateway parameters + * The BE gateway expects a blob of parameters which are kept in the ACPI + * NHLT blob, so query the blob for interface type (i2s/pdm) and instance. + * The port can have multiple settings so pick based on the PCM + * parameters + */ +static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, + struct skl_module_cfg *mconfig, + struct skl_pipe_params *params) +{ + struct skl_pipe *pipe = mconfig->pipe; + struct nhlt_specific_cfg *cfg; + struct skl *skl = get_skl_ctx(dai->dev); + int link_type = skl_tplg_be_link_type(mconfig->dev_type); + + memcpy(pipe->p_params, params, sizeof(*params)); + + /* update the blob based on virtual bus_id*/ + cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, + params->s_fmt, params->ch, + params->s_freq, params->stream); + if (cfg) { + mconfig->formats_config.caps_size = cfg->size; + mconfig->formats_config.caps = (u32 *) &cfg->caps; + } else { + dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n", + mconfig->vbus_id, link_type, + params->stream); + dev_err(dai->dev, "PCM: ch %d, freq %d, fmt %d\n", + params->ch, params->s_freq, params->s_fmt); + return -EINVAL; + } + + return 0; +} + +static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, + struct snd_soc_dapm_widget *w, + struct skl_pipe_params *params) +{ + struct snd_soc_dapm_path *p; + int ret = -EIO; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (p->connect && is_skl_dsp_widget_type(p->source) && + p->source->priv) { + + if (!p->source->power) { + ret = skl_tplg_be_fill_pipe_params( + dai, p->source->priv, + params); + if (ret < 0) + return ret; + } else { + return -EBUSY; + } + } else { + ret = skl_tplg_be_set_src_pipe_params( + dai, p->source, params); + if (ret < 0) + return ret; + } + } + + return ret; +} + +static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, + struct snd_soc_dapm_widget *w, struct skl_pipe_params *params) +{ + struct snd_soc_dapm_path *p = NULL; + int ret = -EIO; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (p->connect && is_skl_dsp_widget_type(p->sink) && + p->sink->priv) { + + if (!p->sink->power) { + ret = skl_tplg_be_fill_pipe_params( + dai, p->sink->priv, params); + if (ret < 0) + return ret; + } else { + return -EBUSY; + } + + } else { + ret = skl_tplg_be_set_sink_pipe_params( + dai, p->sink, params); + if (ret < 0) + return ret; + } + } + + return ret; +} + +/* + * BE hw_params can be a source parameters (capture) or sink parameters + * (playback). Based on sink and source we need to either find the source + * list or the sink list and set the pipeline parameters + */ +int skl_tplg_be_update_params(struct snd_soc_dai *dai, + struct skl_pipe_params *params) +{ + struct snd_soc_dapm_widget *w; + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + w = dai->playback_widget; + + return skl_tplg_be_set_src_pipe_params(dai, w, params); + + } else { + w = dai->capture_widget; + + return skl_tplg_be_set_sink_pipe_params(dai, w, params); + } + + return 0; +} + +static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { + {SKL_MIXER_EVENT, skl_tplg_mixer_event}, + {SKL_VMIXER_EVENT, skl_tplg_vmixer_event}, + {SKL_PGA_EVENT, skl_tplg_pga_event}, +}; + +/* + * The topology binary passes the pin info for a module so initialize the pin + * info passed into module instance + */ +static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin, + struct skl_module_pin *m_pin, + bool is_dynamic, int max_pin) +{ + int i; + + for (i = 0; i < max_pin; i++) { + m_pin[i].id.module_id = dfw_pin[i].module_id; + m_pin[i].id.instance_id = dfw_pin[i].instance_id; + m_pin[i].in_use = false; + m_pin[i].is_dynamic = is_dynamic; + } +} + +/* + * Add pipeline from topology binary into driver pipeline list + * + * If already added we return that instance + * Otherwise we create a new instance and add into driver list + */ +static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, + struct skl *skl, struct skl_dfw_pipe *dfw_pipe) +{ + struct skl_pipeline *ppl; + struct skl_pipe *pipe; + struct skl_pipe_params *params; + + list_for_each_entry(ppl, &skl->ppl_list, node) { + if (ppl->pipe->ppl_id == dfw_pipe->pipe_id) + return ppl->pipe; + } + + ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL); + if (!ppl) + return NULL; + + pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL); + if (!pipe) + return NULL; + + params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL); + if (!params) + return NULL; + + pipe->ppl_id = dfw_pipe->pipe_id; + pipe->memory_pages = dfw_pipe->memory_pages; + pipe->pipe_priority = dfw_pipe->pipe_priority; + pipe->conn_type = dfw_pipe->conn_type; + pipe->state = SKL_PIPE_INVALID; + pipe->p_params = params; + INIT_LIST_HEAD(&pipe->w_list); + + ppl->pipe = pipe; + list_add(&ppl->node, &skl->ppl_list); + + return ppl->pipe; +} + +/* + * Topology core widget load callback + * + * This is used to save the private data for each widget which gives + * information to the driver about module and pipeline parameters which DSP + * FW expects like ids, resource values, formats etc + */ +static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) +{ + int ret; + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl_module_cfg *mconfig; + struct skl_pipe *pipe; + struct skl_dfw_module *dfw_config = + (struct skl_dfw_module *)tplg_w->priv.data; + + if (!tplg_w->priv.size) + goto bind_event; + + mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL); + + if (!mconfig) + return -ENOMEM; + + w->priv = mconfig; + mconfig->id.module_id = dfw_config->module_id; + mconfig->id.instance_id = dfw_config->instance_id; + mconfig->mcps = dfw_config->max_mcps; + mconfig->ibs = dfw_config->ibs; + mconfig->obs = dfw_config->obs; + mconfig->core_id = dfw_config->core_id; + mconfig->max_in_queue = dfw_config->max_in_queue; + mconfig->max_out_queue = dfw_config->max_out_queue; + mconfig->is_loadable = dfw_config->is_loadable; + mconfig->in_fmt.channels = dfw_config->in_fmt.channels; + mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq; + mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth; + mconfig->in_fmt.valid_bit_depth = + dfw_config->in_fmt.valid_bit_depth; + mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg; + mconfig->out_fmt.channels = dfw_config->out_fmt.channels; + mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq; + mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth; + mconfig->out_fmt.valid_bit_depth = + dfw_config->out_fmt.valid_bit_depth; + mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg; + mconfig->params_fixup = dfw_config->params_fixup; + mconfig->converter = dfw_config->converter; + mconfig->m_type = dfw_config->module_type; + mconfig->vbus_id = dfw_config->vbus_id; + + pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe); + if (pipe) + mconfig->pipe = pipe; + + mconfig->dev_type = dfw_config->dev_type; + mconfig->hw_conn_type = dfw_config->hw_conn_type; + mconfig->time_slot = dfw_config->time_slot; + mconfig->formats_config.caps_size = dfw_config->caps.caps_size; + + mconfig->m_in_pin = devm_kzalloc(bus->dev, + (mconfig->max_in_queue) * + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); + if (!mconfig->m_in_pin) + return -ENOMEM; + + mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); + if (!mconfig->m_out_pin) + return -ENOMEM; + + skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin, + dfw_config->is_dynamic_in_pin, + mconfig->max_in_queue); + + skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin, + dfw_config->is_dynamic_out_pin, + mconfig->max_out_queue); + + + if (mconfig->formats_config.caps_size == 0) + goto bind_event; + + mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev, + mconfig->formats_config.caps_size, GFP_KERNEL); + + if (mconfig->formats_config.caps == NULL) + return -ENOMEM; + + memcpy(mconfig->formats_config.caps, dfw_config->caps.caps, + dfw_config->caps.caps_size); + +bind_event: + if (tplg_w->event_type == 0) { + dev_dbg(bus->dev, "ASoC: No event handler required\n"); + return 0; + } + + ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops, + ARRAY_SIZE(skl_tplg_widget_ops), + tplg_w->event_type); + + if (ret) { + dev_err(bus->dev, "%s: No matching event handlers found for %d\n", + __func__, tplg_w->event_type); + return -EINVAL; + } + + return 0; +} + +static struct snd_soc_tplg_ops skl_tplg_ops = { + .widget_load = skl_tplg_widget_load, +}; + +/* This will be read from topology manifest, currently defined here */ +#define SKL_MAX_MCPS 30000000 +#define SKL_FW_MAX_MEM 1000000 + +/* + * SKL topology init routine + */ +int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) +{ + int ret; + const struct firmware *fw; + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = ebus_to_skl(ebus); + + ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); + if (ret < 0) { + dev_err(bus->dev, "tplg fw %s load failed with %d\n", + "dfw_sst.bin", ret); + return ret; + } + + /* + * The complete tplg for SKL is loaded as index 0, we don't use + * any other index + */ + ret = snd_soc_tplg_component_load(&platform->component, + &skl_tplg_ops, fw, 0); + if (ret < 0) { + dev_err(bus->dev, "tplg component load failed%d\n", ret); + return -EINVAL; + } + + skl->resource.max_mcps = SKL_MAX_MCPS; + skl->resource.max_mem = SKL_FW_MAX_MEM; + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 8c7767b..76053a8 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -129,6 +129,11 @@ struct skl_src_module_cfg { enum skl_s_freq src_cfg; } __packed; +struct notification_mask { + u32 notify; + u32 enable; +} __packed; + struct skl_up_down_mixer_cfg { struct skl_base_cfg base_cfg; enum skl_ch_cfg out_ch_cfg; @@ -153,8 +158,7 @@ enum skl_dma_type { union skl_ssp_dma_node { u8 val; struct { - u8 dual_mono:1; - u8 time_slot:3; + u8 time_slot_index:4; u8 i2s_instance:4; } dma_node; }; @@ -263,6 +267,34 @@ struct skl_module_cfg { struct skl_specific_cfg formats_config; }; +struct skl_pipeline { + struct skl_pipe *pipe; + struct list_head node; +}; + +struct skl_dapm_path_list { + struct snd_soc_dapm_path *dapm_path; + struct list_head node; +}; + +static inline struct skl *get_skl_ctx(struct device *dev) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + + return ebus_to_skl(ebus); +} + +int skl_tplg_be_update_params(struct snd_soc_dai *dai, + struct skl_pipe_params *params); +void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, + struct skl_pipe_params *params, int stream); +int skl_tplg_init(struct snd_soc_platform *platform, + struct hdac_ext_bus *ebus); +struct skl_module_cfg *skl_tplg_fe_get_cpr_module( + struct snd_soc_dai *dai, int stream); +int skl_tplg_update_pipe_params(struct device *dev, + struct skl_module_cfg *mconfig, struct skl_pipe_params *params); + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index a506898..2bc396d 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -19,6 +19,29 @@ #ifndef __HDA_TPLG_INTERFACE_H__ #define __HDA_TPLG_INTERFACE_H__ +/* + * Default types range from 0~12. type can range from 0 to 0xff + * SST types start at higher to avoid any overlapping in future + */ +#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS 0x100 +#define SOC_CONTROL_TYPE_HDA_SST_MUX 0x101 +#define SOC_CONTROL_TYPE_HDA_SST_MIX 0x101 +#define SOC_CONTROL_TYPE_HDA_SST_BYTE 0x103 + +#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ +#define MAX_IN_QUEUE 8 +#define MAX_OUT_QUEUE 8 + +/* Event types goes here */ +/* Reserve event type 0 for no event handlers */ +enum skl_event_types { + SKL_EVENT_NONE = 0, + SKL_MIXER_EVENT, + SKL_MUX_EVENT, + SKL_VMIXER_EVENT, + SKL_PGA_EVENT +}; + /** * enum skl_ch_cfg - channel configuration * @@ -83,6 +106,66 @@ enum skl_dev_type { SKL_DEVICE_I2S = 0x2, SKL_DEVICE_SLIMBUS = 0x3, SKL_DEVICE_HDALINK = 0x4, + SKL_DEVICE_HDAHOST = 0x5, SKL_DEVICE_NONE }; + +struct skl_dfw_module_pin { + u16 module_id; + u16 instance_id; +} __packed; + +struct skl_dfw_module_fmt { + u32 channels; + u32 freq; + u32 bit_depth; + u32 valid_bit_depth; + u32 ch_cfg; +} __packed; + +struct skl_dfw_module_caps { + u32 caps_size; + u32 caps[HDA_SST_CFG_MAX]; +}; + +struct skl_dfw_pipe { + u8 pipe_id; + u8 pipe_priority; + u16 conn_type; + u32 memory_pages; +} __packed; + +struct skl_dfw_module { + u16 module_id; + u16 instance_id; + u32 max_mcps; + u8 core_id; + u8 max_in_queue; + u8 max_out_queue; + u8 is_loadable; + u8 conn_type; + u8 dev_type; + u8 hw_conn_type; + u8 time_slot; + u32 obs; + u32 ibs; + u32 params_fixup; + u32 converter; + u32 module_type; + u32 vbus_id; + u8 is_dynamic_in_pin; + u8 is_dynamic_out_pin; + struct skl_dfw_pipe pipe; + struct skl_dfw_module_fmt in_fmt; + struct skl_dfw_module_fmt out_fmt; + struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE]; + struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE]; + struct skl_dfw_module_caps caps; +} __packed; + +struct skl_dfw_algo_data { + u32 max; + char *params; +} __packed; + #endif diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 348d094..5319529 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -166,12 +166,20 @@ static int skl_runtime_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = ebus_to_skl(ebus); + int ret; dev_dbg(bus->dev, "in %s\n", __func__); /* enable controller wake up event */ snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK); + snd_hdac_ext_bus_link_power_down_all(ebus); + + ret = skl_suspend_dsp(skl); + if (ret < 0) + return ret; + snd_hdac_bus_stop_chip(bus); snd_hdac_bus_enter_link_reset(bus); @@ -183,7 +191,7 @@ static int skl_runtime_resume(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *hda = ebus_to_skl(ebus); + struct skl *skl = ebus_to_skl(ebus); int status; dev_dbg(bus->dev, "in %s\n", __func__); @@ -191,12 +199,12 @@ static int skl_runtime_resume(struct device *dev) /* Read STATESTS before controller reset */ status = snd_hdac_chip_readw(bus, STATESTS); - skl_init_pci(hda); + skl_init_pci(skl); snd_hdac_bus_init_chip(bus, true); /* disable controller Wake Up event */ snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0); - return 0; + return skl_resume_dsp(skl); } #endif /* CONFIG_PM */ @@ -453,21 +461,28 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_free; + skl->nhlt = skl_nhlt_init(bus->dev); + + if (skl->nhlt == NULL) + goto out_free; + pci_set_drvdata(skl->pci, ebus); /* check if dsp is there */ if (ebus->ppcap) { - /* TODO register with dsp IPC */ - dev_dbg(bus->dev, "Register dsp\n"); + err = skl_init_dsp(skl); + if (err < 0) { + dev_dbg(bus->dev, "error failed to register dsp\n"); + goto out_free; + } } - if (ebus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(ebus); /* create device for soc dmic */ err = skl_dmic_device_register(skl); if (err < 0) - goto out_free; + goto out_dsp_free; /* register platform dai and controls */ err = skl_platform_register(bus->dev); @@ -491,6 +506,8 @@ out_unregister: skl_platform_unregister(bus->dev); out_dmic_free: skl_dmic_device_unregister(skl); +out_dsp_free: + skl_free_dsp(skl); out_free: skl->init_failed = 1; skl_free(ebus); @@ -507,6 +524,7 @@ static void skl_remove(struct pci_dev *pci) pm_runtime_get_noresume(&pci->dev); pci_dev_put(pci); skl_platform_unregister(&pci->dev); + skl_free_dsp(skl); skl_dmic_device_unregister(skl); skl_free(ebus); dev_set_drvdata(&pci->dev, NULL); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index f7fdbb0..dd2e79a 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -48,6 +48,13 @@ #define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094 #define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20 +struct skl_dsp_resource { + u32 max_mcps; + u32 max_mem; + u32 mcps; + u32 mem; +}; + struct skl { struct hdac_ext_bus ebus; struct pci_dev *pci; @@ -55,8 +62,12 @@ struct skl { unsigned int init_failed:1; /* delayed init failed */ struct platform_device *dmic_dev; - void __iomem *nhlt; /* nhlt ptr */ + void *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ + + struct skl_dsp_resource resource; + struct list_head ppl_list; + struct list_head dapm_path_list; }; #define skl_to_ebus(s) (&(s)->ebus) @@ -72,8 +83,8 @@ struct skl_dma_params { int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); -void __iomem *skl_nhlt_init(struct device *dev); -void skl_nhlt_free(void __iomem *addr); +void *skl_nhlt_init(struct device *dev); +void skl_nhlt_free(void *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn); diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 20728d0..5e21f08 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -296,7 +296,7 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) dev_err(card->dev, "Failed to add TPA6130A2 controls\n"); return err; } - snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42); + snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42); err = omap_mcbsp_st_add_controls(rtd, 2); if (err < 0) { diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 9e4b04e..f3de615 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/dmaengine.h> +#include <linux/dma/pxa-dma.h> #include <sound/core.h> #include <sound/ac97_codec.h> @@ -49,7 +50,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_cold_reset, }; -static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 11; +static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 11, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -57,7 +62,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, }; -static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 12; +static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 12, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -65,7 +74,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { .filter_data = &pxa2xx_ac97_pcm_stereo_out_req, }; -static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10; +static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 10, +}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { .addr = __PREG(MODR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, @@ -73,7 +85,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req, }; -static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9; +static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 9, +}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { .addr = __PREG(MODR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, @@ -81,7 +96,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req, }; -static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8; +static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 8, +}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { .addr = __PREG(MCDR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, @@ -89,9 +107,8 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, }; -static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) +static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { struct snd_dmaengine_dai_dma_data *dma_data; @@ -105,9 +122,8 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, return 0; } -static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) +static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { struct snd_dmaengine_dai_dma_data *dma_data; @@ -121,9 +137,8 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, return 0; } -static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) +static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) return -ENODEV; @@ -139,15 +154,15 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, SNDRV_PCM_RATE_48000) static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = { - .hw_params = pxa2xx_ac97_hw_params, + .startup = pxa2xx_ac97_hifi_startup, }; static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = { - .hw_params = pxa2xx_ac97_hw_aux_params, + .startup = pxa2xx_ac97_aux_startup, }; static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { - .hw_params = pxa2xx_ac97_hw_mic_params, + .startup = pxa2xx_ac97_mic_startup, }; /* diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 6b4e400..0389cf7 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -319,6 +319,9 @@ static int pxa2xx_i2s_probe(struct snd_soc_dai *dai) /* Along with FIFO servicing */ SAIMR &= ~(SAIMR_RFS | SAIMR_TFS); + snd_soc_dai_init_dma_data(dai, &pxa2xx_i2s_pcm_stereo_out, + &pxa2xx_i2s_pcm_stereo_in); + return 0; } diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 29a3fdb..9f39039 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -15,8 +15,6 @@ #include <linux/dmaengine.h> #include <linux/of.h> -#include <mach/dma.h> - #include <sound/core.h> #include <sound/soc.h> #include <sound/pxa2xx-lib.h> @@ -27,11 +25,8 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_dmaengine_dai_dma_data *dma; - int ret; dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); @@ -40,40 +35,13 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, if (!dma) return 0; - /* this may get called several times by oss emulation - * with different params */ - if (prtd->params == NULL) { - prtd->params = dma; - ret = pxa_request_dma("name", DMA_PRIO_LOW, - pxa2xx_pcm_dma_irq, substream); - if (ret < 0) - return ret; - prtd->dma_ch = ret; - } else if (prtd->params != dma) { - pxa_free_dma(prtd->dma_ch); - prtd->params = dma; - ret = pxa_request_dma("name", DMA_PRIO_LOW, - pxa2xx_pcm_dma_irq, substream); - if (ret < 0) - return ret; - prtd->dma_ch = ret; - } - return __pxa2xx_pcm_hw_params(substream, params); } static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { - struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; - __pxa2xx_pcm_hw_free(substream); - if (prtd->dma_ch >= 0) { - pxa_free_dma(prtd->dma_ch); - prtd->dma_ch = -1; - prtd->params = NULL; - } - return 0; } diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 5709057..f1e0c70 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -15,6 +15,14 @@ config SND_SOC_ROCKCHIP_I2S Rockchip I2S device. The device supports upto maximum of 8 channels each for play and record. +config SND_SOC_ROCKCHIP_SPDIF + tristate "Rockchip SPDIF Device Driver" + depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for SPDIF driver for + Rockchip SPDIF transceiver device. + config SND_SOC_ROCKCHIP_MAX98090 tristate "ASoC support for Rockchip boards using a MAX98090 codec" depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 1bc1dc3..c0bf560 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,7 +1,9 @@ # ROCKCHIP Platform Support -snd-soc-i2s-objs := rockchip_i2s.o +snd-soc-rockchip-i2s-objs := rockchip_i2s.o +snd-soc-rockchip-spdif-objs := rockchip_spdif.o -obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-i2s.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o snd-soc-rockchip-max98090-objs := rockchip_max98090.o snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index b936102..58ee645 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -226,6 +226,7 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct rk_i2s_dev *i2s = to_info(dai); + struct snd_soc_pcm_runtime *rtd = substream->private_data; unsigned int val = 0; switch (params_format(params)) { @@ -245,13 +246,46 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val); - regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val); + switch (params_channels(params)) { + case 8: + val |= I2S_CHN_8; + break; + case 6: + val |= I2S_CHN_6; + break; + case 4: + val |= I2S_CHN_4; + break; + case 2: + val |= I2S_CHN_2; + break; + default: + dev_err(i2s->dev, "invalid channel: %d\n", + params_channels(params)); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + regmap_update_bits(i2s->regmap, I2S_RXCR, + I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK, + val); + else + regmap_update_bits(i2s->regmap, I2S_TXCR, + I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK, + val); + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, I2S_DMACR_TDL(16)); regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, I2S_DMACR_RDL(16)); + val = I2S_CKR_TRCM_TXRX; + if (dai->driver->symmetric_rates || rtd->dai_link->symmetric_rates) + val = I2S_CKR_TRCM_TXSHARE; + + regmap_update_bits(i2s->regmap, I2S_CKR, + I2S_CKR_TRCM_MASK, + val); return 0; } @@ -415,10 +449,12 @@ static const struct regmap_config rockchip_i2s_regmap_config = { static int rockchip_i2s_probe(struct platform_device *pdev) { + struct device_node *node = pdev->dev.of_node; struct rk_i2s_dev *i2s; struct resource *res; void __iomem *regs; int ret; + int val; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); if (!i2s) { @@ -475,6 +511,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev) goto err_pm_disable; } + /* refine capture channels */ + if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) { + if (val >= 2 && val <= 8) + rockchip_i2s_dai.capture.channels_max = val; + else + rockchip_i2s_dai.capture.channels_max = 2; + } + ret = devm_snd_soc_register_component(&pdev->dev, &rockchip_i2s_component, &rockchip_i2s_dai, 1); diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h index 93f456f..dc6e2c7 100644 --- a/sound/soc/rockchip/rockchip_i2s.h +++ b/sound/soc/rockchip/rockchip_i2s.h @@ -49,6 +49,9 @@ * RXCR * receive operation control register */ +#define I2S_RXCR_CSR_SHIFT 15 +#define I2S_RXCR_CSR(x) (x << I2S_RXCR_CSR_SHIFT) +#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT) #define I2S_RXCR_HWT BIT(14) #define I2S_RXCR_SJM_SHIFT 12 #define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT) @@ -75,6 +78,12 @@ * CKR * clock generation register */ +#define I2S_CKR_TRCM_SHIFT 28 +#define I2S_CKR_TRCM(x) (x << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_TXSHARE (1 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_RXSHARE (2 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT) #define I2S_CKR_MSS_SHIFT 27 #define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT) #define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT) @@ -207,6 +216,13 @@ enum { ROCKCHIP_DIV_BCLK, }; +/* channel select */ +#define I2S_CSR_SHIFT 15 +#define I2S_CHN_2 (0 << I2S_CSR_SHIFT) +#define I2S_CHN_4 (1 << I2S_CSR_SHIFT) +#define I2S_CHN_6 (2 << I2S_CSR_SHIFT) +#define I2S_CHN_8 (3 << I2S_CSR_SHIFT) + /* I2S REGS */ #define I2S_TXCR (0x0000) #define I2S_RXCR (0x0004) diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c new file mode 100644 index 0000000..a38a302 --- /dev/null +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -0,0 +1,405 @@ +/* sound/soc/rockchip/rk_spdif.c + * + * ALSA SoC Audio Layer - Rockchip I2S Controller driver + * + * Copyright (c) 2014 Rockchip Electronics Co. Ltd. + * Author: Jianqun <jay.xu@rock-chips.com> + * Copyright (c) 2015 Collabora Ltd. + * Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * + * 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/delay.h> +#include <linux/of_gpio.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include <sound/dmaengine_pcm.h> + +#include "rockchip_spdif.h" + +enum rk_spdif_type { + RK_SPDIF_RK3066, + RK_SPDIF_RK3188, + RK_SPDIF_RK3288, +}; + +#define RK3288_GRF_SOC_CON2 0x24c + +struct rk_spdif_dev { + struct device *dev; + + struct clk *mclk; + struct clk *hclk; + + struct snd_dmaengine_dai_dma_data playback_dma_data; + + struct regmap *regmap; +}; + +static const struct of_device_id rk_spdif_match[] = { + { .compatible = "rockchip,rk3066-spdif", + .data = (void *) RK_SPDIF_RK3066 }, + { .compatible = "rockchip,rk3188-spdif", + .data = (void *) RK_SPDIF_RK3188 }, + { .compatible = "rockchip,rk3288-spdif", + .data = (void *) RK_SPDIF_RK3288 }, + {}, +}; +MODULE_DEVICE_TABLE(of, rk_spdif_match); + +static int rk_spdif_runtime_suspend(struct device *dev) +{ + struct rk_spdif_dev *spdif = dev_get_drvdata(dev); + + clk_disable_unprepare(spdif->mclk); + clk_disable_unprepare(spdif->hclk); + + return 0; +} + +static int rk_spdif_runtime_resume(struct device *dev) +{ + struct rk_spdif_dev *spdif = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(spdif->mclk); + if (ret) { + dev_err(spdif->dev, "mclk clock enable failed %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(spdif->hclk); + if (ret) { + dev_err(spdif->dev, "hclk clock enable failed %d\n", ret); + return ret; + } + + return 0; +} + +static int rk_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE; + int srate, mclk; + int ret; + + srate = params_rate(params); + switch (srate) { + case 32000: + case 48000: + case 96000: + mclk = 96000 * 128; /* 12288000 hz */ + break; + case 44100: + mclk = 44100 * 256; /* 11289600 hz */ + break; + case 192000: + mclk = 192000 * 128; /* 24576000 hz */ + break; + default: + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + val |= SPDIF_CFGR_VDW_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val |= SPDIF_CFGR_VDW_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val |= SPDIF_CFGR_VDW_24; + break; + default: + return -EINVAL; + } + + /* Set clock and calculate divider */ + ret = clk_set_rate(spdif->mclk, mclk); + if (ret != 0) { + dev_err(spdif->dev, "Failed to set module clock rate: %d\n", + ret); + return ret; + } + + val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256)); + ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR, + SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE | + SDPIF_CFGR_VDW_MASK, + val); + + return ret; +} + +static int rk_spdif_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR, + SPDIF_DMACR_TDE_ENABLE, + SPDIF_DMACR_TDE_ENABLE); + + if (ret != 0) + return ret; + + ret = regmap_update_bits(spdif->regmap, SPDIF_XFER, + SPDIF_XFER_TXS_START, + SPDIF_XFER_TXS_START); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR, + SPDIF_DMACR_TDE_ENABLE, + SPDIF_DMACR_TDE_DISABLE); + + if (ret != 0) + return ret; + + ret = regmap_update_bits(spdif->regmap, SPDIF_XFER, + SPDIF_XFER_TXS_START, + SPDIF_XFER_TXS_STOP); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int rk_spdif_dai_probe(struct snd_soc_dai *dai) +{ + struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &spdif->playback_dma_data; + + return 0; +} + +static const struct snd_soc_dai_ops rk_spdif_dai_ops = { + .hw_params = rk_spdif_hw_params, + .trigger = rk_spdif_trigger, +}; + +static struct snd_soc_dai_driver rk_spdif_dai = { + .probe = rk_spdif_dai_probe, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &rk_spdif_dai_ops, +}; + +static const struct snd_soc_component_driver rk_spdif_component = { + .name = "rockchip-spdif", +}; + +static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIF_CFGR: + case SPDIF_DMACR: + case SPDIF_INTCR: + case SPDIF_XFER: + case SPDIF_SMPDR: + return true; + default: + return false; + } +} + +static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIF_CFGR: + case SPDIF_SDBLR: + case SPDIF_INTCR: + case SPDIF_INTSR: + case SPDIF_XFER: + return true; + default: + return false; + } +} + +static bool rk_spdif_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIF_INTSR: + case SPDIF_SDBLR: + return true; + default: + return false; + } +} + +static const struct regmap_config rk_spdif_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SPDIF_SMPDR, + .writeable_reg = rk_spdif_wr_reg, + .readable_reg = rk_spdif_rd_reg, + .volatile_reg = rk_spdif_volatile_reg, + .cache_type = REGCACHE_FLAT, +}; + +static int rk_spdif_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct rk_spdif_dev *spdif; + const struct of_device_id *match; + struct resource *res; + void __iomem *regs; + int ret; + + match = of_match_node(rk_spdif_match, np); + if ((int) match->data == RK_SPDIF_RK3288) { + struct regmap *grf; + + grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(grf)) { + dev_err(&pdev->dev, + "rockchip_spdif missing 'rockchip,grf' \n"); + return PTR_ERR(grf); + } + + /* Select the 8 channel SPDIF solution on RK3288 as + * the 2 channel one does not appear to work + */ + regmap_write(grf, RK3288_GRF_SOC_CON2, BIT(1) << 16); + } + + spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); + if (!spdif) + return -ENOMEM; + + spdif->hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(spdif->hclk)) { + dev_err(&pdev->dev, "Can't retrieve rk_spdif bus clock\n"); + return PTR_ERR(spdif->hclk); + } + ret = clk_prepare_enable(spdif->hclk); + if (ret) { + dev_err(spdif->dev, "hclock enable failed %d\n", ret); + return ret; + } + + spdif->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(spdif->mclk)) { + dev_err(&pdev->dev, "Can't retrieve rk_spdif master clock\n"); + return PTR_ERR(spdif->mclk); + } + + ret = clk_prepare_enable(spdif->mclk); + if (ret) { + dev_err(spdif->dev, "clock enable failed %d\n", ret); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + spdif->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "hclk", regs, + &rk_spdif_regmap_config); + if (IS_ERR(spdif->regmap)) { + dev_err(&pdev->dev, + "Failed to initialise managed register map\n"); + return PTR_ERR(spdif->regmap); + } + + spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR; + spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + spdif->playback_dma_data.maxburst = 4; + + spdif->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, spdif); + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_request_idle(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, + &rk_spdif_component, + &rk_spdif_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI\n"); + goto err_pm_runtime; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM\n"); + goto err_pm_runtime; + } + + return 0; + +err_pm_runtime: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int rk_spdif_remove(struct platform_device *pdev) +{ + struct rk_spdif_dev *spdif = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + rk_spdif_runtime_suspend(&pdev->dev); + + clk_disable_unprepare(spdif->mclk); + clk_disable_unprepare(spdif->hclk); + + return 0; +} + +static const struct dev_pm_ops rk_spdif_pm_ops = { + SET_RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume, + NULL) +}; + +static struct platform_driver rk_spdif_driver = { + .probe = rk_spdif_probe, + .remove = rk_spdif_remove, + .driver = { + .name = "rockchip-spdif", + .of_match_table = of_match_ptr(rk_spdif_match), + .pm = &rk_spdif_pm_ops, + }, +}; +module_platform_driver(rk_spdif_driver); + +MODULE_ALIAS("platform:rockchip-spdif"); +MODULE_DESCRIPTION("ROCKCHIP SPDIF transceiver Interface"); +MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.co.uk>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h new file mode 100644 index 0000000..07f86a2 --- /dev/null +++ b/sound/soc/rockchip/rockchip_spdif.h @@ -0,0 +1,63 @@ +/* + * ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver + * + * Copyright (c) 2015 Collabora Ltd. + * Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk> + * + * 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. + */ + +#ifndef _ROCKCHIP_SPDIF_H +#define _ROCKCHIP_SPDIF_H + +/* + * CFGR + * transfer configuration register +*/ +#define SPDIF_CFGR_CLK_DIV_SHIFT (16) +#define SPDIF_CFGR_CLK_DIV_MASK (0xff << SPDIF_CFGR_CLK_DIV_SHIFT) +#define SPDIF_CFGR_CLK_DIV(x) (x << SPDIF_CFGR_CLK_DIV_SHIFT) + +#define SPDIF_CFGR_HALFWORD_SHIFT 2 +#define SPDIF_CFGR_HALFWORD_DISABLE (0 << SPDIF_CFGR_HALFWORD_SHIFT) +#define SPDIF_CFGR_HALFWORD_ENABLE (1 << SPDIF_CFGR_HALFWORD_SHIFT) + +#define SPDIF_CFGR_VDW_SHIFT 0 +#define SPDIF_CFGR_VDW(x) (x << SPDIF_CFGR_VDW_SHIFT) +#define SDPIF_CFGR_VDW_MASK (0xf << SPDIF_CFGR_VDW_SHIFT) + +#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x00) +#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x01) +#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x10) + +/* + * DMACR + * DMA control register +*/ +#define SPDIF_DMACR_TDE_SHIFT 5 +#define SPDIF_DMACR_TDE_DISABLE (0 << SPDIF_DMACR_TDE_SHIFT) +#define SPDIF_DMACR_TDE_ENABLE (1 << SPDIF_DMACR_TDE_SHIFT) + +#define SPDIF_DMACR_TDL_SHIFT 0 +#define SPDIF_DMACR_TDL(x) ((x) << SPDIF_DMACR_TDL_SHIFT) +#define SPDIF_DMACR_TDL_MASK (0x1f << SDPIF_DMACR_TDL_SHIFT) + +/* + * XFER + * Transfer control register +*/ +#define SPDIF_XFER_TXS_SHIFT 0 +#define SPDIF_XFER_TXS_STOP (0 << SPDIF_XFER_TXS_SHIFT) +#define SPDIF_XFER_TXS_START (1 << SPDIF_XFER_TXS_SHIFT) + +#define SPDIF_CFGR (0x0000) +#define SPDIF_SDBLR (0x0004) +#define SPDIF_DMACR (0x0008) +#define SPDIF_INTCR (0x000c) +#define SPDIF_INTSR (0x0010) +#define SPDIF_XFER (0x0018) +#define SPDIF_SMPDR (0x0020) + +#endif /* _ROCKCHIP_SPDIF_H */ diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index c72e9fb..5f5825f 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -26,16 +26,15 @@ #include <mach/gpio-samsung.h> #include "s3c24xx-i2s.h" -static unsigned int rates[] = { +static const unsigned int rates[] = { 11025, 22050, 44100, }; -static struct snd_pcm_hw_constraint_list hw_rates = { +static const struct snd_pcm_hw_constraint_list hw_rates = { .count = ARRAY_SIZE(rates), .list = rates, - .mask = 0, }; static struct snd_soc_jack hp_jack; diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 35e37c4..fa096ab 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -38,16 +38,15 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream, static int rx1950_spk_power(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -static unsigned int rates[] = { +static const unsigned int rates[] = { 16000, 44100, 48000, }; -static struct snd_pcm_hw_constraint_list hw_rates = { +static const struct snd_pcm_hw_constraint_list hw_rates = { .count = ARRAY_SIZE(rates), .list = rates, - .mask = 0, }; static struct snd_soc_jack hp_jack; diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 6ca90aa..206d1ed 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -41,7 +41,7 @@ config SND_SOC_RCAR select SND_SIMPLE_CARD select REGMAP_MMIO help - This option enables R-Car SUR/SCU/SSIU/SSI sound support + This option enables R-Car SRU/SCU/SSIU/SSI sound support config SND_SOC_RSRC_CARD tristate "Renesas Sampling Rate Convert Sound Card" diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index c4ebbb7..2a5b3a2 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -69,11 +69,10 @@ static u32 rsnd_adg_calculate_rbgx(unsigned long div) static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) { struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); int id = rsnd_mod_id(mod); int ws = id; - if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) { + if (rsnd_ssi_is_pin_sharing(io)) { switch (id) { case 1: case 2: diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index eec294d..deed48e 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -300,7 +300,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) /* * rsnd_dai functions */ -#define __rsnd_mod_call(mod, io, func, param...) \ +#define rsnd_mod_call(mod, io, func, param...) \ ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ @@ -308,24 +308,17 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ - int called = 0; \ - if (val == __rsnd_mod_call_##func) { \ - called = 1; \ - ret = (mod)->ops->func(mod, io, param); \ - } \ + int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \ mod->status = (mod->status & ~mask) + \ (add << __rsnd_mod_shift_##func); \ - dev_dbg(dev, "%s[%d] 0x%08x %s\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status, \ - called ? #func : ""); \ + dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), \ + mod->status, call ? #func : ""); \ + if (call) \ + ret = (mod)->ops->func(mod, io, param); \ ret; \ }) -#define rsnd_mod_call(mod, io, func, param...) \ - (!(mod) ? -ENODEV : \ - !((mod)->ops->func) ? 0 : \ - __rsnd_mod_call(mod, io, func, param)) - #define rsnd_dai_call(fn, io, param...) \ ({ \ struct rsnd_mod *mod; \ @@ -334,9 +327,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) mod = (io)->mod[i]; \ if (!mod) \ continue; \ - ret = rsnd_mod_call(mod, io, fn, param); \ - if (ret < 0) \ - break; \ + ret |= rsnd_mod_call(mod, io, fn, param); \ } \ ret; \ }) @@ -502,16 +493,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, break; case SNDRV_PCM_TRIGGER_STOP: ret = rsnd_dai_call(stop, io, priv); - if (ret < 0) - goto dai_trigger_end; - ret = rsnd_dai_call(quit, io, priv); - if (ret < 0) - goto dai_trigger_end; + ret |= rsnd_dai_call(quit, io, priv); - ret = rsnd_platform_call(priv, dai, stop, ssi_id); - if (ret < 0) - goto dai_trigger_end; + ret |= rsnd_platform_call(priv, dai, stop, ssi_id); rsnd_dai_stream_quit(io); break; @@ -1236,20 +1221,11 @@ static int rsnd_probe(struct platform_device *pdev) }; int ret, i; - info = NULL; - of_data = NULL; - if (of_id) { - info = devm_kzalloc(&pdev->dev, - sizeof(struct rcar_snd_info), GFP_KERNEL); - of_data = of_id->data; - } else { - info = pdev->dev.platform_data; - } - - if (!info) { - dev_err(dev, "driver needs R-Car sound information\n"); - return -ENODEV; - } + info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + of_data = of_id->data; /* * init priv data diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index a3e7c71..3cb214a 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -35,7 +35,7 @@ static int rsnd_ctu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_ctu_initialize_lock(mod); @@ -50,7 +50,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); return 0; } @@ -118,10 +118,8 @@ int rsnd_ctu_probe(struct platform_device *pdev, int i, nr, ret; /* This driver doesn't support Gen1 at this point */ - if (rsnd_is_gen1(priv)) { - dev_warn(dev, "CTU is not supported on Gen1\n"); - return -EINVAL; - } + if (rsnd_is_gen1(priv)) + return 0; rsnd_of_parse_ctu(pdev, of_data, priv); diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index bfbb8a5..5d084d0 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -470,7 +470,7 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, dev_err(dev, "DVC is selected without SRC\n"); /* use SSIU or SSI ? */ - if (is_ssi && rsnd_ssi_use_busif(io, mod)) + if (is_ssi && rsnd_ssi_use_busif(io)) is_ssi++; return (is_from) ? diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 8d8eee6..58f6909 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -153,7 +153,7 @@ static int rsnd_dvc_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_dvc_soft_reset(mod); @@ -175,7 +175,7 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); return 0; } @@ -333,10 +333,8 @@ int rsnd_dvc_probe(struct platform_device *pdev, int i, nr, ret; /* This driver doesn't support Gen1 at this point */ - if (rsnd_is_gen1(priv)) { - dev_warn(dev, "CMD is not supported on Gen1\n"); - return -EINVAL; - } + if (rsnd_is_gen1(priv)) + return 0; rsnd_of_parse_dvc(pdev, of_data, priv); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index f04d17b..76da762 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -22,13 +22,15 @@ #include "rsnd.h" struct rsnd_gen { - void __iomem *base[RSND_BASE_MAX]; - struct rsnd_gen_ops *ops; + /* RSND_BASE_MAX base */ + void __iomem *base[RSND_BASE_MAX]; + phys_addr_t res[RSND_BASE_MAX]; struct regmap *regmap[RSND_BASE_MAX]; + + /* RSND_REG_MAX base */ struct regmap_field *regs[RSND_REG_MAX]; - phys_addr_t res[RSND_REG_MAX]; }; #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) @@ -79,11 +81,11 @@ u32 rsnd_read(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return 0; + regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); + dev_dbg(dev, "r %s[%d] - %4d : %08x\n", rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val); - regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); - return val; } @@ -182,6 +184,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, if (IS_ERR(regmap)) return PTR_ERR(regmap); + /* RSND_BASE_MAX base */ gen->base[reg_id] = base; gen->regmap[reg_id] = regmap; gen->res[reg_id] = res->start; @@ -198,6 +201,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, if (IS_ERR(regs)) return PTR_ERR(regs); + /* RSND_REG_MAX base */ gen->regs[conf[i].idx] = regs; } diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 8544403..953dd0b 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -58,7 +58,7 @@ static int rsnd_mix_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_mix_soft_reset(mod); @@ -83,7 +83,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); return 0; } @@ -151,10 +151,8 @@ int rsnd_mix_probe(struct platform_device *pdev, int i, nr, ret; /* This driver doesn't support Gen1 at this point */ - if (rsnd_is_gen1(priv)) { - dev_warn(dev, "MIX is not supported on Gen1\n"); - return -EINVAL; - } + if (rsnd_is_gen1(priv)) + return 0; rsnd_of_parse_mix(pdev, of_data, priv); diff --git a/include/sound/rcar_snd.h b/sound/soc/sh/rcar/rcar_snd.h index d8e33d3..d8e33d3 100644 --- a/include/sound/rcar_snd.h +++ b/sound/soc/sh/rcar/rcar_snd.h diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index e4068d7..0853298 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -21,10 +21,11 @@ #include <linux/of_irq.h> #include <linux/sh_dma.h> #include <linux/workqueue.h> -#include <sound/rcar_snd.h> #include <sound/soc.h> #include <sound/pcm_params.h> +#include "rcar_snd.h" + /* * pseudo register * @@ -329,8 +330,8 @@ struct rsnd_mod { #define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_dma(mod) (&(mod)->dma) #define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) -#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) -#define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) +#define rsnd_mod_power_on(mod) clk_enable((mod)->clk) +#define rsnd_mod_power_off(mod) clk_disable((mod)->clk) #define rsnd_mod_get(ip) (&(ip)->mod) int rsnd_mod_init(struct rsnd_priv *priv, @@ -571,9 +572,12 @@ int rsnd_ssi_probe(struct platform_device *pdev, void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); -int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); -int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod); +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); + +#define rsnd_ssi_is_pin_sharing(io) \ + __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); /* * R-Car SRC diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index ca7a20f..261b502 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -159,7 +159,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, /* * SSI_MODE1 */ - if (rsnd_ssi_is_pin_sharing(ssi_mod)) { + if (rsnd_ssi_is_pin_sharing(io)) { int shift = -1; switch (ssi_id) { case 1: @@ -352,7 +352,7 @@ static int rsnd_src_init(struct rsnd_mod *mod, { struct rsnd_src *src = rsnd_mod_to_src(mod); - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_src_soft_reset(mod); @@ -373,7 +373,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv); - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); if (src->err) dev_warn(dev, "%s[%d] under/over flow err = %d\n", @@ -1036,8 +1036,10 @@ int rsnd_src_probe(struct platform_device *pdev, int i, nr, ret; ops = NULL; - if (rsnd_is_gen1(priv)) + if (rsnd_is_gen1(priv)) { ops = &rsnd_src_gen1_ops; + dev_warn(dev, "Gen1 support will be removed soon\n"); + } if (rsnd_is_gen2(priv)) ops = &rsnd_src_gen2_ops; if (!ops) { diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 5e05f942..1427ec2 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -79,7 +79,6 @@ struct rsnd_ssi { #define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) #define rsnd_ssi_parent(ssi) ((ssi)->parent) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) @@ -87,8 +86,9 @@ struct rsnd_ssi { #define rsnd_ssi_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") -int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod) +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { + struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int use_busif = 0; @@ -184,7 +184,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, u32 cr; if (0 == ssi->usrcnt) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); if (rsnd_rdai_is_clk_master(rdai)) { struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); @@ -265,7 +265,7 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) rsnd_ssi_master_clk_stop(ssi); } - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); ssi->chan = 0; } @@ -395,7 +395,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io, mod)); + rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io)); rsnd_ssi_hw_start(ssi, io); @@ -555,7 +555,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, rsnd_dma_quit(io, rsnd_mod_to_dma(mod)); /* PIO will request IRQ again */ - devm_free_irq(dev, irq, ssi); + devm_free_irq(dev, irq, mod); return 0; } @@ -614,7 +614,7 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, int is_play = rsnd_io_is_play(io); char *name; - if (rsnd_ssi_use_busif(io, mod)) + if (rsnd_ssi_use_busif(io)) name = is_play ? "rxu" : "txu"; else name = is_play ? "rx" : "tx"; @@ -660,7 +660,7 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id); } -int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -671,7 +671,7 @@ static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) { struct rsnd_mod *mod = rsnd_mod_get(ssi); - if (!rsnd_ssi_is_pin_sharing(mod)) + if (!__rsnd_ssi_is_pin_sharing(mod)) return; switch (rsnd_mod_id(mod)) { @@ -700,9 +700,6 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev, struct device *dev = &pdev->dev; int nr, i; - if (!of_data) - return; - node = rsnd_ssi_of_node(priv); if (!node) return; diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 025c38f..12a9820 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -612,8 +612,15 @@ static struct snd_compr_ops soc_compr_dyn_ops = { .get_codec_caps = soc_compr_get_codec_caps }; -/* create a new compress */ -int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) +/** + * snd_soc_new_compress - create a new compress. + * + * @rtd: The runtime for which we will create compress + * @num: the device index number (zero based - shared with normal PCMs) + * + * Return: 0 for success, else error. + */ +int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_platform *platform = rtd->platform; @@ -703,3 +710,4 @@ compr_err: kfree(compr); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_new_compress); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3b471f9..24b0960 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1370,9 +1370,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) soc_dpcm_debugfs_add(rtd); #endif - if (cpu_dai->driver->compress_dai) { + if (cpu_dai->driver->compress_new) { /*create compress_device"*/ - ret = soc_new_compress(rtd, num); + ret = cpu_dai->driver->compress_new(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create compress %s\n", dai_link->stream_name); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index ff8bda4..016eba1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -509,6 +509,18 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, } /** + * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a + * kcontrol + * @kcontrol: The kcontrol + */ +struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget( + struct snd_kcontrol *kcontrol) +{ + return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget); + +/** * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a * kcontrol * @kcontrol: The kcontrol @@ -779,7 +791,7 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, * Determine if a kcontrol is shared. If it is, look it up. If it isn't, * create it. Either way, add the widget into the control's widget list */ -static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, +static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, int kci) { struct snd_soc_dapm_context *dapm = w->dapm; @@ -810,6 +822,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, switch (w->id) { case snd_soc_dapm_switch: case snd_soc_dapm_mixer: + case snd_soc_dapm_pga: wname_in_long_name = true; kcname_in_long_name = true; break; @@ -899,7 +912,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) continue; if (!w->kcontrols[i]) { - ret = dapm_create_or_share_mixmux_kcontrol(w, i); + ret = dapm_create_or_share_kcontrol(w, i); if (ret < 0) return ret; } @@ -952,7 +965,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return -EINVAL; } - ret = dapm_create_or_share_mixmux_kcontrol(w, 0); + ret = dapm_create_or_share_kcontrol(w, 0); if (ret < 0) return ret; @@ -967,9 +980,13 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) /* create new dapm volume control */ static int dapm_new_pga(struct snd_soc_dapm_widget *w) { - if (w->num_kcontrols) - dev_err(w->dapm->dev, - "ASoC: PGA controls not supported: '%s'\n", w->name); + int i, ret; + + for (i = 0; i < w->num_kcontrols; i++) { + ret = dapm_create_or_share_kcontrol(w, i); + if (ret < 0) + return ret; + } return 0; } @@ -3473,11 +3490,29 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: substream.stream = SNDRV_PCM_STREAM_CAPTURE; + if (source->driver->ops && source->driver->ops->startup) { + ret = source->driver->ops->startup(&substream, source); + if (ret < 0) { + dev_err(source->dev, + "ASoC: startup() failed: %d\n", ret); + goto out; + } + source->active++; + } ret = soc_dai_hw_params(&substream, params, source); if (ret < 0) goto out; substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + if (sink->driver->ops && sink->driver->ops->startup) { + ret = sink->driver->ops->startup(&substream, sink); + if (ret < 0) { + dev_err(sink->dev, + "ASoC: startup() failed: %d\n", ret); + goto out; + } + sink->active++; + } ret = soc_dai_hw_params(&substream, params, sink); if (ret < 0) goto out; @@ -3497,6 +3532,18 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, if (ret != 0 && ret != -ENOTSUPP) dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret); ret = 0; + + source->active--; + if (source->driver->ops && source->driver->ops->shutdown) { + substream.stream = SNDRV_PCM_STREAM_CAPTURE; + source->driver->ops->shutdown(&substream, source); + } + + sink->active--; + if (sink->driver->ops && sink->driver->ops->shutdown) { + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + sink->driver->ops->shutdown(&substream, sink); + } break; default: diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 100d92b..ecd38e5 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -207,6 +207,34 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_info_volsw); /** + * snd_soc_info_volsw_sx - Mixer info callback for SX TLV controls + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a single mixer control, or a double + * mixer control that spans 2 registers of the SX TLV type. SX TLV controls + * have a range that represents both positive and negative values either side + * of zero but without a sign bit. + * + * Returns 0 for success. + */ +int snd_soc_info_volsw_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + snd_soc_info_volsw(kcontrol, uinfo); + /* Max represents the number of levels in an SX control not the + * maximum value, so add the minimum value back on + */ + uinfo->value.integer.max += mc->min; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw_sx); + +/** * snd_soc_get_volsw - single mixer get callback * @kcontrol: mixer control * @ucontrol: control element information @@ -560,16 +588,16 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); /** * snd_soc_limit_volume - Set new limit to an existing volume control. * - * @codec: where to look for the control + * @card: where to look for the control * @name: Name of the control * @max: new maximum limit * * Return 0 for success, else error. */ -int snd_soc_limit_volume(struct snd_soc_codec *codec, +int snd_soc_limit_volume(struct snd_soc_card *card, const char *name, int max) { - struct snd_card *card = codec->component.card->snd_card; + struct snd_card *snd_card = card->snd_card; struct snd_kcontrol *kctl; struct soc_mixer_control *mc; int found = 0; @@ -579,7 +607,7 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec, if (unlikely(!name || max <= 0)) return -EINVAL; - list_for_each_entry(kctl, &card->controls, list) { + list_for_each_entry(kctl, &snd_card->controls, list) { if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) { found = 1; break; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 69d01cd..8d7ec80 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1558,7 +1558,7 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos; if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_pcm_dai), count, + sizeof(struct snd_soc_tplg_pcm), count, hdr->payload_size, "PCM DAI")) { dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n", count); @@ -1566,7 +1566,7 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, } dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); - tplg->pos += sizeof(struct snd_soc_tplg_pcm_dai) * count; + tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count; dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL); if (dobj == NULL) diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 8d59d83..bcbf4da 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -283,7 +283,7 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); unsigned long clk_freq; - int hwrate; + int ret, hwrate; u32 val; if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) @@ -293,8 +293,9 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, if (!clk_freq) return -EINVAL; - if (clk_set_rate(scodec->clk_module, clk_freq)) - return -EINVAL; + ret = clk_set_rate(scodec->clk_module, clk_freq); + if (ret) + return ret; hwrate = sun4i_codec_get_hw_rate(params); if (hwrate < 0) @@ -388,8 +389,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = { .rate_max = 192000, .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000 | - SNDRV_PCM_RATE_KNOT, + SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .sig_bits = 24, @@ -571,7 +571,6 @@ static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) { struct snd_soc_card *card; - int ret; card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) @@ -584,12 +583,6 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) card->dev = dev; card->name = "sun4i-codec"; - ret = snd_soc_of_parse_audio_routing(card, "routing"); - if (ret) { - dev_err(dev, "Failed to create our audio routing\n"); - return NULL; - } - return card; }; diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c index 48c6e1a..b9d3a32 100644 --- a/virt/kvm/arm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c @@ -137,6 +137,8 @@ bool kvm_timer_should_fire(struct kvm_vcpu *vcpu) void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; + bool phys_active; + int ret; /* * We're about to run this vcpu again, so there is no need to @@ -151,6 +153,23 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) */ if (kvm_timer_should_fire(vcpu)) kvm_timer_inject_irq(vcpu); + + /* + * We keep track of whether the edge-triggered interrupt has been + * signalled to the vgic/guest, and if so, we mask the interrupt and + * the physical distributor to prevent the timer from raising a + * physical interrupt whenever we run a guest, preventing forward + * VCPU progress. + */ + if (kvm_vgic_get_phys_irq_active(timer->map)) + phys_active = true; + else + phys_active = false; + + ret = irq_set_irqchip_state(timer->map->irq, + IRQCHIP_STATE_ACTIVE, + phys_active); + WARN_ON(ret); } /** diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 6bd1c9b..66c6616 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -531,6 +531,34 @@ bool vgic_handle_set_pending_reg(struct kvm *kvm, return false; } +/* + * If a mapped interrupt's state has been modified by the guest such that it + * is no longer active or pending, without it have gone through the sync path, + * then the map->active field must be cleared so the interrupt can be taken + * again. + */ +static void vgic_handle_clear_mapped_irq(struct kvm_vcpu *vcpu) +{ + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + struct list_head *root; + struct irq_phys_map_entry *entry; + struct irq_phys_map *map; + + rcu_read_lock(); + + /* Check for PPIs */ + root = &vgic_cpu->irq_phys_map_list; + list_for_each_entry_rcu(entry, root, entry) { + map = &entry->map; + + if (!vgic_dist_irq_is_pending(vcpu, map->virt_irq) && + !vgic_irq_is_active(vcpu, map->virt_irq)) + map->active = false; + } + + rcu_read_unlock(); +} + bool vgic_handle_clear_pending_reg(struct kvm *kvm, struct kvm_exit_mmio *mmio, phys_addr_t offset, int vcpu_id) @@ -561,6 +589,7 @@ bool vgic_handle_clear_pending_reg(struct kvm *kvm, vcpu_id, offset); vgic_reg_access(mmio, reg, offset, mode); + vgic_handle_clear_mapped_irq(kvm_get_vcpu(kvm, vcpu_id)); vgic_update_state(kvm); return true; } @@ -598,6 +627,7 @@ bool vgic_handle_clear_active_reg(struct kvm *kvm, ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT); if (mmio->is_write) { + vgic_handle_clear_mapped_irq(kvm_get_vcpu(kvm, vcpu_id)); vgic_update_state(kvm); return true; } @@ -982,6 +1012,12 @@ static int compute_pending_for_cpu(struct kvm_vcpu *vcpu) pend_percpu = vcpu->arch.vgic_cpu.pending_percpu; pend_shared = vcpu->arch.vgic_cpu.pending_shared; + if (!dist->enabled) { + bitmap_zero(pend_percpu, VGIC_NR_PRIVATE_IRQS); + bitmap_zero(pend_shared, nr_shared); + return 0; + } + pending = vgic_bitmap_get_cpu_map(&dist->irq_pending, vcpu_id); enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id); bitmap_and(pend_percpu, pending, enabled, VGIC_NR_PRIVATE_IRQS); @@ -1009,11 +1045,6 @@ void vgic_update_state(struct kvm *kvm) struct kvm_vcpu *vcpu; int c; - if (!dist->enabled) { - set_bit(0, dist->irq_pending_on_cpu); - return; - } - kvm_for_each_vcpu(c, vcpu, kvm) { if (compute_pending_for_cpu(vcpu)) set_bit(c, dist->irq_pending_on_cpu); @@ -1092,6 +1123,15 @@ static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu) struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; struct vgic_lr vlr = vgic_get_lr(vcpu, lr_nr); + /* + * We must transfer the pending state back to the distributor before + * retiring the LR, otherwise we may loose edge-triggered interrupts. + */ + if (vlr.state & LR_STATE_PENDING) { + vgic_dist_irq_set_pending(vcpu, irq); + vlr.hwirq = 0; + } + vlr.state = 0; vgic_set_lr(vcpu, lr_nr, vlr); clear_bit(lr_nr, vgic_cpu->lr_used); @@ -1132,7 +1172,8 @@ static void vgic_queue_irq_to_lr(struct kvm_vcpu *vcpu, int irq, kvm_debug("Set active, clear distributor: 0x%x\n", vlr.state); vgic_irq_clear_active(vcpu, irq); vgic_update_state(vcpu->kvm); - } else if (vgic_dist_irq_is_pending(vcpu, irq)) { + } else { + WARN_ON(!vgic_dist_irq_is_pending(vcpu, irq)); vlr.state |= LR_STATE_PENDING; kvm_debug("Set pending: 0x%x\n", vlr.state); } @@ -1240,7 +1281,7 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; struct vgic_dist *dist = &vcpu->kvm->arch.vgic; unsigned long *pa_percpu, *pa_shared; - int i, vcpu_id, lr, ret; + int i, vcpu_id; int overflow = 0; int nr_shared = vgic_nr_shared_irqs(dist); @@ -1295,31 +1336,6 @@ epilog: */ clear_bit(vcpu_id, dist->irq_pending_on_cpu); } - - for (lr = 0; lr < vgic->nr_lr; lr++) { - struct vgic_lr vlr; - - if (!test_bit(lr, vgic_cpu->lr_used)) - continue; - - vlr = vgic_get_lr(vcpu, lr); - - /* - * If we have a mapping, and the virtual interrupt is - * presented to the guest (as pending or active), then we must - * set the state to active in the physical world. See - * Documentation/virtual/kvm/arm/vgic-mapped-irqs.txt. - */ - if (vlr.state & LR_HW) { - struct irq_phys_map *map; - map = vgic_irq_map_search(vcpu, vlr.irq); - - ret = irq_set_irqchip_state(map->irq, - IRQCHIP_STATE_ACTIVE, - true); - WARN_ON(ret); - } - } } static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) @@ -1421,7 +1437,7 @@ static int vgic_sync_hwirq(struct kvm_vcpu *vcpu, struct vgic_lr vlr) return 0; map = vgic_irq_map_search(vcpu, vlr.irq); - BUG_ON(!map || !map->active); + BUG_ON(!map); ret = irq_get_irqchip_state(map->irq, IRQCHIP_STATE_ACTIVE, @@ -1429,13 +1445,8 @@ static int vgic_sync_hwirq(struct kvm_vcpu *vcpu, struct vgic_lr vlr) WARN_ON(ret); - if (map->active) { - ret = irq_set_irqchip_state(map->irq, - IRQCHIP_STATE_ACTIVE, - false); - WARN_ON(ret); + if (map->active) return 0; - } return 1; } @@ -1607,8 +1618,12 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid, } else { if (level_triggered) { vgic_dist_irq_clear_level(vcpu, irq_num); - if (!vgic_dist_irq_soft_pend(vcpu, irq_num)) + if (!vgic_dist_irq_soft_pend(vcpu, irq_num)) { vgic_dist_irq_clear_pending(vcpu, irq_num); + vgic_cpu_irq_clear(vcpu, irq_num); + if (!compute_pending_for_cpu(vcpu)) + clear_bit(cpuid, dist->irq_pending_on_cpu); + } } ret = false; |