From: Nicolas Dechesne Date: Thu, 23 Jun 2016 20:35:25 +0000 (+0200) Subject: Merge remote-tracking branch 'todor/release/qcomlt-4.4-camss-demo2' into release... X-Git-Tag: KARO-TXSD-2017-03-24~19 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=80c31274bc5e98728c2f22fb80e3d07c08d109fa;hp=02ca998bdafb32190e7bcb40ed55a00e3f3dd4cf;p=karo-tx-linux.git Merge remote-tracking branch 'todor/release/qcomlt-4.4-camss-demo2' into release/qcomlt-4.4 --- diff --git a/.gitignore b/.gitignore index fd3a35592543..18d3c2a10a3f 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,8 @@ Module.symvers # Debian directory (make deb-pkg) # /debian/ +/*.deb +/*.changes # # tar directory (make tar*-pkg) diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt index 5904902862de..87d3714b956a 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt @@ -11,9 +11,7 @@ Required properties : compatible "qcom,rpmcc" should be also included. "qcom,rpmcc-msm8916", "qcom,rpmcc" - "qcom,rpmcc-msm8974", "qcom,rpmcc" "qcom,rpmcc-apq8064", "qcom,rpmcc" - "qcom,rpmcc-apq8084", "qcom,rpmcc" - #clock-cells : shall contain 1 @@ -30,7 +28,7 @@ Example: compatible = "qcom,rpm-msm8916"; qcom,smd-channels = "rpm_requests"; - rpmcc: qcom,rpmcc { + rpmcc: clock-controller { compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc"; #clock-cells = <1>; }; diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd.txt b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd.txt new file mode 100644 index 000000000000..4e3826db143e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd.txt @@ -0,0 +1,101 @@ +msm8916 audio CODEC aka Tombak audio CODEC + +Codec IP is divided into two parts, first analog which is integrated in pmic pm8916 +and secondly digital part which is integrated into application processor. Codec register +controls are also split across pmic an lpass. Analog part is controlled via spmi bus to pmic. + +## Bindings for codec core on pmic: + +Required properties + - compatible = "qcom,msm8916-wcd-codec"; + - reg: represents the slave base address provided to the peripheral. + - interrupt-parent : The parent interrupt controller. + - interrupts: List of interrupts in given SPMI peripheral. + - interrupt-names: Names specified to above list of interrupts in same + order. List of supported interrupt names are: + "spk_cnp_int" - Speaker click and pop interrupt + "spk_clip_int" - Speaker clip interrupt + "spk_ocp_int" - Speaker over current protect interrupt. + "ins_rem_det1" - jack insert removal detect interrupt 1. + "but_rel_det" - button release interrupt + "but_press_det" - button press event + "ins_rem_det" - jack insert removal detect interrup + "mbhc_int" - multi button headset interrupt. + "ear_ocp_int" - Earphone over current protect interrupt. + "hphr_ocp_int" - Headphone R over current protect interrupt. + "hphl_ocp_det" - Headphone L over current protect interrupt + "ear_cnp_int" - earphone cnp interrupt. + "hphr_cnp_int" - hphr click and pop interrupt. + "hphl_cnp_int" - hphl click and pop interrupt + + - vddio-supply: phandle to VDD_CDC_IO regulator device tree node. + - vdd-tx-rx-supply: phandle to VDD_CDC_TX/RX/CX regulator device tree node. + - vdd-micbias-supply: phandle of VDD_MICBIAS supply's regulator device tree + node. +- qcom,lpass-codec-core: phandle to syscon node of lpass code core. + +Optional Properties: +- qcom,micbias1-ext-cap: present if micbias1 has external capacitor connected. +- qcom,micbias2-ext-cap: present if micbias2 has external capacitor connected. + +## Bindings codec core on lpass: + +Required properties + - compatible: should be "qcom,msm8916-lpass-codec" followed by "syscon". + - reg: represents the lpass codec core register map. + +Example: + +spmi_bus { + ... + msm8916_wcd_codec@f000{ + compatible = "qcom,msm8916-wcd-codec"; + reg = <0xf000 0x200>; + reg-names = "pmic-codec-core"; + clocks = <&gcc GCC_CODEC_DIGCODEC_CLK>; + clock-names = "mclk"; + interrupt-parent = <&spmi_bus>; + interrupts = <0x1 0xf0 0x0 IRQ_TYPE_NONE>, + <0x1 0xf0 0x1 IRQ_TYPE_NONE>, + <0x1 0xf0 0x2 IRQ_TYPE_NONE>, + <0x1 0xf0 0x3 IRQ_TYPE_NONE>, + <0x1 0xf0 0x4 IRQ_TYPE_NONE>, + <0x1 0xf0 0x5 IRQ_TYPE_NONE>, + <0x1 0xf0 0x6 IRQ_TYPE_NONE>, + <0x1 0xf0 0x7 IRQ_TYPE_NONE>, + <0x1 0xf1 0x0 IRQ_TYPE_NONE>, + <0x1 0xf1 0x1 IRQ_TYPE_NONE>, + <0x1 0xf1 0x2 IRQ_TYPE_NONE>, + <0x1 0xf1 0x3 IRQ_TYPE_NONE>, + <0x1 0xf1 0x4 IRQ_TYPE_NONE>, + <0x1 0xf1 0x5 IRQ_TYPE_NONE>; + interrupt-names = "spk_cnp_int", + "spk_clip_int", + "spk_ocp_int", + "ins_rem_det1", + "but_rel_det", + "but_press_det", + "ins_rem_det", + "mbhc_int", + "ear_ocp_int", + "hphr_ocp_int", + "hphl_ocp_det", + "ear_cnp_int", + "hphr_cnp_int", + "hphl_cnp_int"; + vddio-supply = <&pm8916_l5>; + vdd-tx-rx-supply = <&pm8916_l5>; + vdd-micbias-supply = <&pm8916_l13>; + qcom,lpass-codec-core = <&lpass_codec_core>; + #sound-dai-cells = <1>; + }; +}; + +soc { + ... + lpass_codec_core: lpass-codec{ + compatible = "qcom,msm8916-lpass-codec", "syscon"; + reg = <0x0771c000 0x400>; + }; + +}; diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 11c676eb8dc3..f0581b00b479 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -500,7 +500,7 @@ dtb-$(CONFIG_ARCH_ORION5X) += \ dtb-$(CONFIG_ARCH_PRIMA2) += \ prima2-evb.dtb dtb-$(CONFIG_ARCH_QCOM) += \ - qcom-apq8064-arrow-db600c.dtb \ + qcom-apq8064-arrow-sd-600eval.dtb \ qcom-apq8064-eI_ERAGON600.dtb \ qcom-apq8064-cm-qs600.dtb \ qcom-apq8064-ifc6410.dtb \ diff --git a/arch/arm/boot/dts/qcom-apq8064-arrow-db600c-pins.dtsi b/arch/arm/boot/dts/qcom-apq8064-arrow-sd-600eval-pins.dtsi similarity index 70% rename from arch/arm/boot/dts/qcom-apq8064-arrow-db600c-pins.dtsi rename to arch/arm/boot/dts/qcom-apq8064-arrow-sd-600eval-pins.dtsi index a3efb9704fcd..b6f2444047d2 100644 --- a/arch/arm/boot/dts/qcom-apq8064-arrow-db600c-pins.dtsi +++ b/arch/arm/boot/dts/qcom-apq8064-arrow-sd-600eval-pins.dtsi @@ -39,6 +39,25 @@ bias-disable; }; }; + + wcnss_pin_a: wcnss { + bt { + function = "riva_bt"; + pins = "gpio16", "gpio17"; + }; + + fm { + function = "riva_fm"; + pins = "gpio14", "gpio15"; + }; + + wlan { + function = "riva_wlan"; + pins = "gpio64", "gpio65", "gpio66", "gpio67", "gpio68"; + drive-strength = <6>; + bias-pull-up; + }; + }; }; &pm8921_mpps { diff --git a/arch/arm/boot/dts/qcom-apq8064-arrow-db600c.dts b/arch/arm/boot/dts/qcom-apq8064-arrow-sd-600eval.dts similarity index 89% rename from arch/arm/boot/dts/qcom-apq8064-arrow-db600c.dts rename to arch/arm/boot/dts/qcom-apq8064-arrow-sd-600eval.dts index e12d7f55f5cd..10b7f570a453 100644 --- a/arch/arm/boot/dts/qcom-apq8064-arrow-db600c.dts +++ b/arch/arm/boot/dts/qcom-apq8064-arrow-sd-600eval.dts @@ -1,10 +1,11 @@ #include "qcom-apq8064-v2.0.dtsi" -#include "qcom-apq8064-arrow-db600c-pins.dtsi" +#include "qcom-apq8064-arrow-sd-600eval-pins.dtsi" #include +#include / { - model = "Arrow Electronics, APQ8064 DB600c"; - compatible = "arrow,db600c", "qcom,apq8064"; + model = "Arrow Electronics, APQ8064 SD_600eval"; + compatible = "arrow,sd_600eval", "qcom,apq8064"; aliases { serial0 = &gsbi7_serial; @@ -38,6 +39,16 @@ }; + smd { + q6@1 { + status = "ok"; + }; + + riva@6 { + status = "ok"; + }; + }; + soc { rpm@108000 { regulators { @@ -73,6 +84,14 @@ bias-pull-down; }; + s2 { + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <1300000>; + qcom,switch-mode-frequency = <1600000>; + bias-pull-down; + regulator-always-on; + }; + s3 { regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1400000>; @@ -82,7 +101,8 @@ s4 { regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; - qcom,switch-mode-frequency = <3200000>; + qcom,switch-mode-frequency = <1600000>; + qcom,force-mode = ; bias-pull-down; regulator-always-on; }; @@ -125,12 +145,18 @@ bias-pull-down; }; + l26 { + regulator-min-microvolt = < 375000>; + regulator-max-microvolt = <1050000>; + bias-pull-down; + }; lvs6 { bias-pull-down; }; lvs7 { bias-pull-down; + regulator-always-on; }; }; }; @@ -288,6 +314,13 @@ target-supply = <&pm8921_lvs7>; }; + wcnss@3204000 { + status = "ok"; + + pinctrl-names = "default"; + pinctrl-0 = <&wcnss_pin_a>; + }; + /* OTG */ phy@12500000 { status = "okay"; @@ -327,6 +360,10 @@ usb@12530000 { status = "okay"; }; + pil_q6v4: pil@28800000 { + qcom,pll-supply = <&pm8921_l26>; + qcom,pll-uV = <1050000>; + }; hdmi: qcom,hdmi-tx@4a00000 { status = "okay"; diff --git a/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts index 91e497adb5bb..5b6d7072acb6 100644 --- a/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts +++ b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts @@ -39,6 +39,12 @@ }; }; + smd { + q6@1 { + status = "okay"; + }; + }; + soc { pinctrl@800000 { card_detect: card_detect { diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi index 5b46c1054b75..ac4e7f91db2d 100644 --- a/arch/arm/boot/dts/qcom-apq8064.dtsi +++ b/arch/arm/boot/dts/qcom-apq8064.dtsi @@ -23,6 +23,11 @@ reg = <0x80000000 0x200000>; no-map; }; + + wcnss_mem: wcnss@8f000000 { + reg = <0x8f000000 0x700000>; + no-map; + }; }; cpus { @@ -122,7 +127,7 @@ polling-delay-passive = <250>; polling-delay = <1000>; - thermal-sensors = <&tsens 7>; + thermal-sensors = <&gcc 7>; trips { cpu_alert0: trip@0 { @@ -149,7 +154,7 @@ polling-delay-passive = <250>; polling-delay = <1000>; - thermal-sensors = <&tsens 8>; + thermal-sensors = <&gcc 8>; trips { cpu_alert1: trip@0 { @@ -176,7 +181,7 @@ polling-delay-passive = <250>; polling-delay = <1000>; - thermal-sensors = <&tsens 9>; + thermal-sensors = <&gcc 9>; trips { cpu_alert2: trip@0 { @@ -203,7 +208,7 @@ polling-delay-passive = <250>; polling-delay = <1000>; - thermal-sensors = <&tsens 10>; + thermal-sensors = <&gcc 10>; trips { cpu_alert3: trip@0 { @@ -227,88 +232,6 @@ }; }; - thermal-zones { - cpu-thermal0 { - polling-delay-passive = <250>; - polling-delay = <1000>; - - thermal-sensors = <&gcc 7>; - - trips { - cpu_alert0: trip@0 { - temperature = <75000>; - hysteresis = <2000>; - type = "passive"; - }; - cpu_crit0: trip@1 { - temperature = <95000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - }; - - cpu-thermal1 { - polling-delay-passive = <250>; - polling-delay = <1000>; - - thermal-sensors = <&gcc 8>; - - trips { - cpu_alert1: trip@0 { - temperature = <75000>; - hysteresis = <2000>; - type = "passive"; - }; - cpu_crit1: trip@1 { - temperature = <95000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - }; - - cpu-thermal2 { - polling-delay-passive = <250>; - polling-delay = <1000>; - - thermal-sensors = <&gcc 9>; - - trips { - cpu_alert2: trip@0 { - temperature = <75000>; - hysteresis = <2000>; - type = "passive"; - }; - cpu_crit2: trip@1 { - temperature = <95000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - }; - - cpu-thermal3 { - polling-delay-passive = <250>; - polling-delay = <1000>; - - thermal-sensors = <&gcc 10>; - - trips { - cpu_alert3: trip@0 { - temperature = <75000>; - hysteresis = <2000>; - type = "passive"; - }; - cpu_crit3: trip@1 { - temperature = <95000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - }; - }; - cpu-pmu { compatible = "qcom,krait-pmu"; interrupts = <1 10 0x304>; @@ -792,6 +715,124 @@ }; }; + smd { + compatible = "qcom,smd"; + + modem@0 { + interrupts = <0 37 IRQ_TYPE_EDGE_RISING>; + + qcom,ipc = <&l2cc 8 3>; + qcom,smd-edge = <0>; + + status = "disabled"; + }; + + q6@1 { + interrupts = <0 90 IRQ_TYPE_EDGE_RISING>; + + qcom,ipc = <&l2cc 8 15>; + qcom,smd-edge = <1>; + + status = "disabled"; + + apr { + compatible = "qcom,apr"; + qcom,smd-channels = "apr_audio_svc"; + rproc = <&pil_q6v4>; + }; + }; + + dsps@3 { + interrupts = <0 138 IRQ_TYPE_EDGE_RISING>; + + qcom,ipc = <&sps_sic_non_secure 0x4080 0>; + qcom,smd-edge = <3>; + + status = "disabled"; + }; + + riva@6 { + interrupts = <0 198 IRQ_TYPE_EDGE_RISING>; + + qcom,ipc = <&l2cc 8 25>; + qcom,smd-edge = <6>; + + status = "disabled"; + + wcnss { + compatible = "qcom,wcnss"; + qcom,smd-channels = "WCNSS_CTRL"; + + qcom,mmio = <&riva>; + + bt { + compatible = "qcom,wcnss-bt"; + }; + + wifi { + compatible = "qcom,wcnss-wlan"; + + interrupts = <0 203 0>, <0 202 0>; + interrupt-names = "tx", "rx"; + + qcom,state = <&apps_smsm 10>, <&apps_smsm 9>; + qcom,state-names = "tx-enable", "tx-rings-empty"; + + local-mac-address = [ 18 00 2d 88 9c a9 ]; + }; + }; + }; + }; + + smsm { + compatible = "qcom,smsm"; + + #address-cells = <1>; + #size-cells = <0>; + + qcom,ipc-1 = <&l2cc 8 4>; + qcom,ipc-2 = <&l2cc 8 14>; + qcom,ipc-3 = <&l2cc 8 23>; + qcom,ipc-4 = <&sps_sic_non_secure 0x4094 0>; + + apps_smsm: apps@0 { + reg = <0>; + #qcom,state-cells = <1>; + }; + + modem_smsm: modem@1 { + reg = <1>; + interrupts = <0 38 IRQ_TYPE_EDGE_RISING>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + q6_smsm: q6@2 { + reg = <2>; + interrupts = <0 89 IRQ_TYPE_EDGE_RISING>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + wcnss_smsm: wcnss@3 { + reg = <3>; + interrupts = <0 204 IRQ_TYPE_EDGE_RISING>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + dsps_smsm: dsps@4 { + reg = <4>; + interrupts = <0 137 IRQ_TYPE_EDGE_RISING>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + soc: soc { #address-cells = <1>; #size-cells = <1>; @@ -907,6 +948,11 @@ regulator-max-microvolt = <1250000>; }; + sps_sic_non_secure: sps-sic-non-secure@12100000 { + compatible = "syscon"; + reg = <0x12100000 0x10000>; + }; + gsbi1: gsbi@12440000 { status = "disabled"; compatible = "qcom,gsbi-v1.0.0"; @@ -1328,6 +1374,37 @@ }; }; + riva: wcnss@3204000 { + compatible = "qcom,riva-pil", "qcom,riva"; + reg = <0x03200800 0x1000>, <0x03202000 0x2000>, <0x03204000 0x100>; + reg-names = "ccu", "dxe", "pmu"; + + interrupts-extended = <&intc 0 199 IRQ_TYPE_EDGE_RISING>, + <&wcnss_smsm 6 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog", "fatal"; + + memory-region = <&wcnss_mem>; + + vddcx-supply = <&pm8921_s3>; + vddmx-supply = <&pm8921_l24>; + vddpx-supply = <&pm8921_s4>; + + status = "disabled"; + + iris { + compatible = "qcom,wcn3660"; + + clocks = <&rpmcc 9>; + clock-names = "xo"; + + vddxo-supply = <&pm8921_l4>; + vddrfa-supply = <&pm8921_s2>; + vddpa-supply = <&pm8921_l10>; + vdddig-supply = <&pm8921_lvs2>; + }; + }; + + usb1_phy: phy@12500000 { compatible = "qcom,usb-otg-ci"; reg = <0x12500000 0x400>; @@ -1603,21 +1680,6 @@ qcom,pas-id = <1>; /* PAS_Q6 */ }; - smd { - compatible = "qcom,smd"; - adsp_a11 { - interrupts = <0 90 IRQ_TYPE_EDGE_RISING>; - qcom,ipc = <&l2cc 8 15>; - qcom,smd-edge = <1>; - qcom,remote-pid = <0x2>; - q6_requests { - compatible = "qcom,apr"; - qcom,smd-channels = "apr_audio_svc"; - rproc = <&pil_q6v4>; - }; - }; - }; - dai_fe: dai_fe { compatible = "qcom,msm-dai-fe"; #sound-dai-cells = <0>; diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig index fa04d74c2ea1..92d69dcae623 100644 --- a/arch/arm/configs/qcom_defconfig +++ b/arch/arm/configs/qcom_defconfig @@ -73,6 +73,7 @@ CONFIG_BT_BNEP_PROTO_FILTER=y CONFIG_BT_HIDP=y CONFIG_BT_HCIUART=y CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_QCOMSMD=y CONFIG_CFG80211=m CONFIG_CFG80211_WEXT=y CONFIG_MAC80211=m @@ -113,6 +114,8 @@ CONFIG_USB_USBNET=y CONFIG_ATH_CARDS=m CONFIG_ATH6KL=m CONFIG_ATH6KL_SDIO=m +CONFIG_WCN36XX=m +CONFIG_WCN36XX_DEBUGFS=y CONFIG_INPUT_EVDEV=y # CONFIG_KEYBOARD_ATKBD is not set # CONFIG_MOUSE_PS2 is not set @@ -246,7 +249,6 @@ CONFIG_USB_CHIPIDEA_HOST=y CONFIG_USB_SERIAL=y CONFIG_USB_MSM_OTG=y CONFIG_USB_GADGET=y -CONFIG_USB_GADGET_DEBUG_FILES=y CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_ETH=m CONFIG_USB_GADGETFS=m @@ -278,12 +280,17 @@ CONFIG_KPSS_XCC=y CONFIG_KRAITCC=y CONFIG_HWSPINLOCK_QCOM=y CONFIG_MSM_IOMMU=y +CONFIG_QCOM_Q6V5_PIL=y CONFIG_QCOM_TZ_PIL=y +CONFIG_QCOM_WCNSS_PIL=y CONFIG_QCOM_GSBI=y CONFIG_QCOM_PM=y CONFIG_QCOM_SMEM=y CONFIG_QCOM_SMD=y CONFIG_QCOM_SMD_RPM=y +CONFIG_QCOM_SMP2P=y +CONFIG_QCOM_SMSM=y +CONFIG_QCOM_WCNSS_CTRL=y CONFIG_PHY_QCOM_APQ8064_SATA=y CONFIG_PHY_QCOM_IPQ806X_SATA=y CONFIG_NVMEM=y diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi index ab0df21a877f..aeed8161fc05 100644 --- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi +++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi @@ -48,22 +48,22 @@ s1 { regulator-min-microvolt = <375000>; regulator-max-microvolt = <1562000>; - + }; s2 { regulator-min-microvolt = <375000>; regulator-max-microvolt = <1562000>; - + }; s3 { regulator-min-microvolt = <375000>; regulator-max-microvolt = <1562000>; - + }; s4 { regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; - + regulator-always-on; regulator-boot-on; }; @@ -71,92 +71,92 @@ l1 { regulator-min-microvolt = <375000>; regulator-max-microvolt = <1525000>; - + }; l2 { regulator-min-microvolt = <375000>; regulator-max-microvolt = <1525000>; - + }; l3 { regulator-min-microvolt = <375000>; regulator-max-microvolt = <1525000>; - + }; l4 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l5 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l6 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l7 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l8 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l9 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l10 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l11 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l12 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l13 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l14 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l15 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l16 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l17 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; l18 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; - + }; }; }; @@ -404,7 +404,7 @@ status = "okay"; clocks = <&gcc GCC_CODEC_DIGCODEC_CLK>; clock-names = "mclk"; - digital = <&wcd_digital>; + qcom,lpass-codec-core = <&lpass_codec_core>; }; /* Internal Codec @@ -426,16 +426,10 @@ pinctrl-names = "default", "sleep"; qcom,model = "DB410c"; qcom,audio-routing = - "MIC BIAS External", "Handset Mic", - "MIC BIAS Internal2", "Headset Mic", - "MIC BIAS External", "Secondary Mic", "AMIC1", "MIC BIAS External", "AMIC2", "MIC BIAS Internal2", "AMIC3", "MIC BIAS External", - "DMIC1", "MIC BIAS Internal1", - "MIC BIAS Internal1", "Digital Mic1", - "DMIC2", "MIC BIAS Internal1", - "MIC BIAS Internal1", "Digital Mic2"; + "DMIC1", "MIC BIAS Internal1"; /* External Primary or External Secondary -ADV7533 HDMI */ external-dai-link@0 { diff --git a/arch/arm64/boot/dts/qcom/msm8916-bus.dtsi b/arch/arm64/boot/dts/qcom/msm8916-bus.dtsi index 77f76fd37fa4..11e707cef476 100644 --- a/arch/arm64/boot/dts/qcom/msm8916-bus.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916-bus.dtsi @@ -95,8 +95,8 @@ qcom,qos-off = <0x1000>; qcom,bus-type = <1>; clock-names = "bus_clk", "bus_a_clk"; - clocks = <&rpmcc RPM_SNOC_CLK>, - <&rpmcc RPM_SNOC_A_CLK>; + clocks = <&rpmcc RPM_SMD_SNOC_CLK>, + <&rpmcc RPM_SMD_SNOC_A_CLK>; }; fab_bimc: fab-bimc { @@ -106,8 +106,8 @@ qcom,base-name = "bimc-base"; qcom,bus-type = <2>; clock-names = "bus_clk", "bus_a_clk"; - clocks = <&rpmcc RPM_BIMC_CLK>, - <&rpmcc RPM_BIMC_A_CLK>; + clocks = <&rpmcc RPM_SMD_BIMC_CLK>, + <&rpmcc RPM_SMD_BIMC_A_CLK>; }; fab_pnoc: fab-pnoc { @@ -119,8 +119,8 @@ qcom,qos-delta = <0x1000>; qcom,bus-type = <1>; clock-names = "bus_clk", "bus_a_clk"; - clocks = <&rpmcc RPM_PCNOC_CLK>, - <&rpmcc RPM_PCNOC_A_CLK>; + clocks = <&rpmcc RPM_SMD_PCNOC_CLK>, + <&rpmcc RPM_SMD_PCNOC_A_CLK>; }; /* SNOC Devices */ diff --git a/arch/arm64/boot/dts/qcom/msm8916-coresight.dtsi b/arch/arm64/boot/dts/qcom/msm8916-coresight.dtsi index c008dc7a32bb..9cac742817aa 100644 --- a/arch/arm64/boot/dts/qcom/msm8916-coresight.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916-coresight.dtsi @@ -17,7 +17,7 @@ compatible = "arm,coresight-tpiu", "arm,primecell"; reg = <0x820000 0x1000>; - clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; + clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk"; port { @@ -32,7 +32,7 @@ compatible = "arm,coresight-funnel", "arm,primecell"; reg = <0x821000 0x1000>; - clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; + clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk"; ports { @@ -69,7 +69,7 @@ compatible = "qcom,coresight-replicator1x", "arm,primecell"; reg = <0x824000 0x1000>; - clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; + clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk"; ports { @@ -102,7 +102,7 @@ compatible = "arm,coresight-tmc", "arm,primecell"; reg = <0x825000 0x1000>; - clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; + clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk"; ports { @@ -129,7 +129,7 @@ compatible = "arm,coresight-tmc", "arm,primecell"; reg = <0x826000 0x1000>; - clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; + clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk"; port { @@ -144,7 +144,7 @@ compatible = "arm,coresight-funnel", "arm,primecell"; reg = <0x841000 0x1000>; - clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; + clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk"; ports { @@ -192,7 +192,7 @@ compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85c000 0x1000>; - clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; + clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk"; cpu = <&CPU0>; @@ -208,7 +208,7 @@ compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85d000 0x1000>; - clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; + clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk"; cpu = <&CPU1>; @@ -224,7 +224,7 @@ compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85e000 0x1000>; - clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; + clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk"; cpu = <&CPU2>; @@ -240,7 +240,7 @@ compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85f000 0x1000>; - clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; + clocks = <&rpmcc RPM_SMD_QDSS_CLK>, <&rpmcc RPM_SMD_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk"; cpu = <&CPU3>; diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 29a4774f757d..f6091aafc984 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -71,15 +71,30 @@ reg = <0x0 0x86800000 0x0 0x04800000>; }; + rmtfs@86700000 { + reg = <0x0 0x86700000 0x0 0xe0000>; + no-map; + }; + peripheral_mem: peripheral_region@8b600000 { no-map; reg = <0x0 0x8b600000 0x0 0x0600000>; }; + wcnss_mem: wcnss@89300000 { + reg = <0x0 0x89300000 0x0 0x600000>; + no-map; + }; + vidc_mem: vidc_region@8f800000 { no-map; reg = <0 0x8f800000 0 0x800000>; }; + + mba_mem: mba@8ea00000 { + no-map; + reg = <0 0x8ea00000 0 0x100000>; + }; }; cpus { @@ -243,10 +258,11 @@ firmware { compatible = "simple-bus"; - scm { + scm: scm { compatible = "qcom,scm"; clocks = <&gcc GCC_CRYPTO_CLK> , <&gcc GCC_CRYPTO_AXI_CLK>, <&gcc GCC_CRYPTO_AHB_CLK>; clock-names = "core", "bus", "iface"; + #reset-cells = <1>; }; }; @@ -880,8 +896,8 @@ /* Audio */ - wcd_digital: codec-digital{ - compatible = "syscon", "qcom,apq8016-wcd-digital-codec"; + lpass_codec_core: lpass-codec{ + compatible = "syscon", "qcom,msm8916-lpass-codec"; reg = <0x0771c000 0x400>; }; @@ -972,197 +988,78 @@ #thermal-sensor-cells = <1>; }; - q6-smp2p { - compatible = "qcom,smp2p"; - qcom,smem = <435>, <428>; - interrupts = <0 27 1>; - qcom,ipc = <&apcs 8 14>; - - qcom,local-pid = <0>; - qcom,remote-pid = <1>; - - q6_smp2p_out: master-kernel { - qcom,entry-name = "master-kernel"; - qcom,outbound; - - gpio-controller; - #gpio-cells = <2>; - }; + hexagon@4080000 { + compatible = "qcom,pil-q6v56-mss", "qcom,q6v5-pil"; + reg = <0x04080000 0x100>, + <0x04020000 0x040>; - q6_smp2p_in: slave-kernel { - qcom,entry-name = "slave-kernel"; - qcom,inbound; + reg-names = "qdsp6", "rmb"; - interrupt-controller; - #interrupt-cells = <2>; - }; - }; + interrupts-extended = <&intc 0 24 1>, + <&hexagon_smp2p_in 0 0>, + <&hexagon_smp2p_in 1 0>, + <&hexagon_smp2p_in 2 0>, + <&hexagon_smp2p_in 3 0>; + interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack"; - wcnss-smp2p { - compatible = "qcom,smp2p"; - qcom,smem = <451>, <431>; + clocks = <&gcc GCC_MSS_CFG_AHB_CLK>, <&gcc GCC_MSS_Q6_BIMC_AXI_CLK>, <&gcc GCC_BOOT_ROM_AHB_CLK>; + clock-names = "iface", "bus", "mem"; - interrupts = <0 143 1>; + qcom,state = <&hexagon_smp2p_out 0>; + qcom,state-names = "stop"; - qcom,ipc = <&apcs 8 18>; + resets = <&scm 0>; + reset-names = "mss_restart"; - qcom,local-pid = <0>; - qcom,remote-pid = <4>; + mx-supply = <&pm8916_l3>; + pll-supply = <&pm8916_l7>; - wcnss_smp2p_out: master-kernel { - qcom,entry-name = "master-kernel"; - qcom,outbound; + qcom,halt-regs = <&tcsr 0x18000 0x19000 0x1a000>; - gpio-controller; - #gpio-cells = <2>; + mba { + memory-region = <&mba_mem>; }; - wcnss_smp2p_in: slave-kernel { - qcom,entry-name = "slave-kernel"; - qcom,inbound; - - interrupt-controller; - #interrupt-cells = <2>; + mpss { + memory-region = <&modem_adsp_mem>; }; }; - qcom,mss@4080000 { - compatible = "qcom,pil-q6v56-mss", "qcom,q6v5-pil"; - reg = <0x04080000 0x100>, - <0x04020000 0x040>, - <0x01810000 0x004>, - <0x01810000 0x004>, - <0x0194f000 0x010>, - <0x01950000 0x008>, - <0x01951000 0x008>; - - reg-names = "qdsp6_base", "rmb_base", "restart_reg_sec", - "halt_q6", "halt_modem", "halt_nc"; - - interrupts-extended = <&intc 0 24 1>, - <&q6_smp2p_in 0 0>, - <&q6_smp2p_in 1 0>, - <&q6_smp2p_in 2 0>, - <&q6_smp2p_in 3 0>; - interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack"; - - clocks = <&gcc GCC_MSS_CFG_AHB_CLK>, <&gcc GCC_MSS_Q6_BIMC_AXI_CLK>, <&gcc GCC_BOOT_ROM_AHB_CLK>; - - clock-names = "iface", "bus", "mem"; + pronto: wcnss@a21b000 { + compatible = "qcom,pronto-v2-pil", "qcom,pronto"; + reg = <0x0a204000 0x2000>, <0x0a202000 0x1000>, <0x0a21b000 0x3000>; + reg-names = "ccu", "dxe", "pmu"; - qcom,mx-supply = <&pm8916_l3>; - qcom,mx-uV = <1050000>; - qcom,pll-supply = <&pm8916_l7>; - qcom,pll-uV = <1800000>; - qcom,proxy-clock-names = "xo"; - qcom,active-clock-names = "iface_clk", "bus_clk", "mem_clk"; - qcom,is-loadable; - qcom,firmware-name = "modem"; - qcom,pil-self-auth; - - - /* GPIO inputs from mss */ - qcom,gpio-err-fatal = <&q6_smp2p_in 0 0>; - qcom,gpio-err-ready = <&q6_smp2p_in 1 0>; - qcom,gpio-proxy-unvote = <&q6_smp2p_in 2 0>; - qcom,gpio-stop-ack = <&q6_smp2p_in 3 0>; - qcom,gpio-ramdump-disable = <&q6_smp2p_in 15 0>; - /* GPIO output to mss */ - qcom,gpio-force-stop = <&q6_smp2p_out 0 0>; - qcom,stop-gpio = <&q6_smp2p_out 0 0>; - memory-region = <&modem_adsp_mem>; - }; - - pronto_rproc:pronto_rproc { - compatible = "qcom,tz-pil"; + memory-region = <&wcnss_mem>; - interrupts-extended = <&intc 0 149 1>, - <&wcnss_smp2p_in 0 0>, - <&wcnss_smp2p_in 1 0>, - <&wcnss_smp2p_in 2 0>, - <&wcnss_smp2p_in 3 0>; + interrupts-extended = <&intc 0 149 IRQ_TYPE_EDGE_RISING>, + <&wcnss_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, + <&wcnss_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, + <&wcnss_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, + <&wcnss_smp2p_in 3 IRQ_TYPE_EDGE_RISING>; interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack"; - clocks = <&gcc GCC_CRYPTO_CLK>, - <&gcc GCC_CRYPTO_AHB_CLK>, - <&gcc GCC_CRYPTO_AXI_CLK>, - <&gcc CRYPTO_CLK_SRC>; - clock-names = "scm_core_clk", "scm_iface_clk", "scm_bus_clk", "scm_src_clk"; - - qcom,firmware-name = "wcnss"; - qcom,pas-id = <6>; - - qcom,crash-reason = <422>; - qcom,smd-edges = <&pronto_smd_edge>; + vddmx-supply = <&pm8916_l3>; + vddpx-supply = <&pm8916_l7>; - qcom,pll-supply = <&pm8916_l7>; - qcom,pll-uV = <1800000>; - qcom,pll-uA = <18000>; - - qcom,stop-gpio = <&wcnss_smp2p_out 0 0>; + qcom,state = <&wcnss_smp2p_out 0>; + qcom,state-names = "stop"; pinctrl-names = "default"; pinctrl-0 = <&wcnss_default>; - memory-region = <&peripheral_mem>; - }; - - qcom,wcn36xx@0a000000 { - compatible = "qcom,wcn3620"; - reg = <0x0a000000 0x280000>, - <0xb011008 0x04>, - <0x0a21b000 0x3000>, - <0x03204000 0x00000100>, - <0x03200800 0x00000200>, - <0x0A100400 0x00000200>, - <0x0A205050 0x00000200>, - <0x0A219000 0x00000020>, - <0x0A080488 0x00000008>, - <0x0A080fb0 0x00000008>, - <0x0A08040c 0x00000008>, - <0x0A0120a8 0x00000008>, - <0x0A012448 0x00000008>, - <0x0A080c00 0x00000001>; - - reg-names = "wcnss_mmio", "wcnss_fiq", - "pronto_phy_base", "riva_phy_base", - "riva_ccu_base", "pronto_a2xb_base", - "pronto_ccpu_base", "pronto_saw2_base", - "wlan_tx_phy_aborts","wlan_brdg_err_source", - "wlan_tx_status", "alarms_txctl", - "alarms_tactl", "pronto_mcu_base"; - - interrupts = <0 145 0 0 146 0>; - interrupt-names = "wcnss_wlantx_irq", "wcnss_wlanrx_irq"; - - // qcom,pronto-vddmx-supply = <&pm8916_l3>; - // qcom,pronto-vddcx-supply = <&pm8916_s1_corner>; - // qcom,pronto-vddpx-supply = <&pm8916_l7>; - // qcom,iris-vddxo-supply = <&pm8916_l7>; - // qcom,iris-vddrfa-supply = <&pm8916_s3>; - // qcom,iris-vddpa-supply = <&pm8916_l9>; - // qcom,iris-vdddig-supply = <&pm8916_l5>; - - pinctrl-names = "wcnss_default"; - // pinctrl-names = "wcnss_default", "wcnss_sleep", - // "wcnss_gpio_default"; - pinctrl-0 = <&wcnss_default>; - // pinctrl-1 = <&wcnss_sleep>; - // pinctrl-2 = <&wcnss_gpio_default>; - - // clocks = <&rpmcc RPM_XO_CLK_SRC>, - // <&rpmcc RPM_RF_CLK2>; - //clock-names = "xo", "rf_clk"; - - rproc = <&pronto_rproc>; - qcom,has-autodetect-xo; - qcom,wlan-rx-buff-count = <512>; - qcom,is-pronto-vt; - qcom,has-pronto-hw; - // qcom,wcnss-adc_tm = <&pm8916_adc_tm>; - }; + iris { + compatible = "qcom,wcn3620"; + clocks = <&rpmcc RPM_SMD_RF_CLK2>; + clock-names = "xo"; + vddxo-supply = <&pm8916_l7>; + vddrfa-supply = <&pm8916_s3>; + vddpa-supply = <&pm8916_l9>; + vdddig-supply = <&pm8916_l5>; + }; + }; qcom,rpm-log@29dc00 { compatible = "qcom,rpm-log"; @@ -1231,7 +1128,7 @@ compatible = "qcom,rpm-msm8916"; qcom,smd-channels = "rpm_requests"; rpmcc: qcom,rpmcc { - compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc"; + compatible = "qcom,rpmcc-msm8916"; #clock-cells = <1>; }; @@ -1279,47 +1176,117 @@ }; }; - pronto_smd_edge: pronto { + pronto { interrupts = <0 142 1>; qcom,ipc = <&apcs 8 17>; qcom,smd-edge = <6>; qcom,remote-pid = <4>; - bt { - compatible = "qcom,hci-smd"; - qcom,smd-channels = "APPS_RIVA_BT_CMD", "APPS_RIVA_BT_ACL"; - qcom,smd-channel-names = "event", "data"; - }; + wcnss { + compatible = "qcom,wcnss"; + qcom,smd-channels = "WCNSS_CTRL"; - ipcrtr { - compatible = "qcom,ipcrtr"; - qcom,smd-channels = "IPCRTR"; - }; + qcom,mmio = <&pronto>; - wifi { - compatible = "qcom,wlan-ctrl"; - qcom,smd-channels = "WLAN_CTRL"; + bt { + compatible = "qcom,wcnss-bt"; + }; - interrupts = <0 145 0>, <0 146 0>; - interrupt-names = "wcnss_wlantx_irq", "wcnss_wlanrx_irq"; + wifi { + compatible = "qcom,wcnss-wlan"; - qcom,wcnss_mmio = <0xfb000000 0x21b000>; + interrupts = <0 145 0>, <0 146 0>; + interrupt-names = "tx", "rx"; - // qcom,tx-enable-gpios = <&apps_smsm 10 0>; - // qcom,tx-rings-empty-gpios = <&apps_smsm 9 0>; + qcom,state = <&apps_smsm 10>, <&apps_smsm 9>; + qcom,state-names = "tx-enable", "tx-rings-empty"; + }; }; + }; + }; - wcnss_ctrl { - compatible = "qcom,wcnss-ctrl"; - qcom,smd-channels = "WCNSS_CTRL"; + hexagon-smp2p { + compatible = "qcom,smp2p"; + qcom,smem = <435>, <428>; - qcom,wcnss_mmio = <0xfb21b000 0x3000>; - }; + interrupts = <0 27 IRQ_TYPE_EDGE_RISING>; + + qcom,ipc = <&apcs 8 14>; + + qcom,local-pid = <0>; + qcom,remote-pid = <1>; + + hexagon_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + + #qcom,state-cells = <1>; + }; + + hexagon_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + wcnss-smp2p { + compatible = "qcom,smp2p"; + qcom,smem = <451>, <431>; + + interrupts = <0 143 IRQ_TYPE_EDGE_RISING>; + + qcom,ipc = <&apcs 8 18>; + + qcom,local-pid = <0>; + qcom,remote-pid = <4>; + + wcnss_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + + #qcom,state-cells = <1>; + }; + + wcnss_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + + interrupt-controller; + #interrupt-cells = <2>; }; }; + smsm { + compatible = "qcom,smsm"; + #address-cells = <1>; + #size-cells = <0>; + + qcom,ipc-1 = <&apcs 0 13>; + qcom,ipc-6 = <&apcs 0 19>; + + apps_smsm: apps@0 { + reg = <0>; + + #qcom,state-cells = <1>; + }; + + hexagon_smsm: hexagon@1 { + reg = <1>; + interrupts = <0 26 IRQ_TYPE_EDGE_RISING>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + wcnss_smsm: wcnss@6 { + reg = <6>; + interrupts = <0 144 IRQ_TYPE_EDGE_RISING>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + }; }; &i2c_freq_100Khz { diff --git a/arch/arm64/boot/dts/qcom/pm8916.dtsi b/arch/arm64/boot/dts/qcom/pm8916.dtsi index 113ccb3fa886..48d206867029 100644 --- a/arch/arm64/boot/dts/qcom/pm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8916.dtsi @@ -219,12 +219,43 @@ }; wcd_codec: codec@f000 { - compatible = "qcom,apq8016-wcd-codec"; + compatible = "qcom,msm8916-wcd-codec"; reg = <0xf000 0x200>; - #sound-dai-cells = <1>; + reg-names = "pmic-codec-core"; + interrupt-parent = <&spmi_bus>; + interrupts = <0x1 0xf0 0x0 IRQ_TYPE_NONE>, + <0x1 0xf0 0x1 IRQ_TYPE_NONE>, + <0x1 0xf0 0x2 IRQ_TYPE_NONE>, + <0x1 0xf0 0x3 IRQ_TYPE_NONE>, + <0x1 0xf0 0x4 IRQ_TYPE_NONE>, + <0x1 0xf0 0x5 IRQ_TYPE_NONE>, + <0x1 0xf0 0x6 IRQ_TYPE_NONE>, + <0x1 0xf0 0x7 IRQ_TYPE_NONE>, + <0x1 0xf1 0x0 IRQ_TYPE_NONE>, + <0x1 0xf1 0x1 IRQ_TYPE_NONE>, + <0x1 0xf1 0x2 IRQ_TYPE_NONE>, + <0x1 0xf1 0x3 IRQ_TYPE_NONE>, + <0x1 0xf1 0x4 IRQ_TYPE_NONE>, + <0x1 0xf1 0x5 IRQ_TYPE_NONE>; + interrupt-names = "spk_cnp_int", + "spk_clip_int", + "spk_ocp_int", + "ins_rem_det1", + "but_rel_det", + "but_press_det", + "ins_rem_det", + "mbhc_int", + "ear_ocp_int", + "hphr_ocp_int", + "hphl_ocp_det", + "ear_cnp_int", + "hphr_cnp_int", + "hphl_cnp_int"; + vddio-supply = <&pm8916_l5>; - vdd-pa-supply = <&pm8916_s4>; - vdd-mic-bias-supply = <&pm8916_l13>; + vdd-tx-rx-supply = <&pm8916_l5>; + vdd-micbias-supply = <&pm8916_l13>; + #sound-dai-cells = <1>; }; }; }; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 9eb4230f513e..a344b42f3ce5 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -23,6 +23,7 @@ CONFIG_CGROUP_HUGETLB=y # CONFIG_NET_NS is not set CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y # CONFIG_COMPAT_BRK is not set CONFIG_PROFILING=y CONFIG_JUMP_LABEL=y @@ -77,6 +78,8 @@ CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y # CONFIG_INET_LRO is not set # CONFIG_IPV6 is not set +CONFIG_QRTR=y +CONFIG_QRTR_SMD=y CONFIG_BPF_JIT=y CONFIG_BT=y CONFIG_BT_RFCOMM=y @@ -153,8 +156,6 @@ CONFIG_SPMI=y CONFIG_PINCTRL_MSM8916=y CONFIG_PINCTRL_QCOM_SPMI_PMIC=y CONFIG_GPIO_PL061=y -CONFIG_GPIO_QCOM_SMSM=y -CONFIG_GPIO_QCOM_SMP2P=y CONFIG_GPIO_XGENE=y CONFIG_POWER_RESET_MSM=y CONFIG_POWER_RESET_XGENE=y @@ -186,7 +187,7 @@ CONFIG_SND=y CONFIG_SND_SOC=y CONFIG_SND_SOC_QCOM=y CONFIG_SND_SOC_APQ8016_SBC=y -CONFIG_SND_SOC_MSM8x16_WCD=y +CONFIG_SND_SOC_MSM8916_WCD=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_DYNAMIC_MINORS=y @@ -212,9 +213,6 @@ CONFIG_USB_TEST=m CONFIG_USB_HSIC_USB3503=y CONFIG_USB_MSM_OTG=y CONFIG_USB_GADGET=y -CONFIG_USB_GADGET_DEBUG=y -CONFIG_USB_GADGET_VERBOSE=y -CONFIG_USB_GADGET_DEBUG_FILES=y CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_CONFIGFS=m CONFIG_USB_ZERO=m @@ -255,11 +253,16 @@ CONFIG_HWSPINLOCK_QCOM=y CONFIG_QCOM_IOMMU_V1=y CONFIG_QCOM_Q6V5_PIL=y CONFIG_QCOM_TZ_PIL=y +CONFIG_QCOM_WCNSS_PIL=y CONFIG_QCOM_GSBI=y CONFIG_QCOM_SMEM=y CONFIG_QCOM_SMD=y CONFIG_QCOM_SMD_RPM=y +CONFIG_QCOM_SMP2P=y +CONFIG_QCOM_SMSM=y +CONFIG_QCOM_WCNSS_CTRL=y CONFIG_MSM_BUS_SCALING=y +CONFIG_QTI_LNX_GPS_PROXY=y CONFIG_BUS_TOPOLOGY_ADHOC=y CONFIG_EXTCON_USB_GPIO=y CONFIG_PHY_XGENE=y @@ -292,7 +295,6 @@ CONFIG_LOCKUP_DETECTOR=y # CONFIG_DETECT_HUNG_TASK is not set # CONFIG_SCHED_DEBUG is not set # CONFIG_DEBUG_PREEMPT is not set -CONFIG_PROVE_LOCKING=y # CONFIG_FTRACE is not set CONFIG_MEMTEST=y CONFIG_CORESIGHT=y diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index bd2aba113cfc..ab8951c40fee 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -172,6 +172,7 @@ config BT_HCIUART_QCA config BT_QCOMSMD tristate "Qualcomm SMD based HCI support" depends on QCOM_SMD + select BT_QCA help Qualcomm SMD based HCI driver. This driver is used to bridge HCI data onto the shared memory diff --git a/drivers/bluetooth/btqcomsmd.c b/drivers/bluetooth/btqcomsmd.c index 4b91c830531e..0cf759b208f2 100644 --- a/drivers/bluetooth/btqcomsmd.c +++ b/drivers/bluetooth/btqcomsmd.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016, Linaro Ltd. * Copyright (c) 2015, Sony Mobile Communications Inc. * * This program is free software; you can redistribute it and/or modify @@ -14,56 +15,55 @@ #include #include #include +#include +#include #include #include #include - -#define EDL_NVM_ACCESS_SET_REQ_CMD 0x01 -#define EDL_NVM_ACCESS_OPCODE 0xfc0b +#include "btqca.h" struct btqcomsmd { + struct hci_dev *hdev; + struct qcom_smd_channel *acl_channel; struct qcom_smd_channel *cmd_channel; }; -static int btqcomsmd_recv(struct hci_dev *hdev, - unsigned type, - const void *data, +static int btqcomsmd_recv(struct hci_dev *hdev, unsigned type, const void *data, size_t count) { struct sk_buff *skb; - void *buf; /* Use GFP_ATOMIC as we're in IRQ context */ skb = bt_skb_alloc(count, GFP_ATOMIC); - if (!skb) + if (!skb) { + hdev->stat.err_rx++; return -ENOMEM; + } bt_cb(skb)->pkt_type = type; - - /* Use io accessor as data might be ioremapped */ - buf = skb_put(skb, count); - memcpy_fromio(buf, data, count); + memcpy(skb_put(skb, count), data, count); return hci_recv_frame(hdev, skb); } -static int btqcomsmd_acl_callback(struct qcom_smd_device *qsdev, +static int btqcomsmd_acl_callback(struct qcom_smd_channel *channel, const void *data, size_t count) { - struct hci_dev *hdev = dev_get_drvdata(&qsdev->dev); + struct btqcomsmd *btq = qcom_smd_get_drvdata(channel); - return btqcomsmd_recv(hdev, HCI_ACLDATA_PKT, data, count); + btq->hdev->stat.byte_rx += count; + return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count); } -static int btqcomsmd_cmd_callback(struct qcom_smd_device *qsdev, +static int btqcomsmd_cmd_callback(struct qcom_smd_channel *channel, const void *data, size_t count) { - struct hci_dev *hdev = dev_get_drvdata(&qsdev->dev); + struct btqcomsmd *btq = qcom_smd_get_drvdata(channel); - return btqcomsmd_recv(hdev, HCI_EVENT_PKT, data, count); + return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count); } static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb) @@ -73,14 +73,16 @@ static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb) switch (bt_cb(skb)->pkt_type) { case HCI_ACLDATA_PKT: - case HCI_SCODATA_PKT: ret = qcom_smd_send(btq->acl_channel, skb->data, skb->len); + hdev->stat.acl_tx++; + hdev->stat.byte_tx += skb->len; break; case HCI_COMMAND_PKT: ret = qcom_smd_send(btq->cmd_channel, skb->data, skb->len); + hdev->stat.cmd_tx++; break; default: - ret = -ENODEV; + ret = -EILSEQ; break; } @@ -89,72 +91,53 @@ static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb) static int btqcomsmd_open(struct hci_dev *hdev) { - set_bit(HCI_RUNNING, &hdev->flags); return 0; } static int btqcomsmd_close(struct hci_dev *hdev) { - clear_bit(HCI_RUNNING, &hdev->flags); - return 0; -} - -static int btqcomsmd_set_bdaddr(struct hci_dev *hdev, - const bdaddr_t *bdaddr) -{ - struct sk_buff *skb; - u8 cmd[9]; - int err; - - cmd[0] = EDL_NVM_ACCESS_SET_REQ_CMD; - cmd[1] = 0x02; /* TAG ID */ - cmd[2] = sizeof(bdaddr_t); /* size */ - memcpy(cmd + 3, bdaddr, sizeof(bdaddr_t)); - skb = __hci_cmd_sync_ev(hdev, - EDL_NVM_ACCESS_OPCODE, - sizeof(cmd), cmd, - HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - BT_ERR("%s: Change address command failed (%d)", - hdev->name, err); - return err; - } - - kfree_skb(skb); - return 0; } -static int btqcomsmd_probe(struct qcom_smd_device *sdev) +static int btqcomsmd_probe(struct platform_device *pdev) { - struct qcom_smd_channel *acl; struct btqcomsmd *btq; struct hci_dev *hdev; + void *wcnss; int ret; - acl = qcom_smd_open_channel(sdev, - "APPS_RIVA_BT_ACL", - btqcomsmd_acl_callback); - if (IS_ERR(acl)) - return PTR_ERR(acl); - - btq = devm_kzalloc(&sdev->dev, sizeof(*btq), GFP_KERNEL); + btq = devm_kzalloc(&pdev->dev, sizeof(*btq), GFP_KERNEL); if (!btq) return -ENOMEM; - btq->acl_channel = acl; - btq->cmd_channel = sdev->channel; + wcnss = dev_get_drvdata(pdev->dev.parent); + + btq->acl_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_ACL", + btqcomsmd_acl_callback); + if (IS_ERR(btq->acl_channel)) + return PTR_ERR(btq->acl_channel); + + btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD", + btqcomsmd_cmd_callback); + if (IS_ERR(btq->cmd_channel)) + return PTR_ERR(btq->cmd_channel); + + qcom_smd_set_drvdata(btq->acl_channel, btq); + qcom_smd_set_drvdata(btq->cmd_channel, btq); hdev = hci_alloc_dev(); if (!hdev) return -ENOMEM; + hci_set_drvdata(hdev, btq); + btq->hdev = hdev; + SET_HCIDEV_DEV(hdev, &pdev->dev); + hdev->bus = HCI_SMD; hdev->open = btqcomsmd_open; hdev->close = btqcomsmd_close; hdev->send = btqcomsmd_send; - hdev->set_bdaddr = btqcomsmd_set_bdaddr; + hdev->set_bdaddr = qca_set_bdaddr_rome; ret = hci_register_dev(hdev); if (ret < 0) { @@ -162,37 +145,37 @@ static int btqcomsmd_probe(struct qcom_smd_device *sdev) return ret; } - hci_set_drvdata(hdev, btq); - dev_set_drvdata(&sdev->dev, hdev); + platform_set_drvdata(pdev, btq); return 0; } -static void btqcomsmd_remove(struct qcom_smd_device *sdev) +static int btqcomsmd_remove(struct platform_device *pdev) { - struct hci_dev *hdev = dev_get_drvdata(&sdev->dev);; + struct btqcomsmd *btq = platform_get_drvdata(pdev); - hci_unregister_dev(hdev); - hci_free_dev(hdev); + hci_unregister_dev(btq->hdev); + hci_free_dev(btq->hdev); + + return 0; } -static const struct qcom_smd_id btqcomsmd_match[] = { - { .name = "APPS_RIVA_BT_CMD" }, - {} +static const struct of_device_id btqcomsmd_of_match[] = { + { .compatible = "qcom,wcnss-bt", }, + { }, }; -static struct qcom_smd_driver btqcomsmd_cmd_driver = { +static struct platform_driver btqcomsmd_driver = { .probe = btqcomsmd_probe, .remove = btqcomsmd_remove, - .callback = btqcomsmd_cmd_callback, - .smd_match_table = btqcomsmd_match, .driver = { .name = "btqcomsmd", - .owner = THIS_MODULE, + .of_match_table = btqcomsmd_of_match, }, }; -module_qcom_smd_driver(btqcomsmd_cmd_driver); +module_platform_driver(btqcomsmd_driver); +MODULE_AUTHOR("Bjorn Andersson "); MODULE_DESCRIPTION("Qualcomm SMD HCI driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 9e850a5392d1..12fea623c189 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -12,21 +12,31 @@ config COMMON_CLK_QCOM select REGMAP_MMIO select RESET_CONTROLLER -config QCOM_CLK_SMD_RPM - tristate "RPM over SMD based Clock Controller" - depends on COMMON_CLK_QCOM && QCOM_SMD_RPM - select QCOM_RPMCC - help - Support for the clocks exposed by the Resource Power Manager - processor on devices like apq8016, apq8084 and msm8974. - config QCOM_CLK_RPM tristate "RPM based Clock Controller" depends on COMMON_CLK_QCOM && MFD_QCOM_RPM select QCOM_RPMCC help - Support for the clocks exposed by the Resource Power Manager - processor on devices like apq8064. + The RPM (Resource Power Manager) is a dedicated hardware engine for + managing the shared SoC resources in order to keep the lowest power + profile. It communicates with other hardware subsystems via shared + memory and accepts clock requests, aggregates the requests and turns + the clocks on/off or scales them on demand. + Say Y if you want to support the clocks exposed by the RPM on + platforms such as apq8064, msm8660, msm8960 etc. + +config QCOM_CLK_SMD_RPM + tristate "RPM over SMD based Clock Controller" + depends on COMMON_CLK_QCOM && QCOM_SMD_RPM + select QCOM_RPMCC + help + The RPM (Resource Power Manager) is a dedicated hardware engine for + managing the shared SoC resources in order to keep the lowest power + profile. It communicates with other hardware subsystems via shared + memory and accepts clock requests, aggregates the requests and turns + the clocks on/off or scales them on demand. + Say Y if you want to support the clocks exposed by the RPM on + platforms such as apq8016, apq8084, msm8974 etc. config APQ_GCC_8084 tristate "APQ8084 Global Clock Controller" diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 400007b13f27..93e127bca238 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -12,8 +12,6 @@ clk-qcom-y += clk-regmap-mux-div.o clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o clk-qcom-y += clk-hfpll.o clk-qcom-y += reset.o -clk-qcom-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o -clk-qcom-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o @@ -31,3 +29,6 @@ obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o obj-$(CONFIG_QCOM_HFPLL) += hfpll.o obj-$(CONFIG_KRAITCC) += krait-cc.o obj-$(CONFIG_QCOM_A53) += clk-a53.o +obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o +obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o +obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o diff --git a/drivers/clk/qcom/clk-rpm.c b/drivers/clk/qcom/clk-rpm.c index 416aa68339d8..05cf83c2ca83 100644 --- a/drivers/clk/qcom/clk-rpm.c +++ b/drivers/clk/qcom/clk-rpm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2016, Linaro Limited * Copyright (c) 2014, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public @@ -12,7 +12,6 @@ * GNU General Public License for more details. */ -#include #include #include #include @@ -25,13 +24,137 @@ #include #include -#include "clk-rpm.h" #include +#include + +#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63 +#define QCOM_RPM_SCALING_ENABLE_ID 0x2 + +#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \ + static struct clk_rpm _platform##_##_active; \ + static struct clk_rpm _platform##_##_name = { \ + .rpm_clk_id = (r_id), \ + .peer = &_platform##_##_active, \ + .rate = INT_MAX, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "pxo_board" }, \ + .num_parents = 1, \ + }, \ + }; \ + static struct clk_rpm _platform##_##_active = { \ + .rpm_clk_id = (r_id), \ + .peer = &_platform##_##_name, \ + .active_only = true, \ + .rate = INT_MAX, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_ops, \ + .name = #_active, \ + .parent_names = (const char *[]){ "pxo_board" }, \ + .num_parents = 1, \ + }, \ + } + +#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \ + static struct clk_rpm _platform##_##_active; \ + static struct clk_rpm _platform##_##_name = { \ + .rpm_clk_id = (r_id), \ + .active_only = true, \ + .peer = &_platform##_##_active, \ + .rate = (r), \ + .branch = true, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_branch_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "pxo_board" }, \ + .num_parents = 1, \ + }, \ + }; \ + static struct clk_rpm _platform##_##_active = { \ + .rpm_clk_id = (r_id), \ + .peer = &_platform##_##_name, \ + .rate = (r), \ + .branch = true, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_branch_ops, \ + .name = #_active, \ + .parent_names = (const char *[]){ "pxo_board" }, \ + .num_parents = 1, \ + }, \ + } + +#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \ + static struct clk_rpm _platform##_##_active; \ + static struct clk_rpm _platform##_##_name = { \ + .rpm_clk_id = (r_id), \ + .peer = &_platform##_##_active, \ + .rate = (r), \ + .branch = true, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_branch_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "cxo_board" }, \ + .num_parents = 1, \ + }, \ + }; \ + static struct clk_rpm _platform##_##_active = { \ + .rpm_clk_id = (r_id), \ + .active_only = true, \ + .peer = &_platform##_##_name, \ + .rate = (r), \ + .branch = true, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_branch_ops, \ + .name = #_active, \ + .parent_names = (const char *[]){ "cxo_board" }, \ + .num_parents = 1, \ + }, \ + } #define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw) +struct clk_rpm { + const int rpm_clk_id; + const bool active_only; + unsigned long rate; + bool enabled; + bool branch; + struct clk_rpm *peer; + struct clk_hw hw; + struct qcom_rpm *rpm; +}; + +struct rpm_cc { + struct qcom_rpm *rpm; + struct clk_onecell_data data; + struct clk *clks[]; +}; + +struct rpm_clk_desc { + struct clk_rpm **clks; + size_t num_clks; +}; + static DEFINE_MUTEX(rpm_clk_lock); +static int clk_rpm_handoff(struct clk_rpm *r) +{ + int ret; + u32 value = INT_MAX; + + ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, + r->rpm_clk_id, &value, 1); + if (ret) + return ret; + ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, + r->rpm_clk_id, &value, 1); + if (ret) + return ret; + + return 0; +} + static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate) { u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ @@ -40,25 +163,69 @@ static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate) r->rpm_clk_id, &value, 1); } +static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate) +{ + u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ + + return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, + r->rpm_clk_id, &value, 1); +} + +static void to_active_sleep(struct clk_rpm *r, unsigned long rate, + unsigned long *active, unsigned long *sleep) +{ + *active = rate; + + /* + * Active-only clocks don't care what the rate is during sleep. So, + * they vote for zero. + */ + if (r->active_only) + *sleep = 0; + else + *sleep = *active; +} + static int clk_rpm_prepare(struct clk_hw *hw) { struct clk_rpm *r = to_clk_rpm(hw); - unsigned long rate = r->rate; + struct clk_rpm *peer = r->peer; + unsigned long this_rate = 0, this_sleep_rate = 0; + unsigned long peer_rate = 0, peer_sleep_rate = 0; + unsigned long active_rate, sleep_rate; int ret = 0; mutex_lock(&rpm_clk_lock); - if (!rate) + /* Don't send requests to the RPM if the rate has not been set. */ + if (!r->rate) goto out; - if (r->branch) - rate = !!rate; + to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); + + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) + to_active_sleep(peer, peer->rate, + &peer_rate, &peer_sleep_rate); - ret = clk_rpm_set_rate_active(r, rate); + active_rate = max(this_rate, peer_rate); + + if (r->branch) + active_rate = !!active_rate; + ret = clk_rpm_set_rate_active(r, active_rate); if (ret) goto out; + sleep_rate = max(this_sleep_rate, peer_sleep_rate); + if (r->branch) + sleep_rate = !!sleep_rate; + + ret = clk_rpm_set_rate_sleep(r, sleep_rate); + if (ret) + /* Undo the active set vote and restore it */ + ret = clk_rpm_set_rate_active(r, peer_rate); + out: if (!ret) r->enabled = true; @@ -71,6 +238,9 @@ out: static void clk_rpm_unprepare(struct clk_hw *hw) { struct clk_rpm *r = to_clk_rpm(hw); + struct clk_rpm *peer = r->peer; + unsigned long peer_rate = 0, peer_sleep_rate = 0; + unsigned long active_rate, sleep_rate; int ret; mutex_lock(&rpm_clk_lock); @@ -78,7 +248,18 @@ static void clk_rpm_unprepare(struct clk_hw *hw) if (!r->rate) goto out; - ret = clk_rpm_set_rate_active(r, r->rate); + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) + to_active_sleep(peer, peer->rate, &peer_rate, + &peer_sleep_rate); + + active_rate = r->branch ? !!peer_rate : peer_rate; + ret = clk_rpm_set_rate_active(r, active_rate); + if (ret) + goto out; + + sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; + ret = clk_rpm_set_rate_sleep(r, sleep_rate); if (ret) goto out; @@ -89,23 +270,45 @@ out: } static int clk_rpm_set_rate(struct clk_hw *hw, - unsigned long rate, unsigned long parent_rate) + unsigned long rate, unsigned long parent_rate) { struct clk_rpm *r = to_clk_rpm(hw); + struct clk_rpm *peer = r->peer; + unsigned long active_rate, sleep_rate; + unsigned long this_rate = 0, this_sleep_rate = 0; + unsigned long peer_rate = 0, peer_sleep_rate = 0; int ret = 0; mutex_lock(&rpm_clk_lock); - if (r->enabled) - ret = clk_rpm_set_rate_active(r, rate); + if (!r->enabled) + goto out; + + to_active_sleep(r, rate, &this_rate, &this_sleep_rate); - if (!ret) - r->rate = rate; + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) + to_active_sleep(peer, peer->rate, + &peer_rate, &peer_sleep_rate); + active_rate = max(this_rate, peer_rate); + ret = clk_rpm_set_rate_active(r, active_rate); + if (ret) + goto out; + + sleep_rate = max(this_sleep_rate, peer_sleep_rate); + ret = clk_rpm_set_rate_sleep(r, sleep_rate); + if (ret) + goto out; + + r->rate = rate; + +out: mutex_unlock(&rpm_clk_lock); return ret; } + static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { @@ -130,59 +333,57 @@ static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, return r->rate; } -const struct clk_ops clk_rpm_ops = { +static const struct clk_ops clk_rpm_ops = { .prepare = clk_rpm_prepare, .unprepare = clk_rpm_unprepare, .set_rate = clk_rpm_set_rate, .round_rate = clk_rpm_round_rate, .recalc_rate = clk_rpm_recalc_rate, }; -EXPORT_SYMBOL_GPL(clk_rpm_ops); -const struct clk_ops clk_rpm_branch_ops = { +static const struct clk_ops clk_rpm_branch_ops = { .prepare = clk_rpm_prepare, .unprepare = clk_rpm_unprepare, .round_rate = clk_rpm_round_rate, .recalc_rate = clk_rpm_recalc_rate, }; -EXPORT_SYMBOL_GPL(clk_rpm_branch_ops); - -struct rpm_cc { - struct qcom_rpm *rpm; - struct clk_onecell_data data; - struct clk *clks[]; -}; - -struct rpm_clk_desc { - struct clk_rpm **clks; - size_t num_clks; -}; /* apq8064 */ -DEFINE_CLK_RPM_PXO_BRANCH(apq8064, pxo, QCOM_RPM_PXO_CLK, 27000000); -DEFINE_CLK_RPM_CXO_BRANCH(apq8064, cxo, QCOM_RPM_CXO_CLK, 19200000); -DEFINE_CLK_RPM(apq8064, afab_clk, QCOM_RPM_APPS_FABRIC_CLK); -DEFINE_CLK_RPM(apq8064, cfpb_clk, QCOM_RPM_CFPB_CLK); -DEFINE_CLK_RPM(apq8064, daytona_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); -DEFINE_CLK_RPM(apq8064, ebi1_clk, QCOM_RPM_EBI1_CLK); -DEFINE_CLK_RPM(apq8064, mmfab_clk, QCOM_RPM_MM_FABRIC_CLK); -DEFINE_CLK_RPM(apq8064, mmfpb_clk, QCOM_RPM_MMFPB_CLK); -DEFINE_CLK_RPM(apq8064, sfab_clk, QCOM_RPM_SYS_FABRIC_CLK); -DEFINE_CLK_RPM(apq8064, sfpb_clk, QCOM_RPM_SFPB_CLK); -DEFINE_CLK_RPM(apq8064, qdss_clk, QCOM_RPM_QDSS_CLK); +DEFINE_CLK_RPM_PXO_BRANCH(apq8064, pxo, pxo_a_clk, QCOM_RPM_PXO_CLK, 27000000); +DEFINE_CLK_RPM_CXO_BRANCH(apq8064, cxo, cxo_a_clk, QCOM_RPM_CXO_CLK, 19200000); +DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); +DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); +DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); +DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); +DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); +DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); +DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); +DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); +DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); static struct clk_rpm *apq8064_clks[] = { - [QCOM_RPM_PXO_CLK] = &apq8064_pxo, - [QCOM_RPM_CXO_CLK] = &apq8064_cxo, - [QCOM_RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, - [QCOM_RPM_CFPB_CLK] = &apq8064_cfpb_clk, - [QCOM_RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk, - [QCOM_RPM_EBI1_CLK] = &apq8064_ebi1_clk, - [QCOM_RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk, - [QCOM_RPM_MMFPB_CLK] = &apq8064_mmfpb_clk, - [QCOM_RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk, - [QCOM_RPM_SFPB_CLK] = &apq8064_sfpb_clk, - [QCOM_RPM_QDSS_CLK] = &apq8064_qdss_clk, + [RPM_PXO_CLK] = &apq8064_pxo, + [RPM_PXO_A_CLK] = &apq8064_pxo_a_clk, + [RPM_CXO_CLK] = &apq8064_cxo, + [RPM_CXO_A_CLK] = &apq8064_cxo_a_clk, + [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, + [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, + [RPM_CFPB_CLK] = &apq8064_cfpb_clk, + [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk, + [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk, + [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk, + [RPM_EBI1_CLK] = &apq8064_ebi1_clk, + [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk, + [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk, + [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk, + [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk, + [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk, + [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk, + [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk, + [RPM_SFPB_CLK] = &apq8064_sfpb_clk, + [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk, + [RPM_QDSS_CLK] = &apq8064_qdss_clk, + [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, }; static const struct rpm_clk_desc rpm_clk_apq8064 = { @@ -202,10 +403,10 @@ static int rpm_clk_probe(struct platform_device *pdev) struct clk *clk; struct rpm_cc *rcc; struct clk_onecell_data *data; - int ret, i; - size_t num_clks; + int ret; + size_t num_clks, i; struct qcom_rpm *rpm; - struct clk_rpm **rpm_clks; + struct clk_rpm **rpm_clks; const struct rpm_clk_desc *desc; rpm = dev_get_drvdata(pdev->dev.parent); @@ -238,6 +439,18 @@ static int rpm_clk_probe(struct platform_device *pdev) } rpm_clks[i]->rpm = rpm; + + ret = clk_rpm_handoff(rpm_clks[i]); + if (ret) + goto err; + } + + for (i = 0; i < num_clks; i++) { + if (!rpm_clks[i]) { + clks[i] = ERR_PTR(-ENOENT); + continue; + } + clk = devm_clk_register(&pdev->dev, &rpm_clks[i]->hw); if (IS_ERR(clk)) { ret = PTR_ERR(clk); @@ -252,10 +465,7 @@ static int rpm_clk_probe(struct platform_device *pdev) if (ret) goto err; - clk_prepare_enable(apq8064_afab_clk.hw.clk); - return 0; - err: dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret); return ret; diff --git a/drivers/clk/qcom/clk-rpm.h b/drivers/clk/qcom/clk-rpm.h deleted file mode 100644 index c0ac30f806b5..000000000000 --- a/drivers/clk/qcom/clk-rpm.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2015, Linaro Limited - * Copyright (c) 2014, The Linux Foundation. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - */ - -#ifndef __QCOM_CLK_RPM_H__ -#define __QCOM_CLK_RPM_H__ - -#include - -struct qcom_rpm; - -struct clk_rpm { - const int rpm_clk_id; - unsigned long rate; - bool enabled; - bool branch; - struct clk_hw hw; - struct qcom_rpm *rpm; -}; - -extern const struct clk_ops clk_rpm_ops; -extern const struct clk_ops clk_rpm_branch_ops; - -#define DEFINE_CLK_RPM(_platform, _name, r_id) \ - static struct clk_rpm _platform##_##_name = { \ - .rpm_clk_id = (r_id), \ - .rate = INT_MAX, \ - .hw.init = &(struct clk_init_data){ \ - .name = #_name, \ - .parent_names = (const char *[]){ "pxo_board" }, \ - .num_parents = 1, \ - .ops = &clk_rpm_ops, \ - }, \ - } - -#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, r_id, r) \ - static struct clk_rpm _platform##_##_name = { \ - .rpm_clk_id = (r_id), \ - .branch = true, \ - .rate = (r), \ - .hw.init = &(struct clk_init_data){ \ - .name = #_name, \ - .parent_names = (const char *[]){ "pxo_board" }, \ - .num_parents = 1, \ - .ops = &clk_rpm_branch_ops, \ - }, \ - } - -#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, r_id, r) \ - static struct clk_rpm _platform##_##_name = { \ - .rpm_clk_id = (r_id), \ - .branch = true, \ - .rate = (r), \ - .hw.init = &(struct clk_init_data){ \ - .name = #_name, \ - .parent_names = (const char *[]){ "cxo_board" }, \ - .num_parents = 1, \ - .ops = &clk_rpm_branch_ops, \ - }, \ - } -#endif diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 749798207bb8..adfb58f51351 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2016, Linaro Limited * Copyright (c) 2014, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public @@ -24,13 +24,164 @@ #include #include -#include "clk-smd-rpm.h" #include +#include + +#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 +#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 +#define QCOM_RPM_SMD_KEY_RATE 0x007a484b +#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45 +#define QCOM_RPM_SMD_KEY_STATE 0x54415453 +#define QCOM_RPM_SCALING_ENABLE_ID 0x2 + +#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \ + key) \ + static struct clk_smd_rpm _platform##_##_active; \ + static struct clk_smd_rpm _platform##_##_name = { \ + .rpm_res_type = (type), \ + .rpm_clk_id = (r_id), \ + .rpm_status_id = (stat_id), \ + .rpm_key = (key), \ + .peer = &_platform##_##_active, \ + .rate = INT_MAX, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_smd_rpm_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "xo_board" }, \ + .num_parents = 1, \ + }, \ + }; \ + static struct clk_smd_rpm _platform##_##_active = { \ + .rpm_res_type = (type), \ + .rpm_clk_id = (r_id), \ + .rpm_status_id = (stat_id), \ + .active_only = true, \ + .rpm_key = (key), \ + .peer = &_platform##_##_name, \ + .rate = INT_MAX, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_smd_rpm_ops, \ + .name = #_active, \ + .parent_names = (const char *[]){ "xo_board" }, \ + .num_parents = 1, \ + }, \ + } + +#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \ + stat_id, r, key) \ + static struct clk_smd_rpm _platform##_##_active; \ + static struct clk_smd_rpm _platform##_##_name = { \ + .rpm_res_type = (type), \ + .rpm_clk_id = (r_id), \ + .rpm_status_id = (stat_id), \ + .rpm_key = (key), \ + .branch = true, \ + .peer = &_platform##_##_active, \ + .rate = (r), \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_smd_rpm_branch_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "xo_board" }, \ + .num_parents = 1, \ + }, \ + }; \ + static struct clk_smd_rpm _platform##_##_active = { \ + .rpm_res_type = (type), \ + .rpm_clk_id = (r_id), \ + .rpm_status_id = (stat_id), \ + .active_only = true, \ + .rpm_key = (key), \ + .branch = true, \ + .peer = &_platform##_##_name, \ + .rate = (r), \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_smd_rpm_branch_ops, \ + .name = #_active, \ + .parent_names = (const char *[]){ "xo_board" }, \ + .num_parents = 1, \ + }, \ + } + +#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \ + __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ + 0, QCOM_RPM_SMD_KEY_RATE) + +#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \ + __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \ + r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE) + +#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \ + __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ + 0, QCOM_RPM_SMD_KEY_STATE) + +#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \ + __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ + QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ + QCOM_RPM_KEY_SOFTWARE_ENABLE) + +#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \ + __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ + QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ + QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY) #define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw) +struct clk_smd_rpm { + const int rpm_res_type; + const int rpm_key; + const int rpm_clk_id; + const int rpm_status_id; + const bool active_only; + bool enabled; + bool branch; + struct clk_smd_rpm *peer; + struct clk_hw hw; + unsigned long rate; + struct qcom_smd_rpm *rpm; +}; + +struct clk_smd_rpm_req { + __le32 key; + __le32 nbytes; + __le32 value; +}; + +struct rpm_cc { + struct qcom_rpm *rpm; + struct clk_onecell_data data; + struct clk *clks[]; +}; + +struct rpm_smd_clk_desc { + struct clk_smd_rpm **clks; + size_t num_clks; +}; + static DEFINE_MUTEX(rpm_smd_clk_lock); +static int clk_smd_rpm_handoff(struct clk_smd_rpm *r) +{ + int ret; + struct clk_smd_rpm_req req = { + .key = cpu_to_le32(r->rpm_key), + .nbytes = cpu_to_le32(sizeof(u32)), + .value = cpu_to_le32(INT_MAX), + }; + + ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, + r->rpm_res_type, r->rpm_clk_id, &req, + sizeof(req)); + if (ret) + return ret; + ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, + r->rpm_res_type, r->rpm_clk_id, &req, + sizeof(req)); + if (ret) + return ret; + + return 0; +} + static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r, unsigned long rate) { @@ -250,39 +401,25 @@ static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm) return 0; } -const struct clk_ops clk_smd_rpm_ops = { +static const struct clk_ops clk_smd_rpm_ops = { .prepare = clk_smd_rpm_prepare, .unprepare = clk_smd_rpm_unprepare, .set_rate = clk_smd_rpm_set_rate, .round_rate = clk_smd_rpm_round_rate, .recalc_rate = clk_smd_rpm_recalc_rate, }; -EXPORT_SYMBOL_GPL(clk_smd_rpm_ops); -const struct clk_ops clk_smd_rpm_branch_ops = { +static const struct clk_ops clk_smd_rpm_branch_ops = { .prepare = clk_smd_rpm_prepare, .unprepare = clk_smd_rpm_unprepare, .round_rate = clk_smd_rpm_round_rate, .recalc_rate = clk_smd_rpm_recalc_rate, }; -EXPORT_SYMBOL_GPL(clk_smd_rpm_branch_ops); - -struct rpm_cc { - struct qcom_rpm *rpm; - struct clk_onecell_data data; - struct clk *clks[]; -}; - -struct rpm_smd_clk_desc { - struct clk_smd_rpm **clks; - size_t num_clks; -}; /* msm8916 */ DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); -DEFINE_CLK_SMD_RPM_BRANCH(msm8916, xo, xo_a, QCOM_SMD_RPM_MISC_CLK, 0, 19200000); DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1); DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2); @@ -294,32 +431,30 @@ DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4); DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5); static struct clk_smd_rpm *msm8916_clks[] = { - [RPM_XO_CLK_SRC] = &msm8916_xo, - [RPM_XO_A_CLK_SRC] = &msm8916_xo_a, - [RPM_PCNOC_CLK] = &msm8916_pcnoc_clk, - [RPM_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk, - [RPM_SNOC_CLK] = &msm8916_snoc_clk, - [RPM_SNOC_A_CLK] = &msm8916_snoc_a_clk, - [RPM_BIMC_CLK] = &msm8916_bimc_clk, - [RPM_BIMC_A_CLK] = &msm8916_bimc_a_clk, - [RPM_QDSS_CLK] = &msm8916_qdss_clk, - [RPM_QDSS_A_CLK] = &msm8916_qdss_a_clk, - [RPM_BB_CLK1] = &msm8916_bb_clk1, - [RPM_BB_CLK1_A] = &msm8916_bb_clk1_a, - [RPM_BB_CLK2] = &msm8916_bb_clk2, - [RPM_BB_CLK2_A] = &msm8916_bb_clk2_a, - [RPM_RF_CLK1] = &msm8916_rf_clk1, - [RPM_RF_CLK1_A] = &msm8916_rf_clk1_a, - [RPM_RF_CLK2] = &msm8916_rf_clk2, - [RPM_RF_CLK2_A] = &msm8916_rf_clk2_a, - [RPM_BB_CLK1_PIN] = &msm8916_bb_clk1_pin, - [RPM_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin, - [RPM_BB_CLK2_PIN] = &msm8916_bb_clk2_pin, - [RPM_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin, - [RPM_RF_CLK1_PIN] = &msm8916_rf_clk1_pin, - [RPM_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin, - [RPM_RF_CLK2_PIN] = &msm8916_rf_clk2_pin, - [RPM_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin, + [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk, + [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk, + [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk, + [RPM_SMD_BB_CLK1] = &msm8916_bb_clk1, + [RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a, + [RPM_SMD_BB_CLK2] = &msm8916_bb_clk2, + [RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a, + [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a, + [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a, + [RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin, + [RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin, + [RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin, + [RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin, + [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin, + [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin, }; static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { @@ -327,167 +462,8 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { .num_clks = ARRAY_SIZE(msm8916_clks), }; -/* msm8974 */ -DEFINE_CLK_SMD_RPM(msm8974, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); -DEFINE_CLK_SMD_RPM(msm8974, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); -DEFINE_CLK_SMD_RPM(msm8974, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); -DEFINE_CLK_SMD_RPM(msm8974, mmssnoc_ahb_clk, mmssnoc_ahb_a_clk, QCOM_SMD_RPM_BUS_CLK, 3); -DEFINE_CLK_SMD_RPM(msm8974, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); -DEFINE_CLK_SMD_RPM(msm8974, ocmemgx_clk, ocmemgx_a_clk, QCOM_SMD_RPM_MEM_CLK, 2); -DEFINE_CLK_SMD_RPM(msm8974, gfx3d_clk_src, gfx3d_a_clk_src, QCOM_SMD_RPM_MEM_CLK, 1); -DEFINE_CLK_SMD_RPM_BRANCH(msm8974, cxo_clk_src, cxo_a_clk_src, QCOM_SMD_RPM_MISC_CLK, 0, 19200000); -DEFINE_CLK_SMD_RPM_QDSS(msm8974, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); -DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_d0, cxo_d0_a, 1); -DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_d1, cxo_d1_a, 2); -DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a0, cxo_a0_a, 4); -DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a1, cxo_a1_a, 5); -DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a2, cxo_a2_a, 6); -DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, div_clk1, div_a_clk1, 11); -DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, div_clk2, div_a_clk2, 12); -DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, diff_clk, diff_a_clk, 7); -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_d0_pin, cxo_d0_a_pin, 1); -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_d1_pin, cxo_d1_a_pin, 2); -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a0_pin, cxo_a0_a_pin, 4); -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a1_pin, cxo_a1_a_pin, 5); -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a2_pin, cxo_a2_a_pin, 6); - -static struct clk_smd_rpm *msm8974_clks[] = { - [RPM_CXO_CLK_SRC] = &msm8974_cxo_clk_src, - [RPM_CXO_A_CLK_SRC] = &msm8974_cxo_a_clk_src, - [RPM_PNOC_CLK] = &msm8974_pnoc_clk, - [RPM_PNOC_A_CLK] = &msm8974_pnoc_a_clk, - [RPM_SNOC_CLK] = &msm8974_snoc_clk, - [RPM_SNOC_A_CLK] = &msm8974_snoc_a_clk, - [RPM_BIMC_CLK] = &msm8974_bimc_clk, - [RPM_BIMC_A_CLK] = &msm8974_bimc_a_clk, - [RPM_QDSS_CLK] = &msm8974_qdss_clk, - [RPM_QDSS_A_CLK] = &msm8974_qdss_a_clk, - [RPM_CNOC_CLK] = &msm8974_cnoc_clk, - [RPM_CNOC_A_CLK] = &msm8974_cnoc_a_clk, - [RPM_MMSSNOC_AHB_CLK] = &msm8974_mmssnoc_ahb_clk, - [RPM_MMSSNOC_AHB_A_CLK] = &msm8974_mmssnoc_ahb_a_clk, - [RPM_OCMEMGX_CLK] = &msm8974_ocmemgx_clk, - [RPM_OCMEMGX_A_CLK] = &msm8974_ocmemgx_a_clk, - [RPM_GFX3D_CLK_SRC] = &msm8974_gfx3d_clk_src, - [RPM_GFX3D_A_CLK_SRC] = &msm8974_gfx3d_a_clk_src, - [RPM_CXO_D0] = &msm8974_cxo_d0, - [RPM_CXO_D0_A] = &msm8974_cxo_d0_a, - [RPM_CXO_D1] = &msm8974_cxo_d1, - [RPM_CXO_D1_A] = &msm8974_cxo_d1_a, - [RPM_CXO_A0] = &msm8974_cxo_a0, - [RPM_CXO_A0_A] = &msm8974_cxo_a0_a, - [RPM_CXO_A1] = &msm8974_cxo_a1, - [RPM_CXO_A1_A] = &msm8974_cxo_a1_a, - [RPM_CXO_A2] = &msm8974_cxo_a2, - [RPM_CXO_A2_A] = &msm8974_cxo_a2_a, - [RPM_DIV_CLK1] = &msm8974_div_clk1, - [RPM_DIV_A_CLK1] = &msm8974_div_a_clk1, - [RPM_DIV_CLK2] = &msm8974_div_clk2, - [RPM_DIV_A_CLK2] = &msm8974_div_a_clk2, - [RPM_DIFF_CLK] = &msm8974_diff_clk, - [RPM_DIFF_A_CLK] = &msm8974_diff_a_clk, - [RPM_CXO_D0_PIN] = &msm8974_cxo_d0_pin, - [RPM_CXO_D0_A_PIN] = &msm8974_cxo_d0_a_pin, - [RPM_CXO_D1_PIN] = &msm8974_cxo_d1_pin, - [RPM_CXO_D1_A_PIN] = &msm8974_cxo_d1_a_pin, - [RPM_CXO_A0_PIN] = &msm8974_cxo_a0_pin, - [RPM_CXO_A0_A_PIN] = &msm8974_cxo_a0_a_pin, - [RPM_CXO_A1_PIN] = &msm8974_cxo_a1_pin, - [RPM_CXO_A1_A_PIN] = &msm8974_cxo_a1_a_pin, - [RPM_CXO_A2_PIN] = &msm8974_cxo_a2_pin, - [RPM_CXO_A2_A_PIN] = &msm8974_cxo_a2_a_pin, -}; - -static const struct rpm_smd_clk_desc rpm_clk_msm8974 = { - .clks = msm8974_clks, - .num_clks = ARRAY_SIZE(msm8974_clks), -}; - -/* apq8084 */ -DEFINE_CLK_SMD_RPM(apq8084, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); -DEFINE_CLK_SMD_RPM(apq8084, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); -DEFINE_CLK_SMD_RPM(apq8084, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); -DEFINE_CLK_SMD_RPM(apq8084, mmssnoc_ahb_clk, mmssnoc_ahb_a_clk, QCOM_SMD_RPM_BUS_CLK, 3); -DEFINE_CLK_SMD_RPM(apq8084, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); -DEFINE_CLK_SMD_RPM(apq8084, ocmemgx_clk, ocmemgx_a_clk, QCOM_SMD_RPM_MEM_CLK, 2); -DEFINE_CLK_SMD_RPM(apq8084, gfx3d_clk_src, gfx3d_a_clk_src, QCOM_SMD_RPM_MEM_CLK, 1); -DEFINE_CLK_SMD_RPM_BRANCH(apq8084, xo_clk_src, xo_a_clk_src, QCOM_SMD_RPM_MISC_CLK, 0, 19200000); -DEFINE_CLK_SMD_RPM_QDSS(apq8084, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); - -DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, bb_clk1, bb_clk1_a, 1); -DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, bb_clk2, bb_clk2_a, 2); -DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, rf_clk1, rf_clk1_a, 4); -DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, rf_clk2, rf_clk2_a, 5); -DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, rf_clk3, rf_clk3_a, 6); -DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, diff_clk1, diff_clk1_a, 7); -DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, div_clk1, div_clk1_a, 11); -DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, div_clk2, div_clk2_a, 12); -DEFINE_CLK_SMD_RPM_XO_BUFFER(apq8084, div_clk3, div_clk3_a, 13); - -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(apq8084, bb_clk1_pin, bb_clk1_a_pin, 1); -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(apq8084, bb_clk2_pin, bb_clk2_a_pin, 2); -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(apq8084, rf_clk1_pin, rf_clk1_a_pin, 4); -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(apq8084, rf_clk2_pin, rf_clk2_a_pin, 5); -DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(apq8084, rf_clk3_pin, rf_clk3_a_pin, 6); - -static struct clk_smd_rpm *apq8084_clks[] = { - [RPM_XO_CLK_SRC] = &apq8084_xo_clk_src, - [RPM_XO_A_CLK_SRC] = &apq8084_xo_a_clk_src, - [RPM_PNOC_CLK] = &apq8084_pnoc_clk, - [RPM_PNOC_A_CLK] = &apq8084_pnoc_a_clk, - [RPM_SNOC_CLK] = &apq8084_snoc_clk, - [RPM_SNOC_A_CLK] = &apq8084_snoc_a_clk, - [RPM_BIMC_CLK] = &apq8084_bimc_clk, - [RPM_BIMC_A_CLK] = &apq8084_bimc_a_clk, - [RPM_QDSS_CLK] = &apq8084_qdss_clk, - [RPM_QDSS_A_CLK] = &apq8084_qdss_a_clk, - [RPM_CNOC_CLK] = &apq8084_cnoc_clk, - [RPM_CNOC_A_CLK] = &apq8084_cnoc_a_clk, - [RPM_MMSSNOC_AHB_CLK] = &apq8084_mmssnoc_ahb_clk, - [RPM_MMSSNOC_AHB_A_CLK] = &apq8084_mmssnoc_ahb_a_clk, - [RPM_OCMEMGX_CLK] = &apq8084_ocmemgx_clk, - [RPM_OCMEMGX_A_CLK] = &apq8084_ocmemgx_a_clk, - [RPM_GFX3D_CLK_SRC] = &apq8084_gfx3d_clk_src, - [RPM_GFX3D_A_CLK_SRC] = &apq8084_gfx3d_a_clk_src, - [RPM_BB_CLK1] = &apq8084_bb_clk1, - [RPM_BB_CLK1_A] = &apq8084_bb_clk1_a, - [RPM_BB_CLK2] = &apq8084_bb_clk2, - [RPM_BB_CLK2_A] = &apq8084_bb_clk2_a, - [RPM_RF_CLK1] = &apq8084_rf_clk1, - [RPM_RF_CLK1_A] = &apq8084_rf_clk1_a, - [RPM_RF_CLK2] = &apq8084_rf_clk2, - [RPM_RF_CLK2_A] = &apq8084_rf_clk2_a, - [RPM_RF_CLK3] = &apq8084_rf_clk3, - [RPM_RF_CLK3_A] = &apq8084_rf_clk3_a, - [RPM_DIFF_CLK1] = &apq8084_diff_clk1, - [RPM_DIFF_CLK1_A] = &apq8084_diff_clk1_a, - [RPM_DIV_CLK1] = &apq8084_div_clk1, - [RPM_DIV_CLK1_A] = &apq8084_div_clk1_a, - [RPM_DIV_CLK2] = &apq8084_div_clk2, - [RPM_DIV_CLK2_A] = &apq8084_div_clk2_a, - [RPM_DIV_CLK3] = &apq8084_div_clk3, - [RPM_DIV_CLK3_A] = &apq8084_div_clk3_a, - [RPM_BB_CLK1_PIN] = &apq8084_bb_clk1_pin, - [RPM_BB_CLK1_A_PIN] = &apq8084_bb_clk1_a_pin, - [RPM_BB_CLK2_PIN] = &apq8084_bb_clk2_pin, - [RPM_BB_CLK2_A_PIN] = &apq8084_bb_clk2_a_pin, - [RPM_RF_CLK1_PIN] = &apq8084_rf_clk1_pin, - [RPM_RF_CLK1_A_PIN] = &apq8084_rf_clk1_a_pin, - [RPM_RF_CLK2_PIN] = &apq8084_rf_clk2_pin, - [RPM_RF_CLK2_A_PIN] = &apq8084_rf_clk2_a_pin, - [RPM_RF_CLK3_PIN] = &apq8084_rf_clk3_pin, - [RPM_RF_CLK3_A_PIN] = &apq8084_rf_clk3_a_pin, -}; - -static const struct rpm_smd_clk_desc rpm_clk_apq8084 = { - .clks = apq8084_clks, - .num_clks = ARRAY_SIZE(apq8084_clks), -}; - static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916}, - { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974}, - { .compatible = "qcom,rpmcc-apq8084", .data = &rpm_clk_apq8084}, { } }; MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); @@ -498,8 +474,8 @@ static int rpm_smd_clk_probe(struct platform_device *pdev) struct clk *clk; struct rpm_cc *rcc; struct clk_onecell_data *data; - int ret, i; - size_t num_clks; + int ret; + size_t num_clks, i; struct qcom_smd_rpm *rpm; struct clk_smd_rpm **rpm_smd_clks; const struct rpm_smd_clk_desc *desc; @@ -534,6 +510,22 @@ static int rpm_smd_clk_probe(struct platform_device *pdev) } rpm_smd_clks[i]->rpm = rpm; + + ret = clk_smd_rpm_handoff(rpm_smd_clks[i]); + if (ret) + goto err; + } + + ret = clk_smd_rpm_enable_scaling(rpm); + if (ret) + goto err; + + for (i = 0; i < num_clks; i++) { + if (!rpm_smd_clks[i]) { + clks[i] = ERR_PTR(-ENOENT); + continue; + } + clk = devm_clk_register(&pdev->dev, &rpm_smd_clks[i]->hw); if (IS_ERR(clk)) { ret = PTR_ERR(clk); @@ -548,12 +540,6 @@ static int rpm_smd_clk_probe(struct platform_device *pdev) if (ret) goto err; - ret = clk_smd_rpm_enable_scaling(rpm); - if (ret) { - of_clk_del_provider(pdev->dev.of_node); - goto err; - } - return 0; err: dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret); diff --git a/drivers/clk/qcom/clk-smd-rpm.h b/drivers/clk/qcom/clk-smd-rpm.h deleted file mode 100644 index 7ac58294d368..000000000000 --- a/drivers/clk/qcom/clk-smd-rpm.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2015, Linaro Limited - * Copyright (c) 2014, The Linux Foundation. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - */ - -#ifndef __QCOM_CLK_SMD_RPM_H__ -#define __QCOM_CLK_SMD_RPM_H__ - -#include - -#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 -#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 -#define QCOM_RPM_SMD_KEY_RATE 0x007a484b -#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45 -#define QCOM_RPM_SMD_KEY_STATE 0x54415453 -#define QCOM_RPM_SCALING_ENABLE_ID 0x2 - -struct qcom_smd_rpm; - -struct clk_smd_rpm { - const int rpm_res_type; - const int rpm_key; - const int rpm_clk_id; - const int rpm_status_id; - const bool active_only; - bool enabled; - bool branch; - struct clk_smd_rpm *peer; - struct clk_hw hw; - unsigned long rate; - struct qcom_smd_rpm *rpm; -}; - -struct clk_smd_rpm_req { - __le32 key; - __le32 nbytes; - __le32 value; -}; - -extern const struct clk_ops clk_smd_rpm_ops; -extern const struct clk_ops clk_smd_rpm_branch_ops; - -#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \ - key) \ - static struct clk_smd_rpm _platform##_##_active; \ - static struct clk_smd_rpm _platform##_##_name = { \ - .rpm_res_type = (type), \ - .rpm_clk_id = (r_id), \ - .rpm_status_id = (stat_id), \ - .rpm_key = (key), \ - .peer = &_platform##_##_active, \ - .rate = INT_MAX, \ - .hw.init = &(struct clk_init_data){ \ - .ops = &clk_smd_rpm_ops, \ - .name = #_name, \ - .parent_names = (const char *[]){ "xo_board" }, \ - .num_parents = 1, \ - }, \ - }; \ - static struct clk_smd_rpm _platform##_##_active = { \ - .rpm_res_type = (type), \ - .rpm_clk_id = (r_id), \ - .rpm_status_id = (stat_id), \ - .rpm_key = (key), \ - .peer = &_platform##_##_name, \ - .active_only = true, \ - .rate = INT_MAX, \ - .hw.init = &(struct clk_init_data){ \ - .ops = &clk_smd_rpm_ops, \ - .name = #_active, \ - .parent_names = (const char *[]){ "xo_board" }, \ - .num_parents = 1, \ - }, \ - } - -#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \ - stat_id, r, key) \ - static struct clk_smd_rpm _platform##_##_active; \ - static struct clk_smd_rpm _platform##_##_name = { \ - .rpm_res_type = (type), \ - .rpm_clk_id = (r_id), \ - .rpm_status_id = (stat_id), \ - .rpm_key = (key), \ - .peer = &_platform##_##_active, \ - .branch = true, \ - .rate = (r), \ - .hw.init = &(struct clk_init_data){ \ - .ops = &clk_smd_rpm_branch_ops, \ - .name = #_name, \ - .parent_names = (const char *[]){ "xo_board" }, \ - .num_parents = 1, \ - }, \ - }; \ - static struct clk_smd_rpm _platform##_##_active = { \ - .rpm_res_type = (type), \ - .rpm_clk_id = (r_id), \ - .rpm_status_id = (stat_id), \ - .rpm_key = (key), \ - .peer = &_platform##_##_name, \ - .active_only = true, \ - .branch = true, \ - .rate = (r), \ - .hw.init = &(struct clk_init_data){ \ - .ops = &clk_smd_rpm_branch_ops, \ - .name = #_active, \ - .parent_names = (const char *[]){ "xo_board" }, \ - .num_parents = 1, \ - }, \ - } - -#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \ - __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ - 0, QCOM_RPM_SMD_KEY_RATE) - -#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \ - __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \ - r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE) - -#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \ - __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ - 0, QCOM_RPM_SMD_KEY_STATE) - -#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \ - __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ - QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ - QCOM_RPM_KEY_SOFTWARE_ENABLE) - -#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \ - __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ - QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ - QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY) - -#endif diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index cabbe1f7e397..ca24cb6bf012 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -501,11 +501,13 @@ int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) req, req_cnt * sizeof(*req), resp, sizeof(*resp)); } -int __qcom_scm_restart_proc(u32 proc_id, int restart, u32 *resp) +int __qcom_scm_pas_mss_reset(bool reset) { + __le32 val = cpu_to_le32(reset); + __le32 resp; - return qcom_scm_call(QCOM_SCM_SVC_PIL, proc_id, - &restart, sizeof(restart), + return qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, + &val, sizeof(val), &resp, sizeof(resp)); } @@ -521,39 +523,23 @@ bool __qcom_scm_pas_supported(u32 peripheral) return ret ? false : !!ret_val; } -int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, const void *metadata, size_t size) +int __qcom_scm_pas_init_image(u32 peripheral, dma_addr_t metadata_phys) { - dma_addr_t mdata_phys; - void *mdata_buf; - u32 scm_ret; + __le32 scm_ret; int ret; - struct pas_init_image_req { - u32 proc; - u32 image_addr; + struct { + __le32 proc; + __le32 image_addr; } request; - /* - * During the scm call memory protection will be enabled for the meta - * data blob, so make sure it's physically contiguous, 4K aligned and - * non-cachable to avoid XPU violations. - */ - mdata_buf = dma_alloc_coherent(dev, size, &mdata_phys, GFP_KERNEL); - if (!mdata_buf) { - pr_err("Allocation of metadata buffer failed.\n"); - return -ENOMEM; - } - memcpy(mdata_buf, metadata, size); - - request.proc = peripheral; - request.image_addr = mdata_phys; + request.proc = cpu_to_le32(peripheral); + request.image_addr = cpu_to_le32(metadata_phys); ret = qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD, &request, sizeof(request), &scm_ret, sizeof(scm_ret)); - dma_free_coherent(dev, size, mdata_buf, mdata_phys); - - return ret ? : scm_ret; + return ret ? : le32_to_cpu(scm_ret); } int __qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index dc64858935fa..7784f1dc8977 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -472,20 +472,15 @@ int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) return ret; } -int __qcom_scm_restart_proc(u32 proc_id, int restart, u32 *resp) +int __qcom_scm_pas_mss_reset(bool reset) { - int ret; struct qcom_scm_desc desc = {0}; - desc.args[0] = restart; + desc.args[0] = reset; desc.args[1] = 0; desc.arginfo = QCOM_SCM_ARGS(2); - ret = qcom_scm_call(QCOM_SCM_SVC_PIL, proc_id, - &desc); - *resp = desc.ret[0]; - - return ret; + return qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, &desc); } bool __qcom_scm_pas_supported(u32 peripheral) @@ -502,37 +497,20 @@ bool __qcom_scm_pas_supported(u32 peripheral) return ret ? false : !!desc.ret[0]; } -int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, const void *metadata, size_t size) +int __qcom_scm_pas_init_image(u32 peripheral, dma_addr_t metadata_phys) { int ret; struct qcom_scm_desc desc = {0}; u32 scm_ret; - dma_addr_t mdata_phys; - void *mdata_buf; - -dev->coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8); - - /* - * During the scm call memory protection will be enabled for the meta - * data blob, so make sure it's physically contiguous, 4K aligned and - * non-cachable to avoid XPU violations. - */ - mdata_buf = dma_alloc_coherent(dev, size, &mdata_phys, GFP_KERNEL); - if (!mdata_buf) { - pr_err("Allocation of metadata buffer failed.\n"); - return -ENOMEM; - } - memcpy(mdata_buf, metadata, size); desc.args[0] = peripheral; - desc.args[1] = mdata_phys; + desc.args[1] = metadata_phys; desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW); ret = qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD, &desc); scm_ret = desc.ret[0]; - dma_free_coherent(dev, size, mdata_buf, mdata_phys); return ret ? : scm_ret; } diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index a1a25185a867..b760fae77d19 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -15,17 +15,22 @@ #include #include #include +#include #include #include #include #include +#include #include "qcom_scm.h" struct qcom_scm { + struct device *dev; struct clk *core_clk; struct clk *iface_clk; struct clk *bus_clk; + + struct reset_controller_dev reset; }; static struct qcom_scm *__scm; @@ -34,44 +39,31 @@ static int qcom_scm_clk_enable(void) { int ret; - if(__scm->core_clk) { - ret = clk_prepare_enable(__scm->core_clk); - if (ret) - goto bail; - } - - if(__scm->iface_clk) { - ret = clk_prepare_enable(__scm->iface_clk); - if (ret) - goto disable_core; - } - - if(__scm->bus_clk) { - ret = clk_prepare_enable(__scm->bus_clk); - if (ret) - goto disable_iface; - } + ret = clk_prepare_enable(__scm->core_clk); + if (ret) + goto bail; + ret = clk_prepare_enable(__scm->iface_clk); + if (ret) + goto disable_core; + ret = clk_prepare_enable(__scm->bus_clk); + if (ret) + goto disable_iface; return 0; disable_iface: - if(__scm->iface_clk) - clk_disable_unprepare(__scm->iface_clk); + clk_disable_unprepare(__scm->iface_clk); disable_core: - if(__scm->core_clk) - clk_disable_unprepare(__scm->core_clk); + clk_disable_unprepare(__scm->core_clk); bail: return ret; } static void qcom_scm_clk_disable(void) { - if(__scm->core_clk) - clk_disable_unprepare(__scm->core_clk); - if(__scm->iface_clk) - clk_disable_unprepare(__scm->iface_clk); - if(__scm->bus_clk) - clk_disable_unprepare(__scm->bus_clk); + clk_disable_unprepare(__scm->core_clk); + clk_disable_unprepare(__scm->iface_clk); + clk_disable_unprepare(__scm->bus_clk); } /** @@ -159,11 +151,6 @@ int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) } EXPORT_SYMBOL(qcom_scm_hdcp_req); -int qcom_scm_restart_proc(u32 pid, int restart, u32 *resp) -{ - return __qcom_scm_restart_proc(pid, restart, resp); -} -EXPORT_SYMBOL(qcom_scm_restart_proc); /** * qcom_scm_pas_supported() - Check if the peripheral authentication service is * available for the given peripherial @@ -196,9 +183,36 @@ EXPORT_SYMBOL(qcom_scm_pas_supported); * * Returns 0 on success. */ -int qcom_scm_pas_init_image(struct device *dev, u32 peripheral, const void *metadata, size_t size) +int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size) { - return __qcom_scm_pas_init_image(dev, peripheral, metadata, size); + dma_addr_t mdata_phys; + void *mdata_buf; + int ret; + + /* + * During the scm call memory protection will be enabled for the meta + * data blob, so make sure it's physically contiguous, 4K aligned and + * non-cachable to avoid XPU violations. + */ + mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, GFP_KERNEL); + if (!mdata_buf) { + dev_err(__scm->dev, "Allocation of metadata buffer failed.\n"); + return -ENOMEM; + } + memcpy(mdata_buf, metadata, size); + + ret = qcom_scm_clk_enable(); + if (ret) + goto free_metadata; + + ret = __qcom_scm_pas_init_image(peripheral, mdata_phys); + + qcom_scm_clk_disable(); + +free_metadata: + dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); + + return ret; } EXPORT_SYMBOL(qcom_scm_pas_init_image); @@ -213,7 +227,16 @@ EXPORT_SYMBOL(qcom_scm_pas_init_image); */ int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) { - return __qcom_scm_pas_mem_setup(peripheral, addr, size); + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = __qcom_scm_pas_mem_setup(peripheral, addr, size); + qcom_scm_clk_disable(); + + return ret; } EXPORT_SYMBOL(qcom_scm_pas_mem_setup); @@ -226,7 +249,16 @@ EXPORT_SYMBOL(qcom_scm_pas_mem_setup); */ int qcom_scm_pas_auth_and_reset(u32 peripheral) { - return __qcom_scm_pas_auth_and_reset(peripheral); + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = __qcom_scm_pas_auth_and_reset(peripheral); + qcom_scm_clk_disable(); + + return ret; } EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset); @@ -238,7 +270,16 @@ EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset); */ int qcom_scm_pas_shutdown(u32 peripheral) { - return __qcom_scm_pas_shutdown(peripheral); + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = __qcom_scm_pas_shutdown(peripheral); + qcom_scm_clk_disable(); + + return ret; } EXPORT_SYMBOL(qcom_scm_pas_shutdown); @@ -351,6 +392,21 @@ static int __init qcom_scm_init(void) return __qcom_scm_init(); } +static int qcom_scm_reset_assert(struct reset_controller_dev *rcdev, unsigned long idx) +{ + return __qcom_scm_pas_mss_reset(1); +} + +static int qcom_scm_reset_deassert(struct reset_controller_dev *rcdev, unsigned long idx) +{ + return __qcom_scm_pas_mss_reset(0); +} + +static struct reset_control_ops scm_reset_ops = { + .assert = qcom_scm_reset_assert, + .deassert = qcom_scm_reset_deassert, +}; + static int qcom_scm_probe(struct platform_device *pdev) { struct qcom_scm *scm; @@ -365,35 +421,45 @@ static int qcom_scm_probe(struct platform_device *pdev) if (!scm) return -ENOMEM; + scm->dev = &pdev->dev; + scm->core_clk = devm_clk_get(&pdev->dev, "core"); if (IS_ERR(scm->core_clk)) { - if (PTR_ERR(scm->core_clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to acquire core clk\n"); + if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_err(&pdev->dev, "failed to acquire core clk\n"); scm->core_clk = NULL; } scm->iface_clk = devm_clk_get(&pdev->dev, "iface"); if (IS_ERR(scm->iface_clk)) { - if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to acquire iface clk\n"); + if (PTR_ERR(scm->iface_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_err(&pdev->dev, "failed to acquire iface clk\n"); scm->iface_clk = NULL; } scm->bus_clk = devm_clk_get(&pdev->dev, "bus"); if (IS_ERR(scm->bus_clk)) { - if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to acquire bus clk\n"); + if (PTR_ERR(scm->bus_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to acquire bus clk\n"); scm->bus_clk = NULL; } - if (scm->core_clk) { + scm->reset.ops = &scm_reset_ops; + scm->reset.nr_resets = 1; + scm->reset.of_node = pdev->dev.of_node; + reset_controller_register(&scm->reset); + /* vote for max clk rate for highest performance */ - rate = clk_round_rate(scm->core_clk, INT_MAX); - ret = clk_set_rate(scm->core_clk, rate); - if (ret) - return ret; - } + rate = clk_round_rate(scm->core_clk, INT_MAX); + ret = clk_set_rate(scm->core_clk, rate); + if (ret) + return ret; __scm = scm; diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 93b94c9ad34f..e1d6a42ba466 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -42,11 +42,13 @@ extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, #define QCOM_SCM_PAS_AUTH_AND_RESET_CMD 0x5 #define QCOM_SCM_PAS_SHUTDOWN_CMD 0x6 #define QCOM_SCM_PAS_IS_SUPPORTED_CMD 0x7 +#define QCOM_SCM_PAS_MSS_RESET 0xa extern bool __qcom_scm_pas_supported(u32 peripheral); -extern int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, const void *metadata, size_t size); +extern int __qcom_scm_pas_init_image(u32 peripheral, dma_addr_t metadata_phys); extern int __qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size); extern int __qcom_scm_pas_auth_and_reset(u32 peripheral); extern int __qcom_scm_pas_shutdown(u32 peripheral); +extern int __qcom_scm_pas_mss_reset(bool reset); /* common error codes */ #define QCOM_SCM_ENOMEM -5 @@ -111,7 +113,6 @@ extern int __qcom_scm_iommu_secure_unmap(u32 id, u32 ctx_id, u64 va, extern int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id); extern int __qcom_scm_get_feat_version(u32 feat); extern int __qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); -extern int __qcom_scm_restart_proc(u32 proc_id, int restart, u32 *resp); extern int __qcom_scm_set_video_state(u32 state, u32 spare); extern int __qcom_scm_mem_protect_video_var(u32 start, u32 size, diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index caf36519f410..b18bea08ff25 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -337,21 +337,6 @@ config GPIO_PXA help Say yes here to support the PXA GPIO device -config GPIO_QCOM_SMSM - bool "Qualcomm Shared Memory State Machine" - depends on QCOM_SMEM - help - Say yes here to support the Qualcomm Shared Memory State Machine. - The state machine is represented by bits in shared memory and is - exposed to the system as GPIOs. - -config GPIO_QCOM_SMP2P - bool "Qualcomm Shared Memory Point to Point support" - depends on QCOM_SMEM - help - Say yes here to support the Qualcomm Shared Memory Point to Point - protocol, exposed to the system as GPIOs. - config GPIO_RCAR tristate "Renesas R-Car GPIO" depends on ARCH_SHMOBILE || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 6c9d22e86b31..986dbd838cea 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -76,8 +76,6 @@ obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o -obj-$(CONFIG_GPIO_QCOM_SMSM) += gpio-qcom-smsm.o -obj-$(CONFIG_GPIO_QCOM_SMP2P) += gpio-qcom-smp2p.o obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o diff --git a/drivers/gpio/gpio-qcom-smp2p.c b/drivers/gpio/gpio-qcom-smp2p.c deleted file mode 100644 index 164f39afebb3..000000000000 --- a/drivers/gpio/gpio-qcom-smp2p.c +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Copyright (c) 2015, Sony Mobile Communications AB. - * Copyright (c) 2012-2013, The Linux Foundation. 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 and - * only 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -/* - * The Shared Memory Point to Point (SMP2P) protocol facilitates communication - * of a single 32-bit value between two processors. Each value has a single - * writer (the local side) and a single reader (the remote side). Values are - * uniquely identified in the system by the directed edge (local processor ID - * to remote processor ID) and a string identifier. - * - * Each processor is responsible for creating the outgoing SMEM items and each - * item is writable by the local processor and readable by the remote - * processor. By using two separate SMEM items that are single-reader and - * single-writer, SMP2P does not require any remote locking mechanisms. - * - * The driver uses the Linux GPIO and interrupt framework to expose a virtual - * GPIO for each outbound entry and a virtual interrupt controller for each - * inbound entry. - */ - -#define SMP2P_MAX_ENTRY 16 -#define SMP2P_MAX_ENTRY_NAME 16 - -#define SMP2P_FEATURE_SSR_ACK 0x1 - -#define SMP2P_MAGIC 0x504d5324 - -/** - * struct smp2p_smem_item - in memory communication structure - * @magic: magic number - * @version: version - must be 1 - * @features: features flag - currently unused - * @local_pid: processor id of sending end - * @remote_pid: processor id of receiving end - * @total_entries: number of entries - always SMP2P_MAX_ENTRY - * @valid_entries: number of allocated entries - * @flags: - * @entries: individual communication entries - * @name: name of the entry - * @value: content of the entry - */ -struct smp2p_smem_item { - u32 magic; - u8 version; - unsigned features:24; - u16 local_pid; - u16 remote_pid; - u16 total_entries; - u16 valid_entries; - u32 flags; - - struct { - u8 name[SMP2P_MAX_ENTRY_NAME]; - u32 value; - } entries[SMP2P_MAX_ENTRY]; -} __packed; - -/** - * struct smp2p_entry - driver context matching one entry - * @node: list entry to keep track of allocated entries - * @smp2p: reference to the device driver context - * @name: name of the entry, to match against smp2p_smem_item - * @value: pointer to smp2p_smem_item entry value - * @last_value: last handled value - * @domain: irq_domain for inbound entries - * @irq_enabled:bitmap to track enabled irq bits - * @irq_rising: bitmap to mark irq bits for rising detection - * @irq_falling:bitmap to mark irq bits for falling detection - * @chip: gpio_chip for outbound entries - */ -struct smp2p_entry { - struct list_head node; - struct qcom_smp2p *smp2p; - - const char *name; - u32 *value; - u32 last_value; - - struct irq_domain *domain; - DECLARE_BITMAP(irq_enabled, 32); - DECLARE_BITMAP(irq_rising, 32); - DECLARE_BITMAP(irq_falling, 32); - - struct gpio_chip chip; -}; - -#define SMP2P_INBOUND 0 -#define SMP2P_OUTBOUND 1 - -/** - * struct qcom_smp2p - device driver context - * @dev: device driver handle - * @in: pointer to the inbound smem item - * @smem_items: ids of the two smem items - * @valid_entries: already scanned inbound entries - * @local_pid: processor id of the inbound edge - * @remote_pid: processor id of the outbound edge - * @ipc_regmap: regmap for the outbound ipc - * @ipc_offset: offset within the regmap - * @ipc_bit: bit in regmap@offset to kick to signal remote processor - * @inbound: list of inbound entries - * @outbound: list of outbound entries - */ -struct qcom_smp2p { - struct device *dev; - - struct smp2p_smem_item *in; - struct smp2p_smem_item *out; - - unsigned smem_items[SMP2P_OUTBOUND + 1]; - - unsigned valid_entries; - - unsigned local_pid; - unsigned remote_pid; - - struct regmap *ipc_regmap; - int ipc_offset; - int ipc_bit; - - struct list_head inbound; - struct list_head outbound; -}; - -static void qcom_smp2p_kick(struct qcom_smp2p *smp2p) -{ - /* Make sure any updated data is written before the kick */ - wmb(); - regmap_write(smp2p->ipc_regmap, smp2p->ipc_offset, BIT(smp2p->ipc_bit)); -} - -static irqreturn_t qcom_smp2p_intr(int irq, void *data) -{ - struct smp2p_smem_item *in; - struct smp2p_entry *entry; - struct qcom_smp2p *smp2p = data; - unsigned smem_id = smp2p->smem_items[SMP2P_INBOUND]; - unsigned pid = smp2p->remote_pid; - size_t size; - int irq_pin; - u32 status; - u32 val; - int i; - u8 tmp_name[SMP2P_MAX_ENTRY_NAME]; - - in = smp2p->in; - - /* Acquire smem item, if not already found */ - if (!in) { - in = qcom_smem_get(pid, smem_id, &size); - if (IS_ERR(in)) { - dev_err(smp2p->dev, - "Unable to acquire remote smp2p item\n"); - return IRQ_HANDLED; - } - - smp2p->in = in; - } - - /* Match newly created entries */ - for (i = smp2p->valid_entries; i < in->valid_entries; i++) { - list_for_each_entry(entry, &smp2p->inbound, node) { - memcpy_fromio(tmp_name, in->entries[i].name, - SMP2P_MAX_ENTRY_NAME); - if (!strcmp(tmp_name, entry->name)) { - entry->value = &in->entries[i].value; - break; - } - } - } - smp2p->valid_entries = i; - - /* Fire interrupts based on any value changes */ - list_for_each_entry(entry, &smp2p->inbound, node) { - /* Ignore entries not yet allocated by the remote side */ - if (!entry->value) - continue; - - val = *entry->value; - - status = val ^ entry->last_value; - entry->last_value = val; - - /* No changes of this entry? */ - if (!status) - continue; - - for_each_set_bit(i, entry->irq_enabled, 32) { - if (!(status & BIT(i))) - continue; - - if (val & BIT(i)) { - if (test_bit(i, entry->irq_rising)) { - irq_pin = irq_find_mapping(entry->domain, i); - handle_nested_irq(irq_pin); - } - } else { - if (test_bit(i, entry->irq_falling)) { - irq_pin = irq_find_mapping(entry->domain, i); - handle_nested_irq(irq_pin); - } - } - - } - } - - return IRQ_HANDLED; -} - -static void smp2p_mask_irq(struct irq_data *irqd) -{ - struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd); - irq_hw_number_t irq = irqd_to_hwirq(irqd); - - clear_bit(irq, entry->irq_enabled); -} - -static void smp2p_unmask_irq(struct irq_data *irqd) -{ - struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd); - irq_hw_number_t irq = irqd_to_hwirq(irqd); - - set_bit(irq, entry->irq_enabled); -} - -static int smp2p_set_irq_type(struct irq_data *irqd, unsigned int type) -{ - struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd); - irq_hw_number_t irq = irqd_to_hwirq(irqd); - - if (!(type & IRQ_TYPE_EDGE_BOTH)) - return -EINVAL; - - if (type & IRQ_TYPE_EDGE_RISING) - set_bit(irq, entry->irq_rising); - else - clear_bit(irq, entry->irq_rising); - - if (type & IRQ_TYPE_EDGE_FALLING) - set_bit(irq, entry->irq_falling); - else - clear_bit(irq, entry->irq_falling); - - return 0; -} - -static struct irq_chip smp2p_irq_chip = { - .name = "smp2p", - .irq_mask = smp2p_mask_irq, - .irq_unmask = smp2p_unmask_irq, - .irq_set_type = smp2p_set_irq_type, -}; - -static int smp2p_irq_map(struct irq_domain *d, - unsigned int irq, - irq_hw_number_t hw) -{ - struct smp2p_entry *entry = d->host_data; - - irq_set_chip_and_handler(irq, &smp2p_irq_chip, handle_level_irq); - irq_set_chip_data(irq, entry); - irq_set_nested_thread(irq, 1); - - irq_set_noprobe(irq); - - return 0; -} - -static const struct irq_domain_ops smp2p_irq_ops = { - .map = smp2p_irq_map, - .xlate = irq_domain_xlate_twocell, -}; - -static int smp2p_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, - int value) -{ - struct smp2p_entry *entry = container_of(chip, struct smp2p_entry, chip); - - if (value) - *entry->value |= BIT(offset); - else - *entry->value &= ~BIT(offset); - - qcom_smp2p_kick(entry->smp2p); - - return 0; -} - -static void smp2p_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - smp2p_gpio_direction_output(chip, offset, value); -} - -static int qcom_smp2p_inbound_entry(struct qcom_smp2p *smp2p, - struct smp2p_entry *entry, - struct device_node *node) -{ - entry->domain = irq_domain_add_linear(node, 32, &smp2p_irq_ops, entry); - if (!entry->domain) { - dev_err(smp2p->dev, "failed to add irq_domain\n"); - return -ENOMEM; - } - - return 0; -} - -static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p, - struct smp2p_entry *entry, - struct device_node *node) -{ - struct smp2p_smem_item *out = smp2p->out; - struct gpio_chip *chip; - int ret; - - /* Allocate an entry from the smem item */ - memcpy_toio(out->entries[out->valid_entries].name, entry->name, SMP2P_MAX_ENTRY_NAME); - out->valid_entries++; - - /* Make the logical entry reference the physical value */ - entry->value = &out->entries[out->valid_entries].value; - - chip = &entry->chip; - chip->base = -1; - chip->ngpio = 32; - chip->label = entry->name; - chip->dev = smp2p->dev; - chip->owner = THIS_MODULE; - chip->of_node = node; - - chip->set = smp2p_gpio_set; - chip->direction_output = smp2p_gpio_direction_output; - - ret = gpiochip_add(chip); - if (ret) - dev_err(smp2p->dev, "failed register gpiochip\n"); - - return 0; -} - -static int qcom_smp2p_alloc_outbound_item(struct qcom_smp2p *smp2p) -{ - struct smp2p_smem_item *out; - unsigned smem_id = smp2p->smem_items[SMP2P_OUTBOUND]; - unsigned pid = smp2p->remote_pid; - int ret; - - ret = qcom_smem_alloc(pid, smem_id, sizeof(*out)); - if (ret < 0 && ret != -EEXIST) { - if (ret != -EPROBE_DEFER) - dev_err(smp2p->dev, - "unable to allocate local smp2p item\n"); - return ret; - } - - out = qcom_smem_get(pid, smem_id, NULL); - if (IS_ERR(out)) { - dev_err(smp2p->dev, "Unable to acquire local smp2p item\n"); - return PTR_ERR(out); - } - - memset_io(out, 0, sizeof(*out)); - out->magic = SMP2P_MAGIC; - out->local_pid = smp2p->local_pid; - out->remote_pid = smp2p->remote_pid; - out->total_entries = SMP2P_MAX_ENTRY; - out->valid_entries = 0; - - /* - * Make sure the rest of the header is written before we validate the - * item by writing a valid version number. - */ - wmb(); - out->version = 1; - - qcom_smp2p_kick(smp2p); - - smp2p->out = out; - - return 0; -} - -static int smp2p_parse_ipc(struct qcom_smp2p *smp2p) -{ - struct device_node *syscon; - struct device *dev = smp2p->dev; - const char *key; - int ret; - - syscon = of_parse_phandle(dev->of_node, "qcom,ipc", 0); - if (!syscon) { - dev_err(dev, "no qcom,ipc node\n"); - return -ENODEV; - } - - smp2p->ipc_regmap = syscon_node_to_regmap(syscon); - if (IS_ERR(smp2p->ipc_regmap)) - return PTR_ERR(smp2p->ipc_regmap); - - key = "qcom,ipc"; - ret = of_property_read_u32_index(dev->of_node, key, 1, &smp2p->ipc_offset); - if (ret < 0) { - dev_err(dev, "no offset in %s\n", key); - return -EINVAL; - } - - ret = of_property_read_u32_index(dev->of_node, key, 2, &smp2p->ipc_bit); - if (ret < 0) { - dev_err(dev, "no bit in %s\n", key); - return -EINVAL; - } - - return 0; -} - -static int qcom_smp2p_probe(struct platform_device *pdev) -{ - struct smp2p_entry *entry; - struct device_node *node; - struct qcom_smp2p *smp2p; - const char *key; - int irq; - int ret; - - smp2p = devm_kzalloc(&pdev->dev, sizeof(*smp2p), GFP_KERNEL); - if (!smp2p) - return -ENOMEM; - - smp2p->dev = &pdev->dev; - INIT_LIST_HEAD(&smp2p->inbound); - INIT_LIST_HEAD(&smp2p->outbound); - - platform_set_drvdata(pdev, smp2p); - - ret = smp2p_parse_ipc(smp2p); - if (ret) - return ret; - - key = "qcom,smem"; - ret = of_property_read_u32_array(pdev->dev.of_node, key, - smp2p->smem_items, 2); - if (ret) - return ret; - - key = "qcom,local-pid"; - ret = of_property_read_u32(pdev->dev.of_node, key, &smp2p->local_pid); - if (ret < 0) { - dev_err(&pdev->dev, "failed to read %s\n", key); - return -EINVAL; - } - - key = "qcom,remote-pid"; - ret = of_property_read_u32(pdev->dev.of_node, key, &smp2p->remote_pid); - if (ret < 0) { - dev_err(&pdev->dev, "failed to read %s\n", key); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "unable to acquire smp2p interrupt\n"); - return irq; - } - - ret = qcom_smp2p_alloc_outbound_item(smp2p); - if (ret < 0) - return ret; - - for_each_available_child_of_node(pdev->dev.of_node, node) { - entry = devm_kzalloc(&pdev->dev, sizeof(*entry), GFP_KERNEL); - if (!entry) { - ret = -ENOMEM; - goto unwind_interfaces; - } - - entry->smp2p = smp2p; - - ret = of_property_read_string(node, "qcom,entry-name", &entry->name); - if (ret < 0) - goto unwind_interfaces; - - if (of_property_read_bool(node, "qcom,inbound")) { - ret = qcom_smp2p_inbound_entry(smp2p, entry, node); - if (ret < 0) - goto unwind_interfaces; - - list_add(&entry->node, &smp2p->inbound); - } else if (of_property_read_bool(node, "qcom,outbound")) { - ret = qcom_smp2p_outbound_entry(smp2p, entry, node); - if (ret < 0) - goto unwind_interfaces; - - list_add(&entry->node, &smp2p->outbound); - } else { - dev_err(&pdev->dev, "neither inbound nor outbound\n"); - ret = -EINVAL; - goto unwind_interfaces; - } - } - - /* Kick the outgoing edge after allocating entries */ - qcom_smp2p_kick(smp2p); - - ret = devm_request_threaded_irq(&pdev->dev, irq, - NULL, qcom_smp2p_intr, - IRQF_ONESHOT, - "smp2p", (void *)smp2p); - if (ret) { - dev_err(&pdev->dev, "failed to request interrupt\n"); - goto unwind_interfaces; - } - - - return 0; - -unwind_interfaces: - list_for_each_entry(entry, &smp2p->inbound, node) - irq_domain_remove(entry->domain); - - list_for_each_entry(entry, &smp2p->outbound, node) - gpiochip_remove(&entry->chip); - - smp2p->out->valid_entries = 0; - - return ret; -} - -static int qcom_smp2p_remove(struct platform_device *pdev) -{ - struct qcom_smp2p *smp2p = platform_get_drvdata(pdev); - struct smp2p_entry *entry; - - list_for_each_entry(entry, &smp2p->inbound, node) - irq_domain_remove(entry->domain); - - list_for_each_entry(entry, &smp2p->outbound, node) - gpiochip_remove(&entry->chip); - - smp2p->out->valid_entries = 0; - - return 0; -} - -static const struct of_device_id qcom_smp2p_of_match[] = { - { .compatible = "qcom,smp2p" }, - {} -}; -MODULE_DEVICE_TABLE(of, qcom_smp2p_of_match); - -static struct platform_driver qcom_smp2p_driver = { - .probe = qcom_smp2p_probe, - .remove = qcom_smp2p_remove, - .driver = { - .name = "qcom_smp2p", - .of_match_table = qcom_smp2p_of_match, - }, -}; -module_platform_driver(qcom_smp2p_driver); - -MODULE_DESCRIPTION("Qualcomm Shared Memory Point to Point driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-qcom-smsm.c b/drivers/gpio/gpio-qcom-smsm.c deleted file mode 100644 index c3e510364d14..000000000000 --- a/drivers/gpio/gpio-qcom-smsm.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (c) 2014, Sony Mobile Communications AB. - * Copyright (c) 2012-2013, The Linux Foundation. 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 and - * only 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#define SMSM_APPS_STATE 0 -#define SMEM_SMSM_SHARED_STATE 85 - -#define SMSM_MAX_STATES 8 - -struct qcom_smsm_state { - unsigned state_id; - struct gpio_chip chip; - - int irq; - - struct regmap *ipc_regmap; - int ipc_bit; - int ipc_offset; -}; - -struct qcom_smsm { - struct device *dev; - - u32 *shared_state; - size_t shared_state_size; - - struct qcom_smsm_state states[SMSM_MAX_STATES]; -}; - -#if 0 -int qcom_smsm_change_state(struct qcom_smsm *smsm, u32 clear_mask, u32 set_mask) -{ - u32 state; - - dev_dbg(smsm->dev, "SMSM_APPS_STATE clear 0x%x set 0x%x\n", clear_mask, set_mask); - print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_OFFSET, 16, 1, smsm->shared_state, smsm->shared_state_size, true); - - state = readl(&smsm->shared_state[SMSM_APPS_STATE]); - state &= ~clear_mask; - state |= set_mask; - writel(state, &smsm->shared_state[SMSM_APPS_STATE]); - - print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_OFFSET, 16, 1, smsm->shared_state, smsm->shared_state_size, true); - - // qcom_smem_signal(-1, smsm->smem, smsm->signal_offset, smsm->signal_bit); - - return 0; -} -EXPORT_SYMBOL(qcom_smsm_change_state); -#endif - -static struct qcom_smsm_state *to_smsm_state(struct gpio_chip *chip) -{ - return container_of(chip, struct qcom_smsm_state, chip); -} - -static struct qcom_smsm *to_smsm(struct qcom_smsm_state *state) -{ - return container_of(state, struct qcom_smsm, states[state->state_id]); -} - -static int smsm_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - struct qcom_smsm_state *state = to_smsm_state(chip); - - if (state->state_id == SMSM_APPS_STATE) - return -EINVAL; - return 0; -} - -static int smsm_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, - int value) -{ - struct qcom_smsm_state *ipc_state; - struct qcom_smsm_state *state = to_smsm_state(chip); - struct qcom_smsm *smsm = to_smsm(state); - unsigned word; - unsigned bit; - u32 val; - int i; - - /* Only SMSM_APPS_STATE supports writing */ - if (state->state_id != SMSM_APPS_STATE) - return -EINVAL; - - offset += state->state_id * 32; - - word = ALIGN(offset / 32, 4); - bit = offset % 32; - - val = readl(smsm->shared_state + word); - if (value) - val |= BIT(bit); - else - val &= ~BIT(bit); - writel(val, smsm->shared_state + word); - - /* XXX: send out interrupts */ - for (i = 0; i < SMSM_MAX_STATES; i++) { - ipc_state = &smsm->states[i]; - if (!ipc_state->ipc_regmap) - continue; - - regmap_write(ipc_state->ipc_regmap, ipc_state->ipc_offset, BIT(ipc_state->ipc_bit)); - } - - dev_err(smsm->dev, "set %d %d\n", offset, value); - - return 0; -} - -static int smsm_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct qcom_smsm_state *state = to_smsm_state(chip); - struct qcom_smsm *smsm = to_smsm(state); - unsigned word; - unsigned bit; - u32 val; - - offset += state->state_id * 32; - - word = ALIGN(offset / 32, 4); - bit = offset % 32; - - val = readl(smsm->shared_state + word); - - return !!(val & BIT(bit)); -} - -static void smsm_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - smsm_gpio_direction_output(chip, offset, value); -} - -static int smsm_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - return -EINVAL; -} - -#ifdef CONFIG_DEBUG_FS -#include - -static void smsm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) -{ - struct qcom_smsm_state *state = to_smsm_state(chip); - struct qcom_smsm *smsm = to_smsm(state); - unsigned i; - u32 val; - - val = readl(smsm->shared_state + state->state_id * 4); - - for (i = 0; i < 32; i++) { - if (val & BIT(i)) - seq_puts(s, "1"); - else - seq_puts(s, "0"); - - if (i == 7 || i == 15 || i == 23) - seq_puts(s, " "); - } - seq_puts(s, "\n"); -} - -#else -#define smsm_gpio_dbg_show NULL -#endif - -static struct gpio_chip smsm_gpio_template = { - .direction_input = smsm_gpio_direction_input, - .direction_output = smsm_gpio_direction_output, - .get = smsm_gpio_get, - .set = smsm_gpio_set, - .to_irq = smsm_gpio_to_irq, - .dbg_show = smsm_gpio_dbg_show, - .owner = THIS_MODULE, -}; - -static int qcom_smsm_probe(struct platform_device *pdev) -{ - struct qcom_smsm_state *state; - struct device_node *syscon_np; - struct device_node *node; - struct qcom_smsm *smsm; - char *key; - u32 sid; - int ret; - - smsm = devm_kzalloc(&pdev->dev, sizeof(*smsm), GFP_KERNEL); - if (!smsm) - return -ENOMEM; - smsm->dev = &pdev->dev; - - ret = qcom_smem_alloc(-1, SMEM_SMSM_SHARED_STATE, 8 * sizeof(uint32_t)); - if (ret < 0 && ret != -EEXIST) { - dev_err(&pdev->dev, "unable to allocate shared state entry\n"); - return ret; - } - - smsm->shared_state = qcom_smem_get(-1, SMEM_SMSM_SHARED_STATE, - &smsm->shared_state_size); - if (IS_ERR(smsm->shared_state)) { - dev_err(&pdev->dev, "Unable to acquire shared state entry\n"); - return PTR_ERR(smsm->shared_state); - } - - dev_err(smsm->dev, "SMEM_SMSM_SHARED_STATE: %d, %zu\n", ret, smsm->shared_state_size); - print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_OFFSET, 16, 1, smsm->shared_state, smsm->shared_state_size, true); - - for_each_child_of_node(pdev->dev.of_node, node) { - key = "reg"; - ret = of_property_read_u32(node, key, &sid); - if (ret || sid >= SMSM_MAX_STATES) { - dev_err(&pdev->dev, "smsm state missing %s\n", key); - return -EINVAL; - } - state = &smsm->states[sid]; - state->state_id = sid; - - state->chip = smsm_gpio_template; - state->chip.base = -1; - state->chip.dev = &pdev->dev; - state->chip.of_node = node; - state->chip.label = node->name; - state->chip.ngpio = 8 * sizeof(u32); - ret = gpiochip_add(&state->chip); - if (ret) { - dev_err(&pdev->dev, "failed register gpiochip\n"); - // goto wooha; - } - - /* The remaining properties are only for non-apps state */ - if (sid == SMSM_APPS_STATE) - continue; - - state->irq = irq_of_parse_and_map(node, 0); - if (state->irq < 0 && state->irq != -EINVAL) { - dev_err(&pdev->dev, "failed to parse smsm interrupt\n"); - return -EINVAL; - } - - syscon_np = of_parse_phandle(node, "qcom,ipc", 0); - if (!syscon_np) { - dev_err(&pdev->dev, "no qcom,ipc node\n"); - return -ENODEV; - } - - state->ipc_regmap = syscon_node_to_regmap(syscon_np); - if (IS_ERR(state->ipc_regmap)) - return PTR_ERR(state->ipc_regmap); - - key = "qcom,ipc"; - ret = of_property_read_u32_index(node, key, 1, &state->ipc_offset); - if (ret < 0) { - dev_err(&pdev->dev, "no offset in %s\n", key); - return -EINVAL; - } - - ret = of_property_read_u32_index(node, key, 2, &state->ipc_bit); - if (ret < 0) { - dev_err(&pdev->dev, "no bit in %s\n", key); - return -EINVAL; - } - - } - - return 0; -} - -static const struct of_device_id qcom_smsm_of_match[] = { - { .compatible = "qcom,smsm" }, - {} -}; -MODULE_DEVICE_TABLE(of, qcom_smsm_of_match); - -static struct platform_driver qcom_smsm_driver = { - .probe = qcom_smsm_probe, - .driver = { - .name = "qcom_smsm", - .owner = THIS_MODULE, - .of_match_table = qcom_smsm_of_match, - }, -}; - -static int __init qcom_smsm_init(void) -{ - return platform_driver_register(&qcom_smsm_driver); -} -arch_initcall(qcom_smsm_init); - -static void __exit qcom_smsm_exit(void) -{ - platform_driver_unregister(&qcom_smsm_driver); -} -module_exit(qcom_smsm_exit) - -MODULE_DESCRIPTION("Qualcomm Shared Memory Signaling Mechanism"); -MODULE_LICENSE("GPLv2"); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index ab46e6a743e0..be522ac6cc81 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -17,8 +17,9 @@ */ #include +#include + #include -#include #include "hdmi.h" void hdmi_set_mode(struct hdmi *hdmi, bool power_on) @@ -417,7 +418,114 @@ static int get_gpio(struct device *dev, struct device_node *of_node, const char } #endif -static void msm_hdmi_register_audio_driver(struct hdmi *hdmi, struct device *dev); +/* + * HDMI audio codec callbacks + */ +static int msm_hdmi_audio_hw_params(struct device *dev, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct hdmi *hdmi = dev_get_drvdata(dev); + unsigned int chan; + unsigned int channel_allocation = 0; + unsigned int rate; + unsigned int level_shift = 0; /* 0dB */ + bool down_mix = false; + + dev_dbg(dev, "%u Hz, %d bit, %d channels\n", params->sample_rate, + params->sample_width, params->cea.channels); + + switch (params->cea.channels) { + case 2: + /* FR and FL speakers */ + channel_allocation = 0; + chan = MSM_HDMI_AUDIO_CHANNEL_2; + break; + case 4: + /* FC, LFE, FR and FL speakers */ + channel_allocation = 0x3; + chan = MSM_HDMI_AUDIO_CHANNEL_4; + break; + case 6: + /* RR, RL, FC, LFE, FR and FL speakers */ + channel_allocation = 0x0B; + chan = MSM_HDMI_AUDIO_CHANNEL_6; + break; + case 8: + /* FRC, FLC, RR, RL, FC, LFE, FR and FL speakers */ + channel_allocation = 0x1F; + chan = MSM_HDMI_AUDIO_CHANNEL_8; + break; + default: + return -EINVAL; + } + + switch (params->sample_rate) { + case 32000: + rate = HDMI_SAMPLE_RATE_32KHZ; + break; + case 44100: + rate = HDMI_SAMPLE_RATE_44_1KHZ; + break; + case 48000: + rate = HDMI_SAMPLE_RATE_48KHZ; + break; + case 88200: + rate = HDMI_SAMPLE_RATE_88_2KHZ; + break; + case 96000: + rate = HDMI_SAMPLE_RATE_96KHZ; + break; + case 176400: + rate = HDMI_SAMPLE_RATE_176_4KHZ; + break; + case 192000: + rate = HDMI_SAMPLE_RATE_192KHZ; + break; + default: + dev_err(dev, "rate[%d] not supported!\n", + params->sample_rate); + return -EINVAL; + } + + hdmi_audio_set_sample_rate(hdmi, rate); + hdmi_audio_info_setup(hdmi, 1, chan, channel_allocation, + level_shift, down_mix); + + return 0; +} + +static void msm_hdmi_audio_shutdown(struct device *dev) +{ + struct hdmi *hdmi = dev_get_drvdata(dev); + + hdmi_audio_info_setup(hdmi, 0, 0, 0, 0, 0); +} + +static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = { + .hw_params = msm_hdmi_audio_hw_params, + .audio_shutdown = msm_hdmi_audio_shutdown, +}; + +static struct hdmi_codec_pdata codec_data = { + .ops = &msm_hdmi_audio_codec_ops, + .max_i2s_channels = 8, + .i2s = 1, +}; + +static int msm_hdmi_register_audio_driver(struct hdmi *hdmi, struct device *dev) +{ + hdmi->audio_pdev = platform_device_register_data(dev, + HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, + &codec_data, + sizeof(codec_data)); + if (IS_ERR(hdmi->audio_pdev)) + return PTR_ERR(hdmi->audio_pdev); + + return 0; +} + static int hdmi_bind(struct device *dev, struct device *master, void *data) { struct drm_device *drm = dev_get_drvdata(master); @@ -427,6 +535,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) #ifdef CONFIG_OF struct device_node *of_node = dev->of_node; const struct of_device_id *match; + int err; match = of_match_node(dt_match, of_node); if (match && match->data) { @@ -501,7 +610,12 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) if (IS_ERR(hdmi)) return PTR_ERR(hdmi); priv->hdmi = hdmi; - msm_hdmi_register_audio_driver(hdmi, dev); + + err = msm_hdmi_register_audio_driver(hdmi, dev); + if (err) { + DRM_ERROR("Failed to attach an audio codec %d\n", err); + hdmi->audio_pdev = NULL; + } return 0; } @@ -512,9 +626,10 @@ static void hdmi_unbind(struct device *dev, struct device *master, struct drm_device *drm = dev_get_drvdata(master); struct msm_drm_private *priv = drm->dev_private; if (priv->hdmi) { - DRM_INFO("%s driver unbound to HDMI\n", HDMI_CODEC_DRV_NAME); - platform_device_unregister(priv->hdmi->audio_pdev); hdmi_destroy(priv->hdmi); + if (priv->hdmi->audio_pdev) + platform_device_unregister(priv->hdmi->audio_pdev); + priv->hdmi = NULL; } } @@ -523,124 +638,6 @@ static const struct component_ops hdmi_ops = { .bind = hdmi_bind, .unbind = hdmi_unbind, }; -/* - * HDMI audio codec callbacks - */ - -static int msm_hdmi_audio_hw_params(struct device *dev, - struct hdmi_codec_daifmt *daifmt, - struct hdmi_codec_params *params) -{ - struct hdmi *hdmi = dev_get_drvdata(dev); - unsigned int chan;// = params->cea.channels; - unsigned int channel_allocation = 0; - unsigned int rate;// - unsigned int level_shift = 0; /* 0dB */ - bool down_mix = false; - dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__, - params->sample_rate, params->sample_width, chan); - - switch (params->cea.channels) { - case 2: - channel_allocation = 0; - chan = MSM_HDMI_AUDIO_CHANNEL_2; - break; - case 4: - channel_allocation = 0; - chan = MSM_HDMI_AUDIO_CHANNEL_4; - break; - case 6: - channel_allocation = 0x0B; - chan = MSM_HDMI_AUDIO_CHANNEL_6; - break; - case 8: - channel_allocation = 0x1F; - chan = MSM_HDMI_AUDIO_CHANNEL_8; - break; - default: - //dev_err(hdmi->dev, "channel[%d] not supported!\n", chan); - return -EINVAL; - } - - switch (params->sample_rate) { - case 32000: - rate = HDMI_SAMPLE_RATE_32KHZ; - case 44100: - rate = HDMI_SAMPLE_RATE_48KHZ; - case 48000: - rate = HDMI_SAMPLE_RATE_48KHZ; - case 88200: - rate = HDMI_SAMPLE_RATE_88_2KHZ; - case 96000: - rate = HDMI_SAMPLE_RATE_96KHZ; - case 176400: - rate = HDMI_SAMPLE_RATE_176_4KHZ; - case 192000: - rate = HDMI_SAMPLE_RATE_192KHZ; - break; - default: - dev_err(dev, "rate[%d] not supported!\n", - params->sample_rate); - return -EINVAL; - } - rate = HDMI_SAMPLE_RATE_48KHZ; - channel_allocation = 0; - - //FIXME.. - hdmi_audio_set_sample_rate(hdmi, rate); - - hdmi_audio_info_setup(hdmi, 1, chan, - channel_allocation, level_shift, down_mix); - - - - return 0; -} - -static int msm_hdmi_audio_startup(struct device *dev, - void (*abort_cb)(struct device *dev)) -{ - struct hdmi *hdmi = dev_get_drvdata(dev); - - dev_dbg(dev, "%s\n", __func__); - - //msm_hdmi_audio_enable(hdmi); - - return 0; -} - -static void msm_hdmi_audio_shutdown(struct device *dev) -{ - struct hdmi *hdmi = dev_get_drvdata(dev); - - dev_dbg(dev, "%s\n", __func__); - - hdmi_audio_info_setup(hdmi, 0, 0, 0, 0, 0); -} - -static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = { - .hw_params = msm_hdmi_audio_hw_params, - .audio_startup = msm_hdmi_audio_startup, - .audio_shutdown = msm_hdmi_audio_shutdown, -}; - -static void msm_hdmi_register_audio_driver(struct hdmi *hdmi, struct device *dev) -{ - struct hdmi_codec_pdata codec_data = { - .ops = &msm_hdmi_audio_codec_ops, - .max_i2s_channels = 2, - .i2s = 1, - }; - //struct platform_device *pdev; - - hdmi->audio_pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, - PLATFORM_DEVID_AUTO, &codec_data, - sizeof(codec_data)); - if (IS_ERR(hdmi->audio_pdev)) - return; - - DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME); -} static int hdmi_dev_probe(struct platform_device *pdev) { diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 7eb16bee5191..c70bd1585e8a 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -155,6 +155,19 @@ struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi); /* * audio: */ +/* Supported HDMI Audio channels and rates */ +#define MSM_HDMI_AUDIO_CHANNEL_2 0 +#define MSM_HDMI_AUDIO_CHANNEL_4 1 +#define MSM_HDMI_AUDIO_CHANNEL_6 2 +#define MSM_HDMI_AUDIO_CHANNEL_8 3 + +#define HDMI_SAMPLE_RATE_32KHZ 0 +#define HDMI_SAMPLE_RATE_44_1KHZ 1 +#define HDMI_SAMPLE_RATE_48KHZ 2 +#define HDMI_SAMPLE_RATE_88_2KHZ 3 +#define HDMI_SAMPLE_RATE_96KHZ 4 +#define HDMI_SAMPLE_RATE_176_4KHZ 5 +#define HDMI_SAMPLE_RATE_192KHZ 6 int hdmi_audio_update(struct hdmi *hdmi); int hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, diff --git a/drivers/gpu/drm/msm/mdp/mdp_format.c b/drivers/gpu/drm/msm/mdp/mdp_format.c index 1c2caffc97e4..b4a8aa4490ee 100644 --- a/drivers/gpu/drm/msm/mdp/mdp_format.c +++ b/drivers/gpu/drm/msm/mdp/mdp_format.c @@ -105,6 +105,12 @@ static const struct mdp_format formats[] = { MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), FMT(XRGB8888, 8, 8, 8, 8, 1, 0, 2, 3, false, true, 4, 4, MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), + FMT(XBGR8888, 8, 8, 8, 8, 2, 0, 1, 3, false, true, 4, 4, + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), + FMT(RGBX8888, 8, 8, 8, 8, 3, 1, 0, 2, false, true, 4, 4, + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), + FMT(BGRX8888, 8, 8, 8, 8, 3, 2, 0, 1, false, true, 4, 4, + MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), FMT(RGB888, 0, 8, 8, 8, 1, 0, 2, 0, false, true, 3, 3, MDP_PLANE_INTERLEAVED, CHROMA_FULL, false), FMT(BGR888, 0, 8, 8, 8, 2, 0, 1, 0, false, true, 3, 3, diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index edb71cb9e5f7..68184c7f8089 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -29,9 +29,27 @@ #define CORE_HC_MODE 0x78 #define HC_MODE_EN 0x1 + #define CORE_POWER 0x0 #define CORE_SW_RST BIT(7) +#define CORE_PWRCTL_STATUS 0xdc +#define CORE_PWRCTL_MASK 0xe0 +#define CORE_PWRCTL_CLEAR 0xe4 +#define CORE_PWRCTL_CTL 0xe8 + +#define CORE_PWRCTL_BUS_OFF BIT(0) +#define CORE_PWRCTL_BUS_ON BIT(1) +#define CORE_PWRCTL_IO_LOW BIT(2) +#define CORE_PWRCTL_IO_HIGH BIT(3) +#define CORE_PWRCTL_BUS_SUCCESS BIT(0) +#define CORE_PWRCTL_IO_SUCCESS BIT(2) +#define REQ_BUS_OFF BIT(0) +#define REQ_BUS_ON BIT(1) +#define REQ_IO_LOW BIT(2) +#define REQ_IO_HIGH BIT(3) + +#define INT_MASK 0xf #define MAX_PHASES 16 #define CORE_DLL_LOCK BIT(7) #define CORE_DLL_EN BIT(16) @@ -56,11 +74,11 @@ struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ + int pwr_irq; /* power irq */ struct clk *clk; /* main SD/MMC bus clock */ struct clk *pclk; /* SDHC peripheral bus clock */ struct clk *bus_clk; /* SDHC bus voter clock */ struct mmc_host *mmc; - struct sdhci_pltfm_data sdhci_msm_pdata; }; /* Platform specific tuning */ @@ -411,6 +429,39 @@ retry: return rc; } +static void sdhci_msm_voltage_switch(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + u32 irq_status, irq_ack = 0; + + irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); + irq_status &= INT_MASK; + + writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR); + + if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF)) + irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH)) + irq_ack |= CORE_PWRCTL_IO_SUCCESS; + + /* + * The driver has to acknowledge the interrupt, switch voltages and + * report back if it succeded or not to this register. The voltage + * switches are handled by the sdhci core, so just report success. + */ + writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); +} + +static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) +{ + struct sdhci_host *host = (struct sdhci_host *)data; + + sdhci_msm_voltage_switch(host); + + return IRQ_HANDLED; +} + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -418,12 +469,20 @@ static const struct of_device_id sdhci_msm_dt_match[] = { MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); -static struct sdhci_ops sdhci_msm_ops = { +static const struct sdhci_ops sdhci_msm_ops = { .platform_execute_tuning = sdhci_msm_execute_tuning, .reset = sdhci_reset, .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = sdhci_set_uhs_signaling, + .voltage_switch = sdhci_msm_voltage_switch, +}; + +static const struct sdhci_pltfm_data sdhci_msm_pdata = { + .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_SINGLE_POWER_WRITE | + SDHCI_QUIRK_NO_CARD_NO_RESET, + .ops = &sdhci_msm_ops, }; static int sdhci_msm_probe(struct platform_device *pdev) @@ -437,17 +496,12 @@ static int sdhci_msm_probe(struct platform_device *pdev) u32 core_version, caps; u8 core_major; - msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL); - if (!msm_host) - return -ENOMEM; - - msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops; - host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0); + host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host)); if (IS_ERR(host)) return PTR_ERR(host); pltfm_host = sdhci_priv(host); - pltfm_host->priv = msm_host; + msm_host = sdhci_pltfm_priv(pltfm_host); msm_host->mmc = host->mmc; msm_host->pdev = pdev; @@ -522,10 +576,6 @@ static int sdhci_msm_probe(struct platform_device *pdev) /* Set HC_MODE_EN bit in HC_MODE register */ writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE)); - host->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET; - host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; - host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE; - host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n", host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> @@ -549,6 +599,22 @@ static int sdhci_msm_probe(struct platform_device *pdev) CORE_VENDOR_SPEC_CAPABILITIES0); } + /* Setup IRQ for handling power/voltage tasks with PMIC */ + msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq"); + if (msm_host->pwr_irq < 0) { + dev_err(&pdev->dev, "Get pwr_irq failed (%d)\n", + msm_host->pwr_irq); + goto clk_disable; + } + + ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL, + sdhci_msm_pwr_irq, IRQF_ONESHOT, + dev_name(&pdev->dev), host); + if (ret) { + dev_err(&pdev->dev, "Request IRQ failed (%d)\n", ret); + goto clk_disable; + } + ret = sdhci_add_host(host); if (ret) goto clk_disable; @@ -571,16 +637,16 @@ static int sdhci_msm_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_msm_host *msm_host = pltfm_host->priv; + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); - sdhci_pltfm_free(pdev); clk_disable_unprepare(msm_host->clk); clk_disable_unprepare(msm_host->pclk); if (!IS_ERR(msm_host->bus_clk)) clk_disable_unprepare(msm_host->bus_clk); + sdhci_pltfm_free(pdev); return 0; } diff --git a/drivers/net/wireless/ath/wcn36xx/Kconfig b/drivers/net/wireless/ath/wcn36xx/Kconfig index 591ebaea8265..394fe5b77c90 100644 --- a/drivers/net/wireless/ath/wcn36xx/Kconfig +++ b/drivers/net/wireless/ath/wcn36xx/Kconfig @@ -1,6 +1,6 @@ config WCN36XX tristate "Qualcomm Atheros WCN3660/3680 support" - depends on MAC80211 && HAS_DMA + depends on MAC80211 && HAS_DMA && QCOM_SMD ---help--- This module adds support for wireless adapters based on Qualcomm Atheros WCN3660 and WCN3680 mobile chipsets. diff --git a/drivers/net/wireless/ath/wcn36xx/Makefile b/drivers/net/wireless/ath/wcn36xx/Makefile index 9f6370f0cabc..50c43b4382ba 100644 --- a/drivers/net/wireless/ath/wcn36xx/Makefile +++ b/drivers/net/wireless/ath/wcn36xx/Makefile @@ -1,10 +1,7 @@ -obj-$(CONFIG_WCN36XX) := wcn36xx.o wcn36xx-platform.o +obj-$(CONFIG_WCN36XX) := wcn36xx.o wcn36xx-y += main.o \ dxe.o \ txrx.o \ smd.o \ pmc.o \ debug.o - -wcn36xx-platform-y += wcn36xx-msm.o\ - wcnss_core.o diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index af0e515f634d..87dfdaf9044c 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -23,6 +23,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include "wcn36xx.h" #include "txrx.h" @@ -35,26 +36,27 @@ void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low) return ch->head_blk_ctl->bd_cpu_addr; } +static void wcn36xx_ccu_write_register(struct wcn36xx *wcn, int addr, int data) +{ + wcn36xx_dbg(WCN36XX_DBG_DXE, + "wcn36xx_ccu_write_register: addr=%x, data=%x\n", + addr, data); + + writel(data, wcn->ccu_base + addr); +} + static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data) { wcn36xx_dbg(WCN36XX_DBG_DXE, "wcn36xx_dxe_write_register: addr=%x, data=%x\n", addr, data); - writel(data, wcn->mmio + addr); + writel(data, wcn->dxe_base + addr); } -#define wcn36xx_dxe_write_register_x(wcn, reg, reg_data) \ -do { \ - if (wcn->chip_version != WCN36XX_CHIP_3660) \ - wcn36xx_dxe_write_register(wcn, reg ## _3680, reg_data); \ - else \ - wcn36xx_dxe_write_register(wcn, reg ## _3660, reg_data); \ -} while (0) \ - static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data) { - *data = readl(wcn->mmio + addr); + *data = readl(wcn->dxe_base + addr); wcn36xx_dbg(WCN36XX_DBG_DXE, "wcn36xx_dxe_read_register: addr=%x, data=%x\n", @@ -150,9 +152,12 @@ int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn) goto out_err; /* Initialize SMSM state Clear TX Enable RING EMPTY STATE */ - ret = wcn->ctrl_ops->smsm_change_state( - WCN36XX_SMSM_WLAN_TX_ENABLE, - WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY); + ret = qcom_smem_state_update_bits(wcn->tx_enable_state, + WCN36XX_SMSM_WLAN_TX_ENABLE | + WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY, + WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY); + if (ret) + goto out_err; return 0; @@ -677,9 +682,9 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, * notify chip about new frame through SMSM bus. */ if (is_low && vif_priv->pw_state == WCN36XX_BMPS) { - wcn->ctrl_ops->smsm_change_state( - 0, - WCN36XX_SMSM_WLAN_TX_ENABLE); + qcom_smem_state_update_bits(wcn->tx_rings_empty_state, + WCN36XX_SMSM_WLAN_TX_ENABLE, + WCN36XX_SMSM_WLAN_TX_ENABLE); } else { /* indicate End Of Packet and generate interrupt on descriptor * done. @@ -701,9 +706,13 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) reg_data = WCN36XX_DXE_REG_RESET; wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data); - /* Setting interrupt path */ - reg_data = WCN36XX_DXE_CCU_INT; - wcn36xx_dxe_write_register_x(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data); + /* Select channels for rx avail and xfer done interrupts... */ + reg_data = (WCN36XX_DXE_INT_CH3_MASK | WCN36XX_DXE_INT_CH1_MASK) << 16 | + WCN36XX_DXE_INT_CH0_MASK | WCN36XX_DXE_INT_CH4_MASK; + if (wcn->is_pronto) + wcn36xx_ccu_write_register(wcn, WCN36XX_CCU_DXE_INT_SELECT_PRONTO, reg_data); + else + wcn36xx_ccu_write_register(wcn, WCN36XX_CCU_DXE_INT_SELECT_RIVA, reg_data); /***************************************/ /* Init descriptors for TX LOW channel */ diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.h b/drivers/net/wireless/ath/wcn36xx/dxe.h index 3eca4f9594f2..c012e807753b 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.h +++ b/drivers/net/wireless/ath/wcn36xx/dxe.h @@ -28,11 +28,10 @@ H2H_TEST_RX_TX = DMA2 */ /* DXE registers */ -#define WCN36XX_DXE_MEM_REG 0x202000 +#define WCN36XX_DXE_MEM_REG 0 -#define WCN36XX_DXE_CCU_INT 0xA0011 -#define WCN36XX_DXE_REG_CCU_INT_3660 0x200b10 -#define WCN36XX_DXE_REG_CCU_INT_3680 0x2050dc +#define WCN36XX_CCU_DXE_INT_SELECT_RIVA 0x310 +#define WCN36XX_CCU_DXE_INT_SELECT_PRONTO 0x10dc /* TODO This must calculated properly but not hardcoded */ #define WCN36XX_DXE_CTRL_TX_L 0x328a44 diff --git a/drivers/net/wireless/ath/wcn36xx/hal.h b/drivers/net/wireless/ath/wcn36xx/hal.h index dbf1769a9699..4f87ef1e1eb8 100644 --- a/drivers/net/wireless/ath/wcn36xx/hal.h +++ b/drivers/net/wireless/ath/wcn36xx/hal.h @@ -51,7 +51,7 @@ #define WCN36XX_HAL_STA_INVALID_IDX 0xFF #define WCN36XX_HAL_BSS_INVALID_IDX 0xFF -/* Default Beacon template size. */ +/* Default Beacon template size */ #define BEACON_TEMPLATE_SIZE 0x180 /* Minimum PVM size that the FW expects. See comment in smd.c for details. */ @@ -350,8 +350,6 @@ enum wcn36xx_hal_host_msg_type { WCN36XX_HAL_AVOID_FREQ_RANGE_IND = 233, - WCN36XX_HAL_PRINT_REG_INFO_IND = 259, - WCN36XX_HAL_MSG_MAX = WCN36XX_HAL_MSG_TYPE_MAX_ENUM_SIZE }; @@ -2889,11 +2887,13 @@ struct update_beacon_rsp_msg { struct wcn36xx_hal_send_beacon_req_msg { struct wcn36xx_hal_msg_header header; - /* length of the template + sizeof(beacon_length) */ - u32 template_length; + /* length of the template + 6. Only qcom knows why */ + u32 beacon_length6; - /* Beacon data. */ + /* length of the template. */ u32 beacon_length; + + /* Beacon data. */ u8 beacon[BEACON_TEMPLATE_SIZE - sizeof(u32)]; u8 bssid[ETH_ALEN]; @@ -4123,7 +4123,7 @@ struct wcn36xx_hal_update_scan_params_req { /* Update scan params - sent from host to PNO to be used during PNO * scanningx */ -struct update_scan_params_req_ex { +struct wcn36xx_hal_update_scan_params_req_ex { struct wcn36xx_hal_msg_header header; @@ -4151,7 +4151,7 @@ struct update_scan_params_req_ex { /* Cb State */ enum phy_chan_bond_state state; -}; +} __packed; /* Update scan params - sent from host to PNO to be used during PNO * scanningx */ diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index cc4903cdc666..2d9402e718fc 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -19,6 +19,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include "wcn36xx.h" unsigned int wcn36xx_dbg_mask; @@ -26,14 +32,14 @@ module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644); MODULE_PARM_DESC(debug_mask, "Debugging mask"); #define CHAN2G(_freq, _idx) { \ - .band = IEEE80211_BAND_2GHZ, \ + .band = NL80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_idx), \ .max_power = 25, \ } #define CHAN5G(_freq, _idx) { \ - .band = IEEE80211_BAND_5GHZ, \ + .band = NL80211_BAND_5GHZ, \ .center_freq = (_freq), \ .hw_value = (_idx), \ .max_power = 25, \ @@ -313,8 +319,6 @@ static int wcn36xx_start(struct ieee80211_hw *hw) wcn36xx_feat_caps_info(wcn); } - wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_ENABLE_MC_ADDR_LIST, 1); - /* DMA channel initialization */ ret = wcn36xx_dxe_init(wcn); if (ret) { @@ -381,8 +385,6 @@ static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) return 0; } -#define WCN36XX_SUPPORTED_FILTERS (FIF_ALLMULTI) - static void wcn36xx_configure_filter(struct ieee80211_hw *hw, unsigned int changed, unsigned int *total, u64 multicast) @@ -394,7 +396,7 @@ static void wcn36xx_configure_filter(struct ieee80211_hw *hw, wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n"); - *total &= WCN36XX_SUPPORTED_FILTERS; + *total &= FIF_ALLMULTI; fp = (void *)(unsigned long)multicast; list_for_each_entry(tmp, &wcn->vif_list, list) { @@ -566,27 +568,61 @@ out: return ret; } -static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const u8 *mac_addr) +static void wcn36xx_hw_scan_worker(struct work_struct *work) { - struct wcn36xx *wcn = hw->priv; + struct wcn36xx *wcn = container_of(work, struct wcn36xx, scan_work); + struct cfg80211_scan_request *req = wcn->scan_req; + u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX]; + int i; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 scan %d channels worker\n", req->n_channels); + + for (i = 0; i < req->n_channels; i++) + channels[i] = req->channels[i]->hw_value; + + wcn36xx_smd_update_scan_params(wcn, channels, req->n_channels); wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN); - wcn36xx_smd_start_scan(wcn); + for (i = 0; i < req->n_channels; i++) { + wcn->scan_freq = req->channels[i]->center_freq; + wcn->scan_band = req->channels[i]->band; + + wcn36xx_smd_start_scan(wcn, req->channels[i]->hw_value); + msleep(30); + wcn36xx_smd_end_scan(wcn, req->channels[i]->hw_value); + + wcn->scan_freq = 0; + } + wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN); + + ieee80211_scan_completed(wcn->hw, false); + + mutex_lock(&wcn->scan_lock); + wcn->scan_req = NULL; + mutex_unlock(&wcn->scan_lock); } -static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static int wcn36xx_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) { struct wcn36xx *wcn = hw->priv; - wcn36xx_smd_end_scan(wcn); - wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN); + mutex_lock(&wcn->scan_lock); + if (wcn->scan_req) { + mutex_unlock(&wcn->scan_lock); + return -EBUSY; + } + wcn->scan_req = &hw_req->req; + mutex_unlock(&wcn->scan_lock); + + schedule_work(&wcn->scan_work); + + return 0; } static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, - enum ieee80211_band band) + enum nl80211_band band) { int i, size; u16 *rates_table; @@ -599,7 +635,7 @@ static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates); rates_table = sta_priv->supported_rates.dsss_rates; - if (band == IEEE80211_BAND_2GHZ) { + if (band == NL80211_BAND_2GHZ) { for (i = 0; i < size; i++) { if (rates & 0x01) { rates_table[i] = wcn_2ghz_rates[i].hw_value; @@ -993,8 +1029,7 @@ static const struct ieee80211_ops wcn36xx_ops = { .configure_filter = wcn36xx_configure_filter, .tx = wcn36xx_tx, .set_key = wcn36xx_set_key, - .sw_scan_start = wcn36xx_sw_scan_start, - .sw_scan_complete = wcn36xx_sw_scan_complete, + .hw_scan = wcn36xx_hw_scan, .bss_info_changed = wcn36xx_bss_info_changed, .set_rts_threshold = wcn36xx_set_rts_threshold, .sta_add = wcn36xx_sta_add, @@ -1019,18 +1054,18 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) ieee80211_hw_set(wcn->hw, SUPPORTS_PS); ieee80211_hw_set(wcn->hw, SIGNAL_DBM); ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL); - - /* 3620 powersaving currently unstable */ - if (wcn->chip_version == WCN36XX_CHIP_3620) - __clear_bit(IEEE80211_HW_SUPPORTS_PS, wcn->hw->flags); + ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS); wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); - wcn->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wcn_band_2ghz; - wcn->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wcn_band_5ghz; + wcn->hw->wiphy->bands[NL80211_BAND_2GHZ] = &wcn_band_2ghz; + wcn->hw->wiphy->bands[NL80211_BAND_5GHZ] = &wcn_band_5ghz; + + wcn->hw->wiphy->max_scan_ssids = WCN36XX_MAX_SCAN_SSIDS; + wcn->hw->wiphy->max_scan_ie_len = WCN36XX_MAX_SCAN_IE_LEN; wcn->hw->wiphy->cipher_suites = cipher_suites; wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); @@ -1059,10 +1094,13 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, struct platform_device *pdev) { + struct device_node *mmio_node; struct resource *res; + int index; + int ret; + /* Set TX IRQ */ - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, - "wcnss_wlantx_irq"); + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tx"); if (!res) { wcn36xx_err("failed to get tx_irq\n"); return -ENOENT; @@ -1070,38 +1108,77 @@ static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, wcn->tx_irq = res->start; /* Set RX IRQ */ - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, - "wcnss_wlanrx_irq"); + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "rx"); if (!res) { wcn36xx_err("failed to get rx_irq\n"); return -ENOENT; } wcn->rx_irq = res->start; - /* Map the memory */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "wcnss_mmio"); - if (!res) { - wcn36xx_err("failed to get mmio\n"); + /* Acquire SMSM tx enable handle */ + wcn->tx_enable_state = qcom_smem_state_get(&pdev->dev, + "tx-enable", &wcn->tx_enable_state_bit); + if (IS_ERR(wcn->tx_enable_state)) { + wcn36xx_err("failed to get tx-enable state\n"); return -ENOENT; } - wcn->mmio = ioremap(res->start, resource_size(res)); - if (!wcn->mmio) { - wcn36xx_err("failed to map io memory\n"); - return -ENOMEM; + + /* Acquire SMSM tx rings empty handle */ + wcn->tx_rings_empty_state = qcom_smem_state_get(&pdev->dev, + "tx-rings-empty", &wcn->tx_rings_empty_state_bit); + if (IS_ERR(wcn->tx_rings_empty_state)) { + wcn36xx_err("failed to get tx-rings-empty state\n"); + return -ENOENT; + } + + mmio_node = of_parse_phandle(pdev->dev.parent->of_node, "qcom,mmio", 0); + if (!mmio_node) { + wcn36xx_err("failed to acquire qcom,mmio reference\n"); + return -EINVAL; + } + + wcn->is_pronto = !!of_device_is_compatible(mmio_node, "qcom,pronto"); + + /* Map the CCU memory */ + index = of_property_match_string(mmio_node, "reg-names", "ccu"); + wcn->ccu_base = of_iomap(mmio_node, index); + if (!wcn->ccu_base) { + wcn36xx_err("failed to map ccu memory\n"); + ret = -ENOMEM; + goto put_mmio_node; + } + + /* Map the DXE memory */ + index = of_property_match_string(mmio_node, "reg-names", "dxe"); + wcn->dxe_base = of_iomap(mmio_node, index); + if (!wcn->dxe_base) { + wcn36xx_err("failed to map dxe memory\n"); + ret = -ENOMEM; + goto unmap_ccu; } + + of_node_put(mmio_node); return 0; + +unmap_ccu: + iounmap(wcn->ccu_base); +put_mmio_node: + of_node_put(mmio_node); + return ret; } static int wcn36xx_probe(struct platform_device *pdev) { struct ieee80211_hw *hw; struct wcn36xx *wcn; + void *wcnss; int ret; - u8 addr[ETH_ALEN]; + const u8 *addr; wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n"); + wcnss = dev_get_drvdata(pdev->dev.parent); + hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops); if (!hw) { wcn36xx_err("failed to alloc hw\n"); @@ -1112,25 +1189,26 @@ static int wcn36xx_probe(struct platform_device *pdev) wcn = hw->priv; wcn->hw = hw; wcn->dev = &pdev->dev; - wcn->dev->dma_mask = kzalloc(sizeof(*wcn->dev->dma_mask), GFP_KERNEL); - if (!wcn->dev->dma_mask) { - ret = -ENOMEM; - goto dma_mask_err; - } - dma_set_mask_and_coherent(wcn->dev, DMA_BIT_MASK(32)); - wcn->wcn36xx_data = pdev->dev.platform_data; - wcn->ctrl_ops = &wcn->wcn36xx_data->ctrl_ops; - wcn->wcn36xx_data->wcn = wcn; - if (!wcn->ctrl_ops->get_chip_type) { - dev_err(&pdev->dev, "Missing ops->get_chip_type\n"); - ret = -EINVAL; + mutex_init(&wcn->hal_mutex); + mutex_init(&wcn->scan_lock); + + INIT_WORK(&wcn->scan_work, wcn36xx_hw_scan_worker); + + wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process); + if (IS_ERR(wcn->smd_channel)) { + wcn36xx_err("failed to open WLAN_CTRL channel\n"); + ret = PTR_ERR(wcn->smd_channel); goto out_wq; } - wcn->chip_version = wcn->ctrl_ops->get_chip_type(wcn); - mutex_init(&wcn->hal_mutex); + qcom_smd_set_drvdata(wcn->smd_channel, hw); - if (!wcn->ctrl_ops->get_hw_mac(wcn, addr)) { + addr = of_get_property(pdev->dev.of_node, "local-mac-address", &ret); + if (addr && ret != ETH_ALEN) { + wcn36xx_err("invalid local-mac-address\n"); + ret = -EINVAL; + goto out_wq; + } else if (addr) { wcn36xx_info("mac address: %pM\n", addr); SET_IEEE80211_PERM_ADDR(wcn->hw, addr); } @@ -1147,14 +1225,14 @@ static int wcn36xx_probe(struct platform_device *pdev) return 0; out_unmap: - iounmap(wcn->mmio); + iounmap(wcn->ccu_base); + iounmap(wcn->dxe_base); out_wq: - kfree(wcn->dev->dma_mask); -dma_mask_err: ieee80211_free_hw(hw); out_err: return ret; } + static int wcn36xx_remove(struct platform_device *pdev) { struct ieee80211_hw *hw = platform_get_drvdata(pdev); @@ -1165,41 +1243,33 @@ static int wcn36xx_remove(struct platform_device *pdev) mutex_destroy(&wcn->hal_mutex); ieee80211_unregister_hw(hw); - iounmap(wcn->mmio); + + qcom_smem_state_put(wcn->tx_enable_state); + qcom_smem_state_put(wcn->tx_rings_empty_state); + + iounmap(wcn->dxe_base); + iounmap(wcn->ccu_base); ieee80211_free_hw(hw); return 0; } -static const struct platform_device_id wcn36xx_platform_id_table[] = { - { - .name = "wcn36xx", - .driver_data = 0 - }, + +static const struct of_device_id wcn36xx_of_match[] = { + { .compatible = "qcom,wcnss-wlan" }, {} }; -MODULE_DEVICE_TABLE(platform, wcn36xx_platform_id_table); +MODULE_DEVICE_TABLE(of, wcn36xx_of_match); static struct platform_driver wcn36xx_driver = { .probe = wcn36xx_probe, .remove = wcn36xx_remove, .driver = { .name = "wcn36xx", + .of_match_table = wcn36xx_of_match, }, - .id_table = wcn36xx_platform_id_table, }; -static int __init wcn36xx_init(void) -{ - platform_driver_register(&wcn36xx_driver); - return 0; -} -module_init(wcn36xx_init); - -static void __exit wcn36xx_exit(void) -{ - platform_driver_unregister(&wcn36xx_driver); -} -module_exit(wcn36xx_exit); +module_platform_driver(wcn36xx_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com"); diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 61754967e2ef..87a62eb6228c 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "smd.h" struct wcn36xx_cfg_val { @@ -104,11 +105,11 @@ static void wcn36xx_smd_set_bss_nw_type(struct wcn36xx *wcn, struct ieee80211_sta *sta, struct wcn36xx_hal_config_bss_params *bss_params) { - if (IEEE80211_BAND_5GHZ == WCN36XX_BAND(wcn)) + if (NL80211_BAND_5GHZ == WCN36XX_BAND(wcn)) bss_params->nw_type = WCN36XX_HAL_11A_NW_TYPE; else if (sta && sta->ht_cap.ht_supported) bss_params->nw_type = WCN36XX_HAL_11N_NW_TYPE; - else if (sta && (sta->supp_rates[IEEE80211_BAND_2GHZ] & 0x7f)) + else if (sta && (sta->supp_rates[NL80211_BAND_2GHZ] & 0x7f)) bss_params->nw_type = WCN36XX_HAL_11G_NW_TYPE; else bss_params->nw_type = WCN36XX_HAL_11B_NW_TYPE; @@ -253,7 +254,7 @@ static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len) init_completion(&wcn->hal_rsp_compl); start = jiffies; - ret = wcn->ctrl_ops->tx(wcn, wcn->hal_buf, len); + ret = qcom_smd_send(wcn->smd_channel, wcn->hal_buf, len); if (ret) { wcn36xx_err("HAL TX failed\n"); goto out; @@ -521,7 +522,7 @@ out: return ret; } -int wcn36xx_smd_start_scan(struct wcn36xx *wcn) +int wcn36xx_smd_start_scan(struct wcn36xx *wcn, u8 scan_channel) { struct wcn36xx_hal_start_scan_req_msg msg_body; int ret = 0; @@ -529,7 +530,7 @@ int wcn36xx_smd_start_scan(struct wcn36xx *wcn) mutex_lock(&wcn->hal_mutex); INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_SCAN_REQ); - msg_body.scan_channel = WCN36XX_HW_CHANNEL(wcn); + msg_body.scan_channel = scan_channel; PREPARE_HAL_BUF(wcn->hal_buf, msg_body); @@ -551,7 +552,7 @@ out: return ret; } -int wcn36xx_smd_end_scan(struct wcn36xx *wcn) +int wcn36xx_smd_end_scan(struct wcn36xx *wcn, u8 scan_channel) { struct wcn36xx_hal_end_scan_req_msg msg_body; int ret = 0; @@ -559,7 +560,7 @@ int wcn36xx_smd_end_scan(struct wcn36xx *wcn) mutex_lock(&wcn->hal_mutex); INIT_HAL_MSG(msg_body, WCN36XX_HAL_END_SCAN_REQ); - msg_body.scan_channel = WCN36XX_HW_CHANNEL(wcn); + msg_body.scan_channel = scan_channel; PREPARE_HAL_BUF(wcn->hal_buf, msg_body); @@ -674,22 +675,25 @@ static int wcn36xx_smd_update_scan_params_rsp(void *buf, size_t len) return 0; } -int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn) +int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn, + u8 *channels, size_t channel_count) { - struct wcn36xx_hal_update_scan_params_req msg_body; + struct wcn36xx_hal_update_scan_params_req_ex msg_body; int ret = 0; mutex_lock(&wcn->hal_mutex); INIT_HAL_MSG(msg_body, WCN36XX_HAL_UPDATE_SCAN_PARAM_REQ); - msg_body.dot11d_enabled = 0; - msg_body.dot11d_resolved = 0; - msg_body.channel_count = 26; + msg_body.dot11d_enabled = false; + msg_body.dot11d_resolved = true; + + msg_body.channel_count = channel_count; + memcpy(msg_body.channels, channels, channel_count); msg_body.active_min_ch_time = 60; msg_body.active_max_ch_time = 120; msg_body.passive_min_ch_time = 60; msg_body.passive_max_ch_time = 110; - msg_body.state = 0; + msg_body.state = PHY_SINGLE_CHANNEL_CENTERED; PREPARE_HAL_BUF(wcn->hal_buf, msg_body); @@ -1388,7 +1392,6 @@ int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif, { struct wcn36xx_hal_send_beacon_req_msg msg_body; int ret = 0, pad, pvm_len; - u32 beacon_length; mutex_lock(&wcn->hal_mutex); INIT_HAL_MSG(msg_body, WCN36XX_HAL_SEND_BEACON_REQ); @@ -1400,16 +1403,16 @@ int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_MESH_POINT) pad = 0; - beacon_length = skb_beacon->len + pad; - msg_body.template_length = beacon_length + sizeof(beacon_length); + msg_body.beacon_length = skb_beacon->len + pad; + /* TODO need to find out why + 6 is needed */ + msg_body.beacon_length6 = msg_body.beacon_length + 6; - if (msg_body.template_length > BEACON_TEMPLATE_SIZE) { + if (msg_body.beacon_length > BEACON_TEMPLATE_SIZE) { wcn36xx_err("Beacon is to big: beacon size=%d\n", - msg_body.template_length); + msg_body.beacon_length); ret = -ENOMEM; goto out; } - msg_body.beacon_length = beacon_length; memcpy(msg_body.beacon, skb_beacon->data, skb_beacon->len); memcpy(msg_body.bssid, vif->addr, ETH_ALEN); @@ -2178,13 +2181,18 @@ out: return ret; } -static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len) +int wcn36xx_smd_rsp_process(struct qcom_smd_channel *channel, + const void *buf, size_t len) { - struct wcn36xx_hal_msg_header *msg_header = buf; + struct wcn36xx_hal_msg_header msg_header; + struct ieee80211_hw *hw = qcom_smd_get_drvdata(channel); + struct wcn36xx *wcn = hw->priv; struct wcn36xx_hal_ind_msg *msg_ind; wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "SMD <<< ", buf, len); - switch (msg_header->msg_type) { + memcpy_fromio(&msg_header, buf, sizeof(struct wcn36xx_hal_msg_header)); + + switch (msg_header.msg_type) { case WCN36XX_HAL_START_RSP: case WCN36XX_HAL_CONFIG_STA_RSP: case WCN36XX_HAL_CONFIG_BSS_RSP: @@ -2220,44 +2228,42 @@ static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len) case WCN36XX_HAL_CH_SWITCH_RSP: case WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_RSP: case WCN36XX_HAL_8023_MULTICAST_LIST_RSP: - memcpy(wcn->hal_buf, buf, len); + memcpy_fromio(wcn->hal_buf, buf, len); wcn->hal_rsp_len = len; complete(&wcn->hal_rsp_compl); break; - case WCN36XX_HAL_DEL_BA_IND: - case WCN36XX_HAL_PRINT_REG_INFO_IND: case WCN36XX_HAL_COEX_IND: case WCN36XX_HAL_AVOID_FREQ_RANGE_IND: case WCN36XX_HAL_OTA_TX_COMPL_IND: case WCN36XX_HAL_MISSED_BEACON_IND: case WCN36XX_HAL_DELETE_STA_CONTEXT_IND: - msg_ind = kmalloc(sizeof(*msg_ind), GFP_KERNEL); - if (!msg_ind) - goto nomem; - msg_ind->msg_len = len; - msg_ind->msg = kmemdup(buf, len, GFP_KERNEL); - if (!msg_ind->msg) { - kfree(msg_ind); -nomem: + msg_ind = kmalloc(sizeof(*msg_ind) + len, GFP_ATOMIC); + if (!msg_ind) { /* * FIXME: Do something smarter then just * printing an error. */ wcn36xx_err("Run out of memory while handling SMD_EVENT (%d)\n", - msg_header->msg_type); + msg_header.msg_type); break; } - mutex_lock(&wcn->hal_ind_mutex); + + msg_ind->msg_len = len; + memcpy_fromio(msg_ind->msg, buf, len); + + spin_lock(&wcn->hal_ind_lock); list_add_tail(&msg_ind->list, &wcn->hal_ind_queue); queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work); - mutex_unlock(&wcn->hal_ind_mutex); + spin_unlock(&wcn->hal_ind_lock); wcn36xx_dbg(WCN36XX_DBG_HAL, "indication arrived\n"); break; default: wcn36xx_err("SMD_EVENT (%d) not supported\n", - msg_header->msg_type); + msg_header.msg_type); } + + return 0; } static void wcn36xx_ind_smd_work(struct work_struct *work) { @@ -2265,8 +2271,9 @@ static void wcn36xx_ind_smd_work(struct work_struct *work) container_of(work, struct wcn36xx, hal_ind_work); struct wcn36xx_hal_msg_header *msg_header; struct wcn36xx_hal_ind_msg *hal_ind_msg; + unsigned long flags; - mutex_lock(&wcn->hal_ind_mutex); + spin_lock_irqsave(&wcn->hal_ind_lock, flags); hal_ind_msg = list_first_entry(&wcn->hal_ind_queue, struct wcn36xx_hal_ind_msg, @@ -2275,8 +2282,6 @@ static void wcn36xx_ind_smd_work(struct work_struct *work) msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg; switch (msg_header->msg_type) { - case WCN36XX_HAL_DEL_BA_IND: - case WCN36XX_HAL_PRINT_REG_INFO_IND: case WCN36XX_HAL_COEX_IND: case WCN36XX_HAL_AVOID_FREQ_RANGE_IND: break; @@ -2300,9 +2305,8 @@ static void wcn36xx_ind_smd_work(struct work_struct *work) msg_header->msg_type); } list_del(wcn->hal_ind_queue.next); - kfree(hal_ind_msg->msg); + spin_unlock_irqrestore(&wcn->hal_ind_lock, flags); kfree(hal_ind_msg); - mutex_unlock(&wcn->hal_ind_mutex); } int wcn36xx_smd_open(struct wcn36xx *wcn) { @@ -2315,25 +2319,15 @@ int wcn36xx_smd_open(struct wcn36xx *wcn) } INIT_WORK(&wcn->hal_ind_work, wcn36xx_ind_smd_work); INIT_LIST_HEAD(&wcn->hal_ind_queue); - mutex_init(&wcn->hal_ind_mutex); - - ret = wcn->ctrl_ops->open(wcn, wcn36xx_smd_rsp_process); - if (ret) { - wcn36xx_err("failed to open control channel\n"); - goto free_wq; - } + spin_lock_init(&wcn->hal_ind_lock); - return ret; + return 0; -free_wq: - destroy_workqueue(wcn->hal_ind_wq); out: return ret; } void wcn36xx_smd_close(struct wcn36xx *wcn) { - wcn->ctrl_ops->close(wcn); destroy_workqueue(wcn->hal_ind_wq); - mutex_destroy(&wcn->hal_ind_mutex); } diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h index d93e3fd73831..8892ccd67b14 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.h +++ b/drivers/net/wireless/ath/wcn36xx/smd.h @@ -46,11 +46,12 @@ struct wcn36xx_fw_msg_status_rsp { struct wcn36xx_hal_ind_msg { struct list_head list; - u8 *msg; size_t msg_len; + u8 msg[]; }; struct wcn36xx; +struct qcom_smd_channel; int wcn36xx_smd_open(struct wcn36xx *wcn); void wcn36xx_smd_close(struct wcn36xx *wcn); @@ -59,11 +60,11 @@ int wcn36xx_smd_load_nv(struct wcn36xx *wcn); int wcn36xx_smd_start(struct wcn36xx *wcn); int wcn36xx_smd_stop(struct wcn36xx *wcn); int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode); -int wcn36xx_smd_start_scan(struct wcn36xx *wcn); -int wcn36xx_smd_end_scan(struct wcn36xx *wcn); +int wcn36xx_smd_start_scan(struct wcn36xx *wcn, u8 scan_channel); +int wcn36xx_smd_end_scan(struct wcn36xx *wcn, u8 scan_channel); int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode); -int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn); +int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn, u8 *channels, size_t channel_count); int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif); int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr); int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index); @@ -127,6 +128,10 @@ int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 sta_index); int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index); int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value); + +int wcn36xx_smd_rsp_process(struct qcom_smd_channel *channel, + const void *buf, size_t len); + int wcn36xx_smd_set_mc_list(struct wcn36xx *wcn, struct ieee80211_vif *vif, struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp); diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c index 37f13410e633..8c387a0a3c09 100644 --- a/drivers/net/wireless/ath/wcn36xx/txrx.c +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -45,9 +45,20 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) skb_put(skb, bd->pdu.mpdu_header_off + bd->pdu.mpdu_len); skb_pull(skb, bd->pdu.mpdu_header_off); + hdr = (struct ieee80211_hdr *) skb->data; + fc = __le16_to_cpu(hdr->frame_control); + sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)); + + /* When scanning associate beacons to this */ + if (ieee80211_is_beacon(hdr->frame_control) && wcn->scan_freq) { + status.freq = wcn->scan_freq; + status.band = wcn->scan_band; + } else { + status.freq = WCN36XX_CENTER_FREQ(wcn); + status.band = WCN36XX_BAND(wcn); + } + status.mactime = 10; - status.freq = WCN36XX_CENTER_FREQ(wcn); - status.band = WCN36XX_BAND(wcn); status.signal = -get_rssi0(bd); status.antenna = 1; status.rate_idx = 1; @@ -57,14 +68,10 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) RX_FLAG_MMIC_STRIPPED | RX_FLAG_DECRYPTED; - wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x\n", status.flag); + wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%llx\n", status.flag); memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - hdr = (struct ieee80211_hdr *) skb->data; - fc = __le16_to_cpu(hdr->frame_control); - sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)); - if (ieee80211_is_beacon(hdr->frame_control)) { wcn36xx_dbg(WCN36XX_DBG_BEACON, "beacon skb %p len %d fc %04x sn %d\n", skb, skb->len, fc, sn); @@ -221,7 +228,7 @@ static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd, /* default rate for unicast */ if (ieee80211_is_mgmt(hdr->frame_control)) - bd->bd_rate = (WCN36XX_BAND(wcn) == IEEE80211_BAND_5GHZ) ? + bd->bd_rate = (WCN36XX_BAND(wcn) == NL80211_BAND_5GHZ) ? WCN36XX_BD_RATE_CTRL : WCN36XX_BD_RATE_MGMT; else if (ieee80211_is_ctl(hdr->frame_control)) diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx-msm.c b/drivers/net/wireless/ath/wcn36xx/wcn36xx-msm.c deleted file mode 100644 index 647f623f1985..000000000000 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx-msm.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2013 Eugene Krasnikov - * Copyright (c) 2013 Qualcomm Atheros, Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "wcn36xx.h" -#include "wcnss_core.h" - -#define MAC_ADDR_0 "wlan/macaddr0" - -struct smd_packet_item { - struct list_head list; - void *buf; - size_t count; -}; - -static int wcn36xx_msm_smsm_change_state(u32 clear_mask, u32 set_mask) -{ - return 0; -} - -static int wcn36xx_msm_get_hw_mac(struct wcn36xx *wcn, u8 *addr) -{ - const struct firmware *addr_file = NULL; - int status; - u8 tmp[18]; - static const u8 qcom_oui[3] = {0x00, 0x0A, 0xF5}; - static const char *files = {MAC_ADDR_0}; - struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data; - - status = request_firmware(&addr_file, files, &pdata->core->dev); - - if (status < 0) { - /* Assign a random mac with Qualcomm oui */ - dev_err(&pdata->core->dev, "Failed (%d) to read macaddress" - "file %s, using a random address instead", status, files); - memcpy(addr, qcom_oui, 3); - get_random_bytes(addr + 3, 3); - } else { - memset(tmp, 0, sizeof(tmp)); - memcpy(tmp, addr_file->data, sizeof(tmp) - 1); - sscanf(tmp, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", - &addr[0], - &addr[1], - &addr[2], - &addr[3], - &addr[4], - &addr[5]); - - release_firmware(addr_file); - } - - return 0; -} - -static int wcn36xx_msm_smd_send_and_wait(struct wcn36xx *wcn, char *buf, size_t len) -{ - int ret = 0; - struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data; - - mutex_lock(&pdata->wlan_ctrl_lock); - ret = qcom_smd_send(pdata->wlan_ctrl_channel, buf, len); - if (ret) { - dev_err(wcn->dev, "wlan ctrl channel tx failed\n"); - } - mutex_unlock(&pdata->wlan_ctrl_lock); - - return ret; -} - -static int wcn36xx_msm_smd_open(struct wcn36xx *wcn, void *rsp_cb) -{ - struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data; - - pdata->cb = rsp_cb; - return 0; -} - -static void wcn36xx_msm_smd_close(struct wcn36xx *wcn) -{ - return; -} - -static int wcn36xx_msm_get_chip_type(struct wcn36xx *wcn) -{ - struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data; - return pdata->chip_type; -} - -static struct wcn36xx_platform_data wcn36xx_data = { - .ctrl_ops = { - .open = wcn36xx_msm_smd_open, - .close = wcn36xx_msm_smd_close, - .tx = wcn36xx_msm_smd_send_and_wait, - .get_hw_mac = wcn36xx_msm_get_hw_mac, - .smsm_change_state = wcn36xx_msm_smsm_change_state, - .get_chip_type = wcn36xx_msm_get_chip_type, - }, -}; - -static void wlan_ctrl_smd_process(struct work_struct *worker) -{ - unsigned long flags; - struct wcn36xx_platform_data *pdata = - container_of(worker, - struct wcn36xx_platform_data, packet_process_work); - - spin_lock_irqsave(&pdata->packet_lock, flags); - while (!list_empty(&pdata->packet_list)) { - struct smd_packet_item *packet; - - packet = list_first_entry(&pdata->packet_list, - struct smd_packet_item, list); - list_del(&packet->list); - spin_unlock_irqrestore(&pdata->packet_lock, flags); - pdata->cb(pdata->wcn, packet->buf, packet->count); - kfree(packet->buf); - spin_lock_irqsave(&pdata->packet_lock, flags); - } - spin_unlock_irqrestore(&pdata->packet_lock, flags); -} - -static int qcom_smd_wlan_ctrl_probe(struct qcom_smd_device *sdev) -{ - pr_info("%s: enter\n", __func__); - mutex_init(&wcn36xx_data.wlan_ctrl_lock); - init_completion(&wcn36xx_data.wlan_ctrl_ack); - - wcn36xx_data.sdev = sdev; - spin_lock_init(&wcn36xx_data.packet_lock); - INIT_LIST_HEAD(&wcn36xx_data.packet_list); - INIT_WORK(&wcn36xx_data.packet_process_work, wlan_ctrl_smd_process); - - dev_set_drvdata(&sdev->dev, &wcn36xx_data); - wcn36xx_data.wlan_ctrl_channel = sdev->channel; - - of_platform_populate(sdev->dev.of_node, NULL, NULL, &sdev->dev); - - return 0; -} - -static void qcom_smd_wlan_ctrl_remove(struct qcom_smd_device *sdev) -{ - of_platform_depopulate(&sdev->dev); -} - -static int qcom_smd_wlan_ctrl_callback(struct qcom_smd_device *qsdev, - const void *data, - size_t count) -{ - unsigned long flags; - struct smd_packet_item *packet = NULL; - struct wcn36xx_platform_data *pdata = dev_get_drvdata(&qsdev->dev); - void *buf = kzalloc(count + sizeof(struct smd_packet_item), - GFP_ATOMIC); - if (!buf) { - dev_err(&pdata->core->dev, "can't allocate buffer\n"); - return -ENOMEM; - } - - memcpy_fromio(buf, data, count); - packet = buf + count; - packet->buf = buf; - packet->count = count; - - spin_lock_irqsave(&pdata->packet_lock, flags); - list_add_tail(&packet->list, &pdata->packet_list); - spin_unlock_irqrestore(&pdata->packet_lock, flags); - schedule_work(&pdata->packet_process_work); - - /* buf will be freed in workqueue */ - - return 0; -} - -static const struct qcom_smd_id qcom_smd_wlan_ctrl_match[] = { - { .name = "WLAN_CTRL" }, - {} -}; - -static struct qcom_smd_driver qcom_smd_wlan_ctrl_driver = { - .probe = qcom_smd_wlan_ctrl_probe, - .remove = qcom_smd_wlan_ctrl_remove, - .callback = qcom_smd_wlan_ctrl_callback, - .smd_match_table = qcom_smd_wlan_ctrl_match, - .driver = { - .name = "qcom_smd_wlan_ctrl", - .owner = THIS_MODULE, - }, -}; - -static const struct of_device_id wcn36xx_msm_match_table[] = { - { .compatible = "qcom,wcn3660", .data = (void *)WCN36XX_CHIP_3660 }, - { .compatible = "qcom,wcn3680", .data = (void *)WCN36XX_CHIP_3680 }, - { .compatible = "qcom,wcn3620", .data = (void *)WCN36XX_CHIP_3620 }, - { } -}; -MODULE_DEVICE_TABLE(of, wcn36xx_msm_match_table); - -static int wcn36xx_msm_probe(struct platform_device *pdev) -{ - int ret; - const struct of_device_id *of_id; - struct resource *r; - struct resource res[3]; - static const char const *rnames[] = { - "wcnss_mmio", "wcnss_wlantx_irq", "wcnss_wlanrx_irq" }; - static const int rtype[] = { - IORESOURCE_MEM, IORESOURCE_IRQ, IORESOURCE_IRQ }; - struct device_node *dn; - int n; - - wcnss_core_prepare(pdev); - - dn = of_parse_phandle(pdev->dev.of_node, "rproc", 0); - if (!dn) { - dev_err(&pdev->dev, "No rproc specified in DT\n"); - } else { - struct rproc *rproc= rproc_get_by_phandle(dn->phandle); - if (rproc) - rproc_boot(rproc); - else { - dev_err(&pdev->dev, "No rproc registered\n"); - } - } - - qcom_smd_driver_register(&qcom_smd_wlan_ctrl_driver); - wcnss_core_init(); - - of_id = of_match_node(wcn36xx_msm_match_table, pdev->dev.of_node); - if (!of_id) - return -EINVAL; - - wcn36xx_data.chip_type = (enum wcn36xx_chip_type)of_id->data; - - wcn36xx_data.core = platform_device_alloc("wcn36xx", -1); - - for (n = 0; n < ARRAY_SIZE(rnames); n++) { - r = platform_get_resource_byname(pdev, rtype[n], rnames[n]); - if (!r) { - dev_err(&wcn36xx_data.core->dev, - "Missing resource %s'\n", rnames[n]); - ret = -ENOMEM; - return ret; - } - res[n] = *r; - } - - platform_device_add_resources(wcn36xx_data.core, res, n); - wcn36xx_data.core->dev.platform_data = &wcn36xx_data; - - platform_device_add(wcn36xx_data.core); - - dev_info(&pdev->dev, "%s initialized\n", __func__); - - return 0; -} - -static int wcn36xx_msm_remove(struct platform_device *pdev) -{ - platform_device_del(wcn36xx_data.core); - platform_device_put(wcn36xx_data.core); - return 0; -} - -static struct platform_driver wcn36xx_msm_driver = { - .probe = wcn36xx_msm_probe, - .remove = wcn36xx_msm_remove, - .driver = { - .name = "wcn36xx-msm", - .owner = THIS_MODULE, - .of_match_table = wcn36xx_msm_match_table, - }, -}; - -static int __init wcn36xx_msm_init(void) -{ - return platform_driver_register(&wcn36xx_msm_driver); -} -module_init(wcn36xx_msm_init); - -static void __exit wcn36xx_msm_exit(void) -{ - platform_driver_unregister(&wcn36xx_msm_driver); -} -module_exit(wcn36xx_msm_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com"); -MODULE_FIRMWARE(MAC_ADDR_0); - diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index b17d284a69c9..35a6590c3ee5 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -35,6 +35,9 @@ /* How many frames until we start a-mpdu TX session */ #define WCN36XX_AMPDU_START_THRESH 20 +#define WCN36XX_MAX_SCAN_SSIDS 9 +#define WCN36XX_MAX_SCAN_IE_LEN 500 + extern unsigned int wcn36xx_dbg_mask; enum wcn36xx_debug_mask { @@ -103,49 +106,6 @@ struct nv_data { u8 table; }; -enum wcn36xx_chip_type { - WCN36XX_CHIP_UNKNOWN, - WCN36XX_CHIP_3660, - WCN36XX_CHIP_3680, - WCN36XX_CHIP_3620, -}; - -/* Interface for platform control path - * - * @open: hook must be called when wcn36xx wants to open control channel. - * @tx: sends a buffer. - */ -struct wcn36xx_platform_ctrl_ops { - int (*open)(struct wcn36xx *wcn, void *rsp_cb); - void (*close)(struct wcn36xx *wcn); - int (*tx)(struct wcn36xx *wcn, char *buf, size_t len); - int (*get_hw_mac)(struct wcn36xx *wcn, u8 *addr); - int (*get_chip_type)(struct wcn36xx *wcn); - int (*smsm_change_state)(u32 clear_mask, u32 set_mask); -}; - -struct wcn36xx_platform_data { - enum wcn36xx_chip_type chip_type; - - struct platform_device *core; - - struct qcom_smd_device *sdev; - struct qcom_smd_channel *wlan_ctrl_channel; - struct completion wlan_ctrl_ack; - struct mutex wlan_ctrl_lock; - - struct pinctrl *pinctrl; - - struct wcn36xx *wcn; - - void (*cb)(struct wcn36xx *wcn, void *buf, size_t len); - struct wcn36xx_platform_ctrl_ops ctrl_ops; - - struct work_struct packet_process_work; - spinlock_t packet_lock; - struct list_head packet_list; -}; - /** * struct wcn36xx_vif - holds VIF related fields * @@ -223,7 +183,7 @@ struct wcn36xx { u8 fw_minor; u8 fw_major; u32 fw_feat_caps[WCN36XX_HAL_CAPS_SIZE]; - enum wcn36xx_chip_type chip_version; + bool is_pronto; /* extra byte for the NULL termination */ u8 crm_version[WCN36XX_HAL_VERSION_LENGTH + 1]; @@ -232,10 +192,16 @@ struct wcn36xx { /* IRQs */ int tx_irq; int rx_irq; - void __iomem *mmio; + void __iomem *ccu_base; + void __iomem *dxe_base; + + struct qcom_smd_channel *smd_channel; + + struct qcom_smem_state *tx_enable_state; + unsigned tx_enable_state_bit; + struct qcom_smem_state *tx_rings_empty_state; + unsigned tx_rings_empty_state_bit; - struct wcn36xx_platform_data *wcn36xx_data; - struct wcn36xx_platform_ctrl_ops *ctrl_ops; /* * smd_buf must be protected with smd_mutex to garantee * that all messages are sent one after another @@ -246,9 +212,15 @@ struct wcn36xx { struct completion hal_rsp_compl; struct workqueue_struct *hal_ind_wq; struct work_struct hal_ind_work; - struct mutex hal_ind_mutex; + spinlock_t hal_ind_lock; struct list_head hal_ind_queue; + struct work_struct scan_work; + struct cfg80211_scan_request *scan_req; + int scan_freq; + int scan_band; + struct mutex scan_lock; + /* DXE channels */ struct wcn36xx_dxe_ch dxe_tx_l_ch; /* TX low */ struct wcn36xx_dxe_ch dxe_tx_h_ch; /* TX high */ diff --git a/drivers/net/wireless/ath/wcn36xx/wcnss_core.c b/drivers/net/wireless/ath/wcn36xx/wcnss_core.c deleted file mode 100644 index e7d389e87886..000000000000 --- a/drivers/net/wireless/ath/wcn36xx/wcnss_core.c +++ /dev/null @@ -1,295 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "wcn36xx.h" -#include "wcnss_core.h" - -#define WCNSS_CTRL_TIMEOUT (msecs_to_jiffies(500)) - -static int wcnss_core_config(struct platform_device *pdev, void __iomem *base) -{ - int ret = 0; - u32 value, iris_read_v = INVALID_IRIS_REG; - int clk_48m = 0; - - value = readl_relaxed(base + SPARE_OFFSET); - value |= WCNSS_FW_DOWNLOAD_ENABLE; - writel_relaxed(value, base + SPARE_OFFSET); - - writel_relaxed(0, base + PMU_OFFSET); - value = readl_relaxed(base + PMU_OFFSET); - value |= WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP | - WCNSS_PMU_CFG_IRIS_XO_EN; - writel_relaxed(value, base + PMU_OFFSET); - - iris_read_v = readl_relaxed(base + IRIS_REG_OFFSET); - pr_info("iris_read_v: 0x%x\n", iris_read_v); - - iris_read_v &= 0xffff; - iris_read_v |= 0x04; - writel_relaxed(iris_read_v, base + IRIS_REG_OFFSET); - - value = readl_relaxed(base + PMU_OFFSET); - value |= WCNSS_PMU_CFG_IRIS_XO_READ; - writel_relaxed(value, base + PMU_OFFSET); - - while (readl_relaxed(base + PMU_OFFSET) & - WCNSS_PMU_CFG_IRIS_XO_READ_STS) - cpu_relax(); - - iris_read_v = readl_relaxed(base + 0x1134); - pr_info("wcnss: IRIS Reg: 0x%08x\n", iris_read_v); - clk_48m = (iris_read_v >> 30) ? 0 : 1; - value &= ~WCNSS_PMU_CFG_IRIS_XO_READ; - - /* XO_MODE[b2:b1]. Clear implies 19.2MHz */ - value &= ~WCNSS_PMU_CFG_IRIS_XO_MODE; - - if (clk_48m) - value |= WCNSS_PMU_CFG_IRIS_XO_MODE_48; - - writel_relaxed(value, base + PMU_OFFSET); - - /* Reset IRIS */ - value |= WCNSS_PMU_CFG_IRIS_RESET; - writel_relaxed(value, base + PMU_OFFSET); - - while (readl_relaxed(base + PMU_OFFSET) & - WCNSS_PMU_CFG_IRIS_RESET_STS) - cpu_relax(); - - /* reset IRIS reset bit */ - value &= ~WCNSS_PMU_CFG_IRIS_RESET; - writel_relaxed(value, base + PMU_OFFSET); - - /* start IRIS XO configuration */ - value |= WCNSS_PMU_CFG_IRIS_XO_CFG; - writel_relaxed(value, base + PMU_OFFSET); - - /* Wait for XO configuration to finish */ - while (readl_relaxed(base + PMU_OFFSET) & - WCNSS_PMU_CFG_IRIS_XO_CFG_STS) - cpu_relax(); - - /* Stop IRIS XO configuration */ - value &= ~(WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP | - WCNSS_PMU_CFG_IRIS_XO_CFG); - writel_relaxed(value, base + PMU_OFFSET); - - msleep(200); - - return ret; -} - -int wcnss_core_prepare(struct platform_device *pdev) -{ - int ret = 0; - struct resource *res; - void __iomem *wcnss_base; - - res = platform_get_resource_byname(pdev, - IORESOURCE_MEM, "pronto_phy_base"); - if (!res) { - ret = -EIO; - dev_err(&pdev->dev, "resource pronto_phy_base failed\n"); - return ret; - } - - wcnss_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(wcnss_base)) { - dev_err(&pdev->dev, "pronto_phy_base map failed\n"); - return PTR_ERR(wcnss_base); - } - - ret = wcnss_core_config(pdev, wcnss_base); - return ret; -} - -struct wcnss_platform_data { - struct qcom_smd_channel *channel; - struct completion ack; - struct mutex lock; - - struct work_struct rx_work; - struct work_struct download_work; - - struct qcom_smd_device *sdev; -}; - -static struct completion fw_ready_compl; -#define NV_FILE_NAME "wlan/prima/WCNSS_qcom_wlan_nv.bin" -static void wcn36xx_nv_download_work(struct work_struct *worker) -{ - int ret = 0, i; - const struct firmware *nv = NULL; - struct wcnss_platform_data *wcnss_data = - container_of(worker, struct wcnss_platform_data, download_work); - struct device *dev = &wcnss_data->sdev->dev; - - struct nvbin_dnld_req_msg *msg; - const void *nv_blob_start; - char *pkt = NULL; - int nv_blob_size = 0, fragments; - - ret = request_firmware(&nv, NV_FILE_NAME, dev); - if (ret || !nv || !nv->data || !nv->size) { - dev_err(dev, "request firmware for %s (ret = %d)\n", - NV_FILE_NAME, ret); - return; - } - - nv_blob_start = nv->data + 4; - nv_blob_size = nv->size -4; - - fragments = (nv_blob_size + NV_FRAGMENT_SIZE - 1)/NV_FRAGMENT_SIZE; - - pkt = kzalloc(sizeof(struct nvbin_dnld_req_msg) + NV_FRAGMENT_SIZE, - GFP_KERNEL); - if (!pkt) { - dev_err(dev, "allocation packet for nv download failed\n"); - release_firmware(nv); - } - - msg = (struct nvbin_dnld_req_msg *)pkt; - msg->hdr.msg_type = WCNSS_NV_DOWNLOAD_REQ; - msg->dnld_req_params.msg_flags = 0; - - i = 0; - do { - int pkt_len = 0; - - msg->dnld_req_params.frag_number = i; - if (nv_blob_size > NV_FRAGMENT_SIZE) { - msg->dnld_req_params.msg_flags &= - ~LAST_FRAGMENT; - pkt_len = NV_FRAGMENT_SIZE; - } else { - pkt_len = nv_blob_size; - msg->dnld_req_params.msg_flags |= - LAST_FRAGMENT | CAN_RECEIVE_CALDATA; - } - - msg->dnld_req_params.nvbin_buffer_size = pkt_len; - msg->hdr.msg_len = - sizeof(struct nvbin_dnld_req_msg) + pkt_len; - - memcpy(pkt + sizeof(struct nvbin_dnld_req_msg), - nv_blob_start + i * NV_FRAGMENT_SIZE, pkt_len); - - ret = qcom_smd_send(wcnss_data->channel, pkt, msg->hdr.msg_len); - if (ret) { - dev_err(dev, "nv download failed\n"); - goto out; - } - - i++; - nv_blob_size -= NV_FRAGMENT_SIZE; - msleep(100); - } while (nv_blob_size > 0); - -out: - kfree(pkt); - release_firmware(nv); - return; -} - -static int qcom_smd_wcnss_ctrl_callback(struct qcom_smd_device *qsdev, - const void *data, - size_t count) -{ - struct wcnss_platform_data *wcnss_data = dev_get_drvdata(&qsdev->dev); - struct smd_msg_hdr phdr; - const unsigned char *tmp = data; - - memcpy_fromio(&phdr, data, sizeof(struct smd_msg_hdr)); - - switch (phdr.msg_type) { - /* CBC COMPLETE means firmware ready for go */ - case WCNSS_CBC_COMPLETE_IND: - complete(&fw_ready_compl); - pr_info("wcnss: received WCNSS_CBC_COMPLETE_IND from FW\n"); - break; - - case WCNSS_NV_DOWNLOAD_RSP: - pr_info("fw_status: %d\n", tmp[sizeof(struct smd_msg_hdr)]); - break; - } - - complete(&wcnss_data->ack); - return 0; -} - -static int qcom_smd_wcnss_ctrl_probe(struct qcom_smd_device *sdev) -{ - struct wcnss_platform_data *wcnss_data; - - wcnss_data = devm_kzalloc(&sdev->dev, sizeof(*wcnss_data), GFP_KERNEL); - if (!wcnss_data) - return -ENOMEM; - - mutex_init(&wcnss_data->lock); - init_completion(&wcnss_data->ack); - - wcnss_data->sdev = sdev; - - dev_set_drvdata(&sdev->dev, wcnss_data); - wcnss_data->channel = sdev->channel; - - INIT_WORK(&wcnss_data->download_work, wcn36xx_nv_download_work); - - of_platform_populate(sdev->dev.of_node, NULL, NULL, &sdev->dev); - - /* We are ready for download here */ - schedule_work(&wcnss_data->download_work); - return 0; -} - -static void qcom_smd_wcnss_ctrl_remove(struct qcom_smd_device *sdev) -{ - of_platform_depopulate(&sdev->dev); -} - -static const struct qcom_smd_id qcom_smd_wcnss_ctrl_match[] = { - { .name = "WCNSS_CTRL" }, - {} -}; - -static struct qcom_smd_driver qcom_smd_wcnss_ctrl_driver = { - .probe = qcom_smd_wcnss_ctrl_probe, - .remove = qcom_smd_wcnss_ctrl_remove, - .callback = qcom_smd_wcnss_ctrl_callback, - .smd_match_table = qcom_smd_wcnss_ctrl_match, - .driver = { - .name = "qcom_smd_wcnss_ctrl", - .owner = THIS_MODULE, - }, -}; - -void wcnss_core_init(void) -{ - int ret = 0; - - init_completion(&fw_ready_compl); - qcom_smd_driver_register(&qcom_smd_wcnss_ctrl_driver); - - ret = wait_for_completion_interruptible_timeout( - &fw_ready_compl, msecs_to_jiffies(FW_READY_TIMEOUT)); - if (ret <= 0) { - pr_err("timeout waiting for wcnss firmware ready indicator\n"); - return; - } - - return; -} - -void wcnss_core_deinit(void) -{ - qcom_smd_driver_unregister(&qcom_smd_wcnss_ctrl_driver); -} diff --git a/drivers/net/wireless/ath/wcn36xx/wcnss_core.h b/drivers/net/wireless/ath/wcn36xx/wcnss_core.h deleted file mode 100644 index 49524c8dddfb..000000000000 --- a/drivers/net/wireless/ath/wcn36xx/wcnss_core.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef _WCNSS_CORE_H_ -#define _WCNSS_CORE_H_ - -#define PMU_OFFSET 0x1004 -#define SPARE_OFFSET 0x1088 -#define IRIS_REG_OFFSET 0x1134 - -#define INVALID_IRIS_REG 0xbaadbaad - -#define WCNSS_PMU_CFG_IRIS_XO_CFG BIT(3) -#define WCNSS_PMU_CFG_IRIS_XO_EN BIT(4) -#define WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP BIT(5) -#define WCNSS_PMU_CFG_IRIS_XO_CFG_STS BIT(6) /* 1: in progress, 0: done */ - -#define WCNSS_PMU_CFG_IRIS_RESET BIT(7) -#define WCNSS_PMU_CFG_IRIS_RESET_STS BIT(8) /* 1: in progress, 0: done */ -#define WCNSS_PMU_CFG_IRIS_XO_READ BIT(9) -#define WCNSS_PMU_CFG_IRIS_XO_READ_STS BIT(10) -#define WCNSS_FW_DOWNLOAD_ENABLE BIT(25) - -#define WCNSS_PMU_CFG_IRIS_XO_MODE 0x6 -#define WCNSS_PMU_CFG_IRIS_XO_MODE_48 (3 << 1) - -#define NV_DOWNLOAD_TIMEOUT 500 -#define NV_FRAGMENT_SIZE 3072 -#define MAX_CALIBRATED_DATA_SIZE (64*1024) -#define LAST_FRAGMENT (1 << 0) -#define MESSAGE_TO_FOLLOW (1 << 1) -#define CAN_RECEIVE_CALDATA (1 << 15) -#define WCNSS_RESP_SUCCESS 1 -#define WCNSS_RESP_FAIL 0 - - -#define WCNSS_NV_DOWNLOAD_REQ 0x01000002 -#define WCNSS_NV_DOWNLOAD_RSP 0x01000003 -#define WCNSS_CBC_COMPLETE_IND 0x0100000C - -/*time out 10s for the firmware status ready indicator */ -#define FW_READY_TIMEOUT (10000) - - -struct smd_msg_hdr { - unsigned int msg_type; - unsigned int msg_len; -}; - -struct nvbin_dnld_req_params { - /* Fragment sequence number of the NV bin Image. NV Bin Image - * might not fit into one message due to size limitation of - * the SMD channel FIFO so entire NV blob is chopped into - * multiple fragments starting with seqeunce number 0. The - * last fragment is indicated by marking is_last_fragment field - * to 1. At receiving side, NV blobs would be concatenated - * together without any padding bytes in between. - */ - unsigned short frag_number; - - /* bit 0: When set to 1 it indicates that no more fragments will - * be sent. - * bit 1: When set, a new message will be followed by this message - * bit 2- bit 14: Reserved - * bit 15: when set, it indicates that the sender is capable of - * receiving Calibrated data. - */ - unsigned short msg_flags; - - /* NV Image size (number of bytes) */ - unsigned int nvbin_buffer_size; - - /* Following the 'nvbin_buffer_size', there should be - * nvbin_buffer_size bytes of NV bin Image i.e. - * uint8[nvbin_buffer_size]. - */ -}; - -struct nvbin_dnld_req_msg { - /* Note: The length specified in nvbin_dnld_req_msg messages - * should be hdr.msg_len = sizeof(nvbin_dnld_req_msg) + - * nvbin_buffer_size. - */ - struct smd_msg_hdr hdr; - struct nvbin_dnld_req_params dnld_req_params; -}; - -struct wcnss_version { - struct smd_msg_hdr hdr; - unsigned char major; - unsigned char minor; - unsigned char version; - unsigned char revision; -}; - - -int wcnss_core_prepare(struct platform_device *pdev); -void wcnss_core_init(void); -void wcnss_core_deinit(void); - -#endif - diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index e310300beaea..9faef1de1856 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -192,6 +192,18 @@ static const struct regulator_ops rpm_smps_ldo_ops = { .enable = rpm_reg_enable, .disable = rpm_reg_disable, .is_enabled = rpm_reg_is_enabled, + .list_voltage = regulator_list_voltage_linear_range, + + .get_voltage = rpm_reg_get_voltage, + .set_voltage = rpm_reg_set_voltage, + + .set_load = rpm_reg_set_load, +}; + +static const struct regulator_ops rpm_smps_ldo_ops_fixed = { + .enable = rpm_reg_enable, + .disable = rpm_reg_disable, + .is_enabled = rpm_reg_is_enabled, .get_voltage = rpm_reg_get_voltage, .set_voltage = rpm_reg_set_voltage, @@ -299,7 +311,7 @@ static const struct regulator_desc pm8941_nldo = { static const struct regulator_desc pm8941_lnldo = { .fixed_uV = 1740000, .n_voltages = 1, - .ops = &rpm_smps_ldo_ops, + .ops = &rpm_smps_ldo_ops_fixed, }; static const struct regulator_desc pm8941_switch = { diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 54d5b637d14c..ff61075a9755 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -81,6 +81,7 @@ config QCOM_Q6V5_PIL tristate "Qualcomm Hexagon V5 Peripherial Image Loader" depends on OF && ARCH_QCOM select REMOTEPROC + select QCOM_MDT_LOADER help Say y here to support the Qualcomm Peripherial Image Loader for the Hexagon V5 based remote processors. @@ -93,4 +94,16 @@ config QCOM_TZ_PIL Say y here to support the TrustZone based Qualcomm Peripherial Image Loader. +config QCOM_MDT_LOADER + tristate + +config QCOM_WCNSS_PIL + tristate "Qualcomm WCNSS Peripheral Image Loader" + depends on OF && ARCH_QCOM + select REMOTEPROC + select QCOM_MDT_LOADER + select QCOM_SCM + help + Peripherial Image Loader for the WCNSS block. + endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 4351f2e462ad..1ce1b0a269ab 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -13,3 +13,6 @@ obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o obj-$(CONFIG_QCOM_TZ_PIL) += qcom_tz_pil.o +obj-$(CONFIG_QCOM_MDT_LOADER) += qcom_mdt_loader.o +obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss.o qcom_wcnss_iris.o +obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o diff --git a/drivers/remoteproc/qcom_mdt_loader.c b/drivers/remoteproc/qcom_mdt_loader.c new file mode 100644 index 000000000000..4efeda908d9a --- /dev/null +++ b/drivers/remoteproc/qcom_mdt_loader.c @@ -0,0 +1,166 @@ +/* + * Qualcomm Peripheral Image Loader + * + * Copyright (C) 2016 Linaro Ltd + * Copyright (C) 2015 Sony Mobile Communications Inc + * Copyright (c) 2012-2013, The Linux Foundation. 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 +#include +#include +#include +#include +#include +#include + +#include "remoteproc_internal.h" +#include "qcom_mdt_loader.h" + +/** + * qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc + * @rproc: remoteproc handle + * @fw: firmware header + * @tablesz: outgoing size of the table + * + * Returns a dummy table. + */ +struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, + const struct firmware *fw, + int *tablesz) +{ + static struct resource_table table = { .ver = 1, }; + + *tablesz = sizeof(table); + return &table; +} +EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table); + +int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, size_t *fw_size, bool *fw_relocate) +{ + const struct elf32_phdr *phdrs; + const struct elf32_phdr *phdr; + const struct elf32_hdr *ehdr; + phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; + phys_addr_t max_addr = 0; + bool relocate = false; + int i; + + ehdr = (struct elf32_hdr *)fw->data; + phdrs = (struct elf32_phdr *)(ehdr + 1); + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &phdrs[i]; + + if (phdr->p_type != PT_LOAD) + continue; + + if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) + continue; + + if (!phdr->p_memsz) + continue; + + if (phdr->p_flags & QCOM_MDT_RELOCATABLE) + relocate = true; + + if (phdr->p_paddr < min_addr) + min_addr = phdr->p_paddr; + + if (phdr->p_paddr + phdr->p_memsz > max_addr) + max_addr = round_up(phdr->p_paddr + phdr->p_memsz, SZ_4K); + } + + if (fw_addr) + *fw_addr = min_addr; + if (fw_size) + *fw_size = max_addr - min_addr; + if (fw_relocate) + *fw_relocate = relocate; + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_mdt_parse); + +/** + * qcom_mdt_load() - load the firmware which header is defined in fw + * @rproc: rproc handle + * @pas_id: PAS identifier to load this firmware into + * @fw: frimware object for the header + * + * Returns 0 on success, negative errno otherwise. + */ +int qcom_mdt_load(struct rproc *rproc, + const struct firmware *fw, + const char *firmware) +{ + const struct elf32_phdr *phdrs; + const struct elf32_phdr *phdr; + const struct elf32_hdr *ehdr; + unsigned fw_name_len; + char *fw_name; + void *ptr; + int ret; + int i; + + ehdr = (struct elf32_hdr *)fw->data; + phdrs = (struct elf32_phdr *)(ehdr + 1); + + fw_name_len = strlen(firmware); + if (fw_name_len <= 4) + return -EINVAL; + + fw_name = kstrdup(firmware, GFP_KERNEL); + if (!fw_name) + return -ENOMEM; + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &phdrs[i]; + + if (phdr->p_type != PT_LOAD) + continue; + + if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) + continue; + + if (!phdr->p_memsz) + continue; + + ptr = rproc_da_to_va(rproc, phdr->p_paddr, phdr->p_memsz); + if (!ptr) { + dev_err(&rproc->dev, "segment outside memory range\n"); + ret = -EINVAL; + break; + } + + if (phdr->p_filesz) { + sprintf(fw_name + fw_name_len - 3, "b%02d", i); + ret = request_firmware(&fw, fw_name, &rproc->dev); + if (ret) { + dev_err(&rproc->dev, "failed to load %s\n", fw_name); + break; + } + + memcpy(ptr, fw->data, fw->size); + + release_firmware(fw); + } + + if (phdr->p_memsz > phdr->p_filesz) + memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); + } + + kfree(fw_name); + + return ret; +} +EXPORT_SYMBOL_GPL(qcom_mdt_load); diff --git a/drivers/remoteproc/qcom_mdt_loader.h b/drivers/remoteproc/qcom_mdt_loader.h new file mode 100644 index 000000000000..c5d7122755b6 --- /dev/null +++ b/drivers/remoteproc/qcom_mdt_loader.h @@ -0,0 +1,13 @@ +#ifndef __QCOM_MDT_LOADER_H__ +#define __QCOM_MDT_LOADER_H__ + +#define QCOM_MDT_TYPE_MASK (7 << 24) +#define QCOM_MDT_TYPE_HASH (2 << 24) +#define QCOM_MDT_RELOCATABLE BIT(27) + +struct resource_table * qcom_mdt_find_rsc_table(struct rproc *rproc, const struct firmware *fw, int *tablesz); +int qcom_mdt_load(struct rproc *rproc, const struct firmware *fw, const char *fw_name); + +int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, size_t *fw_size, bool *fw_relocate); + +#endif diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index ec4333ed791e..5d3372feb095 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -1,6 +1,7 @@ /* * Qualcomm Peripheral Image Loader * + * Copyright (C) 2016 Linaro Ltd. * Copyright (C) 2014 Sony Mobile Communications AB * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. * @@ -14,255 +15,249 @@ * GNU General Public License for more details. */ -#include -#include -#include +#include +#include #include -#include -#include +#include #include -#include -#include -#include +#include +#include +#include #include -#include -#include -#include -#include +#include +#include #include -#include +#include #include -#include +#include +#include #include "remoteproc_internal.h" +#include "qcom_mdt_loader.h" #include -#define SCM_SVC_PIL 0x2 +#define MBA_FIRMWARE_NAME "mba.mbn" +#define MPSS_FIRMWARE_NAME "modem.mdt" -struct mdt_hdr { - struct elf32_hdr hdr; - struct elf32_phdr phdr[]; -}; +#define MPSS_CRASH_REASON_SMEM 421 + +/* RMB Status Register Values */ +#define RMB_PBL_SUCCESS 0x1 + +#define RMB_MBA_XPU_UNLOCKED 0x1 +#define RMB_MBA_XPU_UNLOCKED_SCRIBBLED 0x2 +#define RMB_MBA_META_DATA_AUTH_SUCCESS 0x3 +#define RMB_MBA_AUTH_COMPLETE 0x4 + +/* PBL/MBA interface registers */ +#define RMB_MBA_IMAGE_REG 0x00 +#define RMB_PBL_STATUS_REG 0x04 +#define RMB_MBA_COMMAND_REG 0x08 +#define RMB_MBA_STATUS_REG 0x0C +#define RMB_PMI_META_DATA_REG 0x10 +#define RMB_PMI_CODE_START_REG 0x14 +#define RMB_PMI_CODE_LENGTH_REG 0x18 + +#define RMB_CMD_META_DATA_READY 0x1 +#define RMB_CMD_LOAD_READY 0x2 + +/* QDSP6SS Register Offsets */ +#define QDSP6SS_RESET_REG 0x014 +#define QDSP6SS_GFMUX_CTL_REG 0x020 +#define QDSP6SS_PWR_CTL_REG 0x030 + +/* AXI Halt Register Offsets */ +#define AXI_HALTREQ_REG 0x0 +#define AXI_HALTACK_REG 0x4 +#define AXI_IDLE_REG 0x8 + +#define HALT_ACK_TIMEOUT_MS 100 -struct qproc { +/* QDSP6SS_RESET */ +#define Q6SS_STOP_CORE BIT(0) +#define Q6SS_CORE_ARES BIT(1) +#define Q6SS_BUS_ARES_ENABLE BIT(2) + +/* QDSP6SS_GFMUX_CTL */ +#define Q6SS_CLK_ENABLE BIT(1) + +/* QDSP6SS_PWR_CTL */ +#define Q6SS_L2DATA_SLP_NRET_N_0 BIT(0) +#define Q6SS_L2DATA_SLP_NRET_N_1 BIT(1) +#define Q6SS_L2DATA_SLP_NRET_N_2 BIT(2) +#define Q6SS_L2TAG_SLP_NRET_N BIT(16) +#define Q6SS_ETB_SLP_NRET_N BIT(17) +#define Q6SS_L2DATA_STBY_N BIT(18) +#define Q6SS_SLP_RET_N BIT(19) +#define Q6SS_CLAMP_IO BIT(20) +#define QDSS_BHS_ON BIT(21) +#define QDSS_LDO_BYP BIT(22) + +struct q6v5 { struct device *dev; struct rproc *rproc; void __iomem *reg_base; - void __iomem *halt_base; - void __iomem *halt_q6; - void __iomem *halt_modem; - void __iomem *halt_nc; void __iomem *rmb_base; - void __iomem *restart_sec_base; - struct reset_control *mss_restart; + struct regmap *halt_map; + u32 halt_q6; + u32 halt_modem; + u32 halt_nc; - int wdog_irq; - int fatal_irq; - int ready_irq; - int handover_irq; - int stop_ack_irq; + struct reset_control *mss_restart; - struct gpio_desc *stop_gpio; + struct qcom_smem_state *state; + unsigned stop_bit; - struct regulator *vdd; - struct regulator *cx; - struct regulator *mx; - struct regulator *pll; + struct regulator_bulk_data supply[4]; struct clk *ahb_clk; struct clk *axi_clk; struct clk *rom_clk; struct completion start_done; + struct completion stop_done; + bool running; - void *mba_va; - dma_addr_t mba_da; - size_t mba_size; - struct dma_attrs mba_attrs; + phys_addr_t mba_phys; + void *mba_region; + size_t mba_size; - phys_addr_t reloc_phys; - size_t reloc_size; + phys_addr_t mpss_phys; + phys_addr_t mpss_reloc; + void *mpss_region; + size_t mpss_size; }; -#define VDD_MSS_UV 1000000 -#define VDD_MSS_UV_MAX 1150000 -#define VDD_MSS_UA 100000 - -/* Q6 Register Offsets */ -#define QDSP6SS_RST_EVB 0x010 - -/* AXI Halting Registers */ -#define MSS_Q6_HALT_BASE 0x180 -#define MSS_MODEM_HALT_BASE 0x200 -#define MSS_NC_HALT_BASE 0x280 - -/* RMB Status Register Values */ -#define STATUS_PBL_SUCCESS 0x1 -#define STATUS_XPU_UNLOCKED 0x1 -#define STATUS_XPU_UNLOCKED_SCRIBBLED 0x2 - -/* PBL/MBA interface registers */ -#define RMB_MBA_IMAGE 0x00 -#define RMB_PBL_STATUS 0x04 -#define RMB_MBA_COMMAND 0x08 -#define RMB_MBA_STATUS 0x0C -#define RMB_PMI_META_DATA 0x10 -#define RMB_PMI_CODE_START 0x14 -#define RMB_PMI_CODE_LENGTH 0x18 - -#define POLL_INTERVAL_US 50 - -#define CMD_META_DATA_READY 0x1 -#define CMD_LOAD_READY 0x2 +enum { + Q6V5_SUPPLY_CX, + Q6V5_SUPPLY_MX, + Q6V5_SUPPLY_MSS, + Q6V5_SUPPLY_PLL, +}; -#define STATUS_META_DATA_AUTH_SUCCESS 0x3 -#define STATUS_AUTH_COMPLETE 0x4 +static int q6v5_regulator_init(struct q6v5 *qproc) +{ + int ret; -/* External BHS */ -#define EXTERNAL_BHS_ON BIT(0) -#define EXTERNAL_BHS_STATUS BIT(4) -#define BHS_TIMEOUT_US 50 + qproc->supply[Q6V5_SUPPLY_CX].supply = "cx"; + qproc->supply[Q6V5_SUPPLY_MX].supply = "mx"; + qproc->supply[Q6V5_SUPPLY_MSS].supply = "mss"; + qproc->supply[Q6V5_SUPPLY_PLL].supply = "pll"; -#define MSS_RESTART_ID 0xA + ret = devm_regulator_bulk_get(qproc->dev, + ARRAY_SIZE(qproc->supply), qproc->supply); + if (ret < 0) { + dev_err(qproc->dev, "failed to get supplies\n"); + return ret; + } -/* QDSP6SS Register Offsets */ -#define QDSP6SS_RESET 0x014 -#define QDSP6SS_GFMUX_CTL 0x020 -#define QDSP6SS_PWR_CTL 0x030 -#define QDSP6SS_STRAP_ACC 0x110 + regulator_set_load(qproc->supply[Q6V5_SUPPLY_CX].consumer, 100000); + regulator_set_load(qproc->supply[Q6V5_SUPPLY_MSS].consumer, 100000); + regulator_set_load(qproc->supply[Q6V5_SUPPLY_PLL].consumer, 10000); -/* AXI Halt Register Offsets */ -#define AXI_HALTREQ 0x0 -#define AXI_HALTACK 0x4 -#define AXI_IDLE 0x8 + return 0; +} -#define HALT_ACK_TIMEOUT_US 100000 +static int q6v5_regulator_enable(struct q6v5 *qproc) +{ + int ret; -/* QDSP6SS_RESET */ -#define Q6SS_STOP_CORE BIT(0) -#define Q6SS_CORE_ARES BIT(1) -#define Q6SS_BUS_ARES_ENA BIT(2) + /* TODO: Q6V5_SUPPLY_CX is supposed to be set to super-turbo here */ + ret = regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_MX].consumer, + 1050000, INT_MAX); + if (ret) + return ret; -/* QDSP6SS_GFMUX_CTL */ -#define Q6SS_CLK_ENA BIT(1) -#define Q6SS_CLK_SRC_SEL_C BIT(3) -#define Q6SS_CLK_SRC_SEL_FIELD 0xC -#define Q6SS_CLK_SRC_SWITCH_CLK_OVR BIT(8) + regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_MSS].consumer, + 1000000, 1150000); -/* QDSP6SS_PWR_CTL */ -#define Q6SS_L2DATA_SLP_NRET_N_0 BIT(0) -#define Q6SS_L2DATA_SLP_NRET_N_1 BIT(1) -#define Q6SS_L2DATA_SLP_NRET_N_2 BIT(2) -#define Q6SS_L2TAG_SLP_NRET_N BIT(16) -#define Q6SS_ETB_SLP_NRET_N BIT(17) -#define Q6SS_L2DATA_STBY_N BIT(18) -#define Q6SS_SLP_RET_N BIT(19) -#define Q6SS_CLAMP_IO BIT(20) -#define QDSS_BHS_ON BIT(21) -#define QDSS_LDO_BYP BIT(22) - -/* QDSP6v55 parameters */ -#define QDSP6v55_LDO_ON BIT(26) -#define QDSP6v55_LDO_BYP BIT(25) -#define QDSP6v55_BHS_ON BIT(24) -#define QDSP6v55_CLAMP_WL BIT(21) -#define L1IU_SLP_NRET_N BIT(15) -#define L1DU_SLP_NRET_N BIT(14) -#define L2PLRU_SLP_NRET_N BIT(13) - -#define HALT_CHECK_MAX_LOOPS (200) -#define QDSP6SS_XO_CBCR (0x0038) - -#define QDSP6SS_ACC_OVERRIDE_VAL 0x20 - -#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24)) -static int segment_is_loadable(const struct elf32_phdr *p) -{ - return (p->p_type == PT_LOAD) && - !segment_is_hash(p->p_flags) && - p->p_memsz; + return regulator_bulk_enable(ARRAY_SIZE(qproc->supply), qproc->supply); } -static bool segment_is_relocatable(const struct elf32_phdr *p) +static void q6v5_regulator_disable(struct q6v5 *qproc) { - return !!(p->p_flags & BIT(27)); + regulator_bulk_disable(ARRAY_SIZE(qproc->supply), qproc->supply); + regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_CX].consumer, 0, INT_MAX); + regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_MX].consumer, 0, INT_MAX); + regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_MSS].consumer, 0, 1150000); } -static int qproc_sanity_check(struct rproc *rproc, - const struct firmware *fw) +static int q6v5_load(struct rproc *rproc, const struct firmware *fw) { - if (!fw) { - dev_err(&rproc->dev, "failed to load %s\n", rproc->name); - return -EINVAL; - } + struct q6v5 *qproc = rproc->priv; - /* XXX: ??? */ + memcpy(qproc->mba_region, fw->data, fw->size); return 0; } -static struct resource_table * qproc_find_rsc_table(struct rproc *rproc, - const struct firmware *fw, - int *tablesz) +static const struct rproc_fw_ops q6v5_fw_ops = { + .find_rsc_table = qcom_mdt_find_rsc_table, + .load = q6v5_load, +}; + +static int q6v5_rmb_pbl_wait(struct q6v5 *qproc, int ms) { - static struct resource_table table = { .ver = 1, }; + unsigned long timeout; + s32 val; - *tablesz = sizeof(table); - return &table; + timeout = jiffies + msecs_to_jiffies(ms); + for (;;) { + val = readl(qproc->rmb_base + RMB_PBL_STATUS_REG); + if (val) + break; + + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + + msleep(1); + } + + return val; } -static int qproc_load(struct rproc *rproc, const struct firmware *fw) +static int q6v5_rmb_mba_wait(struct q6v5 *qproc, u32 status, int ms) { - struct qproc *qproc = rproc->priv; - DEFINE_DMA_ATTRS(attrs); - dma_addr_t phys; - dma_addr_t end; - void *ptr; - dma_set_mask(qproc->dev, DMA_BIT_MASK(32)); - dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &attrs); + unsigned long timeout; + s32 val; - ptr = dma_alloc_attrs(qproc->dev, fw->size, &phys, GFP_KERNEL, &attrs); - if (!ptr) { - dev_err(qproc->dev, "failed to allocate mba metadata buffer\n"); - return -ENOMEM; - } + timeout = jiffies + msecs_to_jiffies(ms); + for (;;) { + val = readl(qproc->rmb_base + RMB_MBA_STATUS_REG); + if (val < 0) + break; - end = phys + fw->size; - dev_info(qproc->dev, "loading MBA from %pa to %pa\n", &phys, &end); + if (!status && val) + break; + else if (status && val == status) + break; - memcpy(ptr, fw->data, fw->size); + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; - qproc->mba_va = ptr; - qproc->mba_da = phys; - qproc->mba_size = fw->size; - qproc->mba_attrs = attrs; + msleep(1); + } - return 0; + return val; } -static const struct rproc_fw_ops qproc_fw_ops = { - .find_rsc_table = qproc_find_rsc_table, - .load = qproc_load, - .sanity_check = qproc_sanity_check, -}; - -static void q6v5proc_reset(struct qproc *qproc) +static void q6v5proc_reset(struct q6v5 *qproc) { u32 val; /* Assert resets, stop core */ - val = readl_relaxed(qproc->reg_base + QDSP6SS_RESET); - val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENA | Q6SS_STOP_CORE); - writel_relaxed(val, qproc->reg_base + QDSP6SS_RESET); + val = readl(qproc->reg_base + QDSP6SS_RESET_REG); + val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE); + writel(val, qproc->reg_base + QDSP6SS_RESET_REG); /* Enable power block headswitch, and wait for it to stabilize */ - val = readl_relaxed(qproc->reg_base + QDSP6SS_PWR_CTL); + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); val |= QDSS_BHS_ON | QDSS_LDO_BYP; - writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL); + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); mb(); udelay(1); @@ -270,604 +265,214 @@ static void q6v5proc_reset(struct qproc *qproc) * Turn on memories. L2 banks should be done individually * to minimize inrush current. */ - val = readl_relaxed(qproc->reg_base + QDSP6SS_PWR_CTL); + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); val |= Q6SS_SLP_RET_N | Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLP_NRET_N | Q6SS_L2DATA_STBY_N; - writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL); + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); val |= Q6SS_L2DATA_SLP_NRET_N_2; - writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL); + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); val |= Q6SS_L2DATA_SLP_NRET_N_1; - writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL); + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); val |= Q6SS_L2DATA_SLP_NRET_N_0; - writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL); + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); /* Remove IO clamp */ val &= ~Q6SS_CLAMP_IO; - writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL); + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); /* Bring core out of reset */ - val = readl_relaxed(qproc->reg_base + QDSP6SS_RESET); + val = readl(qproc->reg_base + QDSP6SS_RESET_REG); val &= ~Q6SS_CORE_ARES; - writel_relaxed(val, qproc->reg_base + QDSP6SS_RESET); + writel(val, qproc->reg_base + QDSP6SS_RESET_REG); /* Turn on core clock */ - val = readl_relaxed(qproc->reg_base + QDSP6SS_GFMUX_CTL); - val |= Q6SS_CLK_ENA; - -#if 0 - /* Need a different clock source for v5.2.0 */ - if (qproc->qdsp6v5_2_0) { - val &= ~Q6SS_CLK_SRC_SEL_FIELD; - val |= Q6SS_CLK_SRC_SEL_C; - } - -#endif - /* force clock on during source switch */ -// if (qproc->qdsp6v56) - val |= Q6SS_CLK_SRC_SWITCH_CLK_OVR; - - writel_relaxed(val, qproc->reg_base + QDSP6SS_GFMUX_CTL); + val = readl(qproc->reg_base + QDSP6SS_GFMUX_CTL_REG); + val |= Q6SS_CLK_ENABLE; + writel(val, qproc->reg_base + QDSP6SS_GFMUX_CTL_REG); /* Start core execution */ - val = readl_relaxed(qproc->reg_base + QDSP6SS_RESET); + val = readl(qproc->reg_base + QDSP6SS_RESET_REG); val &= ~Q6SS_STOP_CORE; - writel_relaxed(val, qproc->reg_base + QDSP6SS_RESET); + writel(val, qproc->reg_base + QDSP6SS_RESET_REG); } -static void q6v5proc_halt_axi_port(struct qproc *qproc, void __iomem *halt) +static void q6v5proc_halt_axi_port(struct q6v5 *qproc, + struct regmap *halt_map, + u32 offset) { unsigned long timeout; + unsigned int val; + int ret; - if (readl_relaxed(halt + AXI_IDLE)) + /* Check if we're already idle */ + ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val); + if (!ret && val) return; - /* Assert halt request */ - writel_relaxed(1, halt + AXI_HALTREQ); + /* Assert halt request */ + regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1); - /* Wait for halt */ - timeout = jiffies + 10 * HZ; + /* Wait for halt */ + timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS); for (;;) { - if (readl(halt + AXI_HALTACK) || time_after(jiffies, timeout)) + ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val); + if (ret || val || time_after(jiffies, timeout)) break; msleep(1); } - if (!readl_relaxed(halt + AXI_IDLE)) - dev_err(qproc->dev, "port %pa failed halt\n", &halt); + ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val); + if (ret || !val) + dev_err(qproc->dev, "port failed halt\n"); - /* Clear halt request (port will remain halted until reset) */ - writel_relaxed(0, halt + AXI_HALTREQ); + /* Clear halt request (port will remain halted until reset) */ + regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0); } -static int qproc_mba_load_mdt(struct qproc *qproc, const struct firmware *fw) +static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) { DEFINE_DMA_ATTRS(attrs); - unsigned long timeout; dma_addr_t phys; - dma_addr_t end; void *ptr; int ret; - s32 val; - dma_set_mask(qproc->dev, DMA_BIT_MASK(32)); dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &attrs); - ptr = dma_alloc_attrs(qproc->dev, fw->size, &phys, GFP_KERNEL, &attrs); if (!ptr) { - dev_err(qproc->dev, "failed to allocate mba metadata buffer\n"); + dev_err(qproc->dev, "failed to allocate mdt buffer\n"); return -ENOMEM; } - end = phys + fw->size; - dev_info(qproc->dev, "loading mdt header from %pa to %pa\n", &phys, &end); - memcpy(ptr, fw->data, fw->size); - writel_relaxed(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH); + writel(phys, qproc->rmb_base + RMB_PMI_META_DATA_REG); + writel(RMB_CMD_META_DATA_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); - writel_relaxed(phys, qproc->rmb_base + RMB_PMI_META_DATA); - writel(CMD_META_DATA_READY, qproc->rmb_base + RMB_MBA_COMMAND); + ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_META_DATA_AUTH_SUCCESS, 1000); + if (ret == -ETIMEDOUT) + dev_err(qproc->dev, "MBA header authentication timed out\n"); + else if (ret < 0) + dev_err(qproc->dev, "MBA returned error %d for MDT header\n", ret); - timeout = jiffies + HZ; - for (;;) { - msleep(1); - - val = readl(qproc->rmb_base + RMB_MBA_STATUS); - if (val == STATUS_META_DATA_AUTH_SUCCESS || val < 0) - break; - - if (time_after(jiffies, timeout)) - break; - } - if (val == 0) { - dev_err(qproc->dev, "MBA authentication of headers timed out\n"); - ret = -ETIMEDOUT; - goto out; - } else if (val < 0) { - dev_err(qproc->dev, "MBA returned error %d for headers\n", val); - ret = -EINVAL; - goto out; - } - - dev_err(qproc->dev, "mdt authenticated\n"); - - ret = 0; -out: dma_free_attrs(qproc->dev, fw->size, ptr, phys, &attrs); - return ret; + return ret < 0 ? ret : 0; } - -static int -old_qproc_load_segments(struct qproc *qproc, const struct firmware *fw) +static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw) { - struct device *dev = qproc->dev; + const struct elf32_phdr *phdrs; + const struct elf32_phdr *phdr; struct elf32_hdr *ehdr; - struct elf32_phdr *phdr; - int i, ret = 0; - const u8 *elf_data = fw->data; - const struct firmware *seg_fw; - char fw_name[20]; - - ehdr = (struct elf32_hdr *)elf_data; - phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); - - - - /* go through the available ELF segments */ - for (i = 0; i < ehdr->e_phnum; i++, phdr++) { - u32 da = phdr->p_paddr; - u32 memsz = phdr->p_memsz; - u32 filesz = phdr->p_filesz; - void *ptr; - - if (!segment_is_loadable(phdr)) - continue; - /* - if (phdr->p_type != PT_LOAD) - continue; - - if (segment_is_hash(phdr->p_flags)) - continue; - - if (filesz == 0) - continue; -*/ - //dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", - pr_emerg("phdr(%d): type %d da 0x%x memsz 0x%x filesz 0x%x\n", - i, phdr->p_type, da, memsz, filesz); - - if (filesz > memsz) { - dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", - filesz, memsz); - ret = -EINVAL; - break; - } - - ptr = ioremap(da, memsz); - if (!ptr) { - dev_err(qproc->dev, "failed to allocate mba metadata buffer\n"); - ret = -ENOMEM; - break; - } - - if (filesz) { - snprintf(fw_name, sizeof(fw_name), "modem.b%02d", i); - ret = request_firmware(&seg_fw, fw_name, qproc->dev); - if (ret) { - iounmap(ptr); - break; - } - - memcpy(ptr, seg_fw->data, filesz); - - release_firmware(seg_fw); - } - - if (memsz > filesz) - memset(ptr + filesz, 0, memsz - filesz); - - - - wmb(); - iounmap(ptr); - } - - return ret; -} - -static int qproc_verify_segment(struct qproc *qproc, phys_addr_t paddr, size_t size) -{ - s32 status; - u32 img_length; - - img_length = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH); - dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %x\n", img_length); - - msleep(1); - img_length = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH); - dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %x\n", img_length); - - - if (img_length == 0) { - writel_relaxed(paddr, qproc->rmb_base + RMB_PMI_CODE_START); - writel(CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND); - } - img_length += size; - writel(img_length, qproc->rmb_base + RMB_PMI_CODE_LENGTH); - - img_length = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH); - dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %x\n", img_length); - - status = readl_relaxed(qproc->rmb_base + RMB_MBA_STATUS); - - - if (status < 0) { - dev_err(qproc->dev, "MBA returned error %d\n", status); - } - - printk("DEBUG:pil: status... %08x\n", readl_relaxed(qproc->rmb_base + RMB_MBA_STATUS)); - - return 0; -} - -static int qproc_load_segment(struct qproc *rproc, const char *fw_name, - const struct elf32_phdr *phdr, phys_addr_t paddr) -{ - const struct firmware *fw; - void *ptr; - int ret = 0; - - ptr = ioremap_nocache(paddr, phdr->p_memsz); - if (!ptr) { - dev_err(rproc->dev, "failed to ioremap segment area (%pa+0x%x)\n", &paddr, phdr->p_memsz); - return -EBUSY; - } - - if (phdr->p_filesz) { - ret = request_firmware(&fw, fw_name, rproc->dev); - if (ret) { - dev_err(rproc->dev, "failed to load %s\n", fw_name); - goto out; - } - - memcpy_toio(ptr, fw->data, fw->size); + phys_addr_t boot_addr; + phys_addr_t fw_addr; + bool relocate; + size_t size; + u32 val; + int ret; + int i; - release_firmware(fw); + ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate); + if (ret) { + dev_err(qproc->dev, "failed to parse mdt header\n"); + return ret; } - if (phdr->p_memsz > phdr->p_filesz) - memset_io(ptr + phdr->p_filesz, 0, - phdr->p_memsz - phdr->p_filesz); - - printk("DEBUG:pil: verifing %s size: %x\n",fw_name, phdr->p_memsz); - qproc_verify_segment(rproc, paddr, phdr->p_memsz); - -out: - iounmap(ptr); - return ret; -} - -static int -qproc_load_segments(struct qproc *qproc, const struct firmware *fw) -{ - struct device *dev = qproc->dev; - struct elf32_hdr *ehdr; - struct elf32_phdr *phdr; - int i, ret = 0; - const u8 *elf_data = fw->data; - const struct firmware *seg_fw; - char fw_name[20]; - - const struct mdt_hdr *mdt; - phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; - phys_addr_t max_addr = 0; - size_t align = 0; - bool relocatable = false; - phys_addr_t paddr; - - - ehdr = (struct elf32_hdr *)elf_data; - phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); - - - mdt = (struct mdt_hdr *)fw->data; - ehdr = &mdt->hdr; - - for (i = 0; i < ehdr->e_phnum; i++) { - phdr = &mdt->phdr[i]; - - if (!segment_is_loadable(phdr)) - continue; - - if (phdr->p_paddr < min_addr) { - min_addr = phdr->p_paddr; - - if (segment_is_relocatable(phdr)) { - align = phdr->p_align; - relocatable = true; - } - } - - if (phdr->p_paddr + phdr->p_memsz > max_addr) - max_addr = round_up(phdr->p_paddr + phdr->p_memsz, SZ_4K); - } + if (relocate) + boot_addr = qproc->mpss_phys; + else + boot_addr = fw_addr; - ehdr = (struct elf32_hdr *)elf_data; - phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); - /* go through the available ELF segments */ + ehdr = (struct elf32_hdr *)fw->data; + phdrs = (struct elf32_phdr *)(ehdr + 1); for (i = 0; i < ehdr->e_phnum; i++, phdr++) { - u32 da = phdr->p_paddr; - u32 paddr = phdr->p_paddr; - u32 memsz = phdr->p_memsz; - u32 filesz = phdr->p_filesz; - void *ptr; + phdr = &phdrs[i]; - if (!segment_is_loadable(phdr)) - continue; - /* if (phdr->p_type != PT_LOAD) continue; - if (segment_is_hash(phdr->p_flags)) + if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) continue; - if (filesz == 0) + if (!phdr->p_memsz) continue; -*/ - //dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", - pr_emerg("phdr(%d): type %d paddr 0x%x memsz 0x%x filesz 0x%x\n", - i, phdr->p_type, paddr, memsz, filesz); - - if (filesz > memsz) { - dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", - filesz, memsz); - ret = -EINVAL; - break; - } - paddr = relocatable ? - (phdr->p_paddr - min_addr + qproc->reloc_phys) : - phdr->p_paddr; - - pr_emerg("Relocated-phdr(%d): type %d paddr 0x%x memsz 0x%x filesz 0x%x\n", - i, phdr->p_type, paddr, memsz, filesz); -// if (filesz) { - snprintf(fw_name, sizeof(fw_name), "modem.b%02d", i); - ret = qproc_load_segment(qproc, fw_name, phdr, paddr); -// } -#if 0 - - ptr = ioremap(da, memsz); - if (!ptr) { - dev_err(qproc->dev, "failed to allocate mba metadata buffer\n"); - ret = -ENOMEM; - break; + size = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); + if (!size) { + writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG); + writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); } - if (filesz) { - snprintf(fw_name, sizeof(fw_name), "modem.b%02d", i); - ret = request_firmware(&seg_fw, fw_name, qproc->dev); - if (ret) { - iounmap(ptr); - break; - } - - memcpy(ptr, seg_fw->data, filesz); - - release_firmware(seg_fw); - } - - if (memsz > filesz) - memset(ptr + filesz, 0, memsz - filesz); - - - - wmb(); - iounmap(ptr); -#endif + size += phdr->p_memsz; + writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); } - return ret; + val = readl(qproc->rmb_base + RMB_MBA_STATUS_REG); + return val < 0 ? val : 0; } -static int qproc_verify_segments(struct qproc *qproc, const struct firmware *fw) +static int q6v5_mpss_load(struct q6v5 *qproc) { - struct elf32_hdr *ehdr; - struct elf32_phdr *phdr; - const u8 *elf_data = fw->data; - unsigned long timeout; - phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; - u32 size = 0; - s32 val; + const struct firmware *fw; + phys_addr_t fw_addr; + bool relocate; int ret; - int i; - u32 v; - - ehdr = (struct elf32_hdr *)elf_data; - phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); - - v = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH); - dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %pa\n", &v); - - msleep(1); - - -#if 1 - for (i = 0; i < ehdr->e_phnum; i++, phdr++) { - phys_addr_t da = phdr->p_paddr; - u32 memsz = phdr->p_memsz; - - - if (!segment_is_loadable(phdr)) - continue; - /* - if (phdr->p_type != PT_LOAD) - continue; -*/ - dev_err(qproc->dev, "0x%x %d %d\n", phdr->p_paddr, segment_is_hash(phdr->p_flags), !!(phdr->p_flags & BIT(27))); - - /* - if (segment_is_hash(phdr->p_flags)) - continue; - - if (memsz == 0) - continue; - */ - if (da < min_addr) - min_addr = da; - - size += memsz; - } - - dev_err(qproc->dev, "verify: %pa:%pa\n", &min_addr, &size); - v = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH); - dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %pa\n", &v); -#if 0 -if (v == 0) { - writel_relaxed(min_addr, qproc->rmb_base + RMB_PMI_CODE_START); - writel(CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND); -} - writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH); -#endif -#endif - - v = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH); - dev_err(qproc->dev, "RMB_PMI_CODE_LENGTH: %pa\n", &v); - - printk("DEBUG:pil: status... %08x\n", readl_relaxed(qproc->rmb_base + RMB_MBA_STATUS)); - timeout = jiffies + 10 * HZ; - for (;;) { - msleep(1); - - val = readl(qproc->rmb_base + RMB_MBA_STATUS); - if (val == STATUS_AUTH_COMPLETE || val < 0) - break; - - if (time_after(jiffies, timeout)) - break; - } - if (val == 0) { - dev_err(qproc->dev, "MBA authentication of headers timed out\n"); - ret = -ETIMEDOUT; - goto out; - } else if (val < 0) { - dev_err(qproc->dev, "MBA returned error %d for segments\n", val); - ret = -EINVAL; - goto out; + ret = request_firmware(&fw, MPSS_FIRMWARE_NAME, qproc->dev); + if (ret < 0) { + dev_err(qproc->dev, "unable to load " MPSS_FIRMWARE_NAME "\n"); + return ret; } - ret = 0; -out: - return ret; - - -} - -static int qproc_msa_mba_auth(struct qproc *qproc) -{ - unsigned long timeout; - s32 val; - int ret; - int i; - u32 v; - - timeout = jiffies + 10 * HZ; - for (;;) { - msleep(1); - - val = readl(qproc->rmb_base + RMB_MBA_STATUS); - if (val == STATUS_AUTH_COMPLETE || val < 0) - break; - - if (time_after(jiffies, timeout)) - break; - } - if (val == 0) { - dev_err(qproc->dev, "MBA authentication of headers timed out\n"); - ret = -ETIMEDOUT; -// goto out; - } else if (val < 0) { - dev_err(qproc->dev, "MBA returned error %d for segments\n", val); - ret = -EINVAL; -// goto out; + ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate); + if (ret) { + dev_err(qproc->dev, "failed to parse mdt header\n"); + goto release_firmware; } - printk("DEBUG:pil: status auth... %08x\n", readl_relaxed(qproc->rmb_base + RMB_MBA_STATUS)); - - return 0; -} -static int qproc_load_modem(struct qproc *qproc) -{ - const struct firmware *fw; - int ret; + if (relocate) + qproc->mpss_reloc = fw_addr; - ret = request_firmware(&fw, "modem.mdt", qproc->dev); - if (ret < 0) { - dev_err(qproc->dev, "unable to load modem.mdt\n"); - return ret; - } + /* Initialize the RMB validator */ + writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); - dev_err(qproc->dev, "Loading mba\n"); - ret = qproc_mba_load_mdt(qproc, fw); + ret = q6v5_mpss_init_image(qproc, fw); if (ret) - goto out; + goto release_firmware; - dev_err(qproc->dev, "Loading segments\n"); - ret = qproc_load_segments(qproc, fw); + ret = qcom_mdt_load(qproc->rproc, fw, MPSS_FIRMWARE_NAME); if (ret) - goto out; + goto release_firmware; - ret = qproc_msa_mba_auth(qproc); -// dev_err(qproc->dev, "Verifying segments\n"); -// ret = qproc_verify_segments(qproc, fw); -// if (ret) -// goto out; - return 0; -out: - release_firmware(fw); + ret = q6v5_mpss_validate(qproc, fw); + if (ret) + goto release_firmware; - return ret; -} + ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_AUTH_COMPLETE, 10000); + if (ret == -ETIMEDOUT) + dev_err(qproc->dev, "MBA authentication timed out\n"); + else if (ret < 0) + dev_err(qproc->dev, "MBA returned error %d\n", ret); -static int pil_mss_restart_reg(struct qproc *qproc, int mss_restart) -{ - int ret = 0; - unsigned int resp; - ret = qcom_scm_restart_proc(MSS_RESTART_ID, mss_restart, &resp); - if (ret || resp) - pr_err("Secure MSS restart failed\n"); +release_firmware: + release_firmware(fw); - return ret; + return ret < 0 ? ret : 0; } -static int qproc_start(struct rproc *rproc) +static int q6v5_start(struct rproc *rproc) { - struct qproc *qproc = (struct qproc *)rproc->priv; - unsigned long timeout; + struct q6v5 *qproc = (struct q6v5 *)rproc->priv; int ret; - u32 val; - - printk("DEBUG::: pill................... %s \n", __func__); -// ret = regulator_enable(qproc->vdd); -// if (ret) { -// dev_err(qproc->dev, "failed to enable mss vdd\n"); -// return ret; -// } - ret = regulator_enable(qproc->pll); + ret = q6v5_regulator_enable(qproc); if (ret) { - dev_err(qproc->dev, "failed to enable mss vdd pll\n"); + dev_err(qproc->dev, "failed to enable supplies\n"); return ret; } - - ret = pil_mss_restart_reg(qproc, 0); - //FIXME - //ret = reset_control_deassert(qproc->mss_restart); + ret = reset_control_deassert(qproc->mss_restart); if (ret) { dev_err(qproc->dev, "failed to deassert mss restart\n"); goto disable_vdd; @@ -885,61 +490,54 @@ static int qproc_start(struct rproc *rproc) if (ret) goto disable_axi_clk; - writel_relaxed(qproc->mba_da, qproc->rmb_base + RMB_MBA_IMAGE); - - /* Ensure order of data/entry point and the following reset release */ - wmb(); + writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG); q6v5proc_reset(qproc); - timeout = jiffies + HZ; - for (;;) { - msleep(1); - - val = readl(qproc->rmb_base + RMB_PBL_STATUS); - if (val || time_after(jiffies, timeout)) - break; - } - if (val == 0) { + ret = q6v5_rmb_pbl_wait(qproc, 1000); + if (ret == -ETIMEDOUT) { dev_err(qproc->dev, "PBL boot timed out\n"); - ret = -ETIMEDOUT; goto halt_axi_ports; - } else if (val != STATUS_PBL_SUCCESS) { - dev_err(qproc->dev, "PBL returned unexpected status %d\n", val); + } else if (ret != RMB_PBL_SUCCESS) { + dev_err(qproc->dev, "PBL returned unexpected status %d\n", ret); ret = -EINVAL; goto halt_axi_ports; } - timeout = jiffies + HZ; - for (;;) { - msleep(1); - - val = readl(qproc->rmb_base + RMB_MBA_STATUS); - if (val || time_after(jiffies, timeout)) - break; - } - if (val == 0) { + ret = q6v5_rmb_mba_wait(qproc, 0, 5000); + if (ret == -ETIMEDOUT) { dev_err(qproc->dev, "MBA boot timed out\n"); - ret = -ETIMEDOUT; goto halt_axi_ports; - } else if (val != STATUS_XPU_UNLOCKED && val != STATUS_XPU_UNLOCKED_SCRIBBLED) { - dev_err(qproc->dev, "MBA returned unexpected status %d\n", val); + } else if (ret != RMB_MBA_XPU_UNLOCKED && ret != RMB_MBA_XPU_UNLOCKED_SCRIBBLED) { + dev_err(qproc->dev, "MBA returned unexpected status %d\n", ret); ret = -EINVAL; goto halt_axi_ports; } - dev_info(qproc->dev, "MBA boot done\n"); + dev_info(qproc->dev, "MBA booted, loading mpss\n"); - ret = qproc_load_modem(qproc); + ret = q6v5_mpss_load(qproc); if (ret) goto halt_axi_ports; + ret = wait_for_completion_timeout(&qproc->start_done, + msecs_to_jiffies(5000)); + if (ret == 0) { + dev_err(qproc->dev, "start timed out\n"); + ret = -ETIMEDOUT; + goto halt_axi_ports; + } + + qproc->running = true; + + /* All done, release the handover resources */ + return 0; halt_axi_ports: - q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_Q6_HALT_BASE); - q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_MODEM_HALT_BASE); - q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_NC_HALT_BASE); + q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); + q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); + q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); disable_axi_clk: clk_disable_unprepare(qproc->axi_clk); disable_ahb_clk: @@ -947,215 +545,199 @@ disable_ahb_clk: assert_reset: reset_control_assert(qproc->mss_restart); disable_vdd: -// regulator_disable(qproc->vdd); + q6v5_regulator_disable(qproc); - dma_free_attrs(qproc->dev, qproc->mba_size, qproc->mba_va, qproc->mba_da, &qproc->mba_attrs); return ret; } -static int qproc_stop(struct rproc *rproc) +static int q6v5_stop(struct rproc *rproc) { - struct qproc *qproc = (struct qproc *)rproc->priv; + struct q6v5 *qproc = (struct q6v5 *)rproc->priv; + int ret; + + qproc->running = false; + + qcom_smem_state_update_bits(qproc->state, + BIT(qproc->stop_bit), BIT(qproc->stop_bit)); + + ret = wait_for_completion_timeout(&qproc->stop_done, + msecs_to_jiffies(5000)); + if (ret == 0) + dev_err(qproc->dev, "timed out on wait\n"); - q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_Q6_HALT_BASE); - q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_MODEM_HALT_BASE); - q6v5proc_halt_axi_port(qproc, qproc->halt_base + MSS_NC_HALT_BASE); + qcom_smem_state_update_bits(qproc->state, BIT(qproc->stop_bit), 0); + + q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); + q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); + q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); reset_control_assert(qproc->mss_restart); clk_disable_unprepare(qproc->axi_clk); clk_disable_unprepare(qproc->ahb_clk); -// regulator_disable(qproc->vdd); - - dma_free_attrs(qproc->dev, qproc->mba_size, qproc->mba_va, qproc->mba_da, &qproc->mba_attrs); + q6v5_regulator_disable(qproc); return 0; } -static const struct rproc_ops qproc_ops = { - .start = qproc_start, - .stop = qproc_stop, -}; - -static irqreturn_t qproc_wdog_interrupt(int irq, void *dev) +static void *q6v5_da_to_va(struct rproc *rproc, u64 da, int len) { - struct qproc *qproc = dev; + struct q6v5 *qproc = rproc->priv; + int offset; - dev_err(qproc->dev, " WATCHDOG\n"); + offset = da - qproc->mpss_reloc; + if (offset < 0 || offset + len > qproc->mpss_size) + return NULL; - rproc_report_crash(qproc->rproc, RPROC_WATCHDOG); - return IRQ_HANDLED; + return qproc->mpss_region + offset; } -static irqreturn_t qproc_fatal_interrupt(int irq, void *dev) -{ - struct qproc *qproc = dev; - - dev_err(qproc->dev, " FATAL\n"); +static const struct rproc_ops q6v5_ops = { + .start = q6v5_start, + .stop = q6v5_stop, + .da_to_va = q6v5_da_to_va, +}; - rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR); +static irqreturn_t q6v5_wdog_interrupt(int irq, void *dev) +{ + struct q6v5 *qproc = dev; + size_t len; + char *msg; - return IRQ_HANDLED; -} + /* Sometimes the stop triggers a watchdog rather than a stop-ack */ + if (!qproc->running) { + complete(&qproc->stop_done); + return IRQ_HANDLED; + } -static irqreturn_t qproc_ready_interrupt(int irq, void *dev) -{ - struct qproc *qproc = dev; + msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, MPSS_CRASH_REASON_SMEM, &len); + if (!IS_ERR(msg) && len > 0 && msg[0]) + dev_err(qproc->dev, "watchdog received: %s\n", msg); + else + dev_err(qproc->dev, "watchdog without message\n"); - dev_err(qproc->dev, " READY\n"); + rproc_report_crash(qproc->rproc, RPROC_WATCHDOG); - complete(&qproc->start_done); + if (!IS_ERR(msg)) + msg[0] = '\0'; return IRQ_HANDLED; } -static irqreturn_t qproc_handover_interrupt(int irq, void *dev) +static irqreturn_t q6v5_fatal_interrupt(int irq, void *dev) { - struct qproc *qproc = dev; + struct q6v5 *qproc = dev; + size_t len; + char *msg; - dev_err(qproc->dev, " HANDOVER\n"); - - return IRQ_HANDLED; -} + msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, MPSS_CRASH_REASON_SMEM, &len); + if (!IS_ERR(msg) && len > 0 && msg[0]) + dev_err(qproc->dev, "fatal error received: %s\n", msg); + else + dev_err(qproc->dev, "fatal error without message\n"); -static irqreturn_t qproc_stop_ack_interrupt(int irq, void *dev) -{ - struct qproc *qproc = dev; + rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR); - dev_err(qproc->dev, " STOP-ACK\n"); + if (!IS_ERR(msg)) + msg[0] = '\0'; return IRQ_HANDLED; } -static ssize_t qproc_boot_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) +static irqreturn_t q6v5_handover_interrupt(int irq, void *dev) { - struct qproc *qproc = dev_get_drvdata(dev); - int ret; + struct q6v5 *qproc = dev; - ret = rproc_boot(qproc->rproc); - return ret ? : size; + complete(&qproc->start_done); + return IRQ_HANDLED; } -static ssize_t qproc_shutdown_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) +static irqreturn_t q6v5_stop_ack_interrupt(int irq, void *dev) { - struct qproc *qproc = dev_get_drvdata(dev); + struct q6v5 *qproc = dev; - rproc_shutdown(qproc->rproc); - return size; + complete(&qproc->stop_done); + return IRQ_HANDLED; } -static const struct device_attribute qproc_attrs[] = { - __ATTR(boot, S_IWUSR, 0, qproc_boot_store), - __ATTR(shutdown, S_IWUSR, 0, qproc_shutdown_store), -}; - -static int qproc_init_mem(struct qproc *qproc, struct platform_device *pdev) +static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) { + struct device_node *halt_np; struct resource *res; + int ret; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6_base"); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6"); qproc->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(qproc->reg_base)) + if (IS_ERR(qproc->reg_base)) { + dev_err(qproc->dev, "failed to get qdsp6_base\n"); return PTR_ERR(qproc->reg_base); + } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_base"); - qproc->halt_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(qproc->halt_base)) { -#if 0 - //return PTR_ERR(qproc->halt_base); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_q6"); - qproc->halt_q6 = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(qproc->halt_q6)) - return PTR_ERR(qproc->halt_q6); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb"); + qproc->rmb_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(qproc->rmb_base)) { + dev_err(qproc->dev, "failed to get rmb_base\n"); + return PTR_ERR(qproc->rmb_base); + } + halt_np = of_parse_phandle(pdev->dev.of_node, "qcom,halt-regs", 0); + if (!halt_np) { + dev_err(&pdev->dev, "no qcom,halt-regs node\n"); + return -ENODEV; + } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_modem"); - qproc->halt_modem = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(qproc->halt_modem)) - return PTR_ERR(qproc->halt_modem); + qproc->halt_map = syscon_node_to_regmap(halt_np); + if (IS_ERR(qproc->halt_map)) + return PTR_ERR(qproc->halt_map); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_nc"); - qproc->halt_nc = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(qproc->halt_nc)) - return PTR_ERR(qproc->halt_nc); -#endif + ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,halt-regs", + 1, &qproc->halt_q6); + if (ret < 0) { + dev_err(&pdev->dev, "no q6 halt offset\n"); + return -EINVAL; + } + ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,halt-regs", + 2, &qproc->halt_modem); + if (ret < 0) { + dev_err(&pdev->dev, "no modem halt offset\n"); + return -EINVAL; } -//Only required if self auth is set??? - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb_base"); - qproc->rmb_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(qproc->rmb_base)) - return PTR_ERR(qproc->rmb_base); -// res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "restart_reg_sec"); -// qproc->restart_sec_base = devm_ioremap_resource(&pdev->dev, res); -// if (IS_ERR(qproc->restart_sec_base)) -// return PTR_ERR(qproc->restart_sec_base); + ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,halt-regs", + 3, &qproc->halt_nc); + if (ret < 0) { + dev_err(&pdev->dev, "no nc halt offset\n"); + return -EINVAL; + } return 0; } -static int qproc_init_clocks(struct qproc *qproc) +static int q6v5_init_clocks(struct q6v5 *qproc) { qproc->ahb_clk = devm_clk_get(qproc->dev, "iface"); - if (IS_ERR(qproc->ahb_clk)) + if (IS_ERR(qproc->ahb_clk)) { + dev_err(qproc->dev, "failed to get iface clock\n"); return PTR_ERR(qproc->ahb_clk); + } qproc->axi_clk = devm_clk_get(qproc->dev, "bus"); - if (IS_ERR(qproc->axi_clk)) + if (IS_ERR(qproc->axi_clk)) { + dev_err(qproc->dev, "failed to get bus clock\n"); return PTR_ERR(qproc->axi_clk); + } qproc->rom_clk = devm_clk_get(qproc->dev, "mem"); - if (IS_ERR(qproc->rom_clk)) + if (IS_ERR(qproc->rom_clk)) { + dev_err(qproc->dev, "failed to get mem clock\n"); return PTR_ERR(qproc->rom_clk); + } return 0; } -static int qproc_init_regulators(struct qproc *qproc) -{ - int ret; - u32 uV; - - printk("DEBUG:pil: starting vdd\n"); -// qproc->vdd = devm_regulator_get_optional(qproc->dev, "qcom,vdd"); -// if (IS_ERR(qproc->vdd)) -// return PTR_ERR(qproc->vdd); - -// regulator_set_voltage(qproc->vdd, VDD_MSS_UV, VDD_MSS_UV_MAX); -// regulator_set_load(qproc->vdd, VDD_MSS_UA); - - printk("DEBUG:pil: done vdd\n"); -// qproc->cx = devm_regulator_get(qproc->dev, "qcom,cx"); -// if (IS_ERR(qproc->cx)) -// return PTR_ERR(qproc->cx); - - qproc->mx = devm_regulator_get(qproc->dev, "qcom,mx"); - if (IS_ERR(qproc->mx)) - return PTR_ERR(qproc->mx); - - ret = of_property_read_u32(qproc->dev->of_node, "qcom,mx-uV", &uV); - if (!ret) - regulator_set_voltage(qproc->mx, uV, uV); - - qproc->pll = devm_regulator_get(qproc->dev, "qcom,pll"); - if (IS_ERR(qproc->pll)) - return PTR_ERR(qproc->pll); - - ret = of_property_read_u32(qproc->dev->of_node, "qcom,pll-uV", &uV); - if (!ret) - regulator_set_voltage(qproc->pll, uV, uV); - - return 0; -} - -static int qproc_init_reset(struct qproc *qproc) +static int q6v5_init_reset(struct q6v5 *qproc) { - //FIXME qproc->mss_restart = devm_reset_control_get(qproc->dev, NULL); if (IS_ERR(qproc->mss_restart)) { dev_err(qproc->dev, "failed to acquire mss restart\n"); @@ -1165,7 +747,7 @@ static int qproc_init_reset(struct qproc *qproc) return 0; } -static int qproc_request_irq(struct qproc *qproc, +static int q6v5_request_irq(struct q6v5 *qproc, struct platform_device *pdev, const char *name, irq_handler_t thread_fn) @@ -1181,150 +763,153 @@ static int qproc_request_irq(struct qproc *qproc, ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, thread_fn, IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "qproc", qproc); + "q6v5", qproc); if (ret) dev_err(&pdev->dev, "request %s IRQ failed\n", name); return ret; } -static int qproc_probe(struct platform_device *pdev) +static int q6v5_alloc_memory_region(struct q6v5 *qproc) +{ + struct device_node *child; + struct device_node *node; + struct resource r; + int ret; + + child = of_get_child_by_name(qproc->dev->of_node, "mba"); + node = of_parse_phandle(child, "memory-region", 0); + ret = of_address_to_resource(node, 0, &r); + if (ret) { + dev_err(qproc->dev, "unable to resolve mba region\n"); + return ret; + } + + qproc->mba_phys = r.start; + qproc->mba_size = resource_size(&r); + qproc->mba_region = devm_ioremap_wc(qproc->dev, qproc->mba_phys, qproc->mba_size); + if (!qproc->mba_region) { + dev_err(qproc->dev, "unable to map memory region: %pa+%zx\n", + &r.start, qproc->mba_size); + return -EBUSY; + } + + child = of_get_child_by_name(qproc->dev->of_node, "mpss"); + node = of_parse_phandle(child, "memory-region", 0); + ret = of_address_to_resource(node, 0, &r); + if (ret) { + dev_err(qproc->dev, "unable to resolve mpss region\n"); + return ret; + } + + qproc->mpss_phys = qproc->mpss_reloc = r.start; + qproc->mpss_size = resource_size(&r); + qproc->mpss_region = devm_ioremap_wc(qproc->dev, qproc->mpss_phys, qproc->mpss_size); + if (!qproc->mpss_region) { + dev_err(qproc->dev, "unable to map memory region: %pa+%zx\n", + &r.start, qproc->mpss_size); + return -EBUSY; + } + + return 0; +} + +static int q6v5_probe(struct platform_device *pdev) { - struct qproc *qproc; + struct q6v5 *qproc; struct rproc *rproc; int ret; - int i; - struct device_node *np; - struct resource r; - rproc = rproc_alloc(&pdev->dev, pdev->name, &qproc_ops, - "mba.mbn", sizeof(*qproc)); - if (!rproc) + rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops, + MBA_FIRMWARE_NAME, sizeof(*qproc)); + if (!rproc) { + dev_err(&pdev->dev, "failed to allocate rproc\n"); return -ENOMEM; + } - rproc->fw_ops = &qproc_fw_ops; + rproc->fw_ops = &q6v5_fw_ops; - qproc = (struct qproc *)rproc->priv; + qproc = (struct q6v5 *)rproc->priv; qproc->dev = &pdev->dev; qproc->rproc = rproc; platform_set_drvdata(pdev, qproc); init_completion(&qproc->start_done); + init_completion(&qproc->stop_done); - ret = qproc_init_mem(qproc, pdev); + ret = q6v5_init_mem(qproc, pdev); if (ret) goto free_rproc; - ret = qproc_init_clocks(qproc); + ret = q6v5_alloc_memory_region(qproc); if (ret) goto free_rproc; - ret = qproc_init_regulators(qproc); + ret = q6v5_init_clocks(qproc); if (ret) goto free_rproc; - //FIXME need to convert this to a proper reset... -// ret = qproc_init_reset(qproc); -// if (ret) -// goto free_rproc; + ret = q6v5_regulator_init(qproc); + if (ret) + goto free_rproc; - ret = qproc_request_irq(qproc, pdev, "wdog", qproc_wdog_interrupt); - if (ret < 0) + ret = q6v5_init_reset(qproc); + if (ret) goto free_rproc; - qproc->wdog_irq = ret; - ret = qproc_request_irq(qproc, pdev, "fatal", qproc_fatal_interrupt); + ret = q6v5_request_irq(qproc, pdev, "wdog", q6v5_wdog_interrupt); if (ret < 0) goto free_rproc; - qproc->fatal_irq = ret; - ret = qproc_request_irq(qproc, pdev, "ready", qproc_ready_interrupt); + ret = q6v5_request_irq(qproc, pdev, "fatal", q6v5_fatal_interrupt); if (ret < 0) goto free_rproc; - qproc->ready_irq = ret; - ret = qproc_request_irq(qproc, pdev, "handover", qproc_handover_interrupt); + ret = q6v5_request_irq(qproc, pdev, "handover", q6v5_handover_interrupt); if (ret < 0) goto free_rproc; - qproc->handover_irq = ret; - ret = qproc_request_irq(qproc, pdev, "stop-ack", qproc_stop_ack_interrupt); + ret = q6v5_request_irq(qproc, pdev, "stop-ack", q6v5_stop_ack_interrupt); if (ret < 0) goto free_rproc; - qproc->stop_ack_irq = ret; - - qproc->stop_gpio = devm_gpiod_get(&pdev->dev, "qcom,stop", GPIOD_OUT_LOW); - if (IS_ERR(qproc->stop_gpio)) { - dev_err(&pdev->dev, "failed to acquire stop gpio\n"); - return PTR_ERR(qproc->stop_gpio); - } - - for (i = 0; i < ARRAY_SIZE(qproc_attrs); i++) { - ret = device_create_file(&pdev->dev, &qproc_attrs[i]); - if (ret) { - dev_err(&pdev->dev, "unable to create sysfs file\n"); - goto remove_device_files; - } - } - - np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); - if (!np) { - dev_err(&pdev->dev, "No memory region specified\n"); - } else { - - ret = of_address_to_resource(np, 0, &r); - of_node_put(np); - if (ret) - return ret; - - qproc->reloc_phys = r.start; - qproc->reloc_size = resource_size(&r); - - dev_info(&pdev->dev, "Found relocation area %lu@%pad\n", - qproc->reloc_size, &qproc->reloc_phys); - } + qproc->state = qcom_smem_state_get(&pdev->dev, "stop", &qproc->stop_bit); + if (IS_ERR(qproc->state)) + goto free_rproc; ret = rproc_add(rproc); if (ret) - goto remove_device_files; + goto free_rproc; return 0; -remove_device_files: - for (i--; i >= 0; i--) - device_remove_file(&pdev->dev, &qproc_attrs[i]); - free_rproc: rproc_put(rproc); return ret; } -static int qproc_remove(struct platform_device *pdev) +static int q6v5_remove(struct platform_device *pdev) { - struct qproc *qproc = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < ARRAY_SIZE(qproc_attrs); i++) - device_remove_file(&pdev->dev, &qproc_attrs[i]); + struct q6v5 *qproc = platform_get_drvdata(pdev); + rproc_del(qproc->rproc); rproc_put(qproc->rproc); return 0; } -static const struct of_device_id qproc_of_match[] = { +static const struct of_device_id q6v5_of_match[] = { { .compatible = "qcom,q6v5-pil", }, { }, }; -static struct platform_driver qproc_driver = { - .probe = qproc_probe, - .remove = qproc_remove, +static struct platform_driver q6v5_driver = { + .probe = q6v5_probe, + .remove = q6v5_remove, .driver = { .name = "qcom-q6v5-pil", - .of_match_table = qproc_of_match, + .of_match_table = q6v5_of_match, }, }; -module_platform_driver(qproc_driver); +module_platform_driver(q6v5_driver); diff --git a/drivers/remoteproc/qcom_tz_pil.c b/drivers/remoteproc/qcom_tz_pil.c index ae361fc26dd1..6de08442f57f 100644 --- a/drivers/remoteproc/qcom_tz_pil.c +++ b/drivers/remoteproc/qcom_tz_pil.c @@ -276,8 +276,7 @@ static int qproc_load(struct rproc *rproc, const struct firmware *fw) max_addr = round_up(phdr->p_paddr + phdr->p_memsz, SZ_4K); } - ret = qcom_scm_pas_init_image(qproc->dev, - qproc->pas_id, fw->data, fw->size); + ret = qcom_scm_pas_init_image(qproc->pas_id, fw->data, fw->size); if (ret) { dev_err(qproc->dev, "Invalid firmware metadata\n"); return -EINVAL; diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c new file mode 100644 index 000000000000..648afdb99fb0 --- /dev/null +++ b/drivers/remoteproc/qcom_wcnss.c @@ -0,0 +1,620 @@ +/* + * Qualcomm Peripheral Image Loader + * + * Copyright (C) 2016 Linaro Ltd + * Copyright (C) 2014 Sony Mobile Communications AB + * Copyright (c) 2012-2013, The Linux Foundation. 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_mdt_loader.h" +#include "remoteproc_internal.h" +#include "qcom_wcnss.h" + +#define WCNSS_CRASH_REASON_SMEM 422 +#define WCNSS_FIRMWARE_NAME "wcnss.mdt" +#define WCNSS_PAS_ID 6 + +#define WCNSS_SPARE_NVBIN_DLND BIT(25) + +#define WCNSS_PMU_IRIS_XO_CFG BIT(3) +#define WCNSS_PMU_IRIS_XO_EN BIT(4) +#define WCNSS_PMU_GC_BUS_MUX_SEL_TOP BIT(5) +#define WCNSS_PMU_IRIS_XO_CFG_STS BIT(6) /* 1: in progress, 0: done */ + +#define WCNSS_PMU_IRIS_RESET BIT(7) +#define WCNSS_PMU_IRIS_RESET_STS BIT(8) /* 1: in progress, 0: done */ +#define WCNSS_PMU_IRIS_XO_READ BIT(9) +#define WCNSS_PMU_IRIS_XO_READ_STS BIT(10) + +#define WCNSS_PMU_XO_MODE_MASK GENMASK(2, 1) +#define WCNSS_PMU_XO_MODE_19p2 0 +#define WCNSS_PMU_XO_MODE_48 3 + +static const struct rproc_ops wcnss_ops; + +struct wcnss_data { + size_t pmu_offset; + size_t spare_offset; + + const struct wcnss_vreg_info *vregs; + size_t num_vregs; +}; + +struct qcom_wcnss { + struct device *dev; + struct rproc *rproc; + + void __iomem *pmu_cfg; + void __iomem *spare_out; + + bool use_48mhz_xo; + + int wdog_irq; + int fatal_irq; + int ready_irq; + int handover_irq; + int stop_ack_irq; + + struct qcom_smem_state *state; + unsigned stop_bit; + + struct mutex iris_lock; + struct qcom_iris *iris; + + struct regulator_bulk_data *vregs; + size_t num_vregs; + + struct completion start_done; + struct completion stop_done; + + phys_addr_t mem_phys; + phys_addr_t mem_reloc; + void *mem_region; + size_t mem_size; +}; + +static const struct wcnss_data riva_data = { + .pmu_offset = 0x28, + .spare_offset = 0xb4, + + .vregs = (struct wcnss_vreg_info[]) { + { "vddmx", 1050000, 1150000, 0 }, + { "vddcx", 1050000, 1150000, 0 }, + { "vddpx", 1800000, 1800000, 0 }, + }, + .num_vregs = 3, +}; + +static const struct wcnss_data pronto_v1_data = { + .pmu_offset = 0x1004, + .spare_offset = 0x1088, + + .vregs = (struct wcnss_vreg_info[]) { + { "vddmx", 950000, 1150000, 0 }, + { "vddcx", .super_turbo = true}, + { "vddpx", 1800000, 1800000, 0 }, + }, + .num_vregs = 3, +}; + +static const struct wcnss_data pronto_v2_data = { + .pmu_offset = 0x1004, + .spare_offset = 0x1088, + + .vregs = (struct wcnss_vreg_info[]) { + { "vddmx", 1287500, 1287500, 0 }, + { "vddcx", .super_turbo = true }, + { "vddpx", 1800000, 1800000, 0 }, + }, + .num_vregs = 3, +}; + +void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss, + struct qcom_iris *iris, + bool use_48mhz_xo) +{ + mutex_lock(&wcnss->iris_lock); + + wcnss->iris = iris; + wcnss->use_48mhz_xo = use_48mhz_xo; + + mutex_unlock(&wcnss->iris_lock); +} + +static int wcnss_load(struct rproc *rproc, const struct firmware *fw) +{ + struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; + phys_addr_t fw_addr; + size_t fw_size; + bool relocate; + int ret; + + ret = qcom_scm_pas_init_image(WCNSS_PAS_ID, fw->data, fw->size); + if (ret) { + dev_err(&rproc->dev, "invalid firmware metadata\n"); + return -EINVAL; + } + + ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate); + if (ret) { + dev_err(&rproc->dev, "failed to parse mdt header\n"); + return ret; + } + + if (relocate) { + wcnss->mem_reloc = fw_addr; + + ret = qcom_scm_pas_mem_setup(WCNSS_PAS_ID, wcnss->mem_phys, fw_size); + if (ret) { + dev_err(&rproc->dev, "unable to setup memory for image\n"); + return -EINVAL; + } + } + + return qcom_mdt_load(rproc, fw, rproc->firmware); +} + +static const struct rproc_fw_ops wcnss_fw_ops = { + .find_rsc_table = qcom_mdt_find_rsc_table, + .load = wcnss_load, +}; + +static void wcnss_indicate_nv_download(struct qcom_wcnss *wcnss) +{ + u32 val; + + /* Indicate NV download capability */ + val = readl(wcnss->spare_out); + val |= WCNSS_SPARE_NVBIN_DLND; + writel(val, wcnss->spare_out); +} + +static void wcnss_configure_iris(struct qcom_wcnss *wcnss) +{ + u32 val; + + /* Clear PMU cfg register */ + writel(0, wcnss->pmu_cfg); + + val = WCNSS_PMU_GC_BUS_MUX_SEL_TOP | WCNSS_PMU_IRIS_XO_EN; + writel(val, wcnss->pmu_cfg); + + /* Clear XO_MODE */ + val &= ~WCNSS_PMU_XO_MODE_MASK; + if (wcnss->use_48mhz_xo) + val |= WCNSS_PMU_XO_MODE_48 << 1; + else + val |= WCNSS_PMU_XO_MODE_19p2 << 1; + writel(val, wcnss->pmu_cfg); + + /* Reset IRIS */ + val |= WCNSS_PMU_IRIS_RESET; + writel(val, wcnss->pmu_cfg); + + /* Wait for PMU.iris_reg_reset_sts */ + while (readl(wcnss->pmu_cfg) & WCNSS_PMU_IRIS_RESET_STS) + cpu_relax(); + + /* Clear IRIS reset */ + val &= ~WCNSS_PMU_IRIS_RESET; + writel(val, wcnss->pmu_cfg); + + /* Start IRIS XO configuration */ + val |= WCNSS_PMU_IRIS_XO_CFG; + writel(val, wcnss->pmu_cfg); + + /* Wait for XO configuration to finish */ + while (readl(wcnss->pmu_cfg) & WCNSS_PMU_IRIS_XO_CFG_STS) + cpu_relax(); + + /* Stop IRIS XO configuration */ + val &= ~WCNSS_PMU_GC_BUS_MUX_SEL_TOP; + val &= ~WCNSS_PMU_IRIS_XO_CFG; + writel(val, wcnss->pmu_cfg); + + /* Add some delay for XO to settle */ + msleep(20); +} + +static int wcnss_start(struct rproc *rproc) +{ + struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; + int ret; + + mutex_lock(&wcnss->iris_lock); + if (!wcnss->iris) { + dev_err(wcnss->dev, "no iris registered\n"); + ret = -EINVAL; + goto release_iris_lock; + } + + ret = regulator_bulk_enable(wcnss->num_vregs, wcnss->vregs); + if (ret) + goto release_iris_lock; + + ret = qcom_iris_enable(wcnss->iris); + if (ret) + goto disable_regulators; + + wcnss_indicate_nv_download(wcnss); + wcnss_configure_iris(wcnss); + + ret = qcom_scm_pas_auth_and_reset(WCNSS_PAS_ID); + if (ret) { + dev_err(wcnss->dev, + "failed to authenticate image and release reset\n"); + goto disable_iris; + } + + ret = wait_for_completion_timeout(&wcnss->start_done, + msecs_to_jiffies(5000)); + if (wcnss->ready_irq > 0 && ret == 0) { + /* We have a ready_irq, but it didn't fire in time. */ + dev_err(wcnss->dev, "start timed out\n"); + qcom_scm_pas_shutdown(WCNSS_PAS_ID); + ret = -ETIMEDOUT; + goto disable_iris; + } + + ret = 0; + +disable_iris: + qcom_iris_disable(wcnss->iris); +disable_regulators: + regulator_bulk_disable(wcnss->num_vregs, wcnss->vregs); +release_iris_lock: + mutex_unlock(&wcnss->iris_lock); + + return ret; +} + +static int wcnss_stop(struct rproc *rproc) +{ + struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; + int ret; + + if (wcnss->state) { + qcom_smem_state_update_bits(wcnss->state, + BIT(wcnss->stop_bit), + BIT(wcnss->stop_bit)); + + ret = wait_for_completion_timeout(&wcnss->stop_done, + msecs_to_jiffies(5000)); + if (ret == 0) + dev_err(wcnss->dev, "timed out on wait\n"); + + qcom_smem_state_update_bits(wcnss->state, + BIT(wcnss->stop_bit), + 0); + } + + ret = qcom_scm_pas_shutdown(WCNSS_PAS_ID); + if (ret) + dev_err(wcnss->dev, "failed to shutdown: %d\n", ret); + + return ret; +} + +static void *wcnss_da_to_va(struct rproc *rproc, u64 da, int len) +{ + struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; + int offset; + + offset = da - wcnss->mem_reloc; + if (offset < 0 || offset + len > wcnss->mem_size) + return NULL; + + return wcnss->mem_region + offset; +} + +static const struct rproc_ops wcnss_ops = { + .start = wcnss_start, + .stop = wcnss_stop, + .da_to_va = wcnss_da_to_va, +}; + +static irqreturn_t wcnss_wdog_interrupt(int irq, void *dev) +{ + struct qcom_wcnss *wcnss = dev; + + rproc_report_crash(wcnss->rproc, RPROC_WATCHDOG); + return IRQ_HANDLED; +} + +static irqreturn_t wcnss_fatal_interrupt(int irq, void *dev) +{ + struct qcom_wcnss *wcnss = dev; + size_t len; + char *msg; + + msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, WCNSS_CRASH_REASON_SMEM, &len); + if (!IS_ERR(msg) && len > 0 && msg[0]) + dev_err(wcnss->dev, "fatal error received: %s\n", msg); + + rproc_report_crash(wcnss->rproc, RPROC_FATAL_ERROR); + + if (!IS_ERR(msg)) + msg[0] = '\0'; + + return IRQ_HANDLED; +} + +static irqreturn_t wcnss_ready_interrupt(int irq, void *dev) +{ + struct qcom_wcnss *wcnss = dev; + + complete(&wcnss->start_done); + + return IRQ_HANDLED; +} + +static irqreturn_t wcnss_handover_interrupt(int irq, void *dev) +{ + /* + * XXX: At this point we're supposed to release the resources that we + * have been holding on behalf of the WCNSS. Unfortunately this + * interrupt comes way before the other side seems to be done. + * + * So we're currently relying on the ready interrupt firing later then + * this and we just disable the resources at the end of wcnss_start(). + */ + + return IRQ_HANDLED; +} + +static irqreturn_t wcnss_stop_ack_interrupt(int irq, void *dev) +{ + struct qcom_wcnss *wcnss = dev; + + complete(&wcnss->stop_done); + return IRQ_HANDLED; +} + +static int wcnss_init_regulators(struct qcom_wcnss *wcnss, + const struct wcnss_vreg_info *info, + int num_vregs) +{ + struct regulator_bulk_data *bulk; + int ret; + int i; + + bulk = devm_kcalloc(wcnss->dev, + num_vregs, sizeof(struct regulator_bulk_data), + GFP_KERNEL); + if (!bulk) + return -ENOMEM; + + for (i = 0; i < num_vregs; i++) + bulk[i].supply = info[i].name; + + ret = devm_regulator_bulk_get(wcnss->dev, num_vregs, bulk); + if (ret) + return ret; + + for (i = 0; i < num_vregs; i++) { + if (info[i].max_voltage) + regulator_set_voltage(bulk[i].consumer, + info[i].min_voltage, + info[i].max_voltage); + + if (info[i].load_uA) + regulator_set_load(bulk[i].consumer, info[i].load_uA); + } + + wcnss->vregs = bulk; + wcnss->num_vregs = num_vregs; + + return 0; +} + +static int wcnss_request_irq(struct qcom_wcnss *wcnss, + struct platform_device *pdev, + const char *name, + bool optional, + irq_handler_t thread_fn) +{ + int ret; + + ret = platform_get_irq_byname(pdev, name); + if (ret < 0 && optional) { + dev_dbg(&pdev->dev, "no %s IRQ defined, ignoring\n", name); + return 0; + } else if (ret < 0) { + dev_err(&pdev->dev, "no %s IRQ defined\n", name); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, ret, + NULL, thread_fn, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "wcnss", wcnss); + if (ret) + dev_err(&pdev->dev, "request %s IRQ failed\n", name); + return ret; +} + +static int wcnss_alloc_memory_region(struct qcom_wcnss *wcnss) +{ + struct device_node *node; + struct resource r; + int ret; + + node = of_parse_phandle(wcnss->dev->of_node, "memory-region", 0); + if (!node) { + dev_err(wcnss->dev, "no memory-region specified\n"); + return -EINVAL; + } + + ret = of_address_to_resource(node, 0, &r); + if (ret) + return ret; + + wcnss->mem_phys = wcnss->mem_reloc = r.start; + wcnss->mem_size = resource_size(&r); + wcnss->mem_region = devm_ioremap_wc(wcnss->dev, wcnss->mem_phys, wcnss->mem_size); + if (!wcnss->mem_region) { + dev_err(wcnss->dev, "unable to map memory region: %pa+%zx\n", + &r.start, wcnss->mem_size); + return -EBUSY; + } + + return 0; +} + +static int wcnss_probe(struct platform_device *pdev) +{ + const struct wcnss_data *data; + struct qcom_wcnss *wcnss; + struct resource *res; + struct rproc *rproc; + void __iomem *mmio; + int ret; + + data = of_device_get_match_data(&pdev->dev); + + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + + if (!qcom_scm_pas_supported(WCNSS_PAS_ID)) { + dev_err(&pdev->dev, "PAS is not available for WCNSS\n"); + return -ENXIO; + } + + rproc = rproc_alloc(&pdev->dev, pdev->name, &wcnss_ops, + WCNSS_FIRMWARE_NAME, sizeof(*wcnss)); + if (!rproc) { + dev_err(&pdev->dev, "unable to allocate remoteproc\n"); + return -ENOMEM; + } + + rproc->fw_ops = &wcnss_fw_ops; + + wcnss = (struct qcom_wcnss *)rproc->priv; + wcnss->dev = &pdev->dev; + wcnss->rproc = rproc; + platform_set_drvdata(pdev, wcnss); + + init_completion(&wcnss->start_done); + init_completion(&wcnss->stop_done); + + mutex_init(&wcnss->iris_lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmu"); + mmio = devm_ioremap_resource(&pdev->dev, res); + if (!mmio) { + ret = -ENOMEM; + goto free_rproc; + }; + + ret = wcnss_alloc_memory_region(wcnss); + if (ret) + goto free_rproc; + + wcnss->pmu_cfg = mmio + data->pmu_offset; + wcnss->spare_out = mmio + data->spare_offset; + + ret = wcnss_init_regulators(wcnss, data->vregs, data->num_vregs); + if (ret) + goto free_rproc; + + ret = wcnss_request_irq(wcnss, pdev, "wdog", false, wcnss_wdog_interrupt); + if (ret < 0) + goto free_rproc; + wcnss->wdog_irq = ret; + + ret = wcnss_request_irq(wcnss, pdev, "fatal", false, wcnss_fatal_interrupt); + if (ret < 0) + goto free_rproc; + wcnss->fatal_irq = ret; + + ret = wcnss_request_irq(wcnss, pdev, "ready", true, wcnss_ready_interrupt); + if (ret < 0) + goto free_rproc; + wcnss->ready_irq = ret; + + ret = wcnss_request_irq(wcnss, pdev, "handover", true, wcnss_handover_interrupt); + if (ret < 0) + goto free_rproc; + wcnss->handover_irq = ret; + + ret = wcnss_request_irq(wcnss, pdev, "stop-ack", true, wcnss_stop_ack_interrupt); + if (ret < 0) + goto free_rproc; + wcnss->stop_ack_irq = ret; + + if (wcnss->stop_ack_irq) { + wcnss->state = qcom_smem_state_get(&pdev->dev, "stop", + &wcnss->stop_bit); + if (IS_ERR(wcnss->state)) { + ret = PTR_ERR(wcnss->state); + goto free_rproc; + } + } + + ret = rproc_add(rproc); + if (ret) + goto free_rproc; + + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + +free_rproc: + rproc_put(rproc); + + return ret; +} + +static int wcnss_remove(struct platform_device *pdev) +{ + struct qcom_wcnss *wcnss = platform_get_drvdata(pdev); + + of_platform_depopulate(&pdev->dev); + + qcom_smem_state_put(wcnss->state); + rproc_del(wcnss->rproc); + rproc_put(wcnss->rproc); + + return 0; +} + +static const struct of_device_id wcnss_of_match[] = { + { .compatible = "qcom,riva-pil", &riva_data }, + { .compatible = "qcom,pronto-v1-pil", &pronto_v1_data }, + { .compatible = "qcom,pronto-v2-pil", &pronto_v2_data }, + { }, +}; + +static struct platform_driver wcnss_driver = { + .probe = wcnss_probe, + .remove = wcnss_remove, + .driver = { + .name = "qcom-wcnss-pil", + .of_match_table = wcnss_of_match, + }, +}; + +module_platform_driver(wcnss_driver); diff --git a/drivers/remoteproc/qcom_wcnss.h b/drivers/remoteproc/qcom_wcnss.h new file mode 100644 index 000000000000..9dc4a9fe41e1 --- /dev/null +++ b/drivers/remoteproc/qcom_wcnss.h @@ -0,0 +1,22 @@ +#ifndef __QCOM_WNCSS_H__ +#define __QCOM_WNCSS_H__ + +struct qcom_iris; +struct qcom_wcnss; + +struct wcnss_vreg_info { + const char * const name; + int min_voltage; + int max_voltage; + + int load_uA; + + bool super_turbo; +}; + +int qcom_iris_enable(struct qcom_iris *iris); +void qcom_iris_disable(struct qcom_iris *iris); + +void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss, struct qcom_iris *iris, bool use_48mhz_xo); + +#endif diff --git a/drivers/remoteproc/qcom_wcnss_iris.c b/drivers/remoteproc/qcom_wcnss_iris.c new file mode 100644 index 000000000000..62a37583c0f8 --- /dev/null +++ b/drivers/remoteproc/qcom_wcnss_iris.c @@ -0,0 +1,185 @@ +/* + * Qualcomm Peripheral Image Loader + * + * Copyright (C) 2016 Linaro Ltd + * Copyright (C) 2014 Sony Mobile Communications AB + * Copyright (c) 2012-2013, The Linux Foundation. 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 +#include +#include +#include +#include +#include +#include + +#include "qcom_wcnss.h" + +struct qcom_iris { + struct device *dev; + + struct clk *xo_clk; + + struct regulator_bulk_data *vregs; + size_t num_vregs; +}; + +struct iris_data { + const struct wcnss_vreg_info *vregs; + size_t num_vregs; + + bool use_48mhz_xo; +}; + +static const struct iris_data wcn3620_data = { + .vregs = (struct wcnss_vreg_info[]) { + { "vddxo", 1800000, 1800000, 10000 }, + { "vddrfa", 1300000, 1300000, 100000 }, + { "vddpa", 3300000, 3300000, 515000 }, + { "vdddig", 1800000, 1800000, 10000 }, + }, + .num_vregs = 4, + .use_48mhz_xo = false, +}; + +static const struct iris_data wcn3660_data = { + .vregs = (struct wcnss_vreg_info[]) { + { "vddxo", 1800000, 1800000, 10000 }, + { "vddrfa", 1300000, 1300000, 100000 }, + { "vddpa", 2900000, 3000000, 515000 }, + { "vdddig", 1200000, 1225000, 10000 }, + }, + .num_vregs = 4, + .use_48mhz_xo = true, +}; + +static const struct iris_data wcn3680_data = { + .vregs = (struct wcnss_vreg_info[]) { + { "vddxo", 1800000, 1800000, 10000 }, + { "vddrfa", 1300000, 1300000, 100000 }, + { "vddpa", 3300000, 3300000, 515000 }, + { "vdddig", 1800000, 1800000, 10000 }, + }, + .num_vregs = 4, + .use_48mhz_xo = true, +}; + +int qcom_iris_enable(struct qcom_iris *iris) +{ + int ret; + + ret = regulator_bulk_enable(iris->num_vregs, iris->vregs); + if (ret) + return ret; + + ret = clk_prepare_enable(iris->xo_clk); + if (ret) { + dev_err(iris->dev, "failed to enable xo clk\n"); + goto disable_regulators; + } + + return 0; + +disable_regulators: + regulator_bulk_disable(iris->num_vregs, iris->vregs); + + return ret; +} + +void qcom_iris_disable(struct qcom_iris *iris) +{ + clk_disable_unprepare(iris->xo_clk); + regulator_bulk_disable(iris->num_vregs, iris->vregs); +} + +static int qcom_iris_probe(struct platform_device *pdev) +{ + const struct iris_data *data; + struct qcom_wcnss *wcnss; + struct qcom_iris *iris; + int ret; + int i; + + iris = devm_kzalloc(&pdev->dev, sizeof(struct qcom_iris), GFP_KERNEL); + if (!iris) + return -ENOMEM; + + data = of_device_get_match_data(&pdev->dev); + wcnss = dev_get_drvdata(pdev->dev.parent); + + iris->xo_clk = devm_clk_get(&pdev->dev, "xo"); + if (IS_ERR(iris->xo_clk)) { + if (PTR_ERR(iris->xo_clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to acquire xo clk\n"); + return PTR_ERR(iris->xo_clk); + } + + iris->num_vregs = data->num_vregs; + iris->vregs = devm_kcalloc(&pdev->dev, + iris->num_vregs, + sizeof(struct regulator_bulk_data), + GFP_KERNEL); + if (!iris->vregs) + return -ENOMEM; + + for (i = 0; i < iris->num_vregs; i++) + iris->vregs[i].supply = data->vregs[i].name; + + ret = devm_regulator_bulk_get(&pdev->dev, iris->num_vregs, iris->vregs); + if (ret) { + dev_err(&pdev->dev, "failed to get regulators\n"); + return ret; + } + + for (i = 0; i < iris->num_vregs; i++) { + if (data->vregs[i].max_voltage) + regulator_set_voltage(iris->vregs[i].consumer, + data->vregs[i].min_voltage, + data->vregs[i].max_voltage); + + if (data->vregs[i].load_uA) + regulator_set_load(iris->vregs[i].consumer, + data->vregs[i].load_uA); + } + + qcom_wcnss_assign_iris(wcnss, iris, data->use_48mhz_xo); + + return 0; +} + +static int qcom_iris_remove(struct platform_device *pdev) +{ + struct qcom_wcnss *wcnss = dev_get_drvdata(pdev->dev.parent); + + qcom_wcnss_assign_iris(wcnss, NULL, false); + + return 0; +} + +static const struct of_device_id iris_of_match[] = { + { .compatible = "qcom,wcn3620", .data = &wcn3620_data }, + { .compatible = "qcom,wcn3660", .data = &wcn3660_data }, + { .compatible = "qcom,wcn3680", .data = &wcn3680_data }, + {} +}; + +static struct platform_driver wcnss_driver = { + .probe = qcom_iris_probe, + .remove = qcom_iris_remove, + .driver = { + .name = "qcom-iris", + .of_match_table = iris_of_match, + }, +}; + +module_platform_driver(wcnss_driver); diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index 916af5096f57..9ef9f47b28e2 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -88,8 +88,42 @@ static ssize_t rproc_state_read(struct file *filp, char __user *userbuf, return simple_read_from_buffer(userbuf, count, ppos, buf, i); } +static ssize_t rproc_state_write(struct file *filp, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct rproc *rproc = filp->private_data; + char buf[10]; + int ret; + + if (count > sizeof(buf) || count <= 0) + return -EINVAL; + + ret = copy_from_user(buf, userbuf, count); + if (ret) + return -EFAULT; + + if (buf[count - 1] == '\n') + buf[count - 1] = '\0'; + + if (!strncmp(buf, "start", count)) { + ret = rproc_boot(rproc); + if (ret) { + dev_err(&rproc->dev, "Boot failed: %d\n", ret); + return ret; + } + } else if (!strncmp(buf, "stop", count)) { + rproc_shutdown(rproc); + } else { + dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); + return -EINVAL; + } + + return count; +} + static const struct file_operations rproc_state_ops = { .read = rproc_state_read, + .write = rproc_state_write, .open = simple_open, .llseek = generic_file_llseek, }; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 9f244d633f3b..35fa84660dee 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -83,6 +83,15 @@ config QCOM_WCNSS_CTRL config MSM_BUS_SCALING bool "Bus scaling driver" default n + +config QTI_LNX_GPS_PROXY + tristate "User mode QTI_LNX_GPS_PROXY device driver support" + help + This supports user mode QTI_LNX_GPS_PROXY + + Note that this application programming interface is EXPERIMENTAL + and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes. + help This option enables bus scaling on MSM devices. Bus scaling allows devices to request the clocks be set to rates sufficient @@ -108,8 +117,3 @@ config BUS_TOPOLOGY_ADHOC directionality of connections by explicitly listing device connections thus avoiding illegal routes. -config QCOM_IPCRTR_STUB - tristate "Qualcomm IPCRTR stub driver" - depends on QCOM_SMD - help - Stub driver to bring the IPCRTR channel up. diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 49e0f7c3aeda..56da0a585cfd 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -12,4 +12,4 @@ obj-$(CONFIG_MFD_QCOM_SMD_RPM) += rpm_log.o obj-$(CONFIG_MSM_BUS_SCALING) += msm_bus/ obj-$(CONFIG_BUS_TOPOLOGY_ADHOC) += msm_bus/ -obj-$(CONFIG_QCOM_IPCRTR_STUB) += ipcrtr_stub.o +obj-$(CONFIG_QTI_LNX_GPS_PROXY) += gps_proxy.o diff --git a/drivers/soc/qcom/gps_proxy.c b/drivers/soc/qcom/gps_proxy.c new file mode 100644 index 000000000000..681296243176 --- /dev/null +++ b/drivers/soc/qcom/gps_proxy.c @@ -0,0 +1,304 @@ +/* Copyright (c) 2016, The Linux Foundation. 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 and + * only 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module information */ +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL v2"); + +#define LOC_SERVICE_SVC_ID 0x00000010 +#define LOC_SERVICE_V1 2 +#define LOC_SERVICE_INS_ID 0 + +#define DRIVER_VERSION "v1.0" +#define DRIVER_DESC "GPS TTY driver" +#define MODULE_NAME "gps_proxy" +#define TTY_DRIVER_NAME "gps_serial" +#define TTY_DEV_NAME "ttyGPS" +#define MAX_TTY_BUFFER_SZ 0x10000 +#define GPS_TTY_MAJOR 100 /* experimental range */ +#define DEVICE_NAME "gps_proxy_ch" +#define CLASS_NAME "gps_proxy_class" + +static struct tty_driver *gps_proxy_tty_driver; +static struct tty_port gps_proxy_tty_port; +static bool g_port_open = false; +static struct semaphore g_port_sem; +static int gps_proxy_ch_driver_major = 0; +static struct class* gps_proxy_ch_class = 0; +static struct device* gps_proxy_ch_dev = 0; + +static void serial_port_shutdown(struct tty_port *tport) +{ +} + +static int serial_port_activate(struct tty_port *tport, struct tty_struct *tty) +{ + return 0; +} + +static struct tty_port_operations serial_port_ops = { + .activate = serial_port_activate, + .shutdown = serial_port_shutdown +}; + +static int gps_proxy_open(struct tty_struct *tty, struct file *file) +{ + int rc; + rc = tty_port_open(&gps_proxy_tty_port, tty, file); + g_port_open = true; + up(&g_port_sem); + return rc; +} + +static void gps_proxy_close(struct tty_struct *tty, struct file *file) +{ + tty_port_close(tty->port, tty, file); + g_port_open = false; + down(&g_port_sem); +} + +static void gps_proxy_tty_hangup(struct tty_struct *tty) +{ + tty_port_hangup(tty->port); +} + +static int gps_proxy_write(struct tty_struct *tty, + const unsigned char *buffer, int count) +{ + return count; +} + +static int gps_proxy_write_room(struct tty_struct *tty) +{ + return MAX_TTY_BUFFER_SZ; +} + +static int gps_proxy_tty_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + + +static int gps_proxy_tiocmget(struct tty_struct *tty) +{ + return 0; +} + +static int gps_proxy_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + return 0; +} + +static int gps_proxy_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static struct tty_operations serial_ops = { + .open = gps_proxy_open, + .close = gps_proxy_close, + .hangup = gps_proxy_tty_hangup, + .write = gps_proxy_write, + .write_room = gps_proxy_write_room, + .chars_in_buffer = gps_proxy_tty_chars_in_buffer, + .tiocmget = gps_proxy_tiocmget, + .tiocmset = gps_proxy_tiocmset, + .ioctl = gps_proxy_ioctl, +}; + +int gps_proxy_ch_driver_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +int gps_proxy_ch_driver_close(struct inode *inode, struct file *filp) +{ + return 0; +} + +long gps_proxy_chdev_ioctl(struct file *filp, unsigned int opt, unsigned long arg) +{ + int rc = 0; + struct gps_proxy_data buff; + switch (opt) + { + case QGPS_REGISTER_HANDLE: + /* DOWN is necessary to make client wait till port is open */ + down(&g_port_sem); + /* UP to semaphore is necessary here for close or + next register handle (for parity) */ + up(&g_port_sem); + rc = 0; + break; + case QGPS_SEND_NMEA: + pr_debug(KERN_INFO "Received string: %s\n", + ((struct gps_proxy_data*)arg)->nmea_string); + rc = access_ok(struct gps_proxy_data, (struct gps_proxy_data*)arg, + sizeof(struct gps_proxy_data)); + if (!rc) { + pr_err(KERN_ERR "Invalid argument was received\n"); + return rc; + } + rc = copy_from_user((void*)&buff, (void*)arg, sizeof(buff)); + if (rc) { + pr_err(KERN_ERR "Number of bytes that \ + couldn't be copied: %d", rc); + return -EFAULT; + } + if (buff.nmea_length < QMI_LOC_NMEA_STRING_MAX_LENGTH_V02 + 1) { + pr_debug(KERN_INFO "Received string: %s\n", + buff.nmea_string); + rc = tty_insert_flip_string(&gps_proxy_tty_port, + buff.nmea_string, + strnlen(buff.nmea_string, + QMI_LOC_NMEA_STRING_MAX_LENGTH_V02) + 1); + if (rc < 0) { + pr_err(KERN_ERR "Error flipping string"); + return rc; + } + tty_flip_buffer_push(&gps_proxy_tty_port); + } + else { + pr_err(KERN_ERR "Illegal message size"); + rc = -EFAULT; + } + break; + case QGPS_IS_ACTIVE: + if (g_port_open) + rc = 0; + else + rc = -EFAULT; + break; + default: + rc = -EFAULT; + break; + } + return rc; +} + +struct file_operations gps_proxy_ch_driver_ops = { + open: gps_proxy_ch_driver_open, + unlocked_ioctl: gps_proxy_chdev_ioctl, + release: gps_proxy_ch_driver_close +}; + +static int __init gps_proxy_init(void) +{ + int rc; + struct device *ttydev; + + sema_init(&g_port_sem,0); + + gps_proxy_ch_driver_major = register_chrdev(0, "gps_proxy_ch_dev", + &gps_proxy_ch_driver_ops); + if (gps_proxy_ch_driver_major < 0) { + pr_err(KERN_ERR "Failed to register char device\n"); + return -EFAULT; + } + else { + pr_debug(KERN_INFO "char device registered with major %d\n", + gps_proxy_ch_driver_major); + } + + /* Register the device class */ + gps_proxy_ch_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(gps_proxy_ch_class)){ + unregister_chrdev(gps_proxy_ch_driver_major, DEVICE_NAME); + pr_debug(KERN_ALERT "Failed to register device class\n"); + return -EFAULT; + } + pr_debug(KERN_INFO "EBBChar: device class registered correctly\n"); + + /* Register the device driver */ + gps_proxy_ch_dev = device_create(gps_proxy_ch_class, NULL, + MKDEV(gps_proxy_ch_driver_major, 0), NULL, DEVICE_NAME); + if (IS_ERR(gps_proxy_ch_dev)){ + class_destroy(gps_proxy_ch_class); + unregister_chrdev(gps_proxy_ch_driver_major, DEVICE_NAME); + pr_debug(KERN_ALERT "Failed to create the device\n"); + return -EFAULT; + } + + /* allocate the tty driver */ + gps_proxy_tty_driver = alloc_tty_driver(1); + if (!gps_proxy_tty_driver) + return -ENOMEM; + + tty_port_init(&gps_proxy_tty_port); + gps_proxy_tty_port.ops = &serial_port_ops; + + /* initialize the tty driver */ + gps_proxy_tty_driver->driver_name = TTY_DRIVER_NAME; + gps_proxy_tty_driver->name = TTY_DEV_NAME; + gps_proxy_tty_driver->major = GPS_TTY_MAJOR; + gps_proxy_tty_driver->minor_start = 0; + gps_proxy_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + gps_proxy_tty_driver->subtype = SERIAL_TYPE_NORMAL; + gps_proxy_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + gps_proxy_tty_driver->init_termios = tty_std_termios; + + tty_set_operations(gps_proxy_tty_driver, &serial_ops); + + /* register the tty driver */ + rc = tty_register_driver(gps_proxy_tty_driver); + if (rc) { + pr_err("Failed to register gps_proxy tty driver\n"); + put_tty_driver(gps_proxy_tty_driver); + return rc; + } + + ttydev = tty_port_register_device(&gps_proxy_tty_port, gps_proxy_tty_driver, 0, 0); + if (IS_ERR(ttydev)) { + rc = PTR_ERR(ttydev); + pr_err("Failed to register gps_proxy tty proxy\n"); + return rc; + } + + pr_debug(KERN_INFO DRIVER_DESC " \n" DRIVER_VERSION); + return rc; +} + +static void __exit gps_proxy_exit(void) +{ + tty_unregister_device(gps_proxy_tty_driver, 0); + tty_unregister_driver(gps_proxy_tty_driver); + unregister_chrdev(gps_proxy_ch_driver_major, "gps_proxy_ch_dev"); + device_destroy(gps_proxy_ch_class, MKDEV(gps_proxy_ch_driver_major, 0)); + class_unregister(gps_proxy_ch_class); + class_destroy(gps_proxy_ch_class); +} + +late_initcall(gps_proxy_init); +module_exit(gps_proxy_exit); diff --git a/drivers/soc/qcom/ipcrtr_stub.c b/drivers/soc/qcom/ipcrtr_stub.c deleted file mode 100644 index 6f8d724f4b5e..000000000000 --- a/drivers/soc/qcom/ipcrtr_stub.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2014, Sony Mobile Communications AB. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only 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 -#include -#include -#include -#include -#include -#include -#include - -#include - -static int ipcrtr_stub_callback(struct qcom_smd_device *qsdev, - const void *data, - size_t count) -{ - print_hex_dump(KERN_DEBUG, "IPCRTR <<<: ", DUMP_PREFIX_OFFSET, 16, 1, data, count, true); - - return 0; -} - -static int ipcrtr_stub_probe(struct qcom_smd_device *sdev) -{ - dev_err(&sdev->dev, "ipcrtr initialized\n"); - - return 0; -} - -static const struct of_device_id ipcrtr_stub_of_match[] = { - { .compatible = "qcom,ipcrtr" }, - {} -}; -MODULE_DEVICE_TABLE(of, ipcrtr_stub_of_match); - -static struct qcom_smd_driver ipcrtr_stub_driver = { - .probe = ipcrtr_stub_probe, - .callback = ipcrtr_stub_callback, - .driver = { - .name = "ipcrtr_stub", - .owner = THIS_MODULE, - .of_match_table = ipcrtr_stub_of_match, - }, -}; - -module_qcom_smd_driver(ipcrtr_stub_driver); - -MODULE_AUTHOR("Bjorn Andersson "); -MODULE_DESCRIPTION("Qualcomm IPCRTR stub driver"); -MODULE_LICENSE("GPLv2"); - diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index af3152c73399..abb32001ff3a 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c @@ -33,6 +33,7 @@ */ struct qcom_smd_rpm { struct qcom_smd_channel *rpm_channel; + struct device *dev; struct completion ack; struct mutex lock; @@ -149,14 +150,14 @@ out: } EXPORT_SYMBOL(qcom_rpm_smd_write); -static int qcom_smd_rpm_callback(struct qcom_smd_device *qsdev, +static int qcom_smd_rpm_callback(struct qcom_smd_channel *channel, const void *data, size_t count) { const struct qcom_rpm_header *hdr = data; size_t hdr_length = le32_to_cpu(hdr->length); const struct qcom_rpm_message *msg; - struct qcom_smd_rpm *rpm = dev_get_drvdata(&qsdev->dev); + struct qcom_smd_rpm *rpm = qcom_smd_get_drvdata(channel); const u8 *buf = data + sizeof(struct qcom_rpm_header); const u8 *end = buf + hdr_length; char msgbuf[32]; @@ -165,7 +166,7 @@ static int qcom_smd_rpm_callback(struct qcom_smd_device *qsdev, if (le32_to_cpu(hdr->service_type) != RPM_SERVICE_TYPE_REQUEST || hdr_length < sizeof(struct qcom_rpm_message)) { - dev_err(&qsdev->dev, "invalid request\n"); + dev_err(rpm->dev, "invalid request\n"); return 0; } @@ -206,7 +207,9 @@ static int qcom_smd_rpm_probe(struct qcom_smd_device *sdev) mutex_init(&rpm->lock); init_completion(&rpm->ack); + rpm->dev = &sdev->dev; rpm->rpm_channel = sdev->channel; + qcom_smd_set_drvdata(sdev->channel, rpm); dev_set_drvdata(&sdev->dev, rpm); diff --git a/drivers/soc/qcom/smd.c b/drivers/soc/qcom/smd.c index 1ef645a78540..0b08b9dc8005 100644 --- a/drivers/soc/qcom/smd.c +++ b/drivers/soc/qcom/smd.c @@ -123,7 +123,7 @@ struct qcom_smd_edge { int ipc_bit; struct list_head channels; - rwlock_t channels_lock; + spinlock_t channels_lock; DECLARE_BITMAP(allocated[SMD_ALLOC_TBL_COUNT], SMD_ALLOC_TBL_SIZE); @@ -135,6 +135,70 @@ struct qcom_smd_edge { struct work_struct state_work; }; +/* + * SMD channel states. + */ +enum smd_channel_state { + SMD_CHANNEL_CLOSED, + SMD_CHANNEL_OPENING, + SMD_CHANNEL_OPENED, + SMD_CHANNEL_FLUSHING, + SMD_CHANNEL_CLOSING, + SMD_CHANNEL_RESET, + SMD_CHANNEL_RESET_OPENING +}; + +/** + * struct qcom_smd_channel - smd channel struct + * @edge: qcom_smd_edge this channel is living on + * @qsdev: reference to a associated smd client device + * @name: name of the channel + * @state: local state of the channel + * @remote_state: remote state of the channel + * @info: byte aligned outgoing/incoming channel info + * @info_word: word aligned outgoing/incoming channel info + * @tx_lock: lock to make writes to the channel mutually exclusive + * @fblockread_event: wakeup event tied to tx fBLOCKREADINTR + * @tx_fifo: pointer to the outgoing ring buffer + * @rx_fifo: pointer to the incoming ring buffer + * @fifo_size: size of each ring buffer + * @bounce_buffer: bounce buffer for reading wrapped packets + * @cb: callback function registered for this channel + * @recv_lock: guard for rx info modifications and cb pointer + * @pkt_size: size of the currently handled packet + * @list: lite entry for @channels in qcom_smd_edge + */ +struct qcom_smd_channel { + struct qcom_smd_edge *edge; + + struct qcom_smd_device *qsdev; + + char *name; + enum smd_channel_state state; + enum smd_channel_state remote_state; + + struct smd_channel_info_pair *info; + struct smd_channel_info_word_pair *info_word; + + struct mutex tx_lock; + wait_queue_head_t fblockread_event; + + void *tx_fifo; + void *rx_fifo; + int fifo_size; + + void *bounce_buffer; + qcom_smd_cb_t cb; + + spinlock_t recv_lock; + + int pkt_size; + + void *drvdata; + + struct list_head list; + struct list_head dev_list; +}; /** * struct qcom_smd - smd struct @@ -453,7 +517,6 @@ static void qcom_smd_channel_advance(struct qcom_smd_channel *channel, */ static int qcom_smd_channel_recv_single(struct qcom_smd_channel *channel) { - struct qcom_smd_device *qsdev = channel->qsdev; unsigned tail; size_t len; void *ptr; @@ -473,7 +536,7 @@ static int qcom_smd_channel_recv_single(struct qcom_smd_channel *channel) len = channel->pkt_size; } - ret = channel->cb(qsdev, ptr, len); + ret = channel->cb(channel, ptr, len); if (ret < 0) return ret; @@ -557,18 +620,19 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data) struct qcom_smd_edge *edge = data; struct qcom_smd_channel *channel; unsigned available; - bool kick_worker = false; + bool kick_scanner = false; + bool kick_state = false; /* * Handle state changes or data on each of the channels on this edge */ - read_lock(&edge->channels_lock); + spin_lock(&edge->channels_lock); list_for_each_entry(channel, &edge->channels, list) { spin_lock(&channel->recv_lock); - kick_worker |= qcom_smd_channel_intr(channel); + kick_state |= qcom_smd_channel_intr(channel); spin_unlock(&channel->recv_lock); } - read_unlock(&edge->channels_lock); + spin_unlock(&edge->channels_lock); /* * Creating a new channel requires allocating an smem entry, so we only @@ -578,11 +642,13 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data) available = qcom_smem_get_free_space(edge->remote_pid); if (available != edge->smem_available) { edge->smem_available = available; - kick_worker = true; + kick_scanner = true; } - if (kick_worker) + if (kick_scanner) schedule_work(&edge->scan_work); + if (kick_state) + schedule_work(&edge->state_work); return IRQ_HANDLED; } @@ -667,7 +733,7 @@ int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len) { __le32 hdr[5] = { cpu_to_le32(len), }; int tlen = sizeof(hdr) + len; - int ret, length; + int ret; /* Word aligned channels only accept word size aligned data */ if (channel->info_word && len % 4) @@ -677,13 +743,10 @@ int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len) if (tlen >= channel->fifo_size) return -EINVAL; - length = qcom_smd_get_tx_avail(channel); - ret = mutex_lock_interruptible(&channel->tx_lock); if (ret) return ret; - length = qcom_smd_get_tx_avail(channel); while (qcom_smd_get_tx_avail(channel) < tlen) { if (channel->state != SMD_CHANNEL_OPENED) { ret = -EPIPE; @@ -703,7 +766,6 @@ int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len) SET_TX_CHANNEL_FLAG(channel, fTAIL, 0); - length = qcom_smd_get_tx_avail(channel); qcom_smd_write_fifo(channel, hdr, sizeof(hdr)); qcom_smd_write_fifo(channel, data, len); @@ -975,6 +1037,18 @@ int qcom_smd_driver_register(struct qcom_smd_driver *qsdrv) } EXPORT_SYMBOL(qcom_smd_driver_register); +void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel) +{ + return channel->drvdata; +} +EXPORT_SYMBOL(qcom_smd_get_drvdata); + +void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data) +{ + channel->drvdata = data; +} +EXPORT_SYMBOL(qcom_smd_set_drvdata); + /** * qcom_smd_driver_unregister - unregister a smd driver * @qsdrv: qcom_smd_driver struct @@ -990,9 +1064,10 @@ qcom_smd_find_channel(struct qcom_smd_edge *edge, const char *name) { struct qcom_smd_channel *channel; struct qcom_smd_channel *ret = NULL; + unsigned long flags; unsigned state; - read_lock(&edge->channels_lock); + spin_lock_irqsave(&edge->channels_lock, flags); list_for_each_entry(channel, &edge->channels, list) { if (strcmp(channel->name, name)) continue; @@ -1005,7 +1080,7 @@ qcom_smd_find_channel(struct qcom_smd_edge *edge, const char *name) ret = channel; break; } - read_unlock(&edge->channels_lock); + spin_unlock_irqrestore(&edge->channels_lock, flags); return ret; } @@ -1019,12 +1094,13 @@ qcom_smd_find_channel(struct qcom_smd_edge *edge, const char *name) * Returns a channel handle on success, or -EPROBE_DEFER if the channel isn't * ready. */ -struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_device *sdev, +struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *parent, const char *name, qcom_smd_cb_t cb) { struct qcom_smd_channel *channel; - struct qcom_smd_edge *edge = sdev->channel->edge; + struct qcom_smd_device *sdev = parent->qsdev; + struct qcom_smd_edge *edge = parent->edge; int ret; /* Wait up to HZ for the channel to appear */ @@ -1116,7 +1192,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed /* The channel consist of a rx and tx fifo of equal size */ fifo_size /= 2; - dev_err(smd->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n", + dev_dbg(smd->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n", name, info_size, fifo_size); channel->tx_fifo = fifo_base; @@ -1185,9 +1261,9 @@ static void qcom_channel_scan_worker(struct work_struct *work) if (IS_ERR(channel)) continue; - write_lock_irqsave(&edge->channels_lock, flags); + spin_lock_irqsave(&edge->channels_lock, flags); list_add(&channel->list, &edge->channels); - write_unlock_irqrestore(&edge->channels_lock, flags); + spin_unlock_irqrestore(&edge->channels_lock, flags); dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name); set_bit(i, edge->allocated[tbl]); @@ -1214,12 +1290,13 @@ static void qcom_channel_state_worker(struct work_struct *work) struct qcom_smd_edge, state_work); unsigned remote_state; + unsigned long flags; /* * Register a device for any closed channel where the remote processor * is showing interest in opening the channel. */ - read_lock(&edge->channels_lock); + spin_lock_irqsave(&edge->channels_lock, flags); list_for_each_entry(channel, &edge->channels, list) { if (channel->state != SMD_CHANNEL_CLOSED) continue; @@ -1229,9 +1306,9 @@ static void qcom_channel_state_worker(struct work_struct *work) remote_state != SMD_CHANNEL_OPENED) continue; - read_unlock(&edge->channels_lock); + spin_unlock_irqrestore(&edge->channels_lock, flags); qcom_smd_create_device(channel); - read_lock(&edge->channels_lock); + spin_lock_irqsave(&edge->channels_lock, flags); } /* @@ -1248,11 +1325,11 @@ static void qcom_channel_state_worker(struct work_struct *work) remote_state == SMD_CHANNEL_OPENED) continue; - read_unlock(&edge->channels_lock); + spin_unlock_irqrestore(&edge->channels_lock, flags); qcom_smd_destroy_device(channel); - read_lock(&edge->channels_lock); + spin_lock_irqsave(&edge->channels_lock, flags); } - read_unlock(&edge->channels_lock); + spin_unlock_irqrestore(&edge->channels_lock, flags); } /* @@ -1268,7 +1345,7 @@ static int qcom_smd_parse_edge(struct device *dev, int ret; INIT_LIST_HEAD(&edge->channels); - rwlock_init(&edge->channels_lock); + spin_lock_init(&edge->channels_lock); INIT_WORK(&edge->scan_work, qcom_channel_scan_worker); INIT_WORK(&edge->state_work, qcom_channel_state_worker); diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 5290bb02c547..8b9bfe912f86 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -690,8 +690,7 @@ static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev, smem->regions[i].aux_base = (u32)r.start; smem->regions[i].size = resource_size(&r); - smem->regions[i].virt_base = devm_ioremap_nocache(dev, r.start, - resource_size(&r)); + smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, resource_size(&r)); if (!smem->regions[i].virt_base) return -ENOMEM; diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c index 7a986f881d5c..00538a1a9617 100644 --- a/drivers/soc/qcom/wcnss_ctrl.c +++ b/drivers/soc/qcom/wcnss_ctrl.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016, Linaro Ltd. * Copyright (c) 2015, Sony Mobile Communications Inc. * * This program is free software; you can redistribute it and/or modify @@ -14,8 +15,16 @@ #include #include #include +#include +#include +#include +#include #define WCNSS_REQUEST_TIMEOUT (5 * HZ) +#define WCNSS_CBC_TIMEOUT (10 * HZ) + +#define WCNSS_ACK_DONE_BOOTING 1 +#define WCNSS_ACK_COLD_BOOTING 2 #define NV_FRAGMENT_SIZE 3072 #define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin" @@ -25,17 +34,19 @@ * @dev: device handle * @channel: SMD channel handle * @ack: completion for outstanding requests + * @cbc: completion for cbc complete indication * @ack_status: status of the outstanding request - * @download_nv_work: worker for uploading nv binary + * @probe_work: worker for uploading nv binary */ struct wcnss_ctrl { struct device *dev; struct qcom_smd_channel *channel; struct completion ack; + struct completion cbc; int ack_status; - struct work_struct download_nv_work; + struct work_struct probe_work; }; /* message types */ @@ -48,6 +59,11 @@ enum { WCNSS_UPLOAD_CAL_RESP, WCNSS_DOWNLOAD_CAL_REQ, WCNSS_DOWNLOAD_CAL_RESP, + WCNSS_VBAT_LEVEL_IND, + WCNSS_BUILD_VERSION_REQ, + WCNSS_BUILD_VERSION_RESP, + WCNSS_PM_CONFIG_REQ, + WCNSS_CBC_COMPLETE_IND, }; /** @@ -100,17 +116,17 @@ struct wcnss_download_nv_resp { /** * wcnss_ctrl_smd_callback() - handler from SMD responses - * @qsdev: smd device handle + * @channel: smd channel handle * @data: pointer to the incoming data packet * @count: size of the incoming data packet * * Handles any incoming packets from the remote WCNSS_CTRL service. */ -static int wcnss_ctrl_smd_callback(struct qcom_smd_device *qsdev, +static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel, const void *data, size_t count) { - struct wcnss_ctrl *wcnss = dev_get_drvdata(&qsdev->dev); + struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(channel); const struct wcnss_download_nv_resp *nvresp; const struct wcnss_version_resp *version; const struct wcnss_msg_hdr *hdr = data; @@ -128,7 +144,7 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_device *qsdev, version->major, version->minor, version->version, version->revision); - schedule_work(&wcnss->download_nv_work); + complete(&wcnss->ack); break; case WCNSS_DOWNLOAD_NV_RESP: if (count != sizeof(*nvresp)) { @@ -141,6 +157,10 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_device *qsdev, wcnss->ack_status = nvresp->status; complete(&wcnss->ack); break; + case WCNSS_CBC_COMPLETE_IND: + dev_dbg(wcnss->dev, "cold boot complete\n"); + complete(&wcnss->cbc); + break; default: dev_info(wcnss->dev, "unknown message type %d\n", hdr->type); break; @@ -156,20 +176,32 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_device *qsdev, static int wcnss_request_version(struct wcnss_ctrl *wcnss) { struct wcnss_msg_hdr msg; + int ret; msg.type = WCNSS_VERSION_REQ; msg.len = sizeof(msg); + ret = qcom_smd_send(wcnss->channel, &msg, sizeof(msg)); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_CBC_TIMEOUT); + if (!ret) { + dev_err(wcnss->dev, "timeout waiting for version response\n"); + return -ETIMEDOUT; + } - return qcom_smd_send(wcnss->channel, &msg, sizeof(msg)); + return 0; } /** * wcnss_download_nv() - send nv binary to WCNSS - * @work: work struct to acquire wcnss context + * @wcnss: wcnss_ctrl state handle + * @expect_cbc: indicator to caller that an cbc event is expected + * + * Returns 0 on success. Negative errno on failure. */ -static void wcnss_download_nv(struct work_struct *work) +static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc) { - struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, download_nv_work); struct wcnss_download_nv_req *req; const struct firmware *fw; const void *data; @@ -178,10 +210,10 @@ static void wcnss_download_nv(struct work_struct *work) req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL); if (!req) - return; + return -ENOMEM; ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev); - if (ret) { + if (ret < 0) { dev_err(wcnss->dev, "Failed to load nv file %s: %d\n", NVBIN_FILE, ret); goto free_req; @@ -207,7 +239,7 @@ static void wcnss_download_nv(struct work_struct *work) memcpy(req->fragment, data, req->frag_size); ret = qcom_smd_send(wcnss->channel, req, req->hdr.len); - if (ret) { + if (ret < 0) { dev_err(wcnss->dev, "failed to send smd packet\n"); goto release_fw; } @@ -220,16 +252,63 @@ static void wcnss_download_nv(struct work_struct *work) } while (left > 0); ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT); - if (!ret) + if (!ret) { dev_err(wcnss->dev, "timeout waiting for nv upload ack\n"); - else if (wcnss->ack_status != 1) - dev_err(wcnss->dev, "nv upload response failed err: %d\n", - wcnss->ack_status); + ret = -ETIMEDOUT; + } else { + *expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING; + ret = 0; + } release_fw: release_firmware(fw); free_req: kfree(req); + + return ret; +} + +int wcnss_ctrl_done_loading_nv; +EXPORT_SYMBOL(wcnss_ctrl_done_loading_nv); + +/** + * qcom_wcnss_open_channel() - open additional SMD channel to WCNSS + * @wcnss: wcnss handle, retrieved from drvdata + * @name: SMD channel name + * @cb: callback to handle incoming data on the channel + */ +struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb) +{ + struct wcnss_ctrl *_wcnss = wcnss; + + return qcom_smd_open_channel(_wcnss->channel, name, cb); +} +EXPORT_SYMBOL(qcom_wcnss_open_channel); + +static void wcnss_async_probe(struct work_struct *work) +{ + struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work); + bool expect_cbc; + int ret; + + ret = wcnss_request_version(wcnss); + if (ret < 0) + return; + + ret = wcnss_download_nv(wcnss, &expect_cbc); + if (ret < 0) + return; + + /* Wait for pending cold boot completion if indicated by the nv downloader */ + if (expect_cbc) { + ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT); + if (!ret) + dev_err(wcnss->dev, "expected cold boot completion\n"); + } + + wcnss_ctrl_done_loading_nv = 1; + + of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev); } static int wcnss_ctrl_probe(struct qcom_smd_device *sdev) @@ -244,25 +323,40 @@ static int wcnss_ctrl_probe(struct qcom_smd_device *sdev) wcnss->channel = sdev->channel; init_completion(&wcnss->ack); - INIT_WORK(&wcnss->download_nv_work, wcnss_download_nv); + init_completion(&wcnss->cbc); + INIT_WORK(&wcnss->probe_work, wcnss_async_probe); + qcom_smd_set_drvdata(sdev->channel, wcnss); dev_set_drvdata(&sdev->dev, wcnss); - return wcnss_request_version(wcnss); + qcom_smd_set_drvdata(sdev->channel, wcnss); + + schedule_work(&wcnss->probe_work); + + return 0; +} + +static void wcnss_ctrl_remove(struct qcom_smd_device *sdev) +{ + struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(sdev->channel); + + cancel_work_sync(&wcnss->probe_work); + of_platform_depopulate(&sdev->dev); } -static const struct qcom_smd_id wcnss_ctrl_smd_match[] = { - { .name = "WCNSS_CTRL" }, +static const struct of_device_id wcnss_ctrl_of_match[] = { + { .compatible = "qcom,wcnss", }, {} }; static struct qcom_smd_driver wcnss_ctrl_driver = { .probe = wcnss_ctrl_probe, + .remove = wcnss_ctrl_remove, .callback = wcnss_ctrl_smd_callback, - .smd_match_table = wcnss_ctrl_smd_match, .driver = { .name = "qcom_wcnss_ctrl", .owner = THIS_MODULE, + .of_match_table = wcnss_ctrl_of_match, }, }; diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index 810a7fae3479..68f95acf7971 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -142,6 +142,7 @@ struct spi_qup { struct spi_transfer *xfer; struct completion done; + struct completion dma_tx_done; int error; int w_size; /* bytes per SPI word */ int n_words; @@ -276,16 +277,16 @@ static void spi_qup_fifo_write(struct spi_qup *controller, static void spi_qup_dma_done(void *data) { - struct spi_qup *qup = data; + struct completion *done = data; - complete(&qup->done); + complete(done); } static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer, enum dma_transfer_direction dir, - dma_async_tx_callback callback) + dma_async_tx_callback callback, + void *data) { - struct spi_qup *qup = spi_master_get_devdata(master); unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE; struct dma_async_tx_descriptor *desc; struct scatterlist *sgl; @@ -308,7 +309,7 @@ static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer, return -EINVAL; desc->callback = callback; - desc->callback_param = qup; + desc->callback_param = data; cookie = dmaengine_submit(desc); @@ -324,18 +325,29 @@ static void spi_qup_dma_terminate(struct spi_master *master, dmaengine_terminate_all(master->dma_rx); } -static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer) +static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer, +unsigned long timeout) { + struct spi_qup *qup = spi_master_get_devdata(master); dma_async_tx_callback rx_done = NULL, tx_done = NULL; int ret; + /* before issuing the descriptors, set the QUP to run */ + ret = spi_qup_set_state(qup, QUP_STATE_RUN); + if (ret) { + dev_warn(qup->dev, "cannot set RUN state\n"); + return ret; + } + if (xfer->rx_buf) rx_done = spi_qup_dma_done; - else if (xfer->tx_buf) + + if (xfer->tx_buf) tx_done = spi_qup_dma_done; if (xfer->rx_buf) { - ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done); + ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done, + &qup->done); if (ret) return ret; @@ -343,17 +355,25 @@ static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer) } if (xfer->tx_buf) { - ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done); + ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done, + &qup->dma_tx_done); if (ret) return ret; dma_async_issue_pending(master->dma_tx); } - return 0; + if (xfer->rx_buf && !wait_for_completion_timeout(&qup->done, timeout)) + return -ETIMEDOUT; + + if (xfer->tx_buf && !wait_for_completion_timeout(&qup->dma_tx_done, timeout)) + ret = -ETIMEDOUT; + + return ret; } -static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer) +static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer, + unsigned long timeout) { struct spi_qup *qup = spi_master_get_devdata(master); int ret; @@ -372,6 +392,15 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer) spi_qup_fifo_write(qup, xfer); + ret = spi_qup_set_state(qup, QUP_STATE_RUN); + if (ret) { + dev_warn(qup->dev, "cannot set RUN state\n"); + return ret; + } + + if (!wait_for_completion_timeout(&qup->done, timeout)) + return -ETIMEDOUT; + return 0; } @@ -420,7 +449,6 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) dev_warn(controller->dev, "CLK_OVER_RUN\n"); if (spi_err & SPI_ERROR_CLK_UNDER_RUN) dev_warn(controller->dev, "CLK_UNDER_RUN\n"); - error = -EIO; } @@ -437,7 +465,8 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) controller->xfer = xfer; spin_unlock_irqrestore(&controller->lock, flags); - if (controller->rx_bytes == xfer->len || error) + if ((controller->rx_bytes == xfer->len && + (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) complete(&controller->done); return IRQ_HANDLED; @@ -625,6 +654,7 @@ static int spi_qup_transfer_one(struct spi_master *master, timeout = 100 * msecs_to_jiffies(timeout); reinit_completion(&controller->done); + reinit_completion(&controller->dma_tx_done); spin_lock_irqsave(&controller->lock, flags); controller->xfer = xfer; @@ -634,21 +664,13 @@ static int spi_qup_transfer_one(struct spi_master *master, spin_unlock_irqrestore(&controller->lock, flags); if (controller->use_dma) - ret = spi_qup_do_dma(master, xfer); + ret = spi_qup_do_dma(master, xfer, timeout); else - ret = spi_qup_do_pio(master, xfer); + ret = spi_qup_do_pio(master, xfer, timeout); if (ret) goto exit; - if (spi_qup_set_state(controller, QUP_STATE_RUN)) { - dev_warn(controller->dev, "cannot set EXECUTE state\n"); - goto exit; - } - - if (!wait_for_completion_timeout(&controller->done, timeout)) - ret = -ETIMEDOUT; - exit: spi_qup_set_state(controller, QUP_STATE_RESET); spin_lock_irqsave(&controller->lock, flags); @@ -672,15 +694,23 @@ static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi, qup->use_dma = 0; - if (xfer->rx_buf && (xfer->len % qup->in_blk_sz || - IS_ERR_OR_NULL(master->dma_rx) || - !IS_ALIGNED((size_t)xfer->rx_buf, dma_align))) - return false; + if (xfer->rx_buf) { + if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) || + IS_ERR_OR_NULL(master->dma_rx)) + return false; - if (xfer->tx_buf && (xfer->len % qup->out_blk_sz || - IS_ERR_OR_NULL(master->dma_tx) || - !IS_ALIGNED((size_t)xfer->tx_buf, dma_align))) - return false; + if (qup->qup_v1 && (xfer->len % qup->in_blk_sz)) + return false; + } + + if (xfer->tx_buf) { + if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) || + IS_ERR_OR_NULL(master->dma_tx)) + return false; + + if (qup->qup_v1 && (xfer->len % qup->out_blk_sz)) + return false; + } mode = spi_qup_get_mode(master, xfer); if (mode == QUP_IO_M_MODE_FIFO) @@ -848,6 +878,7 @@ static int spi_qup_probe(struct platform_device *pdev) spin_lock_init(&controller->lock); init_completion(&controller->done); + init_completion(&controller->dma_tx_done); iomode = readl_relaxed(base + QUP_IO_M_MODES); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index dcde955475dc..e1de4944e0ce 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -726,7 +726,7 @@ static void msm_handle_tx(struct uart_port *port) return; } - pio_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); + pio_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); dma_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); dma_min = 1; /* Always DMA */ diff --git a/include/dt-bindings/clock/qcom,rpmcc.h b/include/dt-bindings/clock/qcom,rpmcc.h index 81ae6c956eb8..5924cdb71336 100644 --- a/include/dt-bindings/clock/qcom,rpmcc.h +++ b/include/dt-bindings/clock/qcom,rpmcc.h @@ -14,126 +14,56 @@ #ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H #define _DT_BINDINGS_CLK_MSM_RPMCC_H -/* msm8916 */ -#define RPM_XO_CLK_SRC 0 -#define RPM_XO_A_CLK_SRC 1 -#define RPM_PCNOC_CLK 2 -#define RPM_PCNOC_A_CLK 3 -#define RPM_SNOC_CLK 4 -#define RPM_SNOC_A_CLK 5 -#define RPM_BIMC_CLK 6 -#define RPM_BIMC_A_CLK 7 -#define RPM_QDSS_CLK 8 -#define RPM_QDSS_A_CLK 9 -#define RPM_BB_CLK1 10 -#define RPM_BB_CLK1_A 11 -#define RPM_BB_CLK2 12 -#define RPM_BB_CLK2_A 13 -#define RPM_RF_CLK1 14 -#define RPM_RF_CLK1_A 15 -#define RPM_RF_CLK2 16 -#define RPM_RF_CLK2_A 17 -#define RPM_BB_CLK1_PIN 18 -#define RPM_BB_CLK1_A_PIN 19 -#define RPM_BB_CLK2_PIN 20 -#define RPM_BB_CLK2_A_PIN 21 -#define RPM_RF_CLK1_PIN 22 -#define RPM_RF_CLK1_A_PIN 23 -#define RPM_RF_CLK2_PIN 24 -#define RPM_RF_CLK2_A_PIN 25 - -/* msm8974 */ -#define RPM_CXO_CLK_SRC 0 -#define RPM_CXO_A_CLK_SRC 1 -#define RPM_PNOC_CLK 2 -#define RPM_PNOC_A_CLK 3 -#define RPM_SNOC_CLK 4 -#define RPM_SNOC_A_CLK 5 -#define RPM_BIMC_CLK 6 -#define RPM_BIMC_A_CLK 7 +/* apq8064 */ +#define RPM_PXO_CLK 0 +#define RPM_PXO_A_CLK 1 +#define RPM_CXO_CLK 2 +#define RPM_CXO_A_CLK 3 +#define RPM_APPS_FABRIC_CLK 4 +#define RPM_APPS_FABRIC_A_CLK 5 +#define RPM_CFPB_CLK 6 +#define RPM_CFPB_A_CLK 7 #define RPM_QDSS_CLK 8 #define RPM_QDSS_A_CLK 9 -#define RPM_CXO_D0 10 -#define RPM_CXO_D0_A 11 -#define RPM_CXO_D1 12 -#define RPM_CXO_D1_A 13 -#define RPM_CXO_A0 14 -#define RPM_CXO_A0_A 15 -#define RPM_CXO_A1 16 -#define RPM_CXO_A1_A 17 -#define RPM_CXO_A2 18 -#define RPM_CXO_A2_A 19 -#define RPM_CXO_D0_PIN 20 -#define RPM_CXO_D0_A_PIN 21 -#define RPM_CXO_A2_PIN 22 -#define RPM_CXO_A2_A_PIN 23 -#define RPM_CXO_A1_PIN 24 -#define RPM_CXO_A1_A_PIN 25 -#define RPM_DIFF_CLK 26 -#define RPM_DIFF_A_CLK 27 -#define RPM_CNOC_CLK 28 -#define RPM_CNOC_A_CLK 29 -#define RPM_MMSSNOC_AHB_CLK 30 -#define RPM_MMSSNOC_AHB_A_CLK 31 -#define RPM_OCMEMGX_CLK 32 -#define RPM_OCMEMGX_A_CLK 33 -#define RPM_GFX3D_CLK_SRC 34 -#define RPM_GFX3D_A_CLK_SRC 35 -#define RPM_DIV_CLK1 36 -#define RPM_DIV_A_CLK1 37 -#define RPM_DIV_CLK2 38 -#define RPM_DIV_A_CLK2 39 -#define RPM_CXO_D1_PIN 40 -#define RPM_CXO_D1_A_PIN 41 -#define RPM_CXO_A0_PIN 42 -#define RPM_CXO_A0_A_PIN 43 +#define RPM_DAYTONA_FABRIC_CLK 10 +#define RPM_DAYTONA_FABRIC_A_CLK 11 +#define RPM_EBI1_CLK 12 +#define RPM_EBI1_A_CLK 13 +#define RPM_MM_FABRIC_CLK 14 +#define RPM_MM_FABRIC_A_CLK 15 +#define RPM_MMFPB_CLK 16 +#define RPM_MMFPB_A_CLK 17 +#define RPM_SYS_FABRIC_CLK 18 +#define RPM_SYS_FABRIC_A_CLK 19 +#define RPM_SFPB_CLK 20 +#define RPM_SFPB_A_CLK 21 -/* apq8084 */ -#define RPM_XO_CLK_SRC 0 -#define RPM_XO_A_CLK_SRC 1 -#define RPM_PNOC_CLK 2 -#define RPM_PNOC_A_CLK 3 -#define RPM_SNOC_CLK 4 -#define RPM_SNOC_A_CLK 5 -#define RPM_BIMC_CLK 6 -#define RPM_BIMC_A_CLK 7 -#define RPM_QDSS_CLK 8 -#define RPM_QDSS_A_CLK 9 -#define RPM_BB_CLK1 10 -#define RPM_BB_CLK1_A 11 -#define RPM_BB_CLK2 12 -#define RPM_BB_CLK2_A 13 -#define RPM_RF_CLK1 14 -#define RPM_RF_CLK1_A 15 -#define RPM_RF_CLK2 16 -#define RPM_RF_CLK2_A 17 -#define RPM_BB_CLK1_PIN 18 -#define RPM_BB_CLK1_A_PIN 19 -#define RPM_BB_CLK2_PIN 20 -#define RPM_BB_CLK2_A_PIN 21 -#define RPM_RF_CLK1_PIN 22 -#define RPM_RF_CLK1_A_PIN 23 -#define RPM_RF_CLK2_PIN 24 -#define RPM_RF_CLK2_A_PIN 25 -#define RPM_DIFF_CLK1 26 -#define RPM_DIFF_CLK1_A 27 -#define RPM_CNOC_CLK 28 -#define RPM_CNOC_A_CLK 29 -#define RPM_MMSSNOC_AHB_CLK 30 -#define RPM_MMSSNOC_AHB_A_CLK 31 -#define RPM_OCMEMGX_CLK 32 -#define RPM_OCMEMGX_A_CLK 33 -#define RPM_GFX3D_CLK_SRC 34 -#define RPM_GFX3D_A_CLK_SRC 35 -#define RPM_DIV_CLK1 36 -#define RPM_DIV_CLK1_A 37 -#define RPM_DIV_CLK2 38 -#define RPM_DIV_CLK2_A 39 -#define RPM_DIV_CLK3 40 -#define RPM_DIV_CLK3_A 41 -#define RPM_RF_CLK3_PIN 44 -#define RPM_RF_CLK3_A_PIN 45 -#define RPM_RF_CLK3 46 -#define RPM_RF_CLK3_A 47 +/* msm8916 */ +#define RPM_SMD_XO_CLK_SRC 0 +#define RPM_SMD_XO_A_CLK_SRC 1 +#define RPM_SMD_PCNOC_CLK 2 +#define RPM_SMD_PCNOC_A_CLK 3 +#define RPM_SMD_SNOC_CLK 4 +#define RPM_SMD_SNOC_A_CLK 5 +#define RPM_SMD_BIMC_CLK 6 +#define RPM_SMD_BIMC_A_CLK 7 +#define RPM_SMD_QDSS_CLK 8 +#define RPM_SMD_QDSS_A_CLK 9 +#define RPM_SMD_BB_CLK1 10 +#define RPM_SMD_BB_CLK1_A 11 +#define RPM_SMD_BB_CLK2 12 +#define RPM_SMD_BB_CLK2_A 13 +#define RPM_SMD_RF_CLK1 14 +#define RPM_SMD_RF_CLK1_A 15 +#define RPM_SMD_RF_CLK2 16 +#define RPM_SMD_RF_CLK2_A 17 +#define RPM_SMD_BB_CLK1_PIN 18 +#define RPM_SMD_BB_CLK1_A_PIN 19 +#define RPM_SMD_BB_CLK2_PIN 20 +#define RPM_SMD_BB_CLK2_A_PIN 21 +#define RPM_SMD_RF_CLK1_PIN 22 +#define RPM_SMD_RF_CLK1_A_PIN 23 +#define RPM_SMD_RF_CLK2_PIN 24 +#define RPM_SMD_RF_CLK2_A_PIN 25 #endif diff --git a/include/dt-bindings/sound/msm8916-wcd.h b/include/dt-bindings/sound/msm8916-wcd.h new file mode 100644 index 000000000000..718db3c57e22 --- /dev/null +++ b/include/dt-bindings/sound/msm8916-wcd.h @@ -0,0 +1,7 @@ +#ifndef __DT_MSM8916_WCD_H +#define __DT_MSM8916_WCD_H + +#define MSM8916_WCD_PLAYBACK_DAI 0 +#define MSM8916_WCD_CAPTURE_DAI 1 + +#endif /* __DT_MSM8916_WCD_H */ diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 0b9f01b489cb..93819720e2a4 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -32,8 +32,7 @@ extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp); extern bool qcom_scm_pas_supported(u32 peripheral); -extern int qcom_scm_restart_proc(u32 pid, int restart, u32 *resp); -extern int qcom_scm_pas_init_image(struct device *dev, u32 peripheral, const void *metadata, size_t size); +extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size); extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size); extern int qcom_scm_pas_auth_and_reset(u32 peripheral); extern int qcom_scm_pas_shutdown(u32 peripheral); diff --git a/include/linux/soc/qcom/smd.h b/include/linux/soc/qcom/smd.h index 49764d94ef4f..cbb0f06c41b2 100644 --- a/include/linux/soc/qcom/smd.h +++ b/include/linux/soc/qcom/smd.h @@ -5,74 +5,8 @@ #include struct qcom_smd; +struct qcom_smd_channel; struct qcom_smd_lookup; -struct qcom_smd_device; - -typedef int (*qcom_smd_cb_t)(struct qcom_smd_device *, const void *, size_t); - -/* - * SMD channel states. - */ -enum smd_channel_state { - SMD_CHANNEL_CLOSED, - SMD_CHANNEL_OPENING, - SMD_CHANNEL_OPENED, - SMD_CHANNEL_FLUSHING, - SMD_CHANNEL_CLOSING, - SMD_CHANNEL_RESET, - SMD_CHANNEL_RESET_OPENING -}; - -/** - * struct qcom_smd_channel - smd channel struct - * @edge: qcom_smd_edge this channel is living on - * @qsdev: reference to a associated smd client device - * @name: name of the channel - * @state: local state of the channel - * @remote_state: remote state of the channel - * @info: byte aligned outgoing/incoming channel info - * @info_word: word aligned outgoing/incoming channel info - * @tx_lock: lock to make writes to the channel mutually exclusive - * @fblockread_event: wakeup event tied to tx fBLOCKREADINTR - * @tx_fifo: pointer to the outgoing ring buffer - * @rx_fifo: pointer to the incoming ring buffer - * @fifo_size: size of each ring buffer - * @bounce_buffer: bounce buffer for reading wrapped packets - * @cb: callback function registered for this channel - * @recv_lock: guard for rx info modifications and cb pointer - * @pkt_size: size of the currently handled packet - * @list: lite entry for @channels in qcom_smd_edge - */ - -struct qcom_smd_channel { - struct qcom_smd_edge *edge; - - struct qcom_smd_device *qsdev; - - char *name; - enum smd_channel_state state; - enum smd_channel_state remote_state; - - struct smd_channel_info_pair *info; - struct smd_channel_info_word_pair *info_word; - - struct mutex tx_lock; - wait_queue_head_t fblockread_event; - - void *tx_fifo; - void *rx_fifo; - int fifo_size; - - void *bounce_buffer; - qcom_smd_cb_t cb; - - spinlock_t recv_lock; - - int pkt_size; - - struct list_head list; - struct list_head dev_list; -}; /** * struct qcom_smd_id - struct used for matching a smd device @@ -92,6 +26,8 @@ struct qcom_smd_device { struct qcom_smd_channel *channel; }; +typedef int (*qcom_smd_cb_t)(struct qcom_smd_channel *, const void *, size_t); + /** * struct qcom_smd_driver - smd driver struct * @driver: underlying device driver @@ -111,17 +47,68 @@ struct qcom_smd_driver { qcom_smd_cb_t callback; }; +#if IS_ENABLED(CONFIG_QCOM_SMD) + int qcom_smd_driver_register(struct qcom_smd_driver *drv); void qcom_smd_driver_unregister(struct qcom_smd_driver *drv); +struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *channel, + const char *name, + qcom_smd_cb_t cb); +void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel); +void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data); +int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len); + + +#else + +static inline int qcom_smd_driver_register(struct qcom_smd_driver *drv) +{ + return -ENXIO; +} + +static inline void qcom_smd_driver_unregister(struct qcom_smd_driver *drv) +{ + /* This shouldn't be possible */ + WARN_ON(1); +} + +static inline struct qcom_smd_channel * +qcom_smd_open_channel(struct qcom_smd_channel *channel, + const char *name, + qcom_smd_cb_t cb) +{ + /* This shouldn't be possible */ + WARN_ON(1); + return NULL; +} + +void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel) +{ + /* This shouldn't be possible */ + WARN_ON(1); + return NULL; +} + +void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data) +{ + /* This shouldn't be possible */ + WARN_ON(1); +} + +static inline int qcom_smd_send(struct qcom_smd_channel *channel, + const void *data, int len) +{ + /* This shouldn't be possible */ + WARN_ON(1); + return -ENXIO; +} + +#endif + #define module_qcom_smd_driver(__smd_driver) \ module_driver(__smd_driver, qcom_smd_driver_register, \ qcom_smd_driver_unregister) -int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len); - -struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_device *sdev, - const char *name, - qcom_smd_cb_t cb); #endif diff --git a/include/linux/soc/qcom/wcnss_ctrl.h b/include/linux/soc/qcom/wcnss_ctrl.h new file mode 100644 index 000000000000..a37bc5538f19 --- /dev/null +++ b/include/linux/soc/qcom/wcnss_ctrl.h @@ -0,0 +1,8 @@ +#ifndef __WCNSS_CTRL_H__ +#define __WCNSS_CTRL_H__ + +#include + +struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb); + +#endif diff --git a/include/linux/socket.h b/include/linux/socket.h index 5bf59c8493b7..dbd81e7f21fd 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -200,7 +200,10 @@ struct ucred { #define AF_ALG 38 /* Algorithm sockets */ #define AF_NFC 39 /* NFC sockets */ #define AF_VSOCK 40 /* vSockets */ -#define AF_MAX 41 /* For now.. */ +#define AF_KCM 41 /* Kernel Connection Multiplexor*/ +#define AF_QIPCRTR 42 /* Qualcomm IPC Router */ + +#define AF_MAX 43 /* For now.. */ /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -246,6 +249,8 @@ struct ucred { #define PF_ALG AF_ALG #define PF_NFC AF_NFC #define PF_VSOCK AF_VSOCK +#define PF_KCM AF_KCM +#define PF_QIPCRTR AF_QIPCRTR #define PF_MAX AF_MAX /* Maximum queue length specifiable by listen. */ @@ -322,6 +327,7 @@ struct ucred { #define SOL_CAIF 278 #define SOL_ALG 279 #define SOL_NFC 280 +#define SOL_KCM 281 /* IPX options */ #define IPX_TYPE 1 diff --git a/include/net/kcm.h b/include/net/kcm.h new file mode 100644 index 000000000000..1bcae39070ec --- /dev/null +++ b/include/net/kcm.h @@ -0,0 +1,125 @@ +/* + * Kernel Connection Multiplexor + * + * Copyright (c) 2016 Tom Herbert + * + * 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 __NET_KCM_H_ +#define __NET_KCM_H_ + +#include +#include +#include + +extern unsigned int kcm_net_id; + +struct kcm_tx_msg { + unsigned int sent; + unsigned int fragidx; + unsigned int frag_offset; + unsigned int msg_flags; + struct sk_buff *frag_skb; + struct sk_buff *last_skb; +}; + +struct kcm_rx_msg { + int full_len; + int accum_len; + int offset; +}; + +/* Socket structure for KCM client sockets */ +struct kcm_sock { + struct sock sk; + struct kcm_mux *mux; + struct list_head kcm_sock_list; + int index; + u32 done : 1; + struct work_struct done_work; + + /* Transmit */ + struct kcm_psock *tx_psock; + struct work_struct tx_work; + struct list_head wait_psock_list; + struct sk_buff *seq_skb; + + /* Don't use bit fields here, these are set under different locks */ + bool tx_wait; + bool tx_wait_more; + + /* Receive */ + struct kcm_psock *rx_psock; + struct list_head wait_rx_list; /* KCMs waiting for receiving */ + bool rx_wait; + u32 rx_disabled : 1; +}; + +struct bpf_prog; + +/* Structure for an attached lower socket */ +struct kcm_psock { + struct sock *sk; + struct kcm_mux *mux; + int index; + + u32 tx_stopped : 1; + u32 rx_stopped : 1; + u32 done : 1; + u32 unattaching : 1; + + void (*save_state_change)(struct sock *sk); + void (*save_data_ready)(struct sock *sk); + void (*save_write_space)(struct sock *sk); + + struct list_head psock_list; + + /* Receive */ + struct sk_buff *rx_skb_head; + struct sk_buff **rx_skb_nextp; + struct sk_buff *ready_rx_msg; + struct list_head psock_ready_list; + struct work_struct rx_work; + struct delayed_work rx_delayed_work; + struct bpf_prog *bpf_prog; + struct kcm_sock *rx_kcm; + + /* Transmit */ + struct kcm_sock *tx_kcm; + struct list_head psock_avail_list; +}; + +/* Per net MUX list */ +struct kcm_net { + struct mutex mutex; + struct list_head mux_list; + int count; +}; + +/* Structure for a MUX */ +struct kcm_mux { + struct list_head kcm_mux_list; + struct rcu_head rcu; + struct kcm_net *knet; + + struct list_head kcm_socks; /* All KCM sockets on MUX */ + int kcm_socks_cnt; /* Total KCM socket count for MUX */ + struct list_head psocks; /* List of all psocks on MUX */ + int psocks_cnt; /* Total attached sockets */ + + /* Receive */ + spinlock_t rx_lock ____cacheline_aligned_in_smp; + struct list_head kcm_rx_waiters; /* KCMs waiting for receiving */ + struct list_head psocks_ready; /* List of psocks with a msg ready */ + struct sk_buff_head rx_hold_queue; + + /* Transmit */ + spinlock_t lock ____cacheline_aligned_in_smp; /* TX and mux locking */ + struct list_head psocks_avail; /* List of available psocks */ + struct list_head kcm_tx_waiters; /* KCMs waiting for a TX psock */ +}; + +#endif /* __NET_KCM_H_ */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 760bc4d5a2cf..323237e5bf36 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2157,7 +2157,7 @@ static inline void SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev * @hw: the &struct ieee80211_hw to set the MAC address for * @addr: the address to set */ -static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, u8 *addr) +static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, const u8 *addr) { memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN); } diff --git a/include/sound/soc.h b/include/sound/soc.h index fb955e69a78e..fecde5b4d978 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -172,6 +172,17 @@ .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_R_S_VALUE(reg_left, reg_right, xshift, \ xmin, xmax, xsign_bit, xinvert) } +#define SOC_SINGLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ + .put = snd_soc_put_volsw, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .rreg = xreg, \ + .min = xmin, .max = xmax, .platform_max = xmax, \ + .sign_bit = 7,} } #define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ diff --git a/include/uapi/linux/gps_proxy.h b/include/uapi/linux/gps_proxy.h new file mode 100644 index 000000000000..5ac7f6b873b8 --- /dev/null +++ b/include/uapi/linux/gps_proxy.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2016, The Linux Foundation. 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 and + * only 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. + */ + +#ifndef __GPS_PROXY_H__ +#define __GPS_PROXY_H__ + +#define QMI_LOC_NMEA_STRING_MAX_LENGTH_V02 201 + +enum QGPS_TTY_IOCTL_CMDS { + QGPS_REGISTER_HANDLE_IOC = 0, + QGPS_SEND_NMEA_IOC, + QGPS_IS_ACTIVE_IOC, +}; + +#define QGPS_IOC_MAGIC 'q' +#define QGPS_REGISTER_HANDLE _IO(QGPS_IOC_MAGIC, QGPS_REGISTER_HANDLE_IOC) +#define QGPS_SEND_NMEA _IO(QGPS_IOC_MAGIC, QGPS_SEND_NMEA_IOC) +#define QGPS_IS_ACTIVE _IO(QGPS_IOC_MAGIC, QGPS_IS_ACTIVE_IOC) + +struct gps_proxy_data { + size_t nmea_length; + char nmea_string[QMI_LOC_NMEA_STRING_MAX_LENGTH_V02]; +}; + +#endif /* __GPS_PROXY_H__ */ diff --git a/include/uapi/linux/kcm.h b/include/uapi/linux/kcm.h new file mode 100644 index 000000000000..a5a530940b99 --- /dev/null +++ b/include/uapi/linux/kcm.h @@ -0,0 +1,40 @@ +/* + * Kernel Connection Multiplexor + * + * Copyright (c) 2016 Tom Herbert + * + * 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. + * + * User API to clone KCM sockets and attach transport socket to a KCM + * multiplexor. + */ + +#ifndef KCM_KERNEL_H +#define KCM_KERNEL_H + +struct kcm_attach { + int fd; + int bpf_fd; +}; + +struct kcm_unattach { + int fd; +}; + +struct kcm_clone { + int fd; +}; + +#define SIOCKCMATTACH (SIOCPROTOPRIVATE + 0) +#define SIOCKCMUNATTACH (SIOCPROTOPRIVATE + 1) +#define SIOCKCMCLONE (SIOCPROTOPRIVATE + 2) + +#define KCMPROTO_CONNECTED 0 + +/* Socket options */ +#define KCM_RECV_DISABLE 1 + +#endif + diff --git a/include/uapi/linux/qrtr.h b/include/uapi/linux/qrtr.h new file mode 100644 index 000000000000..66c0748d26e2 --- /dev/null +++ b/include/uapi/linux/qrtr.h @@ -0,0 +1,12 @@ +#ifndef _LINUX_QRTR_H +#define _LINUX_QRTR_H + +#include + +struct sockaddr_qrtr { + __kernel_sa_family_t sq_family; + __u32 sq_node; + __u32 sq_port; +}; + +#endif /* _LINUX_QRTR_H */ diff --git a/kernel/configs/debug.config b/kernel/configs/debug.config new file mode 100644 index 000000000000..451a71bb6bda --- /dev/null +++ b/kernel/configs/debug.config @@ -0,0 +1,13 @@ +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_DEBUG_LOCK_ALLOC=y +CONFIG_PROVE_LOCKING=y +CONFIG_PROVE_RCU=y +CONFIG_SLUB_DEBUG=y +CONFIG_SLUB_DEBUG_ON=y +CONFIG_KASAN=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_USB_GADGET_DEBUG=y +CONFIG_USB_GADGET_DEBUG_FILES=y diff --git a/kernel/configs/distro.config b/kernel/configs/distro.config index 032e3fef8994..e4112b36deb8 100644 --- a/kernel/configs/distro.config +++ b/kernel/configs/distro.config @@ -111,7 +111,6 @@ CONFIG_USB_SERIAL_QT2=m # USB gadget CONFIG_USB_GADGET=y -CONFIG_USB_GADGET_DEBUG_FILES=y CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_CONFIGFS=m CONFIG_USB_ZERO=m @@ -210,7 +209,6 @@ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_PRINTK_TIME=y CONFIG_STACKTRACE=y CONFIG_MMC_BLOCK_MINORS=32 -CONFIG_PROVE_LOCKING=y CONFIG_GPIO_SYSFS=y CONFIG_SND_USB=y CONFIG_SND_USB_AUDIO=y diff --git a/net/Kconfig b/net/Kconfig index 127da94ae25e..1c9fda1c602d 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -233,6 +233,7 @@ source "net/mpls/Kconfig" source "net/hsr/Kconfig" source "net/switchdev/Kconfig" source "net/l3mdev/Kconfig" +source "net/qrtr/Kconfig" config RPS bool @@ -351,6 +352,7 @@ source "net/can/Kconfig" source "net/irda/Kconfig" source "net/bluetooth/Kconfig" source "net/rxrpc/Kconfig" +source "net/kcm/Kconfig" config FIB_RULES bool diff --git a/net/Makefile b/net/Makefile index a5d04098dfce..bdd14553a774 100644 --- a/net/Makefile +++ b/net/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_IRDA) += irda/ obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_SUNRPC) += sunrpc/ obj-$(CONFIG_AF_RXRPC) += rxrpc/ +obj-$(CONFIG_AF_KCM) += kcm/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_L2TP) += l2tp/ obj-$(CONFIG_DECNET) += decnet/ @@ -77,3 +78,4 @@ endif ifneq ($(CONFIG_NET_L3_MASTER_DEV),) obj-y += l3mdev/ endif +obj-$(CONFIG_QRTR) += qrtr/ diff --git a/net/kcm/Kconfig b/net/kcm/Kconfig new file mode 100644 index 000000000000..5db94d940ecc --- /dev/null +++ b/net/kcm/Kconfig @@ -0,0 +1,10 @@ + +config AF_KCM + tristate "KCM sockets" + depends on INET + select BPF_SYSCALL + ---help--- + KCM (Kernel Connection Multiplexor) sockets provide a method + for multiplexing messages of a message based application + protocol over kernel connectons (e.g. TCP connections). + diff --git a/net/kcm/Makefile b/net/kcm/Makefile new file mode 100644 index 000000000000..cb525f7c5a13 --- /dev/null +++ b/net/kcm/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_AF_KCM) += kcm.o + +kcm-y := kcmsock.o diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c new file mode 100644 index 000000000000..30ef69ac6b81 --- /dev/null +++ b/net/kcm/kcmsock.c @@ -0,0 +1,2016 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned int kcm_net_id; + +static struct kmem_cache *kcm_psockp __read_mostly; +static struct kmem_cache *kcm_muxp __read_mostly; +static struct workqueue_struct *kcm_wq; + +static inline struct kcm_sock *kcm_sk(const struct sock *sk) +{ + return (struct kcm_sock *)sk; +} + +static inline struct kcm_tx_msg *kcm_tx_msg(struct sk_buff *skb) +{ + return (struct kcm_tx_msg *)skb->cb; +} + +static inline struct kcm_rx_msg *kcm_rx_msg(struct sk_buff *skb) +{ + return (struct kcm_rx_msg *)((void *)skb->cb + + offsetof(struct qdisc_skb_cb, data)); +} + +static void report_csk_error(struct sock *csk, int err) +{ + csk->sk_err = EPIPE; + csk->sk_error_report(csk); +} + +/* Callback lock held */ +static void kcm_abort_rx_psock(struct kcm_psock *psock, int err, + struct sk_buff *skb) +{ + struct sock *csk = psock->sk; + + /* Unrecoverable error in receive */ + + if (psock->rx_stopped) + return; + + psock->rx_stopped = 1; + + /* Report an error on the lower socket */ + report_csk_error(csk, err); +} + +static void kcm_abort_tx_psock(struct kcm_psock *psock, int err, + bool wakeup_kcm) +{ + struct sock *csk = psock->sk; + struct kcm_mux *mux = psock->mux; + + /* Unrecoverable error in transmit */ + + spin_lock_bh(&mux->lock); + + if (psock->tx_stopped) { + spin_unlock_bh(&mux->lock); + return; + } + + psock->tx_stopped = 1; + + if (!psock->tx_kcm) { + /* Take off psocks_avail list */ + list_del(&psock->psock_avail_list); + } else if (wakeup_kcm) { + /* In this case psock is being aborted while outside of + * write_msgs and psock is reserved. Schedule tx_work + * to handle the failure there. Need to commit tx_stopped + * before queuing work. + */ + smp_mb(); + + queue_work(kcm_wq, &psock->tx_kcm->tx_work); + } + + spin_unlock_bh(&mux->lock); + + /* Report error on lower socket */ + report_csk_error(csk, err); +} + +static int kcm_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); + +/* KCM is ready to receive messages on its queue-- either the KCM is new or + * has become unblocked after being blocked on full socket buffer. Queue any + * pending ready messages on a psock. RX mux lock held. + */ +static void kcm_rcv_ready(struct kcm_sock *kcm) +{ + struct kcm_mux *mux = kcm->mux; + struct kcm_psock *psock; + struct sk_buff *skb; + + if (unlikely(kcm->rx_wait || kcm->rx_psock || kcm->rx_disabled)) + return; + + while (unlikely((skb = __skb_dequeue(&mux->rx_hold_queue)))) { + if (kcm_queue_rcv_skb(&kcm->sk, skb)) { + /* Assuming buffer limit has been reached */ + skb_queue_head(&mux->rx_hold_queue, skb); + WARN_ON(!sk_rmem_alloc_get(&kcm->sk)); + return; + } + } + + while (!list_empty(&mux->psocks_ready)) { + psock = list_first_entry(&mux->psocks_ready, struct kcm_psock, + psock_ready_list); + + if (kcm_queue_rcv_skb(&kcm->sk, psock->ready_rx_msg)) { + /* Assuming buffer limit has been reached */ + WARN_ON(!sk_rmem_alloc_get(&kcm->sk)); + return; + } + + /* Consumed the ready message on the psock. Schedule rx_work to + * get more messages. + */ + list_del(&psock->psock_ready_list); + psock->ready_rx_msg = NULL; + + /* Commit clearing of ready_rx_msg for queuing work */ + smp_mb(); + + queue_work(kcm_wq, &psock->rx_work); + } + + /* Buffer limit is okay now, add to ready list */ + list_add_tail(&kcm->wait_rx_list, + &kcm->mux->kcm_rx_waiters); + kcm->rx_wait = true; +} + +static void kcm_rfree(struct sk_buff *skb) +{ + struct sock *sk = skb->sk; + struct kcm_sock *kcm = kcm_sk(sk); + struct kcm_mux *mux = kcm->mux; + unsigned int len = skb->truesize; + + sk_mem_uncharge(sk, len); + atomic_sub(len, &sk->sk_rmem_alloc); + + /* For reading rx_wait and rx_psock without holding lock */ + smp_mb__after_atomic(); + + if (!kcm->rx_wait && !kcm->rx_psock && + sk_rmem_alloc_get(sk) < sk->sk_rcvlowat) { + spin_lock_bh(&mux->rx_lock); + kcm_rcv_ready(kcm); + spin_unlock_bh(&mux->rx_lock); + } +} + +static int kcm_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + struct sk_buff_head *list = &sk->sk_receive_queue; + + if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) + return -ENOMEM; + + if (!sk_rmem_schedule(sk, skb, skb->truesize)) + return -ENOBUFS; + + skb->dev = NULL; + + skb_orphan(skb); + skb->sk = sk; + skb->destructor = kcm_rfree; + atomic_add(skb->truesize, &sk->sk_rmem_alloc); + sk_mem_charge(sk, skb->truesize); + + skb_queue_tail(list, skb); + + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk); + + return 0; +} + +/* Requeue received messages for a kcm socket to other kcm sockets. This is + * called with a kcm socket is receive disabled. + * RX mux lock held. + */ +static void requeue_rx_msgs(struct kcm_mux *mux, struct sk_buff_head *head) +{ + struct sk_buff *skb; + struct kcm_sock *kcm; + + while ((skb = __skb_dequeue(head))) { + /* Reset destructor to avoid calling kcm_rcv_ready */ + skb->destructor = sock_rfree; + skb_orphan(skb); +try_again: + if (list_empty(&mux->kcm_rx_waiters)) { + skb_queue_tail(&mux->rx_hold_queue, skb); + continue; + } + + kcm = list_first_entry(&mux->kcm_rx_waiters, + struct kcm_sock, wait_rx_list); + + if (kcm_queue_rcv_skb(&kcm->sk, skb)) { + /* Should mean socket buffer full */ + list_del(&kcm->wait_rx_list); + kcm->rx_wait = false; + + /* Commit rx_wait to read in kcm_free */ + smp_wmb(); + + goto try_again; + } + } +} + +/* Lower sock lock held */ +static struct kcm_sock *reserve_rx_kcm(struct kcm_psock *psock, + struct sk_buff *head) +{ + struct kcm_mux *mux = psock->mux; + struct kcm_sock *kcm; + + WARN_ON(psock->ready_rx_msg); + + if (psock->rx_kcm) + return psock->rx_kcm; + + spin_lock_bh(&mux->rx_lock); + + if (psock->rx_kcm) { + spin_unlock_bh(&mux->rx_lock); + return psock->rx_kcm; + } + + if (list_empty(&mux->kcm_rx_waiters)) { + psock->ready_rx_msg = head; + list_add_tail(&psock->psock_ready_list, + &mux->psocks_ready); + spin_unlock_bh(&mux->rx_lock); + return NULL; + } + + kcm = list_first_entry(&mux->kcm_rx_waiters, + struct kcm_sock, wait_rx_list); + list_del(&kcm->wait_rx_list); + kcm->rx_wait = false; + + psock->rx_kcm = kcm; + kcm->rx_psock = psock; + + spin_unlock_bh(&mux->rx_lock); + + return kcm; +} + +static void kcm_done(struct kcm_sock *kcm); + +static void kcm_done_work(struct work_struct *w) +{ + kcm_done(container_of(w, struct kcm_sock, done_work)); +} + +/* Lower sock held */ +static void unreserve_rx_kcm(struct kcm_psock *psock, + bool rcv_ready) +{ + struct kcm_sock *kcm = psock->rx_kcm; + struct kcm_mux *mux = psock->mux; + + if (!kcm) + return; + + spin_lock_bh(&mux->rx_lock); + + psock->rx_kcm = NULL; + kcm->rx_psock = NULL; + + /* Commit kcm->rx_psock before sk_rmem_alloc_get to sync with + * kcm_rfree + */ + smp_mb(); + + if (unlikely(kcm->done)) { + spin_unlock_bh(&mux->rx_lock); + + /* Need to run kcm_done in a task since we need to qcquire + * callback locks which may already be held here. + */ + INIT_WORK(&kcm->done_work, kcm_done_work); + schedule_work(&kcm->done_work); + return; + } + + if (unlikely(kcm->rx_disabled)) { + requeue_rx_msgs(mux, &kcm->sk.sk_receive_queue); + } else if (rcv_ready || unlikely(!sk_rmem_alloc_get(&kcm->sk))) { + /* Check for degenerative race with rx_wait that all + * data was dequeued (accounted for in kcm_rfree). + */ + kcm_rcv_ready(kcm); + } + spin_unlock_bh(&mux->rx_lock); +} + +/* Macro to invoke filter function. */ +#define KCM_RUN_FILTER(prog, ctx) \ + (*prog->bpf_func)(ctx, prog->insnsi) + +/* Lower socket lock held */ +static int kcm_tcp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb, + unsigned int orig_offset, size_t orig_len) +{ + struct kcm_psock *psock = (struct kcm_psock *)desc->arg.data; + struct kcm_rx_msg *rxm; + struct kcm_sock *kcm; + struct sk_buff *head, *skb; + size_t eaten = 0, cand_len; + ssize_t extra; + int err; + bool cloned_orig = false; + + if (psock->ready_rx_msg) + return 0; + + head = psock->rx_skb_head; + if (head) { + /* Message already in progress */ + + if (unlikely(orig_offset)) { + /* Getting data with a non-zero offset when a message is + * in progress is not expected. If it does happen, we + * need to clone and pull since we can't deal with + * offsets in the skbs for a message expect in the head. + */ + orig_skb = skb_clone(orig_skb, GFP_ATOMIC); + if (!orig_skb) { + desc->error = -ENOMEM; + return 0; + } + if (!pskb_pull(orig_skb, orig_offset)) { + kfree_skb(orig_skb); + desc->error = -ENOMEM; + return 0; + } + cloned_orig = true; + orig_offset = 0; + } + + if (!psock->rx_skb_nextp) { + /* We are going to append to the frags_list of head. + * Need to unshare the frag_list. + */ + err = skb_unclone(head, GFP_ATOMIC); + if (err) { + desc->error = err; + return 0; + } + + if (unlikely(skb_shinfo(head)->frag_list)) { + /* We can't append to an sk_buff that already + * has a frag_list. We create a new head, point + * the frag_list of that to the old head, and + * then are able to use the old head->next for + * appending to the message. + */ + if (WARN_ON(head->next)) { + desc->error = -EINVAL; + return 0; + } + + skb = alloc_skb(0, GFP_ATOMIC); + if (!skb) { + desc->error = -ENOMEM; + return 0; + } + skb->len = head->len; + skb->data_len = head->len; + skb->truesize = head->truesize; + *kcm_rx_msg(skb) = *kcm_rx_msg(head); + psock->rx_skb_nextp = &head->next; + skb_shinfo(skb)->frag_list = head; + psock->rx_skb_head = skb; + head = skb; + } else { + psock->rx_skb_nextp = + &skb_shinfo(head)->frag_list; + } + } + } + + while (eaten < orig_len) { + /* Always clone since we will consume something */ + skb = skb_clone(orig_skb, GFP_ATOMIC); + if (!skb) { + desc->error = -ENOMEM; + break; + } + + cand_len = orig_len - eaten; + + head = psock->rx_skb_head; + if (!head) { + head = skb; + psock->rx_skb_head = head; + /* Will set rx_skb_nextp on next packet if needed */ + psock->rx_skb_nextp = NULL; + rxm = kcm_rx_msg(head); + memset(rxm, 0, sizeof(*rxm)); + rxm->offset = orig_offset + eaten; + } else { + /* Unclone since we may be appending to an skb that we + * already share a frag_list with. + */ + err = skb_unclone(skb, GFP_ATOMIC); + if (err) { + desc->error = err; + break; + } + + rxm = kcm_rx_msg(head); + *psock->rx_skb_nextp = skb; + psock->rx_skb_nextp = &skb->next; + head->data_len += skb->len; + head->len += skb->len; + head->truesize += skb->truesize; + } + + if (!rxm->full_len) { + ssize_t len; + + len = KCM_RUN_FILTER(psock->bpf_prog, head); + + if (!len) { + /* Need more header to determine length */ + rxm->accum_len += cand_len; + eaten += cand_len; + WARN_ON(eaten != orig_len); + break; + } else if (len <= (ssize_t)head->len - + skb->len - rxm->offset) { + /* Length must be into new skb (and also + * greater than zero) + */ + desc->error = -EPROTO; + psock->rx_skb_head = NULL; + kcm_abort_rx_psock(psock, EPROTO, head); + break; + } + + rxm->full_len = len; + } + + extra = (ssize_t)(rxm->accum_len + cand_len) - rxm->full_len; + + if (extra < 0) { + /* Message not complete yet. */ + rxm->accum_len += cand_len; + eaten += cand_len; + WARN_ON(eaten != orig_len); + break; + } + + /* Positive extra indicates ore bytes than needed for the + * message + */ + + WARN_ON(extra > cand_len); + + eaten += (cand_len - extra); + + /* Hurray, we have a new message! */ + psock->rx_skb_head = NULL; + +try_queue: + kcm = reserve_rx_kcm(psock, head); + if (!kcm) { + /* Unable to reserve a KCM, message is held in psock. */ + break; + } + + if (kcm_queue_rcv_skb(&kcm->sk, head)) { + /* Should mean socket buffer full */ + unreserve_rx_kcm(psock, false); + goto try_queue; + } + } + + if (cloned_orig) + kfree_skb(orig_skb); + + return eaten; +} + +/* Called with lock held on lower socket */ +static int psock_tcp_read_sock(struct kcm_psock *psock) +{ + read_descriptor_t desc; + + desc.arg.data = psock; + desc.error = 0; + desc.count = 1; /* give more than one skb per call */ + + /* sk should be locked here, so okay to do tcp_read_sock */ + tcp_read_sock(psock->sk, &desc, kcm_tcp_recv); + + unreserve_rx_kcm(psock, true); + + return desc.error; +} + +/* Lower sock lock held */ +static void psock_tcp_data_ready(struct sock *sk) +{ + struct kcm_psock *psock; + + read_lock_bh(&sk->sk_callback_lock); + + psock = (struct kcm_psock *)sk->sk_user_data; + if (unlikely(!psock || psock->rx_stopped)) + goto out; + + if (psock->ready_rx_msg) + goto out; + + if (psock_tcp_read_sock(psock) == -ENOMEM) + queue_delayed_work(kcm_wq, &psock->rx_delayed_work, 0); + +out: + read_unlock_bh(&sk->sk_callback_lock); +} + +static void do_psock_rx_work(struct kcm_psock *psock) +{ + read_descriptor_t rd_desc; + struct sock *csk = psock->sk; + + /* We need the read lock to synchronize with psock_tcp_data_ready. We + * need the socket lock for calling tcp_read_sock. + */ + lock_sock(csk); + read_lock_bh(&csk->sk_callback_lock); + + if (unlikely(csk->sk_user_data != psock)) + goto out; + + if (unlikely(psock->rx_stopped)) + goto out; + + if (psock->ready_rx_msg) + goto out; + + rd_desc.arg.data = psock; + + if (psock_tcp_read_sock(psock) == -ENOMEM) + queue_delayed_work(kcm_wq, &psock->rx_delayed_work, 0); + +out: + read_unlock_bh(&csk->sk_callback_lock); + release_sock(csk); +} + +static void psock_rx_work(struct work_struct *w) +{ + do_psock_rx_work(container_of(w, struct kcm_psock, rx_work)); +} + +static void psock_rx_delayed_work(struct work_struct *w) +{ + do_psock_rx_work(container_of(w, struct kcm_psock, + rx_delayed_work.work)); +} + +static void psock_tcp_state_change(struct sock *sk) +{ + /* TCP only does a POLLIN for a half close. Do a POLLHUP here + * since application will normally not poll with POLLIN + * on the TCP sockets. + */ + + report_csk_error(sk, EPIPE); +} + +static void psock_tcp_write_space(struct sock *sk) +{ + struct kcm_psock *psock; + struct kcm_mux *mux; + struct kcm_sock *kcm; + + read_lock_bh(&sk->sk_callback_lock); + + psock = (struct kcm_psock *)sk->sk_user_data; + if (unlikely(!psock)) + goto out; + + mux = psock->mux; + + spin_lock_bh(&mux->lock); + + /* Check if the socket is reserved so someone is waiting for sending. */ + kcm = psock->tx_kcm; + if (kcm) + queue_work(kcm_wq, &kcm->tx_work); + + spin_unlock_bh(&mux->lock); +out: + read_unlock_bh(&sk->sk_callback_lock); +} + +static void unreserve_psock(struct kcm_sock *kcm); + +/* kcm sock is locked. */ +static struct kcm_psock *reserve_psock(struct kcm_sock *kcm) +{ + struct kcm_mux *mux = kcm->mux; + struct kcm_psock *psock; + + psock = kcm->tx_psock; + + smp_rmb(); /* Must read tx_psock before tx_wait */ + + if (psock) { + WARN_ON(kcm->tx_wait); + if (unlikely(psock->tx_stopped)) + unreserve_psock(kcm); + else + return kcm->tx_psock; + } + + spin_lock_bh(&mux->lock); + + /* Check again under lock to see if psock was reserved for this + * psock via psock_unreserve. + */ + psock = kcm->tx_psock; + if (unlikely(psock)) { + WARN_ON(kcm->tx_wait); + spin_unlock_bh(&mux->lock); + return kcm->tx_psock; + } + + if (!list_empty(&mux->psocks_avail)) { + psock = list_first_entry(&mux->psocks_avail, + struct kcm_psock, + psock_avail_list); + list_del(&psock->psock_avail_list); + if (kcm->tx_wait) { + list_del(&kcm->wait_psock_list); + kcm->tx_wait = false; + } + kcm->tx_psock = psock; + psock->tx_kcm = kcm; + } else if (!kcm->tx_wait) { + list_add_tail(&kcm->wait_psock_list, + &mux->kcm_tx_waiters); + kcm->tx_wait = true; + } + + spin_unlock_bh(&mux->lock); + + return psock; +} + +/* mux lock held */ +static void psock_now_avail(struct kcm_psock *psock) +{ + struct kcm_mux *mux = psock->mux; + struct kcm_sock *kcm; + + if (list_empty(&mux->kcm_tx_waiters)) { + list_add_tail(&psock->psock_avail_list, + &mux->psocks_avail); + } else { + kcm = list_first_entry(&mux->kcm_tx_waiters, + struct kcm_sock, + wait_psock_list); + list_del(&kcm->wait_psock_list); + kcm->tx_wait = false; + psock->tx_kcm = kcm; + + /* Commit before changing tx_psock since that is read in + * reserve_psock before queuing work. + */ + smp_mb(); + + kcm->tx_psock = psock; + queue_work(kcm_wq, &kcm->tx_work); + } +} + +/* kcm sock is locked. */ +static void unreserve_psock(struct kcm_sock *kcm) +{ + struct kcm_psock *psock; + struct kcm_mux *mux = kcm->mux; + + spin_lock_bh(&mux->lock); + + psock = kcm->tx_psock; + + if (WARN_ON(!psock)) { + spin_unlock_bh(&mux->lock); + return; + } + + smp_rmb(); /* Read tx_psock before tx_wait */ + + WARN_ON(kcm->tx_wait); + + kcm->tx_psock = NULL; + psock->tx_kcm = NULL; + + if (unlikely(psock->tx_stopped)) { + if (psock->done) { + /* Deferred free */ + list_del(&psock->psock_list); + mux->psocks_cnt--; + sock_put(psock->sk); + fput(psock->sk->sk_socket->file); + kmem_cache_free(kcm_psockp, psock); + } + + /* Don't put back on available list */ + + spin_unlock_bh(&mux->lock); + + return; + } + + psock_now_avail(psock); + + spin_unlock_bh(&mux->lock); +} + +/* Write any messages ready on the kcm socket. Called with kcm sock lock + * held. Return bytes actually sent or error. + */ +static int kcm_write_msgs(struct kcm_sock *kcm) +{ + struct sock *sk = &kcm->sk; + struct kcm_psock *psock; + struct sk_buff *skb, *head; + struct kcm_tx_msg *txm; + unsigned short fragidx, frag_offset; + unsigned int sent, total_sent = 0; + int ret = 0; + + kcm->tx_wait_more = false; + psock = kcm->tx_psock; + if (unlikely(psock && psock->tx_stopped)) { + /* A reserved psock was aborted asynchronously. Unreserve + * it and we'll retry the message. + */ + unreserve_psock(kcm); + if (skb_queue_empty(&sk->sk_write_queue)) + return 0; + + kcm_tx_msg(skb_peek(&sk->sk_write_queue))->sent = 0; + + } else if (skb_queue_empty(&sk->sk_write_queue)) { + return 0; + } + + head = skb_peek(&sk->sk_write_queue); + txm = kcm_tx_msg(head); + + if (txm->sent) { + /* Send of first skbuff in queue already in progress */ + if (WARN_ON(!psock)) { + ret = -EINVAL; + goto out; + } + sent = txm->sent; + frag_offset = txm->frag_offset; + fragidx = txm->fragidx; + skb = txm->frag_skb; + + goto do_frag; + } + +try_again: + psock = reserve_psock(kcm); + if (!psock) + goto out; + + do { + skb = head; + txm = kcm_tx_msg(head); + sent = 0; + +do_frag_list: + if (WARN_ON(!skb_shinfo(skb)->nr_frags)) { + ret = -EINVAL; + goto out; + } + + for (fragidx = 0; fragidx < skb_shinfo(skb)->nr_frags; + fragidx++) { + skb_frag_t *frag; + + frag_offset = 0; +do_frag: + frag = &skb_shinfo(skb)->frags[fragidx]; + if (WARN_ON(!frag->size)) { + ret = -EINVAL; + goto out; + } + + ret = kernel_sendpage(psock->sk->sk_socket, + frag->page.p, + frag->page_offset + frag_offset, + frag->size - frag_offset, + MSG_DONTWAIT); + if (ret <= 0) { + if (ret == -EAGAIN) { + /* Save state to try again when there's + * write space on the socket + */ + txm->sent = sent; + txm->frag_offset = frag_offset; + txm->fragidx = fragidx; + txm->frag_skb = skb; + + ret = 0; + goto out; + } + + /* Hard failure in sending message, abort this + * psock since it has lost framing + * synchonization and retry sending the + * message from the beginning. + */ + kcm_abort_tx_psock(psock, ret ? -ret : EPIPE, + true); + unreserve_psock(kcm); + + txm->sent = 0; + ret = 0; + + goto try_again; + } + + sent += ret; + frag_offset += ret; + if (frag_offset < frag->size) { + /* Not finished with this frag */ + goto do_frag; + } + } + + if (skb == head) { + if (skb_has_frag_list(skb)) { + skb = skb_shinfo(skb)->frag_list; + goto do_frag_list; + } + } else if (skb->next) { + skb = skb->next; + goto do_frag_list; + } + + /* Successfully sent the whole packet, account for it. */ + skb_dequeue(&sk->sk_write_queue); + kfree_skb(head); + sk->sk_wmem_queued -= sent; + total_sent += sent; + } while ((head = skb_peek(&sk->sk_write_queue))); +out: + if (!head) { + /* Done with all queued messages. */ + WARN_ON(!skb_queue_empty(&sk->sk_write_queue)); + unreserve_psock(kcm); + } + + /* Check if write space is available */ + sk->sk_write_space(sk); + + return total_sent ? : ret; +} + +static void kcm_tx_work(struct work_struct *w) +{ + struct kcm_sock *kcm = container_of(w, struct kcm_sock, tx_work); + struct sock *sk = &kcm->sk; + int err; + + lock_sock(sk); + + /* Primarily for SOCK_DGRAM sockets, also handle asynchronous tx + * aborts + */ + err = kcm_write_msgs(kcm); + if (err < 0) { + /* Hard failure in write, report error on KCM socket */ + pr_warn("KCM: Hard failure on kcm_write_msgs %d\n", err); + report_csk_error(&kcm->sk, -err); + goto out; + } + + /* Primarily for SOCK_SEQPACKET sockets */ + if (likely(sk->sk_socket) && + test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) { + clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); + sk->sk_write_space(sk); + } + +out: + release_sock(sk); +} + +static void kcm_push(struct kcm_sock *kcm) +{ + if (kcm->tx_wait_more) + kcm_write_msgs(kcm); +} + +static int kcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct kcm_sock *kcm = kcm_sk(sk); + struct sk_buff *skb = NULL, *head = NULL; + size_t copy, copied = 0; + long timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); + int eor = (sock->type == SOCK_DGRAM) ? + !(msg->msg_flags & MSG_MORE) : !!(msg->msg_flags & MSG_EOR); + int err = -EPIPE; + + lock_sock(sk); + + /* Per tcp_sendmsg this should be in poll */ + sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk); + + if (sk->sk_err) + goto out_error; + + if (kcm->seq_skb) { + /* Previously opened message */ + head = kcm->seq_skb; + skb = kcm_tx_msg(head)->last_skb; + goto start; + } + + /* Call the sk_stream functions to manage the sndbuf mem. */ + if (!sk_stream_memory_free(sk)) { + kcm_push(kcm); + set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); + err = sk_stream_wait_memory(sk, &timeo); + if (err) + goto out_error; + } + + /* New message, alloc head skb */ + head = alloc_skb(0, sk->sk_allocation); + while (!head) { + kcm_push(kcm); + err = sk_stream_wait_memory(sk, &timeo); + if (err) + goto out_error; + + head = alloc_skb(0, sk->sk_allocation); + } + + skb = head; + + /* Set ip_summed to CHECKSUM_UNNECESSARY to avoid calling + * csum_and_copy_from_iter from skb_do_copy_data_nocache. + */ + skb->ip_summed = CHECKSUM_UNNECESSARY; + +start: + while (msg_data_left(msg)) { + bool merge = true; + int i = skb_shinfo(skb)->nr_frags; + struct page_frag *pfrag = sk_page_frag(sk); + + if (!sk_page_frag_refill(sk, pfrag)) + goto wait_for_memory; + + if (!skb_can_coalesce(skb, i, pfrag->page, + pfrag->offset)) { + if (i == MAX_SKB_FRAGS) { + struct sk_buff *tskb; + + tskb = alloc_skb(0, sk->sk_allocation); + if (!tskb) + goto wait_for_memory; + + if (head == skb) + skb_shinfo(head)->frag_list = tskb; + else + skb->next = tskb; + + skb = tskb; + skb->ip_summed = CHECKSUM_UNNECESSARY; + continue; + } + merge = false; + } + + copy = min_t(int, msg_data_left(msg), + pfrag->size - pfrag->offset); + + if (!sk_wmem_schedule(sk, copy)) + goto wait_for_memory; + + err = skb_copy_to_page_nocache(sk, &msg->msg_iter, skb, + pfrag->page, + pfrag->offset, + copy); + if (err) + goto out_error; + + /* Update the skb. */ + if (merge) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { + skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, copy); + get_page(pfrag->page); + } + + pfrag->offset += copy; + copied += copy; + if (head != skb) { + head->len += copy; + head->data_len += copy; + } + + continue; + +wait_for_memory: + kcm_push(kcm); + err = sk_stream_wait_memory(sk, &timeo); + if (err) + goto out_error; + } + + if (eor) { + bool not_busy = skb_queue_empty(&sk->sk_write_queue); + + /* Message complete, queue it on send buffer */ + __skb_queue_tail(&sk->sk_write_queue, head); + kcm->seq_skb = NULL; + + if (msg->msg_flags & MSG_BATCH) { + kcm->tx_wait_more = true; + } else if (kcm->tx_wait_more || not_busy) { + err = kcm_write_msgs(kcm); + if (err < 0) { + /* We got a hard error in write_msgs but have + * already queued this message. Report an error + * in the socket, but don't affect return value + * from sendmsg + */ + pr_warn("KCM: Hard failure on kcm_write_msgs\n"); + report_csk_error(&kcm->sk, -err); + } + } + } else { + /* Message not complete, save state */ +partial_message: + kcm->seq_skb = head; + kcm_tx_msg(head)->last_skb = skb; + } + + release_sock(sk); + return copied; + +out_error: + kcm_push(kcm); + + if (copied && sock->type == SOCK_SEQPACKET) { + /* Wrote some bytes before encountering an + * error, return partial success. + */ + goto partial_message; + } + + if (head != kcm->seq_skb) + kfree_skb(head); + + err = sk_stream_error(sk, msg->msg_flags, err); + + /* make sure we wake any epoll edge trigger waiter */ + if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && err == -EAGAIN)) + sk->sk_write_space(sk); + + release_sock(sk); + return err; +} + +static struct sk_buff *kcm_wait_data(struct sock *sk, int flags, + long timeo, int *err) +{ + struct sk_buff *skb; + + while (!(skb = skb_peek(&sk->sk_receive_queue))) { + if (sk->sk_err) { + *err = sock_error(sk); + return NULL; + } + + if (sock_flag(sk, SOCK_DONE)) + return NULL; + + if ((flags & MSG_DONTWAIT) || !timeo) { + *err = -EAGAIN; + return NULL; + } + + sk_wait_data(sk, &timeo, NULL); + + /* Handle signals */ + if (signal_pending(current)) { + *err = sock_intr_errno(timeo); + return NULL; + } + } + + return skb; +} + +static int kcm_recvmsg(struct socket *sock, struct msghdr *msg, + size_t len, int flags) +{ + struct sock *sk = sock->sk; + int err = 0; + long timeo; + struct kcm_rx_msg *rxm; + int copied = 0; + struct sk_buff *skb; + + timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); + + lock_sock(sk); + + skb = kcm_wait_data(sk, flags, timeo, &err); + if (!skb) + goto out; + + /* Okay, have a message on the receive queue */ + + rxm = kcm_rx_msg(skb); + + if (len > rxm->full_len) + len = rxm->full_len; + + err = skb_copy_datagram_msg(skb, rxm->offset, msg, len); + if (err < 0) + goto out; + + copied = len; + if (likely(!(flags & MSG_PEEK))) { + if (copied < rxm->full_len) { + if (sock->type == SOCK_DGRAM) { + /* Truncated message */ + msg->msg_flags |= MSG_TRUNC; + goto msg_finished; + } + rxm->offset += copied; + rxm->full_len -= copied; + } else { +msg_finished: + /* Finished with message */ + msg->msg_flags |= MSG_EOR; + skb_unlink(skb, &sk->sk_receive_queue); + kfree_skb(skb); + } + } + +out: + release_sock(sk); + + return copied ? : err; +} + +/* kcm sock lock held */ +static void kcm_recv_disable(struct kcm_sock *kcm) +{ + struct kcm_mux *mux = kcm->mux; + + if (kcm->rx_disabled) + return; + + spin_lock_bh(&mux->rx_lock); + + kcm->rx_disabled = 1; + + /* If a psock is reserved we'll do cleanup in unreserve */ + if (!kcm->rx_psock) { + if (kcm->rx_wait) { + list_del(&kcm->wait_rx_list); + kcm->rx_wait = false; + } + + requeue_rx_msgs(mux, &kcm->sk.sk_receive_queue); + } + + spin_unlock_bh(&mux->rx_lock); +} + +/* kcm sock lock held */ +static void kcm_recv_enable(struct kcm_sock *kcm) +{ + struct kcm_mux *mux = kcm->mux; + + if (!kcm->rx_disabled) + return; + + spin_lock_bh(&mux->rx_lock); + + kcm->rx_disabled = 0; + kcm_rcv_ready(kcm); + + spin_unlock_bh(&mux->rx_lock); +} + +static int kcm_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, unsigned int optlen) +{ + struct kcm_sock *kcm = kcm_sk(sock->sk); + int val, valbool; + int err = 0; + + if (level != SOL_KCM) + return -ENOPROTOOPT; + + if (optlen < sizeof(int)) + return -EINVAL; + + if (get_user(val, (int __user *)optval)) + return -EINVAL; + + valbool = val ? 1 : 0; + + switch (optname) { + case KCM_RECV_DISABLE: + lock_sock(&kcm->sk); + if (valbool) + kcm_recv_disable(kcm); + else + kcm_recv_enable(kcm); + release_sock(&kcm->sk); + break; + default: + err = -ENOPROTOOPT; + } + + return err; +} + +static int kcm_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct kcm_sock *kcm = kcm_sk(sock->sk); + int val, len; + + if (level != SOL_KCM) + return -ENOPROTOOPT; + + if (get_user(len, optlen)) + return -EFAULT; + + len = min_t(unsigned int, len, sizeof(int)); + if (len < 0) + return -EINVAL; + + switch (optname) { + case KCM_RECV_DISABLE: + val = kcm->rx_disabled; + break; + default: + return -ENOPROTOOPT; + } + + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + return 0; +} + +static void init_kcm_sock(struct kcm_sock *kcm, struct kcm_mux *mux) +{ + struct kcm_sock *tkcm; + struct list_head *head; + int index = 0; + + /* For SOCK_SEQPACKET sock type, datagram_poll checks the sk_state, so + * we set sk_state, otherwise epoll_wait always returns right away with + * POLLHUP + */ + kcm->sk.sk_state = TCP_ESTABLISHED; + + /* Add to mux's kcm sockets list */ + kcm->mux = mux; + spin_lock_bh(&mux->lock); + + head = &mux->kcm_socks; + list_for_each_entry(tkcm, &mux->kcm_socks, kcm_sock_list) { + if (tkcm->index != index) + break; + head = &tkcm->kcm_sock_list; + index++; + } + + list_add(&kcm->kcm_sock_list, head); + kcm->index = index; + + mux->kcm_socks_cnt++; + spin_unlock_bh(&mux->lock); + + INIT_WORK(&kcm->tx_work, kcm_tx_work); + + spin_lock_bh(&mux->rx_lock); + kcm_rcv_ready(kcm); + spin_unlock_bh(&mux->rx_lock); +} + +static int kcm_attach(struct socket *sock, struct socket *csock, + struct bpf_prog *prog) +{ + struct kcm_sock *kcm = kcm_sk(sock->sk); + struct kcm_mux *mux = kcm->mux; + struct sock *csk; + struct kcm_psock *psock = NULL, *tpsock; + struct list_head *head; + int index = 0; + + if (csock->ops->family != PF_INET && + csock->ops->family != PF_INET6) + return -EINVAL; + + csk = csock->sk; + if (!csk) + return -EINVAL; + + /* Only support TCP for now */ + if (csk->sk_protocol != IPPROTO_TCP) + return -EINVAL; + + psock = kmem_cache_zalloc(kcm_psockp, GFP_KERNEL); + if (!psock) + return -ENOMEM; + + psock->mux = mux; + psock->sk = csk; + psock->bpf_prog = prog; + INIT_WORK(&psock->rx_work, psock_rx_work); + INIT_DELAYED_WORK(&psock->rx_delayed_work, psock_rx_delayed_work); + + sock_hold(csk); + + write_lock_bh(&csk->sk_callback_lock); + psock->save_data_ready = csk->sk_data_ready; + psock->save_write_space = csk->sk_write_space; + psock->save_state_change = csk->sk_state_change; + csk->sk_user_data = psock; + csk->sk_data_ready = psock_tcp_data_ready; + csk->sk_write_space = psock_tcp_write_space; + csk->sk_state_change = psock_tcp_state_change; + write_unlock_bh(&csk->sk_callback_lock); + + /* Finished initialization, now add the psock to the MUX. */ + spin_lock_bh(&mux->lock); + head = &mux->psocks; + list_for_each_entry(tpsock, &mux->psocks, psock_list) { + if (tpsock->index != index) + break; + head = &tpsock->psock_list; + index++; + } + + list_add(&psock->psock_list, head); + psock->index = index; + + mux->psocks_cnt++; + psock_now_avail(psock); + spin_unlock_bh(&mux->lock); + + /* Schedule RX work in case there are already bytes queued */ + queue_work(kcm_wq, &psock->rx_work); + + return 0; +} + +static int kcm_attach_ioctl(struct socket *sock, struct kcm_attach *info) +{ + struct socket *csock; + struct bpf_prog *prog; + int err; + + csock = sockfd_lookup(info->fd, &err); + if (!csock) + return -ENOENT; + + prog = bpf_prog_get(info->bpf_fd); + if (IS_ERR(prog)) { + err = PTR_ERR(prog); + goto out; + } + + if (prog->type != BPF_PROG_TYPE_SOCKET_FILTER) { + bpf_prog_put(prog); + err = -EINVAL; + goto out; + } + + err = kcm_attach(sock, csock, prog); + if (err) { + bpf_prog_put(prog); + goto out; + } + + /* Keep reference on file also */ + + return 0; +out: + fput(csock->file); + return err; +} + +static void kcm_unattach(struct kcm_psock *psock) +{ + struct sock *csk = psock->sk; + struct kcm_mux *mux = psock->mux; + + /* Stop getting callbacks from TCP socket. After this there should + * be no way to reserve a kcm for this psock. + */ + write_lock_bh(&csk->sk_callback_lock); + csk->sk_user_data = NULL; + csk->sk_data_ready = psock->save_data_ready; + csk->sk_write_space = psock->save_write_space; + csk->sk_state_change = psock->save_state_change; + psock->rx_stopped = 1; + + if (WARN_ON(psock->rx_kcm)) { + write_unlock_bh(&csk->sk_callback_lock); + return; + } + + spin_lock_bh(&mux->rx_lock); + + /* Stop receiver activities. After this point psock should not be + * able to get onto ready list either through callbacks or work. + */ + if (psock->ready_rx_msg) { + list_del(&psock->psock_ready_list); + kfree_skb(psock->ready_rx_msg); + psock->ready_rx_msg = NULL; + } + + spin_unlock_bh(&mux->rx_lock); + + write_unlock_bh(&csk->sk_callback_lock); + + cancel_work_sync(&psock->rx_work); + cancel_delayed_work_sync(&psock->rx_delayed_work); + + bpf_prog_put(psock->bpf_prog); + + kfree_skb(psock->rx_skb_head); + psock->rx_skb_head = NULL; + + spin_lock_bh(&mux->lock); + + if (psock->tx_kcm) { + /* psock was reserved. Just mark it finished and we will clean + * up in the kcm paths, we need kcm lock which can not be + * acquired here. + */ + spin_unlock_bh(&mux->lock); + + /* We are unattaching a socket that is reserved. Abort the + * socket since we may be out of sync in sending on it. We need + * to do this without the mux lock. + */ + kcm_abort_tx_psock(psock, EPIPE, false); + + spin_lock_bh(&mux->lock); + if (!psock->tx_kcm) { + /* psock now unreserved in window mux was unlocked */ + goto no_reserved; + } + psock->done = 1; + + /* Commit done before queuing work to process it */ + smp_mb(); + + /* Queue tx work to make sure psock->done is handled */ + queue_work(kcm_wq, &psock->tx_kcm->tx_work); + spin_unlock_bh(&mux->lock); + } else { +no_reserved: + if (!psock->tx_stopped) + list_del(&psock->psock_avail_list); + list_del(&psock->psock_list); + mux->psocks_cnt--; + spin_unlock_bh(&mux->lock); + + sock_put(csk); + fput(csk->sk_socket->file); + kmem_cache_free(kcm_psockp, psock); + } +} + +static int kcm_unattach_ioctl(struct socket *sock, struct kcm_unattach *info) +{ + struct kcm_sock *kcm = kcm_sk(sock->sk); + struct kcm_mux *mux = kcm->mux; + struct kcm_psock *psock; + struct socket *csock; + struct sock *csk; + int err; + + csock = sockfd_lookup(info->fd, &err); + if (!csock) + return -ENOENT; + + csk = csock->sk; + if (!csk) { + err = -EINVAL; + goto out; + } + + err = -ENOENT; + + spin_lock_bh(&mux->lock); + + list_for_each_entry(psock, &mux->psocks, psock_list) { + if (psock->sk != csk) + continue; + + /* Found the matching psock */ + + if (psock->unattaching || WARN_ON(psock->done)) { + err = -EALREADY; + break; + } + + psock->unattaching = 1; + + spin_unlock_bh(&mux->lock); + + kcm_unattach(psock); + + err = 0; + goto out; + } + + spin_unlock_bh(&mux->lock); + +out: + fput(csock->file); + return err; +} + +static struct proto kcm_proto = { + .name = "KCM", + .owner = THIS_MODULE, + .obj_size = sizeof(struct kcm_sock), +}; + +/* Clone a kcm socket. */ +static int kcm_clone(struct socket *osock, struct kcm_clone *info, + struct socket **newsockp) +{ + struct socket *newsock; + struct sock *newsk; + struct file *newfile; + int err, newfd; + + err = -ENFILE; + newsock = sock_alloc(); + if (!newsock) + goto out; + + newsock->type = osock->type; + newsock->ops = osock->ops; + + __module_get(newsock->ops->owner); + + newfd = get_unused_fd_flags(0); + if (unlikely(newfd < 0)) { + err = newfd; + goto out_fd_fail; + } + + newfile = sock_alloc_file(newsock, 0, osock->sk->sk_prot_creator->name); + if (unlikely(IS_ERR(newfile))) { + err = PTR_ERR(newfile); + goto out_sock_alloc_fail; + } + + newsk = sk_alloc(sock_net(osock->sk), PF_KCM, GFP_KERNEL, + &kcm_proto, true); + if (!newsk) { + err = -ENOMEM; + goto out_sk_alloc_fail; + } + + sock_init_data(newsock, newsk); + init_kcm_sock(kcm_sk(newsk), kcm_sk(osock->sk)->mux); + + fd_install(newfd, newfile); + *newsockp = newsock; + info->fd = newfd; + + return 0; + +out_sk_alloc_fail: + fput(newfile); +out_sock_alloc_fail: + put_unused_fd(newfd); +out_fd_fail: + sock_release(newsock); +out: + return err; +} + +static int kcm_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + int err; + + switch (cmd) { + case SIOCKCMATTACH: { + struct kcm_attach info; + + if (copy_from_user(&info, (void __user *)arg, sizeof(info))) + err = -EFAULT; + + err = kcm_attach_ioctl(sock, &info); + + break; + } + case SIOCKCMUNATTACH: { + struct kcm_unattach info; + + if (copy_from_user(&info, (void __user *)arg, sizeof(info))) + err = -EFAULT; + + err = kcm_unattach_ioctl(sock, &info); + + break; + } + case SIOCKCMCLONE: { + struct kcm_clone info; + struct socket *newsock = NULL; + + if (copy_from_user(&info, (void __user *)arg, sizeof(info))) + err = -EFAULT; + + err = kcm_clone(sock, &info, &newsock); + + if (!err) { + if (copy_to_user((void __user *)arg, &info, + sizeof(info))) { + err = -EFAULT; + sock_release(newsock); + } + } + + break; + } + default: + err = -ENOIOCTLCMD; + break; + } + + return err; +} + +static void free_mux(struct rcu_head *rcu) +{ + struct kcm_mux *mux = container_of(rcu, + struct kcm_mux, rcu); + + kmem_cache_free(kcm_muxp, mux); +} + +static void release_mux(struct kcm_mux *mux) +{ + struct kcm_net *knet = mux->knet; + struct kcm_psock *psock, *tmp_psock; + + /* Release psocks */ + list_for_each_entry_safe(psock, tmp_psock, + &mux->psocks, psock_list) { + if (!WARN_ON(psock->unattaching)) + kcm_unattach(psock); + } + + if (WARN_ON(mux->psocks_cnt)) + return; + + __skb_queue_purge(&mux->rx_hold_queue); + + mutex_lock(&knet->mutex); + list_del_rcu(&mux->kcm_mux_list); + knet->count--; + mutex_unlock(&knet->mutex); + + call_rcu(&mux->rcu, free_mux); +} + +static void kcm_done(struct kcm_sock *kcm) +{ + struct kcm_mux *mux = kcm->mux; + struct sock *sk = &kcm->sk; + int socks_cnt; + + spin_lock_bh(&mux->rx_lock); + if (kcm->rx_psock) { + /* Cleanup in unreserve_rx_kcm */ + WARN_ON(kcm->done); + kcm->rx_disabled = 1; + kcm->done = 1; + spin_unlock_bh(&mux->rx_lock); + return; + } + + if (kcm->rx_wait) { + list_del(&kcm->wait_rx_list); + kcm->rx_wait = false; + } + /* Move any pending receive messages to other kcm sockets */ + requeue_rx_msgs(mux, &sk->sk_receive_queue); + + spin_unlock_bh(&mux->rx_lock); + + if (WARN_ON(sk_rmem_alloc_get(sk))) + return; + + /* Detach from MUX */ + spin_lock_bh(&mux->lock); + + list_del(&kcm->kcm_sock_list); + mux->kcm_socks_cnt--; + socks_cnt = mux->kcm_socks_cnt; + + spin_unlock_bh(&mux->lock); + + if (!socks_cnt) { + /* We are done with the mux now. */ + release_mux(mux); + } + + WARN_ON(kcm->rx_wait); + + sock_put(&kcm->sk); +} + +/* Called by kcm_release to close a KCM socket. + * If this is the last KCM socket on the MUX, destroy the MUX. + */ +static int kcm_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct kcm_sock *kcm; + struct kcm_mux *mux; + struct kcm_psock *psock; + + if (!sk) + return 0; + + kcm = kcm_sk(sk); + mux = kcm->mux; + + sock_orphan(sk); + kfree_skb(kcm->seq_skb); + + lock_sock(sk); + /* Purge queue under lock to avoid race condition with tx_work trying + * to act when queue is nonempty. If tx_work runs after this point + * it will just return. + */ + __skb_queue_purge(&sk->sk_write_queue); + release_sock(sk); + + spin_lock_bh(&mux->lock); + if (kcm->tx_wait) { + /* Take of tx_wait list, after this point there should be no way + * that a psock will be assigned to this kcm. + */ + list_del(&kcm->wait_psock_list); + kcm->tx_wait = false; + } + spin_unlock_bh(&mux->lock); + + /* Cancel work. After this point there should be no outside references + * to the kcm socket. + */ + cancel_work_sync(&kcm->tx_work); + + lock_sock(sk); + psock = kcm->tx_psock; + if (psock) { + /* A psock was reserved, so we need to kill it since it + * may already have some bytes queued from a message. We + * need to do this after removing kcm from tx_wait list. + */ + kcm_abort_tx_psock(psock, EPIPE, false); + unreserve_psock(kcm); + } + release_sock(sk); + + WARN_ON(kcm->tx_wait); + WARN_ON(kcm->tx_psock); + + sock->sk = NULL; + + kcm_done(kcm); + + return 0; +} + +static const struct proto_ops kcm_ops = { + .family = PF_KCM, + .owner = THIS_MODULE, + .release = kcm_release, + .bind = sock_no_bind, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = datagram_poll, + .ioctl = kcm_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = kcm_setsockopt, + .getsockopt = kcm_getsockopt, + .sendmsg = kcm_sendmsg, + .recvmsg = kcm_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +/* Create proto operation for kcm sockets */ +static int kcm_create(struct net *net, struct socket *sock, + int protocol, int kern) +{ + struct kcm_net *knet = net_generic(net, kcm_net_id); + struct sock *sk; + struct kcm_mux *mux; + + switch (sock->type) { + case SOCK_DGRAM: + case SOCK_SEQPACKET: + sock->ops = &kcm_ops; + break; + default: + return -ESOCKTNOSUPPORT; + } + + if (protocol != KCMPROTO_CONNECTED) + return -EPROTONOSUPPORT; + + sk = sk_alloc(net, PF_KCM, GFP_KERNEL, &kcm_proto, kern); + if (!sk) + return -ENOMEM; + + /* Allocate a kcm mux, shared between KCM sockets */ + mux = kmem_cache_zalloc(kcm_muxp, GFP_KERNEL); + if (!mux) { + sk_free(sk); + return -ENOMEM; + } + + spin_lock_init(&mux->lock); + spin_lock_init(&mux->rx_lock); + INIT_LIST_HEAD(&mux->kcm_socks); + INIT_LIST_HEAD(&mux->kcm_rx_waiters); + INIT_LIST_HEAD(&mux->kcm_tx_waiters); + + INIT_LIST_HEAD(&mux->psocks); + INIT_LIST_HEAD(&mux->psocks_ready); + INIT_LIST_HEAD(&mux->psocks_avail); + + mux->knet = knet; + + /* Add new MUX to list */ + mutex_lock(&knet->mutex); + list_add_rcu(&mux->kcm_mux_list, &knet->mux_list); + knet->count++; + mutex_unlock(&knet->mutex); + + skb_queue_head_init(&mux->rx_hold_queue); + + /* Init KCM socket */ + sock_init_data(sock, sk); + init_kcm_sock(kcm_sk(sk), mux); + + return 0; +} + +static struct net_proto_family kcm_family_ops = { + .family = PF_KCM, + .create = kcm_create, + .owner = THIS_MODULE, +}; + +static __net_init int kcm_init_net(struct net *net) +{ + struct kcm_net *knet = net_generic(net, kcm_net_id); + + INIT_LIST_HEAD_RCU(&knet->mux_list); + mutex_init(&knet->mutex); + + return 0; +} + +static __net_exit void kcm_exit_net(struct net *net) +{ + struct kcm_net *knet = net_generic(net, kcm_net_id); + + /* All KCM sockets should be closed at this point, which should mean + * that all multiplexors and psocks have been destroyed. + */ + WARN_ON(!list_empty(&knet->mux_list)); +} + +static struct pernet_operations kcm_net_ops = { + .init = kcm_init_net, + .exit = kcm_exit_net, + .id = &kcm_net_id, + .size = sizeof(struct kcm_net), +}; + +static int __init kcm_init(void) +{ + int err = -ENOMEM; + + kcm_muxp = kmem_cache_create("kcm_mux_cache", + sizeof(struct kcm_mux), 0, + SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); + if (!kcm_muxp) + goto fail; + + kcm_psockp = kmem_cache_create("kcm_psock_cache", + sizeof(struct kcm_psock), 0, + SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); + if (!kcm_psockp) + goto fail; + + kcm_wq = create_singlethread_workqueue("kkcmd"); + if (!kcm_wq) + goto fail; + + err = proto_register(&kcm_proto, 1); + if (err) + goto fail; + + err = sock_register(&kcm_family_ops); + if (err) + goto sock_register_fail; + + err = register_pernet_device(&kcm_net_ops); + if (err) + goto net_ops_fail; + + return 0; + +net_ops_fail: + sock_unregister(PF_KCM); + +sock_register_fail: + proto_unregister(&kcm_proto); + +fail: + kmem_cache_destroy(kcm_muxp); + kmem_cache_destroy(kcm_psockp); + + if (kcm_wq) + destroy_workqueue(kcm_wq); + + return err; +} + +static void __exit kcm_exit(void) +{ + unregister_pernet_device(&kcm_net_ops); + sock_unregister(PF_KCM); + proto_unregister(&kcm_proto); + destroy_workqueue(kcm_wq); + + kmem_cache_destroy(kcm_muxp); + kmem_cache_destroy(kcm_psockp); +} + +module_init(kcm_init); +module_exit(kcm_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(PF_KCM); + diff --git a/net/qrtr/Kconfig b/net/qrtr/Kconfig new file mode 100644 index 000000000000..b83c6807a5ae --- /dev/null +++ b/net/qrtr/Kconfig @@ -0,0 +1,24 @@ +# Qualcomm IPC Router configuration +# + +config QRTR + tristate "Qualcomm IPC Router support" + depends on ARCH_QCOM || COMPILE_TEST + ---help--- + Say Y if you intend to use Qualcomm IPC router protocol. The + protocol is used to communicate with services provided by other + hardware blocks in the system. + + In order to do service lookups, a userspace daemon is required to + maintain a service listing. + +if QRTR + +config QRTR_SMD + tristate "SMD IPC Router channels" + depends on QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n) + ---help--- + Say Y here to support SMD based ipcrouter channels. SMD is the + most common transport for IPC Router. + +endif # QRTR diff --git a/net/qrtr/Makefile b/net/qrtr/Makefile new file mode 100644 index 000000000000..ab09e40f7c74 --- /dev/null +++ b/net/qrtr/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_QRTR) := qrtr.o + +obj-$(CONFIG_QRTR_SMD) += qrtr-smd.o +qrtr-smd-y := smd.o diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c new file mode 100644 index 000000000000..c985ecbe9bd6 --- /dev/null +++ b/net/qrtr/qrtr.c @@ -0,0 +1,1007 @@ +/* + * Copyright (c) 2015, Sony Mobile Communications Inc. + * Copyright (c) 2013, The Linux Foundation. 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 and + * only 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 +#include +#include +#include /* For TIOCINQ/OUTQ */ + +#include + +#include "qrtr.h" + +#define QRTR_PROTO_VER 1 + +/* auto-bind range */ +#define QRTR_MIN_EPH_SOCKET 0x4000 +#define QRTR_MAX_EPH_SOCKET 0x7fff + +enum qrtr_pkt_type { + QRTR_TYPE_DATA = 1, + QRTR_TYPE_HELLO = 2, + QRTR_TYPE_BYE = 3, + QRTR_TYPE_NEW_SERVER = 4, + QRTR_TYPE_DEL_SERVER = 5, + QRTR_TYPE_DEL_CLIENT = 6, + QRTR_TYPE_RESUME_TX = 7, + QRTR_TYPE_EXIT = 8, + QRTR_TYPE_PING = 9, +}; + +/** + * struct qrtr_hdr - (I|R)PCrouter packet header + * @version: protocol version + * @type: packet type; one of QRTR_TYPE_* + * @src_node_id: source node + * @src_port_id: source port + * @confirm_rx: boolean; whether a resume-tx packet should be send in reply + * @size: length of packet, excluding this header + * @dst_node_id: destination node + * @dst_port_id: destination port + */ +struct qrtr_hdr { + __le32 version; + __le32 type; + __le32 src_node_id; + __le32 src_port_id; + __le32 confirm_rx; + __le32 size; + __le32 dst_node_id; + __le32 dst_port_id; +} __packed; + +#define QRTR_HDR_SIZE sizeof(struct qrtr_hdr) +#define QRTR_NODE_BCAST ((unsigned int)-1) +#define QRTR_PORT_CTRL ((unsigned int)-2) + +struct qrtr_sock { + /* WARNING: sk must be the first member */ + struct sock sk; + struct sockaddr_qrtr us; + struct sockaddr_qrtr peer; +}; + +static inline struct qrtr_sock *qrtr_sk(struct sock *sk) +{ + BUILD_BUG_ON(offsetof(struct qrtr_sock, sk) != 0); + return container_of(sk, struct qrtr_sock, sk); +} + +static unsigned int qrtr_local_nid = -1; + +/* for node ids */ +static RADIX_TREE(qrtr_nodes, GFP_KERNEL); +/* broadcast list */ +static LIST_HEAD(qrtr_all_nodes); +/* lock for qrtr_nodes, qrtr_all_nodes and node reference */ +static DEFINE_MUTEX(qrtr_node_lock); + +/* local port allocation management */ +static DEFINE_IDR(qrtr_ports); +static DEFINE_MUTEX(qrtr_port_lock); + +/** + * struct qrtr_node - endpoint node + * @ep_lock: lock for endpoint management and callbacks + * @ep: endpoint + * @ref: reference count for node + * @nid: node id + * @rx_queue: receive queue + * @work: scheduled work struct for recv work + * @item: list item for broadcast list + */ +struct qrtr_node { + struct mutex ep_lock; + struct qrtr_endpoint *ep; + struct kref ref; + unsigned int nid; + + struct sk_buff_head rx_queue; + struct work_struct work; + struct list_head item; +}; + +/* Release node resources and free the node. + * + * Do not call directly, use qrtr_node_release. To be used with + * kref_put_mutex. As such, the node mutex is expected to be locked on call. + */ +static void __qrtr_node_release(struct kref *kref) +{ + struct qrtr_node *node = container_of(kref, struct qrtr_node, ref); + + if (node->nid != QRTR_EP_NID_AUTO) + radix_tree_delete(&qrtr_nodes, node->nid); + + list_del(&node->item); + mutex_unlock(&qrtr_node_lock); + + skb_queue_purge(&node->rx_queue); + kfree(node); +} + +/* Increment reference to node. */ +static struct qrtr_node *qrtr_node_acquire(struct qrtr_node *node) +{ + if (node) + kref_get(&node->ref); + return node; +} + +/* Decrement reference to node and release as necessary. */ +static void qrtr_node_release(struct qrtr_node *node) +{ + if (!node) + return; + kref_put_mutex(&node->ref, __qrtr_node_release, &qrtr_node_lock); +} + +/* Pass an outgoing packet socket buffer to the endpoint driver. */ +static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb) +{ + int rc = -ENODEV; + + mutex_lock(&node->ep_lock); + if (node->ep) + rc = node->ep->xmit(node->ep, skb); + else + kfree_skb(skb); + mutex_unlock(&node->ep_lock); + + return rc; +} + +/* Lookup node by id. + * + * callers must release with qrtr_node_release() + */ +static struct qrtr_node *qrtr_node_lookup(unsigned int nid) +{ + struct qrtr_node *node; + + mutex_lock(&qrtr_node_lock); + node = radix_tree_lookup(&qrtr_nodes, nid); + node = qrtr_node_acquire(node); + mutex_unlock(&qrtr_node_lock); + + return node; +} + +/* Assign node id to node. + * + * This is mostly useful for automatic node id assignment, based on + * the source id in the incoming packet. + */ +static void qrtr_node_assign(struct qrtr_node *node, unsigned int nid) +{ + if (node->nid != QRTR_EP_NID_AUTO || nid == QRTR_EP_NID_AUTO) + return; + + mutex_lock(&qrtr_node_lock); + radix_tree_insert(&qrtr_nodes, nid, node); + node->nid = nid; + mutex_unlock(&qrtr_node_lock); +} + +/** + * qrtr_endpoint_post() - post incoming data + * @ep: endpoint handle + * @data: data pointer + * @len: size of data in bytes + * + * Return: 0 on success; negative error code on failure + */ +int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) +{ + struct qrtr_node *node = ep->node; + const struct qrtr_hdr *phdr = data; + struct sk_buff *skb; + unsigned int psize; + unsigned int size; + unsigned int type; + unsigned int ver; + unsigned int dst; + + if (len < QRTR_HDR_SIZE || len & 3) + return -EINVAL; + + ver = le32_to_cpu(phdr->version); + size = le32_to_cpu(phdr->size); + type = le32_to_cpu(phdr->type); + dst = le32_to_cpu(phdr->dst_port_id); + + psize = (size + 3) & ~3; + + if (ver != QRTR_PROTO_VER) + return -EINVAL; + + if (len != psize + QRTR_HDR_SIZE) + return -EINVAL; + + if (dst != QRTR_PORT_CTRL && type != QRTR_TYPE_DATA) + return -EINVAL; + + skb = netdev_alloc_skb(NULL, len); + if (!skb) + return -ENOMEM; + + skb_reset_transport_header(skb); + memcpy(skb_put(skb, len), data, len); + + skb_queue_tail(&node->rx_queue, skb); + schedule_work(&node->work); + + return 0; +} +EXPORT_SYMBOL_GPL(qrtr_endpoint_post); + +/* Allocate and construct a resume-tx packet. */ +static struct sk_buff *qrtr_alloc_resume_tx(u32 src_node, + u32 dst_node, u32 port) +{ + const int pkt_len = 20; + struct qrtr_hdr *hdr; + struct sk_buff *skb; + u32 *buf; + + skb = alloc_skb(QRTR_HDR_SIZE + pkt_len, GFP_KERNEL); + if (!skb) + return NULL; + skb_reset_transport_header(skb); + + hdr = (struct qrtr_hdr *)skb_put(skb, QRTR_HDR_SIZE); + hdr->version = cpu_to_le32(QRTR_PROTO_VER); + hdr->type = cpu_to_le32(QRTR_TYPE_RESUME_TX); + hdr->src_node_id = cpu_to_le32(src_node); + hdr->src_port_id = cpu_to_le32(QRTR_PORT_CTRL); + hdr->confirm_rx = cpu_to_le32(0); + hdr->size = cpu_to_le32(pkt_len); + hdr->dst_node_id = cpu_to_le32(dst_node); + hdr->dst_port_id = cpu_to_le32(QRTR_PORT_CTRL); + + buf = (u32 *)skb_put(skb, pkt_len); + memset(buf, 0, pkt_len); + buf[0] = cpu_to_le32(QRTR_TYPE_RESUME_TX); + buf[1] = cpu_to_le32(src_node); + buf[2] = cpu_to_le32(port); + + return skb; +} + +static struct qrtr_sock *qrtr_port_lookup(int port); +static void qrtr_port_put(struct qrtr_sock *ipc); + +/* Handle and route a received packet. + * + * This will auto-reply with resume-tx packet as necessary. + */ +static void qrtr_node_rx_work(struct work_struct *work) +{ + struct qrtr_node *node = container_of(work, struct qrtr_node, work); + struct sk_buff *skb; + + while ((skb = skb_dequeue(&node->rx_queue)) != NULL) { + const struct qrtr_hdr *phdr; + u32 dst_node, dst_port; + struct qrtr_sock *ipc; + u32 src_node; + int confirm; + + phdr = (const struct qrtr_hdr *)skb_transport_header(skb); + src_node = le32_to_cpu(phdr->src_node_id); + dst_node = le32_to_cpu(phdr->dst_node_id); + dst_port = le32_to_cpu(phdr->dst_port_id); + confirm = !!phdr->confirm_rx; + + qrtr_node_assign(node, src_node); + + ipc = qrtr_port_lookup(dst_port); + if (!ipc) { + kfree_skb(skb); + } else { + if (sock_queue_rcv_skb(&ipc->sk, skb)) + kfree_skb(skb); + + qrtr_port_put(ipc); + } + + if (confirm) { + skb = qrtr_alloc_resume_tx(dst_node, node->nid, dst_port); + if (!skb) + break; + if (qrtr_node_enqueue(node, skb)) + break; + } + } +} + +/** + * qrtr_endpoint_register() - register a new endpoint + * @ep: endpoint to register + * @nid: desired node id; may be QRTR_EP_NID_AUTO for auto-assignment + * Return: 0 on success; negative error code on failure + * + * The specified endpoint must have the xmit function pointer set on call. + */ +int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid) +{ + struct qrtr_node *node; + + if (!ep || !ep->xmit) + return -EINVAL; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + INIT_WORK(&node->work, qrtr_node_rx_work); + kref_init(&node->ref); + mutex_init(&node->ep_lock); + skb_queue_head_init(&node->rx_queue); + node->nid = QRTR_EP_NID_AUTO; + node->ep = ep; + + qrtr_node_assign(node, nid); + + mutex_lock(&qrtr_node_lock); + list_add(&node->item, &qrtr_all_nodes); + mutex_unlock(&qrtr_node_lock); + ep->node = node; + + return 0; +} +EXPORT_SYMBOL_GPL(qrtr_endpoint_register); + +/** + * qrtr_endpoint_unregister - unregister endpoint + * @ep: endpoint to unregister + */ +void qrtr_endpoint_unregister(struct qrtr_endpoint *ep) +{ + struct qrtr_node *node = ep->node; + + mutex_lock(&node->ep_lock); + node->ep = NULL; + mutex_unlock(&node->ep_lock); + + qrtr_node_release(node); + ep->node = NULL; +} +EXPORT_SYMBOL_GPL(qrtr_endpoint_unregister); + +/* Lookup socket by port. + * + * Callers must release with qrtr_port_put() + */ +static struct qrtr_sock *qrtr_port_lookup(int port) +{ + struct qrtr_sock *ipc; + + if (port == QRTR_PORT_CTRL) + port = 0; + + mutex_lock(&qrtr_port_lock); + ipc = idr_find(&qrtr_ports, port); + if (ipc) + sock_hold(&ipc->sk); + mutex_unlock(&qrtr_port_lock); + + return ipc; +} + +/* Release acquired socket. */ +static void qrtr_port_put(struct qrtr_sock *ipc) +{ + sock_put(&ipc->sk); +} + +/* Remove port assignment. */ +static void qrtr_port_remove(struct qrtr_sock *ipc) +{ + int port = ipc->us.sq_port; + + if (port == QRTR_PORT_CTRL) + port = 0; + + __sock_put(&ipc->sk); + + mutex_lock(&qrtr_port_lock); + idr_remove(&qrtr_ports, port); + mutex_unlock(&qrtr_port_lock); +} + +/* Assign port number to socket. + * + * Specify port in the integer pointed to by port, and it will be adjusted + * on return as necesssary. + * + * Port may be: + * 0: Assign ephemeral port in [QRTR_MIN_EPH_SOCKET, QRTR_MAX_EPH_SOCKET] + * QRTR_MIN_EPH_SOCKET: Specified; available to all + */ +static int qrtr_port_assign(struct qrtr_sock *ipc, int *port) +{ + int rc; + + mutex_lock(&qrtr_port_lock); + if (!*port) { + rc = idr_alloc(&qrtr_ports, ipc, + QRTR_MIN_EPH_SOCKET, QRTR_MAX_EPH_SOCKET + 1, + GFP_ATOMIC); + if (rc >= 0) + *port = rc; + } else if (*port < QRTR_MIN_EPH_SOCKET && !capable(CAP_NET_ADMIN)) { + rc = -EACCES; + } else if (*port == QRTR_PORT_CTRL) { + rc = idr_alloc(&qrtr_ports, ipc, 0, 1, GFP_ATOMIC); + } else { + rc = idr_alloc(&qrtr_ports, ipc, *port, *port + 1, GFP_ATOMIC); + if (rc >= 0) + *port = rc; + } + mutex_unlock(&qrtr_port_lock); + + if (rc == -ENOSPC) + return -EADDRINUSE; + else if (rc < 0) + return rc; + + sock_hold(&ipc->sk); + + return 0; +} + +/* Bind socket to address. + * + * Socket should be locked upon call. + */ +static int __qrtr_bind(struct socket *sock, + const struct sockaddr_qrtr *addr, int zapped) +{ + struct qrtr_sock *ipc = qrtr_sk(sock->sk); + struct sock *sk = sock->sk; + int port; + int rc; + + /* rebinding ok */ + if (!zapped && addr->sq_port == ipc->us.sq_port) + return 0; + + port = addr->sq_port; + rc = qrtr_port_assign(ipc, &port); + if (rc) + return rc; + + /* unbind previous, if any */ + if (!zapped) + qrtr_port_remove(ipc); + ipc->us.sq_port = port; + + sock_reset_flag(sk, SOCK_ZAPPED); + + return 0; +} + +/* Auto bind to an ephemeral port. */ +static int qrtr_autobind(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct sockaddr_qrtr addr; + + if (!sock_flag(sk, SOCK_ZAPPED)) + return 0; + + addr.sq_family = AF_QIPCRTR; + addr.sq_node = qrtr_local_nid; + addr.sq_port = 0; + + return __qrtr_bind(sock, &addr, 1); +} + +/* Bind socket to specified sockaddr. */ +static int qrtr_bind(struct socket *sock, struct sockaddr *saddr, int len) +{ + DECLARE_SOCKADDR(struct sockaddr_qrtr *, addr, saddr); + struct qrtr_sock *ipc = qrtr_sk(sock->sk); + struct sock *sk = sock->sk; + int rc; + + if (len < sizeof(*addr) || addr->sq_family != AF_QIPCRTR) + return -EINVAL; + + if (addr->sq_node != ipc->us.sq_node) + return -EINVAL; + + lock_sock(sk); + rc = __qrtr_bind(sock, addr, sock_flag(sk, SOCK_ZAPPED)); + release_sock(sk); + + return rc; +} + +/* Queue packet to local peer socket. */ +static int qrtr_local_enqueue(struct qrtr_node *node, struct sk_buff *skb) +{ + const struct qrtr_hdr *phdr; + struct qrtr_sock *ipc; + + phdr = (const struct qrtr_hdr *)skb_transport_header(skb); + + ipc = qrtr_port_lookup(le32_to_cpu(phdr->dst_port_id)); + if (!ipc || &ipc->sk == skb->sk) { /* do not send to self */ + kfree_skb(skb); + return -ENODEV; + } + + if (sock_queue_rcv_skb(&ipc->sk, skb)) { + qrtr_port_put(ipc); + kfree_skb(skb); + return -ENOSPC; + } + + qrtr_port_put(ipc); + + return 0; +} + +/* Queue packet for broadcast. */ +static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb) +{ + struct sk_buff *skbn; + + mutex_lock(&qrtr_node_lock); + list_for_each_entry(node, &qrtr_all_nodes, item) { + skbn = skb_clone(skb, GFP_KERNEL); + if (!skbn) + break; + skb_set_owner_w(skbn, skb->sk); + qrtr_node_enqueue(node, skbn); + } + mutex_unlock(&qrtr_node_lock); + + qrtr_local_enqueue(node, skb); + + return 0; +} + +static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) +{ + DECLARE_SOCKADDR(struct sockaddr_qrtr *, addr, msg->msg_name); + int (*enqueue_fn)(struct qrtr_node *, struct sk_buff *); + struct qrtr_sock *ipc = qrtr_sk(sock->sk); + struct sock *sk = sock->sk; + struct qrtr_node *node; + struct qrtr_hdr *hdr; + struct sk_buff *skb; + size_t plen; + int rc; + + if (msg->msg_flags & ~(MSG_DONTWAIT)) + return -EINVAL; + + if (len > 65535) + return -EMSGSIZE; + + lock_sock(sk); + + if (addr) { + if (msg->msg_namelen < sizeof(*addr)) { + release_sock(sk); + return -EINVAL; + } + + if (addr->sq_family != AF_QIPCRTR) { + release_sock(sk); + return -EINVAL; + } + + rc = qrtr_autobind(sock); + if (rc) { + release_sock(sk); + return rc; + } + } else if (sk->sk_state == TCP_ESTABLISHED) { + addr = &ipc->peer; + } else { + release_sock(sk); + return -ENOTCONN; + } + + node = NULL; + if (addr->sq_node == QRTR_NODE_BCAST) { + enqueue_fn = qrtr_bcast_enqueue; + } else if (addr->sq_node == ipc->us.sq_node) { + enqueue_fn = qrtr_local_enqueue; + } else { + enqueue_fn = qrtr_node_enqueue; + node = qrtr_node_lookup(addr->sq_node); + if (!node) { + release_sock(sk); + return -ECONNRESET; + } + } + + plen = (len + 3) & ~3; + skb = sock_alloc_send_skb(sk, plen + QRTR_HDR_SIZE, + msg->msg_flags & MSG_DONTWAIT, &rc); + if (!skb) + goto out_node; + + skb_reset_transport_header(skb); + skb_put(skb, len + QRTR_HDR_SIZE); + + hdr = (struct qrtr_hdr *)skb_transport_header(skb); + hdr->version = cpu_to_le32(QRTR_PROTO_VER); + hdr->src_node_id = cpu_to_le32(ipc->us.sq_node); + hdr->src_port_id = cpu_to_le32(ipc->us.sq_port); + hdr->confirm_rx = cpu_to_le32(0); + hdr->size = cpu_to_le32(len); + hdr->dst_node_id = cpu_to_le32(addr->sq_node); + hdr->dst_port_id = cpu_to_le32(addr->sq_port); + + rc = skb_copy_datagram_from_iter(skb, QRTR_HDR_SIZE, + &msg->msg_iter, len); + if (rc) { + kfree_skb(skb); + goto out_node; + } + + if (plen != len) { + skb_pad(skb, plen - len); + skb_put(skb, plen - len); + } + + if (ipc->us.sq_port == QRTR_PORT_CTRL) { + if (len < 4) { + rc = -EINVAL; + kfree_skb(skb); + goto out_node; + } + + /* control messages already require the type as 'command' */ + skb_copy_bits(skb, QRTR_HDR_SIZE, &hdr->type, 4); + } else { + hdr->type = cpu_to_le32(QRTR_TYPE_DATA); + } + + rc = enqueue_fn(node, skb); + if (rc >= 0) + rc = len; + +out_node: + qrtr_node_release(node); + release_sock(sk); + + return rc; +} + +static int qrtr_recvmsg(struct socket *sock, struct msghdr *msg, + size_t size, int flags) +{ + DECLARE_SOCKADDR(struct sockaddr_qrtr *, addr, msg->msg_name); + const struct qrtr_hdr *phdr; + struct sock *sk = sock->sk; + struct sk_buff *skb; + int copied, rc; + + lock_sock(sk); + + if (sock_flag(sk, SOCK_ZAPPED)) { + release_sock(sk); + return -EADDRNOTAVAIL; + } + + skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, + flags & MSG_DONTWAIT, &rc); + if (!skb) { + release_sock(sk); + return rc; + } + + phdr = (const struct qrtr_hdr *)skb_transport_header(skb); + copied = le32_to_cpu(phdr->size); + if (copied > size) { + copied = size; + msg->msg_flags |= MSG_TRUNC; + } + + rc = skb_copy_datagram_msg(skb, QRTR_HDR_SIZE, msg, copied); + if (rc < 0) + goto out; + rc = copied; + + if (addr) { + addr->sq_family = AF_QIPCRTR; + addr->sq_node = le32_to_cpu(phdr->src_node_id); + addr->sq_port = le32_to_cpu(phdr->src_port_id); + msg->msg_namelen = sizeof(*addr); + } + +out: + skb_free_datagram(sk, skb); + release_sock(sk); + + return rc; +} + +static int qrtr_connect(struct socket *sock, struct sockaddr *saddr, + int len, int flags) +{ + DECLARE_SOCKADDR(struct sockaddr_qrtr *, addr, saddr); + struct qrtr_sock *ipc = qrtr_sk(sock->sk); + struct sock *sk = sock->sk; + int rc; + + if (len < sizeof(*addr) || addr->sq_family != AF_QIPCRTR) + return -EINVAL; + + lock_sock(sk); + + sk->sk_state = TCP_CLOSE; + sock->state = SS_UNCONNECTED; + + rc = qrtr_autobind(sock); + if (rc) { + release_sock(sk); + return rc; + } + + ipc->peer = *addr; + sock->state = SS_CONNECTED; + sk->sk_state = TCP_ESTABLISHED; + + release_sock(sk); + + return 0; +} + +static int qrtr_getname(struct socket *sock, struct sockaddr *saddr, + int *len, int peer) +{ + struct qrtr_sock *ipc = qrtr_sk(sock->sk); + struct sockaddr_qrtr qaddr; + struct sock *sk = sock->sk; + + lock_sock(sk); + if (peer) { + if (sk->sk_state != TCP_ESTABLISHED) { + release_sock(sk); + return -ENOTCONN; + } + + qaddr = ipc->peer; + } else { + qaddr = ipc->us; + } + release_sock(sk); + + *len = sizeof(qaddr); + qaddr.sq_family = AF_QIPCRTR; + + memcpy(saddr, &qaddr, sizeof(qaddr)); + + return 0; +} + +static int qrtr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct qrtr_sock *ipc = qrtr_sk(sock->sk); + struct sock *sk = sock->sk; + struct sockaddr_qrtr *sq; + struct sk_buff *skb; + struct ifreq ifr; + long len = 0; + int rc = 0; + + lock_sock(sk); + + switch (cmd) { + case TIOCOUTQ: + len = sk->sk_sndbuf - sk_wmem_alloc_get(sk); + if (len < 0) + len = 0; + rc = put_user(len, (int __user *)argp); + break; + case TIOCINQ: + skb = skb_peek(&sk->sk_receive_queue); + if (skb) + len = skb->len - QRTR_HDR_SIZE; + rc = put_user(len, (int __user *)argp); + break; + case SIOCGIFADDR: + if (copy_from_user(&ifr, argp, sizeof(ifr))) { + rc = -EFAULT; + break; + } + + sq = (struct sockaddr_qrtr *)&ifr.ifr_addr; + *sq = ipc->us; + if (copy_to_user(argp, &ifr, sizeof(ifr))) { + rc = -EFAULT; + break; + } + break; + case SIOCGSTAMP: + rc = sock_get_timestamp(sk, argp); + break; + case SIOCADDRT: + case SIOCDELRT: + case SIOCSIFADDR: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + rc = -EINVAL; + break; + default: + rc = -ENOIOCTLCMD; + break; + } + + release_sock(sk); + + return rc; +} + +static int qrtr_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct qrtr_sock *ipc; + + if (!sk) + return 0; + + lock_sock(sk); + + ipc = qrtr_sk(sk); + sk->sk_shutdown = SHUTDOWN_MASK; + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_state_change(sk); + + sock_set_flag(sk, SOCK_DEAD); + sock->sk = NULL; + + if (!sock_flag(sk, SOCK_ZAPPED)) + qrtr_port_remove(ipc); + + skb_queue_purge(&sk->sk_receive_queue); + + release_sock(sk); + sock_put(sk); + + return 0; +} + +static const struct proto_ops qrtr_proto_ops = { + .owner = THIS_MODULE, + .family = AF_QIPCRTR, + .bind = qrtr_bind, + .connect = qrtr_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .listen = sock_no_listen, + .sendmsg = qrtr_sendmsg, + .recvmsg = qrtr_recvmsg, + .getname = qrtr_getname, + .ioctl = qrtr_ioctl, + .poll = datagram_poll, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .release = qrtr_release, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static struct proto qrtr_proto = { + .name = "QIPCRTR", + .owner = THIS_MODULE, + .obj_size = sizeof(struct qrtr_sock), +}; + +static int qrtr_create(struct net *net, struct socket *sock, + int protocol, int kern) +{ + struct qrtr_sock *ipc; + struct sock *sk; + + if (sock->type != SOCK_DGRAM) + return -EPROTOTYPE; + + sk = sk_alloc(net, AF_QIPCRTR, GFP_KERNEL, &qrtr_proto, kern); + if (!sk) + return -ENOMEM; + + sock_set_flag(sk, SOCK_ZAPPED); + + sock_init_data(sock, sk); + sock->ops = &qrtr_proto_ops; + + ipc = qrtr_sk(sk); + ipc->us.sq_family = AF_QIPCRTR; + ipc->us.sq_node = qrtr_local_nid; + ipc->us.sq_port = 0; + + return 0; +} + +static const struct nla_policy qrtr_policy[IFA_MAX + 1] = { + [IFA_LOCAL] = { .type = NLA_U32 }, +}; + +static int qrtr_addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + struct nlattr *tb[IFA_MAX + 1]; + struct ifaddrmsg *ifm; + int rc; + + if (!netlink_capable(skb, CAP_NET_ADMIN)) + return -EPERM; + + if (!netlink_capable(skb, CAP_SYS_ADMIN)) + return -EPERM; + + ASSERT_RTNL(); + + rc = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, qrtr_policy); + if (rc < 0) + return rc; + + ifm = nlmsg_data(nlh); + if (!tb[IFA_LOCAL]) + return -EINVAL; + + qrtr_local_nid = nla_get_u32(tb[IFA_LOCAL]); + return 0; +} + +static const struct net_proto_family qrtr_family = { + .owner = THIS_MODULE, + .family = AF_QIPCRTR, + .create = qrtr_create, +}; + +static int __init qrtr_proto_init(void) +{ + int rc; + + rc = proto_register(&qrtr_proto, 1); + if (rc) + return rc; + + rc = sock_register(&qrtr_family); + if (rc) { + proto_unregister(&qrtr_proto); + return rc; + } + + rtnl_register(PF_QIPCRTR, RTM_NEWADDR, qrtr_addr_doit, NULL, NULL); + + return 0; +} +module_init(qrtr_proto_init); + +static void __exit qrtr_proto_fini(void) +{ + rtnl_unregister(PF_QIPCRTR, RTM_NEWADDR); + sock_unregister(qrtr_family.family); + proto_unregister(&qrtr_proto); +} +module_exit(qrtr_proto_fini); + +MODULE_DESCRIPTION("Qualcomm IPC-router driver"); +MODULE_LICENSE("GPL v2"); diff --git a/net/qrtr/qrtr.h b/net/qrtr/qrtr.h new file mode 100644 index 000000000000..2b848718f8fe --- /dev/null +++ b/net/qrtr/qrtr.h @@ -0,0 +1,31 @@ +#ifndef __QRTR_H_ +#define __QRTR_H_ + +#include + +struct sk_buff; + +/* endpoint node id auto assignment */ +#define QRTR_EP_NID_AUTO (-1) + +/** + * struct qrtr_endpoint - endpoint handle + * @xmit: Callback for outgoing packets + * + * The socket buffer passed to the xmit function becomes owned by the endpoint + * driver. As such, when the driver is done with the buffer, it should + * call kfree_skb() on failure, or consume_skb() on success. + */ +struct qrtr_endpoint { + int (*xmit)(struct qrtr_endpoint *ep, struct sk_buff *skb); + /* private: not for endpoint use */ + struct qrtr_node *node; +}; + +int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid); + +void qrtr_endpoint_unregister(struct qrtr_endpoint *ep); + +int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len); + +#endif diff --git a/net/qrtr/smd.c b/net/qrtr/smd.c new file mode 100644 index 000000000000..0d11132b3370 --- /dev/null +++ b/net/qrtr/smd.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2015, Sony Mobile Communications Inc. + * Copyright (c) 2013, The Linux Foundation. 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 and + * only 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 +#include +#include + +#include "qrtr.h" + +struct qrtr_smd_dev { + struct qrtr_endpoint ep; + struct qcom_smd_channel *channel; + struct device *dev; +}; + +/* from smd to qrtr */ +static int qcom_smd_qrtr_callback(struct qcom_smd_channel *channel, + const void *data, size_t len) +{ + struct qrtr_smd_dev *qdev = qcom_smd_get_drvdata(channel); + int rc; + + if (!qdev) + return -EAGAIN; + + rc = qrtr_endpoint_post(&qdev->ep, data, len); + if (rc == -EINVAL) { + dev_err(qdev->dev, "invalid ipcrouter packet\n"); + /* return 0 to let smd drop the packet */ + rc = 0; + } + + return rc; +} + +/* from qrtr to smd */ +static int qcom_smd_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb) +{ + struct qrtr_smd_dev *qdev = container_of(ep, struct qrtr_smd_dev, ep); + int rc; + + rc = skb_linearize(skb); + if (rc) + goto out; + + rc = qcom_smd_send(qdev->channel, skb->data, skb->len); + +out: + if (rc) + kfree_skb(skb); + else + consume_skb(skb); + return rc; +} + +static int qcom_smd_qrtr_probe(struct qcom_smd_device *sdev) +{ + struct qrtr_smd_dev *qdev; + int rc; + + qdev = devm_kzalloc(&sdev->dev, sizeof(*qdev), GFP_KERNEL); + if (!qdev) + return -ENOMEM; + + qdev->channel = sdev->channel; + qdev->dev = &sdev->dev; + qdev->ep.xmit = qcom_smd_qrtr_send; + + rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); + if (rc) + return rc; + + qcom_smd_set_drvdata(sdev->channel, qdev); + dev_set_drvdata(&sdev->dev, qdev); + + dev_dbg(&sdev->dev, "Qualcomm SMD QRTR driver probed\n"); + + return 0; +} + +static void qcom_smd_qrtr_remove(struct qcom_smd_device *sdev) +{ + struct qrtr_smd_dev *qdev = dev_get_drvdata(&sdev->dev); + + qrtr_endpoint_unregister(&qdev->ep); + + dev_set_drvdata(&sdev->dev, NULL); +} + +static const struct qcom_smd_id qcom_smd_qrtr_smd_match[] = { + { "IPCRTR" }, + {} +}; + +static struct qcom_smd_driver qcom_smd_qrtr_driver = { + .probe = qcom_smd_qrtr_probe, + .remove = qcom_smd_qrtr_remove, + .callback = qcom_smd_qrtr_callback, + .smd_match_table = qcom_smd_qrtr_smd_match, + .driver = { + .name = "qcom_smd_qrtr", + .owner = THIS_MODULE, + }, +}; + +module_qcom_smd_driver(qcom_smd_qrtr_driver); + +MODULE_DESCRIPTION("Qualcomm IPC-Router SMD interface driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c20d2d3d9464..258080c36643 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -501,7 +501,7 @@ config SND_SOC_MAX98925 config SND_SOC_MAX9850 tristate -config SND_SOC_MSM8x16_WCD +config SND_SOC_MSM8916_WCD tristate "Qualcomm MSM8x16 WCD" depends on SPMI && MFD_SYSCON diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index afb8af97fd93..27aebb926ad5 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -74,9 +74,9 @@ 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-msm8916-objs := msm8916-wcd.o snd-soc-nau8825-objs := nau8825.o snd-soc-hdmi-codec-objs := hdmi-codec.o -snd-soc-msm8x16-objs := msm8x16-wcd.o msm8x16-wcd-tables.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1792a-codec-objs := pcm1792a.o snd-soc-pcm3008-objs := pcm3008.o @@ -270,9 +270,9 @@ 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_MSM8916_WCD) +=snd-soc-msm8916.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o -obj-$(CONFIG_SND_SOC_MSM8x16_WCD) +=snd-soc-msm8x16.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 diff --git a/sound/soc/codecs/msm8916-wcd-registers.h b/sound/soc/codecs/msm8916-wcd-registers.h new file mode 100644 index 000000000000..88966038654d --- /dev/null +++ b/sound/soc/codecs/msm8916-wcd-registers.h @@ -0,0 +1,710 @@ + /* Copyright (c) 2016, The Linux Foundation. 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 and + * only 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. + */ +#ifndef MSM8916_WCD_REGISTERS_H +#define MSM8916_WCD_REGISTERS_H + +#define CDC_D_REVISION1 (0x000) +#define CDC_D_REVISION1_POR (0x00) +#define CDC_D_REVISION2 (0x001) +#define CDC_D_REVISION2_POR (0x00) +#define CDC_D_PERPH_TYPE (0x004) +#define CDC_D_PERPH_TYPE_POR (0x23) +#define CDC_D_PERPH_SUBTYPE (0x005) +#define CDC_D_PERPH_SUBTYPE_POR (0x01) +#define CDC_D_INT_RT_STS (0x010) +#define CDC_D_INT_RT_STS_POR (0x00) +#define CDC_D_INT_SET_TYPE (0x011) +#define CDC_D_INT_SET_TYPE_POR (0xFF) +#define CDC_D_INT_POLARITY_HIGH (0x012) +#define CDC_D_INT_POLARITY_HIGH_POR (0xFF) +#define CDC_D_INT_POLARITY_LOW (0x013) +#define CDC_D_INT_POLARITY_LOW_POR (0x00) +#define CDC_D_INT_LATCHED_CLR (0x014) +#define CDC_D_INT_LATCHED_CLR_POR (0x00) +#define CDC_D_INT_EN_SET (0x015) +#define CDC_D_INT_EN_SET_POR (0x00) +#define CDC_D_INT_EN_CLR (0x016) +#define CDC_D_INT_EN_CLR_POR (0x00) +#define CDC_D_INT_LATCHED_STS (0x018) +#define CDC_D_INT_LATCHED_STS_POR (0x00) +#define CDC_D_INT_PENDING_STS (0x019) +#define CDC_D_INT_PENDING_STS_POR (0x00) +#define CDC_D_INT_MID_SEL (0x01A) +#define CDC_D_INT_MID_SEL_POR (0x00) +#define CDC_D_INT_PRIORITY (0x01B) +#define CDC_D_INT_PRIORITY_POR (0x00) +#define CDC_D_GPIO_MODE (0x040) +#define CDC_D_GPIO_MODE_POR (0x00) +#define CDC_D_PIN_CTL_OE (0x041) +#define CDC_D_PIN_CTL_OE_POR (0x01) +#define CDC_D_PIN_CTL_DATA (0x042) +#define CDC_D_PIN_CTL_DATA_POR (0x00) +#define CDC_D_PIN_STATUS (0x043) +#define CDC_D_PIN_STATUS_POR (0x00) +#define CDC_D_HDRIVE_CTL (0x044) +#define CDC_D_HDRIVE_CTL_POR (0x00) +#define CDC_D_CDC_RST_CTL (0x046) +#define RST_CTL_DIG_SW_RST_N_MASK BIT(7) +#define RST_CTL_DIG_SW_RST_N_RESET 0 +#define RST_CTL_DIG_SW_RST_N_REMOVE_RESET BIT(7) + +#define CDC_D_CDC_RST_CTL_POR (0x00) +#define CDC_D_CDC_TOP_CLK_CTL (0x048) +#define TOP_CLK_CTL_A_MCLK_MCLK2_EN_MASK (BIT(2) | BIT(3)) +#define TOP_CLK_CTL_A_MCLK_EN_ENABLE BIT(2) +#define TOP_CLK_CTL_A_MCLK2_EN_ENABLE BIT(3) + +#define CDC_D_CDC_TOP_CLK_CTL_POR (0x00) +#define CDC_D_CDC_ANA_CLK_CTL (0x049) +#define ANA_CLK_CTL_EAR_HPHR_CLK_EN_MASK BIT(0) +#define ANA_CLK_CTL_EAR_HPHR_CLK_EN BIT(0) +#define ANA_CLK_CTL_EAR_HPHL_CLK_EN BIT(1) +#define ANA_CLK_CTL_SPKR_CLK_EN_MASK BIT(4) +#define ANA_CLK_CTL_SPKR_CLK_EN BIT(4) +#define ANA_CLK_CTL_TXA_CLK25_EN BIT(5) + +#define CDC_D_CDC_ANA_CLK_CTL_POR (0x00) +#define CDC_D_CDC_DIG_CLK_CTL (0x04A) +#define DIG_CLK_CTL_RXD1_CLK_EN BIT(0) +#define DIG_CLK_CTL_RXD2_CLK_EN BIT(1) +#define DIG_CLK_CTL_RXD3_CLK_EN BIT(3) +#define DIG_CLK_CTL_TXD_CLK_EN BIT(4) +#define DIG_CLK_CTL_NCP_CLK_EN_MASK BIT(6) +#define DIG_CLK_CTL_NCP_CLK_EN BIT(6) +#define DIG_CLK_CTL_RXD_PDM_CLK_EN_MASK BIT(7) +#define DIG_CLK_CTL_RXD_PDM_CLK_EN BIT(7) + +#define CDC_D_CDC_DIG_CLK_CTL_POR (0x00) +#define CDC_D_CDC_CONN_TX1_CTL (0x050) +#define CONN_TX1_SERIAL_TX1_MUX GENMASK(1, 0) +#define CONN_TX1_SERIAL_TX1_ADC_1 0x0 +#define CONN_TX1_SERIAL_TX1_RX_PDM_LB 0x1 +#define CONN_TX1_SERIAL_TX1_ZERO 0x2 + +#define CDC_D_CDC_CONN_TX1_CTL_POR (0x02) +#define CDC_D_CDC_CONN_TX2_CTL (0x051) +#define CONN_TX2_SERIAL_TX2_MUX GENMASK(1, 0) +#define CONN_TX2_SERIAL_TX2_ADC_2 0x0 +#define CONN_TX2_SERIAL_TX2_RX_PDM_LB 0x1 +#define CONN_TX2_SERIAL_TX2_ZERO 0x2 +#define CDC_D_CDC_CONN_TX2_CTL_POR (0x02) +#define CDC_D_CDC_CONN_HPHR_DAC_CTL (0x052) +#define CDC_D_CDC_CONN_HPHR_DAC_CTL_POR (0x00) +#define CDC_D_CDC_CONN_RX1_CTL (0x053) +#define CDC_D_CDC_CONN_RX1_CTL_POR (0x00) +#define CDC_D_CDC_CONN_RX2_CTL (0x054) +#define CDC_D_CDC_CONN_RX2_CTL_POR (0x00) +#define CDC_D_CDC_CONN_RX3_CTL (0x055) +#define CDC_D_CDC_CONN_RX3_CTL_POR (0x00) +#define CDC_D_CDC_CONN_RX_LB_CTL (0x056) +#define CDC_D_CDC_CONN_RX_LB_CTL_POR (0x00) +#define CDC_D_CDC_RX_CTL1 (0x058) +#define CDC_D_CDC_RX_CTL1_POR (0x7C) +#define CDC_D_CDC_RX_CTL2 (0x059) +#define CDC_D_CDC_RX_CTL2_POR (0x7C) +#define CDC_D_CDC_RX_CTL3 (0x05A) +#define CDC_D_CDC_RX_CTL3_POR (0x7C) +#define CDC_D_DEM_BYPASS_DATA0 (0x05B) +#define CDC_D_DEM_BYPASS_DATA0_POR (0x00) +#define CDC_D_DEM_BYPASS_DATA1 (0x05C) +#define CDC_D_DEM_BYPASS_DATA1_POR (0x00) +#define CDC_D_DEM_BYPASS_DATA2 (0x05D) +#define CDC_D_DEM_BYPASS_DATA2_POR (0x00) +#define CDC_D_DEM_BYPASS_DATA3 (0x05E) +#define CDC_D_DEM_BYPASS_DATA3_POR (0x00) +#define CDC_D_DIG_DEBUG_CTL (0x068) +#define CDC_D_DIG_DEBUG_CTL_POR (0x00) +#define CDC_D_DIG_DEBUG_EN (0x069) +#define CDC_D_DIG_DEBUG_EN_POR (0x00) +#define CDC_D_SPARE_0 (0x070) +#define CDC_D_SPARE_0_POR (0x00) +#define CDC_D_SPARE_1 (0x071) +#define CDC_D_SPARE_1_POR (0x00) +#define CDC_D_SPARE_2 (0x072) +#define CDC_D_SPARE_2_POR (0x00) +#define CDC_D_SEC_ACCESS (0x0D0) +#define CDC_D_SEC_ACCESS_POR (0x00) +#define CDC_D_PERPH_RESET_CTL1 (0x0D8) +#define CDC_D_PERPH_RESET_CTL1_POR (0x00) +#define CDC_D_PERPH_RESET_CTL2 (0x0D9) +#define CDC_D_PERPH_RESET_CTL2_POR (0x01) +#define CDC_D_PERPH_RESET_CTL3 (0x0DA) +#define CDC_D_PERPH_RESET_CTL3_POR (0x05) +#define CDC_D_PERPH_RESET_CTL4 (0x0DB) +#define CDC_D_PERPH_RESET_CTL4_POR (0x00) +#define CDC_D_INT_TEST1 (0x0E0) +#define CDC_D_INT_TEST1_POR (0x00) +#define CDC_D_INT_TEST_VAL (0x0E1) +#define CDC_D_INT_TEST_VAL_POR (0x00) +#define CDC_D_TRIM_NUM (0x0F0) +#define CDC_D_TRIM_NUM_POR (0x00) +#define CDC_D_TRIM_CTRL (0x0F1) +#define CDC_D_TRIM_CTRL_POR (0x00) + +#define CDC_A_REVISION1 (0x100) +#define CDC_A_REVISION1_POR (0x00) +#define CDC_A_REVISION2 (0x101) +#define CDC_A_REVISION2_POR (0x00) +#define CDC_A_REVISION3 (0x102) +#define CDC_A_REVISION3_POR (0x00) +#define CDC_A_REVISION4 (0x103) +#define CDC_A_REVISION4_POR (0x00) +#define CDC_A_PERPH_TYPE (0x104) +#define CDC_A_PERPH_TYPE_POR (0x23) +#define CDC_A_PERPH_SUBTYPE (0x105) +#define CDC_A_PERPH_SUBTYPE_POR (0x09) +#define CDC_A_INT_RT_STS (0x110) +#define CDC_A_INT_RT_STS_POR (0x00) +#define CDC_A_INT_SET_TYPE (0x111) +#define CDC_A_INT_SET_TYPE_POR (0x3F) +#define CDC_A_INT_POLARITY_HIGH (0x112) +#define CDC_A_INT_POLARITY_HIGH_POR (0x3F) +#define CDC_A_INT_POLARITY_LOW (0x113) +#define CDC_A_INT_POLARITY_LOW_POR (0x00) +#define CDC_A_INT_LATCHED_CLR (0x114) +#define CDC_A_INT_LATCHED_CLR_POR (0x00) +#define CDC_A_INT_EN_SET (0x115) +#define CDC_A_INT_EN_SET_POR (0x00) +#define CDC_A_INT_EN_CLR (0x116) +#define CDC_A_INT_EN_CLR_POR (0x00) +#define CDC_A_INT_LATCHED_STS (0x118) +#define CDC_A_INT_LATCHED_STS_POR (0x00) +#define CDC_A_INT_PENDING_STS (0x119) +#define CDC_A_INT_PENDING_STS_POR (0x00) +#define CDC_A_INT_MID_SEL (0x11A) +#define CDC_A_INT_MID_SEL_POR (0x00) +#define CDC_A_INT_PRIORITY (0x11B) +#define CDC_A_INT_PRIORITY_POR (0x00) + +#define CDC_A_MICB_1_EN (0x140) +#define MICB_1_EN_MICB_ENABLE BIT(7) +#define MICB_1_EN_BYP_CAP_MASK BIT(6) +#define MICB_1_EN_NO_EXT_BYP_CAP BIT(6) +#define MICB_1_EN_EXT_BYP_CAP 0 +#define MICB_1_EN_PULL_DOWN_EN_MASK BIT(5) +#define MICB_1_EN_PULL_DOWN_EN_ENABLE BIT(5) +#define MICB_1_EN_OPA_STG2_TAIL_CURR_MASK GENMASK(3, 1) +#define MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA BIT(2) +#define MICB_1_EN_TX3_GND_SEL_MASK BIT(0) +#define MICB_1_EN_TX3_GND_SEL_TX_GND 0 +#define CDC_A_MICB_1_EN_POR (0x00) + +#define CDC_A_MICB_1_VAL (0x141) +#define MICB_1_VAL_MICB_OUT_VAL_MASK GENMASK(7, 3) +#define MICB_1_VAL_MICB_OUT_VAL_V2P70V ((0x16) << 3) +#define CDC_A_MICB_1_VAL_POR (0x20) +#define CDC_A_MICB_1_CTL (0x142) + +#define MICB_1_CTL_CFILT_REF_SEL_MASK BIT(1) +#define MICB_1_CTL_CFILT_REF_SEL_HPF_REF BIT(1) +#define MICB_1_CTL_EXT_PRECHARG_EN_MASK BIT(5) +#define MICB_1_CTL_EXT_PRECHARG_EN_ENABLE BIT(5) +#define MICB_1_CTL_INT_PRECHARG_BYP_MASK BIT(6) +#define MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL BIT(6) + +#define CDC_A_MICB_1_CTL_POR (0x00) +#define CDC_A_MICB_1_INT_RBIAS (0x143) + +#define MICB_1_INT_TX1_INT_RBIAS_EN_MASK BIT(7) +#define MICB_1_INT_TX1_INT_RBIAS_EN_ENABLE BIT(7) +#define MICB_1_INT_TX1_INT_RBIAS_EN_DISABLE 0 + +#define MICB_1_INT_TX1_INT_PULLUP_EN_MASK BIT(6) +#define MICB_1_INT_TX1_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(6) +#define MICB_1_INT_TX1_INT_PULLUP_EN_TX1N_TO_GND 0 + +#define MICB_1_INT_TX2_INT_RBIAS_EN_MASK BIT(4) +#define MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE BIT(4) +#define MICB_1_INT_TX2_INT_RBIAS_EN_DISABLE 0 +#define MICB_1_INT_TX2_INT_PULLUP_EN_MASK BIT(3) +#define MICB_1_INT_TX2_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(3) +#define MICB_1_INT_TX2_INT_PULLUP_EN_TX1N_TO_GND 0 + +#define MICB_1_INT_TX3_INT_RBIAS_EN_MASK BIT(1) +#define MICB_1_INT_TX3_INT_RBIAS_EN_ENABLE BIT(1) +#define MICB_1_INT_TX3_INT_RBIAS_EN_DISABLE 0 +#define MICB_1_INT_TX3_INT_PULLUP_EN_MASK BIT(0) +#define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(0) +#define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_GND 0 + +#define CDC_A_MICB_1_INT_RBIAS_POR (0x49) +#define CDC_A_MICB_2_EN (0x144) +#define CDC_A_MICB_2_EN_POR (0x20) +#define CDC_A_TX_1_2_ATEST_CTL_2 (0x145) +#define CDC_A_TX_1_2_ATEST_CTL_2_POR (0x00) +#define CDC_A_MASTER_BIAS_CTL (0x146) +#define MASTER_BIAS_CTL_MASTER_BIAS_EN_MASK BIT(5) +#define MASTER_BIAS_CTL_MASTER_BIAS_EN_ENABLE BIT(5) +#define MASTER_BIAS_CTL_V2L_BUFFER_EN_MASK BIT(4) +#define MASTER_BIAS_CTL_V2L_BUFFER_EN_ENABLE BIT(4) +#define CDC_A_MASTER_BIAS_CTL_POR (0x00) +#define CDC_A_MBHC_DET_CTL_1 (0x147) +#define CDC_A_MBHC_DET_CTL_1_POR (0x35) +#define CDC_A_MBHC_DET_CTL_2 (0x150) +#define CDC_A_MBHC_DET_CTL_2_POR (0x08) +#define CDC_A_MBHC_FSM_CTL (0x151) +#define CDC_A_MBHC_FSM_CTL_POR (0x00) +#define CDC_A_MBHC_DBNC_TIMER (0x152) +#define CDC_A_MBHC_DBNC_TIMER_POR (0x98) +#define CDC_A_MBHC_BTN0_ZDETL_CTL (0x153) +#define CDC_A_MBHC_BTN0_ZDETL_CTL_POR (0x00) +#define CDC_A_MBHC_BTN1_ZDETM_CTL (0x154) +#define CDC_A_MBHC_BTN1_ZDETM_CTL_POR (0x20) +#define CDC_A_MBHC_BTN2_ZDETH_CTL (0x155) +#define CDC_A_MBHC_BTN2_ZDETH_CTL_POR (0x40) +#define CDC_A_MBHC_BTN3_CTL (0x156) +#define CDC_A_MBHC_BTN3_CTL_POR (0x61) +#define CDC_A_MBHC_BTN4_CTL (0x157) +#define CDC_A_MBHC_BTN4_CTL_POR (0x80) +#define CDC_A_MBHC_BTN_RESULT (0x158) +#define CDC_A_MBHC_BTN_RESULT_POR (0x00) +#define CDC_A_MBHC_ZDET_ELECT_RESULT (0x159) +#define CDC_A_MBHC_ZDET_ELECT_RESULT_POR (0x00) +#define CDC_A_TX_1_EN (0x160) +#define CDC_A_TX_1_EN_POR (0x03) +#define CDC_A_TX_2_EN (0x161) +#define CDC_A_TX_2_EN_POR (0x03) +#define CDC_A_TX_1_2_TEST_CTL_1 (0x162) +#define CDC_A_TX_1_2_TEST_CTL_1_POR (0xBF) +#define CDC_A_TX_1_2_TEST_CTL_2 (0x163) +#define CDC_A_TX_1_2_TEST_CTL_2_POR (0x8C) +#define CDC_A_TX_1_2_ATEST_CTL (0x164) +#define CDC_A_TX_1_2_ATEST_CTL_POR (0x00) +#define CDC_A_TX_1_2_OPAMP_BIAS (0x165) +#define CDC_A_TX_1_2_OPAMP_BIAS_POR (0x6B) +#define CDC_A_TX_1_2_TXFE_CLKDIV (0x166) +#define CDC_A_TX_1_2_TXFE_CLKDIV_POR (0x51) +#define CDC_A_TX_3_EN (0x167) +#define CDC_A_TX_3_EN_POR (0x02) +#define CDC_A_NCP_EN (0x180) +#define CDC_A_NCP_EN_POR (0x26) +#define CDC_A_NCP_CLK (0x181) +#define CDC_A_NCP_CLK_POR (0x23) +#define CDC_A_NCP_DEGLITCH (0x182) +#define CDC_A_NCP_DEGLITCH_POR (0x5B) +#define CDC_A_NCP_FBCTRL (0x183) +#define CDC_A_NCP_FBCTRL_FB_CLK_INV_MASK BIT(5) +#define CDC_A_NCP_FBCTRL_FB_CLK_INV BIT(5) +#define CDC_A_NCP_FBCTRL_POR (0x08) +#define CDC_A_NCP_BIAS (0x184) +#define CDC_A_NCP_BIAS_POR (0x29) +#define CDC_A_NCP_VCTRL (0x185) +#define CDC_A_NCP_VCTRL_POR (0x24) +#define CDC_A_NCP_TEST (0x186) +#define CDC_A_NCP_TEST_POR (0x00) +#define CDC_A_NCP_CLIM_ADDR (0x187) +#define CDC_A_NCP_CLIM_ADDR_POR (0xD5) +#define CDC_A_RX_CLOCK_DIVIDER (0x190) +#define CDC_A_RX_CLOCK_DIVIDER_POR (0xE8) +#define CDC_A_RX_COM_OCP_CTL (0x191) +#define CDC_A_RX_COM_OCP_CTL_POR (0xCF) +#define CDC_A_RX_COM_OCP_COUNT (0x192) +#define CDC_A_RX_COM_OCP_COUNT_POR (0x6E) +#define CDC_A_RX_COM_BIAS_DAC (0x193) +#define RX_COM_BIAS_DAC_RX_BIAS_EN_MASK BIT(7) +#define RX_COM_BIAS_DAC_RX_BIAS_EN_ENABLE BIT(7) +#define RX_COM_BIAS_DAC_DAC_REF_EN_MASK BIT(0) +#define RX_COM_BIAS_DAC_DAC_REF_EN_ENABLE BIT(0) + +#define CDC_A_RX_COM_BIAS_DAC_POR (0x10) +#define CDC_A_RX_HPH_BIAS_PA (0x194) +#define CDC_A_RX_HPH_BIAS_PA_POR (0x5A) +#define CDC_A_RX_HPH_BIAS_LDO_OCP (0x195) +#define CDC_A_RX_HPH_BIAS_LDO_OCP_POR (0x69) +#define CDC_A_RX_HPH_BIAS_CNP (0x196) +#define CDC_A_RX_HPH_BIAS_CNP_POR (0x29) +#define CDC_A_RX_HPH_CNP_EN (0x197) +#define CDC_A_RX_HPH_CNP_EN_POR (0x80) +#define CDC_A_RX_HPH_CNP_WG_CTL (0x198) +#define CDC_A_RX_HPH_CNP_WG_CTL_POR (0xDA) +#define CDC_A_RX_HPH_CNP_WG_TIME (0x199) +#define CDC_A_RX_HPH_CNP_WG_TIME_POR (0x16) +#define CDC_A_RX_HPH_L_TEST (0x19A) +#define CDC_A_RX_HPH_L_TEST_POR (0x00) +#define CDC_A_RX_HPH_L_PA_DAC_CTL (0x19B) +#define RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK BIT(1) +#define RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET BIT(1) +#define CDC_A_RX_HPH_L_PA_DAC_CTL_POR (0x20) +#define CDC_A_RX_HPH_R_TEST (0x19C) +#define CDC_A_RX_HPH_R_TEST_POR (0x00) +#define CDC_A_RX_HPH_R_PA_DAC_CTL (0x19D) +#define RX_HPH_R_PA_DAC_CTL_DATA_RESET BIT(1) +#define RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK BIT(1) + +#define CDC_A_RX_HPH_R_PA_DAC_CTL_POR (0x20) +#define CDC_A_RX_EAR_CTL (0x19E) +#define RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK BIT(0) +#define RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE BIT(0) + +#define CDC_A_RX_EAR_CTL_POR (0x12) +#define CDC_A_RX_ATEST (0x19F) +#define CDC_A_RX_ATEST_POR (0x00) +#define CDC_A_RX_HPH_STATUS (0x1A0) +#define CDC_A_RX_HPH_STATUS_POR (0x0C) +#define CDC_A_RX_EAR_STATUS (0x1A1) +#define CDC_A_RX_EAR_STATUS_POR (0x00) +#define CDC_A_SPKR_DAC_CTL (0x1B0) +#define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4) +#define SPKR_DAC_CTL_DAC_RESET_NORMAL 0 + +#define CDC_A_SPKR_DAC_CTL_POR (0x83) +#define CDC_A_SPKR_DRV_CLIP_DET (0x1B1) +#define CDC_A_SPKR_DRV_CLIP_DET_POR (0x91) +#define CDC_A_SPKR_DRV_CTL (0x1B2) +#define SPKR_DRV_CTL_DEF_MASK 0xEF +#define SPKR_DRV_CLASSD_PA_EN_MASK BIT(7) +#define SPKR_DRV_CLASSD_PA_EN_ENABLE BIT(7) +#define SPKR_DRV_CAL_EN BIT(6) +#define SPKR_DRV_SETTLE_EN BIT(5) +#define SPKR_DRV_FW_EN BIT(3) +#define SPKR_DRV_BOOST_SET BIT(2) +#define SPKR_DRV_CMFB_SET BIT(1) +#define SPKR_DRV_GAIN_SET BIT(0) +#define SPKR_DRV_CTL_DEF_VAL (SPKR_DRV_CLASSD_PA_EN_ENABLE | \ + SPKR_DRV_CAL_EN | SPKR_DRV_SETTLE_EN | \ + SPKR_DRV_FW_EN | SPKR_DRV_BOOST_SET | \ + SPKR_DRV_CMFB_SET | SPKR_DRV_GAIN_SET) +#define CDC_A_SPKR_DRV_CTL_POR (0x29) +#define CDC_A_SPKR_ANA_BIAS_SET (0x1B3) +#define CDC_A_SPKR_ANA_BIAS_SET_POR (0x4D) +#define CDC_A_SPKR_OCP_CTL (0x1B4) +#define CDC_A_SPKR_OCP_CTL_POR (0xE1) +#define CDC_A_SPKR_PWRSTG_CTL (0x1B5) +#define SPKR_PWRSTG_CTL_DAC_EN_MASK BIT(0) +#define SPKR_PWRSTG_CTL_DAC_EN_ENABLE BIT(0) +#define SPKR_PWRSTG_CTL_HBRDGE_EN BIT(6) +#define SPKR_PWRSTG_CTL_CLAMP_EN BIT(5) +#define SPKR_PWRSTG_CTL_DEADTIME_T20NS 0 +#define SPKR_PWRSTG_CTL_DEADTIME_T15NS BIT(3) +#define SPKR_PWRSTG_CTL_DEADTIME_T10NS BIT(4) +#define SPKR_PWRSTG_CTL_DEADTIME_T05NS (BIT(3) | BIT(4)) +#define SPKR_PWRSTG_CTL_MASK 0xE0 +#define SPKR_PWRSTG_CTL_DEFAULTS 0xE0 + +#define CDC_A_SPKR_PWRSTG_CTL_POR (0x1E) +#define CDC_A_SPKR_DRV_MISC (0x1B6) +#define CDC_A_SPKR_DRV_MISC_POR (0xCB) +#define CDC_A_SPKR_DRV_DBG (0x1B7) +#define CDC_A_SPKR_DRV_DBG_POR (0x00) +#define CDC_A_CURRENT_LIMIT (0x1C0) +#define CDC_A_CURRENT_LIMIT_POR (0x02) +#define CDC_A_OUTPUT_VOLTAGE (0x1C1) +#define CDC_A_OUTPUT_VOLTAGE_POR (0x14) +#define CDC_A_BYPASS_MODE (0x1C2) +#define CDC_A_BYPASS_MODE_POR (0x00) +#define CDC_A_BOOST_EN_CTL (0x1C3) +#define CDC_A_BOOST_EN_CTL_POR (0x1F) +#define CDC_A_SLOPE_COMP_IP_ZERO (0x1C4) +#define CDC_A_SLOPE_COMP_IP_ZERO_POR (0x8C) +#define CDC_A_RDSON_MAX_DUTY_CYCLE (0x1C5) +#define CDC_A_RDSON_MAX_DUTY_CYCLE_POR (0xC0) +#define CDC_A_BOOST_TEST1_1 (0x1C6) +#define CDC_A_BOOST_TEST1_1_POR (0x00) +#define CDC_A_BOOST_TEST_2 (0x1C7) +#define CDC_A_BOOST_TEST_2_POR (0x00) +#define CDC_A_SPKR_SAR_STATUS (0x1C8) +#define CDC_A_SPKR_SAR_STATUS_POR (0x00) +#define CDC_A_SPKR_DRV_STATUS (0x1C9) +#define CDC_A_SPKR_DRV_STATUS_POR (0x00) +#define CDC_A_PBUS_ADD_CSR (0x1CE) +#define CDC_A_PBUS_ADD_CSR_POR (0x00) +#define CDC_A_PBUS_ADD_SEL (0x1CF) +#define CDC_A_PBUS_ADD_SEL_POR (0x00) +#define CDC_A_SEC_ACCESS (0x1D0) +#define CDC_A_SEC_ACCESS_POR (0x00) +#define CDC_A_PERPH_RESET_CTL1 (0x1D8) +#define CDC_A_PERPH_RESET_CTL1_POR (0x00) +#define CDC_A_PERPH_RESET_CTL2 (0x1D9) +#define CDC_A_PERPH_RESET_CTL2_POR (0x01) +#define CDC_A_PERPH_RESET_CTL3 (0x1DA) +#define CDC_A_PERPH_RESET_CTL3_POR (0x05) +#define CDC_A_PERPH_RESET_CTL4 (0x1DB) +#define CDC_A_PERPH_RESET_CTL4_POR (0x00) +#define CDC_A_INT_TEST1 (0x1E0) +#define CDC_A_INT_TEST1_POR (0x00) +#define CDC_A_INT_TEST_VAL (0x1E1) +#define CDC_A_INT_TEST_VAL_POR (0x00) +#define CDC_A_TRIM_NUM (0x1F0) +#define CDC_A_TRIM_NUM_POR (0x04) +#define CDC_A_TRIM_CTRL1 (0x1F1) +#define CDC_A_TRIM_CTRL1_POR (0x00) +#define CDC_A_TRIM_CTRL2 (0x1F2) +#define CDC_A_TRIM_CTRL2_POR (0x00) +#define CDC_A_TRIM_CTRL3 (0x1F3) +#define CDC_A_TRIM_CTRL3_POR (0x00) +#define CDC_A_TRIM_CTRL4 (0x1F4) +#define CDC_A_TRIM_CTRL4_POR (0x00) + +/* Digital part */ +#define LPASS_CDC_CLK_RX_RESET_CTL (0x200) +#define LPASS_CDC_CLK_RX_RESET_CTL_POR (0x00) +#define LPASS_CDC_CLK_TX_RESET_B1_CTL (0x204) +#define CLK_RX_RESET_B1_CTL_TX1_RESET_MASK BIT(0) +#define CLK_RX_RESET_B1_CTL_TX2_RESET_MASK BIT(1) +#define LPASS_CDC_CLK_TX_RESET_B1_CTL_POR (0x00) +#define LPASS_CDC_CLK_DMIC_B1_CTL (0x208) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_MASK GENMASK(3, 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV2 (0x0 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3 (0x1 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV4 (0x2 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV6 (0x3 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV16 (0x4 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_EN_MASK BIT(0) +#define DMIC_B1_CTL_DMIC0_CLK_EN_ENABLE BIT(0) + +#define LPASS_CDC_CLK_DMIC_B1_CTL_POR (0x00) +#define LPASS_CDC_CLK_RX_I2S_CTL (0x20C) +#define RX_I2S_CTL_RX_I2S_MODE_MASK BIT(5) +#define RX_I2S_CTL_RX_I2S_MODE_16 BIT(5) +#define RX_I2S_CTL_RX_I2S_MODE_32 0 +#define RX_I2S_CTL_RX_I2S_FS_RATE_MASK GENMASK(2, 0) +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_8_KHZ 0x0 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_16_KHZ 0x1 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_32_KHZ 0x2 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ 0x3 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_96_KHZ 0x4 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_192_KHZ 0x5 +#define LPASS_CDC_CLK_RX_I2S_CTL_POR (0x13) +#define LPASS_CDC_CLK_OTHR_RESET_B1_CTL (0x214) +#define LPASS_CDC_CLK_TX_I2S_CTL_POR (0x13) +#define LPASS_CDC_CLK_TX_I2S_CTL (0x210) +#define TX_I2S_CTL_TX_I2S_MODE_MASK BIT(5) +#define TX_I2S_CTL_TX_I2S_MODE_16 BIT(5) +#define TX_I2S_CTL_TX_I2S_MODE_32 0 +#define TX_I2S_CTL_TX_I2S_FS_RATE_MASK GENMASK(2, 0) +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ 0x0 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ 0x1 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ 0x2 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ 0x3 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_96_KHZ 0x4 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_192_KHZ 0x5 + +#define LPASS_CDC_CLK_TX_I2S_CTL_POR (0x13) +#define LPASS_CDC_CLK_OTHR_RESET_B1_CTL (0x214) +#define LPASS_CDC_CLK_OTHR_RESET_B1_CTL_POR (0x00) +#define LPASS_CDC_CLK_TX_CLK_EN_B1_CTL (0x218) +#define LPASS_CDC_CLK_TX_CLK_EN_B1_CTL_POR (0x00) +#define LPASS_CDC_CLK_OTHR_CTL (0x21C) +#define LPASS_CDC_CLK_OTHR_CTL_POR (0x04) +#define LPASS_CDC_CLK_RX_B1_CTL (0x220) +#define LPASS_CDC_CLK_RX_B1_CTL_POR (0x00) +#define LPASS_CDC_CLK_MCLK_CTL (0x224) +#define MCLK_CTL_MCLK_EN_MASK BIT(0) +#define MCLK_CTL_MCLK_EN_ENABLE BIT(0) +#define MCLK_CTL_MCLK_EN_DISABLE 0 +#define LPASS_CDC_CLK_MCLK_CTL_POR (0x00) +#define LPASS_CDC_CLK_PDM_CTL (0x228) +#define LPASS_CDC_CLK_PDM_CTL_PDM_EN_MASK BIT(0) +#define LPASS_CDC_CLK_PDM_CTL_PDM_EN BIT(0) +#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK BIT(1) +#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB BIT(1) +#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_PDM_CLK 0 + +#define LPASS_CDC_CLK_PDM_CTL_POR (0x00) +#define LPASS_CDC_CLK_SD_CTL (0x22C) +#define LPASS_CDC_CLK_SD_CTL_POR (0x00) +#define LPASS_CDC_RX1_B1_CTL (0x240) +#define LPASS_CDC_RX1_B1_CTL_POR (0x00) +#define LPASS_CDC_RX2_B1_CTL (0x260) +#define LPASS_CDC_RX2_B1_CTL_POR (0x00) +#define LPASS_CDC_RX3_B1_CTL (0x280) +#define LPASS_CDC_RX3_B1_CTL_POR (0x00) +#define LPASS_CDC_RX1_B2_CTL (0x244) +#define LPASS_CDC_RX1_B2_CTL_POR (0x00) +#define LPASS_CDC_RX2_B2_CTL (0x264) +#define LPASS_CDC_RX2_B2_CTL_POR (0x00) +#define LPASS_CDC_RX3_B2_CTL (0x284) +#define LPASS_CDC_RX3_B2_CTL_POR (0x00) +#define LPASS_CDC_RX1_B3_CTL (0x248) +#define LPASS_CDC_RX1_B3_CTL_POR (0x00) +#define LPASS_CDC_RX2_B3_CTL (0x268) +#define LPASS_CDC_RX2_B3_CTL_POR (0x00) +#define LPASS_CDC_RX3_B3_CTL (0x288) +#define LPASS_CDC_RX3_B3_CTL_POR (0x00) +#define LPASS_CDC_RX1_B4_CTL (0x24C) +#define LPASS_CDC_RX1_B4_CTL_POR (0x00) +#define LPASS_CDC_RX2_B4_CTL (0x26C) +#define LPASS_CDC_RX2_B4_CTL_POR (0x00) +#define LPASS_CDC_RX3_B4_CTL (0x28C) +#define LPASS_CDC_RX3_B4_CTL_POR (0x00) +#define LPASS_CDC_RX1_B5_CTL (0x250) +#define LPASS_CDC_RX1_B5_CTL_POR (0x68) +#define LPASS_CDC_RX2_B5_CTL (0x270) +#define LPASS_CDC_RX2_B5_CTL_POR (0x68) +#define LPASS_CDC_RX3_B5_CTL (0x290) +#define LPASS_CDC_RX3_B5_CTL_POR (0x68) +#define LPASS_CDC_RX1_B6_CTL (0x254) +#define RXn_B6_CTL_MUTE_MASK BIT(0) +#define RXn_B6_CTL_MUTE_ENABLE BIT(0) +#define RXn_B6_CTL_MUTE_DISABLE 0 +#define LPASS_CDC_RX1_B6_CTL_POR (0x00) +#define LPASS_CDC_RX2_B6_CTL (0x274) +#define LPASS_CDC_RX2_B6_CTL_POR (0x00) +#define LPASS_CDC_RX3_B6_CTL (0x294) +#define LPASS_CDC_RX3_B6_CTL_POR (0x00) +#define LPASS_CDC_RX1_VOL_CTL_B1_CTL (0x258) +#define LPASS_CDC_RX1_VOL_CTL_B1_CTL_POR (0x00) +#define LPASS_CDC_RX2_VOL_CTL_B1_CTL (0x278) +#define LPASS_CDC_RX2_VOL_CTL_B1_CTL_POR (0x00) +#define LPASS_CDC_RX3_VOL_CTL_B1_CTL (0x298) +#define LPASS_CDC_RX3_VOL_CTL_B1_CTL_POR (0x00) +#define LPASS_CDC_RX1_VOL_CTL_B2_CTL (0x25C) +#define LPASS_CDC_RX1_VOL_CTL_B2_CTL_POR (0x00) +#define LPASS_CDC_RX2_VOL_CTL_B2_CTL (0x27C) +#define LPASS_CDC_RX2_VOL_CTL_B2_CTL_POR (0x00) +#define LPASS_CDC_RX3_VOL_CTL_B2_CTL (0x29C) +#define LPASS_CDC_RX3_VOL_CTL_B2_CTL_POR (0x00) +#define LPASS_CDC_TOP_GAIN_UPDATE (0x2A0) +#define LPASS_CDC_TOP_GAIN_UPDATE_POR (0x00) +#define LPASS_CDC_TOP_CTL (0x2A4) +#define TOP_CTL_DIG_MCLK_FREQ_MASK BIT(0) +#define TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ 0 +#define TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ BIT(0) + +#define LPASS_CDC_TOP_CTL_POR (0x01) +#define LPASS_CDC_DEBUG_DESER1_CTL (0x2E0) +#define LPASS_CDC_DEBUG_DESER1_CTL_POR (0x00) +#define LPASS_CDC_DEBUG_DESER2_CTL (0x2E4) +#define LPASS_CDC_DEBUG_DESER2_CTL_POR (0x00) +#define LPASS_CDC_DEBUG_B1_CTL_CFG (0x2E8) +#define LPASS_CDC_DEBUG_B1_CTL_CFG_POR (0x00) +#define LPASS_CDC_DEBUG_B2_CTL_CFG (0x2EC) +#define LPASS_CDC_DEBUG_B2_CTL_CFG_POR (0x00) +#define LPASS_CDC_DEBUG_B3_CTL_CFG (0x2F0) +#define LPASS_CDC_DEBUG_B3_CTL_CFG_POR (0x00) +#define LPASS_CDC_IIR1_GAIN_B1_CTL (0x300) +#define LPASS_CDC_IIR1_GAIN_B1_CTL_POR (0x00) +#define LPASS_CDC_IIR2_GAIN_B1_CTL (0x340) +#define LPASS_CDC_IIR2_GAIN_B1_CTL_POR (0x00) +#define LPASS_CDC_IIR1_GAIN_B2_CTL (0x304) +#define LPASS_CDC_IIR1_GAIN_B2_CTL_POR (0x00) +#define LPASS_CDC_IIR2_GAIN_B2_CTL (0x344) +#define LPASS_CDC_IIR2_GAIN_B2_CTL_POR (0x00) +#define LPASS_CDC_IIR1_GAIN_B3_CTL (0x308) +#define LPASS_CDC_IIR1_GAIN_B3_CTL_POR (0x00) +#define LPASS_CDC_IIR2_GAIN_B3_CTL (0x348) +#define LPASS_CDC_IIR2_GAIN_B3_CTL_POR (0x00) +#define LPASS_CDC_IIR1_GAIN_B4_CTL (0x30C) +#define LPASS_CDC_IIR1_GAIN_B4_CTL_POR (0x00) +#define LPASS_CDC_IIR2_GAIN_B4_CTL (0x34C) +#define LPASS_CDC_IIR2_GAIN_B4_CTL_POR (0x00) +#define LPASS_CDC_IIR1_GAIN_B5_CTL (0x310) +#define LPASS_CDC_IIR1_GAIN_B5_CTL_POR (0x00) +#define LPASS_CDC_IIR2_GAIN_B5_CTL (0x350) +#define LPASS_CDC_IIR2_GAIN_B5_CTL_POR (0x00) +#define LPASS_CDC_IIR1_GAIN_B6_CTL (0x314) +#define LPASS_CDC_IIR1_GAIN_B6_CTL_POR (0x00) +#define LPASS_CDC_IIR2_GAIN_B6_CTL (0x354) +#define LPASS_CDC_IIR2_GAIN_B6_CTL_POR (0x00) +#define LPASS_CDC_IIR1_GAIN_B7_CTL (0x318) +#define LPASS_CDC_IIR1_GAIN_B7_CTL_POR (0x00) +#define LPASS_CDC_IIR2_GAIN_B7_CTL (0x358) +#define LPASS_CDC_IIR2_GAIN_B7_CTL_POR (0x00) +#define LPASS_CDC_IIR1_GAIN_B8_CTL (0x31C) +#define LPASS_CDC_IIR1_GAIN_B8_CTL_POR (0x00) +#define LPASS_CDC_IIR2_GAIN_B8_CTL (0x35C) +#define LPASS_CDC_IIR2_GAIN_B8_CTL_POR (0x00) +#define LPASS_CDC_IIR1_CTL (0x320) +#define LPASS_CDC_IIR1_CTL_POR (0x40) +#define LPASS_CDC_IIR2_CTL (0x360) +#define LPASS_CDC_IIR2_CTL_POR (0x40) +#define LPASS_CDC_IIR1_GAIN_TIMER_CTL (0x324) +#define LPASS_CDC_IIR1_GAIN_TIMER_CTL_POR (0x00) +#define LPASS_CDC_IIR2_GAIN_TIMER_CTL (0x364) +#define LPASS_CDC_IIR2_GAIN_TIMER_CTL_POR (0x00) +#define LPASS_CDC_IIR1_COEF_B1_CTL (0x328) +#define LPASS_CDC_IIR1_COEF_B1_CTL_POR (0x00) +#define LPASS_CDC_IIR2_COEF_B1_CTL (0x368) +#define LPASS_CDC_IIR2_COEF_B1_CTL_POR (0x00) +#define LPASS_CDC_IIR1_COEF_B2_CTL (0x32C) +#define LPASS_CDC_IIR1_COEF_B2_CTL_POR (0x00) +#define LPASS_CDC_IIR2_COEF_B2_CTL (0x36C) +#define LPASS_CDC_IIR2_COEF_B2_CTL_POR (0x00) +#define LPASS_CDC_CONN_RX1_B1_CTL (0x380) +#define LPASS_CDC_CONN_RX1_B1_CTL_POR (0x00) +#define LPASS_CDC_CONN_RX1_B2_CTL (0x384) +#define LPASS_CDC_CONN_RX1_B2_CTL_POR (0x00) +#define LPASS_CDC_CONN_RX1_B3_CTL (0x388) +#define LPASS_CDC_CONN_RX1_B3_CTL_POR (0x00) +#define LPASS_CDC_CONN_RX2_B1_CTL (0x38C) +#define LPASS_CDC_CONN_RX2_B1_CTL_POR (0x00) +#define LPASS_CDC_CONN_RX2_B2_CTL (0x390) +#define LPASS_CDC_CONN_RX2_B2_CTL_POR (0x00) +#define LPASS_CDC_CONN_RX2_B3_CTL (0x394) +#define LPASS_CDC_CONN_RX2_B3_CTL_POR (0x00) +#define LPASS_CDC_CONN_RX3_B1_CTL (0x398) +#define LPASS_CDC_CONN_RX3_B1_CTL_POR (0x00) +#define LPASS_CDC_CONN_RX3_B2_CTL (0x39C) +#define LPASS_CDC_CONN_RX3_B2_CTL_POR (0x00) +#define LPASS_CDC_CONN_TX_B1_CTL (0x3A0) +#define LPASS_CDC_CONN_TX_B1_CTL_POR (0x00) +#define LPASS_CDC_CONN_EQ1_B1_CTL (0x3A8) +#define LPASS_CDC_CONN_EQ1_B1_CTL_POR (0x00) +#define LPASS_CDC_CONN_EQ1_B2_CTL (0x3AC) +#define LPASS_CDC_CONN_EQ1_B2_CTL_POR (0x00) +#define LPASS_CDC_CONN_EQ1_B3_CTL (0x3B0) +#define LPASS_CDC_CONN_EQ1_B3_CTL_POR (0x00) +#define LPASS_CDC_CONN_EQ1_B4_CTL (0x3B4) +#define LPASS_CDC_CONN_EQ1_B4_CTL_POR (0x00) +#define LPASS_CDC_CONN_EQ2_B1_CTL (0x3B8) +#define LPASS_CDC_CONN_EQ2_B1_CTL_POR (0x00) +#define LPASS_CDC_CONN_EQ2_B2_CTL (0x3BC) +#define LPASS_CDC_CONN_EQ2_B2_CTL_POR (0x00) +#define LPASS_CDC_CONN_EQ2_B3_CTL (0x3C0) +#define LPASS_CDC_CONN_EQ2_B3_CTL_POR (0x00) +#define LPASS_CDC_CONN_EQ2_B4_CTL (0x3C4) +#define LPASS_CDC_CONN_EQ2_B4_CTL_POR (0x00) +#define LPASS_CDC_CONN_TX_I2S_SD1_CTL (0x3C8) +#define LPASS_CDC_CONN_TX_I2S_SD1_CTL_POR (0x00) +#define LPASS_CDC_TX1_VOL_CTL_TIMER (0x480) +#define LPASS_CDC_TX1_VOL_CTL_TIMER_POR (0x00) +#define LPASS_CDC_TX2_VOL_CTL_TIMER (0x4A0) +#define LPASS_CDC_TX2_VOL_CTL_TIMER_POR (0x00) +#define LPASS_CDC_TX1_VOL_CTL_GAIN (0x484) +#define LPASS_CDC_TX1_VOL_CTL_GAIN_POR (0x00) +#define LPASS_CDC_TX2_VOL_CTL_GAIN (0x4A4) +#define LPASS_CDC_TX2_VOL_CTL_GAIN_POR (0x00) +#define LPASS_CDC_TX1_VOL_CTL_CFG (0x488) +#define TX_VOL_CTL_CFG_MUTE_EN_MASK BIT(0) +#define TX_VOL_CTL_CFG_MUTE_EN_ENABLE BIT(0) + +#define LPASS_CDC_TX1_VOL_CTL_CFG_POR (0x00) +#define LPASS_CDC_TX2_VOL_CTL_CFG (0x4A8) +#define LPASS_CDC_TX2_VOL_CTL_CFG_POR (0x00) +#define LPASS_CDC_TX1_MUX_CTL (0x48C) +#define TX_MUX_CTL_CUT_OFF_FREQ_MASK GENMASK(5, 4) +#define TX_MUX_CTL_CUT_OFF_FREQ_SHIFT 4 +#define TX_MUX_CTL_CUT_OFF_FREQ_CF_NEG_3DB_4HZ (0x0 << 4) +#define TX_MUX_CTL_CUT_OFF_FREQ_CF_NEG_3DB_75HZ (0x1 << 4) +#define TX_MUX_CTL_CUT_OFF_FREQ_CF_NEG_3DB_150HZ (0x2 << 4) +#define TX_MUX_CTL_HPF_BP_SEL_MASK BIT(3) +#define TX_MUX_CTL_HPF_BP_SEL_BYPASS BIT(3) +#define TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS 0 + +#define LPASS_CDC_TX1_MUX_CTL_POR (0x00) +#define LPASS_CDC_TX2_MUX_CTL (0x4AC) +#define LPASS_CDC_TX2_MUX_CTL_POR (0x00) +#define LPASS_CDC_TX1_CLK_FS_CTL (0x490) +#define LPASS_CDC_TX1_CLK_FS_CTL_POR (0x03) +#define LPASS_CDC_TX2_CLK_FS_CTL (0x4B0) +#define LPASS_CDC_TX2_CLK_FS_CTL_POR (0x03) +#define LPASS_CDC_TX1_DMIC_CTL (0x494) +#define LPASS_CDC_TX1_DMIC_CTL_POR (0x00) +#define LPASS_CDC_TX2_DMIC_CTL (0x4B4) +#define TXN_DMIC_CTL_CLK_SEL_MASK GENMASK(2, 0) +#define TXN_DMIC_CTL_CLK_SEL_DIV2 0x0 +#define TXN_DMIC_CTL_CLK_SEL_DIV3 0x1 +#define TXN_DMIC_CTL_CLK_SEL_DIV4 0x2 +#define TXN_DMIC_CTL_CLK_SEL_DIV6 0x3 +#define TXN_DMIC_CTL_CLK_SEL_DIV16 0x4 +#define LPASS_CDC_TX2_DMIC_CTL_POR (0x00) +#endif diff --git a/sound/soc/codecs/msm8916-wcd.c b/sound/soc/codecs/msm8916-wcd.c new file mode 100644 index 000000000000..fb4d0ad7828a --- /dev/null +++ b/sound/soc/codecs/msm8916-wcd.c @@ -0,0 +1,1582 @@ +/* Copyright (c) 2016, The Linux Foundation. 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 and + * only 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm8916-wcd-registers.h" +#include "msm8916-wcd.h" +#include "dt-bindings/sound/msm8916-wcd.h" + +#define MSM8916_WCD_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) +#define MSM8916_WCD_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +/* Internal status on mute_mask to track mute on different sinks */ +#define MUTE_MASK_HPHL_PA_DISABLE BIT(1) +#define MUTE_MASK_HPHR_PA_DISABLE BIT(2) +#define MUTE_MASK_EAR_PA_DISABLE BIT(3) +#define MUTE_MASK_SPKR_PA_DISABLE BIT(4) + +struct msm8916_wcd_chip { + struct regmap *analog_map; + struct regmap *digital_map; + unsigned int analog_offset; + u16 pmic_rev; + u16 codec_version; + + struct clk *mclk; + struct regulator *vddio; + struct regulator *vdd_tx_rx; + + u32 mute_mask; + u32 rx_bias_count; + bool micbias1_cap_mode; + bool micbias2_cap_mode; + int dmic_clk_cnt; +}; + +static unsigned long rx_gain_reg[] = { + LPASS_CDC_RX1_VOL_CTL_B2_CTL, + LPASS_CDC_RX2_VOL_CTL_B2_CTL, + LPASS_CDC_RX3_VOL_CTL_B2_CTL, +}; + +static unsigned long tx_gain_reg[] = { + LPASS_CDC_TX1_VOL_CTL_GAIN, + LPASS_CDC_TX2_VOL_CTL_GAIN, +}; + +static const char *const rx_mix1_text[] = { + "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3" +}; + +static const char *const dec_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2" +}; +static const char *const rx_mix2_text[] = { "ZERO", "IIR1", "IIR2" }; +static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" }; +static const char *const rdac2_mux_text[] = { "ZERO", "RX2", "RX1" }; +static const char *const hph_text[] = { "ZERO", "Switch", }; + +/* RX1 MIX1 */ +static const struct soc_enum rx_mix1_inp_enum[] = { + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 3, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B2_CTL, 0, 6, rx_mix1_text), +}; + +/* RX1 MIX2 */ +static const struct soc_enum rx_mix2_inp1_chain_enum = +SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B3_CTL, 0, 3, rx_mix2_text); + +/* RX2 MIX1 */ +static const struct soc_enum rx2_mix1_inp_enum[] = { + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 3, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), +}; + +/* RX2 MIX2 */ +static const struct soc_enum rx2_mix2_inp1_chain_enum = +SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B3_CTL, 0, 3, rx_mix2_text); + +/* RX3 MIX1 */ +static const struct soc_enum rx3_mix1_inp_enum[] = { + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 3, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), +}; + +/* DEC */ +static const struct soc_enum dec1_mux_enum = +SOC_ENUM_SINGLE(LPASS_CDC_CONN_TX_B1_CTL, 0, 6, dec_mux_text); + +static const struct soc_enum dec2_mux_enum = +SOC_ENUM_SINGLE(LPASS_CDC_CONN_TX_B1_CTL, 3, 6, dec_mux_text); + +/* RDAC2 MUX */ +static const struct soc_enum rdac2_mux_enum = +SOC_ENUM_SINGLE(CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 3, rdac2_mux_text); + +/* ADC2 MUX */ +static const struct soc_enum adc2_enum = +SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + +static const struct soc_enum hph_enum = +SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(hph_text), hph_text); + +static const struct snd_kcontrol_new spkr_switch[] = { + SOC_DAPM_SINGLE("Switch", CDC_A_SPKR_DAC_CTL, 7, 1, 0) +}; + +static const struct snd_kcontrol_new dec1_mux = +SOC_DAPM_ENUM("DEC1 MUX Mux", dec1_mux_enum); + +static const struct snd_kcontrol_new dec2_mux = +SOC_DAPM_ENUM("DEC2 MUX Mux", dec2_mux_enum); + +static const struct snd_kcontrol_new rdac2_mux = +SOC_DAPM_ENUM("RDAC2 MUX Mux", rdac2_mux_enum); + +static const struct snd_kcontrol_new tx_adc2_mux = +SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + +static const struct snd_kcontrol_new rx_mix1_inp1_mux = +SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp_enum[0]); + +static const struct snd_kcontrol_new rx_mix1_inp2_mux = +SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp_enum[1]); + +static const struct snd_kcontrol_new rx_mix1_inp3_mux = +SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp_enum[2]); + +static const struct snd_kcontrol_new rx2_mix1_inp1_mux = +SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp_enum[0]); + +static const struct snd_kcontrol_new rx2_mix1_inp2_mux = +SOC_DAPM_ENUM("RX2 MIX1 INP2 Mux", rx2_mix1_inp_enum[1]); + +static const struct snd_kcontrol_new rx2_mix1_inp3_mux = +SOC_DAPM_ENUM("RX2 MIX1 INP3 Mux", rx2_mix1_inp_enum[2]); + +static const struct snd_kcontrol_new rx3_mix1_inp1_mux = +SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp_enum[0]); + +static const struct snd_kcontrol_new rx3_mix1_inp2_mux = +SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp_enum[1]); + +static const struct snd_kcontrol_new rx3_mix1_inp3_mux = +SOC_DAPM_ENUM("RX3 MIX1 INP3 Mux", rx3_mix1_inp_enum[2]); + +static const struct snd_kcontrol_new hphl_mux = SOC_DAPM_ENUM("HPHL", hph_enum); + +static const struct snd_kcontrol_new hphr_mux = SOC_DAPM_ENUM("HPHR", hph_enum); + +/* Digital Gain control -38.4 dB to +38.4 dB in 0.3 dB steps */ +static const DECLARE_TLV_DB_SCALE(digital_gain, -3840, 30, 0); + +/* Analog Gain control 0 dB to +24 dB in 6 dB steps */ +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 600, 0); + +static const struct snd_kcontrol_new msm8916_wcd_snd_controls[] = { + SOC_SINGLE_TLV("ADC1 Volume", CDC_A_TX_1_EN, 3, 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", CDC_A_TX_2_EN, 3, 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", CDC_A_TX_3_EN, 3, 8, 0, analog_gain), + SOC_SINGLE_S8_TLV("RX1 Digital Volume", LPASS_CDC_RX1_VOL_CTL_B2_CTL, + -128, 127, digital_gain), + SOC_SINGLE_S8_TLV("RX2 Digital Volume", LPASS_CDC_RX2_VOL_CTL_B2_CTL, + -128, 127, digital_gain), + SOC_SINGLE_S8_TLV("RX3 Digital Volume", LPASS_CDC_RX3_VOL_CTL_B2_CTL, + -128, 127, digital_gain), +}; + +static int msm8916_wcd_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + int ret = -EINVAL; + struct msm8916_wcd_chip *chip = dev_get_drvdata(codec->dev); + u8 *cache = codec->reg_cache; + + if (!msm8916_wcd_reg_readonly[reg]) + cache[reg] = val; + + if (MSM8916_WCD_IS_TOMBAK_REG(reg)) { + /* codec registers inside pmic core */ + ret = regmap_write(chip->analog_map, + chip->analog_offset + reg, val); + } else if (MSM8916_WCD_IS_DIGITAL_REG(reg)) { + /* codec registers in the cpu core */ + u32 v = val & MSM8916_WCD_REG_VAL_MASK; + u16 offset = MSM8916_WCD_DIGITAL_REG(reg); + + ret = regmap_write(chip->digital_map, offset, v); + } + + return ret; +} + +static unsigned int msm8916_wcd_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + int ret = -EINVAL; + u32 val = 0; + struct msm8916_wcd_chip *chip = dev_get_drvdata(codec->dev); + u8 *cache = codec->reg_cache; + + if (!msm8916_wcd_reg_readonly[reg]) + return cache[reg]; + + if (MSM8916_WCD_IS_TOMBAK_REG(reg)) { + ret = regmap_read(chip->analog_map, + chip->analog_offset + reg, &val); + } else if (MSM8916_WCD_IS_DIGITAL_REG(reg)) { + u32 v; + u16 offset = MSM8916_WCD_DIGITAL_REG(reg); + + ret = regmap_read(chip->digital_map, offset, &v); + val = (u8) v; + } + + return val; +} + +static void msm8916_wcd_configure_cap(struct snd_soc_codec *codec, + bool micbias1, bool micbias2) +{ + struct msm8916_wcd_chip *wcd = snd_soc_codec_get_drvdata(codec); + + if (micbias1 && micbias2) { + if ((wcd->micbias1_cap_mode == MICB_1_EN_EXT_BYP_CAP) || + (wcd->micbias2_cap_mode == MICB_1_EN_EXT_BYP_CAP)) + snd_soc_update_bits(codec, CDC_A_MICB_1_EN, + MICB_1_EN_BYP_CAP_MASK, + MICB_1_EN_EXT_BYP_CAP); + else + snd_soc_update_bits(codec, CDC_A_MICB_1_EN, + MICB_1_EN_BYP_CAP_MASK, + MICB_1_EN_NO_EXT_BYP_CAP); + } else if (micbias2) { + snd_soc_update_bits(codec, CDC_A_MICB_1_EN, + MICB_1_EN_BYP_CAP_MASK, + wcd->micbias2_cap_mode); + } else if (micbias1) { + snd_soc_update_bits(codec, CDC_A_MICB_1_EN, + MICB_1_EN_BYP_CAP_MASK, + wcd->micbias1_cap_mode); + } else { + snd_soc_update_bits(codec, CDC_A_MICB_1_EN, + MICB_1_EN_BYP_CAP_MASK, 0); + } +} + +static int msm8916_wcd_hph_pa_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 msm8916_wcd_chip *msm8916_wcd = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, CDC_A_NCP_FBCTRL, + CDC_A_NCP_FBCTRL_FB_CLK_INV_MASK, + CDC_A_NCP_FBCTRL_FB_CLK_INV); + break; + + case SND_SOC_DAPM_POST_PMU: + if (w->shift == 5) + snd_soc_update_bits(codec, LPASS_CDC_RX1_B6_CTL, + RXn_B6_CTL_MUTE_MASK, 0); + else if (w->shift == 4) + snd_soc_update_bits(codec, LPASS_CDC_RX2_B6_CTL, + RXn_B6_CTL_MUTE_MASK, 0); + break; + + case SND_SOC_DAPM_PRE_PMD: + if (w->shift == 5) { + snd_soc_update_bits(codec, LPASS_CDC_RX1_B6_CTL, + RXn_B6_CTL_MUTE_MASK, + RXn_B6_CTL_MUTE_ENABLE); + msm8916_wcd->mute_mask |= MUTE_MASK_HPHL_PA_DISABLE; + } else if (w->shift == 4) { + snd_soc_update_bits(codec, LPASS_CDC_RX2_B6_CTL, + RXn_B6_CTL_MUTE_MASK, + RXn_B6_CTL_MUTE_ENABLE); + msm8916_wcd->mute_mask |= MUTE_MASK_HPHR_PA_DISABLE; + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_NCP_CLK_EN, + DIG_CLK_CTL_NCP_CLK_EN); + break; + } + return 0; +} + +static int msm8916_wcd_hphl_dac_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); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, CDC_A_RX_HPH_L_PA_DAC_CTL, + RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK, + RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET); + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_RXD1_CLK_EN, + DIG_CLK_CTL_RXD1_CLK_EN); + snd_soc_update_bits(codec, CDC_D_CDC_ANA_CLK_CTL, + ANA_CLK_CTL_EAR_HPHL_CLK_EN, + ANA_CLK_CTL_EAR_HPHL_CLK_EN); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, CDC_A_RX_HPH_L_PA_DAC_CTL, + RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK, 0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, CDC_D_CDC_ANA_CLK_CTL, + ANA_CLK_CTL_EAR_HPHL_CLK_EN, 0); + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_RXD1_CLK_EN, 0); + break; + } + return 0; +} + +static int msm8916_wcd_codec_enable_spk_pa(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 msm8916_wcd_chip *msm8916_wcd = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, CDC_D_CDC_ANA_CLK_CTL, + ANA_CLK_CTL_SPKR_CLK_EN_MASK, + ANA_CLK_CTL_SPKR_CLK_EN); + snd_soc_update_bits(codec, CDC_A_SPKR_PWRSTG_CTL, + SPKR_PWRSTG_CTL_DAC_EN_MASK, + SPKR_PWRSTG_CTL_DAC_EN_ENABLE); + snd_soc_update_bits(codec, CDC_A_SPKR_PWRSTG_CTL, + SPKR_PWRSTG_CTL_MASK, + SPKR_PWRSTG_CTL_DEFAULTS); + if (!TOMBAK_IS_1_0(msm8916_wcd->pmic_rev)) + snd_soc_update_bits(codec, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK, + RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, CDC_A_SPKR_DRV_CTL, + SPKR_DRV_CTL_DEF_MASK, + SPKR_DRV_CTL_DEF_VAL); + + snd_soc_update_bits(codec, LPASS_CDC_RX3_B6_CTL, + RXn_B6_CTL_MUTE_MASK, + RXn_B6_CTL_MUTE_DISABLE); + snd_soc_update_bits(codec, w->reg, + SPKR_DRV_CLASSD_PA_EN_MASK, + SPKR_DRV_CLASSD_PA_EN_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, CDC_A_SPKR_PWRSTG_CTL, + SPKR_PWRSTG_CTL_MASK, 0); + snd_soc_update_bits(codec, CDC_A_SPKR_PWRSTG_CTL, + SPKR_PWRSTG_CTL_DAC_EN_MASK, 0); + + snd_soc_update_bits(codec, CDC_A_SPKR_DAC_CTL, + SPKR_DAC_CTL_DAC_RESET_MASK, + SPKR_DAC_CTL_DAC_RESET_NORMAL); + snd_soc_update_bits(codec, CDC_D_CDC_ANA_CLK_CTL, + ANA_CLK_CTL_SPKR_CLK_EN, 0); + if (!TOMBAK_IS_1_0(msm8916_wcd->pmic_rev)) + snd_soc_update_bits(codec, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK, 0); + break; + } + return 0; +} + +static int msm8916_wcd_codec_enable_dig_clk(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 msm8916_wcd_chip *msm8916_wcd = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_RXD_PDM_CLK_EN_MASK, + DIG_CLK_CTL_RXD_PDM_CLK_EN); + break; + case SND_SOC_DAPM_POST_PMD: + if (msm8916_wcd->rx_bias_count == 0) + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_RXD_PDM_CLK_EN_MASK, 0); + break; + } + + return 0; +} + +static int msm8916_wcd_codec_enable_rx_chain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_RXD_PDM_CLK_EN, + DIG_CLK_CTL_RXD_PDM_CLK_EN); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_RXD_PDM_CLK_EN, 0); + snd_soc_update_bits(codec, w->reg, 1 << w->shift, 0x00); + break; + } + return 0; +} + +static int msm8916_wcd_codec_enable_charge_pump(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 msm8916_wcd_chip *msm8916_wcd = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_RXD_PDM_CLK_EN_MASK | + DIG_CLK_CTL_NCP_CLK_EN_MASK, + DIG_CLK_CTL_RXD_PDM_CLK_EN | + DIG_CLK_CTL_NCP_CLK_EN); + break; + case SND_SOC_DAPM_POST_PMU: + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_NCP_CLK_EN_MASK, 0); + if (msm8916_wcd->rx_bias_count == 0) + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_RXD_PDM_CLK_EN_MASK, 0); + break; + } + return 0; +} + +static int msm8916_wcd_codec_enable_rx_bias(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 msm8916_wcd_chip *msm8916_wcd = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm8916_wcd->rx_bias_count++; + if (msm8916_wcd->rx_bias_count == 1) { + snd_soc_update_bits(codec, CDC_A_RX_COM_BIAS_DAC, + RX_COM_BIAS_DAC_RX_BIAS_EN_MASK | + RX_COM_BIAS_DAC_DAC_REF_EN_MASK, + RX_COM_BIAS_DAC_RX_BIAS_EN_ENABLE | + RX_COM_BIAS_DAC_DAC_REF_EN_ENABLE); + } + + break; + case SND_SOC_DAPM_POST_PMD: + msm8916_wcd->rx_bias_count--; + if (msm8916_wcd->rx_bias_count == 0) { + snd_soc_update_bits(codec, CDC_A_RX_COM_BIAS_DAC, + RX_COM_BIAS_DAC_RX_BIAS_EN_MASK | + RX_COM_BIAS_DAC_DAC_REF_EN_MASK, 0); + + } + break; + } + + return 0; +} + +static void msm8916_wcd_micbias2_enable(struct snd_soc_codec *codec, bool on) +{ + if (on) { + snd_soc_update_bits(codec, CDC_A_MICB_1_CTL, + MICB_1_CTL_EXT_PRECHARG_EN_MASK | + MICB_1_CTL_INT_PRECHARG_BYP_MASK, + MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL + | MICB_1_CTL_EXT_PRECHARG_EN_ENABLE); + snd_soc_write(codec, CDC_A_MICB_1_VAL, + MICB_1_VAL_MICB_OUT_VAL_V2P70V); + /* + * Special headset needs MICBIAS as 2.7V so wait for + * 50 msec for the MICBIAS to reach 2.7 volts. + */ + msleep(50); + snd_soc_update_bits(codec, CDC_A_MICB_1_CTL, + MICB_1_CTL_EXT_PRECHARG_EN_MASK | + MICB_1_CTL_INT_PRECHARG_BYP_MASK, 0); + } +} + +static int msm8916_wcd_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + char *internal1_text = "Internal1"; + char *internal2_text = "Internal2"; + char *internal3_text = "Internal3"; + char *external2_text = "External2"; + char *external_text = "External"; + bool micbias2; + + micbias2 = (snd_soc_read(codec, CDC_A_MICB_2_EN) & 0x80); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (strnstr(w->name, internal1_text, 30)) { + snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX1_INT_RBIAS_EN_MASK, + MICB_1_INT_TX1_INT_RBIAS_EN_ENABLE); + } else if (strnstr(w->name, internal2_text, 30)) { + snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX2_INT_RBIAS_EN_MASK, + MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE); + snd_soc_update_bits(codec, w->reg, + MICB_1_EN_BYP_CAP_MASK | + MICB_1_EN_PULL_DOWN_EN_MASK, 0); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX3_INT_RBIAS_EN_MASK, + MICB_1_INT_TX3_INT_RBIAS_EN_ENABLE); + } + if (!strnstr(w->name, external_text, 30)) + snd_soc_update_bits(codec, CDC_A_MICB_1_EN, + MICB_1_EN_OPA_STG2_TAIL_CURR_MASK | + MICB_1_EN_TX3_GND_SEL_MASK, + MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA); + if (w->reg == CDC_A_MICB_1_EN) + msm8916_wcd_configure_cap(codec, true, micbias2); + + break; + case SND_SOC_DAPM_POST_PMU: + if (strnstr(w->name, internal1_text, 30)) { + snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX1_INT_PULLUP_EN_MASK, + MICB_1_INT_TX1_INT_PULLUP_EN_TX1N_TO_MICBIAS); + } else if (strnstr(w->name, internal2_text, 30)) { + snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX2_INT_PULLUP_EN_MASK, + MICB_1_INT_TX2_INT_PULLUP_EN_TX1N_TO_MICBIAS); + msm8916_wcd_micbias2_enable(codec, true); + msm8916_wcd_configure_cap(codec, false, true); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX3_INT_PULLUP_EN_MASK, + MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_MICBIAS); + } else if (strnstr(w->name, external2_text, 30)) { + msm8916_wcd_micbias2_enable(codec, true); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (strnstr(w->name, internal1_text, 30)) { + snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX1_INT_RBIAS_EN_MASK, + MICB_1_INT_TX1_INT_RBIAS_EN_DISABLE); + } else if (strnstr(w->name, internal2_text, 30)) { + msm8916_wcd_micbias2_enable(codec, false); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX3_INT_RBIAS_EN_MASK, + MICB_1_INT_TX3_INT_RBIAS_EN_DISABLE); + } else if (strnstr(w->name, external2_text, 30)) { + msm8916_wcd_micbias2_enable(codec, false); + break; + } + if (w->reg == CDC_A_MICB_1_EN) + msm8916_wcd_configure_cap(codec, false, micbias2); + break; + } + + return 0; +} + +static void msm8916_wcd_codec_enable_adc_block(struct snd_soc_codec *codec, + int enable) +{ + if (enable) { + snd_soc_update_bits(codec, CDC_D_CDC_ANA_CLK_CTL, + ANA_CLK_CTL_TXA_CLK25_EN, + ANA_CLK_CTL_TXA_CLK25_EN); + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_TXD_CLK_EN, + DIG_CLK_CTL_TXD_CLK_EN); + } else { + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_TXD_CLK_EN, 0); + snd_soc_update_bits(codec, CDC_D_CDC_ANA_CLK_CTL, + ANA_CLK_CTL_TXA_CLK25_EN, 0); + } +} + +static int msm8916_wcd_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 adc_reg = CDC_A_TX_1_2_TEST_CTL_2; + u8 init_bit_shift; + + if (w->reg == CDC_A_TX_1_EN) + init_bit_shift = 5; + else + init_bit_shift = 4; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm8916_wcd_codec_enable_adc_block(codec, 1); + if (w->reg == CDC_A_TX_2_EN) + snd_soc_update_bits(codec, CDC_A_MICB_1_CTL, + MICB_1_CTL_CFILT_REF_SEL_MASK, + MICB_1_CTL_CFILT_REF_SEL_HPF_REF); + /* + * Add delay of 10 ms to give sufficient time for the voltage + * to shoot up and settle so that the txfe init does not + * happen when the input voltage is changing too much. + */ + usleep_range(10000, 10010); + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, + 1 << init_bit_shift); + if (w->reg == CDC_A_TX_1_EN) + snd_soc_update_bits(codec, CDC_D_CDC_CONN_TX1_CTL, + CONN_TX1_SERIAL_TX1_MUX, + CONN_TX1_SERIAL_TX1_ADC_1); + else if ((w->reg == CDC_A_TX_2_EN) || (w->reg == CDC_A_TX_3_EN)) + snd_soc_update_bits(codec, CDC_D_CDC_CONN_TX2_CTL, + CONN_TX2_SERIAL_TX2_MUX, + CONN_TX2_SERIAL_TX2_ADC_2); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * Add delay of 12 ms before deasserting the init + * to reduce the tx pop + */ + usleep_range(12000, 12010); + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + msm8916_wcd_codec_enable_adc_block(codec, 0); + if (w->reg == CDC_A_TX_2_EN) + snd_soc_update_bits(codec, CDC_A_MICB_1_CTL, + MICB_1_CTL_CFILT_REF_SEL_MASK, 0); + if (w->reg == CDC_A_TX_1_EN) + snd_soc_update_bits(codec, CDC_D_CDC_CONN_TX1_CTL, + CONN_TX1_SERIAL_TX1_MUX, + CONN_TX1_SERIAL_TX1_ZERO); + else if ((w->reg == CDC_A_TX_2_EN) || (w->reg == CDC_A_TX_3_EN)) + snd_soc_update_bits(codec, CDC_D_CDC_CONN_TX2_CTL, + CONN_TX2_SERIAL_TX2_MUX, + CONN_TX2_SERIAL_TX2_ZERO); + + break; + } + return 0; +} + +static int msm8916_wcd_hphr_dac_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); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, + CDC_A_RX_HPH_R_PA_DAC_CTL, + RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK, + RX_HPH_R_PA_DAC_CTL_DATA_RESET); + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_RXD2_CLK_EN, + DIG_CLK_CTL_RXD2_CLK_EN); + snd_soc_update_bits(codec, CDC_D_CDC_ANA_CLK_CTL, + ANA_CLK_CTL_EAR_HPHR_CLK_EN_MASK, + ANA_CLK_CTL_EAR_HPHR_CLK_EN); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, CDC_A_RX_HPH_R_PA_DAC_CTL, + RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK, 0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, CDC_D_CDC_ANA_CLK_CTL, + ANA_CLK_CTL_EAR_HPHR_CLK_EN_MASK, 0); + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_RXD2_CLK_EN, 0); + break; + } + return 0; +} + +static int msm8916_wcd_codec_enable_interpolator(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 msm8916_wcd_chip *msm8916_wcd = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* apply the digital gain after the interpolator is enabled */ + snd_soc_write(codec, rx_gain_reg[w->shift], + snd_soc_read(codec, rx_gain_reg[w->shift])); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, LPASS_CDC_CLK_RX_RESET_CTL, + 1 << w->shift, 1 << w->shift); + snd_soc_update_bits(codec, LPASS_CDC_CLK_RX_RESET_CTL, + 1 << w->shift, 0x0); + /* disable the mute enabled during the PMD of this device */ + if (msm8916_wcd->mute_mask & MUTE_MASK_HPHL_PA_DISABLE) { + snd_soc_update_bits(codec, + LPASS_CDC_RX1_B6_CTL, + RXn_B6_CTL_MUTE_MASK, + RXn_B6_CTL_MUTE_DISABLE); + msm8916_wcd->mute_mask &= ~(MUTE_MASK_HPHL_PA_DISABLE); + } + if (msm8916_wcd->mute_mask & MUTE_MASK_HPHR_PA_DISABLE) { + snd_soc_update_bits(codec, LPASS_CDC_RX2_B6_CTL, + RXn_B6_CTL_MUTE_MASK, + RXn_B6_CTL_MUTE_DISABLE); + + msm8916_wcd->mute_mask &= ~(MUTE_MASK_HPHR_PA_DISABLE); + } + if (msm8916_wcd->mute_mask & MUTE_MASK_SPKR_PA_DISABLE) { + snd_soc_update_bits(codec, LPASS_CDC_RX3_B6_CTL, + RXn_B6_CTL_MUTE_MASK, + RXn_B6_CTL_MUTE_DISABLE); + + msm8916_wcd->mute_mask &= ~(MUTE_MASK_SPKR_PA_DISABLE); + } + } + return 0; +} + +static int msm8916_wcd_codec_enable_dec(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 decimator; + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + int ret = 0; + u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg; + u8 dec_hpf_cut_of_freq; + char *dec_num; + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + dev_err(codec->dev, "Invalid decimator = %s\n", w->name); + ret = -EINVAL; + goto out; + } + + dec_num = strpbrk(dec_name, "12"); + if (dec_num == NULL) { + dev_err(codec->dev, "Invalid Decimator\n"); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec_num, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "Invalid decimator = %s\n", dec_name); + ret = -EINVAL; + goto out; + } + + dec_reset_reg = LPASS_CDC_CLK_TX_RESET_B1_CTL; + tx_vol_ctl_reg = LPASS_CDC_TX1_VOL_CTL_CFG + 32 * (decimator - 1); + tx_mux_ctl_reg = LPASS_CDC_TX1_MUX_CTL + 32 * (decimator - 1); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enableable TX digital mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, + TX_VOL_CTL_CFG_MUTE_EN_ENABLE); + dec_hpf_cut_of_freq = + snd_soc_read(codec, + tx_mux_ctl_reg) & TX_MUX_CTL_CUT_OFF_FREQ_MASK; + dec_hpf_cut_of_freq >>= TX_MUX_CTL_CUT_OFF_FREQ_SHIFT; + if (dec_hpf_cut_of_freq != + TX_MUX_CTL_CUT_OFF_FREQ_CF_NEG_3DB_150HZ) { + /* set cut of freq to CF_MIN_3DB_150HZ (0x1) */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, + TX_MUX_CTL_CUT_OFF_FREQ_MASK, + TX_MUX_CTL_CUT_OFF_FREQ_CF_NEG_3DB_150HZ); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* enable HPF */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, + TX_MUX_CTL_HPF_BP_SEL_MASK, + TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS); + /* apply the digital gain after the decimator is enabled */ + snd_soc_write(codec, tx_gain_reg[w->shift], + snd_soc_read(codec, tx_gain_reg[w->shift])); + snd_soc_update_bits(codec, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, 0); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, + TX_VOL_CTL_CFG_MUTE_EN_ENABLE); + snd_soc_update_bits(codec, tx_mux_ctl_reg, + TX_MUX_CTL_HPF_BP_SEL_MASK, + TX_MUX_CTL_HPF_BP_SEL_BYPASS); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, + 1 << w->shift); + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0); + snd_soc_update_bits(codec, tx_mux_ctl_reg, + TX_MUX_CTL_HPF_BP_SEL_MASK, + TX_MUX_CTL_HPF_BP_SEL_BYPASS); + snd_soc_update_bits(codec, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, 0); + break; + } + +out: + kfree(widget_name); + return ret; +} + +static int msm8916_wcd_codec_enable_dmic(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 msm8916_wcd_chip *msm8916_wcd = snd_soc_codec_get_drvdata(codec); + u16 dmic_clk_reg = LPASS_CDC_CLK_DMIC_B1_CTL; + unsigned int dmic; + int ret; + char *dec_num = strpbrk(w->name, "12"); + + if (dec_num == NULL) { + dev_err(codec->dev, "Invalid DMIC\n"); + return -EINVAL; + } + + ret = kstrtouint(dec_num, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, "Invalid DMIC line on the codec\n"); + return -EINVAL; + } + if (dmic > 2) { + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (++msm8916_wcd->dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + DMIC_B1_CTL_DMIC0_CLK_SEL_MASK, + DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3); + snd_soc_update_bits(codec, dmic_clk_reg, + DMIC_B1_CTL_DMIC0_CLK_EN_MASK, + DMIC_B1_CTL_DMIC0_CLK_EN_ENABLE); + } + if (dmic == 1) + snd_soc_update_bits(codec, LPASS_CDC_TX1_DMIC_CTL, + TXN_DMIC_CTL_CLK_SEL_MASK, + TXN_DMIC_CTL_CLK_SEL_DIV3); + if (dmic == 2) + snd_soc_update_bits(codec, LPASS_CDC_TX2_DMIC_CTL, + TXN_DMIC_CTL_CLK_SEL_MASK, + TXN_DMIC_CTL_CLK_SEL_DIV3); + break; + case SND_SOC_DAPM_POST_PMD: + if (--msm8916_wcd->dmic_clk_cnt == 0) + snd_soc_update_bits(codec, dmic_clk_reg, + DMIC_B1_CTL_DMIC0_CLK_EN_MASK, 0); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget msm8916_wcd_dapm_widgets[] = { + /*RX stuff */ + SND_SOC_DAPM_AIF_IN("I2S RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("HEADPHONE"), + SND_SOC_DAPM_PGA_E("HPHL PA", CDC_A_RX_HPH_CNP_EN, + 5, 0, NULL, 0, + msm8916_wcd_hph_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux), + SND_SOC_DAPM_MIXER_E("HPHL DAC", + CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, + 0, msm8916_wcd_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PA", CDC_A_RX_HPH_CNP_EN, + 4, 0, NULL, 0, + msm8916_wcd_hph_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, &hphr_mux), + SND_SOC_DAPM_MIXER_E("HPHR DAC", + CDC_A_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, + 0, msm8916_wcd_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("SPK DAC", SND_SOC_NOPM, 0, 0, + spkr_switch, ARRAY_SIZE(spkr_switch)), + + /* Speaker */ + SND_SOC_DAPM_OUTPUT("SPK_OUT"), + SND_SOC_DAPM_PGA_E("SPK PA", CDC_A_SPKR_DRV_CTL, + 6, 0, NULL, 0, msm8916_wcd_codec_enable_spk_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX1 MIX2", LPASS_CDC_CLK_RX_B1_CTL, 0, 0, NULL, + 0, msm8916_wcd_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2 MIX2", LPASS_CDC_CLK_RX_B1_CTL, 1, 0, NULL, + 0, msm8916_wcd_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3 MIX1", LPASS_CDC_CLK_RX_B1_CTL, 2, 0, NULL, + 0, msm8916_wcd_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("RX1 CLK", CDC_D_CDC_DIG_CLK_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RX2 CLK", CDC_D_CDC_DIG_CLK_CTL, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RX3 CLK", CDC_D_CDC_DIG_CLK_CTL, + 2, 0, msm8916_wcd_codec_enable_dig_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX1 CHAIN", LPASS_CDC_RX1_B6_CTL, 0, 0, + NULL, 0, msm8916_wcd_codec_enable_rx_chain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2 CHAIN", LPASS_CDC_RX2_B6_CTL, 0, 0, + NULL, 0, msm8916_wcd_codec_enable_rx_chain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3 CHAIN", LPASS_CDC_RX3_B6_CTL, 0, 0, + NULL, 0, msm8916_wcd_codec_enable_rx_chain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp3_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp3_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp3_mux), + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-micbias", 0, 0), + SND_SOC_DAPM_SUPPLY("CP", CDC_A_NCP_EN, 0, 0, + msm8916_wcd_codec_enable_charge_pump, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, + msm8916_wcd_codec_enable_rx_bias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("SPK_RX_BIAS", SND_SOC_NOPM, 0, 0, + msm8916_wcd_codec_enable_rx_bias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* TX */ + SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, LPASS_CDC_CLK_OTHR_CTL, + 2, 0, NULL, 0), + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", CDC_A_MICB_1_EN, 7, 0, + msm8916_wcd_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", CDC_A_MICB_2_EN, 7, 0, + msm8916_wcd_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal3", CDC_A_MICB_1_EN, 7, 0, + msm8916_wcd_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC1", NULL, CDC_A_TX_1_EN, 7, 0, + msm8916_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP2", NULL, CDC_A_TX_2_EN, 7, 0, + msm8916_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP3", NULL, CDC_A_TX_3_EN, 7, 0, + msm8916_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), + SND_SOC_DAPM_SUPPLY("MIC BIAS External", CDC_A_MICB_1_EN, 7, 0, + msm8916_wcd_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS External2", CDC_A_MICB_2_EN, 7, 0, + msm8916_wcd_codec_enable_micbias, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_MUX_E("DEC1 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0, + &dec1_mux, msm8916_wcd_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("DEC2 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 1, 0, + &dec2_mux, msm8916_wcd_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_AIF_OUT("I2S TX1", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX2", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX3", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + msm8916_wcd_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + msm8916_wcd_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", LPASS_CDC_CLK_RX_I2S_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", LPASS_CDC_CLK_TX_I2S_CTL, 4, 0, + NULL, 0), +}; + +static int msm8916_wcd_codec_parse_dt(struct platform_device *pdev, + struct msm8916_wcd_chip *chip) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int ret; + struct regulator_bulk_data regs[2]; + const char *ext1_cap = "qcom,micbias1-ext-cap"; + const char *ext2_cap = "qcom,micbias2-ext-cap"; + u32 res[2]; + + ret = of_property_read_u32_array(np, "reg", res, 2); + if (ret < 0) + return ret; + + if (of_property_read_bool(pdev->dev.of_node, ext1_cap)) + chip->micbias1_cap_mode = MICB_1_EN_EXT_BYP_CAP; + else + chip->micbias1_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP; + + if (of_property_read_bool(pdev->dev.of_node, ext2_cap)) + chip->micbias2_cap_mode = MICB_1_EN_EXT_BYP_CAP; + else + chip->micbias2_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP; + + chip->analog_offset = res[0]; + chip->digital_map = syscon_regmap_lookup_by_phandle(np, + "qcom,lpass-codec-core"); + if (IS_ERR(chip->digital_map)) + return PTR_ERR(chip->digital_map); + + chip->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(chip->mclk)) { + dev_err(dev, "failed to get mclk\n"); + return PTR_ERR(chip->mclk); + } + + regs[0].supply = "vddio"; + regs[1].supply = "vdd-tx-rx"; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(regs), regs); + if (ret) { + dev_err(dev, "Failed to get regulator supplies %d\n", ret); + return ret; + } + chip->vddio = regs[0].consumer; + chip->vdd_tx_rx = regs[1].consumer; + + return 0; +} + +static int msm8916_wcd_codec_enable_clock_block(struct snd_soc_codec *codec, + int enable) +{ + struct msm8916_wcd_chip *msm8916_wcd = snd_soc_codec_get_drvdata(codec); + unsigned long mclk_rate; + + if (enable) { + snd_soc_update_bits(codec, LPASS_CDC_CLK_MCLK_CTL, + MCLK_CTL_MCLK_EN_MASK, + MCLK_CTL_MCLK_EN_ENABLE); + snd_soc_update_bits(codec, LPASS_CDC_CLK_PDM_CTL, + LPASS_CDC_CLK_PDM_CTL_PDM_EN_MASK | + LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK, + LPASS_CDC_CLK_PDM_CTL_PDM_EN | + LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB); + snd_soc_update_bits(codec, CDC_A_MASTER_BIAS_CTL, + MASTER_BIAS_CTL_MASTER_BIAS_EN_MASK | + MASTER_BIAS_CTL_V2L_BUFFER_EN_MASK, + MASTER_BIAS_CTL_MASTER_BIAS_EN_ENABLE | + MASTER_BIAS_CTL_V2L_BUFFER_EN_ENABLE); + snd_soc_update_bits(codec, CDC_D_CDC_RST_CTL, + RST_CTL_DIG_SW_RST_N_MASK, + RST_CTL_DIG_SW_RST_N_REMOVE_RESET); + + snd_soc_update_bits(codec, CDC_D_CDC_TOP_CLK_CTL, + TOP_CLK_CTL_A_MCLK_MCLK2_EN_MASK, + TOP_CLK_CTL_A_MCLK_EN_ENABLE | + TOP_CLK_CTL_A_MCLK2_EN_ENABLE); + + mclk_rate = clk_get_rate(msm8916_wcd->mclk); + + if (mclk_rate == 12288000) + snd_soc_update_bits(codec, LPASS_CDC_TOP_CTL, + TOP_CTL_DIG_MCLK_FREQ_MASK, + TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ); + + else if (mclk_rate == 9600000) + snd_soc_update_bits(codec, LPASS_CDC_TOP_CTL, + TOP_CTL_DIG_MCLK_FREQ_MASK, + TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ); + } else { + snd_soc_update_bits(codec, CDC_D_CDC_TOP_CLK_CTL, + TOP_CLK_CTL_A_MCLK_MCLK2_EN_MASK, 0); + snd_soc_update_bits(codec, LPASS_CDC_CLK_PDM_CTL, + LPASS_CDC_CLK_PDM_CTL_PDM_EN_MASK | + LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK, 0); + + } + return 0; +} + +static const struct msm8916_wcd_reg_mask_val wcd_reg_defaults[] = { + MSM8916_WCD_REG_VAL(CDC_A_SPKR_DAC_CTL, 0x03), + MSM8916_WCD_REG_VAL(CDC_A_CURRENT_LIMIT, 0x82), + MSM8916_WCD_REG_VAL(CDC_A_SPKR_OCP_CTL, 0xE1), +}; + +static const struct msm8916_wcd_reg_mask_val wcd_reg_defaults_2_0[] = { + MSM8916_WCD_REG_VAL(CDC_D_SEC_ACCESS, 0xA5), + MSM8916_WCD_REG_VAL(CDC_D_PERPH_RESET_CTL3, 0x0F), + MSM8916_WCD_REG_VAL(CDC_A_TX_1_2_OPAMP_BIAS, 0x4F), + MSM8916_WCD_REG_VAL(CDC_A_NCP_FBCTRL, 0x28), + MSM8916_WCD_REG_VAL(CDC_A_SPKR_DRV_CTL, 0x69), + MSM8916_WCD_REG_VAL(CDC_A_SPKR_DRV_DBG, 0x01), + MSM8916_WCD_REG_VAL(CDC_A_BOOST_EN_CTL, 0x5F), + MSM8916_WCD_REG_VAL(CDC_A_SLOPE_COMP_IP_ZERO, 0x88), + MSM8916_WCD_REG_VAL(CDC_A_SEC_ACCESS, 0xA5), + MSM8916_WCD_REG_VAL(CDC_A_PERPH_RESET_CTL3, 0x0F), + MSM8916_WCD_REG_VAL(CDC_A_CURRENT_LIMIT, 0x82), + MSM8916_WCD_REG_VAL(CDC_A_SPKR_DAC_CTL, 0x03), + MSM8916_WCD_REG_VAL(CDC_A_SPKR_OCP_CTL, 0xE1), +}; + +static const struct msm8916_wcd_reg_mask_val msm8916_wcd_reg_init_val[] = { + /** + * Initialize current threshold to 350MA + * number of wait and run cycles to 4096 + */ + {CDC_A_RX_COM_OCP_CTL, 0xFF, 0xD1}, + {CDC_A_RX_COM_OCP_COUNT, 0xFF, 0xFF}, +}; + +static int msm8916_wcd_device_up(struct snd_soc_codec *codec) +{ + struct msm8916_wcd_chip *msm8916_wcd = snd_soc_codec_get_drvdata(codec); + u32 reg; + + snd_soc_write(codec, CDC_D_PERPH_RESET_CTL4, 0x01); + snd_soc_write(codec, CDC_A_PERPH_RESET_CTL4, 0x01); + + for (reg = 0; reg < ARRAY_SIZE(msm8916_wcd_reg_init_val); reg++) + snd_soc_update_bits(codec, + msm8916_wcd_reg_init_val[reg].reg, + msm8916_wcd_reg_init_val[reg].mask, + msm8916_wcd_reg_init_val[reg].val); + + if (TOMBAK_IS_1_0(msm8916_wcd->pmic_rev)) { + for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults); reg++) + snd_soc_write(codec, wcd_reg_defaults[reg].reg, + wcd_reg_defaults[reg].val); + } else { + for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults_2_0); reg++) + snd_soc_write(codec, wcd_reg_defaults_2_0[reg].reg, + wcd_reg_defaults_2_0[reg].val); + } + + return 0; +} + +static int msm8916_wcd_codec_probe(struct snd_soc_codec *codec) +{ + struct msm8916_wcd_chip *chip = dev_get_drvdata(codec->dev); + int err; + + err = regulator_enable(chip->vddio); + if (err < 0) { + dev_err(codec->dev, "failed to enable VDDIO regulator\n"); + return err; + } + + err = regulator_enable(chip->vdd_tx_rx); + if (err < 0) { + dev_err(codec->dev, "failed to enable VDD_TX_RX regulator\n"); + return err; + } + + snd_soc_codec_set_drvdata(codec, chip); + chip->pmic_rev = snd_soc_read(codec, CDC_D_REVISION1); + chip->codec_version = snd_soc_read(codec, CDC_D_PERPH_SUBTYPE); + dev_info(codec->dev, "PMIC REV: %d\t CODEC Version: %d\n", + chip->pmic_rev, chip->codec_version); + + msm8916_wcd_device_up(codec); + /* Set initial cap mode */ + msm8916_wcd_configure_cap(codec, false, false); + + return 0; +} + +static int msm8916_wcd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u8 tx_fs_rate; + + switch (params_rate(params)) { + case 8000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ; + break; + case 16000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ; + break; + case 32000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ; + break; + case 48000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ; + break; + case 96000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_96_KHZ; + break; + case 192000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_192_KHZ; + break; + default: + dev_err(dai->codec->dev, "Invalid sampling rate %d\n", + params_rate(params)); + return -EINVAL; + } + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL, + TX_I2S_CTL_TX_I2S_FS_RATE_MASK, tx_fs_rate); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + break; + default: + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL, + TX_I2S_CTL_TX_I2S_MODE_MASK, + TX_I2S_CTL_TX_I2S_MODE_16); + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_RX_I2S_CTL, + RX_I2S_CTL_RX_I2S_MODE_MASK, + RX_I2S_CTL_RX_I2S_MODE_16); + break; + case SNDRV_PCM_FORMAT_S24_LE: + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL, + TX_I2S_CTL_TX_I2S_MODE_MASK, + TX_I2S_CTL_TX_I2S_MODE_32); + snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_RX_I2S_CTL, + RX_I2S_CTL_RX_I2S_MODE_MASK, + RX_I2S_CTL_RX_I2S_MODE_32); + break; + default: + dev_err(dai->dev, "%s: wrong format selected\n", __func__); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_route audio_map[] = { + {"RX_I2S_CLK", NULL, "CDC_CONN"}, + {"I2S RX1", NULL, "RX_I2S_CLK"}, + {"I2S RX2", NULL, "RX_I2S_CLK"}, + {"I2S RX3", NULL, "RX_I2S_CLK"}, + + {"I2S TX1", NULL, "TX_I2S_CLK"}, + {"I2S TX2", NULL, "TX_I2S_CLK"}, + + {"I2S TX1", NULL, "DEC1 MUX"}, + {"I2S TX2", NULL, "DEC2 MUX"}, + + /* RDAC Connections */ + {"HPHR DAC", NULL, "RDAC2 MUX"}, + {"RDAC2 MUX", "RX1", "RX1 CHAIN"}, + {"RDAC2 MUX", "RX2", "RX2 CHAIN"}, + + /* Headset (RX MIX1 and RX MIX2) */ + {"HEADPHONE", NULL, "HPHL PA"}, + {"HEADPHONE", NULL, "HPHR PA"}, + + {"HPHL PA", NULL, "HPHL"}, + {"HPHR PA", NULL, "HPHR"}, + {"HPHL", "Switch", "HPHL DAC"}, + {"HPHR", "Switch", "HPHR DAC"}, + {"HPHL PA", NULL, "CP"}, + {"HPHL PA", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "CP"}, + {"HPHR PA", NULL, "RX_BIAS"}, + {"HPHL DAC", NULL, "RX1 CHAIN"}, + + {"SPK_OUT", NULL, "SPK PA"}, + {"SPK PA", NULL, "SPK_RX_BIAS"}, + {"SPK PA", NULL, "SPK DAC"}, + {"SPK DAC", "Switch", "RX3 CHAIN"}, + + {"RX1 CHAIN", NULL, "RX1 CLK"}, + {"RX2 CHAIN", NULL, "RX2 CLK"}, + {"RX3 CHAIN", NULL, "RX3 CLK"}, + {"RX1 CHAIN", NULL, "RX1 MIX2"}, + {"RX2 CHAIN", NULL, "RX2 MIX2"}, + {"RX3 CHAIN", NULL, "RX3 MIX1"}, + + {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP3"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP3"}, + {"RX1 MIX2", NULL, "RX1 MIX1"}, + {"RX2 MIX2", NULL, "RX2 MIX1"}, + + {"RX1 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP3", "RX3", "I2S RX3"}, + + {"RX2 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP3", "RX3", "I2S RX3"}, + + {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP3", "RX3", "I2S RX3"}, + + /* Decimator Inputs */ + {"DEC1 MUX", "DMIC1", "DMIC1"}, + {"DEC1 MUX", "DMIC2", "DMIC2"}, + {"DEC1 MUX", "ADC1", "ADC1"}, + {"DEC1 MUX", "ADC2", "ADC2"}, + {"DEC1 MUX", "ADC3", "ADC3"}, + {"DEC1 MUX", NULL, "CDC_CONN"}, + + {"DEC2 MUX", "DMIC1", "DMIC1"}, + {"DEC2 MUX", "DMIC2", "DMIC2"}, + {"DEC2 MUX", "ADC1", "ADC1"}, + {"DEC2 MUX", "ADC2", "ADC2"}, + {"DEC2 MUX", "ADC3", "ADC3"}, + {"DEC2 MUX", NULL, "CDC_CONN"}, + + /* ADC Connections */ + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC3", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "INP2", "ADC2_INP2"}, + {"ADC2 MUX", "INP3", "ADC2_INP3"}, + + {"ADC1", NULL, "AMIC1"}, + {"ADC2_INP2", NULL, "AMIC2"}, + {"ADC2_INP3", NULL, "AMIC3"}, + + {"MIC BIAS Internal1", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal2", NULL, "INT_LDO_H"}, + {"MIC BIAS External", NULL, "INT_LDO_H"}, + {"MIC BIAS External2", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal1", NULL, "vdd-micbias"}, + {"MIC BIAS Internal2", NULL, "vdd-micbias"}, + {"MIC BIAS External", NULL, "vdd-micbias"}, + {"MIC BIAS External2", NULL, "vdd-micbias"}, +}; + +static int msm8916_wcd_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + msm8916_wcd_codec_enable_clock_block(dai->codec, 1); + return 0; +} + +static void msm8916_wcd_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + msm8916_wcd_codec_enable_clock_block(dai->codec, 0); +} + +static struct snd_soc_dai_ops msm8916_wcd_dai_ops = { + .startup = msm8916_wcd_startup, + .shutdown = msm8916_wcd_shutdown, + .hw_params = msm8916_wcd_hw_params, +}; + +static struct snd_soc_dai_driver msm8916_wcd_codec_dai[] = { + [0] = { + .name = "msm8916_wcd_i2s_rx1", + .id = MSM8916_WCD_PLAYBACK_DAI, + .playback = { + .stream_name = "AIF1 Playback", + .rates = MSM8916_WCD_RATES, + .formats = MSM8916_WCD_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 3, + }, + .ops = &msm8916_wcd_dai_ops, + }, + [1] = { + .name = "msm8916_wcd_i2s_tx1", + .id = MSM8916_WCD_CAPTURE_DAI, + .capture = { + .stream_name = "AIF1 Capture", + .rates = MSM8916_WCD_RATES, + .formats = MSM8916_WCD_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &msm8916_wcd_dai_ops, + }, +}; + +static struct snd_soc_codec_driver msm8916_wcd_codec = { + .probe = msm8916_wcd_codec_probe, + .read = msm8916_wcd_read, + .write = msm8916_wcd_write, + .reg_cache_size = MSM8916_WCD_NUM_REGISTERS, + .reg_cache_default = msm8916_wcd_reset_reg_defaults, + .reg_word_size = 1, + .controls = msm8916_wcd_snd_controls, + .num_controls = ARRAY_SIZE(msm8916_wcd_snd_controls), + .dapm_widgets = msm8916_wcd_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm8916_wcd_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static int msm8916_wcd_probe(struct platform_device *pdev) +{ + struct msm8916_wcd_chip *chip; + struct device *dev = &pdev->dev; + int ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->analog_map = dev_get_regmap(dev->parent, NULL); + if (!chip->analog_map) + return -ENXIO; + + ret = msm8916_wcd_codec_parse_dt(pdev, chip); + if (IS_ERR_VALUE(ret)) + return ret; + + ret = clk_prepare_enable(chip->mclk); + if (ret < 0) { + dev_err(dev, "failed to enable mclk %d\n", ret); + return ret; + } + + dev_set_drvdata(dev, chip); + + return snd_soc_register_codec(dev, &msm8916_wcd_codec, + msm8916_wcd_codec_dai, + ARRAY_SIZE(msm8916_wcd_codec_dai)); +} + +static int msm8916_wcd_remove(struct platform_device *pdev) +{ + struct msm8916_wcd_chip *chip = dev_get_drvdata(&pdev->dev); + + clk_disable_unprepare(chip->mclk); + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static const struct of_device_id msm8916_wcd_match_table[] = { + {.compatible = "qcom,msm8916-wcd-codec"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm8916_wcd_match_table); + +static struct platform_driver msm8916_wcd_driver = { + .driver = { + .name = "msm8916-wcd-codec", + .of_match_table = msm8916_wcd_match_table, + }, + .probe = msm8916_wcd_probe, + .remove = msm8916_wcd_remove, +}; + +module_platform_driver(msm8916_wcd_driver); + +MODULE_ALIAS("platform:spmi-wcd-codec"); +MODULE_DESCRIPTION("SPMI PMIC WCD codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm8916-wcd.h b/sound/soc/codecs/msm8916-wcd.h new file mode 100644 index 000000000000..f8e0d498bd73 --- /dev/null +++ b/sound/soc/codecs/msm8916-wcd.h @@ -0,0 +1,308 @@ +/* Copyright (c) 2016, The Linux Foundation. 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 and + * only 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. + */ +#ifndef MSM8916_WCD_H +#define MSM8916_WCD_H + +#include "msm8916-wcd-registers.h" + +#define MSM8916_WCD_NUM_REGISTERS 0x6FF +#define MSM8916_WCD_MAX_REGISTER (MSM8916_WCD_NUM_REGISTERS-1) +#define MSM8916_WCD_REG_VAL(reg, val) {reg, 0, val} +#define MSM8916_WCD_REG_VAL_MASK 0xFF +#define MSM8916_WCD_DIGITAL_REG(reg) ((reg ^ 0x0200) & 0x0FFF) +#define MSM8916_WCD_IS_DIGITAL_REG(reg) \ + (((reg >= 0x200) && (reg <= 0x4FF)) ? 1 : 0) +#define MSM8916_WCD_IS_TOMBAK_REG(reg) \ + (((reg >= 0x000) && (reg <= 0x1FF)) ? 1 : 0) +#define TOMBAK_VERSION_1_0 0 +#define TOMBAK_IS_1_0(ver) ((ver == TOMBAK_VERSION_1_0) ? 1 : 0) + +struct msm8916_wcd_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +const u8 msm8916_wcd_reg_readonly[MSM8916_WCD_NUM_REGISTERS] = { + [CDC_D_REVISION1] = 1, + [CDC_D_REVISION2] = 1, + [CDC_D_PERPH_TYPE] = 1, + [CDC_D_PERPH_SUBTYPE] = 1, + [CDC_D_INT_RT_STS] = 1, + [CDC_D_INT_SET_TYPE] = 1, + [CDC_D_INT_POLARITY_HIGH] = 1, + [CDC_D_INT_POLARITY_LOW] = 1, + [CDC_D_INT_LATCHED_STS] = 1, + [CDC_D_INT_PENDING_STS] = 1, + [CDC_D_PIN_STATUS] = 1, + [CDC_A_REVISION1] = 1, + [CDC_A_REVISION2] = 1, + [CDC_A_REVISION3] = 1, + [CDC_A_REVISION4] = 1, + [CDC_A_PERPH_TYPE] = 1, + [CDC_A_PERPH_SUBTYPE] = 1, + [CDC_A_INT_RT_STS] = 1, + [CDC_A_INT_SET_TYPE] = 1, + [CDC_A_INT_POLARITY_HIGH] = 1, + [CDC_A_INT_POLARITY_LOW] = 1, + [CDC_A_INT_LATCHED_STS] = 1, + [CDC_A_INT_PENDING_STS] = 1, + [CDC_A_MBHC_BTN_RESULT] = 1, + [CDC_A_MBHC_ZDET_ELECT_RESULT] = 1, + [CDC_A_RX_HPH_STATUS] = 1, + [CDC_A_RX_EAR_STATUS] = 1, + [CDC_A_SPKR_SAR_STATUS] = 1, + [CDC_A_SPKR_DRV_STATUS] = 1, + [LPASS_CDC_RX1_B1_CTL] = 1, + [LPASS_CDC_RX2_B1_CTL] = 1, + [LPASS_CDC_RX3_B1_CTL] = 1, + [LPASS_CDC_RX1_B6_CTL] = 1, + [LPASS_CDC_RX2_B6_CTL] = 1, + [LPASS_CDC_RX3_B6_CTL] = 1, + [LPASS_CDC_TX1_VOL_CTL_CFG] = 1, + [LPASS_CDC_TX2_VOL_CTL_CFG] = 1, + [LPASS_CDC_IIR1_COEF_B1_CTL] = 1, + [LPASS_CDC_IIR2_COEF_B1_CTL] = 1, + [LPASS_CDC_CLK_MCLK_CTL] = 1, + [LPASS_CDC_CLK_PDM_CTL] = 1, +}; + +#define MSM8916_REG_POR(reg) [reg] = reg##_POR + +u8 msm8916_wcd_reset_reg_defaults[MSM8916_WCD_NUM_REGISTERS] = { + MSM8916_REG_POR(CDC_D_REVISION1), + MSM8916_REG_POR(CDC_D_REVISION2), + MSM8916_REG_POR(CDC_D_PERPH_TYPE), + MSM8916_REG_POR(CDC_D_PERPH_SUBTYPE), + MSM8916_REG_POR(CDC_D_INT_RT_STS), + MSM8916_REG_POR(CDC_D_INT_SET_TYPE), + MSM8916_REG_POR(CDC_D_INT_POLARITY_HIGH), + MSM8916_REG_POR(CDC_D_INT_POLARITY_LOW), + MSM8916_REG_POR(CDC_D_INT_LATCHED_CLR), + MSM8916_REG_POR(CDC_D_INT_EN_SET), + MSM8916_REG_POR(CDC_D_INT_EN_CLR), + MSM8916_REG_POR(CDC_D_INT_LATCHED_STS), + MSM8916_REG_POR(CDC_D_INT_PENDING_STS), + MSM8916_REG_POR(CDC_D_INT_MID_SEL), + MSM8916_REG_POR(CDC_D_INT_PRIORITY), + MSM8916_REG_POR(CDC_D_GPIO_MODE), + MSM8916_REG_POR(CDC_D_PIN_CTL_OE), + MSM8916_REG_POR(CDC_D_PIN_CTL_DATA), + MSM8916_REG_POR(CDC_D_PIN_STATUS), + MSM8916_REG_POR(CDC_D_HDRIVE_CTL), + MSM8916_REG_POR(CDC_D_CDC_RST_CTL), + MSM8916_REG_POR(CDC_D_CDC_TOP_CLK_CTL), + MSM8916_REG_POR(CDC_D_CDC_ANA_CLK_CTL), + MSM8916_REG_POR(CDC_D_CDC_DIG_CLK_CTL), + MSM8916_REG_POR(CDC_D_CDC_CONN_TX1_CTL), + MSM8916_REG_POR(CDC_D_CDC_CONN_TX2_CTL), + MSM8916_REG_POR(CDC_D_CDC_CONN_HPHR_DAC_CTL), + MSM8916_REG_POR(CDC_D_CDC_CONN_RX1_CTL), + MSM8916_REG_POR(CDC_D_CDC_CONN_RX2_CTL), + MSM8916_REG_POR(CDC_D_CDC_CONN_RX3_CTL), + MSM8916_REG_POR(CDC_D_CDC_CONN_RX_LB_CTL), + MSM8916_REG_POR(CDC_D_CDC_RX_CTL1), + MSM8916_REG_POR(CDC_D_CDC_RX_CTL2), + MSM8916_REG_POR(CDC_D_CDC_RX_CTL3), + MSM8916_REG_POR(CDC_D_DEM_BYPASS_DATA0), + MSM8916_REG_POR(CDC_D_DEM_BYPASS_DATA1), + MSM8916_REG_POR(CDC_D_DEM_BYPASS_DATA2), + MSM8916_REG_POR(CDC_D_DEM_BYPASS_DATA3), + MSM8916_REG_POR(CDC_D_SPARE_0), + MSM8916_REG_POR(CDC_D_SPARE_1), + MSM8916_REG_POR(CDC_D_SPARE_2), + MSM8916_REG_POR(CDC_A_REVISION1), + MSM8916_REG_POR(CDC_A_REVISION2), + MSM8916_REG_POR(CDC_A_REVISION3), + MSM8916_REG_POR(CDC_A_REVISION4), + MSM8916_REG_POR(CDC_A_PERPH_TYPE), + MSM8916_REG_POR(CDC_A_PERPH_SUBTYPE), + MSM8916_REG_POR(CDC_A_INT_RT_STS), + MSM8916_REG_POR(CDC_A_INT_SET_TYPE), + MSM8916_REG_POR(CDC_A_INT_POLARITY_HIGH), + MSM8916_REG_POR(CDC_A_INT_POLARITY_LOW), + MSM8916_REG_POR(CDC_A_INT_LATCHED_CLR), + MSM8916_REG_POR(CDC_A_INT_EN_SET), + MSM8916_REG_POR(CDC_A_INT_EN_CLR), + MSM8916_REG_POR(CDC_A_INT_LATCHED_STS), + MSM8916_REG_POR(CDC_A_INT_PENDING_STS), + MSM8916_REG_POR(CDC_A_INT_MID_SEL), + MSM8916_REG_POR(CDC_A_INT_PRIORITY), + MSM8916_REG_POR(CDC_A_MICB_1_EN), + MSM8916_REG_POR(CDC_A_MICB_1_VAL), + MSM8916_REG_POR(CDC_A_MICB_1_CTL), + MSM8916_REG_POR(CDC_A_MICB_1_INT_RBIAS), + MSM8916_REG_POR(CDC_A_MICB_2_EN), + MSM8916_REG_POR(CDC_A_MBHC_DET_CTL_1), + MSM8916_REG_POR(CDC_A_MBHC_DET_CTL_2), + MSM8916_REG_POR(CDC_A_MBHC_FSM_CTL), + MSM8916_REG_POR(CDC_A_MBHC_DBNC_TIMER), + MSM8916_REG_POR(CDC_A_MBHC_BTN0_ZDETL_CTL), + MSM8916_REG_POR(CDC_A_MBHC_BTN1_ZDETM_CTL), + MSM8916_REG_POR(CDC_A_MBHC_BTN2_ZDETH_CTL), + MSM8916_REG_POR(CDC_A_MBHC_BTN3_CTL), + MSM8916_REG_POR(CDC_A_MBHC_BTN4_CTL), + MSM8916_REG_POR(CDC_A_MBHC_BTN_RESULT), + MSM8916_REG_POR(CDC_A_MBHC_ZDET_ELECT_RESULT), + MSM8916_REG_POR(CDC_A_TX_1_EN), + MSM8916_REG_POR(CDC_A_TX_2_EN), + MSM8916_REG_POR(CDC_A_TX_1_2_TEST_CTL_1), + MSM8916_REG_POR(CDC_A_TX_1_2_TEST_CTL_2), + MSM8916_REG_POR(CDC_A_TX_1_2_ATEST_CTL), + MSM8916_REG_POR(CDC_A_TX_1_2_OPAMP_BIAS), + MSM8916_REG_POR(CDC_A_TX_1_2_TXFE_CLKDIV), + MSM8916_REG_POR(CDC_A_TX_3_EN), + MSM8916_REG_POR(CDC_A_NCP_EN), + MSM8916_REG_POR(CDC_A_NCP_CLK), + MSM8916_REG_POR(CDC_A_NCP_DEGLITCH), + MSM8916_REG_POR(CDC_A_NCP_FBCTRL), + MSM8916_REG_POR(CDC_A_NCP_BIAS), + MSM8916_REG_POR(CDC_A_NCP_VCTRL), + MSM8916_REG_POR(CDC_A_NCP_TEST), + MSM8916_REG_POR(CDC_A_RX_CLOCK_DIVIDER), + MSM8916_REG_POR(CDC_A_RX_COM_OCP_CTL), + MSM8916_REG_POR(CDC_A_RX_COM_OCP_COUNT), + MSM8916_REG_POR(CDC_A_RX_COM_BIAS_DAC), + MSM8916_REG_POR(CDC_A_RX_HPH_BIAS_PA), + MSM8916_REG_POR(CDC_A_RX_HPH_BIAS_LDO_OCP), + MSM8916_REG_POR(CDC_A_RX_HPH_BIAS_CNP), + MSM8916_REG_POR(CDC_A_RX_HPH_CNP_EN), + MSM8916_REG_POR(CDC_A_RX_HPH_CNP_WG_CTL), + MSM8916_REG_POR(CDC_A_RX_HPH_CNP_WG_TIME), + MSM8916_REG_POR(CDC_A_RX_HPH_L_TEST), + MSM8916_REG_POR(CDC_A_RX_HPH_L_PA_DAC_CTL), + MSM8916_REG_POR(CDC_A_RX_HPH_R_TEST), + MSM8916_REG_POR(CDC_A_RX_HPH_R_PA_DAC_CTL), + MSM8916_REG_POR(CDC_A_RX_EAR_CTL), + MSM8916_REG_POR(CDC_A_RX_ATEST), + MSM8916_REG_POR(CDC_A_RX_HPH_STATUS), + MSM8916_REG_POR(CDC_A_RX_EAR_STATUS), + MSM8916_REG_POR(CDC_A_SPKR_DAC_CTL), + MSM8916_REG_POR(CDC_A_SPKR_DRV_CLIP_DET), + MSM8916_REG_POR(CDC_A_SPKR_DRV_CTL), + MSM8916_REG_POR(CDC_A_SPKR_ANA_BIAS_SET), + MSM8916_REG_POR(CDC_A_SPKR_OCP_CTL), + MSM8916_REG_POR(CDC_A_SPKR_PWRSTG_CTL), + MSM8916_REG_POR(CDC_A_SPKR_DRV_MISC), + MSM8916_REG_POR(CDC_A_SPKR_DRV_DBG), + MSM8916_REG_POR(CDC_A_CURRENT_LIMIT), + MSM8916_REG_POR(CDC_A_OUTPUT_VOLTAGE), + MSM8916_REG_POR(CDC_A_BYPASS_MODE), + MSM8916_REG_POR(CDC_A_BOOST_EN_CTL), + MSM8916_REG_POR(CDC_A_SLOPE_COMP_IP_ZERO), + MSM8916_REG_POR(CDC_A_RDSON_MAX_DUTY_CYCLE), + MSM8916_REG_POR(CDC_A_BOOST_TEST1_1), + MSM8916_REG_POR(CDC_A_BOOST_TEST_2), + MSM8916_REG_POR(CDC_A_SPKR_SAR_STATUS), + MSM8916_REG_POR(CDC_A_SPKR_DRV_STATUS), + MSM8916_REG_POR(CDC_A_PBUS_ADD_CSR), + MSM8916_REG_POR(CDC_A_PBUS_ADD_SEL), + MSM8916_REG_POR(LPASS_CDC_CLK_RX_RESET_CTL), + MSM8916_REG_POR(LPASS_CDC_CLK_TX_RESET_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_CLK_DMIC_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_CLK_RX_I2S_CTL), + MSM8916_REG_POR(LPASS_CDC_CLK_TX_I2S_CTL), + MSM8916_REG_POR(LPASS_CDC_CLK_OTHR_RESET_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_CLK_TX_CLK_EN_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_CLK_OTHR_CTL), + MSM8916_REG_POR(LPASS_CDC_CLK_RX_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_CLK_MCLK_CTL), + MSM8916_REG_POR(LPASS_CDC_CLK_PDM_CTL), + MSM8916_REG_POR(LPASS_CDC_CLK_SD_CTL), + MSM8916_REG_POR(LPASS_CDC_RX1_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_RX2_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_RX3_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_RX1_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_RX2_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_RX3_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_RX1_B3_CTL), + MSM8916_REG_POR(LPASS_CDC_RX2_B3_CTL), + MSM8916_REG_POR(LPASS_CDC_RX3_B3_CTL), + MSM8916_REG_POR(LPASS_CDC_RX1_B4_CTL), + MSM8916_REG_POR(LPASS_CDC_RX2_B4_CTL), + MSM8916_REG_POR(LPASS_CDC_RX3_B4_CTL), + MSM8916_REG_POR(LPASS_CDC_RX1_B5_CTL), + MSM8916_REG_POR(LPASS_CDC_RX2_B5_CTL), + MSM8916_REG_POR(LPASS_CDC_RX3_B5_CTL), + MSM8916_REG_POR(LPASS_CDC_RX1_B6_CTL), + MSM8916_REG_POR(LPASS_CDC_RX2_B6_CTL), + MSM8916_REG_POR(LPASS_CDC_RX3_B6_CTL), + MSM8916_REG_POR(LPASS_CDC_RX1_VOL_CTL_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_RX2_VOL_CTL_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_RX3_VOL_CTL_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_RX1_VOL_CTL_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_RX2_VOL_CTL_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_RX3_VOL_CTL_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_TOP_GAIN_UPDATE), + MSM8916_REG_POR(LPASS_CDC_TOP_CTL), + MSM8916_REG_POR(LPASS_CDC_DEBUG_DESER1_CTL), + MSM8916_REG_POR(LPASS_CDC_DEBUG_DESER2_CTL), + MSM8916_REG_POR(LPASS_CDC_DEBUG_B1_CTL_CFG), + MSM8916_REG_POR(LPASS_CDC_DEBUG_B2_CTL_CFG), + MSM8916_REG_POR(LPASS_CDC_DEBUG_B3_CTL_CFG), + MSM8916_REG_POR(LPASS_CDC_IIR1_GAIN_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_GAIN_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR1_GAIN_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_GAIN_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR1_GAIN_B3_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_GAIN_B3_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR1_GAIN_B4_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_GAIN_B4_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR1_GAIN_B5_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_GAIN_B5_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR1_GAIN_B6_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_GAIN_B6_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR1_GAIN_B7_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_GAIN_B7_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR1_GAIN_B8_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_GAIN_B8_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR1_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR1_GAIN_TIMER_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_GAIN_TIMER_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR1_COEF_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_COEF_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR1_COEF_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_IIR2_COEF_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_RX1_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_RX1_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_RX1_B3_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_RX2_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_RX2_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_RX2_B3_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_RX3_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_RX3_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_TX_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_EQ1_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_EQ1_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_EQ1_B3_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_EQ1_B4_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_EQ2_B1_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_EQ2_B2_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_EQ2_B3_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_EQ2_B4_CTL), + MSM8916_REG_POR(LPASS_CDC_CONN_TX_I2S_SD1_CTL), + MSM8916_REG_POR(LPASS_CDC_TX1_VOL_CTL_TIMER), + MSM8916_REG_POR(LPASS_CDC_TX2_VOL_CTL_TIMER), + MSM8916_REG_POR(LPASS_CDC_TX1_VOL_CTL_GAIN), + MSM8916_REG_POR(LPASS_CDC_TX2_VOL_CTL_GAIN), + MSM8916_REG_POR(LPASS_CDC_TX1_VOL_CTL_CFG), + MSM8916_REG_POR(LPASS_CDC_TX2_VOL_CTL_CFG), + MSM8916_REG_POR(LPASS_CDC_TX1_MUX_CTL), + MSM8916_REG_POR(LPASS_CDC_TX2_MUX_CTL), + MSM8916_REG_POR(LPASS_CDC_TX1_CLK_FS_CTL), + MSM8916_REG_POR(LPASS_CDC_TX2_CLK_FS_CTL), + MSM8916_REG_POR(LPASS_CDC_TX1_DMIC_CTL), + MSM8916_REG_POR(LPASS_CDC_TX2_DMIC_CTL), +}; + +#endif /* MSM8916_WCD_H */ diff --git a/sound/soc/codecs/msm8x16-wcd-tables.c b/sound/soc/codecs/msm8x16-wcd-tables.c deleted file mode 100644 index c6b1fd62bacc..000000000000 --- a/sound/soc/codecs/msm8x16-wcd-tables.c +++ /dev/null @@ -1,742 +0,0 @@ -/* Copyright (c) 2014, The Linux Foundation. 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 and - * only 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 "msm8x16_wcd_registers.h" -#include "msm8x16-wcd.h" - -const u8 msm8x16_wcd_reg_readable[MSM8X16_WCD_CACHE_SIZE] = { - [MSM8X16_WCD_A_DIGITAL_REVISION1] = 1, - [MSM8X16_WCD_A_DIGITAL_REVISION2] = 1, - [MSM8X16_WCD_A_DIGITAL_PERPH_TYPE] = 1, - [MSM8X16_WCD_A_DIGITAL_PERPH_SUBTYPE] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_RT_STS] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_SET_TYPE] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_POLARITY_HIGH] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_POLARITY_LOW] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_EN_SET] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_EN_CLR] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_LATCHED_STS] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_PENDING_STS] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_MID_SEL] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_PRIORITY] = 1, - [MSM8X16_WCD_A_DIGITAL_GPIO_MODE] = 1, - [MSM8X16_WCD_A_DIGITAL_PIN_CTL_OE] = 1, - [MSM8X16_WCD_A_DIGITAL_PIN_CTL_DATA] = 1, - [MSM8X16_WCD_A_DIGITAL_PIN_STATUS] = 1, - [MSM8X16_WCD_A_DIGITAL_HDRIVE_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_RST_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_TOP_CLK_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX1_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX2_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_HPHR_DAC_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX1_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX2_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX3_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX_LB_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL1] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL2] = 1, - [MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL3] = 1, - [MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA0] = 1, - [MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA1] = 1, - [MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA2] = 1, - [MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA3] = 1, - [MSM8X16_WCD_A_DIGITAL_DIG_DEBUG_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_SPARE_0] = 1, - [MSM8X16_WCD_A_DIGITAL_SPARE_1] = 1, - [MSM8X16_WCD_A_DIGITAL_SPARE_2] = 1, - [MSM8X16_WCD_A_ANALOG_REVISION1] = 1, - [MSM8X16_WCD_A_ANALOG_REVISION2] = 1, - [MSM8X16_WCD_A_ANALOG_REVISION3] = 1, - [MSM8X16_WCD_A_ANALOG_REVISION4] = 1, - [MSM8X16_WCD_A_ANALOG_PERPH_TYPE] = 1, - [MSM8X16_WCD_A_ANALOG_PERPH_SUBTYPE] = 1, - [MSM8X16_WCD_A_ANALOG_INT_RT_STS] = 1, - [MSM8X16_WCD_A_ANALOG_INT_SET_TYPE] = 1, - [MSM8X16_WCD_A_ANALOG_INT_POLARITY_HIGH] = 1, - [MSM8X16_WCD_A_ANALOG_INT_POLARITY_LOW] = 1, - [MSM8X16_WCD_A_ANALOG_INT_EN_SET] = 1, - [MSM8X16_WCD_A_ANALOG_INT_EN_CLR] = 1, - [MSM8X16_WCD_A_ANALOG_INT_LATCHED_STS] = 1, - [MSM8X16_WCD_A_ANALOG_INT_PENDING_STS] = 1, - [MSM8X16_WCD_A_ANALOG_INT_MID_SEL] = 1, - [MSM8X16_WCD_A_ANALOG_INT_PRIORITY] = 1, - [MSM8X16_WCD_A_ANALOG_MICB_1_EN] = 1, - [MSM8X16_WCD_A_ANALOG_MICB_1_VAL] = 1, - [MSM8X16_WCD_A_ANALOG_MICB_1_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS] = 1, - [MSM8X16_WCD_A_ANALOG_MICB_2_EN] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_DBNC_TIMER] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN0_ZDETL_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN1_ZDETM_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN2_ZDETH_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN3_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN4_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT] = 1, - [MSM8X16_WCD_A_ANALOG_TX_1_EN] = 1, - [MSM8X16_WCD_A_ANALOG_TX_2_EN] = 1, - [MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_1] = 1, - [MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_2] = 1, - [MSM8X16_WCD_A_ANALOG_TX_1_2_ATEST_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_TX_1_2_OPAMP_BIAS] = 1, - [MSM8X16_WCD_A_ANALOG_TX_1_2_TXFE_CLKDIV] = 1, - [MSM8X16_WCD_A_ANALOG_TX_3_EN] = 1, - [MSM8X16_WCD_A_ANALOG_NCP_EN] = 1, - [MSM8X16_WCD_A_ANALOG_NCP_CLK] = 1, - [MSM8X16_WCD_A_ANALOG_NCP_DEGLITCH] = 1, - [MSM8X16_WCD_A_ANALOG_NCP_FBCTRL] = 1, - [MSM8X16_WCD_A_ANALOG_NCP_BIAS] = 1, - [MSM8X16_WCD_A_ANALOG_NCP_VCTRL] = 1, - [MSM8X16_WCD_A_ANALOG_NCP_TEST] = 1, - [MSM8X16_WCD_A_ANALOG_RX_CLOCK_DIVIDER] = 1, - [MSM8X16_WCD_A_ANALOG_RX_COM_OCP_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_RX_COM_OCP_COUNT] = 1, - [MSM8X16_WCD_A_ANALOG_RX_COM_BIAS_DAC] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_PA] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_LDO_OCP] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_CNP] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_EN] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_WG_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_WG_TIME] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_L_TEST] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_L_PA_DAC_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_R_TEST] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_R_PA_DAC_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_RX_EAR_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_RX_ATEST] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_STATUS] = 1, - [MSM8X16_WCD_A_ANALOG_RX_EAR_STATUS] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_DRV_CLIP_DET] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_DRV_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_ANA_BIAS_SET] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_OCP_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_PWRSTG_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_DRV_MISC] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_DRV_DBG] = 1, - [MSM8X16_WCD_A_ANALOG_CURRENT_LIMIT] = 1, - [MSM8X16_WCD_A_ANALOG_OUTPUT_VOLTAGE] = 1, - [MSM8X16_WCD_A_ANALOG_BYPASS_MODE] = 1, - [MSM8X16_WCD_A_ANALOG_BOOST_EN_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_SLOPE_COMP_IP_ZERO] = 1, - [MSM8X16_WCD_A_ANALOG_RDSON_MAX_DUTY_CYCLE] = 1, - [MSM8X16_WCD_A_ANALOG_BOOST_TEST1_1] = 1, - [MSM8X16_WCD_A_ANALOG_BOOST_TEST_2] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_SAR_STATUS] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_DRV_STATUS] = 1, - [MSM8X16_WCD_A_ANALOG_PBUS_ADD_CSR] = 1, - [MSM8X16_WCD_A_ANALOG_PBUS_ADD_SEL] = 1, - [MSM8X16_WCD_A_CDC_CLK_RX_RESET_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_TX_RESET_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_DMIC_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_OTHR_RESET_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_OTHR_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_RX_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_MCLK_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_PDM_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_SD_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX1_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX2_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX3_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX1_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX2_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX3_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX1_B3_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX2_B3_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX3_B3_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX1_B4_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX2_B4_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX3_B4_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX1_B5_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX2_B5_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX3_B5_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX1_B6_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX2_B6_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX3_B6_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_TOP_GAIN_UPDATE] = 1, - [MSM8X16_WCD_A_CDC_TOP_CTL] = 1, - [MSM8X16_WCD_A_CDC_DEBUG_DESER1_CTL] = 1, - [MSM8X16_WCD_A_CDC_DEBUG_DESER2_CTL] = 1, - [MSM8X16_WCD_A_CDC_DEBUG_B1_CTL_CFG] = 1, - [MSM8X16_WCD_A_CDC_DEBUG_B2_CTL_CFG] = 1, - [MSM8X16_WCD_A_CDC_DEBUG_B3_CTL_CFG] = 1, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B3_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B3_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B4_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B4_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B5_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B5_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B6_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B6_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B7_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B7_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B8_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B8_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR1_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_TIMER_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_TIMER_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR1_COEF_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_COEF_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR1_COEF_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_COEF_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_RX1_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_RX1_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_RX1_B3_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_RX2_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_RX2_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_RX2_B3_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_RX3_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_RX3_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_TX_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_EQ1_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_EQ1_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_EQ1_B3_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_EQ1_B4_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_EQ2_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_EQ2_B2_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_EQ2_B3_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_EQ2_B4_CTL] = 1, - [MSM8X16_WCD_A_CDC_CONN_TX_I2S_SD1_CTL] = 1, - [MSM8X16_WCD_A_CDC_TX1_VOL_CTL_TIMER] = 1, - [MSM8X16_WCD_A_CDC_TX2_VOL_CTL_TIMER] = 1, - [MSM8X16_WCD_A_CDC_TX1_VOL_CTL_GAIN] = 1, - [MSM8X16_WCD_A_CDC_TX2_VOL_CTL_GAIN] = 1, - [MSM8X16_WCD_A_CDC_TX1_VOL_CTL_CFG] = 1, - [MSM8X16_WCD_A_CDC_TX2_VOL_CTL_CFG] = 1, - [MSM8X16_WCD_A_CDC_TX1_MUX_CTL] = 1, - [MSM8X16_WCD_A_CDC_TX2_MUX_CTL] = 1, - [MSM8X16_WCD_A_CDC_TX1_CLK_FS_CTL] = 1, - [MSM8X16_WCD_A_CDC_TX2_CLK_FS_CTL] = 1, - [MSM8X16_WCD_A_CDC_TX1_DMIC_CTL] = 1, - [MSM8X16_WCD_A_CDC_TX2_DMIC_CTL] = 1, - [MSM8X16_WCD_A_ANALOG_MASTER_BIAS_CTL] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_LATCHED_CLR] = 1, - [MSM8X16_WCD_A_ANALOG_INT_LATCHED_CLR] = 1, - [MSM8X16_WCD_A_ANALOG_NCP_CLIM_ADDR] = 1, - [MSM8X16_WCD_A_DIGITAL_SEC_ACCESS] = 1, - [MSM8X16_WCD_A_DIGITAL_PERPH_RESET_CTL3] = 1, - [MSM8X16_WCD_A_ANALOG_SEC_ACCESS] = 1, -}; - -const u8 msm8x16_wcd_reg_readonly[MSM8X16_WCD_CACHE_SIZE] = { - [MSM8X16_WCD_A_DIGITAL_REVISION1] = 1, - [MSM8X16_WCD_A_DIGITAL_REVISION2] = 1, - [MSM8X16_WCD_A_DIGITAL_PERPH_TYPE] = 1, - [MSM8X16_WCD_A_DIGITAL_PERPH_SUBTYPE] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_RT_STS] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_SET_TYPE] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_POLARITY_HIGH] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_POLARITY_LOW] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_LATCHED_STS] = 1, - [MSM8X16_WCD_A_DIGITAL_INT_PENDING_STS] = 1, - [MSM8X16_WCD_A_DIGITAL_PIN_STATUS] = 1, - [MSM8X16_WCD_A_ANALOG_REVISION1] = 1, - [MSM8X16_WCD_A_ANALOG_REVISION2] = 1, - [MSM8X16_WCD_A_ANALOG_REVISION3] = 1, - [MSM8X16_WCD_A_ANALOG_REVISION4] = 1, - [MSM8X16_WCD_A_ANALOG_PERPH_TYPE] = 1, - [MSM8X16_WCD_A_ANALOG_PERPH_SUBTYPE] = 1, - [MSM8X16_WCD_A_ANALOG_INT_RT_STS] = 1, - [MSM8X16_WCD_A_ANALOG_INT_SET_TYPE] = 1, - [MSM8X16_WCD_A_ANALOG_INT_POLARITY_HIGH] = 1, - [MSM8X16_WCD_A_ANALOG_INT_POLARITY_LOW] = 1, - [MSM8X16_WCD_A_ANALOG_INT_LATCHED_STS] = 1, - [MSM8X16_WCD_A_ANALOG_INT_PENDING_STS] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT] = 1, - [MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT] = 1, - [MSM8X16_WCD_A_ANALOG_RX_HPH_STATUS] = 1, - [MSM8X16_WCD_A_ANALOG_RX_EAR_STATUS] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_SAR_STATUS] = 1, - [MSM8X16_WCD_A_ANALOG_SPKR_DRV_STATUS] = 1, - [MSM8X16_WCD_A_CDC_RX1_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX2_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX3_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX1_B6_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX2_B6_CTL] = 1, - [MSM8X16_WCD_A_CDC_RX3_B6_CTL] = 1, - [MSM8X16_WCD_A_CDC_TX1_VOL_CTL_CFG] = 1, - [MSM8X16_WCD_A_CDC_TX2_VOL_CTL_CFG] = 1, - [MSM8X16_WCD_A_CDC_IIR1_COEF_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_IIR2_COEF_B1_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_MCLK_CTL] = 1, - [MSM8X16_WCD_A_CDC_CLK_PDM_CTL] = 1, -}; - -u8 msm8x16_wcd_reset_reg_defaults[MSM8X16_WCD_CACHE_SIZE] = { - [MSM8X16_WCD_A_DIGITAL_REVISION1] = - MSM8X16_WCD_A_DIGITAL_REVISION1__POR, - [MSM8X16_WCD_A_DIGITAL_REVISION2] = - MSM8X16_WCD_A_DIGITAL_REVISION2__POR, - [MSM8X16_WCD_A_DIGITAL_PERPH_TYPE] = - MSM8X16_WCD_A_DIGITAL_PERPH_TYPE__POR, - [MSM8X16_WCD_A_DIGITAL_PERPH_SUBTYPE] = - MSM8X16_WCD_A_DIGITAL_PERPH_SUBTYPE__POR, - [MSM8X16_WCD_A_DIGITAL_INT_RT_STS] = - MSM8X16_WCD_A_DIGITAL_INT_RT_STS__POR, - [MSM8X16_WCD_A_DIGITAL_INT_SET_TYPE] = - MSM8X16_WCD_A_DIGITAL_INT_SET_TYPE__POR, - [MSM8X16_WCD_A_DIGITAL_INT_POLARITY_HIGH] = - MSM8X16_WCD_A_DIGITAL_INT_POLARITY_HIGH__POR, - [MSM8X16_WCD_A_DIGITAL_INT_POLARITY_LOW] = - MSM8X16_WCD_A_DIGITAL_INT_POLARITY_LOW__POR, - [MSM8X16_WCD_A_DIGITAL_INT_LATCHED_CLR] = - MSM8X16_WCD_A_DIGITAL_INT_LATCHED_CLR__POR, - [MSM8X16_WCD_A_DIGITAL_INT_EN_SET] = - MSM8X16_WCD_A_DIGITAL_INT_EN_SET__POR, - [MSM8X16_WCD_A_DIGITAL_INT_EN_CLR] = - MSM8X16_WCD_A_DIGITAL_INT_EN_CLR__POR, - [MSM8X16_WCD_A_DIGITAL_INT_LATCHED_STS] = - MSM8X16_WCD_A_DIGITAL_INT_LATCHED_STS__POR, - [MSM8X16_WCD_A_DIGITAL_INT_PENDING_STS] = - MSM8X16_WCD_A_DIGITAL_INT_PENDING_STS__POR, - [MSM8X16_WCD_A_DIGITAL_INT_MID_SEL] = - MSM8X16_WCD_A_DIGITAL_INT_MID_SEL__POR, - [MSM8X16_WCD_A_DIGITAL_INT_PRIORITY] = - MSM8X16_WCD_A_DIGITAL_INT_PRIORITY__POR, - [MSM8X16_WCD_A_DIGITAL_GPIO_MODE] = - MSM8X16_WCD_A_DIGITAL_GPIO_MODE__POR, - [MSM8X16_WCD_A_DIGITAL_PIN_CTL_OE] = - MSM8X16_WCD_A_DIGITAL_PIN_CTL_OE__POR, - [MSM8X16_WCD_A_DIGITAL_PIN_CTL_DATA] = - MSM8X16_WCD_A_DIGITAL_PIN_CTL_DATA__POR, - [MSM8X16_WCD_A_DIGITAL_PIN_STATUS] = - MSM8X16_WCD_A_DIGITAL_PIN_STATUS__POR, - [MSM8X16_WCD_A_DIGITAL_HDRIVE_CTL] = - MSM8X16_WCD_A_DIGITAL_HDRIVE_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_RST_CTL] = - MSM8X16_WCD_A_DIGITAL_CDC_RST_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_TOP_CLK_CTL] = - MSM8X16_WCD_A_DIGITAL_CDC_TOP_CLK_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL] = - MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL] = - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX1_CTL] = - MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX1_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX2_CTL] = - MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX2_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_HPHR_DAC_CTL] = - MSM8X16_WCD_A_DIGITAL_CDC_CONN_HPHR_DAC_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX1_CTL] = - MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX1_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX2_CTL] = - MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX2_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX3_CTL] = - MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX3_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX_LB_CTL] = - MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX_LB_CTL__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL1] = - MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL1__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL2] = - MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL2__POR, - [MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL3] = - MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL3__POR, - [MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA0] = - MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA0__POR, - [MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA1] = - MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA1__POR, - [MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA2] = - MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA2__POR, - [MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA3] = - MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA3__POR, - [MSM8X16_WCD_A_DIGITAL_SPARE_0] = - MSM8X16_WCD_A_DIGITAL_SPARE_0__POR, - [MSM8X16_WCD_A_DIGITAL_SPARE_1] = - MSM8X16_WCD_A_DIGITAL_SPARE_1__POR, - [MSM8X16_WCD_A_DIGITAL_SPARE_2] = - MSM8X16_WCD_A_DIGITAL_SPARE_2__POR, - [MSM8X16_WCD_A_ANALOG_REVISION1] = - MSM8X16_WCD_A_ANALOG_REVISION1__POR, - [MSM8X16_WCD_A_ANALOG_REVISION2] = - MSM8X16_WCD_A_ANALOG_REVISION2__POR, - [MSM8X16_WCD_A_ANALOG_REVISION3] = - MSM8X16_WCD_A_ANALOG_REVISION3__POR, - [MSM8X16_WCD_A_ANALOG_REVISION4] = - MSM8X16_WCD_A_ANALOG_REVISION4__POR, - [MSM8X16_WCD_A_ANALOG_PERPH_TYPE] = - MSM8X16_WCD_A_ANALOG_PERPH_TYPE__POR, - [MSM8X16_WCD_A_ANALOG_PERPH_SUBTYPE] = - MSM8X16_WCD_A_ANALOG_PERPH_SUBTYPE__POR, - [MSM8X16_WCD_A_ANALOG_INT_RT_STS] = - MSM8X16_WCD_A_ANALOG_INT_RT_STS__POR, - [MSM8X16_WCD_A_ANALOG_INT_SET_TYPE] = - MSM8X16_WCD_A_ANALOG_INT_SET_TYPE__POR, - [MSM8X16_WCD_A_ANALOG_INT_POLARITY_HIGH] = - MSM8X16_WCD_A_ANALOG_INT_POLARITY_HIGH__POR, - [MSM8X16_WCD_A_ANALOG_INT_POLARITY_LOW] = - MSM8X16_WCD_A_ANALOG_INT_POLARITY_LOW__POR, - [MSM8X16_WCD_A_ANALOG_INT_LATCHED_CLR] = - MSM8X16_WCD_A_ANALOG_INT_LATCHED_CLR__POR, - [MSM8X16_WCD_A_ANALOG_INT_EN_SET] = - MSM8X16_WCD_A_ANALOG_INT_EN_SET__POR, - [MSM8X16_WCD_A_ANALOG_INT_EN_CLR] = - MSM8X16_WCD_A_ANALOG_INT_EN_CLR__POR, - [MSM8X16_WCD_A_ANALOG_INT_LATCHED_STS] = - MSM8X16_WCD_A_ANALOG_INT_LATCHED_STS__POR, - [MSM8X16_WCD_A_ANALOG_INT_PENDING_STS] = - MSM8X16_WCD_A_ANALOG_INT_PENDING_STS__POR, - [MSM8X16_WCD_A_ANALOG_INT_MID_SEL] = - MSM8X16_WCD_A_ANALOG_INT_MID_SEL__POR, - [MSM8X16_WCD_A_ANALOG_INT_PRIORITY] = - MSM8X16_WCD_A_ANALOG_INT_PRIORITY__POR, - [MSM8X16_WCD_A_ANALOG_MICB_1_EN] = - MSM8X16_WCD_A_ANALOG_MICB_1_EN__POR, - [MSM8X16_WCD_A_ANALOG_MICB_1_VAL] = - MSM8X16_WCD_A_ANALOG_MICB_1_VAL__POR, - [MSM8X16_WCD_A_ANALOG_MICB_1_CTL] = - MSM8X16_WCD_A_ANALOG_MICB_1_CTL__POR, - [MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS] = - MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS__POR, - [MSM8X16_WCD_A_ANALOG_MICB_2_EN] = - MSM8X16_WCD_A_ANALOG_MICB_2_EN__POR, - [MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1] = - MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1__POR, - [MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2] = - MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2__POR, - [MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL] = - MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL__POR, - [MSM8X16_WCD_A_ANALOG_MBHC_DBNC_TIMER] = - MSM8X16_WCD_A_ANALOG_MBHC_DBNC_TIMER__POR, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN0_ZDETL_CTL] = - MSM8X16_WCD_A_ANALOG_MBHC_BTN0_ZDETL_CTL__POR, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN1_ZDETM_CTL] = - MSM8X16_WCD_A_ANALOG_MBHC_BTN1_ZDETM_CTL__POR, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN2_ZDETH_CTL] = - MSM8X16_WCD_A_ANALOG_MBHC_BTN2_ZDETH_CTL__POR, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN3_CTL] = - MSM8X16_WCD_A_ANALOG_MBHC_BTN3_CTL__POR, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN4_CTL] = - MSM8X16_WCD_A_ANALOG_MBHC_BTN4_CTL__POR, - [MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT] = - MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT__POR, - [MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT] = - MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT__POR, - [MSM8X16_WCD_A_ANALOG_TX_1_EN] = - MSM8X16_WCD_A_ANALOG_TX_1_EN__POR, - [MSM8X16_WCD_A_ANALOG_TX_2_EN] = - MSM8X16_WCD_A_ANALOG_TX_2_EN__POR, - [MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_1] = - MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_1__POR, - [MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_2] = - MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_2__POR, - [MSM8X16_WCD_A_ANALOG_TX_1_2_ATEST_CTL] = - MSM8X16_WCD_A_ANALOG_TX_1_2_ATEST_CTL__POR, - [MSM8X16_WCD_A_ANALOG_TX_1_2_OPAMP_BIAS] = - MSM8X16_WCD_A_ANALOG_TX_1_2_OPAMP_BIAS__POR, - [MSM8X16_WCD_A_ANALOG_TX_1_2_TXFE_CLKDIV] = - MSM8X16_WCD_A_ANALOG_TX_1_2_TXFE_CLKDIV__POR, - [MSM8X16_WCD_A_ANALOG_TX_3_EN] = - MSM8X16_WCD_A_ANALOG_TX_3_EN__POR, - [MSM8X16_WCD_A_ANALOG_NCP_EN] = - MSM8X16_WCD_A_ANALOG_NCP_EN__POR, - [MSM8X16_WCD_A_ANALOG_NCP_CLK] = - MSM8X16_WCD_A_ANALOG_NCP_CLK__POR, - [MSM8X16_WCD_A_ANALOG_NCP_DEGLITCH] = - MSM8X16_WCD_A_ANALOG_NCP_DEGLITCH__POR, - [MSM8X16_WCD_A_ANALOG_NCP_FBCTRL] = - MSM8X16_WCD_A_ANALOG_NCP_FBCTRL__POR, - [MSM8X16_WCD_A_ANALOG_NCP_BIAS] = - MSM8X16_WCD_A_ANALOG_NCP_BIAS__POR, - [MSM8X16_WCD_A_ANALOG_NCP_VCTRL] = - MSM8X16_WCD_A_ANALOG_NCP_VCTRL__POR, - [MSM8X16_WCD_A_ANALOG_NCP_TEST] = - MSM8X16_WCD_A_ANALOG_NCP_TEST__POR, - [MSM8X16_WCD_A_ANALOG_RX_CLOCK_DIVIDER] = - MSM8X16_WCD_A_ANALOG_RX_CLOCK_DIVIDER__POR, - [MSM8X16_WCD_A_ANALOG_RX_COM_OCP_CTL] = - MSM8X16_WCD_A_ANALOG_RX_COM_OCP_CTL__POR, - [MSM8X16_WCD_A_ANALOG_RX_COM_OCP_COUNT] = - MSM8X16_WCD_A_ANALOG_RX_COM_OCP_COUNT__POR, - [MSM8X16_WCD_A_ANALOG_RX_COM_BIAS_DAC] = - MSM8X16_WCD_A_ANALOG_RX_COM_BIAS_DAC__POR, - [MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_PA] = - MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_PA__POR, - [MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_LDO_OCP] = - MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_LDO_OCP__POR, - [MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_CNP] = - MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_CNP__POR, - [MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_EN] = - MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_EN__POR, - [MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_WG_CTL] = - MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_WG_CTL__POR, - [MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_WG_TIME] = - MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_WG_TIME__POR, - [MSM8X16_WCD_A_ANALOG_RX_HPH_L_TEST] = - MSM8X16_WCD_A_ANALOG_RX_HPH_L_TEST__POR, - [MSM8X16_WCD_A_ANALOG_RX_HPH_L_PA_DAC_CTL] = - MSM8X16_WCD_A_ANALOG_RX_HPH_L_PA_DAC_CTL__POR, - [MSM8X16_WCD_A_ANALOG_RX_HPH_R_TEST] = - MSM8X16_WCD_A_ANALOG_RX_HPH_R_TEST__POR, - [MSM8X16_WCD_A_ANALOG_RX_HPH_R_PA_DAC_CTL] = - MSM8X16_WCD_A_ANALOG_RX_HPH_R_PA_DAC_CTL__POR, - [MSM8X16_WCD_A_ANALOG_RX_EAR_CTL] = - MSM8X16_WCD_A_ANALOG_RX_EAR_CTL___POR, - [MSM8X16_WCD_A_ANALOG_RX_ATEST] = - MSM8X16_WCD_A_ANALOG_RX_ATEST__POR, - [MSM8X16_WCD_A_ANALOG_RX_HPH_STATUS] = - MSM8X16_WCD_A_ANALOG_RX_HPH_STATUS__POR, - [MSM8X16_WCD_A_ANALOG_RX_EAR_STATUS] = - MSM8X16_WCD_A_ANALOG_RX_EAR_STATUS__POR, - [MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL] = - MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL__POR, - [MSM8X16_WCD_A_ANALOG_SPKR_DRV_CLIP_DET] = - MSM8X16_WCD_A_ANALOG_SPKR_DRV_CLIP_DET__POR, - [MSM8X16_WCD_A_ANALOG_SPKR_DRV_CTL] = - MSM8X16_WCD_A_ANALOG_SPKR_DRV_CTL__POR, - [MSM8X16_WCD_A_ANALOG_SPKR_ANA_BIAS_SET] = - MSM8X16_WCD_A_ANALOG_SPKR_ANA_BIAS_SET__POR, - [MSM8X16_WCD_A_ANALOG_SPKR_OCP_CTL] = - MSM8X16_WCD_A_ANALOG_SPKR_OCP_CTL__POR, - [MSM8X16_WCD_A_ANALOG_SPKR_PWRSTG_CTL] = - MSM8X16_WCD_A_ANALOG_SPKR_PWRSTG_CTL__POR, - [MSM8X16_WCD_A_ANALOG_SPKR_DRV_MISC] = - MSM8X16_WCD_A_ANALOG_SPKR_DRV_MISC__POR, - [MSM8X16_WCD_A_ANALOG_SPKR_DRV_DBG] = - MSM8X16_WCD_A_ANALOG_SPKR_DRV_DBG__POR, - [MSM8X16_WCD_A_ANALOG_CURRENT_LIMIT] = - MSM8X16_WCD_A_ANALOG_CURRENT_LIMIT__POR, - [MSM8X16_WCD_A_ANALOG_OUTPUT_VOLTAGE] = - MSM8X16_WCD_A_ANALOG_OUTPUT_VOLTAGE__POR, - [MSM8X16_WCD_A_ANALOG_BYPASS_MODE] = - MSM8X16_WCD_A_ANALOG_BYPASS_MODE__POR, - [MSM8X16_WCD_A_ANALOG_BOOST_EN_CTL] = - MSM8X16_WCD_A_ANALOG_BOOST_EN_CTL__POR, - [MSM8X16_WCD_A_ANALOG_SLOPE_COMP_IP_ZERO] = - MSM8X16_WCD_A_ANALOG_SLOPE_COMP_IP_ZERO__POR, - [MSM8X16_WCD_A_ANALOG_RDSON_MAX_DUTY_CYCLE] = - MSM8X16_WCD_A_ANALOG_RDSON_MAX_DUTY_CYCLE__POR, - [MSM8X16_WCD_A_ANALOG_BOOST_TEST1_1] = - MSM8X16_WCD_A_ANALOG_BOOST_TEST1_1__POR, - [MSM8X16_WCD_A_ANALOG_BOOST_TEST_2] = - MSM8X16_WCD_A_ANALOG_BOOST_TEST_2__POR, - [MSM8X16_WCD_A_ANALOG_SPKR_SAR_STATUS] = - MSM8X16_WCD_A_ANALOG_SPKR_SAR_STATUS__POR, - [MSM8X16_WCD_A_ANALOG_SPKR_DRV_STATUS] = - MSM8X16_WCD_A_ANALOG_SPKR_DRV_STATUS__POR, - [MSM8X16_WCD_A_ANALOG_PBUS_ADD_CSR] = - MSM8X16_WCD_A_ANALOG_PBUS_ADD_CSR__POR, - [MSM8X16_WCD_A_ANALOG_PBUS_ADD_SEL] = - MSM8X16_WCD_A_ANALOG_PBUS_ADD_SEL__POR, - [MSM8X16_WCD_A_CDC_CLK_RX_RESET_CTL] = - MSM8X16_WCD_A_CDC_CLK_RX_RESET_CTL__POR, - [MSM8X16_WCD_A_CDC_CLK_TX_RESET_B1_CTL] = - MSM8X16_WCD_A_CDC_CLK_TX_RESET_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_CLK_DMIC_B1_CTL] = - MSM8X16_WCD_A_CDC_CLK_DMIC_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL] = - MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL__POR, - [MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL] = - MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL__POR, - [MSM8X16_WCD_A_CDC_CLK_OTHR_RESET_B1_CTL] = - MSM8X16_WCD_A_CDC_CLK_OTHR_RESET_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL] = - MSM8X16_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_CLK_OTHR_CTL] = - MSM8X16_WCD_A_CDC_CLK_OTHR_CTL__POR, - [MSM8X16_WCD_A_CDC_CLK_RX_B1_CTL] = - MSM8X16_WCD_A_CDC_CLK_RX_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_CLK_MCLK_CTL] = - MSM8X16_WCD_A_CDC_CLK_MCLK_CTL__POR, - [MSM8X16_WCD_A_CDC_CLK_PDM_CTL] = - MSM8X16_WCD_A_CDC_CLK_PDM_CTL__POR, - [MSM8X16_WCD_A_CDC_CLK_SD_CTL] = - MSM8X16_WCD_A_CDC_CLK_SD_CTL__POR, - [MSM8X16_WCD_A_CDC_RX1_B1_CTL] = - MSM8X16_WCD_A_CDC_RX1_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_RX2_B1_CTL] = - MSM8X16_WCD_A_CDC_RX2_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_RX3_B1_CTL] = - MSM8X16_WCD_A_CDC_RX3_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_RX1_B2_CTL] = - MSM8X16_WCD_A_CDC_RX1_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_RX2_B2_CTL] = - MSM8X16_WCD_A_CDC_RX2_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_RX3_B2_CTL] = - MSM8X16_WCD_A_CDC_RX3_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_RX1_B3_CTL] = - MSM8X16_WCD_A_CDC_RX1_B3_CTL__POR, - [MSM8X16_WCD_A_CDC_RX2_B3_CTL] = - MSM8X16_WCD_A_CDC_RX2_B3_CTL__POR, - [MSM8X16_WCD_A_CDC_RX3_B3_CTL] = - MSM8X16_WCD_A_CDC_RX3_B3_CTL__POR, - [MSM8X16_WCD_A_CDC_RX1_B4_CTL] = - MSM8X16_WCD_A_CDC_RX1_B4_CTL__POR, - [MSM8X16_WCD_A_CDC_RX2_B4_CTL] = - MSM8X16_WCD_A_CDC_RX2_B4_CTL__POR, - [MSM8X16_WCD_A_CDC_RX3_B4_CTL] = - MSM8X16_WCD_A_CDC_RX3_B4_CTL__POR, - [MSM8X16_WCD_A_CDC_RX1_B5_CTL] = - MSM8X16_WCD_A_CDC_RX1_B5_CTL__POR, - [MSM8X16_WCD_A_CDC_RX2_B5_CTL] = - MSM8X16_WCD_A_CDC_RX2_B5_CTL__POR, - [MSM8X16_WCD_A_CDC_RX3_B5_CTL] = - MSM8X16_WCD_A_CDC_RX3_B5_CTL__POR, - [MSM8X16_WCD_A_CDC_RX1_B6_CTL] = - MSM8X16_WCD_A_CDC_RX1_B6_CTL__POR, - [MSM8X16_WCD_A_CDC_RX2_B6_CTL] = - MSM8X16_WCD_A_CDC_RX2_B6_CTL__POR, - [MSM8X16_WCD_A_CDC_RX3_B6_CTL] = - MSM8X16_WCD_A_CDC_RX3_B6_CTL__POR, - [MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B1_CTL] = - MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B1_CTL] = - MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B1_CTL] = - MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B2_CTL] = - MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B2_CTL] = - MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B2_CTL] = - MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_TOP_GAIN_UPDATE] = - MSM8X16_WCD_A_CDC_TOP_GAIN_UPDATE__POR, - [MSM8X16_WCD_A_CDC_TOP_CTL] = - MSM8X16_WCD_A_CDC_TOP_CTL__POR, - [MSM8X16_WCD_A_CDC_DEBUG_DESER1_CTL] = - MSM8X16_WCD_A_CDC_DEBUG_DESER1_CTL__POR, - [MSM8X16_WCD_A_CDC_DEBUG_DESER2_CTL] = - MSM8X16_WCD_A_CDC_DEBUG_DESER2_CTL__POR, - [MSM8X16_WCD_A_CDC_DEBUG_B1_CTL_CFG] = - MSM8X16_WCD_A_CDC_DEBUG_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_DEBUG_B2_CTL_CFG] = - MSM8X16_WCD_A_CDC_DEBUG_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_DEBUG_B3_CTL_CFG] = - MSM8X16_WCD_A_CDC_DEBUG_B3_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B1_CTL] = - MSM8X16_WCD_A_CDC_IIR1_GAIN_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B1_CTL] = - MSM8X16_WCD_A_CDC_IIR2_GAIN_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B2_CTL] = - MSM8X16_WCD_A_CDC_IIR1_GAIN_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B2_CTL] = - MSM8X16_WCD_A_CDC_IIR2_GAIN_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B3_CTL] = - MSM8X16_WCD_A_CDC_IIR1_GAIN_B3_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B3_CTL] = - MSM8X16_WCD_A_CDC_IIR2_GAIN_B3_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B4_CTL] = - MSM8X16_WCD_A_CDC_IIR1_GAIN_B4_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B4_CTL] = - MSM8X16_WCD_A_CDC_IIR2_GAIN_B4_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B5_CTL] = - MSM8X16_WCD_A_CDC_IIR1_GAIN_B5_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B5_CTL] = - MSM8X16_WCD_A_CDC_IIR2_GAIN_B5_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B6_CTL] = - MSM8X16_WCD_A_CDC_IIR1_GAIN_B6_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B6_CTL] = - MSM8X16_WCD_A_CDC_IIR2_GAIN_B6_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B7_CTL] = - MSM8X16_WCD_A_CDC_IIR1_GAIN_B7_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B7_CTL] = - MSM8X16_WCD_A_CDC_IIR2_GAIN_B7_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_B8_CTL] = - MSM8X16_WCD_A_CDC_IIR1_GAIN_B8_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_B8_CTL] = - MSM8X16_WCD_A_CDC_IIR2_GAIN_B8_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_CTL] = - MSM8X16_WCD_A_CDC_IIR1_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_CTL] = - MSM8X16_WCD_A_CDC_IIR2_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_GAIN_TIMER_CTL] = - MSM8X16_WCD_A_CDC_IIR1_GAIN_TIMER_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_GAIN_TIMER_CTL] = - MSM8X16_WCD_A_CDC_IIR2_GAIN_TIMER_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_COEF_B1_CTL] = - MSM8X16_WCD_A_CDC_IIR1_COEF_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_COEF_B1_CTL] = - MSM8X16_WCD_A_CDC_IIR2_COEF_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR1_COEF_B2_CTL] = - MSM8X16_WCD_A_CDC_IIR1_COEF_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_IIR2_COEF_B2_CTL] = - MSM8X16_WCD_A_CDC_IIR2_COEF_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_RX1_B1_CTL] = - MSM8X16_WCD_A_CDC_CONN_RX1_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_RX1_B2_CTL] = - MSM8X16_WCD_A_CDC_CONN_RX1_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_RX1_B3_CTL] = - MSM8X16_WCD_A_CDC_CONN_RX1_B3_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_RX2_B1_CTL] = - MSM8X16_WCD_A_CDC_CONN_RX2_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_RX2_B2_CTL] = - MSM8X16_WCD_A_CDC_CONN_RX2_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_RX2_B3_CTL] = - MSM8X16_WCD_A_CDC_CONN_RX2_B3_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_RX3_B1_CTL] = - MSM8X16_WCD_A_CDC_CONN_RX3_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_RX3_B2_CTL] = - MSM8X16_WCD_A_CDC_CONN_RX3_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_TX_B1_CTL] = - MSM8X16_WCD_A_CDC_CONN_TX_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_EQ1_B1_CTL] = - MSM8X16_WCD_A_CDC_CONN_EQ1_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_EQ1_B2_CTL] = - MSM8X16_WCD_A_CDC_CONN_EQ1_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_EQ1_B3_CTL] = - MSM8X16_WCD_A_CDC_CONN_EQ1_B3_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_EQ1_B4_CTL] = - MSM8X16_WCD_A_CDC_CONN_EQ1_B4_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_EQ2_B1_CTL] = - MSM8X16_WCD_A_CDC_CONN_EQ2_B1_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_EQ2_B2_CTL] = - MSM8X16_WCD_A_CDC_CONN_EQ2_B2_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_EQ2_B3_CTL] = - MSM8X16_WCD_A_CDC_CONN_EQ2_B3_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_EQ2_B4_CTL] = - MSM8X16_WCD_A_CDC_CONN_EQ2_B4_CTL__POR, - [MSM8X16_WCD_A_CDC_CONN_TX_I2S_SD1_CTL] = - MSM8X16_WCD_A_CDC_CONN_TX_I2S_SD1_CTL__POR, - [MSM8X16_WCD_A_CDC_TX1_VOL_CTL_TIMER] = - MSM8X16_WCD_A_CDC_TX1_VOL_CTL_TIMER__POR, - [MSM8X16_WCD_A_CDC_TX2_VOL_CTL_TIMER] = - MSM8X16_WCD_A_CDC_TX2_VOL_CTL_TIMER__POR, - [MSM8X16_WCD_A_CDC_TX1_VOL_CTL_GAIN] = - MSM8X16_WCD_A_CDC_TX1_VOL_CTL_GAIN__POR, - [MSM8X16_WCD_A_CDC_TX2_VOL_CTL_GAIN] = - MSM8X16_WCD_A_CDC_TX2_VOL_CTL_GAIN__POR, - [MSM8X16_WCD_A_CDC_TX1_VOL_CTL_CFG] = - MSM8X16_WCD_A_CDC_TX1_VOL_CTL_CFG__POR, - [MSM8X16_WCD_A_CDC_TX2_VOL_CTL_CFG] = - MSM8X16_WCD_A_CDC_TX2_VOL_CTL_CFG__POR, - [MSM8X16_WCD_A_CDC_TX1_MUX_CTL] = - MSM8X16_WCD_A_CDC_TX1_MUX_CTL__POR, - [MSM8X16_WCD_A_CDC_TX2_MUX_CTL] = - MSM8X16_WCD_A_CDC_TX2_MUX_CTL__POR, - [MSM8X16_WCD_A_CDC_TX1_CLK_FS_CTL] = - MSM8X16_WCD_A_CDC_TX1_CLK_FS_CTL__POR, - [MSM8X16_WCD_A_CDC_TX2_CLK_FS_CTL] = - MSM8X16_WCD_A_CDC_TX2_CLK_FS_CTL__POR, - [MSM8X16_WCD_A_CDC_TX1_DMIC_CTL] = - MSM8X16_WCD_A_CDC_TX1_DMIC_CTL__POR, - [MSM8X16_WCD_A_CDC_TX2_DMIC_CTL] = - MSM8X16_WCD_A_CDC_TX2_DMIC_CTL__POR, -}; diff --git a/sound/soc/codecs/msm8x16-wcd.c b/sound/soc/codecs/msm8x16-wcd.c deleted file mode 100644 index 77a13cffbb14..000000000000 --- a/sound/soc/codecs/msm8x16-wcd.c +++ /dev/null @@ -1,2271 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "msm8x16-wcd.h" -#include "msm8x16_wcd_registers.h" - -#define MSM8X16_WCD_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) -#define MSM8X16_WCD_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S24_LE) - -#define TOMBAK_VERSION_1_0 0 -#define TOMBAK_IS_1_0(ver) \ - ((ver == TOMBAK_VERSION_1_0) ? 1 : 0) - -#define HPHL_PA_DISABLE (0x01 << 1) -#define HPHR_PA_DISABLE (0x01 << 2) -#define EAR_PA_DISABLE (0x01 << 3) -#define SPKR_PA_DISABLE (0x01 << 4) - -#define MICBIAS_DEFAULT_VAL 1800000 -#define MICBIAS_MIN_VAL 1600000 -#define MICBIAS_STEP_SIZE 50000 - -#define DEFAULT_BOOST_VOLTAGE 5000 -#define MIN_BOOST_VOLTAGE 4000 -#define MAX_BOOST_VOLTAGE 5550 -#define BOOST_VOLTAGE_STEP 50 - -#define VOLTAGE_CONVERTER(value, min_value, step_size)\ - ((value - min_value)/step_size); - -enum { - AIF1_PB = 0, - AIF1_CAP, - NUM_CODEC_DAIS, -}; - -static unsigned long rx_digital_gain_reg[] = { - MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B2_CTL, - MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B2_CTL, - MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B2_CTL, -}; - -static unsigned long tx_digital_gain_reg[] = { - MSM8X16_WCD_A_CDC_TX1_VOL_CTL_GAIN, - MSM8X16_WCD_A_CDC_TX2_VOL_CTL_GAIN, -}; - -struct wcd_chip { - struct regmap *analog_map; - struct regmap *digital_map; - unsigned int analog_base; - u16 pmic_rev; - u16 codec_version; - bool spk_boost_set; - u32 mute_mask; - u32 rx_bias_count; - bool ear_pa_boost_set; - bool lb_mode; - struct clk *mclk; - - struct regulator *vddio; - struct regulator *vdd_pa; - struct regulator *vdd_px; - struct regulator *vdd_cp; - struct regulator *vdd_mic_bias; -}; - -static int msm8x16_wcd_volatile(struct snd_soc_codec *codec, unsigned int reg) -{ - return msm8x16_wcd_reg_readonly[reg]; -} - -static int msm8x16_wcd_readable(struct snd_soc_codec *ssc, unsigned int reg) -{ - return msm8x16_wcd_reg_readable[reg]; -} - -static int __msm8x16_wcd_reg_write(struct snd_soc_codec *codec, - unsigned short reg, u8 val) -{ - int ret = -EINVAL; - struct wcd_chip *chip = dev_get_drvdata(codec->dev); - - if (MSM8X16_WCD_IS_TOMBAK_REG(reg)) { - ret = regmap_write(chip->analog_map, - chip->analog_base + reg, val); - } else if (MSM8X16_WCD_IS_DIGITAL_REG(reg)) { - u32 temp = val & 0x000000FF; - u16 offset = (reg ^ 0x0200) & 0x0FFF; - - ret = regmap_write(chip->digital_map, offset, temp); - } - - return ret; -} - -static int msm8x16_wcd_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - if (reg == SND_SOC_NOPM) - return 0; - - BUG_ON(reg > MSM8X16_WCD_MAX_REGISTER); - if (!msm8x16_wcd_volatile(codec, reg)) - msm8x16_wcd_reset_reg_defaults[reg] = value; - - return __msm8x16_wcd_reg_write(codec, reg, (u8)value); -} - -static int __msm8x16_wcd_reg_read(struct snd_soc_codec *codec, - unsigned short reg) -{ - int ret = -EINVAL; - u32 temp = 0; - struct wcd_chip *chip = dev_get_drvdata(codec->dev); - - if (MSM8X16_WCD_IS_TOMBAK_REG(reg)) { - ret = regmap_read(chip->analog_map, - chip->analog_base + reg, &temp); - } else if (MSM8X16_WCD_IS_DIGITAL_REG(reg)) { - u32 val; - u16 offset = (reg ^ 0x0200) & 0x0FFF; - - ret = regmap_read(chip->digital_map, offset, &val); - temp = (u8)val; - } - - if (ret < 0) { - dev_err(codec->dev, - "%s: codec read failed for reg 0x%x\n", - __func__, reg); - return ret; - } - - dev_dbg(codec->dev, "Read 0x%02x from 0x%x\n", temp, reg); - - return temp; -} - -static unsigned int msm8x16_wcd_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - unsigned int val; - - if (reg == SND_SOC_NOPM) - return 0; - - BUG_ON(reg > MSM8X16_WCD_MAX_REGISTER); - - if (!msm8x16_wcd_volatile(codec, reg) && - msm8x16_wcd_readable(codec, reg) && - reg < codec->driver->reg_cache_size) { - return msm8x16_wcd_reset_reg_defaults[reg]; - } - - val = __msm8x16_wcd_reg_read(codec, reg); - - return val; -} - -static const struct msm8x16_wcd_reg_mask_val msm8x16_wcd_reg_defaults[] = { - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL, 0x03), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_CURRENT_LIMIT, 0x82), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_SPKR_OCP_CTL, 0xE1), -}; - -static const struct msm8x16_wcd_reg_mask_val msm8x16_wcd_reg_defaults_2_0[] = { - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_DIGITAL_PERPH_RESET_CTL3, 0x0F), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_TX_1_2_OPAMP_BIAS, 0x4B), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_NCP_FBCTRL, 0x28), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_SPKR_DRV_CTL, 0x69), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_SPKR_DRV_DBG, 0x01), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_BOOST_EN_CTL, 0x5F), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_SLOPE_COMP_IP_ZERO, 0x88), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL3, 0x0F), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_CURRENT_LIMIT, 0x82), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL, 0x03), - MSM8X16_WCD_REG_VAL(MSM8X16_WCD_A_ANALOG_SPKR_OCP_CTL, 0xE1), -}; - -static int msm8x16_wcd_bringup(struct snd_soc_codec *codec) -{ - snd_soc_write(codec, MSM8X16_WCD_A_DIGITAL_PERPH_RESET_CTL4, 0x01); - snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL4, 0x01); - return 0; -} - -static const struct msm8x16_wcd_reg_mask_val - msm8x16_wcd_codec_reg_init_val[] = { - - /* Initialize current threshold to 350MA - * number of wait and run cycles to 4096 - */ - {MSM8X16_WCD_A_ANALOG_RX_COM_OCP_CTL, 0xFF, 0xD1}, - {MSM8X16_WCD_A_ANALOG_RX_COM_OCP_COUNT, 0xFF, 0xFF}, -}; - -static void msm8x16_wcd_codec_init_reg(struct snd_soc_codec *codec) -{ - u32 i; - - for (i = 0; i < ARRAY_SIZE(msm8x16_wcd_codec_reg_init_val); i++) - snd_soc_update_bits(codec, - msm8x16_wcd_codec_reg_init_val[i].reg, - msm8x16_wcd_codec_reg_init_val[i].mask, - msm8x16_wcd_codec_reg_init_val[i].val); -} - -static void msm8x16_wcd_update_reg_defaults(struct snd_soc_codec *codec) -{ - u32 i; - struct wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - - if (TOMBAK_IS_1_0(msm8x16_wcd->pmic_rev)) { - for (i = 0; i < ARRAY_SIZE(msm8x16_wcd_reg_defaults); i++) - snd_soc_write(codec, msm8x16_wcd_reg_defaults[i].reg, - msm8x16_wcd_reg_defaults[i].val); - } else { - for (i = 0; i < ARRAY_SIZE(msm8x16_wcd_reg_defaults_2_0); i++) - snd_soc_write(codec, - msm8x16_wcd_reg_defaults_2_0[i].reg, - msm8x16_wcd_reg_defaults_2_0[i].val); - } -} - -static int msm8x16_wcd_device_up(struct snd_soc_codec *codec) -{ - u32 reg; - - dev_dbg(codec->dev, "%s: device up!\n", __func__); - msm8x16_wcd_bringup(codec); - - for (reg = 0; reg < ARRAY_SIZE(msm8x16_wcd_reset_reg_defaults); reg++) - if (msm8x16_wcd_reg_readable[reg]) - msm8x16_wcd_write(codec, - reg, msm8x16_wcd_reset_reg_defaults[reg]); - - /* delay is required to make sure sound card state updated */ - usleep_range(5000, 5100); - - msm8x16_wcd_codec_init_reg(codec); - msm8x16_wcd_update_reg_defaults(codec); - - return 0; -} - -static int msm8x16_wcd_codec_enable_clock_block(struct snd_soc_codec *codec, - int enable) -{ - struct wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - unsigned long mclk_rate; - - if (enable) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_CLK_MCLK_CTL, 0x01, 0x01); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_CLK_PDM_CTL, 0x03, 0x03); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_MASTER_BIAS_CTL, 0x30, 0x30); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_RST_CTL, 0x80, 0x80); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x0C); - - mclk_rate = clk_get_rate(msm8x16_wcd->mclk); - - if (mclk_rate == 12288000) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_TOP_CTL, 0x01, 0x00); - else if (mclk_rate == 9600000) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_TOP_CTL, 0x01, 0x01); - } else { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x00); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_CLK_PDM_CTL, 0x03, 0x00); - - } - return 0; -} - -#define MICBIAS_EXT_BYP_CAP 0x00 -#define MICBIAS_NO_EXT_BYP_CAP 0x01 - -static void msm8x16_wcd_configure_cap(struct snd_soc_codec *codec, - bool micbias1, bool micbias2) -{ - -// struct msm8916_asoc_mach_data *pdata = NULL; -//FIXME should come from DT - int micbias1_cap_mode = MICBIAS_EXT_BYP_CAP, micbias2_cap_mode = MICBIAS_NO_EXT_BYP_CAP; - - //pdata = snd_soc_card_get_drvdata(codec->card); - - pr_debug("\n %s: micbias1 %x micbias2 = %d\n", __func__, micbias1, - micbias2); - if (micbias1 && micbias2) { - if ((micbias1_cap_mode - == MICBIAS_EXT_BYP_CAP) || - (micbias2_cap_mode - == MICBIAS_EXT_BYP_CAP)) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_MICB_1_EN, - 0x40, (MICBIAS_EXT_BYP_CAP << 6)); - else - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_MICB_1_EN, - 0x40, (MICBIAS_NO_EXT_BYP_CAP << 6)); - } else if (micbias2) { - snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN, - 0x40, (micbias2_cap_mode << 6)); - } else if (micbias1) { - snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN, - 0x40, (micbias1_cap_mode << 6)); - } else { - snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN, - 0x40, 0x00); - } -} - -static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) -{ - struct wcd_chip *chip = dev_get_drvdata(codec->dev); - int err; - - snd_soc_codec_set_drvdata(codec, chip); - - regulator_set_voltage(chip->vddio, 1800000, 1800000); - err = regulator_enable(chip->vddio); - if (err < 0) { - dev_err(codec->dev, "failed to enable VDD regulator\n"); - return err; - } - regulator_set_voltage(chip->vdd_pa, 1800000, 2200000); - err = regulator_enable(chip->vdd_pa); - if (err < 0) { - dev_err(codec->dev, "failed to enable VDD regulator\n"); - return err; - } - - regulator_set_voltage(chip->vdd_mic_bias, 3075000, 3075000); - err = regulator_enable(chip->vdd_mic_bias); - if (err < 0) { - dev_err(codec->dev, "failed to enable micbias regulator\n"); - return err; - } - - chip->pmic_rev = snd_soc_read(codec, MSM8X16_WCD_A_DIGITAL_REVISION1); - dev_info(codec->dev, "%s :PMIC REV: %d", __func__, - chip->pmic_rev); - - chip->codec_version = snd_soc_read(codec, - MSM8X16_WCD_A_DIGITAL_PERPH_SUBTYPE); - dev_info(codec->dev, "%s :CODEC Version: %d", __func__, - chip->codec_version); - - msm8x16_wcd_device_up(codec); - - /* Set initial cap mode */ - msm8x16_wcd_configure_cap(codec, false, false); - - msm8x16_wcd_codec_enable_clock_block(codec, 1); - - return 0; -} - -static int msm8x16_wcd_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n", - __func__, - substream->name, substream->stream); - return 0; -} - -static void msm8x16_wcd_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - dev_dbg(dai->codec->dev, - "%s(): substream = %s stream = %d\n", __func__, - substream->name, substream->stream); -} - -static int msm8x16_wcd_set_interpolator_rate(struct snd_soc_dai *dai, - u8 rx_fs_rate_reg_val, u32 sample_rate) -{ - return 0; -} - -static int msm8x16_wcd_set_decimator_rate(struct snd_soc_dai *dai, - u8 tx_fs_rate_reg_val, u32 sample_rate) -{ - - return 0; -} - -static int msm8x16_wcd_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - u8 tx_fs_rate, rx_fs_rate; - int ret; - - dev_err(dai->codec->dev, - "%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n", - __func__, dai->name, dai->id, params_rate(params), - params_channels(params), params_format(params)); - - switch (params_rate(params)) { - case 8000: - tx_fs_rate = 0x00; - rx_fs_rate = 0x00; - break; - case 16000: - tx_fs_rate = 0x01; - rx_fs_rate = 0x20; - break; - case 32000: - tx_fs_rate = 0x02; - rx_fs_rate = 0x40; - break; - case 48000: - tx_fs_rate = 0x03; - rx_fs_rate = 0x60; - break; - case 96000: - tx_fs_rate = 0x04; - rx_fs_rate = 0x80; - break; - case 192000: - tx_fs_rate = 0x05; - rx_fs_rate = 0xA0; - break; - default: - dev_err(dai->codec->dev, - "%s: Invalid sampling rate %d\n", __func__, - params_rate(params)); - return -EINVAL; - } - - switch (substream->stream) { - case SNDRV_PCM_STREAM_CAPTURE: - snd_soc_update_bits(dai->codec, - MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL, 0x07, tx_fs_rate); - ret = msm8x16_wcd_set_decimator_rate(dai, tx_fs_rate, - params_rate(params)); - if (ret < 0) { - dev_err(dai->codec->dev, - "%s: set decimator rate failed %d\n", __func__, - ret); - return ret; - } - break; - case SNDRV_PCM_STREAM_PLAYBACK: - ret = msm8x16_wcd_set_interpolator_rate(dai, rx_fs_rate, - params_rate(params)); - if (ret < 0) { - dev_err(dai->codec->dev, - "%s: set decimator rate failed %d\n", __func__, - ret); - return ret; - } - break; - default: - dev_err(dai->codec->dev, - "%s: Invalid stream type %d\n", __func__, - substream->stream); - return -EINVAL; - } - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - snd_soc_update_bits(dai->codec, - MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL, 0x20, 0x20); - snd_soc_update_bits(dai->codec, - MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL, 0x20, 0x20); - break; - case SNDRV_PCM_FORMAT_S24_LE: - snd_soc_update_bits(dai->codec, - MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL, 0x20, 0x00); - snd_soc_update_bits(dai->codec, - MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL, 0x20, 0x00); - break; - default: - dev_err(dai->dev, "%s: wrong format selected\n", - __func__); - return -EINVAL; - } - - return 0; -} - -static int msm8x16_wcd_set_dai_sysclk(struct snd_soc_dai *dai, - int clk_id, unsigned int freq, int dir) -{ - dev_dbg(dai->codec->dev, "%s\n", __func__); - return 0; -} - -static int msm8x16_wcd_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) - -{ - dev_dbg(dai->codec->dev, "%s\n", __func__); - return 0; -} - -static int msm8x16_wcd_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) -{ - dev_dbg(dai->codec->dev, "%s\n", __func__); - - return 0; -} - -static struct snd_soc_dai_ops msm8x16_wcd_dai_ops = { - .startup = msm8x16_wcd_startup, - .shutdown = msm8x16_wcd_shutdown, - .hw_params = msm8x16_wcd_hw_params, - .set_sysclk = msm8x16_wcd_set_dai_sysclk, - .set_fmt = msm8x16_wcd_set_dai_fmt, - .set_channel_map = msm8x16_wcd_set_channel_map, -}; - -static struct snd_soc_dai_driver msm8x16_wcd_codec_dai[] = { - [0] = { - .name = "msm8x16_wcd_i2s_rx1", - .id = AIF1_PB, - .playback = { - .stream_name = "AIF1 Playback", - .rates = MSM8X16_WCD_RATES, - .formats = MSM8X16_WCD_FORMATS, - .rate_max = 192000, - .rate_min = 8000, - .channels_min = 1, - .channels_max = 3, - }, - .ops = &msm8x16_wcd_dai_ops, - }, - [1] = { - .name = "msm8x16_wcd_i2s_tx1", - .id = AIF1_CAP, - .capture = { - .stream_name = "AIF1 Capture", - .rates = MSM8X16_WCD_RATES, - .formats = MSM8X16_WCD_FORMATS, - .rate_max = 192000, - .rate_min = 8000, - .channels_min = 1, - .channels_max = 4, - }, - .ops = &msm8x16_wcd_dai_ops, - }, -}; - -static int msm8x16_wcd_codec_remove(struct snd_soc_codec *codec) -{ - /* TODO */ - return 0; -}; - -static int msm8x16_wcd_spk_boost_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct wcd_chip *msm8x16_wcd = dev_get_drvdata(codec->dev); - - if (msm8x16_wcd->spk_boost_set == false) { - ucontrol->value.integer.value[0] = 0; - } else if (msm8x16_wcd->spk_boost_set == true) { - ucontrol->value.integer.value[0] = 1; - } else { - dev_err(codec->dev, "%s: ERROR: Unsupported Speaker Boost = %d\n", - __func__, msm8x16_wcd->spk_boost_set); - return -EINVAL; - } - - dev_dbg(codec->dev, "%s: msm8x16_wcd->spk_boost_set = %d\n", __func__, - msm8x16_wcd->spk_boost_set); - return 0; -} - -static int msm8x16_wcd_spk_boost_set(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - - switch (ucontrol->value.integer.value[0]) { - case 0: - msm8x16_wcd->spk_boost_set = false; - break; - case 1: - msm8x16_wcd->spk_boost_set = true; - break; - default: - return -EINVAL; - } - dev_dbg(codec->dev, "%s: msm8x16_wcd->spk_boost_set = %d\n", - __func__, msm8x16_wcd->spk_boost_set); - return 0; -} - -static const char * const hph_text[] = { - "ZERO", "Switch", -}; - -static const struct soc_enum hph_enum = - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(hph_text), hph_text); - -static const struct snd_kcontrol_new hphl_mux[] = { - SOC_DAPM_ENUM("HPHL", hph_enum) -}; - -static const struct snd_kcontrol_new hphr_mux[] = { - SOC_DAPM_ENUM("HPHR", hph_enum) -}; - -static const struct snd_kcontrol_new spkr_switch[] = { - SOC_DAPM_SINGLE("Switch", - MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL, 7, 1, 0) -}; - -static void msm8x16_wcd_codec_enable_adc_block(struct snd_soc_codec *codec, - int enable) -{ - //struct msm8x16_wcd_priv *wcd8x16 = snd_soc_codec_get_drvdata(codec); - - dev_dbg(codec->dev, "%s %d\n", __func__, enable); - - if (enable) { - //wcd8x16->adc_count++; - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL, - 0x20, 0x20); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0x10, 0x10); - } else { - //wcd8x16->adc_count--; - //if (!wcd8x16->adc_count) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0x10, 0x00); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL, - 0x20, 0x0); - //} - } -} - -static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); -static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); - -static const char * const rx_mix1_text[] = { - "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3" -}; - -static const char * const rx_mix2_text[] = { - "ZERO", "IIR1", "IIR2" -}; - -static const char * const dec_mux_text[] = { - "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2" -}; - -static const char * const adc2_mux_text[] = { - "ZERO", "INP2", "INP3" -}; - -static const char * const rdac2_mux_text[] = { - "ZERO", "RX2", "RX1" -}; - -static const char * const iir_inp1_text[] = { - "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3" -}; - -static const char * const iir1_inp1_text[] = { - "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3" -}; - -static const struct soc_enum adc2_enum = - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adc2_mux_text), adc2_mux_text); - -/* RX1 MIX1 */ -static const struct soc_enum rx_mix1_inp1_chain_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX1_B1_CTL, - 0, 6, rx_mix1_text); - -static const struct soc_enum rx_mix1_inp2_chain_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX1_B1_CTL, - 3, 6, rx_mix1_text); - -static const struct soc_enum rx_mix1_inp3_chain_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX1_B2_CTL, - 0, 6, rx_mix1_text); -/* RX1 MIX2 */ -static const struct soc_enum rx_mix2_inp1_chain_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX1_B3_CTL, - 0, 3, rx_mix2_text); - -/* RX2 MIX1 */ -static const struct soc_enum rx2_mix1_inp1_chain_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX2_B1_CTL, - 0, 6, rx_mix1_text); - -static const struct soc_enum rx2_mix1_inp2_chain_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX2_B1_CTL, - 3, 6, rx_mix1_text); - -static const struct soc_enum rx2_mix1_inp3_chain_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX2_B1_CTL, - 0, 6, rx_mix1_text); - -/* RX2 MIX2 */ -static const struct soc_enum rx2_mix2_inp1_chain_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX2_B3_CTL, - 0, 3, rx_mix2_text); - -/* RX3 MIX1 */ -static const struct soc_enum rx3_mix1_inp1_chain_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX3_B1_CTL, - 0, 6, rx_mix1_text); - -static const struct soc_enum rx3_mix1_inp2_chain_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX3_B1_CTL, - 3, 6, rx_mix1_text); - -/* DEC */ -static const struct soc_enum dec1_mux_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_TX_B1_CTL, - 0, 6, dec_mux_text); - -static const struct soc_enum dec2_mux_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_TX_B1_CTL, - 3, 6, dec_mux_text); - -static const struct soc_enum rdac2_mux_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_DIGITAL_CDC_CONN_HPHR_DAC_CTL, - 0, 3, rdac2_mux_text); - -static const struct soc_enum iir1_inp1_mux_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_EQ1_B1_CTL, - 0, 6, iir_inp1_text); - -static const struct soc_enum iir2_inp1_mux_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_EQ2_B1_CTL, - 0, 6, iir_inp1_text); -static const struct snd_kcontrol_new iir2_inp1_mux = - SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum); - -static const struct soc_enum rx3_mix1_inp3_chain_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_CONN_RX3_B1_CTL, - 0, 6, rx_mix1_text); -static const struct snd_kcontrol_new rx_mix1_inp1_mux = - SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum); - -static const struct snd_kcontrol_new dec1_mux = - SOC_DAPM_ENUM("DEC1 MUX Mux", dec1_mux_enum); - -static const struct snd_kcontrol_new dec2_mux = - SOC_DAPM_ENUM("DEC2 MUX Mux", dec2_mux_enum); - -static const struct snd_kcontrol_new rdac2_mux = - SOC_DAPM_ENUM("RDAC2 MUX Mux", rdac2_mux_enum); - -static const struct snd_kcontrol_new iir1_inp1_mux = - SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); - -static const struct snd_kcontrol_new rx_mix1_inp2_mux = - SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum); - -static const struct snd_kcontrol_new rx_mix1_inp3_mux = - SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp3_chain_enum); - -static const struct snd_kcontrol_new rx2_mix1_inp1_mux = - SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum); - -static const struct snd_kcontrol_new rx2_mix1_inp2_mux = - SOC_DAPM_ENUM("RX2 MIX1 INP2 Mux", rx2_mix1_inp2_chain_enum); - -static const struct snd_kcontrol_new rx2_mix1_inp3_mux = - SOC_DAPM_ENUM("RX2 MIX1 INP3 Mux", rx2_mix1_inp3_chain_enum); - -static const struct snd_kcontrol_new rx3_mix1_inp1_mux = - SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum); - -static const struct snd_kcontrol_new rx3_mix1_inp2_mux = - SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum); - -static const struct snd_kcontrol_new rx3_mix1_inp3_mux = - SOC_DAPM_ENUM("RX3 MIX1 INP3 Mux", rx3_mix1_inp3_chain_enum); - -static const struct snd_kcontrol_new rx1_mix2_inp1_mux = - SOC_DAPM_ENUM("RX1 MIX2 INP1 Mux", rx_mix2_inp1_chain_enum); - -static const struct snd_kcontrol_new rx2_mix2_inp1_mux = - SOC_DAPM_ENUM("RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum); - -static const struct snd_kcontrol_new tx_adc2_mux = - SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); - -static const char * const msm8x16_wcd_loopback_mode_ctrl_text[] = { - "DISABLE", "ENABLE"}; -static const struct soc_enum msm8x16_wcd_loopback_mode_ctl_enum[] = { - SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_loopback_mode_ctrl_text), -}; - -static int msm8x16_wcd_codec_enable_on_demand_supply( - struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - int ret = 0; - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - struct wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - ret = regulator_enable(msm8x16_wcd->vdd_mic_bias); - if (ret) - dev_err(codec->dev, "%s: Failed to enable vdd micbias\n", - __func__); - break; - case SND_SOC_DAPM_POST_PMD: - ret = regulator_disable(msm8x16_wcd->vdd_mic_bias); - if (ret) - dev_err(codec->dev, "%s: Failed to disable vdd-micbias\n", - __func__); - break; - default: - break; - } - - return ret; -} - -static const char * const msm8x16_wcd_ear_pa_boost_ctrl_text[] = { - "DISABLE", "ENABLE"}; -static const struct soc_enum msm8x16_wcd_ear_pa_boost_ctl_enum[] = { - SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_ear_pa_boost_ctrl_text), -}; - -static const char * const msm8x16_wcd_ear_pa_gain_text[] = { - "POS_6_DB", "POS_1P5_DB"}; -static const struct soc_enum msm8x16_wcd_ear_pa_gain_enum[] = { - SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_ear_pa_gain_text), -}; - -static const char * const msm8x16_wcd_spk_boost_ctrl_text[] = { - "DISABLE", "ENABLE"}; -static const struct soc_enum msm8x16_wcd_spk_boost_ctl_enum[] = { - SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_spk_boost_ctrl_text), -}; - -/*cut of frequency for high pass filter*/ -static const char * const cf_text[] = { - "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz" -}; - -static const struct soc_enum cf_dec1_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_TX1_MUX_CTL, 4, 3, cf_text); - -static const struct soc_enum cf_dec2_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_TX2_MUX_CTL, 4, 3, cf_text); - -static const struct soc_enum cf_rxmix1_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_RX1_B4_CTL, 0, 3, cf_text); - -static const struct soc_enum cf_rxmix2_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_RX2_B4_CTL, 0, 3, cf_text); - -static const struct soc_enum cf_rxmix3_enum = - SOC_ENUM_SINGLE(MSM8X16_WCD_A_CDC_RX3_B4_CTL, 0, 3, cf_text); - -static const struct snd_kcontrol_new msm8x16_wcd_snd_controls[] = { - - SOC_ENUM_EXT("Speaker Boost", msm8x16_wcd_spk_boost_ctl_enum[0], - msm8x16_wcd_spk_boost_get, msm8x16_wcd_spk_boost_set), - - SOC_SINGLE_TLV("ADC1 Volume", MSM8X16_WCD_A_ANALOG_TX_1_EN, 3, - 8, 0, analog_gain), - SOC_SINGLE_TLV("ADC2 Volume", MSM8X16_WCD_A_ANALOG_TX_2_EN, 3, - 8, 0, analog_gain), - SOC_SINGLE_TLV("ADC3 Volume", MSM8X16_WCD_A_ANALOG_TX_3_EN, 3, - 8, 0, analog_gain), - - SOC_SINGLE_SX_TLV("RX1 Digital Volume", - MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B2_CTL, - 0, -84, 40, digital_gain), - SOC_SINGLE_SX_TLV("RX2 Digital Volume", - MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B2_CTL, - 0, -84, 40, digital_gain), - SOC_SINGLE_SX_TLV("RX3 Digital Volume", - MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B2_CTL, - 0, -84, 40, digital_gain), - - SOC_SINGLE("RX1 HPF Switch", - MSM8X16_WCD_A_CDC_RX1_B5_CTL, 2, 1, 0), - SOC_SINGLE("RX2 HPF Switch", - MSM8X16_WCD_A_CDC_RX2_B5_CTL, 2, 1, 0), - SOC_SINGLE("RX3 HPF Switch", - MSM8X16_WCD_A_CDC_RX3_B5_CTL, 2, 1, 0), - - SOC_ENUM("RX1 HPF cut off", cf_rxmix1_enum), - SOC_ENUM("RX2 HPF cut off", cf_rxmix2_enum), - SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum), -}; - -static const struct snd_kcontrol_new ear_pa_switch[] = { - SOC_DAPM_SINGLE("Switch", - MSM8X16_WCD_A_ANALOG_RX_EAR_CTL, 5, 1, 0) -}; - -static int msm8x16_wcd_codec_enable_ear_pa(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 wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - dev_dbg(codec->dev, - "%s: Sleeping 20ms after select EAR PA\n", - __func__); - snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_RX_EAR_CTL, - 0x80, 0x80); - break; - case SND_SOC_DAPM_POST_PMU: - dev_dbg(codec->dev, - "%s: Sleeping 20ms after enabling EAR PA\n", - __func__); - snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_RX_EAR_CTL, - 0x40, 0x40); - usleep_range(7000, 7100); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX1_B6_CTL, 0x01, 0x00); - break; - case SND_SOC_DAPM_PRE_PMD: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX1_B6_CTL, 0x01, 0x01); - msleep(20); - msm8x16_wcd->mute_mask |= EAR_PA_DISABLE; - break; - case SND_SOC_DAPM_POST_PMD: - dev_dbg(codec->dev, - "%s: Sleeping 7ms after disabling EAR PA\n", - __func__); - snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_RX_EAR_CTL, - 0x40, 0x00); - usleep_range(7000, 7100); - /* - * Reset pa select bit from ear to hph after ear pa - * is disabled to reduce ear turn off pop - */ - snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_RX_EAR_CTL, - 0x80, 0x00); - break; - } - return 0; -} - -static int msm8x16_wcd_codec_enable_adc(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - //struct snd_soc_codec *codec = w->codec; - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - u16 adc_reg; - u8 init_bit_shift; - - dev_dbg(codec->dev, "%s %d\n", __func__, event); - - adc_reg = MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_2; - - if (w->reg == MSM8X16_WCD_A_ANALOG_TX_1_EN) - init_bit_shift = 5; - else if ((w->reg == MSM8X16_WCD_A_ANALOG_TX_2_EN) || - (w->reg == MSM8X16_WCD_A_ANALOG_TX_3_EN)) - init_bit_shift = 4; - else { - dev_err(codec->dev, "%s: Error, invalid adc register\n", - __func__); - return -EINVAL; - } - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - msm8x16_wcd_codec_enable_adc_block(codec, 1); - if (w->reg == MSM8X16_WCD_A_ANALOG_TX_2_EN) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_MICB_1_CTL, 0x02, 0x02); - /* - * Add delay of 10 ms to give sufficient time for the voltage - * to shoot up and settle so that the txfe init does not - * happen when the input voltage is changing too much. - */ - usleep_range(10000, 10010); - snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, - 1 << init_bit_shift); - if (w->reg == MSM8X16_WCD_A_ANALOG_TX_1_EN) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX1_CTL, - 0x03, 0x00); - else if ((w->reg == MSM8X16_WCD_A_ANALOG_TX_2_EN) || - (w->reg == MSM8X16_WCD_A_ANALOG_TX_3_EN)) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX2_CTL, - 0x03, 0x00); - usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); - break; - case SND_SOC_DAPM_POST_PMU: - /* - * Add delay of 12 ms before deasserting the init - * to reduce the tx pop - */ - usleep_range(12000, 12010); - snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00); - usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); - break; - case SND_SOC_DAPM_POST_PMD: - msm8x16_wcd_codec_enable_adc_block(codec, 0); - if (w->reg == MSM8X16_WCD_A_ANALOG_TX_2_EN) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_MICB_1_CTL, 0x02, 0x00); - if (w->reg == MSM8X16_WCD_A_ANALOG_TX_1_EN) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX1_CTL, - 0x03, 0x02); - else if ((w->reg == MSM8X16_WCD_A_ANALOG_TX_2_EN) || - (w->reg == MSM8X16_WCD_A_ANALOG_TX_3_EN)) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX2_CTL, - 0x03, 0x02); - - break; - } - return 0; -} - -static int msm8x16_wcd_codec_enable_spk_pa(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 wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x01); - if (!msm8x16_wcd->spk_boost_set) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL, 0x10, 0x10); - usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0xE0); - if (!TOMBAK_IS_1_0(msm8x16_wcd->pmic_rev)) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_EAR_CTL, 0x01, 0x01); - break; - case SND_SOC_DAPM_POST_PMU: - usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); - if (msm8x16_wcd->spk_boost_set) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_SPKR_DRV_CTL, 0xEF, 0xEF); - else - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL, 0x10, 0x00); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX3_B6_CTL, 0x01, 0x00); - snd_soc_update_bits(codec, w->reg, 0x80, 0x80); - break; - case SND_SOC_DAPM_PRE_PMD: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX3_B6_CTL, 0x01, 0x01); - msleep(20); - msm8x16_wcd->mute_mask |= SPKR_PA_DISABLE; - snd_soc_update_bits(codec, w->reg, 0x80, 0x00); - if (msm8x16_wcd->spk_boost_set) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_SPKR_DRV_CTL, 0xEF, 0x00); - else - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL, 0x10, 0x00); - break; - case SND_SOC_DAPM_POST_PMD: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0x00); - if (!TOMBAK_IS_1_0(msm8x16_wcd->pmic_rev)) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_EAR_CTL, 0x01, 0x00); - usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x00); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x00); - break; - } - return 0; -} - -static void msm8x16_wcd_micbias_2_enable(struct snd_soc_codec *codec, bool on) -{ - if (on) { - snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_CTL, - 0x60, 0x60); - snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL, - 0xC0); - /* - * Special headset needs MICBIAS as 2.7V so wait for - * 50 msec for the MICBIAS to reach 2.7 volts. - */ - msleep(50); - snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_CTL, - 0x60, 0x00); - } -} - -static s32 g_dmic_clk_cnt; -static int msm8x16_wcd_codec_enable_dmic(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - u8 dmic_clk_en; - u16 dmic_clk_reg; - s32 *dmic_clk_cnt; - unsigned int dmic; - int ret; - char *dec_num = strpbrk(w->name, "12"); - - if (dec_num == NULL) { - dev_err(codec->dev, "%s: Invalid DMIC\n", __func__); - return -EINVAL; - } - - ret = kstrtouint(dec_num, 10, &dmic); - if (ret < 0) { - dev_err(codec->dev, - "%s: Invalid DMIC line on the codec\n", __func__); - return -EINVAL; - } - - switch (dmic) { - case 1: - case 2: - dmic_clk_en = 0x01; - dmic_clk_cnt = &g_dmic_clk_cnt; - dmic_clk_reg = MSM8X16_WCD_A_CDC_CLK_DMIC_B1_CTL; - dev_dbg(codec->dev, - "%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n", - __func__, event, dmic, *dmic_clk_cnt); - break; - default: - dev_err(codec->dev, "%s: Invalid DMIC Selection\n", __func__); - return -EINVAL; - } - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - (*dmic_clk_cnt)++; - if (*dmic_clk_cnt == 1) { - snd_soc_update_bits(codec, dmic_clk_reg, - 0x0E, 0x02); - snd_soc_update_bits(codec, dmic_clk_reg, - dmic_clk_en, dmic_clk_en); - } - if (dmic == 1) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_TX1_DMIC_CTL, 0x07, 0x01); - if (dmic == 2) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_TX2_DMIC_CTL, 0x07, 0x01); - break; - case SND_SOC_DAPM_POST_PMD: - (*dmic_clk_cnt)--; - if (*dmic_clk_cnt == 0) - snd_soc_update_bits(codec, dmic_clk_reg, - dmic_clk_en, 0); - break; - } - return 0; -} - -static int msm8x16_wcd_codec_enable_micbias(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 wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - u16 micb_int_reg; - char *internal1_text = "Internal1"; - char *internal2_text = "Internal2"; - char *internal3_text = "Internal3"; - char *external2_text = "External2"; - char *external_text = "External"; - bool micbias2; - - switch (w->reg) { - case MSM8X16_WCD_A_ANALOG_MICB_1_EN: - case MSM8X16_WCD_A_ANALOG_MICB_2_EN: - micb_int_reg = MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS; - break; - default: - dev_err(codec->dev, - "%s: Error, invalid micbias register 0x%x\n", - __func__, w->reg); - return -EINVAL; - } - - micbias2 = (snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN) & 0x80); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - if (strnstr(w->name, internal1_text, 30)) { - snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x80); - } else if (strnstr(w->name, internal2_text, 30)) { - snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x10); - snd_soc_update_bits(codec, w->reg, 0x60, 0x00); - } else if (strnstr(w->name, internal3_text, 30)) { - snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x2); - } - if (!strnstr(w->name, external_text, 30)) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_MICB_1_EN, 0x05, 0x04); - if (w->reg == MSM8X16_WCD_A_ANALOG_MICB_1_EN) - msm8x16_wcd_configure_cap(codec, true, micbias2); - - break; - case SND_SOC_DAPM_POST_PMU: - usleep_range(20000, 20100); - if (strnstr(w->name, internal1_text, 30)) { - snd_soc_update_bits(codec, micb_int_reg, 0x40, 0x40); - } else if (strnstr(w->name, internal2_text, 30)) { - snd_soc_update_bits(codec, micb_int_reg, 0x08, 0x08); - msm8x16_wcd_micbias_2_enable(codec, true); - - msm8x16_wcd_configure_cap(codec, false, true); - regmap_write(msm8x16_wcd->analog_map, 0xf144, 0x95); - } else if (strnstr(w->name, internal3_text, 30)) { - snd_soc_update_bits(codec, micb_int_reg, 0x01, 0x01); - } else if (strnstr(w->name, external2_text, 30)) { - msm8x16_wcd_micbias_2_enable(codec, true); - } - break; - case SND_SOC_DAPM_POST_PMD: - if (strnstr(w->name, internal1_text, 30)) { - snd_soc_update_bits(codec, micb_int_reg, 0xC0, 0x40); - } else if (strnstr(w->name, internal2_text, 30)) { - msm8x16_wcd_micbias_2_enable(codec, false); - } else if (strnstr(w->name, internal3_text, 30)) { - snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0); - } else if (strnstr(w->name, external2_text, 30)) { - /* - * send micbias turn off event to mbhc driver and then - * break, as no need to set MICB_1_EN register. - */ - msm8x16_wcd_micbias_2_enable(codec, false); - break; - } - if (w->reg == MSM8X16_WCD_A_ANALOG_MICB_1_EN) - msm8x16_wcd_configure_cap(codec, false, micbias2); - break; - } - - return 0; -} - -#define TX_MUX_CTL_CUT_OFF_FREQ_MASK 0x30 -#define CF_MIN_3DB_4HZ 0x0 -#define CF_MIN_3DB_75HZ 0x1 -#define CF_MIN_3DB_150HZ 0x2 - -static int msm8x16_wcd_codec_enable_dec(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 decimator; - char *dec_name = NULL; - char *widget_name = NULL; - char *temp; - int ret = 0; - u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg; - u8 dec_hpf_cut_of_freq; - int offset; - char *dec_num; - dev_dbg(codec->dev, "%s %d\n", __func__, event); - - widget_name = kstrndup(w->name, 15, GFP_KERNEL); - if (!widget_name) - return -ENOMEM; - temp = widget_name; - - dec_name = strsep(&widget_name, " "); - widget_name = temp; - if (!dec_name) { - dev_err(codec->dev, - "%s: Invalid decimator = %s\n", __func__, w->name); - ret = -EINVAL; - goto out; - } - - dec_num = strpbrk(dec_name, "12"); - if (dec_num == NULL) { - dev_err(codec->dev, "%s: Invalid Decimator\n", __func__); - ret = -EINVAL; - goto out; - } - - ret = kstrtouint(dec_num, 10, &decimator); - if (ret < 0) { - dev_err(codec->dev, - "%s: Invalid decimator = %s\n", __func__, dec_name); - ret = -EINVAL; - goto out; - } - - dev_err(codec->dev, - "%s(): widget = %s dec_name = %s decimator = %u\n", __func__, - w->name, dec_name, decimator); - - if (w->reg == MSM8X16_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL) { - dec_reset_reg = MSM8X16_WCD_A_CDC_CLK_TX_RESET_B1_CTL; - offset = 0; - } else { - dev_err(codec->dev, "%s: Error, incorrect dec\n", __func__); - ret = -EINVAL; - goto out; - } - - tx_vol_ctl_reg = MSM8X16_WCD_A_CDC_TX1_VOL_CTL_CFG + - 32 * (decimator - 1); - tx_mux_ctl_reg = MSM8X16_WCD_A_CDC_TX1_MUX_CTL + - 32 * (decimator - 1); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - /* Enableable TX digital mute */ - snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); - dec_hpf_cut_of_freq = snd_soc_read(codec, tx_mux_ctl_reg); - dec_hpf_cut_of_freq = (dec_hpf_cut_of_freq & 0x30) >> 4; - if ((dec_hpf_cut_of_freq != CF_MIN_3DB_150HZ)) { - - /* set cut of freq to CF_MIN_3DB_150HZ (0x1); */ - snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, - CF_MIN_3DB_150HZ << 4); - } - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_TX_1_2_TXFE_CLKDIV, - 0xFF, 0x42); - - break; - case SND_SOC_DAPM_POST_PMU: - /* enable HPF */ - snd_soc_update_bits(codec, tx_mux_ctl_reg , 0x08, 0x00); - /* apply the digital gain after the decimator is enabled*/ - if ((w->shift) < ARRAY_SIZE(tx_digital_gain_reg)) - snd_soc_write(codec, - tx_digital_gain_reg[w->shift + offset], - snd_soc_read(codec, - tx_digital_gain_reg[w->shift + offset]) - ); - snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); - break; - case SND_SOC_DAPM_PRE_PMD: - snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); - msleep(20); - snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); - break; - case SND_SOC_DAPM_POST_PMD: - snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, - 1 << w->shift); - snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0); - snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); - snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); - break; - } - -out: - kfree(widget_name); - return ret; -} - -static int msm8x16_wcd_codec_enable_interpolator(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 wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - /* apply the digital gain after the interpolator is enabled*/ - if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg)) - snd_soc_write(codec, - rx_digital_gain_reg[w->shift], - snd_soc_read(codec, - rx_digital_gain_reg[w->shift]) - ); - break; - case SND_SOC_DAPM_POST_PMD: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_CLK_RX_RESET_CTL, - 1 << w->shift, 1 << w->shift); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_CLK_RX_RESET_CTL, - 1 << w->shift, 0x0); - /* - * disable the mute enabled during the PMD of this device - */ - if (msm8x16_wcd->mute_mask & HPHL_PA_DISABLE) { - pr_debug("disabling HPHL mute\n"); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX1_B6_CTL, 0x01, 0x00); - msm8x16_wcd->mute_mask &= ~(HPHL_PA_DISABLE); - } - if (msm8x16_wcd->mute_mask & HPHR_PA_DISABLE) { - pr_debug("disabling HPHR mute\n"); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX2_B6_CTL, 0x01, 0x00); - msm8x16_wcd->mute_mask &= ~(HPHR_PA_DISABLE); - } - if (msm8x16_wcd->mute_mask & SPKR_PA_DISABLE) { - pr_debug("disabling SPKR mute\n"); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX3_B6_CTL, 0x01, 0x00); - msm8x16_wcd->mute_mask &= ~(SPKR_PA_DISABLE); - } - if (msm8x16_wcd->mute_mask & EAR_PA_DISABLE) { - pr_debug("disabling EAR mute\n"); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX1_B6_CTL, 0x01, 0x00); - msm8x16_wcd->mute_mask &= ~(EAR_PA_DISABLE); - } - } - return 0; -} - -static int msm8x16_wcd_codec_enable_dig_clk(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 wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - if (w->shift == 2) - snd_soc_update_bits(codec, w->reg, 0x80, 0x80); - if (msm8x16_wcd->spk_boost_set) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_SEC_ACCESS, - 0xA5, 0xA5); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL3, - 0x0F, 0x0F); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_CURRENT_LIMIT, - 0x82, 0x82); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0x20, 0x20); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_BOOST_EN_CTL, - 0xDF, 0xDF); - usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_CURRENT_LIMIT, - 0x83, 0x83); - } else if (msm8x16_wcd->ear_pa_boost_set) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_SEC_ACCESS, - 0xA5, 0xA5); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL3, - 0x07, 0x07); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_BYPASS_MODE, - 0x40, 0x40); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_BYPASS_MODE, - 0x80, 0x80); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_BYPASS_MODE, - 0x02, 0x02); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_BOOST_EN_CTL, - 0xDF, 0xDF); - } else { - snd_soc_update_bits(codec, w->reg, 1<shift, - 1<shift); - } - break; - case SND_SOC_DAPM_POST_PMD: - if (msm8x16_wcd->spk_boost_set) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_BOOST_EN_CTL, - 0xDF, 0x5F); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0x20, 0x00); - } else if (msm8x16_wcd->ear_pa_boost_set) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_BOOST_EN_CTL, - 0x80, 0x00); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_BYPASS_MODE, - 0x80, 0x00); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_BYPASS_MODE, - 0x02, 0x00); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_BYPASS_MODE, - 0x40, 0x00); - } else { - snd_soc_update_bits(codec, w->reg, 1<shift, 0x00); - } - break; - } - return 0; -} - -static int msm8x16_wcd_codec_enable_rx_chain(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0x80, 0x80); - dev_dbg(codec->dev, - "%s: PMU:Sleeping 20ms after disabling mute\n", - __func__); - break; - case SND_SOC_DAPM_POST_PMD: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0x80, 0x00); - dev_dbg(codec->dev, - "%s: PMD:Sleeping 20ms after disabling mute\n", - __func__); - snd_soc_update_bits(codec, w->reg, - 1 << w->shift, 0x00); - msleep(20); - break; - } - return 0; -} - -static int msm8x16_wcd_codec_enable_rx_bias(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 wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - msm8x16_wcd->rx_bias_count++; - if (msm8x16_wcd->rx_bias_count == 1) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_COM_BIAS_DAC, - 0x81, 0x81); - break; - case SND_SOC_DAPM_POST_PMD: - msm8x16_wcd->rx_bias_count--; - if (msm8x16_wcd->rx_bias_count == 0) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_COM_BIAS_DAC, - 0x81, 0x00); - break; - } - dev_dbg(codec->dev, "%s rx_bias_count = %d\n", - __func__, msm8x16_wcd->rx_bias_count); - return 0; -} - -static int msm8x16_wcd_codec_enable_charge_pump(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 wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - if (!(strcmp(w->name, "EAR CP"))) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0x80, 0x80); - else - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0xC0, 0xC0); - break; - case SND_SOC_DAPM_POST_PMU: - usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); - break; - case SND_SOC_DAPM_POST_PMD: - usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); - if (!(strcmp(w->name, "EAR CP"))) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0x80, 0x00); - else { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0x40, 0x00); - if (msm8x16_wcd->rx_bias_count == 0) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0x80, 0x00); - dev_dbg(codec->dev, "%s: rx_bias_count = %d\n", - __func__, msm8x16_wcd->rx_bias_count); - } - break; - } - return 0; -} - -static int msm8x16_wcd_hphl_dac_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); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x02); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x01); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x02); - break; - case SND_SOC_DAPM_POST_PMU: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x00); - break; - case SND_SOC_DAPM_POST_PMD: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x00); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x00); - break; - } - return 0; -} - -static int msm8x16_wcd_hph_pa_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 wcd_chip *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - if (w->shift == 5) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_HPH_L_TEST, 0x04, 0x04); - } else if (w->shift == 4) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_HPH_R_TEST, 0x04, 0x04); - } - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_NCP_FBCTRL, 0x20, 0x20); - break; - - case SND_SOC_DAPM_POST_PMU: - usleep_range(4000, 4100); - if (w->shift == 5) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX1_B6_CTL, 0x01, 0x00); - else if (w->shift == 4) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX2_B6_CTL, 0x01, 0x00); - usleep_range(10000, 10100); - break; - - case SND_SOC_DAPM_PRE_PMD: - if (w->shift == 5) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX1_B6_CTL, 0x01, 0x01); - msleep(20); - msm8x16_wcd->mute_mask |= HPHL_PA_DISABLE; - } else if (w->shift == 4) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX2_B6_CTL, 0x01, 0x01); - msleep(20); - msm8x16_wcd->mute_mask |= HPHR_PA_DISABLE; - } - break; - case SND_SOC_DAPM_POST_PMD: - if (w->shift == 5) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_HPH_L_TEST, 0x04, 0x00); - - } else if (w->shift == 4) { - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_HPH_R_TEST, 0x04, 0x00); - } - usleep_range(4000, 4100); - - usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, 0x40, 0x40); - dev_dbg(codec->dev, - "%s: sleep 10 ms after %s PA disable.\n", __func__, - w->name); - usleep_range(10000, 10100); - break; - } - return 0; -} - -static const struct snd_soc_dapm_route audio_map[] = { - {"RX_I2S_CLK", NULL, "CDC_CONN"}, - {"I2S RX1", NULL, "RX_I2S_CLK"}, - {"I2S RX2", NULL, "RX_I2S_CLK"}, - {"I2S RX3", NULL, "RX_I2S_CLK"}, - - {"I2S TX1", NULL, "TX_I2S_CLK"}, - {"I2S TX2", NULL, "TX_I2S_CLK"}, - - {"I2S TX1", NULL, "DEC1 MUX"}, - {"I2S TX2", NULL, "DEC2 MUX"}, - - /* RDAC Connections */ - {"HPHR DAC", NULL, "RDAC2 MUX"}, - {"RDAC2 MUX", "RX1", "RX1 CHAIN"}, - {"RDAC2 MUX", "RX2", "RX2 CHAIN"}, - - /* Earpiece (RX MIX1) */ - {"EAR", NULL, "EAR_S"}, - {"EAR_S", "Switch", "EAR PA"}, - {"EAR PA", NULL, "RX_BIAS"}, - {"EAR PA", NULL, "HPHL DAC"}, - {"EAR PA", NULL, "HPHR DAC"}, - {"EAR PA", NULL, "EAR CP"}, - - /* Headset (RX MIX1 and RX MIX2) */ - {"HEADPHONE", NULL, "HPHL PA"}, - {"HEADPHONE", NULL, "HPHR PA"}, - - {"HPHL PA", NULL, "HPHL"}, - {"HPHR PA", NULL, "HPHR"}, - {"HPHL", "Switch", "HPHL DAC"}, - {"HPHR", "Switch", "HPHR DAC"}, - {"HPHL PA", NULL, "CP"}, - {"HPHL PA", NULL, "RX_BIAS"}, - {"HPHR PA", NULL, "CP"}, - {"HPHR PA", NULL, "RX_BIAS"}, - {"HPHL DAC", NULL, "RX1 CHAIN"}, - - {"SPK_OUT", NULL, "SPK PA"}, - {"SPK PA", NULL, "SPK_RX_BIAS"}, - {"SPK PA", NULL, "SPK DAC"}, - {"SPK DAC", "Switch", "RX3 CHAIN"}, - {"SPK DAC", NULL, "VDD_SPKDRV"}, - - {"RX1 CHAIN", NULL, "RX1 CLK"}, - {"RX2 CHAIN", NULL, "RX2 CLK"}, - {"RX3 CHAIN", NULL, "RX3 CLK"}, - {"RX1 CHAIN", NULL, "RX1 MIX2"}, - {"RX2 CHAIN", NULL, "RX2 MIX2"}, - {"RX3 CHAIN", NULL, "RX3 MIX1"}, - - {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, - {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, - {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, - {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, - {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, - {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, - {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, - {"RX1 MIX2", NULL, "RX1 MIX1"}, - {"RX1 MIX2", NULL, "RX1 MIX2 INP1"}, - {"RX2 MIX2", NULL, "RX2 MIX1"}, - {"RX2 MIX2", NULL, "RX2 MIX2 INP1"}, - - {"RX1 MIX1 INP1", "RX1", "I2S RX1"}, - {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, - {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, - {"RX1 MIX1 INP1", "IIR1", "IIR1"}, - {"RX1 MIX1 INP1", "IIR2", "IIR2"}, - {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, - {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, - {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, - {"RX1 MIX1 INP2", "IIR1", "IIR1"}, - {"RX1 MIX1 INP2", "IIR2", "IIR2"}, - {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, - {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, - {"RX1 MIX1 INP3", "RX3", "I2S RX3"}, - - {"RX2 MIX1 INP1", "RX1", "I2S RX1"}, - {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, - {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, - {"RX2 MIX1 INP1", "IIR1", "IIR1"}, - {"RX2 MIX1 INP1", "IIR2", "IIR2"}, - {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, - {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, - {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, - {"RX2 MIX1 INP2", "IIR1", "IIR1"}, - {"RX2 MIX1 INP2", "IIR2", "IIR2"}, - - {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, - {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, - {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, - {"RX3 MIX1 INP1", "IIR1", "IIR1"}, - {"RX3 MIX1 INP1", "IIR2", "IIR2"}, - {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, - {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, - {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, - {"RX3 MIX1 INP2", "IIR1", "IIR1"}, - {"RX3 MIX1 INP2", "IIR2", "IIR2"}, - - {"RX1 MIX2 INP1", "IIR1", "IIR1"}, - {"RX2 MIX2 INP1", "IIR1", "IIR1"}, - {"RX1 MIX2 INP1", "IIR2", "IIR2"}, - {"RX2 MIX2 INP1", "IIR2", "IIR2"}, - - /* Decimator Inputs */ - {"DEC1 MUX", "DMIC1", "DMIC1"}, - {"DEC1 MUX", "DMIC2", "DMIC2"}, - {"DEC1 MUX", "ADC1", "ADC1"}, - {"DEC1 MUX", "ADC2", "ADC2"}, - {"DEC1 MUX", "ADC3", "ADC3"}, - {"DEC1 MUX", NULL, "CDC_CONN"}, - - {"DEC2 MUX", "DMIC1", "DMIC1"}, - {"DEC2 MUX", "DMIC2", "DMIC2"}, - {"DEC2 MUX", "ADC1", "ADC1"}, - {"DEC2 MUX", "ADC2", "ADC2"}, - {"DEC2 MUX", "ADC3", "ADC3"}, - {"DEC2 MUX", NULL, "CDC_CONN"}, - - /* ADC Connections */ - {"ADC2", NULL, "ADC2 MUX"}, - {"ADC3", NULL, "ADC2 MUX"}, - {"ADC2 MUX", "INP2", "ADC2_INP2"}, - {"ADC2 MUX", "INP3", "ADC2_INP3"}, - - {"ADC1", NULL, "AMIC1"}, - {"ADC2_INP2", NULL, "AMIC2"}, - {"ADC2_INP3", NULL, "AMIC3"}, - - /* TODO: Fix this */ - {"IIR1", NULL, "IIR1 INP1 MUX"}, - {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"}, - {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"}, - {"IIR2", NULL, "IIR2 INP1 MUX"}, - {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"}, - {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"}, - {"MIC BIAS Internal1", NULL, "INT_LDO_H"}, - {"MIC BIAS Internal2", NULL, "INT_LDO_H"}, - {"MIC BIAS External", NULL, "INT_LDO_H"}, - {"MIC BIAS External2", NULL, "INT_LDO_H"}, - {"MIC BIAS Internal1", NULL, "MICBIAS_REGULATOR"}, - {"MIC BIAS Internal2", NULL, "MICBIAS_REGULATOR"}, - {"MIC BIAS External", NULL, "MICBIAS_REGULATOR"}, - {"MIC BIAS External2", NULL, "MICBIAS_REGULATOR"}, -}; - -static int msm8x16_wcd_hphr_dac_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); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x02); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x02); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x01); - break; - case SND_SOC_DAPM_POST_PMU: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x00); - break; - case SND_SOC_DAPM_POST_PMD: - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x00); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x00); - break; - } - return 0; -} - -static const struct snd_soc_dapm_widget msm8x16_wcd_dapm_widgets[] = { - /*RX stuff */ - SND_SOC_DAPM_OUTPUT("EAR"), - - SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM, - 0, 0, NULL, 0, msm8x16_wcd_codec_enable_ear_pa, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER("EAR_S", SND_SOC_NOPM, 0, 0, - ear_pa_switch, ARRAY_SIZE(ear_pa_switch)), - - SND_SOC_DAPM_AIF_IN("I2S RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), - - SND_SOC_DAPM_AIF_IN("I2S RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), - - SND_SOC_DAPM_AIF_IN("I2S RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), - - SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0), - - SND_SOC_DAPM_OUTPUT("HEADPHONE"), - SND_SOC_DAPM_PGA_E("HPHL PA", MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_EN, - 5, 0, NULL, 0, - msm8x16_wcd_hph_pa_event, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | - SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, hphl_mux), - - SND_SOC_DAPM_MIXER_E("HPHL DAC", - MSM8X16_WCD_A_ANALOG_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, - 0, msm8x16_wcd_hphl_dac_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_PGA_E("HPHR PA", MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_EN, - 4, 0, NULL, 0, - msm8x16_wcd_hph_pa_event, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | - SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, hphr_mux), - - SND_SOC_DAPM_MIXER_E("HPHR DAC", - MSM8X16_WCD_A_ANALOG_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, - 0, msm8x16_wcd_hphr_dac_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_MIXER("SPK DAC", SND_SOC_NOPM, 0, 0, - spkr_switch, ARRAY_SIZE(spkr_switch)), - - /* Speaker */ - SND_SOC_DAPM_OUTPUT("SPK_OUT"), - - SND_SOC_DAPM_PGA_E("SPK PA", MSM8X16_WCD_A_ANALOG_SPKR_DRV_CTL, - 6, 0, NULL, 0, msm8x16_wcd_codec_enable_spk_pa, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_MIXER_E("RX1 MIX1", - MSM8X16_WCD_A_CDC_CLK_RX_B1_CTL, 0, 0, NULL, 0, - msm8x16_wcd_codec_enable_interpolator, - SND_SOC_DAPM_PRE_REG| - SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD| - SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_MIXER_E("RX2 MIX1", - MSM8X16_WCD_A_CDC_CLK_RX_B1_CTL, 1, 0, NULL, 0, - msm8x16_wcd_codec_enable_interpolator, - SND_SOC_DAPM_PRE_REG| - SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_MIXER_E("RX1 MIX2", - MSM8X16_WCD_A_CDC_CLK_RX_B1_CTL, 0, 0, NULL, - 0, msm8x16_wcd_codec_enable_interpolator, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("RX2 MIX2", - MSM8X16_WCD_A_CDC_CLK_RX_B1_CTL, 1, 0, NULL, - 0, msm8x16_wcd_codec_enable_interpolator, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("RX3 MIX1", - MSM8X16_WCD_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL, - 0, msm8x16_wcd_codec_enable_interpolator, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_SUPPLY("RX1 CLK", MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 0, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("RX2 CLK", MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 1, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("RX3 CLK", MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL, - 2, 0, msm8x16_wcd_codec_enable_dig_clk, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("RX1 CHAIN", MSM8X16_WCD_A_CDC_RX1_B6_CTL, 0, 0, - NULL, 0, - msm8x16_wcd_codec_enable_rx_chain, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("RX2 CHAIN", MSM8X16_WCD_A_CDC_RX2_B6_CTL, 0, 0, - NULL, 0, - msm8x16_wcd_codec_enable_rx_chain, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("RX3 CHAIN", MSM8X16_WCD_A_CDC_RX3_B6_CTL, 0, 0, - NULL, 0, - msm8x16_wcd_codec_enable_rx_chain, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0, - &rx_mix1_inp1_mux), - SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0, - &rx_mix1_inp2_mux), - SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0, - &rx_mix1_inp3_mux), - - SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0, - &rx2_mix1_inp1_mux), - SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0, - &rx2_mix1_inp2_mux), - SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0, - &rx2_mix1_inp3_mux), - - SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0, - &rx3_mix1_inp1_mux), - SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0, - &rx3_mix1_inp2_mux), - SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0, - &rx3_mix1_inp3_mux), - - SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0, - &rx1_mix2_inp1_mux), - SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0, - &rx2_mix2_inp1_mux), - - SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM, - ON_DEMAND_MICBIAS, 0, - msm8x16_wcd_codec_enable_on_demand_supply, - SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_SUPPLY("CP", MSM8X16_WCD_A_ANALOG_NCP_EN, 0, 0, - msm8x16_wcd_codec_enable_charge_pump, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_SUPPLY("EAR CP", MSM8X16_WCD_A_ANALOG_NCP_EN, 4, 0, - msm8x16_wcd_codec_enable_charge_pump, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, - 0, 0, msm8x16_wcd_codec_enable_rx_bias, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_SUPPLY("SPK_RX_BIAS", SND_SOC_NOPM, 0, 0, - msm8x16_wcd_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), - - /* TX */ - - SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, MSM8X16_WCD_A_CDC_CLK_OTHR_CTL, - 2, 0, NULL, 0), - - SND_SOC_DAPM_INPUT("AMIC1"), - SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", - MSM8X16_WCD_A_ANALOG_MICB_1_EN, 7, 0, - msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", - MSM8X16_WCD_A_ANALOG_MICB_2_EN, 7, 0, - msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("MIC BIAS Internal3", - MSM8X16_WCD_A_ANALOG_MICB_1_EN, 7, 0, - msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_ADC_E("ADC1", NULL, MSM8X16_WCD_A_ANALOG_TX_1_EN, 7, 0, - msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_ADC_E("ADC2_INP2", - NULL, MSM8X16_WCD_A_ANALOG_TX_2_EN, 7, 0, - msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_ADC_E("ADC2_INP3", - NULL, MSM8X16_WCD_A_ANALOG_TX_3_EN, 7, 0, - msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), - - SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, - &tx_adc2_mux), - - SND_SOC_DAPM_SUPPLY("MIC BIAS External", - MSM8X16_WCD_A_ANALOG_MICB_1_EN, 7, 0, - msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_SUPPLY("MIC BIAS External2", - MSM8X16_WCD_A_ANALOG_MICB_2_EN, 7, 0, - msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_POST_PMD), - - - SND_SOC_DAPM_INPUT("AMIC3"), - - SND_SOC_DAPM_MUX_E("DEC1 MUX", - MSM8X16_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0, - &dec1_mux, msm8x16_wcd_codec_enable_dec, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_MUX_E("DEC2 MUX", - MSM8X16_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL, 1, 0, - &dec2_mux, msm8x16_wcd_codec_enable_dec, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_MUX("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux), - - SND_SOC_DAPM_INPUT("AMIC2"), - - SND_SOC_DAPM_AIF_OUT("I2S TX1", "AIF1 Capture", 0, SND_SOC_NOPM, - 0, 0), - SND_SOC_DAPM_AIF_OUT("I2S TX2", "AIF1 Capture", 0, SND_SOC_NOPM, - 0, 0), - SND_SOC_DAPM_AIF_OUT("I2S TX3", "AIF1 Capture", 0, SND_SOC_NOPM, - 0, 0), - - - /* Digital Mic Inputs */ - SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, - msm8x16_wcd_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, - msm8x16_wcd_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), - - /* Sidetone */ - SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), - SND_SOC_DAPM_PGA("IIR1", - MSM8X16_WCD_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0), - - SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux), - SND_SOC_DAPM_PGA("IIR2", - MSM8X16_WCD_A_CDC_CLK_SD_CTL, 1, 0, NULL, 0), - - SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", - MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL, 4, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", - MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL, 4, 0, - NULL, 0), -}; - -static struct snd_soc_codec_driver msm8x16_wcd_codec = { - .probe = msm8x16_wcd_codec_probe, - .remove = msm8x16_wcd_codec_remove, - .read = msm8x16_wcd_read, - .write = msm8x16_wcd_write, - .reg_cache_size = MSM8X16_WCD_CACHE_SIZE, - .reg_cache_default = msm8x16_wcd_reset_reg_defaults, - .reg_word_size = 1, - .controls = msm8x16_wcd_snd_controls, - .num_controls = ARRAY_SIZE(msm8x16_wcd_snd_controls), - .dapm_widgets = msm8x16_wcd_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(msm8x16_wcd_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), -}; - -static int msm8x16_wcd_codec_parse_dt(struct platform_device *pdev, - struct wcd_chip *chip) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - int ret; - u32 res[2]; - - ret = of_property_read_u32_array(np, "reg", res, 2); - if (ret < 0) - return ret; - - chip->analog_base = res[0]; - - chip->digital_map = syscon_regmap_lookup_by_phandle(np, "digital"); - if (IS_ERR(chip->digital_map)) - return PTR_ERR(chip->digital_map); - - chip->vddio = devm_regulator_get(dev, "vddio"); - if (IS_ERR(chip->vddio)) { - dev_err(dev, "Failed to get vdd supply\n"); - return PTR_ERR(chip->vddio); - } - - chip->vdd_pa = devm_regulator_get(dev, "vdd-pa"); - if (IS_ERR(chip->vdd_pa)) { - dev_err(dev, "Failed to get vdd supply\n"); - return PTR_ERR(chip->vdd_pa); - } - - chip->vdd_mic_bias = devm_regulator_get(dev, "vdd-mic-bias"); - if (IS_ERR(chip->vdd_mic_bias)) { - dev_err(dev, "Failed to get vdd micbias supply\n"); - return PTR_ERR(chip->vdd_mic_bias); - } - - chip->mclk = devm_clk_get(dev, "mclk"); - - return 0; -} - -static int wcd_probe(struct platform_device *pdev) -{ - struct wcd_chip *chip; - struct device *dev = &pdev->dev; - int ret; - - chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->analog_map = dev_get_regmap(dev->parent, NULL); - if (!chip->analog_map) - return -ENXIO; - - ret = msm8x16_wcd_codec_parse_dt(pdev, chip); - if (IS_ERR_VALUE(ret)) - return ret; - - clk_set_rate(chip->mclk, 9600000); - clk_prepare_enable(chip->mclk); - - dev_set_drvdata(dev, chip); - - return snd_soc_register_codec(dev, &msm8x16_wcd_codec, - msm8x16_wcd_codec_dai, - ARRAY_SIZE(msm8x16_wcd_codec_dai)); -} - -static int wcd_remove(struct platform_device *pdev) -{ - snd_soc_unregister_codec(&pdev->dev); - - return 0; -} - -static const struct of_device_id wcd_match_table[] = { - { .compatible = "qcom,apq8016-wcd-codec" }, - { .compatible = "qcom,msm8x16-wcd-codec" }, - { } -}; -MODULE_DEVICE_TABLE(of, wcd_match_table); - -static struct platform_driver wcd_driver = { - .driver = { - .name = "msm8x16-wcd-codec", - .of_match_table = wcd_match_table, - }, - .probe = wcd_probe, - .remove = wcd_remove, -}; -module_platform_driver(wcd_driver); - -MODULE_ALIAS("platform:spmi-wcd-codec"); -MODULE_DESCRIPTION("SPMI PMIC WCD codec driver"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm8x16-wcd.h b/sound/soc/codecs/msm8x16-wcd.h deleted file mode 100644 index ad4c9d0c5ae6..000000000000 --- a/sound/soc/codecs/msm8x16-wcd.h +++ /dev/null @@ -1,234 +0,0 @@ -/* Copyright (c) 2014, The Linux Foundation. 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 and - * only 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. - */ -#ifndef MSM8X16_WCD_H -#define MSM8X16_WCD_H - -#include - -#define MSM8X16_WCD_NUM_REGISTERS 0x6FF -#define MSM8X16_WCD_MAX_REGISTER (MSM8X16_WCD_NUM_REGISTERS-1) -#define MSM8X16_WCD_CACHE_SIZE MSM8X16_WCD_NUM_REGISTERS -#define MSM8X16_WCD_NUM_IRQ_REGS 2 -#define MAX_REGULATOR 7 -#define MSM8X16_WCD_REG_VAL(reg, val) {reg, 0, val} -#define MSM8X16_TOMBAK_LPASS_AUDIO_CORE_DIG_CODEC_CLK_SEL 0xFE03B004 -#define MSM8X16_TOMBAK_LPASS_DIGCODEC_CMD_RCGR 0x0181C09C -#define MSM8X16_TOMBAK_LPASS_DIGCODEC_CFG_RCGR 0x0181C0A0 -#define MSM8X16_TOMBAK_LPASS_DIGCODEC_M 0x0181C0A4 -#define MSM8X16_TOMBAK_LPASS_DIGCODEC_N 0x0181C0A8 -#define MSM8X16_TOMBAK_LPASS_DIGCODEC_D 0x0181C0AC -#define MSM8X16_TOMBAK_LPASS_DIGCODEC_CBCR 0x0181C0B0 -#define MSM8X16_TOMBAK_LPASS_DIGCODEC_AHB_CBCR 0x0181C0B4 - -#define MSM8X16_CODEC_NAME "msm8x16_wcd_codec" - -#define MSM8X16_WCD_IS_DIGITAL_REG(reg) \ - (((reg >= 0x200) && (reg <= 0x4FF)) ? 1 : 0) -#define MSM8X16_WCD_IS_TOMBAK_REG(reg) \ - (((reg >= 0x000) && (reg <= 0x1FF)) ? 1 : 0) -/* - * MCLK activity indicators during suspend and resume call - */ -#define MCLK_SUS_DIS 1 -#define MCLK_SUS_RSC 2 -#define MCLK_SUS_NO_ACT 3 - -#define NUM_DECIMATORS 2 - -extern const u8 msm8x16_wcd_reg_readable[MSM8X16_WCD_CACHE_SIZE]; -extern const u8 msm8x16_wcd_reg_readonly[MSM8X16_WCD_CACHE_SIZE]; -extern u8 msm8x16_wcd_reset_reg_defaults[MSM8X16_WCD_CACHE_SIZE]; - -enum msm8x16_wcd_pid_current { - MSM8X16_WCD_PID_MIC_2P5_UA, - MSM8X16_WCD_PID_MIC_5_UA, - MSM8X16_WCD_PID_MIC_10_UA, - MSM8X16_WCD_PID_MIC_20_UA, -}; - -struct msm8x16_wcd_reg_mask_val { - u16 reg; - u8 mask; - u8 val; -}; - -enum msm8x16_wcd_mbhc_analog_pwr_cfg { - MSM8X16_WCD_ANALOG_PWR_COLLAPSED = 0, - MSM8X16_WCD_ANALOG_PWR_ON, - MSM8X16_WCD_NUM_ANALOG_PWR_CONFIGS, -}; - -/* Number of input and output I2S port */ -enum { - MSM8X16_WCD_RX1 = 0, - MSM8X16_WCD_RX2, - MSM8X16_WCD_RX3, - MSM8X16_WCD_RX_MAX, -}; - -enum { - MSM8X16_WCD_TX1 = 0, - MSM8X16_WCD_TX2, - MSM8X16_WCD_TX3, - MSM8X16_WCD_TX4, - MSM8X16_WCD_TX_MAX, -}; - -enum { - /* INTR_REG 0 - Digital Periph */ - MSM8X16_WCD_IRQ_SPKR_CNP = 0, - MSM8X16_WCD_IRQ_SPKR_CLIP, - MSM8X16_WCD_IRQ_SPKR_OCP, - MSM8X16_WCD_IRQ_MBHC_INSREM_DET1, - MSM8X16_WCD_IRQ_MBHC_RELEASE, - MSM8X16_WCD_IRQ_MBHC_PRESS, - MSM8X16_WCD_IRQ_MBHC_INSREM_DET, - MSM8X16_WCD_IRQ_MBHC_HS_DET, - /* INTR_REG 1 - Analog Periph */ - MSM8X16_WCD_IRQ_EAR_OCP, - MSM8X16_WCD_IRQ_HPHR_OCP, - MSM8X16_WCD_IRQ_HPHL_OCP, - MSM8X16_WCD_IRQ_EAR_CNP, - MSM8X16_WCD_IRQ_HPHR_CNP, - MSM8X16_WCD_IRQ_HPHL_CNP, - MSM8X16_WCD_NUM_IRQS, -}; - -enum wcd_notify_event { - WCD_EVENT_INVALID, - /* events for micbias ON and OFF */ - WCD_EVENT_PRE_MICBIAS_2_OFF, - WCD_EVENT_POST_MICBIAS_2_OFF, - WCD_EVENT_PRE_MICBIAS_2_ON, - WCD_EVENT_POST_MICBIAS_2_ON, - /* events for PA ON and OFF */ - WCD_EVENT_PRE_HPHL_PA_ON, - WCD_EVENT_POST_HPHL_PA_OFF, - WCD_EVENT_PRE_HPHR_PA_ON, - WCD_EVENT_POST_HPHR_PA_OFF, - WCD_EVENT_LAST, -}; - -enum { - ON_DEMAND_MICBIAS = 0, - ON_DEMAND_SUPPLIES_MAX, -}; - -/* - * The delay list is per codec HW specification. - * Please add delay in the list in the future instead - * of magic number - */ -enum { - CODEC_DELAY_1_MS = 1000, - CODEC_DELAY_1_1_MS = 1100, -}; -#if 0 -struct msm8x16_wcd_regulator { - const char *name; - int min_uv; - int max_uv; - int optimum_ua; - bool ondemand; - struct regulator *regulator; -}; - -struct msm8916_asoc_mach_data { - int codec_type; - int ext_pa; - int us_euro_gpio; - int mclk_freq; - int lb_mode; - atomic_t mclk_rsc_ref; - atomic_t mclk_enabled; - struct mutex cdc_mclk_mutex; - struct delayed_work disable_mclk_work; - struct afe_digital_clk_cfg digital_cdc_clk; -}; - -struct msm8x16_wcd_pdata { - int irq; - int irq_base; - int num_irqs; - int reset_gpio; - void *msm8x16_wcd_ahb_base_vaddr; - struct wcd9xxx_micbias_setting micbias; - struct msm8x16_wcd_regulator regulator[MAX_REGULATOR]; - u32 mclk_rate; -}; - -enum msm8x16_wcd_micbias_num { - MSM8X16_WCD_MICBIAS1 = 0, -}; - -struct msm8x16_wcd { - struct device *dev; - struct mutex io_lock; - u8 version; - - int reset_gpio; - int (*read_dev)(struct snd_soc_codec *codec, - unsigned short reg); - int (*write_dev)(struct snd_soc_codec *codec, - unsigned short reg, u8 val); - - u32 num_of_supplies; - struct regulator_bulk_data *supplies; - - u8 idbyte[4]; - - int num_irqs; - u32 mclk_rate; - char __iomem *dig_base; -}; - -struct on_demand_supply { - struct regulator *supply; - atomic_t ref; -}; - -struct msm8x16_wcd_priv { - struct snd_soc_codec *codec; - u16 pmic_rev; - u32 adc_count; - u32 rx_bias_count; - s32 dmic_1_2_clk_cnt; - u32 mute_mask; - bool mclk_enabled; - bool clock_active; - bool config_mode_active; - bool spk_boost_set; - bool ear_pa_boost_set; - bool dec_active[NUM_DECIMATORS]; - struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX]; - /* mbhc module */ - struct wcd_mbhc mbhc; - struct blocking_notifier_head notifier; - -}; - -extern int msm8x16_wcd_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, - bool dapm); - -extern int msm8x16_wcd_hs_detect(struct snd_soc_codec *codec, - struct wcd_mbhc_config *mbhc_cfg); - -extern void msm8x16_wcd_hs_detect_exit(struct snd_soc_codec *codec); - -extern int msm8x16_register_notifier(struct snd_soc_codec *codec, - struct notifier_block *nblock); - -extern int msm8x16_unregister_notifier(struct snd_soc_codec *codec, - struct notifier_block *nblock); -#endif -#endif - diff --git a/sound/soc/codecs/msm8x16_wcd_registers.h b/sound/soc/codecs/msm8x16_wcd_registers.h deleted file mode 100644 index 03d92c844e49..000000000000 --- a/sound/soc/codecs/msm8x16_wcd_registers.h +++ /dev/null @@ -1,518 +0,0 @@ - /* Copyright (c) 2014, The Linux Foundation. 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 and - * only 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. - */ -#ifndef MSM8X16_WCD_REGISTERS_H -#define MSM8X16_WCD_REGISTERS_H - -#define MSM8X16_WCD_A_DIGITAL_REVISION1 (0x000) -#define MSM8X16_WCD_A_DIGITAL_REVISION1__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_REVISION2 (0x001) -#define MSM8X16_WCD_A_DIGITAL_REVISION2__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_PERPH_TYPE (0x004) -#define MSM8X16_WCD_A_DIGITAL_PERPH_TYPE__POR (0x23) -#define MSM8X16_WCD_A_DIGITAL_PERPH_SUBTYPE (0x005) -#define MSM8X16_WCD_A_DIGITAL_PERPH_SUBTYPE__POR (0x01) -#define MSM8X16_WCD_A_DIGITAL_INT_RT_STS (0x010) -#define MSM8X16_WCD_A_DIGITAL_INT_RT_STS__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_INT_SET_TYPE (0x011) -#define MSM8X16_WCD_A_DIGITAL_INT_SET_TYPE__POR (0xFF) -#define MSM8X16_WCD_A_DIGITAL_INT_POLARITY_HIGH (0x012) -#define MSM8X16_WCD_A_DIGITAL_INT_POLARITY_HIGH__POR (0xFF) -#define MSM8X16_WCD_A_DIGITAL_INT_POLARITY_LOW (0x013) -#define MSM8X16_WCD_A_DIGITAL_INT_POLARITY_LOW__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_INT_LATCHED_CLR (0x014) -#define MSM8X16_WCD_A_DIGITAL_INT_LATCHED_CLR__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_INT_EN_SET (0x015) -#define MSM8X16_WCD_A_DIGITAL_INT_EN_SET__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_INT_EN_CLR (0x016) -#define MSM8X16_WCD_A_DIGITAL_INT_EN_CLR__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_INT_LATCHED_STS (0x018) -#define MSM8X16_WCD_A_DIGITAL_INT_LATCHED_STS__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_INT_PENDING_STS (0x019) -#define MSM8X16_WCD_A_DIGITAL_INT_PENDING_STS__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_INT_MID_SEL (0x01A) -#define MSM8X16_WCD_A_DIGITAL_INT_MID_SEL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_INT_PRIORITY (0x01B) -#define MSM8X16_WCD_A_DIGITAL_INT_PRIORITY__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_GPIO_MODE (0x040) -#define MSM8X16_WCD_A_DIGITAL_GPIO_MODE__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_PIN_CTL_OE (0x041) -#define MSM8X16_WCD_A_DIGITAL_PIN_CTL_OE__POR (0x01) -#define MSM8X16_WCD_A_DIGITAL_PIN_CTL_DATA (0x042) -#define MSM8X16_WCD_A_DIGITAL_PIN_CTL_DATA__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_PIN_STATUS (0x043) -#define MSM8X16_WCD_A_DIGITAL_PIN_STATUS__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_HDRIVE_CTL (0x044) -#define MSM8X16_WCD_A_DIGITAL_HDRIVE_CTL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_CDC_RST_CTL (0x046) -#define MSM8X16_WCD_A_DIGITAL_CDC_RST_CTL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_CDC_TOP_CLK_CTL (0x048) -#define MSM8X16_WCD_A_DIGITAL_CDC_TOP_CLK_CTL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL (0x049) -#define MSM8X16_WCD_A_DIGITAL_CDC_ANA_CLK_CTL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL (0x04A) -#define MSM8X16_WCD_A_DIGITAL_CDC_DIG_CLK_CTL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX1_CTL (0x050) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX1_CTL__POR (0x02) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX2_CTL (0x051) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_TX2_CTL__POR (0x02) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_HPHR_DAC_CTL (0x052) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_HPHR_DAC_CTL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX1_CTL (0x053) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX1_CTL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX2_CTL (0x054) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX2_CTL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX3_CTL (0x055) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX3_CTL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX_LB_CTL (0x056) -#define MSM8X16_WCD_A_DIGITAL_CDC_CONN_RX_LB_CTL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL1 (0x058) -#define MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL1__POR (0x7C) -#define MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL2 (0x059) -#define MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL2__POR (0x7C) -#define MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL3 (0x05A) -#define MSM8X16_WCD_A_DIGITAL_CDC_RX_CTL3__POR (0x7C) -#define MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA0 (0x05B) -#define MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA0__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA1 (0x05C) -#define MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA1__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA2 (0x05D) -#define MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA2__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA3 (0x05E) -#define MSM8X16_WCD_A_DIGITAL_DEM_BYPASS_DATA3__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_DIG_DEBUG_CTL (0x068) -#define MSM8X16_WCD_A_DIGITAL_DIG_DEBUG_CTL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_DIG_DEBUG_EN (0x069) -#define MSM8X16_WCD_A_DIGITAL_DIG_DEBUG_EN__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_SPARE_0 (0x070) -#define MSM8X16_WCD_A_DIGITAL_SPARE_0__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_SPARE_1 (0x071) -#define MSM8X16_WCD_A_DIGITAL_SPARE_1__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_SPARE_2 (0x072) -#define MSM8X16_WCD_A_DIGITAL_SPARE_2__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_SEC_ACCESS (0x0D0) -#define MSM8X16_WCD_A_DIGITAL_SEC_ACCESS__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_PERPH_RESET_CTL1 (0x0D8) -#define MSM8X16_WCD_A_DIGITAL_PERPH_RESET_CTL1__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_PERPH_RESET_CTL2 (0x0D9) -#define MSM8X16_WCD_A_DIGITAL_PERPH_RESET_CTL2__POR (0x01) -#define MSM8X16_WCD_A_DIGITAL_PERPH_RESET_CTL3 (0x0DA) -#define MSM8X16_WCD_A_DIGITAL_PERPH_RESET_CTL3__POR (0x05) -#define MSM8X16_WCD_A_DIGITAL_PERPH_RESET_CTL4 (0x0DB) -#define MSM8X16_WCD_A_DIGITAL_PERPH_RESET_CTL4__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_INT_TEST1 (0x0E0) -#define MSM8X16_WCD_A_DIGITAL_INT_TEST1__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_INT_TEST_VAL (0x0E1) -#define MSM8X16_WCD_A_DIGITAL_INT_TEST_VAL__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_TRIM_NUM (0x0F0) -#define MSM8X16_WCD_A_DIGITAL_TRIM_NUM__POR (0x00) -#define MSM8X16_WCD_A_DIGITAL_TRIM_CTRL (0x0F1) -#define MSM8X16_WCD_A_DIGITAL_TRIM_CTRL__POR (0x00) - -#define MSM8X16_WCD_A_ANALOG_REVISION1 (0x100) -#define MSM8X16_WCD_A_ANALOG_REVISION1__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_REVISION2 (0x101) -#define MSM8X16_WCD_A_ANALOG_REVISION2__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_REVISION3 (0x102) -#define MSM8X16_WCD_A_ANALOG_REVISION3__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_REVISION4 (0x103) -#define MSM8X16_WCD_A_ANALOG_REVISION4__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_PERPH_TYPE (0x104) -#define MSM8X16_WCD_A_ANALOG_PERPH_TYPE__POR (0x23) -#define MSM8X16_WCD_A_ANALOG_PERPH_SUBTYPE (0x105) -#define MSM8X16_WCD_A_ANALOG_PERPH_SUBTYPE__POR (0x09) -#define MSM8X16_WCD_A_ANALOG_INT_RT_STS (0x110) -#define MSM8X16_WCD_A_ANALOG_INT_RT_STS__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_INT_SET_TYPE (0x111) -#define MSM8X16_WCD_A_ANALOG_INT_SET_TYPE__POR (0x3F) -#define MSM8X16_WCD_A_ANALOG_INT_POLARITY_HIGH (0x112) -#define MSM8X16_WCD_A_ANALOG_INT_POLARITY_HIGH__POR (0x3F) -#define MSM8X16_WCD_A_ANALOG_INT_POLARITY_LOW (0x113) -#define MSM8X16_WCD_A_ANALOG_INT_POLARITY_LOW__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_INT_LATCHED_CLR (0x114) -#define MSM8X16_WCD_A_ANALOG_INT_LATCHED_CLR__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_INT_EN_SET (0x115) -#define MSM8X16_WCD_A_ANALOG_INT_EN_SET__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_INT_EN_CLR (0x116) -#define MSM8X16_WCD_A_ANALOG_INT_EN_CLR__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_INT_LATCHED_STS (0x118) -#define MSM8X16_WCD_A_ANALOG_INT_LATCHED_STS__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_INT_PENDING_STS (0x119) -#define MSM8X16_WCD_A_ANALOG_INT_PENDING_STS__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_INT_MID_SEL (0x11A) -#define MSM8X16_WCD_A_ANALOG_INT_MID_SEL__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_INT_PRIORITY (0x11B) -#define MSM8X16_WCD_A_ANALOG_INT_PRIORITY__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_MICB_1_EN (0x140) -#define MSM8X16_WCD_A_ANALOG_MICB_1_EN__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_MICB_1_VAL (0x141) -#define MSM8X16_WCD_A_ANALOG_MICB_1_VAL__POR (0x20) -#define MSM8X16_WCD_A_ANALOG_MICB_1_CTL (0x142) -#define MSM8X16_WCD_A_ANALOG_MICB_1_CTL__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS (0x143) -#define MSM8X16_WCD_A_ANALOG_MICB_1_INT_RBIAS__POR (0x49) -#define MSM8X16_WCD_A_ANALOG_MICB_2_EN (0x144) -#define MSM8X16_WCD_A_ANALOG_MICB_2_EN__POR (0x20) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_ATEST_CTL_2 (0x145) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_ATEST_CTL_2__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_MASTER_BIAS_CTL (0x146) -#define MSM8X16_WCD_A_ANALOG_MASTER_BIAS_CTL__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1 (0x147) -#define MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1__POR (0x35) -#define MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2 (0x150) -#define MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2__POR (0x08) -#define MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL (0x151) -#define MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_MBHC_DBNC_TIMER (0x152) -#define MSM8X16_WCD_A_ANALOG_MBHC_DBNC_TIMER__POR (0x98) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN0_ZDETL_CTL (0x153) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN0_ZDETL_CTL__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN1_ZDETM_CTL (0x154) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN1_ZDETM_CTL__POR (0x20) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN2_ZDETH_CTL (0x155) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN2_ZDETH_CTL__POR (0x40) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN3_CTL (0x156) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN3_CTL__POR (0x61) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN4_CTL (0x157) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN4_CTL__POR (0x80) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT (0x158) -#define MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT (0x159) -#define MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_TX_1_EN (0x160) -#define MSM8X16_WCD_A_ANALOG_TX_1_EN__POR (0x03) -#define MSM8X16_WCD_A_ANALOG_TX_2_EN (0x161) -#define MSM8X16_WCD_A_ANALOG_TX_2_EN__POR (0x03) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_1 (0x162) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_1__POR (0xBF) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_2 (0x163) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_TEST_CTL_2__POR (0x8C) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_ATEST_CTL (0x164) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_ATEST_CTL__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_OPAMP_BIAS (0x165) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_OPAMP_BIAS__POR (0x6B) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_TXFE_CLKDIV (0x166) -#define MSM8X16_WCD_A_ANALOG_TX_1_2_TXFE_CLKDIV__POR (0x51) -#define MSM8X16_WCD_A_ANALOG_TX_3_EN (0x167) -#define MSM8X16_WCD_A_ANALOG_TX_3_EN__POR (0x02) -#define MSM8X16_WCD_A_ANALOG_NCP_EN (0x180) -#define MSM8X16_WCD_A_ANALOG_NCP_EN__POR (0x26) -#define MSM8X16_WCD_A_ANALOG_NCP_CLK (0x181) -#define MSM8X16_WCD_A_ANALOG_NCP_CLK__POR (0x23) -#define MSM8X16_WCD_A_ANALOG_NCP_DEGLITCH (0x182) -#define MSM8X16_WCD_A_ANALOG_NCP_DEGLITCH__POR (0x5B) -#define MSM8X16_WCD_A_ANALOG_NCP_FBCTRL (0x183) -#define MSM8X16_WCD_A_ANALOG_NCP_FBCTRL__POR (0x08) -#define MSM8X16_WCD_A_ANALOG_NCP_BIAS (0x184) -#define MSM8X16_WCD_A_ANALOG_NCP_BIAS__POR (0x29) -#define MSM8X16_WCD_A_ANALOG_NCP_VCTRL (0x185) -#define MSM8X16_WCD_A_ANALOG_NCP_VCTRL__POR (0x24) -#define MSM8X16_WCD_A_ANALOG_NCP_TEST (0x186) -#define MSM8X16_WCD_A_ANALOG_NCP_TEST__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_NCP_CLIM_ADDR (0x187) -#define MSM8X16_WCD_A_ANALOG_NCP_CLIM_ADDR__POR (0xD5) -#define MSM8X16_WCD_A_ANALOG_RX_CLOCK_DIVIDER (0x190) -#define MSM8X16_WCD_A_ANALOG_RX_CLOCK_DIVIDER__POR (0xE8) -#define MSM8X16_WCD_A_ANALOG_RX_COM_OCP_CTL (0x191) -#define MSM8X16_WCD_A_ANALOG_RX_COM_OCP_CTL__POR (0xCF) -#define MSM8X16_WCD_A_ANALOG_RX_COM_OCP_COUNT (0x192) -#define MSM8X16_WCD_A_ANALOG_RX_COM_OCP_COUNT__POR (0x6E) -#define MSM8X16_WCD_A_ANALOG_RX_COM_BIAS_DAC (0x193) -#define MSM8X16_WCD_A_ANALOG_RX_COM_BIAS_DAC__POR (0x10) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_PA (0x194) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_PA__POR (0x5A) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_LDO_OCP (0x195) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_LDO_OCP__POR (0x69) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_CNP (0x196) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_BIAS_CNP__POR (0x29) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_EN (0x197) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_EN__POR (0x80) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_WG_CTL (0x198) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_WG_CTL__POR (0xDA) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_WG_TIME (0x199) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_WG_TIME__POR (0x16) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_L_TEST (0x19A) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_L_TEST__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_L_PA_DAC_CTL (0x19B) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_L_PA_DAC_CTL__POR (0x20) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_R_TEST (0x19C) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_R_TEST__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_R_PA_DAC_CTL (0x19D) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_R_PA_DAC_CTL__POR (0x20) -#define MSM8X16_WCD_A_ANALOG_RX_EAR_CTL (0x19E) -#define MSM8X16_WCD_A_ANALOG_RX_EAR_CTL___POR (0x12) -#define MSM8X16_WCD_A_ANALOG_RX_ATEST (0x19F) -#define MSM8X16_WCD_A_ANALOG_RX_ATEST__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_STATUS (0x1A0) -#define MSM8X16_WCD_A_ANALOG_RX_HPH_STATUS__POR (0x0C) -#define MSM8X16_WCD_A_ANALOG_RX_EAR_STATUS (0x1A1) -#define MSM8X16_WCD_A_ANALOG_RX_EAR_STATUS__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL (0x1B0) -#define MSM8X16_WCD_A_ANALOG_SPKR_DAC_CTL__POR (0x83) -#define MSM8X16_WCD_A_ANALOG_SPKR_DRV_CLIP_DET (0x1B1) -#define MSM8X16_WCD_A_ANALOG_SPKR_DRV_CLIP_DET__POR (0x91) -#define MSM8X16_WCD_A_ANALOG_SPKR_DRV_CTL (0x1B2) -#define MSM8X16_WCD_A_ANALOG_SPKR_DRV_CTL__POR (0x29) -#define MSM8X16_WCD_A_ANALOG_SPKR_ANA_BIAS_SET (0x1B3) -#define MSM8X16_WCD_A_ANALOG_SPKR_ANA_BIAS_SET__POR (0x4D) -#define MSM8X16_WCD_A_ANALOG_SPKR_OCP_CTL (0x1B4) -#define MSM8X16_WCD_A_ANALOG_SPKR_OCP_CTL__POR (0xE1) -#define MSM8X16_WCD_A_ANALOG_SPKR_PWRSTG_CTL (0x1B5) -#define MSM8X16_WCD_A_ANALOG_SPKR_PWRSTG_CTL__POR (0x1E) -#define MSM8X16_WCD_A_ANALOG_SPKR_DRV_MISC (0x1B6) -#define MSM8X16_WCD_A_ANALOG_SPKR_DRV_MISC__POR (0xCB) -#define MSM8X16_WCD_A_ANALOG_SPKR_DRV_DBG (0x1B7) -#define MSM8X16_WCD_A_ANALOG_SPKR_DRV_DBG__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_CURRENT_LIMIT (0x1C0) -#define MSM8X16_WCD_A_ANALOG_CURRENT_LIMIT__POR (0x02) -#define MSM8X16_WCD_A_ANALOG_OUTPUT_VOLTAGE (0x1C1) -#define MSM8X16_WCD_A_ANALOG_OUTPUT_VOLTAGE__POR (0x14) -#define MSM8X16_WCD_A_ANALOG_BYPASS_MODE (0x1C2) -#define MSM8X16_WCD_A_ANALOG_BYPASS_MODE__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_BOOST_EN_CTL (0x1C3) -#define MSM8X16_WCD_A_ANALOG_BOOST_EN_CTL__POR (0x1F) -#define MSM8X16_WCD_A_ANALOG_SLOPE_COMP_IP_ZERO (0x1C4) -#define MSM8X16_WCD_A_ANALOG_SLOPE_COMP_IP_ZERO__POR (0x8C) -#define MSM8X16_WCD_A_ANALOG_RDSON_MAX_DUTY_CYCLE (0x1C5) -#define MSM8X16_WCD_A_ANALOG_RDSON_MAX_DUTY_CYCLE__POR (0xC0) -#define MSM8X16_WCD_A_ANALOG_BOOST_TEST1_1 (0x1C6) -#define MSM8X16_WCD_A_ANALOG_BOOST_TEST1_1__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_BOOST_TEST_2 (0x1C7) -#define MSM8X16_WCD_A_ANALOG_BOOST_TEST_2__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_SPKR_SAR_STATUS (0x1C8) -#define MSM8X16_WCD_A_ANALOG_SPKR_SAR_STATUS__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_SPKR_DRV_STATUS (0x1C9) -#define MSM8X16_WCD_A_ANALOG_SPKR_DRV_STATUS__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_PBUS_ADD_CSR (0x1CE) -#define MSM8X16_WCD_A_ANALOG_PBUS_ADD_CSR__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_PBUS_ADD_SEL (0x1CF) -#define MSM8X16_WCD_A_ANALOG_PBUS_ADD_SEL__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_SEC_ACCESS (0x1D0) -#define MSM8X16_WCD_A_ANALOG_SEC_ACCESS__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL1 (0x1D8) -#define MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL1__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL2 (0x1D9) -#define MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL2__POR (0x01) -#define MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL3 (0x1DA) -#define MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL3__POR (0x05) -#define MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL4 (0x1DB) -#define MSM8X16_WCD_A_ANALOG_PERPH_RESET_CTL4__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_INT_TEST1 (0x1E0) -#define MSM8X16_WCD_A_ANALOG_INT_TEST1__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_INT_TEST_VAL (0x1E1) -#define MSM8X16_WCD_A_ANALOG_INT_TEST_VAL__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_TRIM_NUM (0x1F0) -#define MSM8X16_WCD_A_ANALOG_TRIM_NUM__POR (0x04) -#define MSM8X16_WCD_A_ANALOG_TRIM_CTRL1 (0x1F1) -#define MSM8X16_WCD_A_ANALOG_TRIM_CTRL1__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_TRIM_CTRL2 (0x1F2) -#define MSM8X16_WCD_A_ANALOG_TRIM_CTRL2__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_TRIM_CTRL3 (0x1F3) -#define MSM8X16_WCD_A_ANALOG_TRIM_CTRL3__POR (0x00) -#define MSM8X16_WCD_A_ANALOG_TRIM_CTRL4 (0x1F4) -#define MSM8X16_WCD_A_ANALOG_TRIM_CTRL4__POR (0x00) - -/* Digital part */ -#define MSM8X16_WCD_A_CDC_CLK_RX_RESET_CTL (0x200) -#define MSM8X16_WCD_A_CDC_CLK_RX_RESET_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CLK_TX_RESET_B1_CTL (0x204) -#define MSM8X16_WCD_A_CDC_CLK_TX_RESET_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CLK_DMIC_B1_CTL (0x208) -#define MSM8X16_WCD_A_CDC_CLK_DMIC_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL (0x20C) -#define MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL__POR (0x13) -#define MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL (0x210) -#define MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL__POR (0x13) -#define MSM8X16_WCD_A_CDC_CLK_OTHR_RESET_B1_CTL (0x214) -#define MSM8X16_WCD_A_CDC_CLK_OTHR_RESET_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL (0x218) -#define MSM8X16_WCD_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CLK_OTHR_CTL (0x21C) -#define MSM8X16_WCD_A_CDC_CLK_OTHR_CTL__POR (0x04) -#define MSM8X16_WCD_A_CDC_CLK_RX_B1_CTL (0x220) -#define MSM8X16_WCD_A_CDC_CLK_RX_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CLK_MCLK_CTL (0x224) -#define MSM8X16_WCD_A_CDC_CLK_MCLK_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CLK_PDM_CTL (0x228) -#define MSM8X16_WCD_A_CDC_CLK_PDM_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CLK_SD_CTL (0x22C) -#define MSM8X16_WCD_A_CDC_CLK_SD_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX1_B1_CTL (0x240) -#define MSM8X16_WCD_A_CDC_RX1_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX2_B1_CTL (0x260) -#define MSM8X16_WCD_A_CDC_RX2_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX3_B1_CTL (0x280) -#define MSM8X16_WCD_A_CDC_RX3_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX1_B2_CTL (0x244) -#define MSM8X16_WCD_A_CDC_RX1_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX2_B2_CTL (0x264) -#define MSM8X16_WCD_A_CDC_RX2_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX3_B2_CTL (0x284) -#define MSM8X16_WCD_A_CDC_RX3_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX1_B3_CTL (0x248) -#define MSM8X16_WCD_A_CDC_RX1_B3_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX2_B3_CTL (0x268) -#define MSM8X16_WCD_A_CDC_RX2_B3_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX3_B3_CTL (0x288) -#define MSM8X16_WCD_A_CDC_RX3_B3_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX1_B4_CTL (0x24C) -#define MSM8X16_WCD_A_CDC_RX1_B4_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX2_B4_CTL (0x26C) -#define MSM8X16_WCD_A_CDC_RX2_B4_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX3_B4_CTL (0x28C) -#define MSM8X16_WCD_A_CDC_RX3_B4_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX1_B5_CTL (0x250) -#define MSM8X16_WCD_A_CDC_RX1_B5_CTL__POR (0x68) -#define MSM8X16_WCD_A_CDC_RX2_B5_CTL (0x270) -#define MSM8X16_WCD_A_CDC_RX2_B5_CTL__POR (0x68) -#define MSM8X16_WCD_A_CDC_RX3_B5_CTL (0x290) -#define MSM8X16_WCD_A_CDC_RX3_B5_CTL__POR (0x68) -#define MSM8X16_WCD_A_CDC_RX1_B6_CTL (0x254) -#define MSM8X16_WCD_A_CDC_RX1_B6_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX2_B6_CTL (0x274) -#define MSM8X16_WCD_A_CDC_RX2_B6_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX3_B6_CTL (0x294) -#define MSM8X16_WCD_A_CDC_RX3_B6_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B1_CTL (0x258) -#define MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B1_CTL (0x278) -#define MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B1_CTL (0x298) -#define MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B2_CTL (0x25C) -#define MSM8X16_WCD_A_CDC_RX1_VOL_CTL_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B2_CTL (0x27C) -#define MSM8X16_WCD_A_CDC_RX2_VOL_CTL_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B2_CTL (0x29C) -#define MSM8X16_WCD_A_CDC_RX3_VOL_CTL_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_TOP_GAIN_UPDATE (0x2A0) -#define MSM8X16_WCD_A_CDC_TOP_GAIN_UPDATE__POR (0x00) -#define MSM8X16_WCD_A_CDC_TOP_CTL (0x2A4) -#define MSM8X16_WCD_A_CDC_TOP_CTL__POR (0x01) -#define MSM8X16_WCD_A_CDC_DEBUG_DESER1_CTL (0x2E0) -#define MSM8X16_WCD_A_CDC_DEBUG_DESER1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_DEBUG_DESER2_CTL (0x2E4) -#define MSM8X16_WCD_A_CDC_DEBUG_DESER2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_DEBUG_B1_CTL_CFG (0x2E8) -#define MSM8X16_WCD_A_CDC_DEBUG_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_DEBUG_B2_CTL_CFG (0x2EC) -#define MSM8X16_WCD_A_CDC_DEBUG_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_DEBUG_B3_CTL_CFG (0x2F0) -#define MSM8X16_WCD_A_CDC_DEBUG_B3_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B1_CTL (0x300) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B1_CTL (0x340) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B2_CTL (0x304) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B2_CTL (0x344) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B3_CTL (0x308) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B3_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B3_CTL (0x348) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B3_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B4_CTL (0x30C) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B4_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B4_CTL (0x34C) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B4_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B5_CTL (0x310) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B5_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B5_CTL (0x350) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B5_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B6_CTL (0x314) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B6_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B6_CTL (0x354) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B6_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B7_CTL (0x318) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B7_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B7_CTL (0x358) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B7_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B8_CTL (0x31C) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_B8_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B8_CTL (0x35C) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_B8_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR1_CTL (0x320) -#define MSM8X16_WCD_A_CDC_IIR1_CTL__POR (0x40) -#define MSM8X16_WCD_A_CDC_IIR2_CTL (0x360) -#define MSM8X16_WCD_A_CDC_IIR2_CTL__POR (0x40) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_TIMER_CTL (0x324) -#define MSM8X16_WCD_A_CDC_IIR1_GAIN_TIMER_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_TIMER_CTL (0x364) -#define MSM8X16_WCD_A_CDC_IIR2_GAIN_TIMER_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR1_COEF_B1_CTL (0x328) -#define MSM8X16_WCD_A_CDC_IIR1_COEF_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR2_COEF_B1_CTL (0x368) -#define MSM8X16_WCD_A_CDC_IIR2_COEF_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR1_COEF_B2_CTL (0x32C) -#define MSM8X16_WCD_A_CDC_IIR1_COEF_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_IIR2_COEF_B2_CTL (0x36C) -#define MSM8X16_WCD_A_CDC_IIR2_COEF_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_RX1_B1_CTL (0x380) -#define MSM8X16_WCD_A_CDC_CONN_RX1_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_RX1_B2_CTL (0x384) -#define MSM8X16_WCD_A_CDC_CONN_RX1_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_RX1_B3_CTL (0x388) -#define MSM8X16_WCD_A_CDC_CONN_RX1_B3_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_RX2_B1_CTL (0x38C) -#define MSM8X16_WCD_A_CDC_CONN_RX2_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_RX2_B2_CTL (0x390) -#define MSM8X16_WCD_A_CDC_CONN_RX2_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_RX2_B3_CTL (0x394) -#define MSM8X16_WCD_A_CDC_CONN_RX2_B3_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_RX3_B1_CTL (0x398) -#define MSM8X16_WCD_A_CDC_CONN_RX3_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_RX3_B2_CTL (0x39C) -#define MSM8X16_WCD_A_CDC_CONN_RX3_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_TX_B1_CTL (0x3A0) -#define MSM8X16_WCD_A_CDC_CONN_TX_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_EQ1_B1_CTL (0x3A8) -#define MSM8X16_WCD_A_CDC_CONN_EQ1_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_EQ1_B2_CTL (0x3AC) -#define MSM8X16_WCD_A_CDC_CONN_EQ1_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_EQ1_B3_CTL (0x3B0) -#define MSM8X16_WCD_A_CDC_CONN_EQ1_B3_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_EQ1_B4_CTL (0x3B4) -#define MSM8X16_WCD_A_CDC_CONN_EQ1_B4_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_EQ2_B1_CTL (0x3B8) -#define MSM8X16_WCD_A_CDC_CONN_EQ2_B1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_EQ2_B2_CTL (0x3BC) -#define MSM8X16_WCD_A_CDC_CONN_EQ2_B2_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_EQ2_B3_CTL (0x3C0) -#define MSM8X16_WCD_A_CDC_CONN_EQ2_B3_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_EQ2_B4_CTL (0x3C4) -#define MSM8X16_WCD_A_CDC_CONN_EQ2_B4_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_CONN_TX_I2S_SD1_CTL (0x3C8) -#define MSM8X16_WCD_A_CDC_CONN_TX_I2S_SD1_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_TX1_VOL_CTL_TIMER (0x480) -#define MSM8X16_WCD_A_CDC_TX1_VOL_CTL_TIMER__POR (0x00) -#define MSM8X16_WCD_A_CDC_TX2_VOL_CTL_TIMER (0x4A0) -#define MSM8X16_WCD_A_CDC_TX2_VOL_CTL_TIMER__POR (0x00) -#define MSM8X16_WCD_A_CDC_TX1_VOL_CTL_GAIN (0x484) -#define MSM8X16_WCD_A_CDC_TX1_VOL_CTL_GAIN__POR (0x00) -#define MSM8X16_WCD_A_CDC_TX2_VOL_CTL_GAIN (0x4A4) -#define MSM8X16_WCD_A_CDC_TX2_VOL_CTL_GAIN__POR (0x00) -#define MSM8X16_WCD_A_CDC_TX1_VOL_CTL_CFG (0x488) -#define MSM8X16_WCD_A_CDC_TX1_VOL_CTL_CFG__POR (0x00) -#define MSM8X16_WCD_A_CDC_TX2_VOL_CTL_CFG (0x4A8) -#define MSM8X16_WCD_A_CDC_TX2_VOL_CTL_CFG__POR (0x00) -#define MSM8X16_WCD_A_CDC_TX1_MUX_CTL (0x48C) -#define MSM8X16_WCD_A_CDC_TX1_MUX_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_TX2_MUX_CTL (0x4AC) -#define MSM8X16_WCD_A_CDC_TX2_MUX_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_TX1_CLK_FS_CTL (0x490) -#define MSM8X16_WCD_A_CDC_TX1_CLK_FS_CTL__POR (0x03) -#define MSM8X16_WCD_A_CDC_TX2_CLK_FS_CTL (0x4B0) -#define MSM8X16_WCD_A_CDC_TX2_CLK_FS_CTL__POR (0x03) -#define MSM8X16_WCD_A_CDC_TX1_DMIC_CTL (0x494) -#define MSM8X16_WCD_A_CDC_TX1_DMIC_CTL__POR (0x00) -#define MSM8X16_WCD_A_CDC_TX2_DMIC_CTL (0x4B4) -#define MSM8X16_WCD_A_CDC_TX2_DMIC_CTL__POR (0x00) -#endif diff --git a/sound/soc/qcom/qdsp6/core/apr_tal.c b/sound/soc/qcom/qdsp6/core/apr_tal.c index cca94c69554f..db115ce34541 100644 --- a/sound/soc/qcom/qdsp6/core/apr_tal.c +++ b/sound/soc/qcom/qdsp6/core/apr_tal.c @@ -64,25 +64,11 @@ struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest, return NULL; } pr_info("apr_tal:Wakeup done\n"); - apr_svc_ch[dl][dest][svc].dest_state = 0; - } - - rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].wait, - (apr_svc_ch[dl][dest][svc].ch->state == SMD_CHANNEL_OPENED), 5 * HZ); - if (rc == 0) { - pr_err("apr_tal:TIMEOUT for OPEN event\n"); - apr_tal_close(&apr_svc_ch[dl][dest][svc]); - return NULL; - } - if (!apr_svc_ch[dl][dest][svc].dest_state) { - apr_svc_ch[dl][dest][svc].dest_state = 1; - pr_info("apr_tal:Waiting for apr svc init\n"); - msleep(200); - pr_info("apr_tal:apr svc init done\n"); } apr_svc_ch[dl][dest][svc].func = func; apr_svc_ch[dl][dest][svc].priv = priv; + pr_info("apr_tal:apr svc init done\n"); return &apr_svc_ch[dl][dest][svc]; } @@ -99,13 +85,13 @@ int apr_tal_close(struct apr_svc_ch_dev *apr_ch) } -static int qcom_smd_q6_callback(struct qcom_smd_device *sdev, +static int qcom_smd_q6_callback(struct qcom_smd_channel *channel, const void *data, size_t count) { - struct apr_svc_ch_dev *apr_ch = dev_get_drvdata(&sdev->dev); + struct apr_svc_ch_dev *apr_ch = qcom_smd_get_drvdata(channel); - memcpy_fromio(apr_ch->data, data, count); + memcpy(apr_ch->data, data, count); if (apr_ch->func) apr_ch->func(apr_ch->data, count, apr_ch->priv); @@ -115,22 +101,25 @@ static int qcom_smd_q6_callback(struct qcom_smd_device *sdev, static int qcom_smd_q6_probe(struct qcom_smd_device *sdev) { - int dest = APR_DEST_QDSP6; - int clnt = APR_CLIENT_AUDIO; - - apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO].ch = sdev->channel; + struct apr_svc_ch_dev *apr = &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO]; pr_info("apr_tal:Q6 Is Up\n"); - apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1; - wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest); - dev_set_drvdata(&sdev->dev, &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO]); + qcom_smd_set_drvdata(sdev->channel, apr); + + apr->ch = sdev->channel; + apr->dest_state = 1; + wake_up(&apr->dest); return 0; } static void qcom_smd_q6_remove(struct qcom_smd_device *sdev) { + struct apr_svc_ch_dev *apr = &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO]; + + apr->ch = NULL; + apr->dest_state = 0; }