# Debian directory (make deb-pkg)
#
/debian/
+/*.deb
+/*.changes
#
# tar directory (make tar*-pkg)
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
compatible = "qcom,rpm-msm8916";
qcom,smd-channels = "rpm_requests";
- rpmcc: qcom,rpmcc {
+ rpmcc: clock-controller {
compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc";
#clock-cells = <1>;
};
--- /dev/null
+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>;
+ };
+
+};
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 \
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 {
#include "qcom-apq8064-v2.0.dtsi"
-#include "qcom-apq8064-arrow-db600c-pins.dtsi"
+#include "qcom-apq8064-arrow-sd-600eval-pins.dtsi"
#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/mfd/qcom-rpm.h>
/ {
- 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;
};
+ smd {
+ q6@1 {
+ status = "ok";
+ };
+
+ riva@6 {
+ status = "ok";
+ };
+ };
+
soc {
rpm@108000 {
regulators {
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>;
s4 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
- qcom,switch-mode-frequency = <3200000>;
+ qcom,switch-mode-frequency = <1600000>;
+ qcom,force-mode = <QCOM_RPM_FORCE_MODE_AUTO>;
bias-pull-down;
regulator-always-on;
};
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;
};
};
};
target-supply = <&pm8921_lvs7>;
};
+ wcnss@3204000 {
+ status = "ok";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&wcnss_pin_a>;
+ };
+
/* OTG */
phy@12500000 {
status = "okay";
usb@12530000 {
status = "okay";
};
+ pil_q6v4: pil@28800000 {
+ qcom,pll-supply = <&pm8921_l26>;
+ qcom,pll-uV = <1050000>;
+ };
hdmi: qcom,hdmi-tx@4a00000 {
status = "okay";
};
};
+ smd {
+ q6@1 {
+ status = "okay";
+ };
+ };
+
soc {
pinctrl@800000 {
card_detect: card_detect {
reg = <0x80000000 0x200000>;
no-map;
};
+
+ wcnss_mem: wcnss@8f000000 {
+ reg = <0x8f000000 0x700000>;
+ no-map;
+ };
};
cpus {
polling-delay-passive = <250>;
polling-delay = <1000>;
- thermal-sensors = <&tsens 7>;
+ thermal-sensors = <&gcc 7>;
trips {
cpu_alert0: trip@0 {
polling-delay-passive = <250>;
polling-delay = <1000>;
- thermal-sensors = <&tsens 8>;
+ thermal-sensors = <&gcc 8>;
trips {
cpu_alert1: trip@0 {
polling-delay-passive = <250>;
polling-delay = <1000>;
- thermal-sensors = <&tsens 9>;
+ thermal-sensors = <&gcc 9>;
trips {
cpu_alert2: trip@0 {
polling-delay-passive = <250>;
polling-delay = <1000>;
- thermal-sensors = <&tsens 10>;
+ thermal-sensors = <&gcc 10>;
trips {
cpu_alert3: trip@0 {
};
};
- 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>;
};
};
+ 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>;
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";
};
};
+ 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>;
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>;
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
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
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
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
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;
};
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>;
-
+
};
};
};
status = "okay";
clocks = <&gcc GCC_CODEC_DIGCODEC_CLK>;
clock-names = "mclk";
- digital = <&wcd_digital>;
+ qcom,lpass-codec-core = <&lpass_codec_core>;
};
/*
Internal Codec
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 {
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 {
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 {
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 */
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 {
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 {
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 {
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 {
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 {
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 {
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>;
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>;
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>;
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>;
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 {
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>;
};
};
/* 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>;
};
#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";
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>;
};
};
};
- 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 {
};
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>;
};
};
};
# 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
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
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
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
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
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
# 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
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
/*
+ * Copyright (c) 2016, Linaro Ltd.
* Copyright (c) 2015, Sony Mobile Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smd.h>
+#include <linux/soc/qcom/wcnss_ctrl.h>
+#include <linux/platform_device.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci.h>
-
-#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)
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;
}
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) {
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 <bjorn.andersson@sonymobile.com>");
MODULE_DESCRIPTION("Qualcomm SMD HCI driver");
MODULE_LICENSE("GPL v2");
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"
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
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
/*
- * 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
* GNU General Public License for more details.
*/
-#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
-#include "clk-rpm.h"
#include <dt-bindings/mfd/qcom-rpm.h>
+#include <dt-bindings/clock/qcom,rpmcc.h>
+
+#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 */
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;
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);
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;
}
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)
{
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 = {
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);
}
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);
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;
+++ /dev/null
-/*
- * 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 <linux/clk-provider.h>
-
-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
/*
- * 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
#include <linux/platform_device.h>
#include <linux/soc/qcom/smd-rpm.h>
-#include "clk-smd-rpm.h"
#include <dt-bindings/clock/qcom,rpmcc.h>
+#include <dt-bindings/mfd/qcom-rpm.h>
+
+#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)
{
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);
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 = {
.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);
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;
}
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);
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);
+++ /dev/null
-/*
- * 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 <linux/clk-provider.h>
-
-#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
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));
}
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)
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)
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;
}
#include <linux/module.h>
#include <linux/cpumask.h>
#include <linux/export.h>
+#include <linux/dma-mapping.h>
#include <linux/types.h>
#include <linux/qcom_scm.h>
#include <linux/of.h>
#include <linux/clk.h>
+#include <linux/reset-controller.h>
#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;
{
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);
}
/**
}
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
*
* 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);
*/
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);
*/
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);
*/
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);
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;
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;
#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
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,
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
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
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of_platform.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/memblock.h>
-#include <linux/slab.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/regmap.h>
-#include <linux/list.h>
-#include <linux/gpio.h>
-#include <linux/mfd/syscon.h>
-
-#include <linux/delay.h>
-
-#include <linux/soc/qcom/smem.h>
-
-/*
- * 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");
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of_platform.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/memblock.h>
-#include <linux/slab.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/hwspinlock.h>
-#include <linux/regmap.h>
-#include <linux/gpio.h>
-#include <linux/mfd/syscon.h>
-
-#include <linux/delay.h>
-
-#include <linux/soc/qcom/smem.h>
-
-#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 <linux/seq_file.h>
-
-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");
*/
#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+
#include <sound/hdmi-codec.h>
-#include <sound/msm_hdmi_audio.h>
#include "hdmi.h"
void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
}
#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);
#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) {
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;
}
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;
}
}
.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)
{
/*
* 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,
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,
#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)
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 */
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" },
{},
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)
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;
/* 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) >>
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;
{
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;
}
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.
-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
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/interrupt.h>
+#include <linux/soc/qcom/smem_state.h>
#include "wcn36xx.h"
#include "txrx.h"
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",
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;
* 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.
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 */
*/
/* 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
#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. */
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
};
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];
/* 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;
/* Cb State */
enum phy_chan_bond_state state;
-};
+} __packed;
/* Update scan params - sent from host to PNO to be used during PNO
* scanningx */
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/soc/qcom/smd.h>
+#include <linux/soc/qcom/smem_state.h>
+#include <linux/soc/qcom/wcnss_ctrl.h>
#include "wcn36xx.h"
unsigned int wcn36xx_dbg_mask;
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, \
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) {
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)
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) {
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;
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;
.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,
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);
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;
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");
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);
}
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);
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");
#include <linux/etherdevice.h>
#include <linux/firmware.h>
#include <linux/bitops.h>
+#include <linux/soc/qcom/smd.h>
#include "smd.h"
struct wcn36xx_cfg_val {
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;
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;
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;
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);
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;
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);
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);
{
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);
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);
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:
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)
{
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,
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;
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)
{
}
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);
}
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);
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);
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);
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;
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);
/* 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))
+++ /dev/null
-/*
- * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
- * 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 <linux/completion.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/workqueue.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/clk.h>
-#include <linux/remoteproc.h>
-#include <linux/soc/qcom/smd.h>
-#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);
-
/* 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 {
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
*
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];
/* 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
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 */
+++ /dev/null
-#include <linux/completion.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/workqueue.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/clk.h>
-#include <linux/soc/qcom/smd.h>
-#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);
-}
+++ /dev/null
-#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
-
.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,
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 = {
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.
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
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
--- /dev/null
+/*
+ * 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 <linux/elf.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/qcom_scm.h>
+#include <linux/remoteproc.h>
+#include <linux/slab.h>
+
+#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);
--- /dev/null
+#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
/*
* 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.
*
* GNU General Public License for more details.
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/dma-mapping.h>
-#include <linux/firmware.h>
-#include <linux/remoteproc.h>
+#include <linux/io.h>
#include <linux/interrupt.h>
-#include <linux/memblock.h>
-#include <linux/gpio/consumer.h>
-#include <linux/of.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
#include <linux/of_address.h>
-#include <linux/elf.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
-#include <linux/soc/qcom/smem.h>
+#include <linux/remoteproc.h>
#include <linux/reset.h>
-#include <linux/qcom_scm.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/soc/qcom/smem_state.h>
#include "remoteproc_internal.h"
+#include "qcom_mdt_loader.h"
#include <linux/qcom_scm.h>
-#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);
* 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;
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:
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");
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)
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);
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;
--- /dev/null
+/*
+ * 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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/qcom_scm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/remoteproc.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/soc/qcom/smem_state.h>
+
+#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);
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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 <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/qcom_scm.h>
+#include <linux/regulator/consumer.h>
+
+#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);
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,
};
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
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.
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
--- /dev/null
+/* 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 <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <asm/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <uapi/linux/gps_proxy.h>
+
+/* 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);
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of_platform.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/firmware.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-
-#include <linux/soc/qcom/smd.h>
-
-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 <bjorn.andersson@sonymobile.com>");
-MODULE_DESCRIPTION("Qualcomm IPCRTR stub driver");
-MODULE_LICENSE("GPLv2");
-
*/
struct qcom_smd_rpm {
struct qcom_smd_channel *rpm_channel;
+ struct device *dev;
struct completion ack;
struct mutex lock;
}
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];
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;
}
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);
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);
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
*/
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;
len = channel->pkt_size;
}
- ret = channel->cb(qsdev, ptr, len);
+ ret = channel->cb(channel, ptr, len);
if (ret < 0)
return ret;
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
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;
}
{
__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)
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;
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);
}
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
{
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;
ret = channel;
break;
}
- read_unlock(&edge->channels_lock);
+ spin_unlock_irqrestore(&edge->channels_lock, flags);
return ret;
}
* 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 */
/* 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;
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]);
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;
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);
}
/*
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);
}
/*
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);
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;
/*
+ * Copyright (c) 2016, Linaro Ltd.
* Copyright (c) 2015, Sony Mobile Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smd.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/wcnss_ctrl.h>
#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"
* @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 */
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,
};
/**
/**
* 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;
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)) {
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;
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;
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;
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;
}
} 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)
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,
},
};
struct spi_transfer *xfer;
struct completion done;
+ struct completion dma_tx_done;
int error;
int w_size; /* bytes per SPI word */
int n_words;
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;
return -EINVAL;
desc->callback = callback;
- desc->callback_param = qup;
+ desc->callback_param = data;
cookie = dmaengine_submit(desc);
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;
}
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;
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;
}
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;
}
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;
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;
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);
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)
spin_lock_init(&controller->lock);
init_completion(&controller->done);
+ init_completion(&controller->dma_tx_done);
iomode = readl_relaxed(base + QUP_IO_M_MODES);
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 */
#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
--- /dev/null
+#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 */
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);
#include <linux/mod_devicetable.h>
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
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
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
--- /dev/null
+#ifndef __WCNSS_CTRL_H__
+#define __WCNSS_CTRL_H__
+
+#include <linux/soc/qcom/smd.h>
+
+struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb);
+
+#endif
#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
#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. */
#define SOL_CAIF 278
#define SOL_ALG 279
#define SOL_NFC 280
+#define SOL_KCM 281
/* IPX options */
#define IPX_TYPE 1
--- /dev/null
+/*
+ * Kernel Connection Multiplexor
+ *
+ * Copyright (c) 2016 Tom Herbert <tom@herbertland.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef __NET_KCM_H_
+#define __NET_KCM_H_
+
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <uapi/linux/kcm.h>
+
+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_ */
* @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);
}
.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 | \
--- /dev/null
+/* 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__ */
--- /dev/null
+/*
+ * Kernel Connection Multiplexor
+ *
+ * Copyright (c) 2016 Tom Herbert <tom@herbertland.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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
+
--- /dev/null
+#ifndef _LINUX_QRTR_H
+#define _LINUX_QRTR_H
+
+#include <linux/socket.h>
+
+struct sockaddr_qrtr {
+ __kernel_sa_family_t sq_family;
+ __u32 sq_node;
+ __u32 sq_port;
+};
+
+#endif /* _LINUX_QRTR_H */
--- /dev/null
+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
# 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
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
source "net/hsr/Kconfig"
source "net/switchdev/Kconfig"
source "net/l3mdev/Kconfig"
+source "net/qrtr/Kconfig"
config RPS
bool
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
+source "net/kcm/Kconfig"
config FIB_RULES
bool
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/
ifneq ($(CONFIG_NET_L3_MASTER_DEV),)
obj-y += l3mdev/
endif
+obj-$(CONFIG_QRTR) += qrtr/
--- /dev/null
+
+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).
+
--- /dev/null
+obj-$(CONFIG_AF_KCM) += kcm.o
+
+kcm-y := kcmsock.o
--- /dev/null
+#include <linux/bpf.h>
+#include <linux/errno.h>
+#include <linux/errqueue.h>
+#include <linux/file.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+#include <linux/rculist.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <net/kcm.h>
+#include <net/netns/generic.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+#include <uapi/linux/kcm.h>
+
+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);
+
--- /dev/null
+# 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
--- /dev/null
+obj-$(CONFIG_QRTR) := qrtr.o
+
+obj-$(CONFIG_QRTR_SMD) += qrtr-smd.o
+qrtr-smd-y := smd.o
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/qrtr.h>
+#include <linux/termios.h> /* For TIOCINQ/OUTQ */
+
+#include <net/sock.h>
+
+#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; requires CAP_NET_ADMIN
+ * >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");
--- /dev/null
+#ifndef __QRTR_H_
+#define __QRTR_H_
+
+#include <linux/types.h>
+
+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
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/soc/qcom/smd.h>
+
+#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");
config SND_SOC_MAX9850
tristate
-config SND_SOC_MSM8x16_WCD
+config SND_SOC_MSM8916_WCD
tristate "Qualcomm MSM8x16 WCD"
depends on SPMI && MFD_SYSCON
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
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
--- /dev/null
+ /* 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
--- /dev/null
+/* 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 <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#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");
--- /dev/null
+/* 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 */
+++ /dev/null
-/* 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,
-};
+++ /dev/null
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/regulator/consumer.h>
-#include <linux/types.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-#include <linux/mfd/syscon.h>
-
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/tlv.h>
-
-#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<<w->shift,
- 1<<w->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<<w->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");
+++ /dev/null
-/* 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 <linux/types.h>
-
-#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
-
+++ /dev/null
- /* 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
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];
}
}
-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);
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;
}